diff --git a/fix-testcase-empty-efi-variables.patch b/fix-testcase-empty-efi-variables.patch new file mode 100644 index 0000000..6bcaa48 --- /dev/null +++ b/fix-testcase-empty-efi-variables.patch @@ -0,0 +1,58 @@ +From 61f9b77634578c0bf0c3bf6c4b386057e8661a1c Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 12 Jun 2024 14:41:38 +0800 +Subject: [PATCH] testcase: fix playback on empty EFI variables + +For systems in UEFI Setup mode, there is no PK, KEK, or db. However, +those variables are still recorded in the TPM event log with zero +length. To avoid failing on reading those files, this commit changes the +file reading flag so that testcase playback won't stop on those EFI +variables. + +Signed-off-by: Gary Lin +--- + src/testcase.c | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +diff --git a/src/testcase.c b/src/testcase.c +index f74238b..998aedd 100644 +--- a/src/testcase.c ++++ b/src/testcase.c +@@ -224,12 +224,18 @@ testcase_write_file(const char *directory, const char *name, const buffer_t *bp) + } + + static buffer_t * +-testcase_read_file(const char *directory, const char *name) ++__testcase_read_file(const char *directory, const char *name, int flags) + { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/%s", directory, name); +- return runtime_read_file(path, 0); ++ return runtime_read_file(path, flags); ++} ++ ++static buffer_t * ++testcase_read_file(const char *directory, const char *name) ++{ ++ return __testcase_read_file(directory, name, 0); + } + + testcase_t * +@@ -314,7 +320,12 @@ testcase_record_efi_variable(testcase_t *tc, const char *name, const buffer_t *d + buffer_t * + testcase_playback_efi_variable(testcase_t *tc, const char *name) + { +- return testcase_read_file(tc->efi_directory, name); ++ /* For systems in UEFI Setup mode, there is no PK, KEK, or db, but those ++ * variables are still recorded in the TPM event log with zero length. ++ * Set the file reading flag to skip those EFI variable files. ++ */ ++ return __testcase_read_file(tc->efi_directory, name, ++ RUNTIME_MISSING_FILE_OKAY); + } + + void +-- +2.35.3 + diff --git a/pcr-oracle.changes b/pcr-oracle.changes index 5a57245..b1e05f5 100644 --- a/pcr-oracle.changes +++ b/pcr-oracle.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Aug 5 06:11:52 UTC 2024 - Gary Ching-Pang Lin + +- Add support-ecc-srk.patch to support ECC SRK +- Add fix-testcase-empty-efi-variables.patch to fix the testcase + playback on empty EFI variables + ------------------------------------------------------------------- Mon Mar 25 20:16:53 UTC 2024 - Alberto Planas Dominguez diff --git a/pcr-oracle.spec b/pcr-oracle.spec index ec4af33..8f7bd43 100644 --- a/pcr-oracle.spec +++ b/pcr-oracle.spec @@ -34,6 +34,10 @@ Patch1: fix_loader_conf.patch Patch2: fix_grub_bls_entry.patch # PATCH-FIX-UPSTREAM fix_grub_bls_cmdline.patch gh#okirch/pcr-oracle!52 (cont) Patch3: fix_grub_bls_cmdline.patch +# PATCH-FIX-UPSTREAM support-ecc-srk.patch gh#okirch/pcr-oracle!56 +Patch4: support-ecc-srk.patch +# PATCH-FIX-UPSTREAM fix-testcase-empty-efi-variables.patch gh#okirch/pcr-oracle!58 +Patch5: fix-testcase-empty-efi-variables.patch BuildRequires: libopenssl-devel >= 0.9.8 BuildRequires: tpm2-0-tss-devel >= 2.4.0 Requires: libtss2-tcti-device0 diff --git a/support-ecc-srk.patch b/support-ecc-srk.patch new file mode 100644 index 0000000..d4ba306 --- /dev/null +++ b/support-ecc-srk.patch @@ -0,0 +1,413 @@ +From 60ce42dbf61ce89012d9bc71c92c5cc92759b02c Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 3 Apr 2024 14:41:49 +0800 +Subject: [PATCH 1/3] Update the comment for SRK template + +USERWITHAUTH and NODA are set according to "TCG TPM v2.0 Provisioning +Guidance". This commit updates the comment to add the reference. + +Signed-off-by: Gary Lin +--- + src/pcr-policy.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/pcr-policy.c b/src/pcr-policy.c +index 8f2c42c..f03b6e0 100644 +--- a/src/pcr-policy.c ++++ b/src/pcr-policy.c +@@ -67,8 +67,9 @@ static TPM2B_PUBLIC SRK_template = { + .publicArea = { + .type = TPM2_ALG_RSA, + .nameAlg = TPM2_ALG_SHA256, +- /* For reasons not clear to me, grub2 derives the SRK using the NODA attribute, +- * which means it is not subject to dictionary attack protections. */ ++ /* Per "Storage Primary Key (SRK) Templates" in section 7.5.1 of ++ * TCG TPM v2.0 Provisioning Guidance 1.0 Revision 1.0, the ++ * template for shared SRKs sets USERWITHAUTH and NODA. */ + .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT \ + |TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT \ + |TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH \ +-- +2.35.3 + + +From 0085c5a6a47f433dc69739c54b9db11796aff62e Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 10 Apr 2024 16:20:44 +0800 +Subject: [PATCH 2/3] Support SRK template with ECC_NIST_P256 + +When sealing data with SRK, the data is actually encrypted with the +symmetric key, and the selection of the asymmetric algorithm is only +a parameter for KDF to derive the symmetric key. Compared with RSA, +ECC NIST-P256 provides the faster key generation, so it's a better +choice for the SRK template in general. + +This commit adds a new option, '--ecc-srk', to switch the default SRK +template from the RSA one to the ECC one, so that the user can specify +the SRK template when sealing/unsealing data. + +Signed-off-by: Gary Lin +--- + src/oracle.c | 7 +++++++ + src/pcr-policy.c | 44 +++++++++++++++++++++++++++++++++++++++++--- + src/pcr.h | 1 + + 3 files changed, 49 insertions(+), 3 deletions(-) + +diff --git a/src/oracle.c b/src/oracle.c +index 1cafafc..f391430 100644 +--- a/src/oracle.c ++++ b/src/oracle.c +@@ -92,6 +92,7 @@ enum { + OPT_RSA_PUBLIC_KEY, + OPT_RSA_GENERATE_KEY, + OPT_RSA_BITS, ++ OPT_ECC_SRK, + OPT_INPUT, + OPT_OUTPUT, + OPT_AUTHORIZED_POLICY, +@@ -125,6 +126,7 @@ static struct option options[] = { + { "public-key", required_argument, 0, OPT_RSA_PUBLIC_KEY }, + { "rsa-generate-key", no_argument, 0, OPT_RSA_GENERATE_KEY }, + { "rsa-bits", required_argument, 0, OPT_RSA_BITS }, ++ { "ecc-srk", no_argument, 0, OPT_ECC_SRK }, + { "input", required_argument, 0, OPT_INPUT }, + { "output", required_argument, 0, OPT_OUTPUT }, + { "authorized-policy", required_argument, 0, OPT_AUTHORIZED_POLICY }, +@@ -1042,6 +1044,8 @@ main(int argc, char **argv) + unsigned int rsa_bits = 2048; + int c, exit_code = 0; + ++ set_srk_alg("RSA"); ++ + while ((c = getopt_long(argc, argv, "dhA:CF:LSZ", options, NULL)) != EOF) { + switch (c) { + case 'A': +@@ -1109,6 +1113,9 @@ main(int argc, char **argv) + case OPT_RSA_BITS: + opt_rsa_bits = optarg; + break; ++ case OPT_ECC_SRK: ++ set_srk_alg("ECC"); ++ break; + case OPT_INPUT: + opt_input = optarg; + break; +diff --git a/src/pcr-policy.c b/src/pcr-policy.c +index f03b6e0..f65becf 100644 +--- a/src/pcr-policy.c ++++ b/src/pcr-policy.c +@@ -62,7 +62,7 @@ struct target_platform { + const stored_key_t *public_key_file); + }; + +-static TPM2B_PUBLIC SRK_template = { ++static TPM2B_PUBLIC RSA_SRK_template = { + .size = sizeof(TPMT_PUBLIC), + .publicArea = { + .type = TPM2_ALG_RSA, +@@ -88,6 +88,35 @@ static TPM2B_PUBLIC SRK_template = { + } + }; + ++static TPM2B_PUBLIC ECC_SRK_template = { ++ .size = sizeof(TPMT_PUBLIC), ++ .publicArea = { ++ .type = TPM2_ALG_ECC, ++ .nameAlg = TPM2_ALG_SHA256, ++ /* Per "Storage Primary Key (SRK) Templates" in section 7.5.1 of ++ * TCG TPM v2.0 Provisioning Guidance 1.0 Revision 1.0, the ++ * template for shared SRKs sets USERWITHAUTH and NODA. */ ++ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT \ ++ |TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT \ ++ |TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH \ ++ |TPMA_OBJECT_NODA, ++ .parameters = { ++ .eccDetail = { ++ .symmetric = { ++ .algorithm = TPM2_ALG_AES, ++ .keyBits = { .sym = 128 }, ++ .mode = { .sym = TPM2_ALG_CFB }, ++ }, ++ .scheme = { TPM2_ALG_NULL }, ++ .curveID = TPM2_ECC_NIST_P256, ++ .kdf.scheme = TPM2_ALG_NULL ++ } ++ } ++ } ++}; ++ ++static const TPM2B_PUBLIC *SRK_template; ++ + static const TPM2B_PUBLIC seal_public_template = { + .size = sizeof(TPMT_PUBLIC), + .publicArea = { +@@ -107,10 +136,19 @@ static const TPM2B_PUBLIC seal_public_template = { + } + }; + ++void ++set_srk_alg (const char *alg) ++{ ++ if (strcmp(alg, "RSA") == 0) ++ SRK_template = &RSA_SRK_template; ++ else ++ SRK_template = &ECC_SRK_template; ++} ++ + void + set_srk_rsa_bits (const unsigned int rsa_bits) + { +- SRK_template.publicArea.parameters.rsaDetail.keyBits = rsa_bits; ++ RSA_SRK_template.publicArea.parameters.rsaDetail.keyBits = rsa_bits; + } + + static inline const tpm_evdigest_t * +@@ -609,7 +647,7 @@ esys_create_primary(ESYS_CONTEXT *esys_context, ESYS_TR *handle_ret) + t0 = timing_begin(); + rc = Esys_CreatePrimary(esys_context, ESYS_TR_RH_OWNER, + ESYS_TR_PASSWORD, +- ESYS_TR_NONE, ESYS_TR_NONE, &in_sensitive, &SRK_template, ++ ESYS_TR_NONE, ESYS_TR_NONE, &in_sensitive, SRK_template, + NULL, &creation_pcr, handle_ret, + NULL, NULL, + NULL, NULL); +diff --git a/src/pcr.h b/src/pcr.h +index 4d8f816..f1dc9af 100644 +--- a/src/pcr.h ++++ b/src/pcr.h +@@ -39,6 +39,7 @@ typedef struct tpm_pcr_selection { + const tpm_algo_info_t * algo_info; + } tpm_pcr_selection_t; + ++extern void set_srk_alg (const char *alg); + extern void set_srk_rsa_bits (const unsigned int rsa_bits); + extern void pcr_bank_initialize(tpm_pcr_bank_t *bank, unsigned int pcr_mask, const tpm_algo_info_t *algo); + extern bool pcr_bank_wants_pcr(tpm_pcr_bank_t *bank, unsigned int index); +-- +2.35.3 + + +From 207bd496868d65455e63aa9586bfc6e02900a4a1 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 24 Apr 2024 14:53:25 +0800 +Subject: [PATCH 3/3] Update to conform the latest TPM 2.0 Key File + +For TPM 2.0 Key File, the default asymmetric algorithm of SRK is ECC +NIST-P256, not RSA 2048. A new field 'rsaParent' is introduced to note +the key is sealed with RSA SRK. + +This commit implements two new fields: description and rsaParent in +tpm2key.c/tpm2key-asn.h and sets rsaParent when RSA SRK is used to seal +the key. When unsealing a tpm2key, the SRK template is set to the RSA +template if rsaParent is TRUE. Otherwise, the SRK template is switched +to the ECC SRK template. + +A new testing script, test-tpm2key-ecc.sh, is also added to test the +tpm2key file sealed with ECC SRK. + +Signed-off-by: Gary Lin +--- + src/pcr-policy.c | 8 +++ + src/tpm2key-asn.h | 4 ++ + src/tpm2key.c | 2 + + test-tpm2key-ecc.sh | 127 ++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 141 insertions(+) + create mode 100755 test-tpm2key-ecc.sh + +diff --git a/src/pcr-policy.c b/src/pcr-policy.c +index f65becf..90f60ff 100644 +--- a/src/pcr-policy.c ++++ b/src/pcr-policy.c +@@ -1505,6 +1505,11 @@ tpm2key_unseal_secret(const char *input_path, const char *output_path, + if (!tpm2key_read_file(input_path, &tpm2key)) + return false; + ++ if (tpm2key->rsaParent == 1) ++ SRK_template = &RSA_SRK_template; ++ else ++ SRK_template = &ECC_SRK_template; ++ + buffer_init_read(&buf, tpm2key->pubkey->data, tpm2key->pubkey->length); + rc = Tss2_MU_TPM2B_PUBLIC_Unmarshal(buf.data, buf.size, &buf.rpos, &pub); + if (rc != TSS2_RC_SUCCESS) +@@ -1634,6 +1639,9 @@ tpm2key_write_sealed_secret(const char *pathname, + if (!tpm2key_basekey(&tpm2key, TPM2_RH_OWNER, sealed_public, sealed_private)) + goto cleanup; + ++ if (SRK_template->publicArea.type == TPM2_ALG_RSA) ++ tpm2key->rsaParent = 1; ++ + if (pcr_sel && !tpm2key_add_policy_policypcr(tpm2key, pcr_sel)) + goto cleanup; + +diff --git a/src/tpm2key-asn.h b/src/tpm2key-asn.h +index 3f1c0d7..d0cdfaa 100644 +--- a/src/tpm2key-asn.h ++++ b/src/tpm2key-asn.h +@@ -77,6 +77,8 @@ DEFINE_STACK_OF(TSSAUTHPOLICY); + * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL + * secret [2] EXPLICIT OCTET STRING OPTIONAL + * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL ++ * description [4] EXPLICIT UTF8String OPTIONAL ++ * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL + * parent INTEGER + * pubkey OCTET STRING + * privkey OCTET STRING +@@ -89,6 +91,8 @@ typedef struct { + STACK_OF(TSSOPTPOLICY) *policy; + ASN1_OCTET_STRING *secret; + STACK_OF(TSSAUTHPOLICY) *authPolicy; ++ ASN1_UTF8STRING description; ++ ASN1_BOOLEAN rsaParent; + ASN1_INTEGER *parent; + ASN1_OCTET_STRING *pubkey; + ASN1_OCTET_STRING *privkey; +diff --git a/src/tpm2key.c b/src/tpm2key.c +index cabd791..af4c984 100644 +--- a/src/tpm2key.c ++++ b/src/tpm2key.c +@@ -278,6 +278,8 @@ ASN1_SEQUENCE(TSSPRIVKEY) = { + ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, policy, TSSOPTPOLICY, 1), + ASN1_EXP_OPT(TSSPRIVKEY, secret, ASN1_OCTET_STRING, 2), + ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, authPolicy, TSSAUTHPOLICY, 3), ++ ASN1_EXP_OPT(TSSPRIVKEY, description, ASN1_UTF8STRING, 4), ++ ASN1_EXP_OPT(TSSPRIVKEY, rsaParent, ASN1_BOOLEAN, 5), + ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER), + ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING), + ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING) +diff --git a/test-tpm2key-ecc.sh b/test-tpm2key-ecc.sh +new file mode 100755 +index 0000000..d87acff +--- /dev/null ++++ b/test-tpm2key-ecc.sh +@@ -0,0 +1,127 @@ ++#!/bin/bash ++# ++# This script needs to be run with root privilege ++# ++ ++# TESTDIR=policy.test ++PCR_MASK=0,2,4,12 ++ ++pcr_oracle=pcr-oracle ++if [ -x pcr-oracle ]; then ++ pcr_oracle=$PWD/pcr-oracle ++fi ++ ++function call_oracle { ++ ++ echo "****************" ++ echo "pcr-oracle $*" ++ $pcr_oracle --target-platform tpm2.0 -d "$@" ++} ++ ++if [ -z "$TESTDIR" ]; then ++ tmpdir=$(mktemp -d /tmp/pcrtestXXXXXX) ++ trap "cd / && rm -rf $tmpdir" 0 1 2 10 11 15 ++ ++ TESTDIR=$tmpdir ++fi ++ ++trap "echo 'FAIL: command exited with error'; exit 1" ERR ++ ++echo "This is super secret" >$TESTDIR/secret ++ ++set -e ++cd $TESTDIR ++ ++echo "Seal the secret with PCR policy" ++call_oracle \ ++ --from current \ ++ --input secret \ ++ --output sealed \ ++ --ecc-srk \ ++ seal-secret $PCR_MASK ++ ++echo "Unseal the sealed with PCR policy" ++call_oracle \ ++ --input sealed \ ++ --output recovered \ ++ unseal-secret ++ ++if ! cmp secret recovered; then ++ echo "BAD: Unable to recover original secret" ++ echo "Secret:" ++ od -tx1c secret ++ echo "Recovered:" ++ od -tx1c recovered ++ exit 1 ++else ++ echo "NICE: we were able to recover the original secret" ++fi ++ ++rm -f sealed recovered ++ ++call_oracle \ ++ --rsa-generate-key \ ++ --private-key policy-key.pem \ ++ --auth authorized.policy \ ++ create-authorized-policy $PCR_MASK ++ ++call_oracle \ ++ --private-key policy-key.pem \ ++ --public-key policy-pubkey \ ++ store-public-key ++ ++call_oracle \ ++ --auth authorized.policy \ ++ --input secret \ ++ --output sealed \ ++ --ecc-srk \ ++ seal-secret ++ ++for attempt in first second; do ++ echo "Sign the set of PCRs we want to authorize" ++ call_oracle \ ++ --policy-name "authorized-policy-test" \ ++ --private-key policy-key.pem \ ++ --from current \ ++ --input sealed \ ++ --output sealed-signed \ ++ sign $PCR_MASK ++ ++ echo "$attempt attempt to unseal the secret" ++ call_oracle \ ++ --input sealed-signed \ ++ --output recovered \ ++ unseal-secret ++ ++ if ! cmp secret recovered; then ++ echo "BAD: Unable to recover original secret" ++ echo "Secret:" ++ od -tx1c secret ++ echo "Recovered:" ++ od -tx1c recovered ++ exit 1 ++ else ++ echo "NICE: we were able to recover the original secret" ++ fi ++ ++ if [ "$attempt" = "second" ]; then ++ break ++ fi ++ ++ echo "Extend PCR 12. Unsealing should fail afterwards" ++ tpm2_pcrextend 12:sha256=21d2013e3081f1e455fdd5ba6230a8620c3cfc9a9c31981d857fe3891f79449e ++ rm -f recovered ++ call_oracle \ ++ --input sealed-signed \ ++ --output recovered \ ++ unseal-secret || true ++ ++ if [ -s recovered ] && ! cmp secret recovered; then ++ echo "BAD: We were still able to recover the original secret. Something stinks" ++ exit 1 ++ else ++ echo "GOOD: After changing a PCR, the secret can no longer be unsealed" ++ fi ++ ++ echo "Now recreate the signed PCR policy" ++done +-- +2.35.3 +