summaryrefslogtreecommitdiff
path: root/src/shared/tpm2-util.c
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@chown.ath.cx>2022-02-26 10:41:16 +0100
committerLennart Poettering <lennart@poettering.net>2022-03-16 22:52:42 +0100
commitda29de23ef200b17bb780e5a0efb6cff28c72287 (patch)
treef78aed9a49e59138b08d23f0daac412d0b196995 /src/shared/tpm2-util.c
parentc68ac12a0e56272b6521558f85e04ac2bc05b093 (diff)
downloadsystemd-da29de23ef200b17bb780e5a0efb6cff28c72287.tar.gz
tpm2: enable parameter encryption
Use a salted, unbound HMAC session with the primary key used as tpmKey, which mean that the random salt will be encrypted with the primary key while in transit. Decrypt/encrypt flags are set on the new session with AES in CFB mode. There is no fallback to XOR mode. This provides confidentiality and replay protection, both when sealing and unsealing. There is no protection against man in the middle attacks since we have no way to authenticate the TPM at the moment. The exception is unsealing with PIN, as an attacker will be unable to generate the proper HMAC digest.
Diffstat (limited to 'src/shared/tpm2-util.c')
-rw-r--r--src/shared/tpm2-util.c104
1 files changed, 90 insertions, 14 deletions
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 44fe899acd..3dfc5d8b7d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -36,6 +36,7 @@ TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySes
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;
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;
TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
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;
@@ -66,6 +67,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_PolicyPCR),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_TR_SetAuth),
DLSYM_ARG(Esys_Unseal));
if (r < 0)
@@ -595,8 +597,71 @@ static int tpm2_get_best_pcr_bank(
return 0;
}
+static int tpm2_make_encryption_session(
+ ESYS_CONTEXT *c,
+ ESYS_TR tpmKey,
+ ESYS_TR *ret_session) {
+
+ static const TPMT_SYM_DEF symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits = {
+ .aes = 128,
+ },
+ .mode = {
+ .aes = TPM2_ALG_CFB,
+ },
+ };
+ const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
+ TPMA_SESSION_CONTINUESESSION;
+ ESYS_TR session = ESYS_TR_NONE;
+ TSS2_RC rc;
+
+ assert(c);
+
+ log_debug("Starting HMAC encryption session.");
+
+ /* 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. */
+ rc = sym_Esys_StartAuthSession(
+ c,
+ tpmKey,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ NULL,
+ TPM2_SE_HMAC,
+ &symmetric,
+ TPM2_ALG_SHA256,
+ &session);
+ 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);
+ 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;
+ }
+
+ session = flush_context_verbose(c, session);
+ return 0;
+}
+
static int tpm2_make_pcr_session(
ESYS_CONTEXT *c,
+ ESYS_TR tpmKey,
+ ESYS_TR parent_session,
uint32_t pcr_mask,
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
bool use_pin,
@@ -645,9 +710,9 @@ static int tpm2_make_pcr_session(
rc = sym_Esys_StartAuthSession(
c,
+ tpmKey,
ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
+ parent_session,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
@@ -771,7 +836,7 @@ int tpm2_seal(
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
- ESYS_TR primary = ESYS_TR_NONE;
+ ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
TPMI_ALG_HASH pcr_bank;
@@ -816,8 +881,12 @@ int tpm2_seal(
if (r < 0)
return r;
- r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, !!pin, NULL, &policy_digest,
- &pcr_bank);
+ r = tpm2_make_encryption_session(c.esys_context, primary, &session);
+ if (r < 0)
+ goto finish;
+
+ r = tpm2_make_pcr_session(c.esys_context, primary, session, pcr_mask, UINT16_MAX, !!pin, NULL,
+ &policy_digest, &pcr_bank);
if (r < 0)
goto finish;
@@ -869,7 +938,7 @@ int tpm2_seal(
rc = sym_Esys_Create(
c.esys_context,
primary,
- ESYS_TR_PASSWORD,
+ session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
ESYS_TR_NONE,
&hmac_sensitive,
@@ -951,6 +1020,7 @@ int tpm2_seal(
finish:
explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
primary = flush_context_verbose(c.esys_context, primary);
+ session = flush_context_verbose(c.esys_context, session);
return r;
}
@@ -968,7 +1038,8 @@ int tpm2_unseal(
size_t *ret_secret_size) {
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
- ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_key = ESYS_TR_NONE;
+ 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;
@@ -1019,7 +1090,16 @@ int tpm2_unseal(
if (r < 0)
return r;
- r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, !!pin, &session, &policy_digest, NULL);
+ r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
+ if (r < 0)
+ return r;
+
+ r = tpm2_make_encryption_session(c.esys_context, primary, &hmac_session);
+ if (r < 0)
+ goto finish;
+
+ r = tpm2_make_pcr_session(c.esys_context, primary, hmac_session, pcr_mask, pcr_bank, !!pin, &session,
+ &policy_digest, NULL);
if (r < 0)
goto finish;
@@ -1030,16 +1110,12 @@ int tpm2_unseal(
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt.");
- r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
- if (r < 0)
- return r;
-
log_debug("Loading HMAC key into TPM.");
rc = sym_Esys_Load(
c.esys_context,
primary,
- ESYS_TR_PASSWORD,
+ hmac_session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
ESYS_TR_NONE,
&private,
@@ -1084,7 +1160,7 @@ int tpm2_unseal(
c.esys_context,
hmac_key,
session,
- ESYS_TR_NONE,
+ hmac_session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc != TSS2_RC_SUCCESS) {