diff --git a/.github/workflows/seal-test.yml b/.github/workflows/seal-test.yml new file mode 100644 index 00000000..1d7a0a4d --- /dev/null +++ b/.github/workflows/seal-test.yml @@ -0,0 +1,71 @@ +name: Seal Test Suite + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + paths: + - 'examples/seal/**' + - 'examples/nvram/seal_nv.c' + - 'examples/nvram/nvram.h' + - 'src/tpm2_wrap.c' + - 'wolftpm/tpm2_wrap.h' + pull_request: + branches: [ '*' ] + paths: + - 'examples/seal/**' + - 'examples/nvram/seal_nv.c' + - 'examples/nvram/nvram.h' + - 'src/tpm2_wrap.c' + - 'wolftpm/tpm2_wrap.h' + +jobs: + seal-test: + runs-on: ubuntu-latest + steps: + - name: Checkout wolfTPM + uses: actions/checkout@v4 + + - name: Checkout wolfSSL + uses: actions/checkout@v4 + with: + repository: wolfssl/wolfssl + ref: master + path: wolfssl + + - name: Build and install wolfSSL + working-directory: ./wolfssl + run: | + ./autogen.sh + ./configure --enable-wolftpm --enable-pkcallbacks + make -j + sudo make install + sudo ldconfig + + - name: Checkout ibmswtpm2 + uses: actions/checkout@v4 + with: + repository: kgoldman/ibmswtpm2 + path: ibmswtpm2 + + - name: Build and start SWTPM + working-directory: ./ibmswtpm2/src + run: | + make + ./tpm_server & + + - name: Build wolfTPM + run: | + ./autogen.sh + ./configure --enable-swtpm --enable-debug + make -j + + - name: Run seal tests + run: bash examples/seal/seal_test.sh + + - name: Upload failure logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: seal-test-logs + path: seal_test.log + retention-days: 5 diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index 9e81cef0..080b4af0 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -2,7 +2,7 @@ name: Zephyr wolfTPM Tests on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] diff --git a/.gitignore b/.gitignore index d6a057fb..8d372436 100644 --- a/.gitignore +++ b/.gitignore @@ -70,12 +70,15 @@ examples/nvram/store examples/nvram/read examples/nvram/counter examples/nvram/policy_nv +examples/nvram/seal_nv examples/gpio/gpio_config examples/gpio/gpio_set examples/gpio/gpio_read examples/gpio/gpio_nuvoton examples/seal/seal examples/seal/unseal +examples/seal/seal_pcr +examples/seal/seal_policy_auth examples/attestation/make_credential examples/attestation/activate_credential examples/attestation/certify diff --git a/CMakeLists.txt b/CMakeLists.txt index 08a5549f..3fa3abe9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -518,6 +518,7 @@ if (WOLFTPM_EXAMPLES) add_tpm_example(policy_nv nvram/policy_nv.c) add_tpm_example(read nvram/read.c) add_tpm_example(store nvram/store.c) + add_tpm_example(seal_nv nvram/seal_nv.c) add_tpm_example(extend pcr/extend.c) add_tpm_example(policy_sign pcr/policy_sign.c) add_tpm_example(policy pcr/policy.c) @@ -527,6 +528,8 @@ if (WOLFTPM_EXAMPLES) add_tpm_example(pkcs7 pkcs7/pkcs7.c) add_tpm_example(seal seal/seal.c) add_tpm_example(unseal seal/unseal.c) + add_tpm_example(seal_pcr seal/seal_pcr.c) + add_tpm_example(seal_policy_auth seal/seal_policy_auth.c) add_tpm_example(clock_set timestamp/clock_set.c) add_tpm_example(signed_timestamp timestamp/signed_timestamp.c) add_tpm_example(tls_client_notpm tls/tls_client_notpm.c) diff --git a/examples/nvram/include.am b/examples/nvram/include.am index 4e1d5e0f..7e406343 100644 --- a/examples/nvram/include.am +++ b/examples/nvram/include.am @@ -33,6 +33,12 @@ examples_nvram_extend_SOURCES = examples/nvram/extend.c \ examples/tpm_test_keys.c examples_nvram_extend_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_nvram_extend_DEPENDENCIES = src/libwolftpm.la + +noinst_PROGRAMS += examples/nvram/seal_nv +examples_nvram_seal_nv_SOURCES = examples/nvram/seal_nv.c \ + examples/tpm_test_keys.c +examples_nvram_seal_nv_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) +examples_nvram_seal_nv_DEPENDENCIES = src/libwolftpm.la endif example_nvramdir = $(exampledir)/nvram @@ -41,10 +47,12 @@ dist_example_nvram_DATA = \ examples/nvram/read.c \ examples/nvram/counter.c \ examples/nvram/policy_nv.c \ - examples/nvram/extend.c + examples/nvram/extend.c \ + examples/nvram/seal_nv.c DISTCLEANFILES+= examples/nvram/.libs/store \ examples/nvram/.libs/read \ examples/nvram/.libs/counter \ examples/nvram/.libs/policy_nv \ - examples/nvram/.libs/extend + examples/nvram/.libs/extend \ + examples/nvram/.libs/seal_nv diff --git a/examples/nvram/nvram.h b/examples/nvram/nvram.h index 2f97832e..4d4740ce 100644 --- a/examples/nvram/nvram.h +++ b/examples/nvram/nvram.h @@ -29,9 +29,8 @@ int TPM2_NVRAM_Store_Example(void* userCtx, int argc, char *argv[]); int TPM2_NVRAM_Read_Example(void* userCtx, int argc, char *argv[]); int TPM2_NVRAM_Counter_Example(void* userCtx, int argc, char *argv[]); -int TPM2_PCR_Seal_With_Policy_Auth_NV_Test(void* userCtx, int argc, char *argv[]); -int TPM2_PCR_Seal_With_Policy_Auth_NV_External_Test(void* userCtx, int argc, char *argv[]); int TPM2_NVRAM_PolicyNV_Example(void* userCtx, int argc, char *argv[]); +int TPM2_NVRAM_SealNV_Example(void* userCtx, int argc, char *argv[]); int TPM2_NVRAM_Extend_Example(void* userCtx, int argc, char *argv[]); #ifdef __cplusplus diff --git a/examples/nvram/seal_nv.c b/examples/nvram/seal_nv.c new file mode 100644 index 00000000..e649ea84 --- /dev/null +++ b/examples/nvram/seal_nv.c @@ -0,0 +1,382 @@ +/* seal_nv.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* NV storage with PCR policy. + * + * Stores a secret in TPM NV memory protected by PCR policy. + * Only readable when PCR values match. + * + * Sealing method: NV + PCR policy + * Complexity: Medium + * Flexibility: High + * Use case: Storing small metadata (keys/passwords) directly in TPM + * silicon instead of disk + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#include + +#ifndef WOLFTPM2_NO_WRAPPER + +#include +#include +#include +#include + +/******************************************************************************/ +/* --- BEGIN TPM2.0 NV Seal Example -- */ +/******************************************************************************/ + +#define TPM2_DEMO_NV_SEAL_INDEX 0x01800203 + +static void usage(void) +{ + printf("Expected usage:\n"); + printf("./examples/nvram/seal_nv [-pcr=N] [-nvindex=0xNNNNNNNN]\n"); + printf("\t[-store/-read/-delete] [-ownerauth=str] [-secretstr=str]\n"); + printf("\t[-aes/-xor]\n"); + printf("* -pcr=N: PCR index to use (default %d)\n", TPM2_DEMO_PCR_INDEX); + printf("* -nvindex=handle: NV index (default 0x%x)\n", + TPM2_DEMO_NV_SEAL_INDEX); + printf("* -store: Store secret to NV\n"); + printf("* -read: Read secret from NV\n"); + printf("* -delete: Delete NV index (cleanup)\n"); + printf("* -ownerauth=str: Owner authorization password (default empty)\n"); + printf("* -secretstr=str: Secret to store (default TestNVSeal)\n"); + printf("* -aes/xor: Use Parameter Encryption\n"); +} + +int TPM2_NVRAM_SealNV_Example(void* userCtx, int argc, char *argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_SESSION tpmSession; + WOLFTPM2_SESSION paramEncSession; + WOLFTPM2_HANDLE parent; + WOLFTPM2_NV nv; + TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; + TPM_ALG_ID pcrAlg = TPM_ALG_SHA256; + byte pcrIndex = TPM2_DEMO_PCR_INDEX; + byte pcrArray[1]; + word32 pcrArraySz; + word32 nvIndex = TPM2_DEMO_NV_SEAL_INDEX; + const char* ownerAuth = ""; + const char* secretStr = "TestNVSeal"; + int doStore = 0; + int doRead = 0; + int doDelete = 0; + byte pcrDigest[TPM_SHA256_DIGEST_SIZE]; + int pcrDigestSz; + byte policyDigest[TPM_SHA256_DIGEST_SIZE]; + word32 policyDigestSz; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&tpmSession, 0, sizeof(tpmSession)); + XMEMSET(¶mEncSession, 0, sizeof(paramEncSession)); + XMEMSET(&parent, 0, sizeof(parent)); + XMEMSET(&nv, 0, sizeof(nv)); + + if (argc >= 2) { + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + } + while (argc > 1) { + if (XSTRNCMP(argv[argc-1], "-pcr=", XSTRLEN("-pcr=")) == 0) { + pcrIndex = (byte)XATOI(argv[argc-1] + XSTRLEN("-pcr=")); + if (pcrIndex > PCR_LAST) { + printf("PCR index out of range (0-23)\n"); + usage(); + return BAD_FUNC_ARG; + } + } + else if (XSTRNCMP(argv[argc-1], "-nvindex=", + XSTRLEN("-nvindex=")) == 0) { + nvIndex = (word32)XSTRTOUL(argv[argc-1] + XSTRLEN("-nvindex="), + NULL, 0); + } + else if (XSTRCMP(argv[argc-1], "-store") == 0) { + doStore = 1; + } + else if (XSTRCMP(argv[argc-1], "-read") == 0) { + doRead = 1; + } + else if (XSTRCMP(argv[argc-1], "-delete") == 0) { + doDelete = 1; + } + else if (XSTRNCMP(argv[argc-1], "-ownerauth=", + XSTRLEN("-ownerauth=")) == 0) { + ownerAuth = argv[argc-1] + XSTRLEN("-ownerauth="); + } + else if (XSTRNCMP(argv[argc-1], "-secretstr=", + XSTRLEN("-secretstr=")) == 0) { + secretStr = argv[argc-1] + XSTRLEN("-secretstr="); + } + else if (XSTRCMP(argv[argc-1], "-aes") == 0) { + paramEncAlg = TPM_ALG_CFB; + } + else if (XSTRCMP(argv[argc-1], "-xor") == 0) { + paramEncAlg = TPM_ALG_XOR; + } + else { + printf("Warning: Unrecognized option: %s\n", argv[argc-1]); + } + argc--; + } + + /* Default to store if nothing specified */ + if (!doStore && !doRead && !doDelete) { + doStore = 1; + } + + pcrArray[0] = pcrIndex; + pcrArraySz = 1; + + printf("TPM2.0 NV Seal Example\n"); + printf("\tPCR Index: %d\n", pcrIndex); + printf("\tNV Index: 0x%x\n", nvIndex); + printf("\tUse Parameter Encryption: %s\n", TPM2_GetAlgName(paramEncAlg)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_Init failed\n"); + goto exit; + } + + if (paramEncAlg != TPM_ALG_NULL) { + /* Start TPM session for parameter encryption */ + rc = wolfTPM2_StartSession(&dev, ¶mEncSession, NULL, NULL, + TPM_SE_HMAC, paramEncAlg); + if (rc != 0) goto exit; + printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", + (word32)paramEncSession.handle.hndl); + /* Set TPM session attributes for parameter encryption. + * Use index 2 to avoid conflict with NV handle auth on index 1 */ + rc = wolfTPM2_SetAuthSession(&dev, 2, ¶mEncSession, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + if (rc != 0) goto exit; + } + + /* Validate that the PCR bank is available */ + pcrDigestSz = (int)sizeof(pcrDigest); + rc = wolfTPM2_ReadPCR(&dev, pcrIndex, pcrAlg, pcrDigest, &pcrDigestSz); + if (rc != TPM_RC_SUCCESS) { + printf("PCR %d with %s is not available (bank may not be active)\n", + pcrIndex, TPM2_GetAlgName(pcrAlg)); + goto exit; + } + + /* Set owner auth */ + parent.hndl = TPM_RH_OWNER; + if (XSTRLEN(ownerAuth) > 0) { + parent.auth.size = (int)XSTRLEN(ownerAuth); + XMEMCPY(parent.auth.buffer, ownerAuth, parent.auth.size); + } + + /* ---- DELETE ---- */ + if (doDelete) { + printf("\nDeleting NV index 0x%x\n", nvIndex); + rc = wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_NVDeleteAuth failed 0x%x: %s\n", + rc, wolfTPM2_GetRCString(rc)); + } + else { + printf("NV index 0x%x deleted\n", nvIndex); + } + goto exit; + } + + /* ---- STORE ---- */ + if (doStore) { + WOLFTPM2_SESSION trialSession; + word32 nvAttributes; + word32 secretSz = (word32)XSTRLEN(secretStr); + + XMEMSET(&trialSession, 0, sizeof(trialSession)); + + printf("\nStoring secret (%d bytes) to NV index 0x%x\n", + secretSz, nvIndex); + + /* Step 1: Trial session to compute PCR policy digest */ + rc = wolfTPM2_StartSession(&dev, &trialSession, NULL, NULL, + TPM_SE_TRIAL, TPM_ALG_NULL); + if (rc != 0) goto exit; + + rc = wolfTPM2_PolicyPCR(&dev, trialSession.handle.hndl, + pcrAlg, pcrArray, pcrArraySz); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &trialSession.handle); + goto exit; + } + + policyDigestSz = (word32)sizeof(policyDigest); + rc = wolfTPM2_GetPolicyDigest(&dev, trialSession.handle.hndl, + policyDigest, &policyDigestSz); + wolfTPM2_UnloadHandle(&dev, &trialSession.handle); + if (rc != 0) goto exit; + + printf("PCR Policy Digest (%d bytes)\n", policyDigestSz); + + /* Step 2: Create NV index with PCR policy for read and write */ + rc = wolfTPM2_GetNvAttributesTemplate(parent.hndl, &nvAttributes); + if (rc != 0) goto exit; + /* Clear auth read/write, set policy read/write */ + nvAttributes &= ~(TPMA_NV_AUTHREAD | TPMA_NV_AUTHWRITE); + nvAttributes |= (TPMA_NV_POLICYREAD | TPMA_NV_POLICYWRITE); + + rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, nvIndex, + nvAttributes, secretSz, NULL, 0, + policyDigest, policyDigestSz); + if (rc == TPM_RC_NV_DEFINED) { + /* Delete existing and recreate with correct policy */ + printf("NV index exists, deleting and recreating\n"); + rc = wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex); + if (rc != 0) { + printf("wolfTPM2_NVDeleteAuth failed\n"); + goto exit; + } + rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, nvIndex, + nvAttributes, secretSz, NULL, 0, + policyDigest, policyDigestSz); + } + if (rc != 0) { + printf("wolfTPM2_NVCreateAuthPolicy failed\n"); + goto exit; + } + printf("Created NV index 0x%x (%d bytes)\n", nvIndex, secretSz); + + /* Step 3: Start policy session for write */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL); + if (rc != 0) goto exit; + + /* Set auth session to populate authHash for HMAC calculation. + * NVWriteAuthPolicy internally uses SetSessionHandle (partial update) + * which requires authHash to already be set in dev->session. */ + rc = wolfTPM2_SetAuthSession(&dev, 0, &tpmSession, + TPMA_SESSION_continueSession); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + goto exit; + } + + /* Write using policy session (internally calls PolicyPCR) */ + rc = wolfTPM2_NVWriteAuthPolicy(&dev, &tpmSession, pcrAlg, + pcrArray, pcrArraySz, &nv, nvIndex, + (byte*)secretStr, secretSz, 0); + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_NVWriteAuthPolicy failed\n"); + goto exit; + } + printf("Wrote %d bytes to NV index 0x%x\n", secretSz, nvIndex); + } + + /* ---- READ ---- */ + if (doRead) { + byte readBuf[MAX_SYM_DATA]; + word32 readSz; + TPMS_NV_PUBLIC nvPublic; + + printf("\nReading from NV index 0x%x\n", nvIndex); + + /* Get NV index size from public data */ + rc = wolfTPM2_NVReadPublic(&dev, nvIndex, &nvPublic); + if (rc != 0) { + printf("wolfTPM2_NVReadPublic failed\n"); + goto exit; + } + readSz = nvPublic.dataSize; + if (readSz > (word32)sizeof(readBuf)) { + readSz = (word32)sizeof(readBuf); + } + + /* Start policy session for read */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL); + if (rc != 0) goto exit; + + /* Set auth session to populate authHash for HMAC calculation */ + rc = wolfTPM2_SetAuthSession(&dev, 0, &tpmSession, + TPMA_SESSION_continueSession); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + goto exit; + } + + /* Read using policy session (internally calls PolicyPCR) */ + rc = wolfTPM2_NVReadAuthPolicy(&dev, &tpmSession, pcrAlg, + pcrArray, pcrArraySz, &nv, nvIndex, + readBuf, &readSz, 0); + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_NVReadAuthPolicy failed\n"); + goto exit; + } + + printf("Read %d bytes from NV index 0x%x: %.*s\n", + readSz, nvIndex, readSz, readBuf); + } + +exit: + if (rc != 0) { + printf("\nFailure 0x%x: %s\n\n", rc, wolfTPM2_GetRCString(rc)); + } + + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + wolfTPM2_UnloadHandle(&dev, ¶mEncSession.handle); + + wolfTPM2_Cleanup(&dev); + + return rc; +} + +/******************************************************************************/ +/* --- END TPM2.0 NV Seal Example -- */ +/******************************************************************************/ +#endif /* !WOLFTPM2_NO_WRAPPER */ + +#ifndef NO_MAIN_DRIVER +int main(int argc, char *argv[]) +{ + int rc = NOT_COMPILED_IN; + +#ifndef WOLFTPM2_NO_WRAPPER + rc = TPM2_NVRAM_SealNV_Example(NULL, argc, argv); +#else + printf("Wrapper code not compiled in\n"); + (void)argc; + (void)argv; +#endif + + return rc; +} +#endif diff --git a/examples/pcr/pcr.h b/examples/pcr/pcr.h index 80f047ba..bbdb1752 100644 --- a/examples/pcr/pcr.h +++ b/examples/pcr/pcr.h @@ -31,7 +31,6 @@ int TPM2_PCR_Extend_Test(void* userCtx, int argc, char *argv[]); int TPM2_PCR_Reset_Test(void* userCtx, int argc, char *argv[]); int TPM2_PCR_Policy_Test(void* userCtx, int argc, char *argv[]); int TPM2_PCR_PolicySign_Example(void* userCtx, int argc, char *argv[]); -int TPM2_PCR_Seal_With_Policy_Auth_Test(void* userCtx, int argc, char *argv[]); #ifdef __cplusplus } /* extern "C" */ diff --git a/examples/run_examples.sh b/examples/run_examples.sh index a6a3aef7..6ef8c4c1 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -683,6 +683,145 @@ if [ $NO_FILESYSTEM -eq 0 ]; then fi fi +# Seal/Unseal (PCR-only Policy) +echo -e "Seal/Unseal (PCR-only policy)" +if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $NO_FILESYSTEM -eq 0 ]; then + # Reset PCR 16 and extend with known value + ./examples/pcr/reset 16 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 reset failed! $RESULT" && exit 1 + echo aaa > aaa_pcr.bin + ./examples/pcr/extend 16 aaa_pcr.bin >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 extend failed! $RESULT" && exit 1 + + # Seal and unseal with PCR-only policy (should succeed) + TMPFILE=$(mktemp) + SECRET_STRING="SealPCRTestSecret" + ./examples/seal/seal_pcr -both -pcr=16 -secretstr=$SECRET_STRING &> $TMPFILE + RESULT=$? + cat $TMPFILE >> $TPMPWD/run.out + [ $RESULT -ne 0 ] && echo -e "seal_pcr both failed! $RESULT" && exit 1 + grep "$SECRET_STRING" $TMPFILE >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_pcr secret match failed! $RESULT" && exit 1 + + # Extend PCR 16 again so it changes, then try unseal (should fail) + echo bbb > bbb_pcr.bin + ./examples/pcr/extend 16 bbb_pcr.bin >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 extend (2nd) failed! $RESULT" && exit 1 + ./examples/seal/seal_pcr -unseal -pcr=16 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -eq 0 ] && echo -e "seal_pcr unseal should have failed after PCR change! $RESULT" && exit 1 + + rm -f $TMPFILE sealblob.bin aaa_pcr.bin bbb_pcr.bin +fi + +# Seal/Unseal (PolicyAuthorize - self-contained) +echo -e "Seal/Unseal (PolicyAuthorize)" +if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ] && [ $NO_FILESYSTEM -eq 0 ]; then + # Reset PCR 16 and extend with known value + ./examples/pcr/reset 16 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 reset failed! $RESULT" && exit 1 + echo aaa > aaa_pa.bin + ./examples/pcr/extend 16 aaa_pa.bin >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 extend failed! $RESULT" && exit 1 + + if [ $WOLFCRYPT_ECC -eq 1 ]; then + TMPFILE=$(mktemp) + SECRET_STRING="PolicyAuthECCSecret" + ./examples/seal/seal_policy_auth -both -ecc -pcr=16 -secretstr=$SECRET_STRING &> $TMPFILE + RESULT=$? + cat $TMPFILE >> $TPMPWD/run.out + [ $RESULT -ne 0 ] && echo -e "seal_policy_auth ecc failed! $RESULT" && exit 1 + grep "$SECRET_STRING" $TMPFILE >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_policy_auth ecc match failed! $RESULT" && exit 1 + rm -f $TMPFILE sealblob.bin authkey.bin + fi + + if [ $WOLFCRYPT_RSA -eq 1 ]; then + TMPFILE=$(mktemp) + SECRET_STRING="PolicyAuthRSASecret" + ./examples/seal/seal_policy_auth -both -rsa -pcr=16 -secretstr=$SECRET_STRING &> $TMPFILE + RESULT=$? + cat $TMPFILE >> $TPMPWD/run.out + [ $RESULT -ne 0 ] && echo -e "seal_policy_auth rsa failed! $RESULT" && exit 1 + grep "$SECRET_STRING" $TMPFILE >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_policy_auth rsa match failed! $RESULT" && exit 1 + rm -f $TMPFILE sealblob.bin authkey.bin + fi + + rm -f aaa_pa.bin +fi + +# NV Seal (PCR Policy) +echo -e "NV Seal (PCR policy)" +if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $NO_FILESYSTEM -eq 0 ]; then + # Reset PCR 16 and extend with known value + ./examples/pcr/reset 16 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 reset failed! $RESULT" && exit 1 + echo aaa > aaa_nv.bin + ./examples/pcr/extend 16 aaa_nv.bin >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr 16 extend failed! $RESULT" && exit 1 + + # Store secret to NV with PCR policy + SECRET_STRING="NVSealTestSecret" + ./examples/nvram/seal_nv -store -pcr=16 -secretstr=$SECRET_STRING >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv store failed! $RESULT" && exit 1 + + # Read back and verify + TMPFILE=$(mktemp) + ./examples/nvram/seal_nv -read -pcr=16 &> $TMPFILE + RESULT=$? + cat $TMPFILE >> $TPMPWD/run.out + [ $RESULT -ne 0 ] && echo -e "seal_nv read failed! $RESULT" && exit 1 + grep "$SECRET_STRING" $TMPFILE >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv read match failed! $RESULT" && exit 1 + + # Delete NV index (cleanup) + ./examples/nvram/seal_nv -delete >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv delete failed! $RESULT" && exit 1 + + rm -f $TMPFILE aaa_nv.bin + + # seal_nv with XOR parameter encryption + if [ $WOLFCRYPT_ENABLE -eq 1 ]; then + echo aaa > aaa_nv.bin + ./examples/pcr/reset 16 >> $TPMPWD/run.out 2>&1 + ./examples/pcr/extend 16 aaa_nv.bin >> $TPMPWD/run.out 2>&1 + + SECRET_STRING="NVSealXorTest" + ./examples/nvram/seal_nv -store -pcr=16 -xor -secretstr=$SECRET_STRING >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv store -xor failed! $RESULT" && exit 1 + + TMPFILE=$(mktemp) + ./examples/nvram/seal_nv -read -pcr=16 -xor &> $TMPFILE + RESULT=$? + cat $TMPFILE >> $TPMPWD/run.out + [ $RESULT -ne 0 ] && echo -e "seal_nv read -xor failed! $RESULT" && exit 1 + grep "$SECRET_STRING" $TMPFILE >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv read -xor match failed! $RESULT" && exit 1 + + ./examples/nvram/seal_nv -delete >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "seal_nv delete (xor) failed! $RESULT" && exit 1 + + rm -f $TMPFILE aaa_nv.bin + fi +fi + run_tpm_policy() { # Usage: run_tpm_policy [ecc/rsa] [key] [pcrs] echo -e "TPM Seal/Unseal (Policy Auth) test $1 $2 $3" diff --git a/examples/seal/README.md b/examples/seal/README.md new file mode 100644 index 00000000..ed724cf9 --- /dev/null +++ b/examples/seal/README.md @@ -0,0 +1,128 @@ +# wolfTPM Seal/Unseal Examples + +This directory contains examples demonstrating TPM 2.0 seal/unseal operations +with different authorization policies, listed from simplest to most flexible. + +## Examples + +### seal / unseal (Password Policy) + +The simplest seal/unseal using a password-based authorization policy. + +```sh +./examples/seal/seal keyblob.bin mySecretData +./examples/seal/unseal output.bin keyblob.bin +``` + +### seal_pcr (PCR-Only Policy) + +Seals a secret bound to specific PCR values. The secret can only be unsealed +when the PCR values match what was measured at seal time. No password or +signing key required. + +**Use case:** Static Root of Trust — bind secrets to a specific boot state. + +```sh +# Seal and unseal in one step +./examples/seal/seal_pcr -both -pcr=16 -secretstr="MySecret" + +# Separate seal/unseal (e.g., seal on first boot, unseal on subsequent boots) +./examples/seal/seal_pcr -seal -pcr=16 -secretstr="MySecret" +./examples/seal/seal_pcr -unseal -pcr=16 + +# With parameter encryption +./examples/seal/seal_pcr -both -pcr=16 -xor -secretstr="MySecret" +./examples/seal/seal_pcr -both -pcr=16 -aes -secretstr="MySecret" + +# Custom sealed blob filename +./examples/seal/seal_pcr -seal -sealblob=myblob.bin -secretstr="MySecret" +./examples/seal/seal_pcr -unseal -sealblob=myblob.bin +``` + +### seal_policy_auth (PolicyAuthorize + PCR) + +Seals a secret using PolicyAuthorize with a TPM-resident signing key and PCR +policy. The signing key can re-authorize the policy for new PCR values, +allowing secrets to survive authorized changes (e.g., OS updates). + +**Use case:** Flexible measured boot with authorized policy updates. + +**Note:** `authkey.bin` and `sealblob.bin` must be kept together. If the +signing key is regenerated, the sealed blob becomes un-unsealable. + +```sh +# ECC signing key (default) +./examples/seal/seal_policy_auth -both -ecc -pcr=16 -secretstr="MySecret" + +# RSA signing key +./examples/seal/seal_policy_auth -both -rsa -pcr=16 -secretstr="MySecret" + +# Separate seal/unseal +./examples/seal/seal_policy_auth -seal -ecc -pcr=16 -secretstr="MySecret" +./examples/seal/seal_policy_auth -unseal -ecc -pcr=16 + +# With parameter encryption +./examples/seal/seal_policy_auth -both -ecc -pcr=16 -xor -secretstr="MySecret" +./examples/seal/seal_policy_auth -both -rsa -pcr=16 -aes -secretstr="MySecret" +``` + +### seal_nv (NV Storage + PCR Policy) + +Stores a secret in TPM NV (non-volatile) memory protected by a PCR policy. +Unlike file-based sealed blobs, the secret lives entirely inside the TPM. +Located at `examples/nvram/seal_nv`. + +**Use case:** Secrets that must persist in TPM hardware without external files. + +```sh +# Store, read, delete lifecycle +./examples/nvram/seal_nv -store -pcr=16 -secretstr="MySecret" +./examples/nvram/seal_nv -read -pcr=16 +./examples/nvram/seal_nv -delete + +# Custom NV index +./examples/nvram/seal_nv -store -pcr=16 -nvindex=0x01800204 -secretstr="MySecret" +./examples/nvram/seal_nv -read -pcr=16 -nvindex=0x01800204 +./examples/nvram/seal_nv -delete -nvindex=0x01800204 +``` + +## Testing + +### Standalone Test Script + +`seal_test.sh` runs 28 tests across all three seal example groups: + +```sh +bash examples/seal/seal_test.sh +``` + +Tests include positive cases (seal/unseal lifecycle, secret verification), +negative cases (PCR mismatch, missing auth key), parameter encryption variants +(XOR, AES), and custom filenames/NV indices. + +Output uses colored PASS/FAIL/SKIP with a summary. Verbose output is saved +to `seal_test.log`. + +#### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `WOLFCRYPT_ENABLE` | 1 | wolfCrypt support compiled in | +| `WOLFCRYPT_DEFAULT` | 0 | Using default (reduced) wolfCrypt config | +| `WOLFCRYPT_ECC` | 1 | ECC support available | +| `WOLFCRYPT_RSA` | 1 | RSA support available | + +### Integration Tests + +The seal examples are also tested as part of `examples/run_examples.sh` +which runs during `make check`. + +## Policy Comparison + +| Feature | seal (password) | seal_pcr | seal_policy_auth | seal_nv | +|---------|----------------|----------|-----------------|---------| +| Authorization | Password | PCR values | Signing key + PCR | PCR values | +| Complexity | Low | Low | High | Medium | +| Survives PCR change | N/A | No | Yes (with auth key) | No | +| Storage | File | File | File (blob + key) | TPM NV | +| Parameter Encryption | Yes | Yes | Yes | Yes | diff --git a/examples/seal/include.am b/examples/seal/include.am index 52bb2bd9..c4ffc225 100644 --- a/examples/seal/include.am +++ b/examples/seal/include.am @@ -3,7 +3,9 @@ if BUILD_EXAMPLES noinst_PROGRAMS += examples/seal/seal \ - examples/seal/unseal + examples/seal/unseal \ + examples/seal/seal_pcr \ + examples/seal/seal_policy_auth noinst_HEADERS += examples/seal/seal.h @@ -16,13 +18,28 @@ examples_seal_unseal_SOURCES = examples/seal/unseal.c \ examples/tpm_test_keys.c examples_seal_unseal_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_seal_unseal_DEPENDENCIES = src/libwolftpm.la + +examples_seal_seal_pcr_SOURCES = examples/seal/seal_pcr.c \ + examples/tpm_test_keys.c +examples_seal_seal_pcr_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) +examples_seal_seal_pcr_DEPENDENCIES = src/libwolftpm.la + +examples_seal_seal_policy_auth_SOURCES = examples/seal/seal_policy_auth.c \ + examples/tpm_test_keys.c +examples_seal_seal_policy_auth_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) +examples_seal_seal_policy_auth_DEPENDENCIES = src/libwolftpm.la endif example_sealdir = $(exampledir)/seal dist_example_seal_DATA = \ examples/seal/seal.c \ - examples/seal/unseal.c + examples/seal/unseal.c \ + examples/seal/seal_pcr.c \ + examples/seal/seal_policy_auth.c DISTCLEANFILES+= examples/seal/.libs/seal DISTCLEANFILES+= examples/seal/.libs/unseal +DISTCLEANFILES+= examples/seal/.libs/seal_pcr +DISTCLEANFILES+= examples/seal/.libs/seal_policy_auth +EXTRA_DIST += examples/seal/seal_test.sh diff --git a/examples/seal/seal.h b/examples/seal/seal.h index 415ddcdb..6d383394 100644 --- a/examples/seal/seal.h +++ b/examples/seal/seal.h @@ -28,7 +28,8 @@ int TPM2_Seal_Example(void* userCtx, int argc, char *argv[]); int TPM2_Unseal_Example(void* userCtx, int argc, char *argv[]); -int TPM2_PCR_Seal_With_Policy_Auth_Test(void* userCtx, int argc, char *argv[]); +int TPM2_Seal_PCR_Example(void* userCtx, int argc, char *argv[]); +int TPM2_Seal_PolicyAuth_Example(void* userCtx, int argc, char *argv[]); #ifdef __cplusplus } /* extern "C" */ diff --git a/examples/seal/seal_pcr.c b/examples/seal/seal_pcr.c new file mode 100644 index 00000000..1dbaf6ad --- /dev/null +++ b/examples/seal/seal_pcr.c @@ -0,0 +1,363 @@ +/* seal_pcr.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Example for TPM 2.0 PCR policy seal/unseal. + * + * Seals a secret that can only be unsealed when specific PCR values match. + * No password, no signing key - simplest "measured boot" scenario. + * + * Sealing method: PCR-only policy + * Complexity: Low + * Flexibility: Low (PCR values are hard-coded at seal time) + * Use case: Static Root of Trust - bind secrets to a specific boot state + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#include + +#ifndef WOLFTPM2_NO_WRAPPER + +#include +#include +#include +#include + +/******************************************************************************/ +/* --- BEGIN TPM2.0 PCR Seal Example -- */ +/******************************************************************************/ + +static void usage(void) +{ + printf("Expected usage:\n"); + printf("./examples/seal/seal_pcr [-pcr=N] [-seal/-unseal/-both]\n"); + printf("\t[-sealblob=file] [-secretstr=str] [-aes/-xor]\n"); + printf("* -pcr=N: PCR index to use (default %d)\n", TPM2_DEMO_PCR_INDEX); + printf("* -seal: Seal only\n"); + printf("* -unseal: Unseal only\n"); + printf("* -both: Seal then unseal (default)\n"); + printf("* -sealblob=file: Sealed blob filename (default sealblob.bin)\n"); + printf("* -secretstr=str: Secret string to seal (default TestSealPCR)\n"); + printf("* -aes/xor: Use Parameter Encryption\n"); +} + +int TPM2_Seal_PCR_Example(void* userCtx, int argc, char *argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY storage; + WOLFTPM2_KEYBLOB sealBlob; + WOLFTPM2_SESSION tpmSession; + TPMT_PUBLIC sealTemplate; + TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; + TPM_ALG_ID pcrAlg = TPM_ALG_SHA256; + byte pcrIndex = TPM2_DEMO_PCR_INDEX; + byte pcrArray[1]; + word32 pcrArraySz; + const char* sealFile = "sealblob.bin"; + const char* secretStr = "TestSealPCR"; + int doSeal = 0; + int doUnseal = 0; + byte policyDigest[TPM_SHA256_DIGEST_SIZE]; + word32 policyDigestSz; + byte pcrDigest[TPM_SHA256_DIGEST_SIZE]; + int pcrDigestSz; + Unseal_In unsealIn; + Unseal_Out unsealOut; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&storage, 0, sizeof(storage)); + XMEMSET(&sealBlob, 0, sizeof(sealBlob)); + XMEMSET(&tpmSession, 0, sizeof(tpmSession)); + XMEMSET(&unsealIn, 0, sizeof(unsealIn)); + XMEMSET(&unsealOut, 0, sizeof(unsealOut)); + + if (argc >= 2) { + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + } + while (argc > 1) { + if (XSTRNCMP(argv[argc-1], "-pcr=", XSTRLEN("-pcr=")) == 0) { + pcrIndex = (byte)XATOI(argv[argc-1] + XSTRLEN("-pcr=")); + if (pcrIndex > PCR_LAST) { + printf("PCR index out of range (0-23)\n"); + usage(); + return BAD_FUNC_ARG; + } + } + else if (XSTRCMP(argv[argc-1], "-seal") == 0) { + doSeal = 1; + } + else if (XSTRCMP(argv[argc-1], "-unseal") == 0) { + doUnseal = 1; + } + else if (XSTRCMP(argv[argc-1], "-both") == 0) { + doSeal = 1; + doUnseal = 1; + } + else if (XSTRNCMP(argv[argc-1], "-sealblob=", + XSTRLEN("-sealblob=")) == 0) { + sealFile = argv[argc-1] + XSTRLEN("-sealblob="); + } + else if (XSTRNCMP(argv[argc-1], "-secretstr=", + XSTRLEN("-secretstr=")) == 0) { + secretStr = argv[argc-1] + XSTRLEN("-secretstr="); + } + else if (XSTRCMP(argv[argc-1], "-aes") == 0) { + paramEncAlg = TPM_ALG_CFB; + } + else if (XSTRCMP(argv[argc-1], "-xor") == 0) { + paramEncAlg = TPM_ALG_XOR; + } + else { + printf("Warning: Unrecognized option: %s\n", argv[argc-1]); + } + argc--; + } + + /* Default to both seal and unseal */ + if (!doSeal && !doUnseal) { + doSeal = 1; + doUnseal = 1; + } + + pcrArray[0] = pcrIndex; + pcrArraySz = 1; + + printf("TPM2.0 PCR Seal Example\n"); + printf("\tPCR Index: %d\n", pcrIndex); + printf("\tSeal Blob: %s\n", sealFile); + printf("\tUse Parameter Encryption: %s\n", TPM2_GetAlgName(paramEncAlg)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_Init failed\n"); + goto exit; + } + + /* Validate that the PCR bank is available */ + pcrDigestSz = (int)sizeof(pcrDigest); + rc = wolfTPM2_ReadPCR(&dev, pcrIndex, pcrAlg, pcrDigest, &pcrDigestSz); + if (rc != TPM_RC_SUCCESS) { + printf("PCR %d with %s is not available (bank may not be active)\n", + pcrIndex, TPM2_GetAlgName(pcrAlg)); + goto exit; + } + + /* Get SRK */ + rc = getPrimaryStoragekey(&dev, &storage, TPM_ALG_RSA); + if (rc != 0) goto exit; + + if (paramEncAlg != TPM_ALG_NULL) { + /* Start an authenticated session (salted / unbound) with param enc */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, &storage, NULL, + TPM_SE_HMAC, paramEncAlg); + if (rc != 0) goto exit; + printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", + (word32)tpmSession.handle.hndl); + rc = wolfTPM2_SetAuthSession(&dev, 1, &tpmSession, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + if (rc != 0) goto exit; + } + + /* ---- SEAL ---- */ + if (doSeal) { + WOLFTPM2_SESSION trialSession; + XMEMSET(&trialSession, 0, sizeof(trialSession)); + + printf("\nSealing secret: %s\n", secretStr); + + /* Step 1: Trial session to compute PCR policy digest */ + rc = wolfTPM2_StartSession(&dev, &trialSession, NULL, NULL, + TPM_SE_TRIAL, TPM_ALG_NULL); + if (rc != 0) goto exit; + + rc = wolfTPM2_PolicyPCR(&dev, trialSession.handle.hndl, + pcrAlg, pcrArray, pcrArraySz); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &trialSession.handle); + goto exit; + } + + policyDigestSz = (word32)sizeof(policyDigest); + rc = wolfTPM2_GetPolicyDigest(&dev, trialSession.handle.hndl, + policyDigest, &policyDigestSz); + wolfTPM2_UnloadHandle(&dev, &trialSession.handle); + if (rc != 0) goto exit; + + printf("Policy Digest (%d bytes)\n", policyDigestSz); + + /* Step 2: Create seal template with PCR policy */ + wolfTPM2_GetKeyTemplate_KeySeal(&sealTemplate, pcrAlg); + /* Do NOT set TPMA_OBJECT_userWithAuth - policy-only access */ + sealTemplate.authPolicy.size = policyDigestSz; + XMEMCPY(sealTemplate.authPolicy.buffer, policyDigest, policyDigestSz); + + /* Step 3: Create sealed blob */ + rc = wolfTPM2_CreateKeySeal_ex(&dev, &sealBlob, &storage.handle, + &sealTemplate, NULL, 0, pcrAlg, pcrArray, pcrArraySz, + (const byte*)secretStr, (int)XSTRLEN(secretStr)); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_CreateKeySeal_ex failed\n"); + goto exit; + } + printf("Created sealed blob (pub %d, priv %d bytes)\n", + sealBlob.pub.size, sealBlob.priv.size); + + #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) + rc = writeKeyBlob(sealFile, &sealBlob); + if (rc != 0) goto exit; + printf("Sealed blob written to: %s\n", sealFile); + #else + printf("Filesystem support not available for saving sealed blob\n"); + (void)sealFile; + #endif + } + + /* ---- UNSEAL ---- */ + if (doUnseal) { + WOLFTPM2_SESSION policySession; + XMEMSET(&policySession, 0, sizeof(policySession)); + + printf("\nUnsealing secret...\n"); + + /* Clear param enc session from seal phase */ + if (paramEncAlg != TPM_ALG_NULL) { + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + wolfTPM2_UnsetAuth(&dev, 1); + } + + /* Step 1: Load sealed blob from disk */ + if (!doSeal) { + /* If we didn't just seal, load from file */ + #if !defined(NO_FILESYSTEM) + rc = readKeyBlob(sealFile, &sealBlob); + if (rc != 0) goto exit; + #else + printf("Filesystem support not available for loading sealed blob\n"); + rc = NOT_COMPILED_IN; + goto exit; + #endif + } + + /* Step 2: Load sealed blob into TPM */ + rc = wolfTPM2_LoadKey(&dev, &sealBlob, &storage.handle); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_LoadKey failed\n"); + goto exit; + } + printf("Loaded sealed blob to 0x%x\n", (word32)sealBlob.handle.hndl); + + /* Step 3: Start policy session and satisfy PCR policy. + * If param enc is requested, create a salted policy session that + * handles both authorization and parameter encryption. */ + rc = wolfTPM2_StartSession(&dev, &policySession, + (paramEncAlg != TPM_ALG_NULL) ? &storage : NULL, NULL, + TPM_SE_POLICY, paramEncAlg); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + rc = wolfTPM2_PolicyPCR(&dev, policySession.handle.hndl, + pcrAlg, pcrArray, pcrArraySz); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + /* Step 4: Use policy session for unseal (with param enc if set) */ + { + word32 sessionAttrs = TPMA_SESSION_continueSession; + if (paramEncAlg != TPM_ALG_NULL) { + sessionAttrs |= (TPMA_SESSION_decrypt | + TPMA_SESSION_encrypt); + } + rc = wolfTPM2_SetAuthSession(&dev, 0, &policySession, + sessionAttrs); + } + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + wolfTPM2_SetAuthHandleName(&dev, 0, &sealBlob.handle); + + unsealIn.itemHandle = sealBlob.handle.hndl; + rc = TPM2_Unseal(&unsealIn, &unsealOut); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + + if (rc != TPM_RC_SUCCESS) { + printf("TPM2_Unseal failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + goto exit; + } + + printf("Unsealed secret (%d bytes): %.*s\n", + unsealOut.outData.size, + unsealOut.outData.size, unsealOut.outData.buffer); + } + +exit: + if (rc != 0) { + printf("\nFailure 0x%x: %s\n\n", rc, wolfTPM2_GetRCString(rc)); + } + + wolfTPM2_UnloadHandle(&dev, &storage.handle); + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + + wolfTPM2_Cleanup(&dev); + + return rc; +} + +/******************************************************************************/ +/* --- END TPM2.0 PCR Seal Example -- */ +/******************************************************************************/ +#endif /* !WOLFTPM2_NO_WRAPPER */ + +#ifndef NO_MAIN_DRIVER +int main(int argc, char *argv[]) +{ + int rc = NOT_COMPILED_IN; + +#ifndef WOLFTPM2_NO_WRAPPER + rc = TPM2_Seal_PCR_Example(NULL, argc, argv); +#else + printf("Wrapper code not compiled in\n"); + (void)argc; + (void)argv; +#endif + + return rc; +} +#endif diff --git a/examples/seal/seal_policy_auth.c b/examples/seal/seal_policy_auth.c new file mode 100644 index 00000000..662fd949 --- /dev/null +++ b/examples/seal/seal_policy_auth.c @@ -0,0 +1,530 @@ +/* seal_policy_auth.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Self-contained PolicyAuthorize seal/unseal example. + * + * Creates a TPM-internal signing key, seals a secret with PolicyAuthorize + * + PCR policy, then unseals using the same key. No pre-existing signing + * key is required — the TPM generates one internally. However, the key + * blob file (authkey.bin) must be retained for subsequent unseal operations. + * + * Sealing method: PolicyAuthorize (with PCR policy) + * Complexity: High + * Flexibility: High (authorized updates - e.g., OS updates that change + * PCRs but are signed by a trusted key) + * Use case: Flexible measured boot with authorized policy updates + * + * IMPORTANT: The signing key blob (authkey.bin) MUST be kept alongside + * the sealed blob (sealblob.bin). If the signing key is regenerated + * (even with the same template), the TPM Name changes and the sealed + * blob becomes un-unsealable. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#include + +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_WOLFCRYPT) + +#include +#include +#include +#include + +/******************************************************************************/ +/* --- BEGIN TPM2.0 PolicyAuthorize Seal Example -- */ +/******************************************************************************/ + +static void usage(void) +{ + printf("Expected usage:\n"); + printf("./examples/seal/seal_policy_auth [-ecc/-rsa] [-pcr=N]\n"); + printf("\t[-seal/-unseal/-both] [-sealblob=file] [-authkey=file]\n"); + printf("\t[-secretstr=str] [-aes/-xor]\n"); + printf("* -ecc/-rsa: Signing key type (default ECC)\n"); + printf("* -pcr=N: PCR index to use (default %d)\n", TPM2_DEMO_PCR_INDEX); + printf("* -seal: Seal only\n"); + printf("* -unseal: Unseal only\n"); + printf("* -both: Seal then unseal (default)\n"); + printf("* -sealblob=file: Sealed blob filename (default sealblob.bin)\n"); + printf("* -authkey=file: Auth key blob filename (default authkey.bin)\n"); + printf("* -secretstr=str: Secret string to seal (default TestPolicyAuth)\n"); + printf("* -aes/xor: Use Parameter Encryption\n"); + printf("\nNOTE: authkey.bin and sealblob.bin must be kept together.\n"); +} + +int TPM2_Seal_PolicyAuth_Example(void* userCtx, int argc, char *argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY storage; + WOLFTPM2_KEYBLOB authKeyBlob; + WOLFTPM2_KEYBLOB sealBlob; + WOLFTPM2_SESSION tpmSession; + TPMT_PUBLIC sealTemplate; + TPM_ALG_ID paramEncAlg = TPM_ALG_NULL; + TPM_ALG_ID alg = TPM_ALG_ECC; + TPM_ALG_ID pcrAlg = TPM_ALG_SHA256; + byte pcrIndex = TPM2_DEMO_PCR_INDEX; + byte pcrArray[1]; + word32 pcrArraySz; + const char* sealFile = "sealblob.bin"; + const char* authKeyFile = "authkey.bin"; + const char* secretStr = "TestPolicyAuth"; + int doSeal = 0; + int doUnseal = 0; + byte policyDigest[TPM_SHA256_DIGEST_SIZE]; + word32 policyDigestSz; + Unseal_In unsealIn; + Unseal_Out unsealOut; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&storage, 0, sizeof(storage)); + XMEMSET(&authKeyBlob, 0, sizeof(authKeyBlob)); + XMEMSET(&sealBlob, 0, sizeof(sealBlob)); + XMEMSET(&tpmSession, 0, sizeof(tpmSession)); + XMEMSET(&unsealIn, 0, sizeof(unsealIn)); + XMEMSET(&unsealOut, 0, sizeof(unsealOut)); + + if (argc >= 2) { + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + } + while (argc > 1) { + if (XSTRCMP(argv[argc-1], "-ecc") == 0) { + alg = TPM_ALG_ECC; + } + else if (XSTRCMP(argv[argc-1], "-rsa") == 0) { + alg = TPM_ALG_RSA; + } + else if (XSTRNCMP(argv[argc-1], "-pcr=", XSTRLEN("-pcr=")) == 0) { + pcrIndex = (byte)XATOI(argv[argc-1] + XSTRLEN("-pcr=")); + if (pcrIndex > PCR_LAST) { + printf("PCR index out of range (0-23)\n"); + usage(); + return BAD_FUNC_ARG; + } + } + else if (XSTRCMP(argv[argc-1], "-seal") == 0) { + doSeal = 1; + } + else if (XSTRCMP(argv[argc-1], "-unseal") == 0) { + doUnseal = 1; + } + else if (XSTRCMP(argv[argc-1], "-both") == 0) { + doSeal = 1; + doUnseal = 1; + } + else if (XSTRNCMP(argv[argc-1], "-sealblob=", + XSTRLEN("-sealblob=")) == 0) { + sealFile = argv[argc-1] + XSTRLEN("-sealblob="); + } + else if (XSTRNCMP(argv[argc-1], "-authkey=", + XSTRLEN("-authkey=")) == 0) { + authKeyFile = argv[argc-1] + XSTRLEN("-authkey="); + } + else if (XSTRNCMP(argv[argc-1], "-secretstr=", + XSTRLEN("-secretstr=")) == 0) { + secretStr = argv[argc-1] + XSTRLEN("-secretstr="); + } + else if (XSTRCMP(argv[argc-1], "-aes") == 0) { + paramEncAlg = TPM_ALG_CFB; + } + else if (XSTRCMP(argv[argc-1], "-xor") == 0) { + paramEncAlg = TPM_ALG_XOR; + } + else { + printf("Warning: Unrecognized option: %s\n", argv[argc-1]); + } + argc--; + } + + /* Default to both seal and unseal */ + if (!doSeal && !doUnseal) { + doSeal = 1; + doUnseal = 1; + } + + pcrArray[0] = pcrIndex; + pcrArraySz = 1; + + printf("TPM2.0 PolicyAuthorize Seal Example\n"); + printf("\tKey Type: %s\n", TPM2_GetAlgName(alg)); + printf("\tPCR Index: %d\n", pcrIndex); + printf("\tSeal Blob: %s\n", sealFile); + printf("\tAuth Key: %s\n", authKeyFile); + printf("\tUse Parameter Encryption: %s\n", TPM2_GetAlgName(paramEncAlg)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_Init failed\n"); + goto exit; + } + + /* Get SRK */ + rc = getPrimaryStoragekey(&dev, &storage, TPM_ALG_RSA); + if (rc != 0) goto exit; + + if (paramEncAlg != TPM_ALG_NULL) { + /* Start an authenticated session (salted / unbound) with param enc */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, &storage, NULL, + TPM_SE_HMAC, paramEncAlg); + if (rc != 0) goto exit; + printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", + (word32)tpmSession.handle.hndl); + rc = wolfTPM2_SetAuthSession(&dev, 1, &tpmSession, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + if (rc != 0) goto exit; + } + + /* ---- SEAL ---- */ + if (doSeal) { + TPMT_PUBLIC keyTemplate; + + printf("\nSealing secret: %s\n", secretStr); + + /* Step 1: Create signing key under SRK */ + if (alg == TPM_ALG_ECC) { + rc = wolfTPM2_GetKeyTemplate_ECC(&keyTemplate, + TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_sign | TPMA_OBJECT_noDA, + TPM_ECC_NIST_P256, TPM_ALG_ECDSA); + } + else { + rc = wolfTPM2_GetKeyTemplate_RSA(&keyTemplate, + TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_sign | TPMA_OBJECT_noDA); + keyTemplate.parameters.rsaDetail.scheme.scheme = TPM_ALG_RSASSA; + keyTemplate.parameters.rsaDetail.scheme.details.rsassa.hashAlg = + TPM_ALG_SHA256; + } + if (rc != 0) goto exit; + + rc = wolfTPM2_CreateKey(&dev, &authKeyBlob, &storage.handle, + &keyTemplate, (const byte*)gKeyAuth, + (int)sizeof(gKeyAuth) - 1); + if (rc != 0) { + printf("wolfTPM2_CreateKey (auth key) failed\n"); + goto exit; + } + /* Set auth on handle after create (CreateKey zeroes the keyBlob) */ + authKeyBlob.handle.auth.size = (int)sizeof(gKeyAuth) - 1; + XMEMCPY(authKeyBlob.handle.auth.buffer, gKeyAuth, + authKeyBlob.handle.auth.size); + printf("Created %s signing key (pub %d, priv %d bytes)\n", + TPM2_GetAlgName(alg), authKeyBlob.pub.size, authKeyBlob.priv.size); + + #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) + /* Save key blob to disk - MUST keep alongside sealed blob */ + rc = writeKeyBlob(authKeyFile, &authKeyBlob); + if (rc != 0) goto exit; + printf("Auth key blob written to: %s\n", authKeyFile); + #else + printf("Filesystem support not available for saving auth key\n"); + (void)authKeyFile; + #endif + + /* Step 2: Compute PolicyAuthorize digest from key's public area */ + policyDigestSz = TPM2_GetHashDigestSize(pcrAlg); + XMEMSET(policyDigest, 0, sizeof(policyDigest)); + rc = wolfTPM2_PolicyAuthorizeMake(pcrAlg, &authKeyBlob.pub, + policyDigest, &policyDigestSz, NULL, 0); + if (rc != 0) { + printf("wolfTPM2_PolicyAuthorizeMake failed\n"); + goto exit; + } + printf("PolicyAuthorize Digest (%d bytes)\n", policyDigestSz); + + /* Step 3: Create seal template with PolicyAuthorize digest */ + wolfTPM2_GetKeyTemplate_KeySeal(&sealTemplate, pcrAlg); + /* Do NOT set TPMA_OBJECT_userWithAuth - policy-only access */ + sealTemplate.authPolicy.size = policyDigestSz; + XMEMCPY(sealTemplate.authPolicy.buffer, policyDigest, policyDigestSz); + + /* Step 4: Create sealed blob */ + rc = wolfTPM2_CreateKeySeal_ex(&dev, &sealBlob, &storage.handle, + &sealTemplate, NULL, 0, pcrAlg, NULL, 0, + (const byte*)secretStr, (int)XSTRLEN(secretStr)); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_CreateKeySeal_ex failed\n"); + goto exit; + } + printf("Created sealed blob (pub %d, priv %d bytes)\n", + sealBlob.pub.size, sealBlob.priv.size); + + #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) + rc = writeKeyBlob(sealFile, &sealBlob); + if (rc != 0) goto exit; + printf("Sealed blob written to: %s\n", sealFile); + #else + printf("Filesystem support not available for saving sealed blob\n"); + (void)sealFile; + #endif + } + + /* ---- UNSEAL ---- */ + if (doUnseal) { + WOLFTPM2_SESSION policySession; + byte pcrDigest[TPM_SHA256_DIGEST_SIZE]; + word32 pcrDigestSz; + byte sig[512]; /* up to 4096-bit key */ + int sigSz; + TPMT_TK_VERIFIED checkTicket; + TPMI_ALG_SIG_SCHEME sigAlg; + byte* policyRef = NULL; + word32 policyRefSz = 0; + + XMEMSET(&policySession, 0, sizeof(policySession)); + XMEMSET(&checkTicket, 0, sizeof(checkTicket)); + + printf("\nUnsealing secret...\n"); + + /* Clear param enc session from seal phase (nonces are stale). + * A fresh session will be started before unseal if needed. */ + if (paramEncAlg != TPM_ALG_NULL) { + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + wolfTPM2_UnsetAuth(&dev, 1); + } + + /* Step 1: Load auth key blob */ + if (!doSeal) { + /* If we didn't just seal, load from file */ + #if !defined(NO_FILESYSTEM) + rc = readKeyBlob(authKeyFile, &authKeyBlob); + if (rc != 0) { + printf("Error loading auth key blob from: %s\n", authKeyFile); + goto exit; + } + #else + printf("Filesystem support not available\n"); + rc = NOT_COMPILED_IN; + goto exit; + #endif + } + + /* Set auth for the key */ + authKeyBlob.handle.auth.size = (int)sizeof(gKeyAuth) - 1; + XMEMCPY(authKeyBlob.handle.auth.buffer, gKeyAuth, + authKeyBlob.handle.auth.size); + + /* Load auth key into TPM */ + rc = wolfTPM2_LoadKey(&dev, &authKeyBlob, &storage.handle); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_LoadKey (auth key) failed\n"); + goto exit; + } + printf("Loaded auth key to 0x%x\n", (word32)authKeyBlob.handle.hndl); + + /* Step 2: Load sealed blob */ + if (!doSeal) { + #if !defined(NO_FILESYSTEM) + rc = readKeyBlob(sealFile, &sealBlob); + if (rc != 0) { + printf("Error loading sealed blob from: %s\n", sealFile); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + goto exit; + } + #else + rc = NOT_COMPILED_IN; + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + goto exit; + #endif + } + + rc = wolfTPM2_LoadKey(&dev, &sealBlob, &storage.handle); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_LoadKey (seal blob) failed\n"); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + goto exit; + } + printf("Loaded sealed blob to 0x%x\n", (word32)sealBlob.handle.hndl); + + /* Step 3: Start policy session and apply PCR policy. + * If param enc is requested, create a salted policy session that + * handles both authorization and parameter encryption. */ + rc = wolfTPM2_StartSession(&dev, &policySession, + (paramEncAlg != TPM_ALG_NULL) ? &storage : NULL, NULL, + TPM_SE_POLICY, paramEncAlg); + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + rc = wolfTPM2_PolicyPCR(&dev, policySession.handle.hndl, + pcrAlg, pcrArray, pcrArraySz); + if (rc != 0) { + printf("wolfTPM2_PolicyPCR failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + /* Step 4: Get policy digest from session */ + pcrDigestSz = (word32)sizeof(pcrDigest); + rc = wolfTPM2_GetPolicyDigest(&dev, policySession.handle.hndl, + pcrDigest, &pcrDigestSz); + if (rc != 0) { + printf("wolfTPM2_GetPolicyDigest failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + printf("PCR Policy Digest (%d bytes)\n", pcrDigestSz); + + /* Step 5: Hash digest with policyRef (empty) */ + policyDigestSz = pcrDigestSz; + XMEMCPY(policyDigest, pcrDigest, pcrDigestSz); + rc = wolfTPM2_PolicyRefMake(pcrAlg, policyDigest, &policyDigestSz, + policyRef, policyRefSz); + if (rc != 0) { + printf("wolfTPM2_PolicyRefMake failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + /* Step 6: Sign the policy digest with the TPM auth key */ + wolfTPM2_SetAuthHandle(&dev, 0, &authKeyBlob.handle); + sigSz = (int)sizeof(sig); + rc = wolfTPM2_SignHash(&dev, (WOLFTPM2_KEY*)&authKeyBlob, + policyDigest, policyDigestSz, sig, &sigSz); + if (rc != 0) { + printf("wolfTPM2_SignHash failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + printf("Signed policy digest (%d bytes)\n", sigSz); + + /* Step 7: Verify the signature and get a ticket from the TPM */ + sigAlg = (alg == TPM_ALG_RSA) ? TPM_ALG_RSASSA : TPM_ALG_ECDSA; + rc = wolfTPM2_VerifyHashTicket(&dev, (WOLFTPM2_KEY*)&authKeyBlob, + sig, sigSz, policyDigest, policyDigestSz, sigAlg, pcrAlg, + &checkTicket); + if (rc != 0) { + printf("wolfTPM2_VerifyHashTicket failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + printf("Verify ticket: tag 0x%x, hi 0x%x\n", + checkTicket.tag, checkTicket.hierarchy); + + /* Step 8: PolicyAuthorize with the ticket */ + rc = wolfTPM2_PolicyAuthorize(&dev, policySession.handle.hndl, + &authKeyBlob.pub, &checkTicket, pcrDigest, pcrDigestSz, + policyRef, policyRefSz); + if (rc != 0) { + printf("wolfTPM2_PolicyAuthorize failed\n"); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + + /* Done with auth key */ + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + + /* Step 9: Unseal using the policy session (with param enc if set) */ + { + word32 sessionAttrs = TPMA_SESSION_continueSession; + if (paramEncAlg != TPM_ALG_NULL) { + sessionAttrs |= (TPMA_SESSION_decrypt | + TPMA_SESSION_encrypt); + } + rc = wolfTPM2_SetAuthSession(&dev, 0, &policySession, + sessionAttrs); + } + if (rc != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + goto exit; + } + wolfTPM2_SetAuthHandleName(&dev, 0, &sealBlob.handle); + + unsealIn.itemHandle = sealBlob.handle.hndl; + rc = TPM2_Unseal(&unsealIn, &unsealOut); + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + + if (rc != TPM_RC_SUCCESS) { + printf("TPM2_Unseal failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + goto exit; + } + + printf("Unsealed secret (%d bytes): %.*s\n", + unsealOut.outData.size, + unsealOut.outData.size, unsealOut.outData.buffer); + } + +exit: + if (rc != 0) { + printf("\nFailure 0x%x: %s\n\n", rc, wolfTPM2_GetRCString(rc)); + } + + wolfTPM2_UnloadHandle(&dev, &authKeyBlob.handle); + wolfTPM2_UnloadHandle(&dev, &sealBlob.handle); + wolfTPM2_UnloadHandle(&dev, &storage.handle); + wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); + + wolfTPM2_Cleanup(&dev); + + return rc; +} + +/******************************************************************************/ +/* --- END TPM2.0 PolicyAuthorize Seal Example -- */ +/******************************************************************************/ +#endif /* !WOLFTPM2_NO_WRAPPER && !WOLFTPM2_NO_WOLFCRYPT */ + +#ifndef NO_MAIN_DRIVER +int main(int argc, char *argv[]) +{ + int rc = NOT_COMPILED_IN; + +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_WOLFCRYPT) + rc = TPM2_Seal_PolicyAuth_Example(NULL, argc, argv); +#else + printf("Example not compiled in! Requires Wrapper and wolfCrypt\n"); + (void)argc; + (void)argv; +#endif + + return rc; +} +#endif diff --git a/examples/seal/seal_test.sh b/examples/seal/seal_test.sh new file mode 100755 index 00000000..32df9759 --- /dev/null +++ b/examples/seal/seal_test.sh @@ -0,0 +1,288 @@ +#!/bin/bash +# wolfTPM seal/unseal standalone test script. +# Exercises seal_pcr, seal_policy_auth, and seal_nv with full lifecycle. +# Usage: bash examples/seal/seal_test.sh (from wolfTPM root) + +PASS=0; FAIL=0; SKIP=0 +LOGFILE="seal_test.log" +rm -f "$LOGFILE"; touch "$LOGFILE" +CLEANUP_FILES="" + +# Colors (terminal-aware) +if [ -t 1 ]; then + GRN="\033[32m"; RED="\033[31m"; YLW="\033[33m"; RST="\033[0m"; BLD="\033[1m" +else + GRN=""; RED=""; YLW=""; RST=""; BLD="" +fi + +# Feature gating (same defaults as run_examples.sh) +: "${WOLFCRYPT_ENABLE:=1}" "${WOLFCRYPT_DEFAULT:=0}" +: "${WOLFCRYPT_ECC:=1}" "${WOLFCRYPT_RSA:=1}" + +cleanup() { + for f in $CLEANUP_FILES; do rm -f "$f"; done + rm -f sealblob.bin authkey.bin custom_seal.bin aaa_pcr16.bin bbb_pcr16.bin + if [ -x ./examples/nvram/seal_nv ]; then + ./examples/nvram/seal_nv -delete >> "$LOGFILE" 2>&1 + ./examples/nvram/seal_nv -delete -nvindex=0x01800204 >> "$LOGFILE" 2>&1 + fi +} +trap cleanup EXIT + +pass() { echo -e " ${GRN}PASS${RST} - $1"; PASS=$((PASS + 1)); } +fail() { echo -e " ${RED}FAIL${RST} - $1"; FAIL=$((FAIL + 1)); } +skip() { echo -e " ${YLW}SKIP${RST} - $1"; SKIP=$((SKIP + 1)); } + +# run_test [secret_to_verify] -- +# If secret_to_verify is provided (non-empty before --), greps output for it. +run_test() { + local desc="$1" expect="$2"; shift 2 + local secret="" + if [ "$1" != "--" ]; then secret="$1"; shift; fi + shift # consume -- + + local tmpout="seal_test_tmp_$$.out" + CLEANUP_FILES="$CLEANUP_FILES $tmpout" + echo "=== $desc ===" >> "$LOGFILE" + echo "CMD: $*" >> "$LOGFILE" + "$@" > "$tmpout" 2>&1; local rc=$? + cat "$tmpout" >> "$LOGFILE" + echo "EXIT: $rc" >> "$LOGFILE" + + if [ "$expect" = "expect_pass" ]; then + if [ $rc -ne 0 ]; then + fail "$desc (exit code: $rc)"; rm -f "$tmpout"; return 1 + fi + if [ -n "$secret" ] && ! grep -F -q -- "$secret" "$tmpout"; then + fail "$desc (secret not found in output)"; rm -f "$tmpout"; return 1 + fi + pass "$desc"; rm -f "$tmpout"; return 0 + else + if [ $rc -ne 0 ]; then pass "$desc"; else fail "$desc (expected failure)"; fi + rm -f "$tmpout" + fi +} + +setup_pcr() { + ./examples/pcr/reset 16 >> "$LOGFILE" 2>&1 || return 1 + echo aaa > aaa_pcr16.bin + ./examples/pcr/extend 16 aaa_pcr16.bin >> "$LOGFILE" 2>&1 || return 1 +} + +change_pcr() { + echo bbb > bbb_pcr16.bin + ./examples/pcr/extend 16 bbb_pcr16.bin >> "$LOGFILE" 2>&1 || return 1 +} + +# Pre-flight +echo -e "${BLD}wolfTPM Seal Test Suite${RST}" +echo "" +if [ ! -x ./examples/pcr/reset ] || [ ! -x ./examples/pcr/extend ]; then + echo -e "${RED}ERROR: PCR utilities not found. Build examples first.${RST}"; exit 1 +fi +HAS_PCR=0; HAS_PA=0; HAS_NV=0 +[ -x ./examples/seal/seal_pcr ] && HAS_PCR=1 +[ -x ./examples/seal/seal_policy_auth ] && HAS_PA=1 +[ -x ./examples/nvram/seal_nv ] && HAS_NV=1 + +# ============================================================ +# Group 1: seal_pcr (PCR-only policy) +# ============================================================ +echo -e "${BLD}Group 1: seal_pcr (PCR-only policy)${RST}" +if [ $HAS_PCR -eq 0 ]; then + skip "seal_pcr not compiled" +else + # 1.1: seal+unseal, verify secret + setup_pcr; S="SealPCRSecret123" + run_test "1.1 seal_pcr -both" expect_pass "$S" -- \ + ./examples/seal/seal_pcr -both -pcr=16 -secretstr="$S" + rm -f sealblob.bin + + # 1.2: split seal/unseal + setup_pcr; S="SealPCRSplit456" + run_test "1.2a seal_pcr -seal" expect_pass -- \ + ./examples/seal/seal_pcr -seal -pcr=16 -secretstr="$S" + run_test "1.2b seal_pcr -unseal (verify)" expect_pass "$S" -- \ + ./examples/seal/seal_pcr -unseal -pcr=16 + rm -f sealblob.bin + + # 1.3: PCR mismatch (negative) + setup_pcr + run_test "1.3a seal_pcr -seal" expect_pass -- \ + ./examples/seal/seal_pcr -seal -pcr=16 -secretstr="WillFail789" + change_pcr + run_test "1.3b seal_pcr -unseal after PCR change (expect fail)" expect_fail -- \ + ./examples/seal/seal_pcr -unseal -pcr=16 + rm -f sealblob.bin + + # 1.4: XOR param encryption + if [ $WOLFCRYPT_ENABLE -eq 1 ]; then + setup_pcr; S="SealPCRXor101" + run_test "1.4 seal_pcr -both -xor" expect_pass "$S" -- \ + ./examples/seal/seal_pcr -both -pcr=16 -xor -secretstr="$S" + rm -f sealblob.bin + else + skip "1.4 seal_pcr -xor (wolfCrypt disabled)" + fi + + # 1.5: AES param encryption + if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ]; then + setup_pcr; S="SealPCRAes202" + run_test "1.5 seal_pcr -both -aes" expect_pass "$S" -- \ + ./examples/seal/seal_pcr -both -pcr=16 -aes -secretstr="$S" + rm -f sealblob.bin + else + skip "1.5 seal_pcr -aes (wolfCrypt default or disabled)" + fi + + # 1.6: custom sealblob filename + setup_pcr; S="SealPCRCustom303" + run_test "1.6a seal_pcr -sealblob=custom_seal.bin" expect_pass -- \ + ./examples/seal/seal_pcr -seal -pcr=16 -sealblob=custom_seal.bin -secretstr="$S" + run_test "1.6b seal_pcr -unseal custom blob (verify)" expect_pass "$S" -- \ + ./examples/seal/seal_pcr -unseal -pcr=16 -sealblob=custom_seal.bin + rm -f custom_seal.bin +fi +echo "" + +# ============================================================ +# Group 2: seal_policy_auth (PolicyAuthorize) +# ============================================================ +echo -e "${BLD}Group 2: seal_policy_auth (PolicyAuthorize)${RST}" +if [ $HAS_PA -eq 0 ]; then + skip "seal_policy_auth not compiled" +elif [ $WOLFCRYPT_ENABLE -ne 1 ] || [ $WOLFCRYPT_DEFAULT -ne 0 ]; then + skip "seal_policy_auth requires wolfCrypt (non-default)" +else + # ECC tests + if [ $WOLFCRYPT_ECC -eq 1 ]; then + setup_pcr; S="PolicyAuthECC01" + run_test "2.1 policy_auth -ecc -both" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -both -ecc -pcr=16 -secretstr="$S" + rm -f sealblob.bin authkey.bin + + setup_pcr; S="PolicyAuthECCSplit" + run_test "2.3a policy_auth -ecc -seal" expect_pass -- \ + ./examples/seal/seal_policy_auth -seal -ecc -pcr=16 -secretstr="$S" + run_test "2.3b policy_auth -ecc -unseal (verify)" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -unseal -ecc -pcr=16 + rm -f sealblob.bin authkey.bin + + setup_pcr + run_test "2.5a policy_auth -ecc -seal" expect_pass -- \ + ./examples/seal/seal_policy_auth -seal -ecc -pcr=16 -secretstr="WillFailECC" + rm -f authkey.bin + run_test "2.5b policy_auth -ecc unseal without auth key (expect fail)" expect_fail -- \ + ./examples/seal/seal_policy_auth -unseal -ecc -pcr=16 + rm -f sealblob.bin authkey.bin + + setup_pcr; S="PolicyAuthECCXor" + run_test "2.6 policy_auth -ecc -both -xor" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -both -ecc -pcr=16 -xor -secretstr="$S" + rm -f sealblob.bin authkey.bin + else + for t in "2.1 -ecc both" "2.3a -ecc seal" "2.3b -ecc unseal" \ + "2.5 -ecc PCR change" "2.6 -ecc -xor"; do + skip "policy_auth $t (ECC disabled)" + done + fi + + # RSA tests + if [ $WOLFCRYPT_RSA -eq 1 ]; then + setup_pcr; S="PolicyAuthRSA01" + run_test "2.2 policy_auth -rsa -both" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -both -rsa -pcr=16 -secretstr="$S" + rm -f sealblob.bin authkey.bin + + setup_pcr; S="PolicyAuthRSASplit" + run_test "2.4a policy_auth -rsa -seal" expect_pass -- \ + ./examples/seal/seal_policy_auth -seal -rsa -pcr=16 -secretstr="$S" + run_test "2.4b policy_auth -rsa -unseal (verify)" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -unseal -rsa -pcr=16 + rm -f sealblob.bin authkey.bin + + setup_pcr; S="PolicyAuthRSAAes" + run_test "2.7 policy_auth -rsa -both -aes" expect_pass "$S" -- \ + ./examples/seal/seal_policy_auth -both -rsa -pcr=16 -aes -secretstr="$S" + rm -f sealblob.bin authkey.bin + else + for t in "2.2 -rsa both" "2.4a -rsa seal" "2.4b -rsa unseal" "2.7 -rsa -aes"; do + skip "policy_auth $t (RSA disabled)" + done + fi +fi +echo "" + +# ============================================================ +# Group 3: seal_nv (NV + PCR policy) +# ============================================================ +echo -e "${BLD}Group 3: seal_nv (NV + PCR policy)${RST}" +if [ $HAS_NV -eq 0 ]; then + skip "seal_nv not compiled" +else + # 3.1: store/read/delete lifecycle + setup_pcr; S="NVSealTest001" + run_test "3.1a seal_nv -store" expect_pass -- \ + ./examples/nvram/seal_nv -store -pcr=16 -secretstr="$S" + run_test "3.1b seal_nv -read (verify)" expect_pass "$S" -- \ + ./examples/nvram/seal_nv -read -pcr=16 + run_test "3.1c seal_nv -delete" expect_pass -- \ + ./examples/nvram/seal_nv -delete + + # 3.2: PCR mismatch (negative) + setup_pcr; S="NVSealFail002" + run_test "3.2a seal_nv -store" expect_pass -- \ + ./examples/nvram/seal_nv -store -pcr=16 -secretstr="$S" + change_pcr + run_test "3.2b seal_nv -read after PCR change (expect fail)" expect_fail -- \ + ./examples/nvram/seal_nv -read -pcr=16 + setup_pcr + run_test "3.2d seal_nv -delete (cleanup)" expect_pass -- \ + ./examples/nvram/seal_nv -delete + + # 3.3: custom NV index + setup_pcr; S="NVSealCustom003" + run_test "3.3a seal_nv -store nvindex=0x01800204" expect_pass -- \ + ./examples/nvram/seal_nv -store -pcr=16 -nvindex=0x01800204 -secretstr="$S" + run_test "3.3b seal_nv -read nvindex=0x01800204 (verify)" expect_pass "$S" -- \ + ./examples/nvram/seal_nv -read -pcr=16 -nvindex=0x01800204 + run_test "3.3c seal_nv -delete nvindex=0x01800204" expect_pass -- \ + ./examples/nvram/seal_nv -delete -nvindex=0x01800204 + + # 3.4: XOR param encryption + if [ $WOLFCRYPT_ENABLE -eq 1 ]; then + setup_pcr; S="NVSealXor004" + run_test "3.4a seal_nv -store -xor" expect_pass -- \ + ./examples/nvram/seal_nv -store -pcr=16 -xor -secretstr="$S" + run_test "3.4b seal_nv -read -xor (verify)" expect_pass "$S" -- \ + ./examples/nvram/seal_nv -read -pcr=16 -xor + run_test "3.4c seal_nv -delete" expect_pass -- \ + ./examples/nvram/seal_nv -delete + else + skip "3.4 seal_nv -xor (wolfCrypt disabled)" + fi + + # 3.5: AES param encryption + if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ]; then + setup_pcr; S="NVSealAes005" + run_test "3.5a seal_nv -store -aes" expect_pass -- \ + ./examples/nvram/seal_nv -store -pcr=16 -aes -secretstr="$S" + run_test "3.5b seal_nv -read -aes (verify)" expect_pass "$S" -- \ + ./examples/nvram/seal_nv -read -pcr=16 -aes + run_test "3.5c seal_nv -delete" expect_pass -- \ + ./examples/nvram/seal_nv -delete + else + skip "3.5 seal_nv -aes (wolfCrypt default or disabled)" + fi +fi +echo "" + +# Summary +TOTAL=$((PASS + FAIL + SKIP)) +echo -e "${BLD}Summary:${RST} $TOTAL tests |" \ + "${GRN}$PASS passed${RST} |" \ + "${RED}$FAIL failed${RST} |" \ + "${YLW}$SKIP skipped${RST}" +echo "Detailed log: $LOGFILE" +[ $FAIL -ne 0 ] && exit 1 +exit 0