| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 
 | /* SPDX-License-Identifier: BSD-3-Clause */
#ifndef LIB_TPM2_NV_UTIL_H_
#define LIB_TPM2_NV_UTIL_H_
#include <tss2/tss2_esys.h>
#include <string.h>
#include "log.h"
#include "tpm2.h"
#include "tpm2_capability.h"
#include "tpm2_session.h"
#include "tpm2_auth_util.h"
#include "tpm2_hierarchy.h"
#include "tpm2_util.h"
/*
 * The default buffer size when one cannot be determined via get capability.
 */
#define NV_DEFAULT_BUFFER_SIZE 512
/**
 * Reads the public portion of a Non-Volatile (nv) index.
 * @param context
 *  The ESAPI context.
 * @param nv_index
 *  The index to read.
 * @param nv_public
 *  The public data structure to store the results in.
 * @return
 *  tool_rc indicating status.
 */
static inline tool_rc tpm2_util_nv_read_public(ESYS_CONTEXT *context,
        TPMI_RH_NV_INDEX nv_index, TPM2B_NV_PUBLIC **nv_public) {
    ESYS_TR tr_object;
    tool_rc rc = tpm2_from_tpm_public(context, nv_index, ESYS_TR_NONE,
            ESYS_TR_NONE, ESYS_TR_NONE, &tr_object);
    if (rc != tool_rc_success) {
        return rc;
    }
    rc = tpm2_nv_readpublic(context, tr_object, nv_public, NULL);
    tool_rc tmp_rc = tpm2_close(context, &tr_object);
    if (tmp_rc != tool_rc_success) {
        rc = tmp_rc;
    }
    return rc;
}
/**
 * Retrieves the maximum transmission size for an NV buffer by
 * querying the capabilities for TPM2_PT_NV_BUFFER_MAX.
 * @param context
 *  The Enhanced System API (ESAPI) context
 * @param size
 *  The size of the buffer.
 * @return
 *  tool_rc Indicating status.
 */
static inline tool_rc tpm2_util_nv_max_buffer_size(ESYS_CONTEXT *ectx,
        UINT32 *size) {
    /* Get the maximum read block size */
    TPMS_CAPABILITY_DATA *cap_data;
    TPMI_YES_NO more_data;
    tool_rc rc = tpm2_getcap(ectx, TPM2_CAP_TPM_PROPERTIES,
            TPM2_PT_NV_BUFFER_MAX, 1, &more_data, &cap_data);
    if (rc != tool_rc_success) {
        return rc;
    }
    if ( cap_data->data.tpmProperties.tpmProperty[0].property == TPM2_PT_NV_BUFFER_MAX ) {
        *size = cap_data->data.tpmProperties.tpmProperty[0].value;
    } else {
        /* TPM2_PT_NV_BUFFER_MAX is not part of the module spec <= 0.98*/
        *size = NV_DEFAULT_BUFFER_SIZE;
    }
    free(cap_data);
    return rc;
}
/**
 * Reads data at Non-Volatile (nv) index.
 * @param ectx
 *  The ESAPI context.
 * @param nv_index
 *  The index to read.
 * @param size
 *  The number of bytes to read.
 * @param offset
 *  Offset (in bytes) from which to start reading.
 * @param hierarchy
 *  TPMI_RH_NV hierarchy value.
 * @param sdata
 *  Session authorization
 * @param sess
 *  tpm2 session.
 * @param data_buffer
 *  (callee-allocated) Buffer containing data at nv_index
 * @param bytes_written
 *  The number of bytes written to data_buffer
 * @return
 *  tool_rc indicating status.
 */
static inline tool_rc tpm2_util_nv_read(ESYS_CONTEXT *ectx,
        TPMI_RH_NV_INDEX nv_index, UINT16 size, UINT16 offset,
        tpm2_loaded_object * auth_hierarchy_obj, UINT8 **data_buffer,
        UINT16 *bytes_written, TPM2B_DIGEST *cp_hash) {
    *data_buffer = NULL;
    TPM2B_NV_PUBLIC *nv_public = NULL;
    tool_rc rc = tpm2_util_nv_read_public(ectx, nv_index, &nv_public);
    if (rc != tool_rc_success) {
        goto out;
    }
    UINT16 data_size = nv_public->nvPublic.dataSize;
    free(nv_public);
    /* if size is 0, assume the whole object */
    if (size == 0) {
        size = data_size;
    }
    if (offset > data_size) {
        LOG_ERR("Requested offset to read from is greater than size. offset=%u"
                ", size=%u", offset, data_size);
        rc = tool_rc_general_error;
        goto out;
    }
    if (offset + size > data_size) {
        LOG_ERR("Requested to read more bytes than available from offset,"
                " offset=%u, request-read-size=%u actual-data-size=%u", offset,
                size, data_size);
        rc = tool_rc_general_error;
        goto out;
    }
    if (cp_hash) {
        goto tpm2_util_nv_read_collect_cp_hash;
    }
    UINT32 max_data_size;
    rc = tpm2_util_nv_max_buffer_size(ectx, &max_data_size);
    if (rc != tool_rc_success) {
        goto out;
    }
    if (max_data_size > TPM2_MAX_NV_BUFFER_SIZE) {
        max_data_size = TPM2_MAX_NV_BUFFER_SIZE;
    } else if (max_data_size == 0) {
        max_data_size = NV_DEFAULT_BUFFER_SIZE;
    }
tpm2_util_nv_read_collect_cp_hash:
    if (cp_hash) {
        TPM2B_MAX_NV_BUFFER *nv_data;
        rc = tpm2_nv_read(ectx, auth_hierarchy_obj, nv_index, size, offset,
            &nv_data, cp_hash);
        if (rc != tool_rc_success) {
            LOG_ERR("Failed cpHash for NVRAM read at index 0x%X", nv_index);
        }
        goto out;
    }
    *data_buffer = malloc(data_size);
    if (!*data_buffer) {
        LOG_ERR("oom");
        rc = tool_rc_general_error;
        goto out;
    }
    UINT16 data_offset = 0;
    while (size > 0) {
        UINT16 bytes_to_read = size > max_data_size ? max_data_size : size;
        TPM2B_MAX_NV_BUFFER *nv_data;
        rc = tpm2_nv_read(ectx, auth_hierarchy_obj, nv_index, bytes_to_read,
            offset, &nv_data, cp_hash);
        if (rc != tool_rc_success) {
            LOG_ERR("Failed to read NVRAM area at index 0x%X", nv_index);
            goto out;
        }
        size -= nv_data->size;
        offset += nv_data->size;
        memcpy(*data_buffer + data_offset, nv_data->buffer, nv_data->size);
        data_offset += nv_data->size;
        free(nv_data);
    }
    if (bytes_written) {
        *bytes_written = data_offset;
    }
out:
    if (rc != tool_rc_success && *data_buffer != NULL) {
        free(*data_buffer);
        *data_buffer = NULL;
    }
    return rc;
}
static inline bool on_arg_nv_index(int argc, char **argv,
        TPMI_RH_NV_INDEX *nv_index) {
    if (argc > 1) {
        LOG_ERR("Specify single value for NV Index");
        return false;
    }
    if (!argc) {
        LOG_ERR("Specify at least a single value for NV Index");
        return false;
    }
    bool result = tpm2_util_handle_from_optarg(argv[0], nv_index,
            TPM2_HANDLE_FLAGS_NV);
    if (!result) {
        LOG_ERR("Could not convert NV index to number, got: \"%s\"", argv[0]);
        return false;
    }
    if (*nv_index == 0) {
        LOG_ERR("NV Index cannot be 0");
        return false;
    }
    return true;
}
#endif /* LIB_TPM2_NV_UTIL_H_ */
 |