summaryrefslogtreecommitdiffstats
path: root/src/shared/tpm2-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/tpm2-util.c')
-rw-r--r--src/shared/tpm2-util.c878
1 files changed, 608 insertions, 270 deletions
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index c7e0b24..87ce53c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "constants.h"
+#include "creds-util.h"
#include "cryptsetup-util.h"
#include "dirent-util.h"
#include "dlfcn-util.h"
@@ -25,8 +26,10 @@
#include "nulstr-util.h"
#include "parse-util.h"
#include "random-util.h"
+#include "recurse-dir.h"
#include "sha256.h"
#include "sort-util.h"
+#include "sparse-endian.h"
#include "stat-util.h"
#include "string-table.h"
#include "sync-util.h"
@@ -34,76 +37,87 @@
#include "tpm2-util.h"
#include "virt.h"
+#if HAVE_OPENSSL
+# include <openssl/hmac.h>
+#endif
+
#if HAVE_TPM2
static void *libtss2_esys_dl = NULL;
static void *libtss2_rc_dl = NULL;
static void *libtss2_mu_dl = NULL;
-static TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
-static TSS2_RC (*sym_Esys_CreateLoaded)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_TEMPLATE *inPublic, ESYS_TR *objectHandle, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic) = NULL;
-static TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
-static TSS2_RC (*sym_Esys_EvictControl)(ESYS_CONTEXT *esysContext, ESYS_TR auth, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPMI_DH_PERSISTENT persistentHandle, ESYS_TR *newObjectHandle) = NULL;
-static void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
-static TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
-static void (*sym_Esys_Free)(void *ptr) = NULL;
-static TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData) = NULL;
-static TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
-static TSS2_RC (*sym_Esys_Import)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DATA *encryptionKey, const TPM2B_PUBLIC *objectPublic, const TPM2B_PRIVATE *duplicate, const TPM2B_ENCRYPTED_SECRET *inSymSeed, const TPMT_SYM_DEF_OBJECT *symmetricAlg, TPM2B_PRIVATE **outPrivate) = NULL;
-static TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
-static TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
-static TSS2_RC (*sym_Esys_LoadExternal)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR hierarchy, ESYS_TR *objectHandle) = NULL;
-static TSS2_RC (*sym_Esys_NV_DefineSpace)(ESYS_CONTEXT *esysContext, ESYS_TR authHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_AUTH *auth, const TPM2B_NV_PUBLIC *publicInfo, ESYS_TR *nvHandle);
-static TSS2_RC (*sym_Esys_NV_UndefineSpace)(ESYS_CONTEXT *esysContext, ESYS_TR authHandle, ESYS_TR nvIndex, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3);
-static TSS2_RC (*sym_Esys_NV_Write)(ESYS_CONTEXT *esysContext, ESYS_TR authHandle, ESYS_TR nvIndex, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_MAX_NV_BUFFER *data, UINT16 offset);
-static TSS2_RC (*sym_Esys_PCR_Extend)(ESYS_CONTEXT *esysContext, ESYS_TR pcrHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPML_DIGEST_VALUES *digests) = NULL;
-static TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues) = NULL;
-static TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL;
-static TSS2_RC (*sym_Esys_PolicyAuthorize)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *approvedPolicy, const TPM2B_NONCE *policyRef, const TPM2B_NAME *keySign, const TPMT_TK_VERIFIED *checkTicket) = NULL;
-static TSS2_RC (*sym_Esys_PolicyAuthorizeNV)(ESYS_CONTEXT *esysContext, ESYS_TR authHandle, ESYS_TR nvIndex, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3);
-static TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
-static TSS2_RC (*sym_Esys_PolicyOR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPML_DIGEST *pHashList) = NULL;
-static TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
-static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
-static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
-static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
-static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
-static TSS2_RC (*sym_Esys_TR_Close)(ESYS_CONTEXT *esys_context, ESYS_TR *rsrc_handle) = NULL;
-static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
-static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
-static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
-static TSS2_RC (*sym_Esys_TR_GetTpmHandle)(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, TPM2_HANDLE *tpm_handle) = NULL;
-static TSS2_RC (*sym_Esys_TR_Serialize)(ESYS_CONTEXT *esys_context, ESYS_TR object, uint8_t **buffer, size_t *buffer_size) = NULL;
-static TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
-static TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags) = NULL;
-static TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask) = NULL;
-static TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
-static TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *digest, const TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation) = NULL;
-
-static TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2_HANDLE_Marshal)(TPM2_HANDLE src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_DIGEST_Marshal)(TPM2B_DIGEST const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_ENCRYPTED_SECRET_Marshal)(TPM2B_ENCRYPTED_SECRET const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_ENCRYPTED_SECRET_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_ENCRYPTED_SECRET *dest) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_NAME_Marshal)(TPM2B_NAME const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_SENSITIVE_Marshal)(TPM2B_SENSITIVE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPMS_NV_PUBLIC_Marshal)(TPMS_NV_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_NV_PUBLIC_Marshal)(TPM2B_NV_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_NV_PUBLIC *dest) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPMS_ECC_POINT_Marshal)(TPMS_ECC_POINT const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_UINT32_Marshal)(UINT32 src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-
-static const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
+static DLSYM_FUNCTION(Esys_Create);
+static DLSYM_FUNCTION(Esys_CreateLoaded);
+static DLSYM_FUNCTION(Esys_CreatePrimary);
+static DLSYM_FUNCTION(Esys_EvictControl);
+static DLSYM_FUNCTION(Esys_Finalize);
+static DLSYM_FUNCTION(Esys_FlushContext);
+static DLSYM_FUNCTION(Esys_Free);
+static DLSYM_FUNCTION(Esys_GetCapability);
+static DLSYM_FUNCTION(Esys_GetRandom);
+static DLSYM_FUNCTION(Esys_Import);
+static DLSYM_FUNCTION(Esys_Initialize);
+static DLSYM_FUNCTION(Esys_Load);
+static DLSYM_FUNCTION(Esys_LoadExternal);
+static DLSYM_FUNCTION(Esys_NV_DefineSpace);
+static DLSYM_FUNCTION(Esys_NV_UndefineSpace);
+static DLSYM_FUNCTION(Esys_NV_Write);
+static DLSYM_FUNCTION(Esys_PCR_Extend);
+static DLSYM_FUNCTION(Esys_PCR_Read);
+static DLSYM_FUNCTION(Esys_PolicyAuthValue);
+static DLSYM_FUNCTION(Esys_PolicyAuthorize);
+static DLSYM_FUNCTION(Esys_PolicyAuthorizeNV);
+static DLSYM_FUNCTION(Esys_PolicyGetDigest);
+static DLSYM_FUNCTION(Esys_PolicyOR);
+static DLSYM_FUNCTION(Esys_PolicyPCR);
+static DLSYM_FUNCTION(Esys_PolicySigned);
+static DLSYM_FUNCTION(Esys_ReadPublic);
+static DLSYM_FUNCTION(Esys_StartAuthSession);
+static DLSYM_FUNCTION(Esys_Startup);
+static DLSYM_FUNCTION(Esys_TestParms);
+static DLSYM_FUNCTION(Esys_TR_Close);
+static DLSYM_FUNCTION(Esys_TR_Deserialize);
+static DLSYM_FUNCTION(Esys_TR_FromTPMPublic);
+static DLSYM_FUNCTION(Esys_TR_GetName);
+static DLSYM_FUNCTION(Esys_TR_GetTpmHandle);
+static DLSYM_FUNCTION(Esys_TR_Serialize);
+static DLSYM_FUNCTION(Esys_TR_SetAuth);
+static DLSYM_FUNCTION(Esys_TRSess_GetAttributes);
+static DLSYM_FUNCTION(Esys_TRSess_GetNonceTPM);
+static DLSYM_FUNCTION(Esys_TRSess_SetAttributes);
+static DLSYM_FUNCTION(Esys_Unseal);
+static DLSYM_FUNCTION(Esys_VerifySignature);
+
+static DLSYM_FUNCTION(Tss2_MU_TPM2_CC_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2_HANDLE_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_DIGEST_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_ENCRYPTED_SECRET_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_ENCRYPTED_SECRET_Unmarshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_NAME_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_PRIVATE_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_PRIVATE_Unmarshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_PUBLIC_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_PUBLIC_Unmarshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_SENSITIVE_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPML_PCR_SELECTION_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPMS_NV_PUBLIC_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_NV_PUBLIC_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal);
+static DLSYM_FUNCTION(Tss2_MU_TPMS_ECC_POINT_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPMT_HA_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_TPMT_PUBLIC_Marshal);
+static DLSYM_FUNCTION(Tss2_MU_UINT32_Marshal);
+
+static DLSYM_FUNCTION(Tss2_RC_Decode);
int dlopen_tpm2(void) {
int r;
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-esys.so.0");
+
r = dlopen_many_sym_or_warn(
&libtss2_esys_dl, "libtss2-esys.so.0", LOG_DEBUG,
DLSYM_ARG(Esys_Create),
@@ -130,6 +144,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_PolicyGetDigest),
DLSYM_ARG(Esys_PolicyOR),
DLSYM_ARG(Esys_PolicyPCR),
+ DLSYM_ARG(Esys_PolicySigned),
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
@@ -141,6 +156,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_TR_Serialize),
DLSYM_ARG(Esys_TR_SetAuth),
DLSYM_ARG(Esys_TRSess_GetAttributes),
+ DLSYM_ARG(Esys_TRSess_GetNonceTPM),
DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_Unseal),
DLSYM_ARG(Esys_VerifySignature));
@@ -153,12 +169,22 @@ int dlopen_tpm2(void) {
if (r < 0)
log_debug("libtss2-esys too old, does not include Esys_TR_GetTpmHandle.");
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-rc.so.0");
+
r = dlopen_many_sym_or_warn(
&libtss2_rc_dl, "libtss2-rc.so.0", LOG_DEBUG,
DLSYM_ARG(Tss2_RC_Decode));
if (r < 0)
return r;
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-mu.so.0");
+
return dlopen_many_sym_or_warn(
&libtss2_mu_dl, "libtss2-mu.so.0", LOG_DEBUG,
DLSYM_ARG(Tss2_MU_TPM2_CC_Marshal),
@@ -1895,7 +1921,7 @@ int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) {
_cleanup_free_ void *buf = NULL;
size_t buf_size = 0;
- r = unhexmem(p, SIZE_MAX, &buf, &buf_size);
+ r = unhexmem(p, &buf, &buf_size);
if (r < 0)
return log_debug_errno(r, "Invalid pcr hash value '%s': %m", p);
@@ -2084,7 +2110,7 @@ int tpm2_create_primary(
session ? session->esys_handle : ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
- sensitive ? sensitive : &(TPM2B_SENSITIVE_CREATE) {},
+ sensitive ?: &(TPM2B_SENSITIVE_CREATE) {},
template,
/* outsideInfo= */ NULL,
&(TPML_PCR_SELECTION) {},
@@ -2254,9 +2280,9 @@ static int tpm2_load_external(
#if HAVE_TSS2_ESYS3
/* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
* hierarchy, older versions need TPM2_RH_* instead. */
- ESYS_TR_RH_OWNER,
+ private ? ESYS_TR_RH_NULL : ESYS_TR_RH_OWNER,
#else
- TPM2_RH_OWNER,
+ private ? TPM2_RH_NULL : TPM2_RH_OWNER,
#endif
&handle->esys_handle);
if (rc != TSS2_RC_SUCCESS)
@@ -3074,7 +3100,7 @@ static void tpm2_trim_auth_value(TPM2B_AUTH *auth) {
log_debug("authValue ends in 0, trimming as required by the TPM2 specification Part 1 section 'HMAC Computation' authValue Note 2.");
}
-int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
+int tpm2_auth_value_from_pin(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
TPM2B_AUTH auth = {};
int r;
@@ -3121,7 +3147,7 @@ int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin) {
CLEANUP_ERASE(auth);
- r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
+ r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
if (r < 0)
return r;
@@ -3408,7 +3434,7 @@ int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name)
*
* The handle must reference a key already present in the TPM. It may be either a public key only, or a
* public/private keypair. */
-static int tpm2_get_name(
+int tpm2_get_name(
Tpm2Context *c,
const Tpm2Handle *handle,
TPM2B_NAME **ret_name) {
@@ -3546,6 +3572,150 @@ int tpm2_policy_auth_value(
return tpm2_get_policy_digest(c, session, ret_policy_digest);
}
+/* Extend 'digest' with the PolicySigned calculated hash. */
+int tpm2_calculate_policy_signed(TPM2B_DIGEST *digest, const TPM2B_NAME *name) {
+ TPM2_CC command = TPM2_CC_PolicySigned;
+ TSS2_RC rc;
+ int r;
+
+ assert(digest);
+ assert(digest->size == SHA256_DIGEST_SIZE);
+ assert(name);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_debug_errno(r, "TPM2 support not installed: %m");
+
+ uint8_t buf[sizeof(command)];
+ size_t offset = 0;
+
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PolicySigned command: %s", sym_Tss2_RC_Decode(rc));
+
+ if (offset != sizeof(command))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Offset 0x%zx wrong after marshalling PolicySigned command", offset);
+
+ struct iovec data[] = {
+ IOVEC_MAKE(buf, offset),
+ IOVEC_MAKE(name->name, name->size),
+ };
+
+ r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ const TPM2B_NONCE policyRef = {}; /* For now, we do not make use of the policyRef stuff */
+
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, digest, policyRef.buffer, policyRef.size, /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicySigned calculated digest");
+
+ return 0;
+}
+
+int tpm2_policy_signed_hmac_sha256(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const Tpm2Handle *hmac_key_handle,
+ const struct iovec *hmac_key,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+#if HAVE_OPENSSL
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(session);
+ assert(hmac_key_handle);
+ assert(iovec_is_set(hmac_key));
+
+ /* This sends a TPM2_PolicySigned command to the tpm. As signature key we use an HMAC-SHA256 key
+ * specified in the hmac_key parameter. The secret key must be loaded into the TPM already and
+ * referenced in hmac_key_handle. */
+
+ log_debug("Submitting PolicySigned policy for HMAC-SHA256.");
+
+ /* Acquire the nonce from the TPM that we shall sign */
+ _cleanup_(Esys_Freep) TPM2B_NONCE *nonce = NULL;
+ rc = sym_Esys_TRSess_GetNonceTPM(
+ c->esys_context,
+ session->esys_handle,
+ &nonce);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to determine NoneTPM of auth session: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ be32_t expiration = htobe64(0);
+ const TPM2B_DIGEST cpHashA = {}; /* For now, we do not make use of the cpHashA stuff */
+ const TPM2B_NONCE policyRef = {}; /* ditto, we do not bother with policyRef */
+
+ /* Put together the data to sign, as per TPM2 Spec Part 3, 23.3.1 */
+ struct iovec data_to_sign[] = {
+ IOVEC_MAKE(nonce->buffer, nonce->size),
+ IOVEC_MAKE(&expiration, sizeof(expiration)),
+ IOVEC_MAKE(cpHashA.buffer, cpHashA.size),
+ IOVEC_MAKE(policyRef.buffer, policyRef.size),
+ };
+
+ /* Now calculate the digest of the data we put together */
+ TPM2B_DIGEST digest_to_sign;
+ r = tpm2_digest_many(TPM2_ALG_SHA256, &digest_to_sign, data_to_sign, ELEMENTSOF(data_to_sign), /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ unsigned char hmac_signature[SHA256_DIGEST_SIZE];
+ unsigned hmac_signature_size = sizeof(hmac_signature);
+
+ /* And sign this with our key */
+ if (!HMAC(EVP_sha256(),
+ hmac_key->iov_base,
+ hmac_key->iov_len,
+ digest_to_sign.buffer,
+ digest_to_sign.size,
+ hmac_signature,
+ &hmac_signature_size))
+ return -ENOTRECOVERABLE;
+
+ /* Now bring the signature into a format that the TPM understands */
+ TPMT_SIGNATURE sig = {
+ .sigAlg = TPM2_ALG_HMAC,
+ .signature.hmac.hashAlg = TPM2_ALG_SHA256,
+ };
+ assert(hmac_signature_size == sizeof(sig.signature.hmac.digest.sha256));
+ memcpy(sig.signature.hmac.digest.sha256, hmac_signature, hmac_signature_size);
+
+ /* And submit the whole shebang to the TPM */
+ rc = sym_Esys_PolicySigned(
+ c->esys_context,
+ hmac_key_handle->esys_handle,
+ session->esys_handle,
+ /* shandle1= */ ESYS_TR_NONE,
+ /* shandle2= */ ESYS_TR_NONE,
+ /* shandle3= */ ESYS_TR_NONE,
+ nonce,
+ &cpHashA,
+ &policyRef,
+ expiration,
+ &sig,
+ /* timeout= */ NULL,
+ /* policyTicket= */ NULL);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add PolicySigned policy to TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
+#else /* HAVE_OPENSSL */
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
+#endif
+}
+
int tpm2_calculate_policy_authorize_nv(
const TPM2B_NV_PUBLIC *public_info,
TPM2B_DIGEST *digest) {
@@ -4151,7 +4321,7 @@ static const struct {
static int tpm2_ecc_curve_from_openssl_curve_id(int openssl_ecc_curve_id, TPM2_ECC_CURVE *ret) {
assert(ret);
- FOREACH_ARRAY(t, tpm2_openssl_ecc_curve_table, ELEMENTSOF(tpm2_openssl_ecc_curve_table))
+ FOREACH_ELEMENT(t, tpm2_openssl_ecc_curve_table)
if (t->openssl_ecc_curve_id == openssl_ecc_curve_id) {
*ret = t->tpm2_ecc_curve_id;
return 0;
@@ -4164,7 +4334,7 @@ static int tpm2_ecc_curve_from_openssl_curve_id(int openssl_ecc_curve_id, TPM2_E
static int tpm2_ecc_curve_to_openssl_curve_id(TPM2_ECC_CURVE tpm2_ecc_curve_id, int *ret) {
assert(ret);
- FOREACH_ARRAY(t, tpm2_openssl_ecc_curve_table, ELEMENTSOF(tpm2_openssl_ecc_curve_table))
+ FOREACH_ELEMENT(t, tpm2_openssl_ecc_curve_table)
if (t->tpm2_ecc_curve_id == tpm2_ecc_curve_id) {
*ret = t->openssl_ecc_curve_id;
return 0;
@@ -4801,7 +4971,7 @@ static int tpm2_calculate_seal_private(
TPM2B_AUTH auth = {};
if (pin) {
- r = tpm2_get_pin_auth(parent->publicArea.nameAlg, pin, &auth);
+ r = tpm2_auth_value_from_pin(parent->publicArea.nameAlg, pin, &auth);
if (r < 0)
return r;
}
@@ -5035,7 +5205,7 @@ static int tpm2_calculate_seal_ecc_seed(
size_t bits = (size_t) r * 8;
_cleanup_free_ void *seed = NULL;
- size_t seed_size;
+ size_t seed_size = 0; /* Explicit initialization to appease gcc */
r = tpm2_kdfe(parent->publicArea.nameAlg,
shared_secret,
shared_secret_size,
@@ -5072,7 +5242,7 @@ static int tpm2_calculate_seal_seed(
log_debug("Calculating encrypted seed for sealed object.");
_cleanup_free_ void *seed = NULL, *encrypted_seed = NULL;
- size_t seed_size, encrypted_seed_size;
+ size_t seed_size = 0, encrypted_seed_size = 0; /* Explicit initialization to appease gcc */
if (parent->publicArea.type == TPM2_ALG_RSA)
r = tpm2_calculate_seal_rsa_seed(parent, &seed, &seed_size, &encrypted_seed, &encrypted_seed_size);
else if (parent->publicArea.type == TPM2_ALG_ECC)
@@ -5095,28 +5265,22 @@ int tpm2_calculate_seal(
TPM2_HANDLE parent_handle,
const TPM2B_PUBLIC *parent_public,
const TPMA_OBJECT *attributes,
- const void *secret,
- size_t secret_size,
+ const struct iovec *secret,
const TPM2B_DIGEST *policy,
const char *pin,
- void **ret_secret,
- size_t *ret_secret_size,
- void **ret_blob,
- size_t *ret_blob_size,
- void **ret_serialized_parent,
- size_t *ret_serialized_parent_size) {
+ struct iovec *ret_secret,
+ struct iovec *ret_blob,
+ struct iovec *ret_serialized_parent) {
#if HAVE_OPENSSL
int r;
assert(parent_public);
- assert(secret || secret_size == 0);
+ assert(iovec_is_valid(secret));
assert(secret || ret_secret);
assert(!(secret && ret_secret)); /* Either provide a secret, or we create one, but not both */
assert(ret_blob);
- assert(ret_blob_size);
assert(ret_serialized_parent);
- assert(ret_serialized_parent_size);
log_debug("Calculating sealed object.");
@@ -5137,27 +5301,27 @@ int tpm2_calculate_seal(
parent_handle);
}
- _cleanup_(erase_and_freep) void *generated_secret = NULL;
+ _cleanup_(iovec_done_erase) struct iovec generated_secret = {};
if (!secret) {
/* No secret provided, generate a random secret. We use SHA256 digest length, though it can
* be up to TPM2_MAX_SEALED_DATA. The secret length is not limited to the nameAlg hash
* size. */
- secret_size = TPM2_SHA256_DIGEST_SIZE;
- generated_secret = malloc(secret_size);
- if (!generated_secret)
+ generated_secret.iov_len = TPM2_SHA256_DIGEST_SIZE;
+ generated_secret.iov_base = malloc(generated_secret.iov_len);
+ if (!generated_secret.iov_base)
return log_oom_debug();
- r = crypto_random_bytes(generated_secret, secret_size);
+ r = crypto_random_bytes(generated_secret.iov_base, generated_secret.iov_len);
if (r < 0)
return log_debug_errno(r, "Failed to generate secret key: %m");
- secret = generated_secret;
+ secret = &generated_secret;
}
- if (secret_size > TPM2_MAX_SEALED_DATA)
+ if (secret->iov_len > TPM2_MAX_SEALED_DATA)
return log_debug_errno(SYNTHETIC_ERRNO(EOVERFLOW),
"Secret size %zu too large, limit is %d bytes.",
- secret_size, TPM2_MAX_SEALED_DATA);
+ secret->iov_len, TPM2_MAX_SEALED_DATA);
TPM2B_DIGEST random_seed;
TPM2B_ENCRYPTED_SECRET seed;
@@ -5166,7 +5330,7 @@ int tpm2_calculate_seal(
return r;
TPM2B_PUBLIC public;
- r = tpm2_calculate_seal_public(parent_public, attributes, policy, &random_seed, secret, secret_size, &public);
+ r = tpm2_calculate_seal_public(parent_public, attributes, policy, &random_seed, secret->iov_base, secret->iov_len, &public);
if (r < 0)
return r;
@@ -5176,13 +5340,12 @@ int tpm2_calculate_seal(
return r;
TPM2B_PRIVATE private;
- r = tpm2_calculate_seal_private(parent_public, &name, pin, &random_seed, secret, secret_size, &private);
+ r = tpm2_calculate_seal_private(parent_public, &name, pin, &random_seed, secret->iov_base, secret->iov_len, &private);
if (r < 0)
return r;
- _cleanup_free_ void *blob = NULL;
- size_t blob_size;
- r = tpm2_marshal_blob(&public, &private, &seed, &blob, &blob_size);
+ _cleanup_(iovec_done) struct iovec blob = {};
+ r = tpm2_marshal_blob(&public, &private, &seed, &blob.iov_base, &blob.iov_len);
if (r < 0)
return log_debug_errno(r, "Could not create sealed blob: %m");
@@ -5191,25 +5354,20 @@ int tpm2_calculate_seal(
if (r < 0)
return r;
- _cleanup_free_ void *serialized_parent = NULL;
- size_t serialized_parent_size;
+ _cleanup_(iovec_done) struct iovec serialized_parent = {};
r = tpm2_calculate_serialize(
parent_handle,
&parent_name,
parent_public,
- &serialized_parent,
- &serialized_parent_size);
+ &serialized_parent.iov_base,
+ &serialized_parent.iov_len);
if (r < 0)
return r;
if (ret_secret)
- *ret_secret = TAKE_PTR(generated_secret);
- if (ret_secret_size)
- *ret_secret_size = secret_size;
- *ret_blob = TAKE_PTR(blob);
- *ret_blob_size = blob_size;
- *ret_serialized_parent = TAKE_PTR(serialized_parent);
- *ret_serialized_parent_size = serialized_parent_size;
+ *ret_secret = TAKE_STRUCT(generated_secret);
+ *ret_blob = TAKE_STRUCT(blob);
+ *ret_serialized_parent = TAKE_STRUCT(serialized_parent);
return 0;
#else /* HAVE_OPENSSL */
@@ -5221,21 +5379,16 @@ int tpm2_seal(Tpm2Context *c,
uint32_t seal_key_handle,
const TPM2B_DIGEST *policy,
const char *pin,
- void **ret_secret,
- size_t *ret_secret_size,
- void **ret_blob,
- size_t *ret_blob_size,
+ struct iovec *ret_secret,
+ struct iovec *ret_blob,
uint16_t *ret_primary_alg,
- void **ret_srk_buf,
- size_t *ret_srk_buf_size) {
+ struct iovec *ret_srk) {
uint16_t primary_alg = 0;
int r;
assert(ret_secret);
- assert(ret_secret_size);
assert(ret_blob);
- assert(ret_blob_size);
/* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
* is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
@@ -5255,13 +5408,22 @@ int tpm2_seal(Tpm2Context *c,
usec_t start = now(CLOCK_MONOTONIC);
+ TPMA_OBJECT hmac_attributes =
+ TPMA_OBJECT_FIXEDTPM |
+ TPMA_OBJECT_FIXEDPARENT;
+
+ /* If protected by PIN, a user-selected low-entropy password, enable DA protection.
+ Without a PIN, the key's left protected only by a PCR policy, which does not benefit
+ from DA protection. */
+ hmac_attributes |= pin ? 0 : TPMA_OBJECT_NODA;
+
/* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
* LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
* because it's a key type that is universally supported and suitable for symmetric binary blobs. */
TPMT_PUBLIC hmac_template = {
.type = TPM2_ALG_KEYEDHASH,
.nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
+ .objectAttributes = hmac_attributes,
.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
.unique.keyedHash.size = SHA256_DIGEST_SIZE,
.authPolicy = policy ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
@@ -5274,7 +5436,7 @@ int tpm2_seal(Tpm2Context *c,
CLEANUP_ERASE(hmac_sensitive);
if (pin) {
- r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
+ r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
if (r < 0)
return r;
}
@@ -5290,7 +5452,7 @@ int tpm2_seal(Tpm2Context *c,
return log_debug_errno(r, "Failed to generate secret key: %m");
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
- if (ret_srk_buf) {
+ if (ret_srk) {
_cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
if (IN_SET(seal_key_handle, 0, TPM2_SRK_HANDLE)) {
@@ -5328,7 +5490,7 @@ int tpm2_seal(Tpm2Context *c,
if (seal_key_handle != 0)
log_debug("Using primary alg sealing, but seal key handle also provided; ignoring seal key handle.");
- /* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
+ /* TODO: force all callers to provide ret_srk, so we can stop sealing with the legacy templates. */
primary_alg = TPM2_ALG_ECC;
TPM2B_PUBLIC template = {
@@ -5372,47 +5534,46 @@ int tpm2_seal(Tpm2Context *c,
if (r < 0)
return r;
- _cleanup_(erase_and_freep) void *secret = NULL;
- secret = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
- if (!secret)
+ _cleanup_(iovec_done_erase) struct iovec secret = {};
+ secret.iov_base = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
+ if (!secret.iov_base)
return log_oom_debug();
+ secret.iov_len = hmac_sensitive.data.size;
log_debug("Marshalling private and public part of HMAC key.");
- _cleanup_free_ void *blob = NULL;
- size_t blob_size = 0;
- r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blob, &blob_size);
+ _cleanup_(iovec_done) struct iovec blob = {};
+ r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blob.iov_base, &blob.iov_len);
if (r < 0)
return log_debug_errno(r, "Could not create sealed blob: %m");
if (DEBUG_LOGGING)
log_debug("Completed TPM2 key sealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
- _cleanup_free_ void *srk_buf = NULL;
- size_t srk_buf_size = 0;
- if (ret_srk_buf) {
+ if (ret_srk) {
+ _cleanup_(iovec_done) struct iovec srk = {};
_cleanup_(Esys_Freep) void *tmp = NULL;
- r = tpm2_serialize(c, primary_handle, &tmp, &srk_buf_size);
+ size_t tmp_size;
+
+ r = tpm2_serialize(c, primary_handle, &tmp, &tmp_size);
if (r < 0)
return r;
/*
* make a copy since we don't want the caller to understand that
* ESYS allocated the pointer. It would make tracking what deallocator
- * to use for srk_buf in which context a PITA.
+ * to use for srk in which context a PITA.
*/
- srk_buf = memdup(tmp, srk_buf_size);
- if (!srk_buf)
+ srk.iov_base = memdup(tmp, tmp_size);
+ if (!srk.iov_base)
return log_oom_debug();
+ srk.iov_len = tmp_size;
- *ret_srk_buf = TAKE_PTR(srk_buf);
- *ret_srk_buf_size = srk_buf_size;
+ *ret_srk = TAKE_STRUCT(srk);
}
- *ret_secret = TAKE_PTR(secret);
- *ret_secret_size = hmac_sensitive.data.size;
- *ret_blob = TAKE_PTR(blob);
- *ret_blob_size = blob_size;
+ *ret_secret = TAKE_STRUCT(secret);
+ *ret_blob = TAKE_STRUCT(blob);
if (ret_primary_alg)
*ret_primary_alg = primary_alg;
@@ -5425,31 +5586,24 @@ int tpm2_seal(Tpm2Context *c,
int tpm2_unseal(Tpm2Context *c,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
- const void *pubkey,
- size_t pubkey_size,
+ const struct iovec *pubkey,
uint32_t pubkey_pcr_mask,
JsonVariant *signature,
const char *pin,
const Tpm2PCRLockPolicy *pcrlock_policy,
uint16_t primary_alg,
- const void *blob,
- size_t blob_size,
- const void *known_policy_hash,
- size_t known_policy_hash_size,
- const void *srk_buf,
- size_t srk_buf_size,
- void **ret_secret,
- size_t *ret_secret_size) {
+ const struct iovec *blob,
+ const struct iovec *known_policy_hash,
+ const struct iovec *srk,
+ struct iovec *ret_secret) {
TSS2_RC rc;
int r;
- assert(blob);
- assert(blob_size > 0);
- assert(known_policy_hash_size == 0 || known_policy_hash);
- assert(pubkey_size == 0 || pubkey);
+ assert(iovec_is_set(blob));
+ assert(iovec_is_valid(known_policy_hash));
+ assert(iovec_is_valid(pubkey));
assert(ret_secret);
- assert(ret_secret_size);
assert(TPM2_PCR_MASK_VALID(hash_pcr_mask));
assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
@@ -5467,7 +5621,7 @@ int tpm2_unseal(Tpm2Context *c,
TPM2B_PUBLIC public;
TPM2B_PRIVATE private;
TPM2B_ENCRYPTED_SECRET seed = {};
- r = tpm2_unmarshal_blob(blob, blob_size, &public, &private, &seed);
+ r = tpm2_unmarshal_blob(blob->iov_base, blob->iov_len, &public, &private, &seed);
if (r < 0)
return log_debug_errno(r, "Could not extract parts from blob: %m");
@@ -5480,8 +5634,8 @@ int tpm2_unseal(Tpm2Context *c,
}
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
- if (srk_buf) {
- r = tpm2_deserialize(c, srk_buf, srk_buf_size, &primary_handle);
+ if (iovec_is_set(srk)) {
+ r = tpm2_deserialize(c, srk->iov_base, srk->iov_len, &primary_handle);
if (r < 0)
return r;
} else if (primary_alg != 0) {
@@ -5537,14 +5691,13 @@ int tpm2_unseal(Tpm2Context *c,
return r;
TPM2B_PUBLIC pubkey_tpm2b;
- _cleanup_free_ void *fp = NULL;
- size_t fp_size = 0;
- if (pubkey) {
- r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b);
+ _cleanup_(iovec_done) struct iovec fp = {};
+ if (iovec_is_set(pubkey)) {
+ r = tpm2_tpm2b_public_from_pem(pubkey->iov_base, pubkey->iov_len, &pubkey_tpm2b);
if (r < 0)
return log_debug_errno(r, "Could not create TPMT_PUBLIC: %m");
- r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp, &fp_size);
+ r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp.iov_base, &fp.iov_len);
if (r < 0)
return log_debug_errno(r, "Could not get key fingerprint: %m");
}
@@ -5582,8 +5735,8 @@ int tpm2_unseal(Tpm2Context *c,
policy_session,
hash_pcr_mask,
pcr_bank,
- pubkey ? &pubkey_tpm2b : NULL,
- fp, fp_size,
+ iovec_is_set(pubkey) ? &pubkey_tpm2b : NULL,
+ fp.iov_base, fp.iov_len,
pubkey_pcr_mask,
signature,
!!pin,
@@ -5594,11 +5747,12 @@ int tpm2_unseal(Tpm2Context *c,
/* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
* wait until the TPM2 tells us to go away. */
- if (known_policy_hash_size > 0 &&
- memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0) {
-
+ if (iovec_is_set(known_policy_hash) && memcmp_nn(policy_digest->buffer,
+ policy_digest->size,
+ known_policy_hash->iov_base,
+ known_policy_hash->iov_len) != 0) {
#if HAVE_OPENSSL
- if (pubkey_size > 0 &&
+ if (iovec_is_set(pubkey) &&
pubkey_tpm2b.publicArea.type == TPM2_ALG_RSA &&
pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent == TPM2_RSA_DEFAULT_EXPONENT) {
/* Due to bug #30546, if using RSA pubkey with the default exponent, we may
@@ -5630,23 +5784,24 @@ int tpm2_unseal(Tpm2Context *c,
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
}
- _cleanup_(erase_and_freep) char *secret = NULL;
- secret = memdup(unsealed->buffer, unsealed->size);
+ _cleanup_(iovec_done_erase) struct iovec secret = {};
+ secret.iov_base = memdup(unsealed->buffer, unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
- if (!secret)
+ if (!secret.iov_base)
return log_oom_debug();
+ secret.iov_len = unsealed->size;
if (DEBUG_LOGGING)
log_debug("Completed TPM2 key unsealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
- *ret_secret = TAKE_PTR(secret);
- *ret_secret_size = unsealed->size;
+ *ret_secret = TAKE_STRUCT(secret);
return 0;
}
static TPM2_HANDLE generate_random_nv_index(void) {
- return TPM2_NV_INDEX_FIRST + (TPM2_HANDLE) random_u64_range(TPM2_NV_INDEX_LAST - TPM2_NV_INDEX_FIRST + 1);
+ return TPM2_NV_INDEX_UNASSIGNED_FIRST +
+ (TPM2_HANDLE) random_u64_range(TPM2_NV_INDEX_UNASSIGNED_LAST - TPM2_NV_INDEX_UNASSIGNED_FIRST + 1);
}
int tpm2_define_policy_nv_index(
@@ -5654,8 +5809,6 @@ int tpm2_define_policy_nv_index(
const Tpm2Handle *session,
TPM2_HANDLE requested_nv_index,
const TPM2B_DIGEST *write_policy,
- const char *pin,
- const TPM2B_AUTH *auth,
TPM2_HANDLE *ret_nv_index,
Tpm2Handle **ret_nv_handle,
TPM2B_NV_PUBLIC *ret_nv_public) {
@@ -5665,7 +5818,10 @@ int tpm2_define_policy_nv_index(
int r;
assert(c);
- assert(pin || auth);
+
+ /* Allocates an nvindex to store a policy for use in PolicyAuthorizeNV in. This is where pcrlock then
+ * stores its predicted PCR policies in. If 'requested_nv_index' will try to allocate the specified
+ * nvindex, otherwise will find a free one, and use that. */
r = tpm2_handle_new(c, &new_handle);
if (r < 0)
@@ -5673,17 +5829,6 @@ int tpm2_define_policy_nv_index(
new_handle->flush = false; /* This is a persistent NV index, don't flush hence */
- TPM2B_AUTH _auth = {};
- CLEANUP_ERASE(_auth);
-
- if (!auth) {
- r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &_auth);
- if (r < 0)
- return r;
-
- auth = &_auth;
- }
-
for (unsigned try = 0; try < 25U; try++) {
TPM2_HANDLE nv_index;
@@ -5711,7 +5856,7 @@ int tpm2_define_policy_nv_index(
/* shandle1= */ session ? session->esys_handle : ESYS_TR_PASSWORD,
/* shandle2= */ ESYS_TR_NONE,
/* shandle3= */ ESYS_TR_NONE,
- auth,
+ /* auth= */ NULL,
&public_info,
&new_handle->esys_handle);
@@ -5937,10 +6082,7 @@ int tpm2_unseal_data(
"Failed to unseal data: %s", sym_Tss2_RC_Decode(rc));
_cleanup_(iovec_done) struct iovec d = {};
- d = (struct iovec) {
- .iov_base = memdup(unsealed->buffer, unsealed->size),
- .iov_len = unsealed->size,
- };
+ d = IOVEC_MAKE(memdup(unsealed->buffer, unsealed->size), unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
@@ -6011,7 +6153,7 @@ int tpm2_list_devices(void) {
}
}
- if (table_get_rows(t) <= 1) {
+ if (table_isempty(t)) {
log_info("No suitable TPM2 devices found.");
return 0;
}
@@ -6839,6 +6981,43 @@ int tpm2_pcrlock_search_file(const char *path, FILE **ret_file, char **ret_path)
return 0;
}
+int tpm2_pcrlock_policy_from_json(
+ JsonVariant *v,
+ Tpm2PCRLockPolicy *ret_policy) {
+
+ /* We use a type check of _JSON_VARIANT_TYPE_INVALID for the integer fields to allow
+ * json_dispatch_uint32() to parse strings as integers to work around the integer type weakness of
+ * JSON's design. */
+ JsonDispatch policy_dispatch[] = {
+ { "pcrBank", JSON_VARIANT_STRING, json_dispatch_tpm2_algorithm, offsetof(Tpm2PCRLockPolicy, algorithm), JSON_MANDATORY },
+ { "pcrValues", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(Tpm2PCRLockPolicy, prediction_json), JSON_MANDATORY },
+ { "nvIndex", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(Tpm2PCRLockPolicy, nv_index), JSON_MANDATORY },
+ { "nvHandle", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, nv_handle), JSON_MANDATORY },
+ { "nvPublic", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, nv_public), JSON_MANDATORY },
+ { "srkHandle", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, srk_handle), JSON_MANDATORY },
+ { "pinPublic", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, pin_public), JSON_MANDATORY },
+ { "pinPrivate", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, pin_private), JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy policy = {};
+ int r;
+
+ assert(v);
+ assert(ret_policy);
+
+ r = json_dispatch(v, policy_dispatch, JSON_LOG, &policy);
+ if (r < 0)
+ return r;
+
+ r = tpm2_pcr_prediction_from_json(&policy.prediction, policy.algorithm, policy.prediction_json);
+ if (r < 0)
+ return r;
+
+ *ret_policy = TAKE_STRUCT(policy);
+ return 1;
+}
+
int tpm2_pcrlock_policy_load(
const char *path,
Tpm2PCRLockPolicy *ret_policy) {
@@ -6855,41 +7034,141 @@ int tpm2_pcrlock_policy_load(
if (r < 0)
return log_error_errno(r, "Failed to load TPM2 pcrlock policy file: %m");
- _cleanup_(json_variant_unrefp) JsonVariant *configuration_json = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = json_parse_file(
f,
discovered_path,
/* flags = */ 0,
- &configuration_json,
+ &v,
/* ret_line= */ NULL,
/* ret_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse existing pcrlock policy file '%s': %m", discovered_path);
- JsonDispatch policy_dispatch[] = {
- { "pcrBank", JSON_VARIANT_STRING, json_dispatch_tpm2_algorithm, offsetof(Tpm2PCRLockPolicy, algorithm), JSON_MANDATORY },
- { "pcrValues", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(Tpm2PCRLockPolicy, prediction_json), JSON_MANDATORY },
- { "nvIndex", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(Tpm2PCRLockPolicy, nv_index), JSON_MANDATORY },
- { "nvHandle", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, nv_handle), JSON_MANDATORY },
- { "nvPublic", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, nv_public), JSON_MANDATORY },
- { "srkHandle", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, srk_handle), JSON_MANDATORY },
- { "pinPublic", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, pin_public), JSON_MANDATORY },
- { "pinPrivate", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(Tpm2PCRLockPolicy, pin_private), JSON_MANDATORY },
- {}
- };
+ return tpm2_pcrlock_policy_from_json(v, ret_policy);
+}
- _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy policy = {};
+static int pcrlock_policy_load_credential(
+ const char *name,
+ const struct iovec *data,
+ Tpm2PCRLockPolicy *ret) {
+
+ _cleanup_free_ char *c = NULL;
+ int r;
+
+ assert(name);
- r = json_dispatch(configuration_json, policy_dispatch, JSON_LOG, &policy);
+ c = strdup(name);
+ if (!c)
+ return log_oom();
+
+ ascii_strlower(c); /* Lowercase, to match what we did at encryption time */
+
+ _cleanup_(iovec_done) struct iovec decoded = {};
+ r = decrypt_credential_and_warn(
+ c,
+ now(CLOCK_REALTIME),
+ /* tpm2_device= */ NULL,
+ /* tpm2_signature_path= */ NULL,
+ UID_INVALID,
+ data,
+ CREDENTIAL_ALLOW_NULL,
+ &decoded);
if (r < 0)
return r;
- r = tpm2_pcr_prediction_from_json(&policy.prediction, policy.algorithm, policy.prediction_json);
+ if (memchr(decoded.iov_base, 0, decoded.iov_len))
+ return log_error_errno(r, "Credential '%s' contains embedded NUL byte, refusing.", name);
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ r = json_parse(decoded.iov_base,
+ /* flags= */ 0,
+ &v,
+ /* ret_line= */ NULL,
+ /* ret_column= */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse pcrlock policy: %m");
+
+ r = tpm2_pcrlock_policy_from_json(v, ret);
if (r < 0)
return r;
- *ret_policy = TAKE_STRUCT(policy);
- return 1;
+ return 0;
+}
+
+int tpm2_pcrlock_policy_from_credentials(
+ const struct iovec *srk,
+ const struct iovec *nv,
+ Tpm2PCRLockPolicy *ret) {
+
+ _cleanup_close_ int dfd = -EBADF;
+ int r;
+
+ /* During boot we'll not have access to the pcrlock.json file in /var/. In order to support
+ * pcrlock-bound root file systems we'll store a copy of the JSON data, wrapped in an (plaintext)
+ * credential in the ESP or XBOOTLDR partition. There might be multiple of those however (because of
+ * multi-boot), hence we use the SRK and NV data from the LUKS2 header as search key, and parse all
+ * such JSON policies until we find a matching one. */
+
+ const char *cp = secure_getenv("SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY") ?: ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY;
+
+ dfd = open(cp, O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0) {
+ if (errno == ENOENT) {
+ log_debug("No encrypted system credentials passed.");
+ return 0;
+ }
+
+ return log_error_errno(errno, "Failed to open system credentials directory.");
+ }
+
+ _cleanup_free_ DirectoryEntries *de = NULL;
+ r = readdir_all(dfd, RECURSE_DIR_IGNORE_DOT, &de);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate system credentials: %m");
+
+ FOREACH_ARRAY(i, de->entries, de->n_entries) {
+ _cleanup_(iovec_done) struct iovec data = {};
+ struct dirent *d = *i;
+
+ if (!startswith_no_case(d->d_name, "pcrlock.")) /* VFAT is case-insensitive, hence don't be too strict here */
+ continue;
+
+ r = read_full_file_full(
+ dfd, d->d_name,
+ /* offset= */ UINT64_MAX,
+ /* size= */ CREDENTIAL_ENCRYPTED_SIZE_MAX,
+ READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER,
+ /* bind_name= */ NULL,
+ (char**) &data.iov_base,
+ &data.iov_len);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read credentials file %s/%s, skipping: %m", ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, d->d_name);
+ continue;
+ }
+
+ _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy loaded_policy = {};
+ r = pcrlock_policy_load_credential(
+ d->d_name,
+ &data,
+ &loaded_policy);
+ if (r < 0) {
+ log_warning_errno(r, "Loading of pcrlock policy from credential '%s/%s' failed, skipping.", ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, d->d_name);
+ continue;
+ }
+
+ if ((!srk || iovec_memcmp(srk, &loaded_policy.srk_handle) == 0) &&
+ (!nv || iovec_memcmp(nv, &loaded_policy.nv_handle) == 0)) {
+ *ret = TAKE_STRUCT(loaded_policy);
+ return 1;
+ }
+ }
+
+ log_info("No pcrlock policy found among system credentials.");
+ *ret = (Tpm2PCRLockPolicy) {};
+ return 0;
}
int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret) {
@@ -6929,6 +7208,75 @@ int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret) {
*ret = device_key_public;
return 0;
}
+
+int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_AUTH *pin, Tpm2Handle **ret) {
+ int r;
+
+ assert(c);
+ assert(pin);
+ assert(ret);
+
+ log_debug("Converting PIN into TPM2 HMAC-SHA256 object.");
+
+ /* Load the PIN (which we have stored in the "auth" TPM2B_AUTH) into the TPM as an HMAC key so that
+ * we can use it in a TPM2_PolicySigned() to write to the nvindex. For that we'll prep a pair of
+ * TPM2B_PUBLIC and TPM2B_SENSITIVE that defines an HMAC-SHA256 keyed hash function, and initialize
+ * it based on the provided PIN data. */
+
+ TPM2B_PUBLIC auth_hmac_public = {
+ .publicArea = {
+ .type = TPM2_ALG_KEYEDHASH,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_SIGN_ENCRYPT,
+ .parameters.keyedHashDetail.scheme = {
+ .scheme = TPM2_ALG_HMAC,
+ .details.hmac.hashAlg = TPM2_ALG_SHA256,
+ },
+ .unique.keyedHash.size = SHA256_DIGEST_SIZE,
+ },
+ };
+
+ TPM2B_SENSITIVE auth_hmac_private = {
+ .sensitiveArea = {
+ .sensitiveType = TPM2_ALG_KEYEDHASH,
+ .sensitive.bits.size = pin->size,
+ .seedValue.size = SHA256_DIGEST_SIZE,
+ },
+ };
+
+ /* Copy in the key data */
+ memcpy_safe(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, pin->buffer, pin->size);
+
+ /* NB: We initialize the seed of the TPMT_SENSITIVE structure to all zeroes, since we want a stable
+ * "name" of the PIN object */
+
+ /* Now calculate the "unique" field for the public area, based on the sensitive data, according to
+ * the algorithm in the TPM2 spec, part 1, Section 27.5.3.2 */
+ struct iovec sensitive_data[] = {
+ IOVEC_MAKE(auth_hmac_private.sensitiveArea.seedValue.buffer, auth_hmac_private.sensitiveArea.seedValue.size),
+ IOVEC_MAKE(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, auth_hmac_private.sensitiveArea.sensitive.bits.size),
+ };
+ r = tpm2_digest_many(
+ auth_hmac_public.publicArea.nameAlg,
+ &auth_hmac_public.publicArea.unique.keyedHash,
+ sensitive_data,
+ ELEMENTSOF(sensitive_data),
+ /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ /* And now load the public/private parts into the TPM and get a handle back */
+ r = tpm2_load_external(
+ c,
+ session,
+ &auth_hmac_public,
+ &auth_hmac_private,
+ ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load PIN into TPM2: %m");
+
+ return 0;
+}
#endif
char *tpm2_pcr_mask_to_string(uint32_t mask) {
@@ -7002,18 +7350,14 @@ int tpm2_make_luks2_json(
int keyslot,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
- const void *pubkey,
- size_t pubkey_size,
+ const struct iovec *pubkey,
uint32_t pubkey_pcr_mask,
uint16_t primary_alg,
- const void *blob,
- size_t blob_size,
- const void *policy_hash,
- size_t policy_hash_size,
- const void *salt,
- size_t salt_size,
- const void *srk_buf,
- size_t srk_buf_size,
+ const struct iovec *blob,
+ const struct iovec *policy_hash,
+ const struct iovec *salt,
+ const struct iovec *srk,
+ const struct iovec *pcrlock_nv,
TPM2Flags flags,
JsonVariant **ret) {
@@ -7021,9 +7365,9 @@ int tpm2_make_luks2_json(
_cleanup_free_ char *keyslot_as_string = NULL;
int r;
- assert(blob || blob_size == 0);
- assert(policy_hash || policy_hash_size == 0);
- assert(pubkey || pubkey_size == 0);
+ assert(iovec_is_valid(pubkey));
+ assert(iovec_is_valid(blob));
+ assert(iovec_is_valid(policy_hash));
if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
return -ENOMEM;
@@ -7046,17 +7390,18 @@ int tpm2_make_luks2_json(
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-tpm2")),
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
- JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
+ JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_IOVEC_BASE64(blob)),
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(hmj)),
- JSON_BUILD_PAIR_CONDITION(!!tpm2_hash_alg_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_hash_alg_to_string(pcr_bank))),
- JSON_BUILD_PAIR_CONDITION(!!tpm2_asym_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_asym_alg_to_string(primary_alg))),
- JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
- JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)),
- JSON_BUILD_PAIR("tpm2_pcrlock", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PCRLOCK)),
+ JSON_BUILD_PAIR_CONDITION(pcr_bank != 0 && tpm2_hash_alg_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_hash_alg_to_string(pcr_bank))),
+ JSON_BUILD_PAIR_CONDITION(primary_alg != 0 && tpm2_asym_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_asym_alg_to_string(primary_alg))),
+ JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_IOVEC_HEX(policy_hash)),
+ JSON_BUILD_PAIR_CONDITION(FLAGS_SET(flags, TPM2_FLAGS_USE_PIN), "tpm2-pin", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR_CONDITION(FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK), "tpm2_pcrlock", JSON_BUILD_BOOLEAN(true)),
JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", JSON_BUILD_VARIANT(pkmj)),
- JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size)),
- JSON_BUILD_PAIR_CONDITION(salt, "tpm2_salt", JSON_BUILD_BASE64(salt, salt_size)),
- JSON_BUILD_PAIR_CONDITION(srk_buf, "tpm2_srk", JSON_BUILD_BASE64(srk_buf, srk_buf_size))));
+ JSON_BUILD_PAIR_CONDITION(iovec_is_set(pubkey), "tpm2_pubkey", JSON_BUILD_IOVEC_BASE64(pubkey)),
+ JSON_BUILD_PAIR_CONDITION(iovec_is_set(salt), "tpm2_salt", JSON_BUILD_IOVEC_BASE64(salt)),
+ JSON_BUILD_PAIR_CONDITION(iovec_is_set(srk), "tpm2_srk", JSON_BUILD_IOVEC_BASE64(srk)),
+ JSON_BUILD_PAIR_CONDITION(iovec_is_set(pcrlock_nv), "tpm2_pcrlock_nv", JSON_BUILD_IOVEC_BASE64(pcrlock_nv))));
if (r < 0)
return r;
@@ -7071,22 +7416,17 @@ int tpm2_parse_luks2_json(
int *ret_keyslot,
uint32_t *ret_hash_pcr_mask,
uint16_t *ret_pcr_bank,
- void **ret_pubkey,
- size_t *ret_pubkey_size,
+ struct iovec *ret_pubkey,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
- void **ret_blob,
- size_t *ret_blob_size,
- void **ret_policy_hash,
- size_t *ret_policy_hash_size,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_srk_buf,
- size_t *ret_srk_buf_size,
+ struct iovec *ret_blob,
+ struct iovec *ret_policy_hash,
+ struct iovec *ret_salt,
+ struct iovec *ret_srk,
+ struct iovec *ret_pcrlock_nv,
TPM2Flags *ret_flags) {
- _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL, *srk_buf = NULL;
- size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0, salt_size = 0, srk_buf_size = 0;
+ _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
@@ -7151,7 +7491,7 @@ int tpm2_parse_luks2_json(
if (!w)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-blob' field.");
- r = json_variant_unbase64(w, &blob, &blob_size);
+ r = json_variant_unbase64_iovec(w, &blob);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2-blob' field.");
@@ -7159,7 +7499,7 @@ int tpm2_parse_luks2_json(
if (!w)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-policy-hash' field.");
- r = json_variant_unhex(w, &policy_hash, &policy_hash_size);
+ r = json_variant_unhex_iovec(w, &policy_hash);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2-policy-hash' field.");
@@ -7181,7 +7521,7 @@ int tpm2_parse_luks2_json(
w = json_variant_by_key(v, "tpm2_salt");
if (w) {
- r = json_variant_unbase64(w, &salt, &salt_size);
+ r = json_variant_unbase64_iovec(w, &salt);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2_salt' field.");
}
@@ -7195,7 +7535,7 @@ int tpm2_parse_luks2_json(
w = json_variant_by_key(v, "tpm2_pubkey");
if (w) {
- r = json_variant_unbase64(w, &pubkey, &pubkey_size);
+ r = json_variant_unbase64_iovec(w, &pubkey);
if (r < 0)
return log_debug_errno(r, "Failed to decode PCR public key.");
} else if (pubkey_pcr_mask != 0)
@@ -7203,11 +7543,18 @@ int tpm2_parse_luks2_json(
w = json_variant_by_key(v, "tpm2_srk");
if (w) {
- r = json_variant_unbase64(w, &srk_buf, &srk_buf_size);
+ r = json_variant_unbase64_iovec(w, &srk);
if (r < 0)
return log_debug_errno(r, "Invalid base64 data in 'tpm2_srk' field.");
}
+ w = json_variant_by_key(v, "tpm2_pcrlock_nv");
+ if (w) {
+ r = json_variant_unbase64_iovec(w, &pcrlock_nv);
+ if (r < 0)
+ return log_debug_errno(r, "Invalid base64 data in 'tpm2_pcrlock_nv' field.");
+ }
+
if (ret_keyslot)
*ret_keyslot = keyslot;
if (ret_hash_pcr_mask)
@@ -7215,32 +7562,23 @@ int tpm2_parse_luks2_json(
if (ret_pcr_bank)
*ret_pcr_bank = pcr_bank;
if (ret_pubkey)
- *ret_pubkey = TAKE_PTR(pubkey);
- if (ret_pubkey_size)
- *ret_pubkey_size = pubkey_size;
+ *ret_pubkey = TAKE_STRUCT(pubkey);
if (ret_pubkey_pcr_mask)
*ret_pubkey_pcr_mask = pubkey_pcr_mask;
if (ret_primary_alg)
*ret_primary_alg = primary_alg;
if (ret_blob)
- *ret_blob = TAKE_PTR(blob);
- if (ret_blob_size)
- *ret_blob_size = blob_size;
+ *ret_blob = TAKE_STRUCT(blob);
if (ret_policy_hash)
- *ret_policy_hash = TAKE_PTR(policy_hash);
- if (ret_policy_hash_size)
- *ret_policy_hash_size = policy_hash_size;
+ *ret_policy_hash = TAKE_STRUCT(policy_hash);
if (ret_salt)
- *ret_salt = TAKE_PTR(salt);
- if (ret_salt_size)
- *ret_salt_size = salt_size;
+ *ret_salt = TAKE_STRUCT(salt);
+ if (ret_srk)
+ *ret_srk = TAKE_STRUCT(srk);
+ if (ret_pcrlock_nv)
+ *ret_pcrlock_nv = TAKE_STRUCT(pcrlock_nv);
if (ret_flags)
*ret_flags = flags;
- if (ret_srk_buf)
- *ret_srk_buf = TAKE_PTR(srk_buf);
- if (ret_srk_buf_size)
- *ret_srk_buf_size = srk_buf_size;
-
return 0;
}
@@ -7553,7 +7891,7 @@ int tpm2_load_pcr_signature(const char *path, JsonVariant **ret) {
/* Tries to load a JSON PCR signature file. Takes an absolute path, a simple file name or NULL. In
* the latter two cases searches in /etc/, /usr/lib/, /run/, as usual. */
- search = strv_split_nulstr(CONF_PATHS_NULSTR("systemd"));
+ search = strv_new(CONF_PATHS("systemd"));
if (!search)
return log_oom_debug();
@@ -7618,7 +7956,7 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
size_t saltlen,
uint8_t ret_key[static SHA256_DIGEST_SIZE]) {
- uint8_t _cleanup_(erase_and_freep) *buffer = NULL;
+ _cleanup_(erase_and_freep) uint8_t *buffer = NULL;
uint8_t u[SHA256_DIGEST_SIZE];
/* To keep this simple, since derived KeyLen (dkLen in docs)