forked from pool/grub2
7ad3520153
- Amend the TPM2 stack and add authorized policy mode to tpm2_key_protector OBS-URL: https://build.opensuse.org/request/show/1063960 OBS-URL: https://build.opensuse.org/package/show/Base:System/grub2?expand=0&rev=438
795 lines
27 KiB
Diff
795 lines
27 KiB
Diff
From 47220d1ce8fffac3654454b8a981385133b7c23a Mon Sep 17 00:00:00 2001
|
|
From: Gary Lin <glin@suse.com>
|
|
Date: Wed, 8 Feb 2023 11:26:25 +0800
|
|
Subject: [PATCH 13/13] tpm2: support unsealing key with authorized policy
|
|
|
|
To solve the PCR brittleness, TPM 2.0 allows the administrator to
|
|
'authorize' a new PCR policy, i.e. a new set of PCR values, to unseal
|
|
the existing key. This commit extends the SRK mode to support the
|
|
authorized policy mode to unseal the disk encryption key.
|
|
|
|
The usage of the authorized policy mode is very similar to the SRK mode
|
|
except two additional arguments: "-P" for the publicy key and "-S" for
|
|
the signed policy.
|
|
|
|
Example of the authorized policy mode:
|
|
|
|
tpm2_key_protector_init -m authpol -b sha256 -p 0,2,4,7 \
|
|
-k (hd0,gpt1)/boot/grub2/sealed.key \
|
|
-P (hd0,gpt1)/boot/grub2/pub.key \
|
|
-S (hd0,gpt1)/boot/grub2/pol.sig
|
|
|
|
Signed-off-by: Gary Lin <glin@suse.com>
|
|
---
|
|
grub-core/tpm2/module.c | 614 +++++++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 602 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
|
|
index c819ef616..8e1b14146 100644
|
|
--- a/grub-core/tpm2/module.c
|
|
+++ b/grub-core/tpm2/module.c
|
|
@@ -35,7 +35,8 @@ typedef enum grub_tpm2_protector_mode
|
|
{
|
|
GRUB_TPM2_PROTECTOR_MODE_UNSET,
|
|
GRUB_TPM2_PROTECTOR_MODE_SRK,
|
|
- GRUB_TPM2_PROTECTOR_MODE_NV
|
|
+ GRUB_TPM2_PROTECTOR_MODE_NV,
|
|
+ GRUB_TPM2_PROTECTOR_MODE_AUTHPOL
|
|
} grub_tpm2_protector_mode_t;
|
|
|
|
struct grub_tpm2_protector_context
|
|
@@ -47,6 +48,8 @@ struct grub_tpm2_protector_context
|
|
TPM_ALG_ID asymmetric;
|
|
TPM_ALG_ID bank;
|
|
const char *keyfile;
|
|
+ const char *pkfile;
|
|
+ const char *sigfile;
|
|
TPM_HANDLE srk;
|
|
TPM_HANDLE nv;
|
|
const char *efivar;
|
|
@@ -62,8 +65,8 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
.arg = NULL,
|
|
.type = ARG_TYPE_STRING,
|
|
.doc =
|
|
- N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV "
|
|
- "Index ('nv')."),
|
|
+ N_("Unseal key using SRK ('srk') (default), retrieve it from an NV "
|
|
+ "Index ('nv'), or unseal key with a authorized policy ('authpol')."),
|
|
},
|
|
{
|
|
.longarg = "pcrs",
|
|
@@ -85,7 +88,7 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
N_("Bank of PCRs used to authorize key release: "
|
|
"SHA1, SHA256 (default), or SHA384."),
|
|
},
|
|
- /* SRK-mode options */
|
|
+ /* SRK-mode and Authorized Policy-mode options */
|
|
{
|
|
.longarg = "keyfile",
|
|
.shortarg = 'k',
|
|
@@ -93,8 +96,9 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
.arg = NULL,
|
|
.type = ARG_TYPE_STRING,
|
|
.doc =
|
|
- N_("Required in SRK mode, path to the sealed key file to unseal using "
|
|
- "the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed_key)."),
|
|
+ N_("Required in SRK and Authorized Policy mode, path to the sealed "
|
|
+ "key file to unseal using the TPM "
|
|
+ "(e.g., (hd0,gpt1)/boot/grub2/sealed_key)."),
|
|
},
|
|
{
|
|
.longarg = "srk",
|
|
@@ -103,8 +107,8 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
.arg = NULL,
|
|
.type = ARG_TYPE_STRING,
|
|
.doc =
|
|
- N_("In SRK mode, the SRK handle if the SRK is persistent "
|
|
- "(default is 0x81000001)."),
|
|
+ N_("In SRK and Authorized Policy mode, the SRK handle if the SRK is "
|
|
+ "persistent (default is 0x81000001)."),
|
|
},
|
|
{
|
|
.longarg = "asymmetric",
|
|
@@ -113,7 +117,8 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
.arg = NULL,
|
|
.type = ARG_TYPE_STRING,
|
|
.doc =
|
|
- N_("In SRK mode, the type of SRK: RSA (default) or ECC."),
|
|
+ N_("In SRK and Authorized Policy mode, the type of SRK: RSA "
|
|
+ "(default) or ECC."),
|
|
},
|
|
/* NV Index-mode options */
|
|
{
|
|
@@ -136,6 +141,26 @@ static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
|
|
.doc =
|
|
N_("Publish the unsealed key to the indicated UEFI variable."),
|
|
},
|
|
+ /* Authorized Policy-mode options */
|
|
+ {
|
|
+ .longarg = "pkfile",
|
|
+ .shortarg = 'P',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("Public key file to verify the PCR policy signature"
|
|
+ "(e.g., (hd0,gpt1)/boot/grub2/pub.key)"),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "sigfile",
|
|
+ .shortarg = 'S',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("PCR policy signature file (e.g., (hd0,gpt1)/boot/grub2/pol.sig)"),
|
|
+ },
|
|
/* End of list */
|
|
{0, 0, 0, 0, 0, 0}
|
|
};
|
|
@@ -199,6 +224,66 @@ grub_tpm2_protector_read_file (const char *filepath, void **buffer,
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_unmarshal_pkfile (void *pub_key,
|
|
+ grub_size_t pub_key_size,
|
|
+ TPM2B_PUBLIC *pk)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (pub_key_size > buf.cap)
|
|
+ {
|
|
+ grub_dprintf ("tpm2", "Public key file is larger than decode buffer "
|
|
+ "(%" PRIuGRUB_SIZE " vs %" PRIuGRUB_SIZE " bytes).\n", pub_key_size, buf.cap);
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (buf.data, pub_key, pub_key_size);
|
|
+ buf.size = pub_key_size;
|
|
+
|
|
+ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, pk);
|
|
+
|
|
+ if (buf.error)
|
|
+ {
|
|
+ grub_dprintf ("tpm2", "Could not unmarshal public key file, it is likely "
|
|
+ "malformed.\n");
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_unmarshal_sigfile (void *sig,
|
|
+ grub_size_t sig_size,
|
|
+ TPMT_SIGNATURE *signature)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (sig_size > buf.cap)
|
|
+ {
|
|
+ grub_dprintf ("tpm2", "Signed PCR policy file is larger than decode buffer "
|
|
+ "(%" PRIuGRUB_SIZE " vs %" PRIuGRUB_SIZE " bytes).\n", sig_size, buf.cap);
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (buf.data, sig, sig_size);
|
|
+ buf.size = sig_size;
|
|
+
|
|
+ grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (&buf, signature);
|
|
+
|
|
+ if (buf.error)
|
|
+ {
|
|
+ grub_dprintf ("tpm2", "Could not unmarshal public key file, it is likely "
|
|
+ "malformed.\n");
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
static grub_err_t
|
|
grub_tpm2_protector_unmarshal_keyfile (void *sealed_key,
|
|
grub_size_t sealed_key_size,
|
|
@@ -486,6 +571,433 @@ grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
|
|
N_("NV Index mode is not implemented yet"));
|
|
}
|
|
|
|
+static grub_err_t
|
|
+get_pcr_digest (const struct grub_tpm2_protector_context *ctx,
|
|
+ TPM2B_DIGEST *pcr_digest)
|
|
+{
|
|
+ TPM_RC rc;
|
|
+ TPML_PCR_SELECTION pcr_list_out = { 0 };
|
|
+ TPML_DIGEST pcr_values = { 0 };
|
|
+ grub_size_t pcr_digest_len;
|
|
+ TPM2B_AUTH auth = { 0 };
|
|
+ TPMI_DH_OBJECT sequence = 0;
|
|
+ TPMS_AUTH_COMMAND authCmd;
|
|
+ grub_uint8_t i;
|
|
+ TPM2B_DIGEST result_digest;
|
|
+ grub_err_t err = GRUB_ERR_INVALID_COMMAND;
|
|
+
|
|
+ if (!pcr_digest)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ /* PCR Read */
|
|
+ rc = TPM2_PCR_Read (NULL, &ctx->pcr_list, NULL, &pcr_list_out, &pcr_values, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ return grub_error (err, N_("Failed to read PCRs (TPM error: 0x%x)."), rc);
|
|
+ }
|
|
+
|
|
+ if ((pcr_list_out.count != ctx->pcr_list.count) ||
|
|
+ (ctx->pcr_list.pcrSelections[0].sizeOfSelect !=
|
|
+ pcr_list_out.pcrSelections[0].sizeOfSelect))
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ return grub_error (err, N_("Could not read all the specified PCRs."));
|
|
+ }
|
|
+
|
|
+ /* Check the hash algorithm */
|
|
+ switch (ctx->bank)
|
|
+ {
|
|
+ case TPM_ALG_SHA1:
|
|
+ pcr_digest_len = TPM_SHA1_DIGEST_SIZE;
|
|
+ break;
|
|
+ case TPM_ALG_SHA256:
|
|
+ pcr_digest_len = TPM_SHA256_DIGEST_SIZE;
|
|
+ break;
|
|
+ case TPM_ALG_SHA384:
|
|
+ pcr_digest_len = TPM_SHA384_DIGEST_SIZE;
|
|
+ break;
|
|
+ case TPM_ALG_SHA512:
|
|
+ pcr_digest_len = TPM_SHA512_DIGEST_SIZE;
|
|
+ break;
|
|
+ default:
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+
|
|
+ /* Start the hash sequence with an empty password (auth) */
|
|
+ rc = TPM2_HashSequenceStart (NULL, &auth, ctx->bank, &sequence, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ return grub_error (err,
|
|
+ N_("Failed to start hash sequence (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ }
|
|
+
|
|
+ /* Set up the password session with an empty password for TPM2_SequenceUpdate */
|
|
+ /* and TPM2_SequenceComplete */
|
|
+ grub_memset (&authCmd, 0, sizeof (TPMS_AUTH_COMMAND));
|
|
+ authCmd.sessionHandle = TPM_RS_PW;
|
|
+
|
|
+ for (i = 0; i < ctx->pcr_count; i++)
|
|
+ {
|
|
+ if (pcr_values.digests[i].size != pcr_digest_len)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err,
|
|
+ N_("Bad PCR value size: expected %" PRIuGRUB_SIZE " bytes but got %u bytes.\n"),
|
|
+ pcr_digest_len, pcr_values.digests[i].size);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ rc = TPM2_SequenceUpdate (sequence, &authCmd,
|
|
+ (TPM2B_MAX_BUFFER *)&pcr_values.digests[i],
|
|
+ NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err,
|
|
+ N_("Failed to update hash sequence (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rc = TPM2_SequenceComplete (sequence, &authCmd, NULL, TPM_RH_NULL,
|
|
+ &result_digest, NULL, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err,
|
|
+ N_("Failed to complete hash sequence (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *pcr_digest = result_digest;
|
|
+ sequence = 0;
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+
|
|
+ /* End the sequence if necessary */
|
|
+ if (sequence != 0)
|
|
+ {
|
|
+ grub_memset (&authCmd, 0, sizeof (TPMS_AUTH_COMMAND));
|
|
+ authCmd.sessionHandle = TPM_RS_PW;
|
|
+ TPM2_SequenceComplete (sequence, &authCmd, NULL, TPM_RH_NULL,
|
|
+ &result_digest, NULL, NULL);
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_authpol_digest (const struct grub_tpm2_protector_context *ctx,
|
|
+ TPM2B_DIGEST *digest)
|
|
+{
|
|
+ TPM_RC rc;
|
|
+ TPM2B_DIGEST pcr_digest;
|
|
+ TPM2B_NONCE nonce = { 0 };
|
|
+ TPMT_SYM_DEF symmetric = { 0 };
|
|
+ TPMI_SH_AUTH_SESSION session = 0;
|
|
+ TPM2B_DIGEST policy_digest = { 0 };
|
|
+ grub_err_t err;
|
|
+
|
|
+ err = get_pcr_digest (ctx, &pcr_digest);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ /* Start Trial Session to calculate the policy digest */
|
|
+ nonce.size = TPM_SHA256_DIGEST_SIZE;
|
|
+ symmetric.algorithm = TPM_ALG_NULL;
|
|
+
|
|
+ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonce, NULL,
|
|
+ TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256,
|
|
+ &session, NULL, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err,
|
|
+ N_("Failed to start trial policy session (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* PCR Policy */
|
|
+ rc = TPM2_PolicyPCR (session, NULL, &pcr_digest, &ctx->pcr_list, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, _("Failed to submit PCR policy (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Retrieve Policy Digest */
|
|
+ rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, _("Failed to get policy digest (TPM error: 0x%x)."),
|
|
+ rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Epilogue */
|
|
+ *digest = policy_digest;
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+error:
|
|
+ TPM2_FlushContext (session);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_authpol_recover (const struct grub_tpm2_protector_context *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ TPM_RC rc;
|
|
+ TPM2B_DIGEST pcr_policy;
|
|
+ TPM2B_DIGEST pcr_policy_hash;
|
|
+ TPM2B_PUBLIC pub_key;
|
|
+ void *pub_key_bytes = NULL;
|
|
+ grub_size_t pub_key_size;
|
|
+ TPM2B_NAME pubname;
|
|
+ TPMT_SIGNATURE signature;
|
|
+ void *sig_bytes = NULL;
|
|
+ grub_size_t sig_size;
|
|
+ TPM2_SEALED_KEY sealed_key;
|
|
+ void *sealed_key_bytes = NULL;
|
|
+ grub_size_t sealed_key_size;
|
|
+ TPM_HANDLE pubkey_handle = 0;
|
|
+ TPM_HANDLE primary_handle = 0;
|
|
+ TPM_HANDLE sealed_key_handle = 0;
|
|
+ TPMT_SYM_DEF symmetric = { 0 };
|
|
+ TPM2B_NONCE nonceCaller = { 0 };
|
|
+ TPMI_SH_AUTH_SESSION session;
|
|
+ TPM2B_SENSITIVE_DATA data;
|
|
+ TPMS_AUTH_COMMAND authCmd = { 0 };
|
|
+ TPMT_TK_VERIFIED verification_ticket;
|
|
+ grub_uint8_t *key_out;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /* Retrieve Public Key */
|
|
+ err = grub_tpm2_protector_read_file (ctx->pkfile, &pub_key_bytes,
|
|
+ &pub_key_size);
|
|
+ if (err)
|
|
+ return grub_error (err, N_("Failed to read public key file %s"),
|
|
+ ctx->pkfile);
|
|
+
|
|
+ err = grub_tpm2_protector_unmarshal_pkfile (pub_key_bytes,
|
|
+ pub_key_size,
|
|
+ &pub_key);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to unmarshal public key, ensure the public "
|
|
+ "key file is in TPM wire format"));
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Retrieve Signed PCR Policy */
|
|
+ err = grub_tpm2_protector_read_file (ctx->sigfile, &sig_bytes,
|
|
+ &sig_size);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to read signed pcr policy file %s"),
|
|
+ ctx->sigfile);
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ err = grub_tpm2_protector_unmarshal_sigfile (sig_bytes,
|
|
+ sig_size,
|
|
+ &signature);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to unmarshal signed PCR policy, ensure the signed "
|
|
+ "PCR policy file is in TPM wire format"));
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Retrieve Sealed Key */
|
|
+ err = grub_tpm2_protector_read_file (ctx->keyfile, &sealed_key_bytes,
|
|
+ &sealed_key_size);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to read key file %s"), ctx->keyfile);
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ err = grub_tpm2_protector_unmarshal_keyfile (sealed_key_bytes,
|
|
+ sealed_key_size,
|
|
+ &sealed_key);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to unmarshal key, ensure the key file is in "
|
|
+ "TPM wire format"));
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Reproduce the policy signed by the public key */
|
|
+ err = grub_tpm2_protector_authpol_digest (ctx, &pcr_policy);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to get the policy digest"));
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Load the public key */
|
|
+ rc = TPM2_LoadExternal (NULL, NULL, &pub_key, TPM_RH_OWNER,
|
|
+ &pubkey_handle, &pubname, NULL);
|
|
+ if (rc)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to load public key (TPM2_LoadExternal failed "
|
|
+ "with TSS/TPM error %u)"), rc);
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Calculate the digest of the polcy for VerifySignature */
|
|
+ rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)&pcr_policy, TPM_ALG_SHA256,
|
|
+ TPM_RH_NULL, &pcr_policy_hash, NULL, NULL);
|
|
+ if (rc)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to create PCR policy hash (TPM2_Hash failed "
|
|
+ "with TSS/TPM error %u)"), rc);
|
|
+ goto exit2;
|
|
+ }
|
|
+
|
|
+ /* Verify the signature against the public key and the reproduced policy digest */
|
|
+ rc = TPM2_VerifySignature (pubkey_handle, NULL, &pcr_policy_hash, &signature,
|
|
+ &verification_ticket, NULL);
|
|
+ if (rc)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to verify signature (TPM2_VerifySignature "
|
|
+ "failed with TSS/TPM error %u)"), rc);
|
|
+ goto exit2;
|
|
+ }
|
|
+
|
|
+ /* Get the handle of the primary storage key */
|
|
+ err = grub_tpm2_protector_srk_get (ctx, &primary_handle);
|
|
+ if (err)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to create primary"));
|
|
+ goto exit2;
|
|
+ }
|
|
+
|
|
+ /* Load Sealed Key */
|
|
+ /* Use the password session with an empty password */
|
|
+ grub_memset (&authCmd, 0, sizeof (authCmd));
|
|
+ authCmd.sessionHandle = TPM_RS_PW;
|
|
+ /* Load the sealed object into TPM */
|
|
+ rc = TPM2_Load (primary_handle, &authCmd, &sealed_key.private, &sealed_key.public,
|
|
+ &sealed_key_handle, NULL, NULL);
|
|
+ if (rc)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to load sealed key (TPM2_Load failed with "
|
|
+ "TSS/TPM error %u)"), rc);
|
|
+ goto exit3;
|
|
+ }
|
|
+
|
|
+ /* Start a policy session to authorize the signed policy */
|
|
+ symmetric.algorithm = TPM_ALG_AES;
|
|
+ symmetric.keyBits.aes = 128;
|
|
+ symmetric.mode.aes = TPM_ALG_CFB;
|
|
+ nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
+
|
|
+ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL,
|
|
+ TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
|
|
+ &session, NULL, NULL);
|
|
+ if (rc)
|
|
+ {
|
|
+ grub_error (err, N_("Failed to start auth session (TPM2_StartAuthSession "
|
|
+ "failed with TSS/TPM error %u)"), rc);
|
|
+ goto exit4;
|
|
+ }
|
|
+
|
|
+ /* Send the PolicyPCR command to generate the policy digest based on the */
|
|
+ /* current PCR values */
|
|
+ rc = TPM2_PolicyPCR (session, NULL, NULL, &ctx->pcr_list, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to submit PCR policy (TPM2_PolicyPCR failed "
|
|
+ "with TSS/TPM error: 0x%u).\n"), rc);
|
|
+ goto exit5;
|
|
+ }
|
|
+
|
|
+ /* Authorize the signed policy with the public key and the verification ticket */
|
|
+ rc = TPM2_PolicyAuthorize (session, NULL, &pcr_policy, NULL, &pubname,
|
|
+ &verification_ticket, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to authorize PCR policy (TPM2_PolicyAuthorize "
|
|
+ "failed with TSS/TPM error: 0x%u).\n"), rc);
|
|
+ goto exit5;
|
|
+ }
|
|
+
|
|
+ /* Unseal the key with the policy session that authorizes the signed policy */
|
|
+ grub_memset (&authCmd, 0, sizeof (authCmd));
|
|
+ authCmd.sessionHandle = session;
|
|
+ rc = TPM2_Unseal (sealed_key_handle, &authCmd, &data, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = GRUB_ERR_BAD_DEVICE;
|
|
+ grub_error (err, N_("Failed to unseal sealed key (TPM2_Unseal failed"
|
|
+ "with TSS/TPM error: 0x%u).\n"), rc);
|
|
+ grub_millisleep(500);
|
|
+ goto exit5;
|
|
+ }
|
|
+
|
|
+ /* Epilogue */
|
|
+ key_out = grub_malloc (data.size);
|
|
+ if (!key_out)
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_MEMORY;
|
|
+ grub_error (err, N_("No memory left to allocate unlock key buffer"));
|
|
+ goto exit4;
|
|
+ }
|
|
+
|
|
+ grub_printf("TPM2: unsealed %u bytes of key material\n", data.size);
|
|
+
|
|
+ if (ctx->efivar)
|
|
+ {
|
|
+ rc = grub_tpm2_protector_publish_key (data.buffer, data.size, ctx->efivar);
|
|
+ if (rc)
|
|
+ goto exit4;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (key_out, data.buffer, data.size);
|
|
+
|
|
+ *key = key_out;
|
|
+ *key_size = data.size;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+exit5:
|
|
+ TPM2_FlushContext (session);
|
|
+
|
|
+exit4:
|
|
+ TPM2_FlushContext (sealed_key_handle);
|
|
+
|
|
+exit3:
|
|
+ TPM2_FlushContext (primary_handle);
|
|
+
|
|
+exit2:
|
|
+ TPM2_FlushContext (pubkey_handle);
|
|
+
|
|
+exit1:
|
|
+ grub_free (sealed_key_bytes);
|
|
+ grub_free (pub_key_bytes);
|
|
+ grub_free (sig_bytes);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
static grub_err_t
|
|
grub_tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx,
|
|
grub_uint8_t **key, grub_size_t *key_size)
|
|
@@ -496,6 +1008,8 @@ grub_tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx,
|
|
return grub_tpm2_protector_srk_recover (ctx, key, key_size);
|
|
case GRUB_TPM2_PROTECTOR_MODE_NV:
|
|
return grub_tpm2_protector_nv_recover (ctx, key, key_size);
|
|
+ case GRUB_TPM2_PROTECTOR_MODE_AUTHPOL:
|
|
+ return grub_tpm2_protector_authpol_recover (ctx, key, key_size);
|
|
default:
|
|
return GRUB_ERR_BAD_ARGUMENT;
|
|
}
|
|
@@ -543,7 +1057,10 @@ initialize_pcr_list (struct grub_tpm2_protector_context *ctx)
|
|
static grub_err_t
|
|
grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
|
|
{
|
|
- if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET && ctx->keyfile &&
|
|
+ ctx->pkfile && ctx->sigfile)
|
|
+ ctx->mode = GRUB_TPM2_PROTECTOR_MODE_AUTHPOL;
|
|
+ else if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
|
|
ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
|
|
|
|
/* Checks for SRK mode */
|
|
@@ -556,6 +1073,14 @@ grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
N_("In SRK mode, an NV Index cannot be specified"));
|
|
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->pkfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In SRK mode, an a public key cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->sigfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In SRK mode, an a signed pcr policy cannot be specified"));
|
|
+
|
|
/* Checks for NV mode */
|
|
if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
@@ -575,6 +1100,34 @@ grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
|
|
N_("In NV Index mode, an asymmetric key type cannot be "
|
|
"specified"));
|
|
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->pkfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, an a public key cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->sigfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In NV Index mode, an a signed pcr policy cannot be specified"));
|
|
+
|
|
+ /* Checks for Authorized Policy mode */
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_AUTHPOL && !ctx->keyfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In Authorized Policy mode, a key file must be specified: "
|
|
+ "--keyfile or -k"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_AUTHPOL && !ctx->pkfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In Authorized Policy mode, a public key file must be specified: "
|
|
+ "--pkfile or -P"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_AUTHPOL && !ctx->sigfile)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In Authorized Policy mode, a signed pcr file must be specified: "
|
|
+ "--sigfile or -S"));
|
|
+
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_AUTHPOL && ctx->nv)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("In Authorized Policy mode, an NV Index cannot be specified"));
|
|
+
|
|
/* Defaults assignment */
|
|
if (!ctx->bank)
|
|
ctx->bank = TPM_ALG_SHA256;
|
|
@@ -585,7 +1138,8 @@ grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
|
|
ctx->pcr_count = 1;
|
|
}
|
|
|
|
- if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK)
|
|
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK ||
|
|
+ ctx->mode == GRUB_TPM2_PROTECTOR_MODE_AUTHPOL)
|
|
{
|
|
if (!ctx->srk)
|
|
ctx->srk = TPM2_SRK_HANDLE;
|
|
@@ -619,6 +1173,18 @@ grub_tpm2_protector_parse_keyfile (const char *value, const char **keyfile)
|
|
return grub_tpm2_protector_parse_string (value, keyfile, "keyfile");
|
|
}
|
|
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_parse_pkfile (const char *value, const char **pkfile)
|
|
+{
|
|
+ return grub_tpm2_protector_parse_string (value, pkfile, "pkfile");
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_tpm2_protector_parse_sigfile (const char *value, const char **sigfile)
|
|
+{
|
|
+ return grub_tpm2_protector_parse_string (value, sigfile, "sigfile");
|
|
+}
|
|
+
|
|
static grub_err_t
|
|
grub_tpm2_protector_parse_efivar (const char *value, const char **efivar)
|
|
{
|
|
@@ -633,6 +1199,8 @@ grub_tpm2_protector_parse_mode (const char *value,
|
|
*mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
|
|
else if (grub_strcmp (value, "nv") == 0)
|
|
*mode = GRUB_TPM2_PROTECTOR_MODE_NV;
|
|
+ else if (grub_strcmp (value, "authpol") == 0)
|
|
+ *mode = GRUB_TPM2_PROTECTOR_MODE_AUTHPOL;
|
|
else
|
|
return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
N_("Value '%s' is not a valid TPM2 key protector mode"),
|
|
@@ -722,6 +1290,22 @@ grub_tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
|
|
return err;
|
|
}
|
|
|
|
+ if (state[8].set) /* pkfile */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_pkfile (state[8].arg,
|
|
+ &grub_tpm2_protector_ctx.pkfile);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[9].set) /* sigfile */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_sigfile (state[9].arg,
|
|
+ &grub_tpm2_protector_ctx.sigfile);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
err = grub_tpm2_protector_check_args (&grub_tpm2_protector_ctx);
|
|
|
|
/* This command only initializes the protector, so nothing else to do. */
|
|
@@ -739,6 +1323,8 @@ grub_tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__
|
|
N_("tpm2_key_protector_clear accepts no arguments"));
|
|
|
|
grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.pkfile);
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.sigfile);
|
|
grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
|
|
|
|
return GRUB_ERR_NONE;
|
|
@@ -761,7 +1347,9 @@ GRUB_MOD_INIT (tpm2)
|
|
"[-k sealed_key_file_path] "
|
|
"[-s srk_handle] "
|
|
"[-a asymmetric_key_type] "
|
|
- "[-n nv_index]"),
|
|
+ "[-n nv_index] "
|
|
+ "[-P public_key_file_path] "
|
|
+ "[-S signature_file_path]"),
|
|
N_("Initialize the TPM2 key protector."),
|
|
grub_tpm2_protector_init_cmd_options);
|
|
grub_tpm2_protector_clear_cmd =
|
|
@@ -775,6 +1363,8 @@ GRUB_MOD_INIT (tpm2)
|
|
GRUB_MOD_FINI (tpm2)
|
|
{
|
|
grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.pkfile);
|
|
+ grub_free ((void *) grub_tpm2_protector_ctx.sigfile);
|
|
grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
|
|
|
|
grub_key_protector_unregister (&grub_tpm2_key_protector);
|
|
--
|
|
2.35.3
|
|
|