least important ones are skipped gracefully rather than the allocation failing
arbitrarily. Ties are broken by name. Priority does not affect the NV index,
the algorithm, or anything measured into the NvPCR.
+* `orderly` — a boolean, defaulting to `true`. It controls whether the NV index
+ is allocated with the `TPMA_NV_ORDERLY` attribute set, which selects whether
+ the TPM keeps the NV index in RAM or in persistent memory (NVRAM). On
+ physical TPMs RAM is typically much more constrained than persistent memory,
+ but persistent memory is subject to wear. We hence prefer `TPMA_NV_ORDERLY`
+ disabled (i.e. NVRAM) for NvPCRs that are written only once each boot — which
+ translates into a conservative number of write cycles over the lifetime of a
+ TPM — but enabled (i.e. RAM) for NvPCRs we expect to be written many times
+ during runtime, so that we minimize wear. This reflects real-life experience
+ where the RAM in TPMs is so constrained that allocating many NvPCRs in TPM
+ RAM simply doesn't work. For now, only the `hardware` NvPCR (which is written
+ just once, early at boot) sets this flag to false.
There's one complication: these NV indexes (like any NV indexes) can be deleted
by anyone with access to the TPM, and then be recreated. This could be used to
const Tpm2Handle *session,
TPM2_HANDLE nv_index,
TPMI_ALG_HASH algorithm,
+ bool orderly,
Tpm2Handle **ret_nv_handle) {
_cleanup_(tpm2_handle_freep) Tpm2Handle *new_handle = NULL;
.nvIndex = nv_index,
.nameAlg = algorithm,
.attributes = TPMA_NV_CLEAR_STCLEAR |
- TPMA_NV_ORDERLY |
+ (orderly ? TPMA_NV_ORDERLY : 0) |
TPMA_NV_OWNERWRITE |
TPMA_NV_AUTHWRITE |
TPMA_NV_OWNERREAD |
if (nv_public_real->size < endoffsetof_field(TPMS_NV_PUBLIC, attributes) + sizeof_field(TPMS_NV_PUBLIC, dataSize) ||
nv_public_real->nvPublic.nvIndex != public_info.nvPublic.nvIndex ||
nv_public_real->nvPublic.nameAlg != public_info.nvPublic.nameAlg ||
- ((nv_public_real->nvPublic.attributes ^ public_info.nvPublic.attributes) & ~TPMA_NV_WRITTEN) != 0 ||
+ ((nv_public_real->nvPublic.attributes ^ public_info.nvPublic.attributes) & ~(TPMA_NV_WRITTEN|TPMA_NV_ORDERLY)) != 0 ||
nv_public_real->nvPublic.dataSize != public_info.nvPublic.dataSize)
return log_debug_errno(SYNTHETIC_ERRNO(EEXIST),
"Public data of nvindex 0x%x does not match our expectations.", nv_index);
uint16_t algorithm;
uint32_t nv_index;
uint64_t priority;
+ bool orderly;
} NvPCRData;
static void nvpcr_data_done(NvPCRData *d) {
{ "algorithm", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_tpm2_algorithm, offsetof(NvPCRData, algorithm), 0 },
{ "nvIndex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(NvPCRData, nv_index), SD_JSON_MANDATORY },
{ "priority", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(NvPCRData, priority), 0 },
+ { "orderly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(NvPCRData, orderly), 0 },
{},
};
_cleanup_(nvpcr_data_done) NvPCRData p = {
.algorithm = TPM2_ALG_SHA256,
.priority = TPM2_NVPCR_PRIORITY_DEFAULT,
+ .orderly = true,
};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
session,
p.nv_index,
p.algorithm,
+ p.orderly,
&nv_handle);
if (r < 0)
return r;