_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
_cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
_cleanup_fclose_ FILE *privkeyf = NULL;
- ESYS_TR session_handle = ESYS_TR_NONE;
TSS2_RC rc;
size_t n;
int r;
if (r < 0)
return r;
- r = tpm2_context_init(arg_tpm2_device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
};
PcrState *p = pcr_states + i;
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ r = tpm2_handle_new(c, &session);
+ if (r < 0)
+ return r;
+
rc = sym_Esys_StartAuthSession(
- c.esys_context,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
TPM2_SE_TRIAL,
&symmetric,
TPM2_ALG_SHA256,
- &session_handle);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ &session->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
/* Generate a single hash value from the PCRs included in our policy. Given that that's
* exactly one, the calculation is trivial. */
sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
int tpmalg = tpm2_hash_alg_from_string(EVP_MD_name(p->md));
- if (tpmalg < 0) {
- log_error_errno(tpmalg, "Unsupported PCR bank");
- goto finish;
- }
+ if (tpmalg < 0)
+ return log_error_errno(tpmalg, "Unsupported PCR bank");
TPML_PCR_SELECTION pcr_selection;
tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c.esys_context,
- session_handle,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&intermediate_digest,
&pcr_selection);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
_cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
rc = sym_Esys_PolicyGetDigest(
- c.esys_context,
- session_handle,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&pcr_policy_digest);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
-
- session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
mdctx = EVP_MD_CTX_new();
- if (!mdctx) {
- r = log_oom();
- goto finish;
- }
+ if (!mdctx)
+ return log_oom();
- if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to initialize signature context.");
- goto finish;
- }
+ if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to initialize signature context.");
- if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to sign data.");
- goto finish;
- }
+ if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to sign data.");
size_t ss;
- if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to finalize signature");
- goto finish;
- }
+ if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to finalize signature");
_cleanup_free_ void *sig = malloc(ss);
- if (!sig) {
- r = log_oom();
- goto finish;
- }
+ if (!sig)
+ return log_oom();
- if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to acquire signature data");
- goto finish;
- }
+ if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to acquire signature data");
_cleanup_free_ void *pubkey_fp = NULL;
size_t pubkey_fp_size = 0;
r = pubkey_fingerprint(pubkey, EVP_sha256(), &pubkey_fp, &pubkey_fp_size);
if (r < 0)
- goto finish;
+ return r;
_cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
r = tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &a);
- if (r < 0) {
- log_error_errno(r, "Failed to build JSON PCR mask array: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON PCR mask array: %m");
_cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
r = json_build(&bv, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp, pubkey_fp_size)), /* SHA256 fingerprint of public key (DER) used for the signature */
JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest->buffer, pcr_policy_digest->size)), /* TPM2 policy hash that is signed */
JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig, ss)))); /* signature data */
- if (r < 0) {
- log_error_errno(r, "Failed to build JSON object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON object: %m");
_cleanup_(json_variant_unrefp) JsonVariant *av = NULL;
av = json_variant_ref(json_variant_by_key(v, p->bank));
r = json_variant_append_array_nodup(&av, bv);
- if (r < 0) {
- log_error_errno(r, "Failed to append JSON object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to append JSON object: %m");
r = json_variant_set_field(&v, p->bank, av);
- if (r < 0) {
- log_error_errno(r, "Failed to add JSON field: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to add JSON field: %m");
}
/* Return to the original kernel measurement for the next phase calculation */
pager_open(arg_pager_flags);
json_variant_dump(v, arg_json_format_flags, stdout, NULL);
- r = 0;
-finish:
- session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
- return r;
+ return 0;
}
static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
}
-void tpm2_context_destroy(struct tpm2_context *c) {
- assert(c);
+static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
+ if (!c)
+ return NULL;
if (c->esys_context)
sym_Esys_Finalize(&c->esys_context);
c->tcti_context = mfree(c->tcti_context);
c->tcti_dl = safe_dlclose(c->tcti_dl);
-}
-static inline void Esys_Finalize_wrapper(ESYS_CONTEXT **c) {
- /* A wrapper around Esys_Finalize() for use with _cleanup_(). Only reasons we need this wrapper is
- * because the function itself warn logs if we'd pass a pointer to NULL, and we don't want that. */
- if (*c)
- sym_Esys_Finalize(c);
+ return mfree(c);
}
-ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
- TSS2_RC rc;
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
- if (!c || handle == ESYS_TR_NONE)
- return ESYS_TR_NONE;
+int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
+ _cleanup_tpm2_context_ Tpm2Context *context = NULL;
+ TSS2_RC rc;
+ int r;
- rc = sym_Esys_FlushContext(c, handle);
- if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
- * in error paths, where we cannot do anything about failures anymore. And
- * when it is called in successful codepaths by this time we already did
- * what we wanted to do, and got the results we wanted so there's no
- * reason to make this fail more loudly than necessary. */
- log_debug("Failed to get flush context of TPM, ignoring: %s", sym_Tss2_RC_Decode(rc));
+ assert(ret_context);
- return ESYS_TR_NONE;
-}
+ context = new0(Tpm2Context, 1);
+ if (!context)
+ return log_oom();
-int tpm2_context_init(const char *device, struct tpm2_context *ret) {
- _cleanup_(Esys_Finalize_wrapper) ESYS_CONTEXT *c = NULL;
- _cleanup_free_ TSS2_TCTI_CONTEXT *tcti = NULL;
- _cleanup_(dlclosep) void *dl = NULL;
- TSS2_RC rc;
- int r;
+ context->n_ref = 1;
r = dlopen_tpm2();
if (r < 0)
if (!filename_is_valid(fn))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name '%s' not valid, refusing.", driver);
- dl = dlopen(fn, RTLD_NOW);
- if (!dl)
+ context->tcti_dl = dlopen(fn, RTLD_NOW);
+ if (!context->tcti_dl)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
- func = dlsym(dl, TSS2_TCTI_INFO_SYMBOL);
+ func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to find TCTI info symbol " TSS2_TCTI_INFO_SYMBOL ": %s",
if (!info)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to get TCTI info data.");
-
log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
rc = info->init(NULL, &sz, NULL);
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
- tcti = malloc0(sz);
- if (!tcti)
+ context->tcti_context = malloc0(sz);
+ if (!context->tcti_context)
return log_oom();
- rc = info->init(tcti, &sz, param);
+ rc = info->init(context->tcti_context, &sz, param);
if (rc != TPM2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
}
- rc = sym_Esys_Initialize(&c, tcti, NULL);
+ rc = sym_Esys_Initialize(&context->esys_context, context->tcti_context, NULL);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TPM context: %s", sym_Tss2_RC_Decode(rc));
- rc = sym_Esys_Startup(c, TPM2_SU_CLEAR);
+ rc = sym_Esys_Startup(context->esys_context, TPM2_SU_CLEAR);
if (rc == TPM2_RC_INITIALIZE)
log_debug("TPM already started up.");
else if (rc == TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
- *ret = (struct tpm2_context) {
- .esys_context = TAKE_PTR(c),
- .tcti_context = TAKE_PTR(tcti),
- .tcti_dl = TAKE_PTR(dl),
- };
+ *ret_context = TAKE_PTR(context);
+
+ return 0;
+}
+
+static void tpm2_handle_flush(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle) {
+ if (!esys_context || esys_handle == ESYS_TR_NONE)
+ return;
+
+ TSS2_RC rc = sym_Esys_FlushContext(esys_context, esys_handle);
+ if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
+ * in error paths, where we cannot do anything about failures anymore. And
+ * when it is called in successful codepaths by this time we already did
+ * what we wanted to do, and got the results we wanted so there's no
+ * reason to make this fail more loudly than necessary. */
+ log_debug("Failed to flush TPM handle, ignoring: %s", sym_Tss2_RC_Decode(rc));
+}
+
+Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
+ if (!handle)
+ return NULL;
+
+ _cleanup_tpm2_context_ Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
+ if (context)
+ tpm2_handle_flush(context->esys_context, handle->esys_handle);
+
+ return mfree(handle);
+}
+
+int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
+ _cleanup_tpm2_handle_ Tpm2Handle *handle = NULL;
+
+ assert(ret_handle);
+
+ handle = new0(Tpm2Handle, 1);
+ if (!handle)
+ return log_oom();
+
+ handle->tpm2_context = tpm2_context_ref(context);
+ handle->esys_handle = ESYS_TR_NONE;
+
+ *ret_handle = TAKE_PTR(handle);
return 0;
}
#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
-static int tpm2_credit_random(ESYS_CONTEXT *c) {
+static int tpm2_credit_random(Tpm2Context *c) {
size_t rps, done = 0;
TSS2_RC rc;
usec_t t;
_cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
rc = sym_Esys_GetRandom(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
}
static int tpm2_make_primary(
- ESYS_CONTEXT *c,
- ESYS_TR *ret_primary,
+ Tpm2Context *c,
+ Tpm2Handle **ret_primary,
TPMI_ALG_PUBLIC alg,
TPMI_ALG_PUBLIC *ret_alg) {
};
static const TPML_PCR_SELECTION creation_pcr = {};
- ESYS_TR primary = ESYS_TR_NONE;
TSS2_RC rc;
usec_t ts;
+ int r;
log_debug("Creating primary key on TPM.");
ts = now(CLOCK_MONOTONIC);
+ _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ r = tpm2_handle_new(c, &primary);
+ if (r < 0)
+ return r;
+
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
rc = sym_Esys_CreatePrimary(
- c,
+ c->esys_context,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
&primary_template_ecc,
NULL,
&creation_pcr,
- &primary,
+ &primary->esys_handle,
NULL,
NULL,
NULL,
if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
rc = sym_Esys_CreatePrimary(
- c,
+ c->esys_context,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
&primary_template_rsa,
NULL,
&creation_pcr,
- &primary,
+ &primary->esys_handle,
NULL,
NULL,
NULL,
log_debug("Generating primary key on TPM2 took %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
- *ret_primary = primary;
+ if (ret_primary)
+ *ret_primary = TAKE_PTR(primary);
if (ret_alg)
*ret_alg = alg;
}
static int tpm2_pcr_mask_good(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
TPMI_ALG_HASH bank,
uint32_t mask) {
tpm2_pcr_mask_to_selection(mask, bank, &selection);
rc = sym_Esys_PCR_Read(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
}
static int tpm2_get_best_pcr_bank(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
assert(c);
rc = sym_Esys_GetCapability(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
}
int tpm2_get_good_pcr_banks(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
TPMI_ALG_HASH **ret) {
assert(ret);
rc = sym_Esys_GetCapability(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
}
int tpm2_get_good_pcr_banks_strv(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
char ***ret) {
}
static int tpm2_make_encryption_session(
- ESYS_CONTEXT *c,
- ESYS_TR primary,
- ESYS_TR bind_key,
+ Tpm2Context *c,
+ const Tpm2Handle *primary,
+ const Tpm2Handle *bind_key,
const char *pin,
- ESYS_TR *ret_session) {
+ Tpm2Handle **ret_session) {
static const TPMT_SYM_DEF symmetric = {
.algorithm = TPM2_ALG_AES,
};
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
- ESYS_TR session = ESYS_TR_NONE;
TSS2_RC rc;
+ int r;
assert(c);
+ assert(ret_session);
/*
* if a pin is set for the seal object, use it to bind the session
hash_pin(pin, strlen(pin), &auth);
- rc = sym_Esys_TR_SetAuth(c, bind_key, &auth);
+ rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key->esys_handle, &auth);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
* means that the random salt will be encrypted with the well-known key. That way, only the TPM can
* recover the salt, which is then used for key derivation. */
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ r = tpm2_handle_new(c, &session);
+ if (r < 0)
+ return r;
+
rc = sym_Esys_StartAuthSession(
- c,
- primary,
- bind_key,
+ c->esys_context,
+ primary->esys_handle,
+ bind_key->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
TPM2_SE_HMAC,
&symmetric,
TPM2_ALG_SHA256,
- &session);
+ &session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
/* Enable parameter encryption/decryption with AES in CFB mode. Together with HMAC digests (which are
* always used for sessions), this provides confidentiality, integrity and replay protection for
* operations that use this session. */
- rc = sym_Esys_TRSess_SetAttributes(c, session, sessionAttributes, 0xff);
+ rc = sym_Esys_TRSess_SetAttributes(c->esys_context, session->esys_handle, sessionAttributes, 0xff);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to configure TPM session: %s",
sym_Tss2_RC_Decode(rc));
- if (ret_session) {
- *ret_session = session;
- session = ESYS_TR_NONE;
- }
+ *ret_session = TAKE_PTR(session);
- session = tpm2_flush_context_verbose(c, session);
return 0;
}
#endif
static int tpm2_make_policy_session(
- ESYS_CONTEXT *c,
- ESYS_TR primary,
- ESYS_TR parent_session,
+ Tpm2Context *c,
+ const Tpm2Handle *primary,
+ const Tpm2Handle *parent_session,
TPM2_SE session_type,
uint32_t hash_pcr_mask,
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
uint32_t pubkey_pcr_mask,
JsonVariant *signature_json,
bool use_pin,
- ESYS_TR *ret_session,
+ Tpm2Handle **ret_session,
TPM2B_DIGEST **ret_policy_digest,
TPMI_ALG_HASH *ret_pcr_bank) {
.mode.aes = TPM2_ALG_CFB,
};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
- ESYS_TR session = ESYS_TR_NONE, pubkey_handle = ESYS_TR_NONE;
TSS2_RC rc;
int r;
}
#endif
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ r = tpm2_handle_new(c, &session);
+ if (r < 0)
+ return r;
+
rc = sym_Esys_StartAuthSession(
- c,
- primary,
+ c->esys_context,
+ primary->esys_handle,
ESYS_TR_NONE,
- parent_session,
+ parent_session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
session_type,
&symmetric,
TPM2_ALG_SHA256,
- &session);
+ &session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
TPM2B_PUBLIC pubkey_tpm2;
r = openssl_pubkey_to_tpm2_pubkey(pk, &pubkey_tpm2);
if (r < 0)
- goto finish;
+ return r;
+
+ _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+ r = tpm2_handle_new(c, &pubkey_handle);
+ if (r < 0)
+ return r;
rc = sym_Esys_LoadExternal(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
#else
TPM2_RH_OWNER,
#endif
- &pubkey_handle);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ &pubkey_handle->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
/* Acquire the "name" of what we just loaded */
_cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
rc = sym_Esys_TR_GetName(
- c,
- pubkey_handle,
+ c->esys_context,
+ pubkey_handle->esys_handle,
&pubkey_name);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
/* Put together the PCR policy we want to use */
TPML_PCR_SELECTION pcr_selection;
tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
&pcr_selection);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
/* Get the policy hash of the PCR policy */
_cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
rc = sym_Esys_PolicyGetDigest(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&approved_policy);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
/* When we are unlocking and have a signature, let's pass it to the TPM */
_cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
&signature_raw,
&signature_size);
if (r < 0)
- goto finish;
+ return r;
/* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
* hence we need to do the SHA256 part ourselves, first */
.sig.size = signature_size,
},
};
- if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer)) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
- goto finish;
- }
+ if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
rc = sym_Esys_VerifySignature(
- c,
- pubkey_handle,
+ c->esys_context,
+ pubkey_handle->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&signature_hash,
&policy_signature,
&check_ticket_buffer);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
check_ticket = check_ticket_buffer;
} else {
}
rc = sym_Esys_PolicyAuthorize(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
/* policyRef= */ &(const TPM2B_NONCE) {},
pubkey_name,
check_ticket);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
#else
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
#endif
TPML_PCR_SELECTION pcr_selection;
tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
&pcr_selection);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
}
if (use_pin) {
log_debug("Configuring PIN policy.");
rc = sym_Esys_PolicyAuthValue(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add authValue policy to TPM: %s",
- sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add authValue policy to TPM: %s",
+ sym_Tss2_RC_Decode(rc));
}
if (DEBUG_LOGGING || ret_policy_digest) {
log_debug("Acquiring policy digest.");
rc = sym_Esys_PolicyGetDigest(
- c,
- session,
+ c->esys_context,
+ session->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&policy_digest);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
if (DEBUG_LOGGING) {
_cleanup_free_ char *h = NULL;
h = hexmem(policy_digest->buffer, policy_digest->size);
- if (!h) {
- r = log_oom();
- goto finish;
- }
+ if (!h)
+ return log_oom();
log_debug("Session policy digest: %s", h);
}
}
- if (ret_session) {
- *ret_session = session;
- session = ESYS_TR_NONE;
- }
+ if (ret_session)
+ *ret_session = TAKE_PTR(session);
if (ret_policy_digest)
*ret_policy_digest = TAKE_PTR(policy_digest);
if (ret_pcr_bank)
*ret_pcr_bank = pcr_bank;
- r = 0;
-
-finish:
- session = tpm2_flush_context_verbose(c, session);
- pubkey_handle = tpm2_flush_context_verbose(c, pubkey_handle);
- return r;
+ return 0;
}
int tpm2_seal(const char *device,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg) {
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
static const TPML_PCR_SELECTION creation_pcr = {};
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
- ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
CLEANUP_ERASE(hmac_sensitive);
- r = tpm2_context_init(device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(device, &c);
if (r < 0)
return r;
- r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg);
+ _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ r = tpm2_make_primary(c, &primary, 0, &primary_alg);
if (r < 0)
return r;
/* we cannot use the bind key before its created */
- r = tpm2_make_encryption_session(c.esys_context, primary, ESYS_TR_NONE, NULL, &session);
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &session);
if (r < 0)
- goto finish;
+ return r;
r = tpm2_make_policy_session(
- c.esys_context,
+ c,
primary,
session,
TPM2_SE_TRIAL,
&policy_digest,
&pcr_bank);
if (r < 0)
- goto finish;
+ return r;
/* 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
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
- (void) tpm2_credit_random(c.esys_context);
+ (void) tpm2_credit_random(c);
log_debug("Generating secret key data.");
r = crypto_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
- if (r < 0) {
- log_error_errno(r, "Failed to generate secret key: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate secret key: %m");
log_debug("Creating HMAC key.");
rc = sym_Esys_Create(
- c.esys_context,
- primary,
- session, /* use HMAC session to enable parameter encryption */
+ c->esys_context,
+ primary->esys_handle,
+ session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
ESYS_TR_NONE,
&hmac_sensitive,
NULL,
NULL,
NULL);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
- if (!secret) {
- r = log_oom();
- goto finish;
- }
+ if (!secret)
+ return log_oom();
log_debug("Marshalling private and public part of HMAC key.");
size_t offset = 0;
buf = malloc(k);
- if (!buf) {
- r = log_oom();
- goto finish;
- }
+ if (!buf)
+ return log_oom();
rc = sym_Tss2_MU_TPM2B_PRIVATE_Marshal(private, buf, k, &offset);
if (rc == TSS2_RC_SUCCESS) {
break;
}
}
- if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
+ if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
- if (k > SIZE_MAX / 2) {
- r = log_oom();
- goto finish;
- }
+ if (k > SIZE_MAX / 2)
+ return log_oom();
k *= 2;
}
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;
- r = 0;
-
-finish:
- primary = tpm2_flush_context_verbose(c.esys_context, primary);
- session = tpm2_flush_context_verbose(c.esys_context, session);
- return r;
+ return 0;
}
#define RETRY_UNSEAL_MAX 30u
void **ret_secret,
size_t *ret_secret_size) {
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
- ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE,
- hmac_key = ESYS_TR_NONE;
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(erase_and_freep) char *secret = NULL;
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
- r = tpm2_context_init(device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(device, &c);
if (r < 0)
return r;
- r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
+ _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ r = tpm2_make_primary(c, &primary, primary_alg, NULL);
if (r < 0)
return r;
* is provided. If an attacker gives back a bad key, we already lost since
* primary key is not verified and they could attack there as well.
*/
+ _cleanup_tpm2_handle_ Tpm2Handle *hmac_key = NULL;
+ r = tpm2_handle_new(c, &hmac_key);
+ if (r < 0)
+ return r;
+
rc = sym_Esys_Load(
- c.esys_context,
- primary,
+ c->esys_context,
+ primary->esys_handle,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&private,
&public,
- &hmac_key);
+ &hmac_key->esys_handle);
if (rc != TSS2_RC_SUCCESS) {
/* If we're in dictionary attack lockout mode, we should see a lockout error here, which we
* need to translate for the caller. */
if (rc == TPM2_RC_LOCKOUT)
- r = log_error_errno(
+ return log_error_errno(
SYNTHETIC_ERRNO(ENOLCK),
"TPM2 device is in dictionary attack lockout mode.");
else
- r = log_error_errno(
+ return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to load HMAC key in TPM: %s",
sym_Tss2_RC_Decode(rc));
- goto finish;
}
- r = tpm2_make_encryption_session(c.esys_context, primary, hmac_key, pin, &hmac_session);
+ _cleanup_tpm2_handle_ Tpm2Handle *hmac_session = NULL;
+ r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session);
if (r < 0)
- goto finish;
+ return r;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
r = tpm2_make_policy_session(
- c.esys_context,
+ c,
primary,
hmac_session,
TPM2_SE_POLICY,
pubkey_pcr_mask,
signature,
!!pin,
- &session,
+ &policy_session,
&policy_digest,
/* ret_pcr_bank= */ NULL);
if (r < 0)
- goto finish;
+ return r;
/* 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. */
log_debug("Unsealing HMAC key.");
rc = sym_Esys_Unseal(
- c.esys_context,
- hmac_key,
- session,
- hmac_session, /* use HMAC session to enable parameter encryption */
+ c->esys_context,
+ hmac_key->esys_handle,
+ policy_session->esys_handle,
+ hmac_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
- if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
- log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
- session = tpm2_flush_context_verbose(c.esys_context, session);
- continue;
- }
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
- }
-
- break;
+ if (rc == TSS2_RC_SUCCESS)
+ break;
+ if (rc != TPM2_RC_PCR_CHANGED || i == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
}
secret = memdup(unsealed->buffer, unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
- if (!secret) {
- r = log_oom();
- goto finish;
- }
+ if (!secret)
+ return log_oom();
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;
- r = 0;
-
-finish:
- primary = tpm2_flush_context_verbose(c.esys_context, primary);
- session = tpm2_flush_context_verbose(c.esys_context, session);
- hmac_key = tpm2_flush_context_verbose(c.esys_context, hmac_key);
- return r;
+ return 0;
}
#endif
#if HAVE_TPM2
int tpm2_extend_bytes(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
char **banks,
unsigned pcr_index,
const void *data,
}
rc = sym_Esys_PCR_Extend(
- c,
+ c->esys_context,
ESYS_TR_PCR0 + pcr_index,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,