diff --git a/Makefile.am b/Makefile.am index 8ac71a852..777b896a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,8 @@ endif include src_vars.mk +ACLOCAL_AMFLAGS = -I m4 --install + INCLUDE_DIRS = -I$(top_srcdir)/tools -I$(top_srcdir)/lib LIB_COMMON := lib/libcommon.a @@ -201,7 +203,13 @@ tpm2_tools = \ tools/tpm2_ecdhzgen.c \ tools/tpm2_zgen2phase.c \ tools/tpm2_sessionconfig.c \ - tools/tpm2_getpolicydigest.c + tools/tpm2_getpolicydigest.c \ + tools/tpm2_encapsulate.c \ + tools/tpm2_decapsulate.c \ + tools/tpm2_signsequenceflow.c \ + tools/tpm2_verifysequenceflow.c \ + tools/tpm2_signdigest.c \ + tools/tpm2_verifydigestsignature.c # Create the symlinks for each tool to the tpm2 and optional tss2 bundled executables install-exec-hook: @@ -275,7 +283,7 @@ test_unit_test_tpm2_auth_util_LDFLAGS = -Wl,--wrap=Esys_TR_SetAuth \ -Wl,--wrap=fread \ -Wl,--wrap=fseek \ -Wl,--wrap=ftell \ - -Wl,--wrap=feof \ + -Wl,--wrap=feof, \ -Wl,--wrap=fclose test_unit_test_tpm2_auth_util_LDADD = $(CMOCKA_LIBS) $(LDADD) @@ -475,6 +483,12 @@ if HAVE_MAN_PAGES man/man1/tpm2_zgen2phase.1 \ man/man1/tpm2_sessionconfig.1 \ man/man1/tpm2_getpolicydigest.1 \ + man/man1/tpm2_encapsulate.1 \ + man/man1/tpm2_decapsulate.1 \ + man/man1/tpm2_signsequenceflow.1 \ + man/man1/tpm2_verifysequenceflow.1 \ + man/man1/tpm2_signdigest.1 \ + man/man1/tpm2_verifydigestsignature.1 \ man/man1/tpm2.1 if HAVE_FAPI @@ -648,20 +662,14 @@ dist-hook: done; prepare-check: -if HAVE_EFIVAR_GE_39 - cp $(abs_top_srcdir)/test/integration/fixtures/event-pretty-39/event-*.yaml \ - $(abs_top_srcdir)/test/integration/fixtures/ -endif -if HAVE_EFIVAR_L_39 +if HAVE_EFIVAR_H cp $(abs_top_srcdir)/test/integration/fixtures/event-pretty/event-*.yaml \ $(abs_top_srcdir)/test/integration/fixtures/ -endif -if HAVE_EFIVAR_NO +else cp $(abs_top_srcdir)/test/integration/fixtures/event-raw/event-*.yaml \ $(abs_top_srcdir)/test/integration/fixtures/ endif - check: prepare-check if !HAVE_PANDOC diff --git a/lib/tpm2.c b/lib/tpm2.c index e607454af..e708e3c7e 100644 --- a/lib/tpm2.c +++ b/lib/tpm2.c @@ -21,6 +21,117 @@ static inline UINT16 tpm2_error_get(TSS2_RC rc) { return ((rc & TPM2_ERROR_TSS2_RC_ERROR_MASK)); } +tool_rc tpm2_encapsulate(ESYS_CONTEXT *esys_context, ESYS_TR object_handle, + TPM2B_KEM_CIPHERTEXT **ciphertext, TPM2B_SHARED_SECRET **sharedSecret) { + + TSS2_RC rval = Esys_Encapsulate(esys_context, object_handle, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + ciphertext, sharedSecret); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_Encapsulate, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +tool_rc tpm2_decapsulate(ESYS_CONTEXT *esys_context, ESYS_TR object_handle, + TPM2B_KEM_CIPHERTEXT *ciphertext, TPM2B_SHARED_SECRET **sharedSecret) { + + TSS2_RC rval = Esys_Decapsulate(esys_context, object_handle, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + ciphertext, sharedSecret); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_Decapsulate, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +tool_rc tpm2_signsequencestart(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_AUTH *auth, TPM2B_SIGNATURE_CTX *context, ESYS_TR *sequenceHandle) { + + TSS2_RC rval = Esys_SignSequenceStart(esys_context, keyHandle, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + auth, context, sequenceHandle); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_SignSequenceStart, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +tool_rc tpm2_signsequencecomplete(ESYS_CONTEXT *esys_context, ESYS_TR sequence_handle, + ESYS_TR key_handle, TPM2B_MAX_BUFFER *buffer, TPMT_SIGNATURE **signature) { + + TSS2_RC rval = Esys_SignSequenceComplete(esys_context, sequence_handle, + key_handle, ESYS_TR_PASSWORD, ESYS_TR_PASSWORD, ESYS_TR_NONE, + buffer, signature); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_SignSequenceComplete, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +tool_rc tpm2_verifysequencestart(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_AUTH *auth, TPM2B_SIGNATURE_HINT *hint, TPM2B_SIGNATURE_CTX *context, ESYS_TR *sequenceHandle) { + + TSS2_RC rval = Esys_VerifySequenceStart(esys_context, keyHandle, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + auth, hint, context, sequenceHandle); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_VerifySequenceStart, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +tool_rc tpm2_verifysequencecomplete(ESYS_CONTEXT *esys_context, ESYS_TR sequenceHandle, + ESYS_TR keyHandle, TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation) { + + TSS2_RC rval = Esys_VerifySequenceComplete(esys_context, sequenceHandle, keyHandle, + ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, + signature, validation); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_VerifySequenceComplete, rval); + return tool_rc_from_tpm(rval); + } + return tool_rc_success; +} + +tool_rc tpm2_signdigest(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_SIGNATURE_CTX *context, TPM2B_DIGEST *digest, TPMT_TK_HASHCHECK *validation, + TPMT_SIGNATURE **signature) { + + TSS2_RC rval = Esys_SignDigest(esys_context, keyHandle, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, + context, digest, validation, signature); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_SignDigest, rval); + return tool_rc_from_tpm(rval); + } + return tool_rc_success; +} + +tool_rc tpm2_verifydigestsignature(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_SIGNATURE_CTX *context, TPM2B_DIGEST *digest, TPMT_SIGNATURE *signature, + TPMT_TK_VERIFIED **validation) { + + TSS2_RC rval = Esys_VerifyDigestSignature(esys_context, keyHandle, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, + context, digest, signature, validation); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Esys_VerifyDigestSignature, rval); + return tool_rc_from_tpm(rval); + } + return tool_rc_success; +} + tool_rc tpm2_readpublic(ESYS_CONTEXT *esys_context, ESYS_TR object_handle, TPM2B_PUBLIC **out_public, TPM2B_NAME **name, TPM2B_NAME **qualified_name) { diff --git a/lib/tpm2.h b/lib/tpm2.h index e8c16cd3b..d2b6ef1b3 100644 --- a/lib/tpm2.h +++ b/lib/tpm2.h @@ -20,6 +20,32 @@ tool_rc tpm2_tr_deserialize(ESYS_CONTEXT *esys_context, uint8_t const *buffer, tool_rc tpm2_tr_serialize(ESYS_CONTEXT *esys_context, ESYS_TR object, uint8_t **buffer, size_t *buffer_size); +tool_rc tpm2_encapsulate(ESYS_CONTEXT *esys_context, ESYS_TR object_handle, + TPM2B_KEM_CIPHERTEXT **ciphertext, TPM2B_SHARED_SECRET **sharedSecret); + +tool_rc tpm2_decapsulate(ESYS_CONTEXT *esys_context, ESYS_TR object_handle, + TPM2B_KEM_CIPHERTEXT *ciphertext, TPM2B_SHARED_SECRET **sharedSecret); + +tool_rc tpm2_signsequencestart(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_AUTH *auth, TPM2B_SIGNATURE_CTX *context, ESYS_TR *sequenceHandle); + +tool_rc tpm2_verifysequencestart(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_AUTH *auth, TPM2B_SIGNATURE_HINT *hint, TPM2B_SIGNATURE_CTX *context, ESYS_TR *sequenceHandle); + +tool_rc tpm2_signsequencecomplete(ESYS_CONTEXT *esys_context, ESYS_TR sequence_handle, + ESYS_TR key_handle, TPM2B_MAX_BUFFER *buffer, TPMT_SIGNATURE **signature); + +tool_rc tpm2_verifysequencecomplete(ESYS_CONTEXT *esys_context, ESYS_TR sequenceHandle, + ESYS_TR keyHandle, TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation); + +tool_rc tpm2_signdigest(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_SIGNATURE_CTX *context, TPM2B_DIGEST *digest, TPMT_TK_HASHCHECK *validation, + TPMT_SIGNATURE **signature); + +tool_rc tpm2_verifydigestsignature(ESYS_CONTEXT *esys_context, ESYS_TR keyHandle, + TPM2B_SIGNATURE_CTX *context, TPM2B_DIGEST *digest, TPMT_SIGNATURE *signature, + TPMT_TK_VERIFIED **validation); + tool_rc tpm2_nv_readpublic(ESYS_CONTEXT *esys_context, TPMI_RH_NV_INDEX nv_index, TPM2B_NAME *precalc_nvname, TPM2B_NV_PUBLIC **nv_public, TPM2B_NAME **nv_name, TPM2B_DIGEST *cp_hash, TPM2B_DIGEST *rp_hash, diff --git a/lib/tpm2_alg_util.c b/lib/tpm2_alg_util.c index 42946752e..3a5e245a9 100644 --- a/lib/tpm2_alg_util.c +++ b/lib/tpm2_alg_util.c @@ -47,6 +47,9 @@ static void tpm2_alg_util_for_each_alg(alg_iter iterator, void *userdata) { // Asymmetric { .name = "rsa", .id = TPM2_ALG_RSA, .flags = tpm2_alg_util_flags_asymmetric|tpm2_alg_util_flags_base }, { .name = "ecc", .id = TPM2_ALG_ECC, .flags = tpm2_alg_util_flags_asymmetric|tpm2_alg_util_flags_base }, + { .name = "mlkem", .id = TPM2_ALG_MLKEM, .flags = tpm2_alg_util_flags_symmetric | tpm2_alg_util_flags_base}, + { .name = "mldsa", .id = TPM2_ALG_MLDSA, .flags = tpm2_alg_util_flags_sig}, + // Symmetric { .name = "tdes", .id = TPM2_ALG_TDES, .flags = tpm2_alg_util_flags_symmetric }, @@ -84,6 +87,7 @@ static void tpm2_alg_util_for_each_alg(alg_iter iterator, void *userdata) { { .name = "oaep", .id = TPM2_ALG_OAEP, .flags = tpm2_alg_util_flags_enc_scheme | tpm2_alg_util_flags_rsa_scheme }, { .name = "rsaes", .id = TPM2_ALG_RSAES, .flags = tpm2_alg_util_flags_enc_scheme | tpm2_alg_util_flags_rsa_scheme }, { .name = "ecdh", .id = TPM2_ALG_ECDH, .flags = tpm2_alg_util_flags_enc_scheme }, + // Key derivation functions { .name = "kdf1_sp800_56a", .id = TPM2_ALG_KDF1_SP800_56A, .flags = tpm2_alg_util_flags_kdf }, @@ -242,7 +246,8 @@ static alg_parser_rc handle_scheme_sign(const char *scheme, DO_SCHEME_HALG(buf_ptr, 6, TPM2_ALG_RSASSA); } else if (!strncmp(buf_ptr, "oaep", 4)) { DO_SCHEME_HALG(buf_ptr, 4, TPM2_ALG_OAEP); - } + + } } /* If we're not expecting a hash alg then halg should be NULL */ @@ -370,6 +375,58 @@ static alg_parser_rc handle_ecc(const char *ext, TPM2B_PUBLIC *public) { return ext[0] == '\0' ? alg_parser_rc_continue : alg_parser_rc_error; } +static void init_mlkem_public(TPM2B_PUBLIC *public, UINT16 parameter_set){ + + TPMI_ALG_HASH name_alg = public->publicArea.nameAlg; + TPMA_OBJECT attrs = public->publicArea.objectAttributes; + TPM2B_DIGEST auth_policy = public->publicArea.authPolicy; + + *public = (TPM2B_PUBLIC){ + .size = 0, + .publicArea = { + .type = TPM2_ALG_MLKEM, + .nameAlg = name_alg, + .objectAttributes = attrs, + .authPolicy = auth_policy, + .parameters.mlkemDetail = { + .symmetric = { + .algorithm = TPM2_ALG_NULL, + .keyBits.aes = 0, + .mode.aes = 0, + }, + .parameterSet = parameter_set, + }, + .unique.mlkem = { + .size = 0, + }, + }, + }; +} + +static void init_mldsa_public(TPM2B_PUBLIC *public, UINT16 parameter_set){ + + TPMI_ALG_HASH name_alg = public->publicArea.nameAlg; + TPMA_OBJECT attrs = public->publicArea.objectAttributes; + TPM2B_DIGEST auth_policy = public->publicArea.authPolicy; + + *public = (TPM2B_PUBLIC){ + .size = 0, + .publicArea = { + .type = TPM2_ALG_MLDSA, + .nameAlg = name_alg, + .objectAttributes = attrs, + .authPolicy = auth_policy, + .parameters.mldsaDetail = { + .parameterSet = parameter_set, + .allowExternalMu = 1, + }, + .unique.mldsa = { + .size = 0, + }, + }, + }; +} + static alg_parser_rc handle_aes(const char *ext, TPM2B_PUBLIC *public) { public->publicArea.type = TPM2_ALG_SYMCIPHER; @@ -426,6 +483,55 @@ static alg_parser_rc handle_keyedhash(TPM2B_PUBLIC *public) { return alg_parser_rc_done; } +static alg_parser_rc handle_mlkem(const char *ext, TPM2B_PUBLIC *public){ + + if (ext == NULL || ext[0] == '\0') { + return alg_parser_rc_error; + } + + if(!strcmp(ext, "512")){ + init_mlkem_public(public, TPM2_MLKEM_PARMS_512); + return alg_parser_rc_done; + } + + if(!strcmp(ext, "768")){ + init_mlkem_public(public, TPM2_MLKEM_PARMS_768); + return alg_parser_rc_done; + } + + if(!strcmp(ext, "1024")){ + init_mlkem_public(public, TPM2_MLKEM_PARMS_1024); + return alg_parser_rc_done; + } + + return alg_parser_rc_error; + +} + +static alg_parser_rc handle_mldsa(const char *ext, TPM2B_PUBLIC *public){ + + if (ext == NULL || ext[0] == '\0') { + return alg_parser_rc_error; + } + + if(!strcmp(ext, "44")){ + init_mldsa_public(public, TPM2_MLDSA_PARMS_44); + return alg_parser_rc_done; + } + + if(!strcmp(ext, "65")){ + init_mldsa_public(public, TPM2_MLDSA_PARMS_65); + return alg_parser_rc_done; + } + + if(!strcmp(ext, "87")){ + init_mldsa_public(public, TPM2_MLDSA_PARMS_87); + return alg_parser_rc_done; + } + + return alg_parser_rc_error; +} + static alg_parser_rc handle_object(const char *object, TPM2B_PUBLIC *public) { if (!strncmp(object, "rsa", 3)) { @@ -434,6 +540,12 @@ static alg_parser_rc handle_object(const char *object, TPM2B_PUBLIC *public) { } else if (!strncmp(object, "ecc", 3)) { object += 3; return handle_ecc(object, public); + } else if (!strncmp(object, "mlkem", 5)) { + object += 5; + return handle_mlkem(object, public); + } else if (!strncmp(object, "mldsa", 5)) { + object += 5; + return handle_mldsa(object, public); } else if (!strncmp(object, "aes", 3)) { object += 3; return handle_aes(object, public); @@ -499,6 +611,8 @@ static alg_parser_rc handle_scheme(const char *scheme, TPM2B_PUBLIC *public) { return alg_parser_rc_error; } + + static alg_parser_rc handle_asym_detail(const char *detail, TPM2B_PUBLIC *public) { @@ -934,6 +1048,22 @@ static tool_rc tpm2_public_to_scheme(ESYS_CONTEXT *ectx, ESYS_TR key, TPMI_ALG_P rc = tool_rc_success; goto out; } + + /* + * MLDSA is also a signing key type. Handle it here rather than treating it as + * a keyed hash object + */ + + if (*type == TPM2_ALG_MLDSA) { + + sigscheme->scheme = pp->asymDetail.scheme.scheme; + /* they all have a hash alg, and for most schemes' thats it */ + sigscheme->details.any.hashAlg + = pp->asymDetail.scheme.details.anySig.hashAlg; + + rc = tool_rc_success; + goto out; + } /* keyed hash could be the only one left */ sigscheme->scheme = pp->keyedHashDetail.scheme.scheme; @@ -1058,6 +1188,7 @@ tool_rc tpm2_alg_util_public_init(const char *alg_details, const char *name_halg */ TPM2B_PUBLIC tmp = *public; bool res = tpm2_alg_util_handle_ext_alg(alg_details, &tmp); + if (!res) { LOG_ERR("Could not handle algorithm: \"%s\"", alg_details); return tool_rc_unsupported; diff --git a/lib/tpm2_cc_util.c b/lib/tpm2_cc_util.c index cba5ea1c4..9bb54bf4f 100644 --- a/lib/tpm2_cc_util.c +++ b/lib/tpm2_cc_util.c @@ -130,6 +130,12 @@ static const cc_map _g_map[] = { ADDCC(TPM2_CC_AC_Send), ADDCC(TPM2_CC_Policy_AC_SendSelect), ADDCC(TPM2_CC_Vendor_TCG_Test), + ADDCC(TPM2_CC_Encapsulate), + ADDCC(TPM2_CC_Decapsulate), + ADDCC(TPM2_CC_SignSequenceStart), + ADDCC(TPM2_CC_SignSequenceComplete), + ADDCC(TPM2_CC_SignDigest), + ADDCC(TPM2_CC_VerifyDigestSignature) }; bool tpm2_cc_util_from_str(const char *str, TPM2_CC *cc) { diff --git a/lib/tpm2_convert.c b/lib/tpm2_convert.c index 96979647e..571f62cca 100644 --- a/lib/tpm2_convert.c +++ b/lib/tpm2_convert.c @@ -717,6 +717,19 @@ UINT8 *tpm2_convert_sig(UINT16 *size, TPMT_SIGNATURE *signature) { case TPM2_ALG_SM2: { return extract_ecdsa(&signature->signature.ecdsa, size); } + /* + * MLDSA is a valid signature scheme and must be converted here explicitly + * instead of falling through to the unknown scheme case + */ + case TPM2_ALG_MLDSA: { + *size = signature->signature.mldsa.size; + buffer=malloc(*size); + if(!buffer){ + goto nomem; + } + memcpy(buffer, signature->signature.mldsa.buffer, *size); + break; + } default: LOG_ERR("%s: unknown signature scheme: 0x%x", __func__, signature->sigAlg); diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c index d4b46c95a..c85bd22ed 100644 --- a/lib/tpm2_util.c +++ b/lib/tpm2_util.c @@ -369,6 +369,17 @@ static void tpm2_util_public_to_keydata(TPMT_PUBLIC *public, keydata->entries[1].name = "y"; keydata->entries[1].value = (TPM2B *) &public->unique.ecc.y; return; + case TPM2_ALG_MLKEM: + keydata->len = 1; + keydata->entries[0].name = "mlkem"; + keydata->entries[0].value = (TPM2B *) &public->unique.mlkem; + return; + case TPM2_ALG_MLDSA: + keydata->len = 1; + keydata->entries[0].name = tpm2_alg_util_algtostr( + public->type, tpm2_alg_util_flags_any); + keydata->entries[0].value = (TPM2B *) &public->unique.mldsa; + return; default: LOG_WARN("The algorithm type(0x%4.4x) is not supported", public->type); diff --git a/man/tpm2_decapsulate.1.md b/man/tpm2_decapsulate.1.md new file mode 100755 index 000000000..2e7e36a6d --- /dev/null +++ b/man/tpm2_decapsulate.1.md @@ -0,0 +1,77 @@ +% tpm2_decapsulate(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_decapsulate**(1) - Perform a KEM decapsulation with a private TPM key. + +# SYNOPSIS + +**tpm2_decapsulate** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_decapsulate**(1) - This command performs the private key operation of a +Key Encapsulation Mechanism (KEM) using a TPM private key. + +Given a private key and a ciphertext produced by an earlier encapsulation operation +such as **TPM2_Encapsulate()**, the command returns the same **sharedSecret** that +was produced during encapsulation. + + +# OPTIONS + + * **-c**, **\--object-context**=_OBJECT_: + + Reference to the loaded KEM key to use for decapsulation. + + * **-p**, **\--auth**=_AUTH_: + + The authorization value for the key. + + * **-i**, **\--ciphertext**=_FILE_: + + Input file path containing the encapsulated ciphertext. + + * **-s**, **\--shared-secret**=_FILE_: + + Output file path for the decaspsulated shared secret + + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mlkem. + +```bash +tpm2 createprimary -C o -G mlkem1024 -g sha256 -c mlkem.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' +``` + +Step 2 is to encapsulate. + +```bash +tpm2_encapsulate -c mlkem.ctx --shared-secret=secret.bin --ciphertext=ciphertext.bin +``` + +Step 3 is to decapsulate. + +```bash +tpm2_decapsulate -c mlkem.ctx --ciphertext=ciphertext.bin --shared-secret=secret_dec.bin +``` + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/man/tpm2_encapsulate.1.md b/man/tpm2_encapsulate.1.md new file mode 100755 index 000000000..8f7f043d4 --- /dev/null +++ b/man/tpm2_encapsulate.1.md @@ -0,0 +1,64 @@ +% tpm2_encapsulate(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_encapsulate**(1) - Perform a KEM encapsulation with a public TPM key. + +# SYNOPSIS + +**tpm2_encapsulate** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_encapsulate**(1) - This command performs the public key operation of a +Key Encapsulation Mechanism (KEM) using the public portion of a TPM key. + +Given a recipient public key referenced by **keyHandle**, the command produces : +- a **shared secret** +- an associated **ciphertext** + + +# OPTIONS + + * **-c**, **\--object-context**=_OBJECT_: + + The public portion of the KEM key. + + * **-s**, **\--shared-secret**=_FILE_: + + Output file path for the generated shared secret. + + * **-t**, **\--ciphertext**=_FILE_: + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mlkem. + +```bash +tpm2 createprimary -C o -G mlkem1024 -g sha256 -c mlkem.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' +``` + +Step 2 is to encapsulate. + +```bash +tpm2_encapsulate -c mlkem.ctx --shared-secret=secret.bin --ciphertext=ciphertext.bin +``` + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/man/tpm2_signdigest.1.md b/man/tpm2_signdigest.1.md new file mode 100755 index 000000000..59dbe4d88 --- /dev/null +++ b/man/tpm2_signdigest.1.md @@ -0,0 +1,82 @@ +% tpm2_signdigest(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_signdigest**(1) - Sign a message digest with a TPM signinkg key. + +# SYNOPSIS + +**tpm2_signdigest** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_signdigest**(1) - This command signs a precomputed digest using a TPM +signing key. + +The key referenced by **keyHandle** must use a signing scheme that supports +signing a digest, such as **TPM_ALG_ECDSA**. + +The size of **digest** must match the size of the hash algorithm required by the +key signing scheme. + +**NOTE**: Unrestricted **ML-DSA** keys can only be used with **TPM2_SignDigest()** +if **allowExternalMu** is TRUE. + +# OPTIONS + + * **-c**, **\--key-context**=_OBJECT_: + + Reference to the signing key that will perform the signature operation. + + * **-p**, **\--auth**=_AUTH_: + + The authorization value for the signing key specified by **-c**. + + * **-g**, **\--context**=_FILE_: + + Optional additional signing context. + + * **-d**, **\--digest**=_FILE_: + + The digest to sign. + + * **-t**, **\--ticket**=_FILE_: + + Input file containing the **TPMT_TK_HASHCHECK** validation ticket. + + * **-o**, **\--signature**=_FILE_: + + Output file path for the generated signature. + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mldsa. + +```bash +tpm2 createprimary -C o -G mldsa87 -g sha512 -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' +``` + +Step 2 is to sign. + +```bash +tpm2 signdigest -c signkey.ctx -d digest.bin -o signature.bin +``` + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/man/tpm2_signsequenceflow.1.md b/man/tpm2_signsequenceflow.1.md new file mode 100755 index 000000000..ff57215b7 --- /dev/null +++ b/man/tpm2_signsequenceflow.1.md @@ -0,0 +1,81 @@ +% tpm2_signsequence(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_signsequence**(1) - Sign a message using a TPM signature sequence. + +# SYNOPSIS + +**tpm2_signsequence** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_signsequence**(1) - This command signs a message using the TPM sequence signing +flow built from : +- **TPM2_SignSequenceStart()** +- **TPM2_SequenceUpdate()** +- **TPM2_SignSequenceComplete()** + +This command is intended for signing message data. + + +# OPTIONS + + * **-c**, **\--key-context**=_OBJECT_: + + Reference to the signing key used to start and complete the sign sequence. + + * **-P**, **\--key-auth**=_AUTH_: + + The authorization value for the signing key. + + * **-i**, **\--input**=_FILE_: + + Input file containing the message to sign. + + * **-p**, **\--sequence-auth**=_AUTH_: + + The authorization value for the sequence. + + * **-s**, **\--signature**=_FILE_: + + Output file path for the generated signature. + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mldsa. + +```bash +tpm2 createprimary -C o -G mldsa87 -g sha256 -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' +``` + +Step 2 is to sign. + +```bash +tpm2_signsequenceflow -c signkey.ctx -i msg.bin -s signature.bin +``` +Step 3 is to save the public part of the key for the verify. + +```bash +tpm2_readpublic -c signkey.ctx -o signpub.out +``` + + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/man/tpm2_verifydigestsignature.1.md b/man/tpm2_verifydigestsignature.1.md new file mode 100755 index 000000000..bfa15c9d8 --- /dev/null +++ b/man/tpm2_verifydigestsignature.1.md @@ -0,0 +1,86 @@ +% tpm2_verifydigestsignature(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_verifydigestsignature**(1) - Verify a signature over a message digest with a +TPM key. + +# SYNOPSIS + +**tpm2_verifydigestsignature** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_verifydigestsignature**(1) - This command verifies a signature over a +precomputed digest using a TPM verification key. + +The key referenced by **keyHandle** must use a signing scheme that supports +signing a digest, such as **TPM_ALG_ECDSA** + + + +**NOTE**: Unrestricted **ML-DSA** keys can only be used with **TPM2_SignDigest()** +if **allowExternalMu** is TRUE. + +# OPTIONS + + * **-c**, **\--key-context**=_OBJECT_: + + Reference to the public key used for the signature verification. + + * **-g**, **\--context**=_FILE_: + + Optionnal additional verification context. + + * **-d**, **\--digest**=_FILE_: + + The digest whose signature is to be verified. + + * **-s**, **\--signature**=_FILE_: + + Input file containing the signature to verify. + + * **-t**, **\--validation**=_FILE_: + + Output file path for the returned validation ticket. + + On success, the ticket tag will be **TPM_ST_DIGEST_VERIFIED**. + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mldsa. + +```bash +tpm2 createprimary -C o -G mldsa87 -g sha512 -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' +``` + +Step 2 is to sign. + +```bash +tpm2 signdigest -c signkey.ctx -d digest.bin -o signature.bin +``` + +Step 3 is to verify the signature. + +```bash +tpm2 verifydigestsignature -c signkey.ctx -d digest.bin -s signature.bin -t validation.bin +``` + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/man/tpm2_verifysequenceflow.1.md b/man/tpm2_verifysequenceflow.1.md new file mode 100755 index 000000000..9f4e1c6e9 --- /dev/null +++ b/man/tpm2_verifysequenceflow.1.md @@ -0,0 +1,102 @@ +% tpm2_verifysequence(1) tpm2-tools | General Commands Manual + +# NAME + +**tpm2_verifysequence**(1) - Verify a signature over a message using a TPM +verification sequence. + +# SYNOPSIS + +**tpm2_verifysequence** [*OPTIONS*] + +# DESCRIPTION + +**tpm2_verifysequence**(1) - This command verifies a signature over a message +using the TPM sequence verification flow built from : +- **TPM2_VerifySequenceStart()** +- **TPM2_SequenceUpdate()** +- **TPM2_VerifySequenceComplete()** + +This command is intended for verifying signatures over message data. + + +# OPTIONS + + * **-c**, **\--key-context**=_OBJECT_: + + Reference to the key used to start and complete the verification sequence. + + * **-p**, **\--sequence-auth**=_AUTH_: + + Authorization for the sequence. + + * **-i**, **\--input**=_FILE_: + + Input file containing the message whose signature is to be verified. + + * **-s**, **\--signature**=_FILE_: + + Input file containing the signature to verify. + + * **-t**, **\--ticket**=_FILE_: + + The validation file. + + * **-h**, **\--hint**=_FILE_: + + Additional information from the signature to be verified. + + * **-C**, **\--context**=_OBJECT_: + + Additional context + + +## References + +[context object format](common/ctxobj.md) details the methods for specifying +_OBJECT_. + +[authorization formatting](common/authorizations.md) details the methods for +specifying _AUTH_. + +[common options](common/options.md) collection of common options that provide +information many users may expect. + +[common tcti options](common/tcti.md) collection of options used to configure +the various known TCTI modules. + +# EXAMPLES + +## Setup +The first step is to create the primary object with mldsa. + +```bash +tpm2 createprimary -C o -G mldsa87 -g sha256 -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' +``` + +Step 2 is to sign. + +```bash +tpm2_signsequenceflow -c signkey.ctx -i msg.bin -s signature.bin +``` +Step 2 is to save the public part of the key for the verify. + +```bash +tpm2_readpublic -c signkey.ctx -o signpub.out +``` + +Step 3 is to load the public part of the key. + +```bash +tpm2_loadexternal -u signpub.out -c verifypub.ctx +``` + +Step 4 is to verified the signature + +```bash +tpm2_verifysequenceflow -c verifypub.ctx -i msg.bin -s signature.bin -t verified.ticket +``` + +[returns](common/returns.md) + +[footer](common/footer.md) diff --git a/test/integration/tests/pqc_activatecredential.sh b/test/integration/tests/pqc_activatecredential.sh new file mode 100755 index 000000000..21bfc042d --- /dev/null +++ b/test/integration/tests/pqc_activatecredential.sh @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f signkey.ctx + rm -f child.* + rm -f mlkem_act.name mlkem_protect.pub + rm -f secret.bin recovered_secret.bin + rm -f cred.out + shut_down +} +trap cleanup EXIT + +start_up + +test_activatecredential(){ + alg="$1" + alg2="$2" + + tpm2 clear + + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' -u child.pub -r child.priv -R + + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + + tpm2 readpublic -c child.ctx -n mlkem_act.name + tpm2 readpublic -c signkey.ctx -o mlkem_protect.pub + tpm2 flushcontext -t + echo -n "helo world" > secret.bin + tpm2 makecredential -u mlkem_protect.pub -n mlkem_act.name -s secret.bin -o cred.out + + tpm2 activatecredential -c child.ctx -C signkey.ctx -i cred.out -o recovered_secret.bin + +} +test_activatecredential mlkem512 sha256 +test_activatecredential mlkem768 sha256 +test_activatecredential mlkem1024 sha256 +test_activatecredential mlkem512 sha512 +test_activatecredential mlkem768 sha512 +test_activatecredential mlkem1024 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_auditdigest.sh b/test/integration/tests/pqc_auditdigest.sh new file mode 100755 index 000000000..f1aec4d80 --- /dev/null +++ b/test/integration/tests/pqc_auditdigest.sh @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f child.* + shut_down +} +trap cleanup EXIT + +start_up + +test_getcommandauditdigest(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 flushcontext -t + tpm2 flushcontext -l + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv + tpm2 flushcontext -t + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx + tpm2 setcommandauditstatus -C p -g "$2" TPM2_CC_GetRandom + tpm2 getrandom --hex 10 + tpm2 getcommandauditdigest -c child.ctx -m audit.attes --scheme=mldsa -s signature.out + tpm2 clear + tpm2 flushcontext -t + tpm2 flushcontext -l +} + +test_getsessionauditdigest(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 flushcontext -t + tpm2 flushcontext -l + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv + tpm2 flushcontext -t + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx + tpm2 startauthsession -S mysession.ctx --audit-session + tpm2 getrandom --hex 10 -S mysession.ctx + tpm2 getsessionauditdigest -c child.ctx --scheme=mldsa -S mysession.ctx -m session_audit.attest -s session_audit.sig + tpm2 clear + tpm2 flushcontext -t + tpm2 flushcontext -l +} +test_getcommandauditdigest mldsa44 sha256 +test_getcommandauditdigest mldsa65 sha256 +test_getcommandauditdigest mldsa44 sha512 +test_getcommandauditdigest mldsa65 sha512 + +test_getsessionauditdigest mldsa44 sha256 +test_getsessionauditdigest mldsa65 sha256 +test_getsessionauditdigest mldsa44 sha512 +test_getsessionauditdigest mldsa65 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_certify.sh b/test/integration/tests/pqc_certify.sh new file mode 100755 index 000000000..2c172f269 --- /dev/null +++ b/test/integration/tests/pqc_certify.sh @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f obj.* mldsa.* mlkem.* signer.* + rm -f *.out + rm -f attestat.ion + rm -f sig.nature + rm -f creation.hash + shut_down +} +trap cleanup EXIT + +start_up + +test_certify_mldsa_mlkem(){ + alg_mldsa="$1" + alg_mlkem="$2" + alg="$3" + + tpm2 clear + tpm2 createprimary -C o -G rsa -g "$3" -c primary.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C primary.ctx -g "$3" -G "$2" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' -u mlkem.pub -r mlkem.priv -R + + tpm2 load -C primary.ctx -u mlkem.pub -r mlkem.priv -c mlkem.ctx -R + tpm2 create -C primary.ctx -g "$3" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u mldsa.pub -r mldsa.priv --creation-data obj.creation.data --creation-hash obj.creation.hash --creation-ticket obj.creation.ticket -R + + tpm2 load -C primary.ctx -u mldsa.pub -r mldsa.priv -c mldsa.ctx -R + + tpm2 certify -c mlkem.ctx -C mldsa.ctx -g "$3" -o attest.out -s sig.out --scheme=mldsa + +} + +test_certifycreation(){ + alg="$1" + alg2="$2" + + tpm2 clear + + tpm2 createprimary -C o -G rsa -g "$2" -c primary.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C primary.ctx -g "$2" -G rsa -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' -u obj.pub -r obj.priv --creation-data obj.creation.data --creation-hash obj.creation.hash --creation-ticket obj.creation.ticket -R + + tpm2 load -C primary.ctx -u obj.pub -r obj.priv -c obj.ctx -R + tpm2 create -C primary.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u mldsa.pub -r mldsa.priv -R + + tpm2 load -C primary.ctx -u mldsa.pub -r mldsa.priv -c mldsa.ctx -R + + tpm2 certifycreation -c obj.ctx -C mldsa.ctx -d obj.creation.hash -t obj.creation.ticket -g "$2" -o sig.nature --scheme=mldsa --attestation=attestat.ion + +} + +test_certify_mldsa_mlkem mldsa44 mlkem512 sha256 +test_certify_mldsa_mlkem mldsa44 mlkem768 sha256 +test_certify_mldsa_mlkem mldsa44 mlkem1024 sha256 +test_certify_mldsa_mlkem mldsa44 mlkem512 sha512 +test_certify_mldsa_mlkem mldsa44 mlkem768 sha512 +test_certify_mldsa_mlkem mldsa44 mlkem1024 sha512 + +test_certify_mldsa_mlkem mldsa65 mlkem512 sha256 +test_certify_mldsa_mlkem mldsa65 mlkem768 sha256 +test_certify_mldsa_mlkem mldsa65 mlkem1024 sha256 +test_certify_mldsa_mlkem mldsa65 mlkem512 sha512 +test_certify_mldsa_mlkem mldsa65 mlkem768 sha512 +test_certify_mldsa_mlkem mldsa65 mlkem1024 sha512 + +test_certifycreation mldsa44 sha256 +test_certifycreation mldsa65 sha256 +test_certifycreation mldsa44 sha512 +test_certifycreation mldsa65 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_create_load.sh b/test/integration/tests/pqc_create_load.sh new file mode 100755 index 000000000..3552715a5 --- /dev/null +++ b/test/integration/tests/pqc_create_load.sh @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f signkey.ctx + rm -f child.* + shut_down +} +trap cleanup EXIT + +start_up + +test_createmldsa(){ + alg="$1" + alg2="$2" + + tpm2 clear + + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv -R + + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + +} + +test_createmlkem(){ + alg="$1" + alg2="$2" + tpm2 clear + + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' -u child.pub -r child.priv -R + + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + +} + +test_createmldsa mldsa44 sha256 +test_createmldsa mldsa65 sha256 +test_createmldsa mldsa44 sha512 +test_createmldsa mldsa65 sha512 + +test_createmlkem mlkem512 sha256 +test_createmlkem mlkem768 sha256 +test_createmlkem mlkem1024 sha256 +test_createmlkem mlkem512 sha512 +test_createmlkem mlkem768 sha512 +test_createmlkem mlkem1024 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_duplicate.sh b/test/integration/tests/pqc_duplicate.sh new file mode 100755 index 000000000..1924235ae --- /dev/null +++ b/test/integration/tests/pqc_duplicate.sh @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f new_parent.* + rm -f *.dat key.* dup.* duplicate.priv + shut_down +} +trap cleanup EXIT + +start_up + +test_duplicate_mlkem(){ + alg="$1" + alg2="$2" + + tpm2 clear + + #TPM createprimary + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx + + #TPM create + tpm2 create -C signkey.ctx -g "$2" -G rsa -a 'restricted|sensitivedataorigin|userwithauth|decrypt' -u new_parent.pub -r new_parent.priv -R + + #TPM load + tpm2 load -C signkey.ctx -u new_parent.pub -r new_parent.priv -c new_parent.ctx -R + + #startauthsession + tpm2 startauthsession -S session.dat + + #policycommand + tpm2 policycommandcode -S session.dat -L dpolicy.dat TPM2_CC_Duplicate + tpm2 flushcontext session.dat + + #TPM create mlkem + tpm2 create -C signkey.ctx -g "$2" -G "$1" -p foo -r key.prv -u key.pub -L dpolicy.dat -a 'sensitivedataorigin|userwithauth|decrypt' -R + + #TPM load + tpm2 load -C signkey.ctx -r key.prv -u key.pub -c key.ctx -R + #TPM readpublic + tpm2 readpublic -c key.ctx -o dup.pub + #TPM startauthsession + tpm2 startauthsession --policy-session -S session.dat + tpm2 policycommandcode -S session.dat -L dpolicy.dat TPM2_CC_Duplicate + #TPM duplicate + tpm2 flushcontext -t + tpm2 duplicate -C new_parent.ctx -c key.ctx -G null -r duplicate.priv -s seed.dat -p "session:session.dat" + + #TPM import + tpm2 import -C new_parent.ctx -u dup.pub -i duplicate.priv -r dup.prv -s seed.dat -L dpolicy.dat + +} + +test_duplicate_mldsa(){ + alg="$1" + alg2="$2" + + tpm2 clear + #TPM createprimary + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx + #TPM create + tpm2 create -C signkey.ctx -g "$2" -G rsa -a 'restricted|sensitivedataorigin|userwithauth|decrypt' -u new_parent.pub -r new_parent.priv -R + #TPM load + tpm2 load -C signkey.ctx -u new_parent.pub -r new_parent.priv -c new_parent.ctx -R + #startauthsession + tpm2 startauthsession -S session.dat + #policycommand + tpm2 policycommandcode -S session.dat -L dpolicy.dat TPM2_CC_Duplicate + tpm2 flushcontext session.dat + #TPM create mldsa + tpm2 create -C signkey.ctx -g "$2" -G "$1" -p foo -r key.prv -u key.pub -L dpolicy.dat -a 'sensitivedataorigin|userwithauth|sign' -R + #TPM load + tpm2 load -C signkey.ctx -r key.prv -u key.pub -c key.ctx -R + #TPM readpublic + tpm2 readpublic -c key.ctx -o dup.pub + #TPM startauthsession + tpm2 startauthsession --policy-session -S session.dat + tpm2 policycommandcode -S session.dat -L dpolicy.dat TPM2_CC_Duplicate + #TPM duplicate + tpm2 flushcontext -t + tpm2 duplicate -C new_parent.ctx -c key.ctx -G null -r duplicate.priv -s seed.dat -p "session:session.dat" + #TPM import + tpm2 import -C new_parent.ctx -u dup.pub -i duplicate.priv -r dup.prv -s seed.dat -L dpolicy.dat +} +test_duplicate_mlkem mlkem512 sha256 +test_duplicate_mlkem mlkem768 sha256 +test_duplicate_mlkem mlkem1024 sha256 + +test_duplicate_mldsa mldsa44 sha256 +test_duplicate_mldsa mldsa65 sha256 + +exit 0 diff --git a/test/integration/tests/pqc_kem.sh b/test/integration/tests/pqc_kem.sh new file mode 100755 index 000000000..0fd9ecb33 --- /dev/null +++ b/test/integration/tests/pqc_kem.sh @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f key_*.ctx + rm -f ct_*.bin ss_enc_*.bin ss_dec_*.bin + shut_down +} +trap cleanup EXIT + +start_up + +test_mlkem(){ + tpm2_flushcontext -l + tpm2_clear + alg="$1" + alg2="$2" + + ctx="key_${alg}.ctx" + ct="ct_${alg}.bin" + ss_enc="ss_enc_${alg}.bin" + ss_dec="ss_dec${alg}.bin" + + #Create mlkem key + tpm2 createprimary -C o -G "$alg" -g "$alg2" -c "$ctx" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt' + + #Test TPM2_Encapsulate + tpm2_encapsulate -c "$ctx" -t "$ct" -s "$ss_enc" + test -s "$ct" + test -s "$ss_enc" + + # Test TPM2_Decapsulate + tpm2_decapsulate -c "$ctx" --ciphertext="$ct" --shared-secret="$ss_dec" + test -s "$ss_dec" + + cmp "$ss_enc" "$ss_dec" + + + +} + +test_loadexternal_mlkem(){ + + alg="$1" + alg2="$2" + + ctx="key_${alg}.ctx" + ct="ct_${alg}.bin" + ss_enc="ss_enc_${alg}.bin" + ss_dec="ss_dec${alg}.bin" + + tpm2 clear + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'sensitivedataorigin|userwithauth|decrypt' -u child.pub -r child.priv + tpm2 flushcontext -t + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx + tpm2 flushcontext -t + tpm2 readpublic -c child.ctx -o mlkem.pub + tpm2 flushcontext -t + tpm2 loadexternal -C o -u mlkem.pub -c mlkem.ctx -n mlkem.name + + #Test TPM2_Encapsulate + tpm2_encapsulate -c mlkem.ctx -t "$ct" -s "$ss_enc" + test -s "$ct" + test -s "$ss_enc" + + # Test TPM2_Decapsulate + tpm2_decapsulate -c child.ctx --ciphertext="$ct" --shared-secret="$ss_dec" + test -s "$ss_dec" + + cmp "$ss_enc" "$ss_dec" + + + tpm2 clear + tpm2 flushcontext -t + tpm2 flushcontext -l + +} + +test_mlkem mlkem512 sha256 +test_mlkem mlkem768 sha256 +test_mlkem mlkem1024 sha256 +test_mlkem mlkem512 sha512 +test_mlkem mlkem768 sha512 +test_mlkem mlkem1024 sha512 + +test_loadexternal_mlkem mlkem512 sha256 +test_loadexternal_mlkem mlkem768 sha256 +test_loadexternal_mlkem mlkem1024 sha256 +test_loadexternal_mlkem mlkem512 sha512 +test_loadexternal_mlkem mlkem768 sha512 +test_loadexternal_mlkem mlkem1024 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_loadexternal.sh b/test/integration/tests/pqc_loadexternal.sh new file mode 100755 index 000000000..7b4770efa --- /dev/null +++ b/test/integration/tests/pqc_loadexternal.sh @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f child.* + rm -f mldsa.* mlkem.* + shut_down +} +trap cleanup EXIT + +start_up + +test_loadexternal_mldsa(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv -p foo -R + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + + tpm2 readpublic -c child.ctx -o mldsa.pub + + tpm2 loadexternal -C o -u mldsa.pub -c mldsa.ctx -n mldsa.name -R + +} + +test_loadexternal_mlkem(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'sensitivedataorigin|userwithauth|decrypt' -u child.pub -r child.priv -p foo -R + + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + + tpm2 readpublic -c child.ctx -o mlkem.pub + + tpm2 loadexternal -C o -u mlkem.pub -c mlkem.ctx -n mlkem.name -R + +} +test_loadexternal_mldsa mldsa44 sha256 +test_loadexternal_mldsa mldsa65 sha256 +test_loadexternal_mldsa mldsa44 sha512 +test_loadexternal_mldsa mldsa65 sha512 + +test_loadexternal_mlkem mlkem512 sha256 +test_loadexternal_mlkem mlkem768 sha256 +test_loadexternal_mlkem mlkem1024 sha256 +test_loadexternal_mlkem mlkem512 sha512 +test_loadexternal_mlkem mlkem768 sha512 +test_loadexternal_mlkem mlkem1024 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_nvcertify.sh b/test/integration/tests/pqc_nvcertify.sh new file mode 100755 index 000000000..a5742daea --- /dev/null +++ b/test/integration/tests/pqc_nvcertify.sh @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f child.* + rm -f signature.bin attestation.bin + shut_down +} +trap cleanup EXIT + +start_up + +test_nvcertify(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 nvdefine -C o -s 64 -a "authread|authwrite|ownerwrite|ownerread" 1 + dd if=/dev/urandom bs=1 count=64 status=none | \ + tpm2 nvwrite 1 -i- + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv -R + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + tpm2 nvcertify -C child.ctx -g "$2" -f plain -s mldsa -o signature.bin --attestation attestation.bin --size 0 1 + +} + +test_nvcertify mldsa44 sha256 +test_nvcertify mldsa65 sha256 +test_nvcertify mldsa44 sha512 +test_nvcertify mldsa65 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_quote.sh b/test/integration/tests/pqc_quote.sh new file mode 100755 index 000000000..9f4e93dd4 --- /dev/null +++ b/test/integration/tests/pqc_quote.sh @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f *.ctx + rm -f child.* + shut_down +} +trap cleanup EXIT + +start_up + +test_quotemldsa(){ + alg="$1" + alg2="$2" + + tpm2 clear + tpm2 createprimary -C o -G rsa -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt' + tpm2 create -C signkey.ctx -g "$2" -G "$1" -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -u child.pub -r child.priv -R + tpm2 load -C signkey.ctx -u child.pub -r child.priv -c child.ctx -R + tpm2 quote -c child.ctx -l 0x0004:16,17,18+0x000b:16,17,18 --scheme=mldsa +} + +test_quotemldsa mldsa44 sha256 +test_quotemldsa mldsa65 sha256 +test_quotemldsa mldsa44 sha512 +test_quotemldsa mldsa65 sha512 + +exit 0 diff --git a/test/integration/tests/pqc_sign.sh b/test/integration/tests/pqc_sign.sh new file mode 100755 index 000000000..396e6687a --- /dev/null +++ b/test/integration/tests/pqc_sign.sh @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: BSD-3-Clause +#!/usr/bin/env bash + +source helpers.sh + +cleanup() { + + rm -f signkey.ctx + rm -f mldsa.pub mldsa.priv mldsa.ctx + rm -f signature.bin msg.bin + rm -f verified.ticket + rm -f signpub.out + rm -f verifypub.ctx + rm -f signkey.ctx +} +trap cleanup EXIT + +start_up +tpm2 clear +test_pqc_sign(){ + alg="$1" + alg2="$2" + echo -n "This is a test message for verifysequence with MLDSA" > msg.bin + + tpm2 createprimary -C o -G "$1" -g "$2" -c signkey.ctx -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' + tpm2 signsequence -c signkey.ctx -i msg.bin -s signature.bin + tpm2 readpublic -c signkey.ctx -o signpub.out + tpm2 clear + + tpm2 loadexternal -u signpub.out -c verifypub.ctx + tpm2 verifysequence -c verifypub.ctx -i msg.bin -s signature.bin -t verified.ticket + tpm2 flushcontext -t +} + +test_pqc_sign mldsa44 sha256 +test_pqc_sign mldsa65 sha256 +test_pqc_sign mldsa44 sha512 +test_pqc_sign mldsa65 sha512 + + +exit 0 diff --git a/test/integration/tests/pqc_signdigest.sh b/test/integration/tests/pqc_signdigest.sh new file mode 100755 index 000000000..0dd17c504 --- /dev/null +++ b/test/integration/tests/pqc_signdigest.sh @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: BSD-3-Clause + +source helpers.sh + +cleanup() { + + rm -f primary.ctx + rm -f mldsa.pub mldsa.priv mldsa.ctx + rm -f mldsa2.pub mldsa2.priv mldsa2.ctx + rm -f badkey.pub badkey.priv badkey.ctx + rm -f msg.dat msg2.dat + rm -f digest.bin digest2.bin digest_bad.bin + rm -f sig.bin sig2.bin + rm -f validation.tkt +} +trap cleanup EXIT + +start_up + +keyauth=keypass + +test_signdigest(){ + alg="$1" + alg2="$2" + + tpm2 clear + #Create a primary parent + tpm2 createprimary -C o -g "$alg2" -G rsa -c primary.ctx + + # Create and load an MLDSA key + tpm2 create -C primary.ctx -G "$alg" -u mldsa.pub -r mldsa.priv -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign' -R + tpm2 load -C primary.ctx -u mldsa.pub -r mldsa.priv -c mldsa.ctx -R + + # Prepare test data + echo -n "This is a test message for verifydigestsignature with MLDSA" > msg.dat + echo -n "This is another message for verifydigestsignature with MLDSA" > msg2.dat + + # Compute digests externally + openssl dgst -sha512 -binary -out digest.bin msg.dat + openssl dgst -sha512 -binary -out digest2.bin msg2.dat + + + # Generate a valid digest signature first + tpm2 signdigest -c mldsa.ctx -o sig.bin -d digest.bin + test -s sig.bin + + + # TPM verifydigestsignature + tpm2 verifydigestsignature -c mldsa.ctx -s sig.bin -d digest.bin -t validation.tkt + test -s validation.tkt +} + +test_signdigest mldsa44 sha256 +test_signdigest mldsa65 sha256 +test_signdigest mldsa44 sha512 +test_signdigest mldsa65 sha512 + +exit 0 diff --git a/tools/tpm2_decapsulate.c b/tools/tpm2_decapsulate.c new file mode 100644 index 000000000..69c606042 --- /dev/null +++ b/tools/tpm2_decapsulate.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_capability.h" +#include "tpm2_tool.h" +#include "tpm2_alg_util.h" +#include "tpm2_util.h" + +typedef struct tpm_decapsulate_ctx tpm_decapsulate_ctx; +struct tpm_decapsulate_ctx { + + char *context_arg; + char *auth_arg; + char *in_ciphertext_file; + char *out_secret_file; + tpm2_loaded_object context_object; +}; + +static tpm_decapsulate_ctx ctx = {0}; + +static bool load_ciphertext(const char *path, TPM2B_KEM_CIPHERTEXT *ciphertext){ + + FILE *f = fopen(path, "rb"); + + if(!f){ + LOG_ERR("Cannot open ciphertext file : %s", path); + return false; + } + + size_t n = fread(ciphertext->buffer, 1, sizeof(ciphertext->buffer), f); + if (ferror(f)){ + fclose(f); + LOG_ERR("Error while reading ciphertext file: %s", path); + return false; + } + + fclose(f); + + if (n > sizeof(ciphertext->buffer)){ + LOG_ERR("Ciphertext file too large"); + return false; + } + + ciphertext->size = (UINT16)n; + return true; + +} + +static tool_rc check_options(void){ + + if(!ctx.context_arg){ + LOG_ERR("Expected option -c for object context"); + return tool_rc_option_error; + } + + if(!ctx.in_ciphertext_file){ + LOG_ERR("Expected option -i for ciphertext input"); + return tool_rc_option_error; + } + + if(!ctx.out_secret_file){ + LOG_ERR("Expected option -s for shared secret output"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + + +static tool_rc decapsulate_and_save(ESYS_CONTEXT *ectx) { + + TPM2B_SHARED_SECRET *sharedSecret = NULL; + TPM2B_KEM_CIPHERTEXT ciphertext = {0}; + + bool ret = load_ciphertext(ctx.in_ciphertext_file, &ciphertext); + if (!ret) { + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_Decapsulate(ectx, + ctx.context_object.tr_handle, + ESYS_TR_PASSWORD, + ESYS_TR_NONE, + ESYS_TR_NONE, + &ciphertext, + &sharedSecret); + + if (rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_Decapsulate, rval); + return tool_rc_general_error; + } + + if (!sharedSecret){ + LOG_ERR("TPM returned NULL shared secret"); + return tool_rc_general_error; + } + + tpm2_tool_output("shared secret: "); + for (UINT16 i=0; i < sharedSecret->size; i++){ + tpm2_tool_output("%02x", sharedSecret->buffer[i]); + } + tpm2_tool_output("\n"); + + ret = files_save_bytes_to_file(ctx.out_secret_file, + sharedSecret->buffer, + sharedSecret->size); + if (!ret) { + LOG_ERR("Cannot save shared secret"); + Esys_Free(sharedSecret); + return tool_rc_general_error; + } + Esys_Free(sharedSecret); + return tool_rc_success; + +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.context_arg = value; + break; + case 's': + ctx.out_secret_file = value; + break; + case 'p': + ctx.auth_arg = value; + break; + case 'i' : + ctx.in_ciphertext_file = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "object-context", required_argument, NULL, 'c' }, + { "shared-secret", required_argument, NULL, 's' }, + { "auth", required_argument, NULL, 'p' }, + { "ciphertext", required_argument, NULL, 'i' } + }; + + *opts = tpm2_options_new("c:p:i:s:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc init(ESYS_CONTEXT *ectx) { + + tool_rc rc = tpm2_util_object_load_auth(ectx, ctx.context_arg, + ctx.auth_arg, &ctx.context_object, false, + TPM2_HANDLE_ALL_W_NV); + if (rc != tool_rc_success) { + return rc; + } + + return tool_rc_success; +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *context, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(); + if (rc != tool_rc_success){ + return rc; + } + + rc = init(context); + if (rc != tool_rc_success) { + return rc; + } + + return decapsulate_and_save(context); +} + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("decapsulate", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL) diff --git a/tools/tpm2_encapsulate.c b/tools/tpm2_encapsulate.c new file mode 100644 index 000000000..9a9f586d6 --- /dev/null +++ b/tools/tpm2_encapsulate.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_convert.h" +#include "tpm2_tool.h" + +typedef struct tpm_encapsulate_ctx tpm_encapsulate_ctx; +struct tpm_encapsulate_ctx { + + char *context_arg; + char *out_secret_file; + char *out_ciphertext_file; + tpm2_loaded_object context_object; +}; + +static tpm_encapsulate_ctx ctx = {0}; + + +static tool_rc check_options(void){ + + if (!ctx.context_arg) { + LOG_ERR("Expected options -c for object context"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + +static tool_rc encapsulate_and_save(ESYS_CONTEXT *ectx) { + + tool_rc rc = tool_rc_general_error; + TPM2B_SHARED_SECRET *sharedSecret = NULL; + TPM2B_KEM_CIPHERTEXT *ciphertext = NULL; + + tool_rc tmp_rc = tpm2_encapsulate(ectx, ctx.context_object.tr_handle, + &ciphertext, &sharedSecret); + + if (tmp_rc != tool_rc_success) { + return tmp_rc; + } + + if (sharedSecret == NULL || ciphertext == NULL){ + LOG_ERR("TPM returned NULL output for encapsulate"); + goto error; + } + + tpm2_tool_output("shared secret: "); + UINT16 i; + for (i = 0; i < sharedSecret->size; i++) { + tpm2_tool_output("%02x", sharedSecret->buffer[i]); + } + tpm2_tool_output("\n"); + + tpm2_tool_output("ciphertext: "); + for (i = 0; i < ciphertext->size; i++) { + tpm2_tool_output("%02x", ciphertext->buffer[i]); + } + tpm2_tool_output("\n"); + + if (ctx.out_secret_file){ + bool ret = files_save_bytes_to_file(ctx.out_secret_file, + sharedSecret->buffer, + sharedSecret->size); + if (!ret) { + LOG_ERR("Cannot save shared secret file"); + goto error; + } + } + + if (ctx.out_ciphertext_file){ + bool ret = files_save_bytes_to_file(ctx.out_ciphertext_file, + ciphertext->buffer, + ciphertext->size); + if (!ret){ + LOG_ERR("Cannot save ciphertext file"); + goto error; + } + } + rc = tool_rc_success; + +error: + free(sharedSecret); + free(ciphertext); + + return rc; +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.context_arg = value; + break; + case 's': + ctx.out_secret_file = value; + break; + case 't': + ctx.out_ciphertext_file = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "object-context", required_argument, NULL, 'c' }, + { "shared-secret", required_argument, NULL, 's' }, + { "ciphertext", required_argument, NULL, 't' } + }; + + *opts = tpm2_options_new("c:s:t:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc init(ESYS_CONTEXT *context) { + + tool_rc rc = tpm2_util_object_load(context, ctx.context_arg, + &ctx.context_object, TPM2_HANDLE_ALL_W_NV); + if (rc != tool_rc_success) { + return rc; + } + + return tool_rc_success; +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *context, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(); + if (rc != tool_rc_success){ + return rc; + } + + rc = init(context); + if (rc != tool_rc_success) { + return rc; + } + + return encapsulate_and_save(context); +} + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("encapsulate", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL) diff --git a/tools/tpm2_getsessionauditdigest.c b/tools/tpm2_getsessionauditdigest.c index 365b8374d..fd7b1ece4 100644 --- a/tools/tpm2_getsessionauditdigest.c +++ b/tools/tpm2_getsessionauditdigest.c @@ -189,8 +189,8 @@ static tool_rc process_inputs(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { /* * Setup signature scheme */ - rc = tpm2_alg_util_get_signature_scheme(ectx, - ctx.key.object.tr_handle, &ctx.sig_hash_algorithm, TPM2_ALG_NULL, + rc = tpm2_alg_util_get_signature_scheme(ectx, + ctx.key.object.tr_handle, &ctx.sig_hash_algorithm, ctx.sig_scheme, &ctx.in_scheme); if (rc != tool_rc_success) { return rc; diff --git a/tools/tpm2_nvcertify.c b/tools/tpm2_nvcertify.c index ee7b13ce7..286d54cdb 100644 --- a/tools/tpm2_nvcertify.c +++ b/tools/tpm2_nvcertify.c @@ -387,8 +387,12 @@ static tool_rc check_options(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { goto is_input_options_args_valid_out; } - if (ctx.size == 0) { - ctx.size = nv_public->nvPublic.dataSize; + if (ctx.size == 0) { + if (ctx.sig_scheme == TPM2_ALG_MLDSA) { + ctx.size = 0; + } else { + ctx.size = nv_public->nvPublic.dataSize; + } } if (ctx.offset + ctx.size > nv_public->nvPublic.dataSize) { diff --git a/tools/tpm2_signdigest.c b/tools/tpm2_signdigest.c new file mode 100644 index 000000000..3e34c1dfe --- /dev/null +++ b/tools/tpm2_signdigest.c @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_convert.h" +#include "tpm2_tool.h" +#include "object.h" +#include "pcr.h" +#include "tpm2_alg_util.h" +#include "tpm2_auth_util.h" +#include "tpm2_hierarchy.h" +#include "tpm2_options.h" + +typedef struct tpm_signdigest_ctx tpm_signdigest_ctx; +struct tpm_signdigest_ctx { + + struct{ + const char *ctx_path; + const char *auth_str; + tpm2_loaded_object object; + }key; + + const char *context_path; + TPM2B_SIGNATURE_CTX context; + const char *digest_path; + TPM2B_DIGEST digest; + const char *ticket_path; + TPMT_TK_HASHCHECK validation; + + const char *signature_path; + TPMT_SIGNATURE *signature; +}; + +static tpm_signdigest_ctx ctx = { + .context ={ + .size = 0, + }, + .digest = { + .size = 0, + }, + .validation = { + .tag = TPM2_ST_HASHCHECK, + .hierarchy = TPM2_RH_NULL, + .digest = { + .size = 0, + }, + }, + .signature = NULL, +}; + +static bool load_signature_context(const char *path, TPM2B_SIGNATURE_CTX *context){ + + if (!path){ + context->size = 0; + return true; + } + + unsigned long size = 0; + bool result = files_get_file_size_path(path, &size); + if (!result){ + LOG_ERR("Unable to get signature context file: %s", path); + return false; + } + + if (size > sizeof(context->buffer)){ + LOG_ERR("Signature context too large: got %lu max %lu", size, sizeof(context->buffer)); + return false; + } + + context->size = size; + return files_load_bytes_from_path(path, context->buffer, &context->size); + +} + +static bool load_digest(const char *path, TPM2B_DIGEST *digest){ + + if (!path){ + LOG_ERR("Expected digest input file"); + return false; + } + + unsigned long size = 0; + bool result = files_get_file_size_path(path, &size); + if (!result){ + LOG_ERR("Unable to get digest file size: %s", path); + return false; + } + + if (size > sizeof(digest->buffer)){ + LOG_ERR("Digest too large: got %lu max %zu", size, sizeof(digest->buffer)); + return false; + } + + digest->size = size; + return files_load_bytes_from_path(path, digest->buffer, &digest->size); + +} + +static bool load_ticket(const char *path, TPMT_TK_HASHCHECK *validation){ + + if (!path){ + validation->tag = TPM2_ST_HASHCHECK; + validation->hierarchy = TPM2_RH_NULL; + validation->digest.size = 0; + return true; + } + + FILE *f = fopen(path, "rb"); + if (!f){ + LOG_ERR("Cannot open file"); + return false; + } + + UINT16 tag = 0; + UINT32 hierarchy = 0; + UINT16 digest_size = 0; + + if (fread(&tag, sizeof(tag), 1, f) != 1){ + LOG_ERR("Could not read ticket tag"); + fclose(f); + return false; + } + + if (fread(&hierarchy, sizeof(hierarchy), 1, f) != 1){ + LOG_ERR("Could not read ticket hierarchy"); + fclose(f); + return false; + } + + if (fread(&digest_size, sizeof(digest_size), 1, f) != 1){ + LOG_ERR("Could not read ticket digest size"); + fclose(f); + return false; + } + + if (digest_size > sizeof(validation->digest.buffer)){ + LOG_ERR("Ticket digest too large"); + fclose(f); + return false; + } + + if (fread(validation->digest.buffer, 1, digest_size, f) != digest_size){ + LOG_ERR("Could not read ticket digest bytes"); + fclose(f); + return false; + } + + fclose(f); + + validation->tag = tag; + validation->hierarchy = hierarchy; + validation->digest.size = digest_size; + + return true; +} + +static tool_rc check_options(void){ + + if (!ctx.key.ctx_path) { + LOG_ERR("Expected options -c for key object"); + return tool_rc_option_error; + } + + if (!ctx.digest_path) { + LOG_ERR("Expected options -d for input digest"); + return tool_rc_option_error; + } + + if (!ctx.signature_path) { + LOG_ERR("Expected options -o for signature output"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + +static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + + tool_rc rc = tpm2_util_object_load_auth(ectx, ctx.key.ctx_path, + ctx.key.auth_str, &ctx.key.object, false, + TPM2_HANDLE_ALL_W_NV); + + if (rc != tool_rc_success){ + return rc; + } + + bool result = load_signature_context(ctx.context_path, &ctx.context); + if(!result){ + return tool_rc_general_error; + } + + result = load_digest(ctx.digest_path, &ctx.digest); + if(!result){ + return tool_rc_general_error; + } + + result = load_ticket(ctx.ticket_path, &ctx.validation); + if(!result){ + return tool_rc_general_error; + } + + return tool_rc_success; +} + +static tool_rc sign_digest(ESYS_CONTEXT *ectx){ + + ESYS_TR shandle1 = ESYS_TR_PASSWORD; + + TSS2_RC rval = Esys_SignDigest(ectx, ctx.key.object.tr_handle, + shandle1, ESYS_TR_NONE, ESYS_TR_NONE, + &ctx.context, &ctx.digest, &ctx.validation, &ctx.signature); + + if (rval != TPM2_RC_SUCCESS){ + LOG_PERR(Esys_SignDigest, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +static bool save_signature_file(const char *path, TPMT_SIGNATURE *signature){ + + if(!path){ + return true; + } + + UINT8 buffer[8192]; + size_t offset = 0; + + TSS2_RC rval = Tss2_MU_TPMT_SIGNATURE_Marshal(signature, buffer, + sizeof(buffer), &offset); + if (rval != TSS2_RC_SUCCESS){ + return false; + } + + FILE *f = fopen(path, "wb"); + if (!f){ + LOG_ERR("Cannot open signature output file"); + return false; + } + + size_t size_file = fwrite(buffer, 1, offset, f); + fclose(f); + + if(size_file != offset){ + LOG_ERR("Cannot save signature file"); + return false; + } + + return true; + + +} + +static tool_rc process_output(ESYS_CONTEXT *ectx){ + + UNUSED(ectx); + + bool result = save_signature_file(ctx.signature_path, ctx.signature); + if(!result){ + LOG_ERR("Could not save signature to file : %s", ctx.signature_path); + return tool_rc_general_error; + } + + return tool_rc_success; +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.key.ctx_path = value; + break; + case 'p': + ctx.key.auth_str = value; + break; + case 'g': + ctx.context_path = value; + break; + case 'd': + ctx.digest_path = value; + break; + case 't': + ctx.ticket_path = value; + break; + case 'o': + ctx.signature_path = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "key-context", required_argument, NULL, 'c' }, + { "auth", required_argument, NULL, 'p' }, + { "context", required_argument, NULL, 'g' }, + { "digest", required_argument, NULL, 'd' }, + { "ticket", required_argument, NULL, 't' }, + { "signature", required_argument, NULL, 'o' } + }; + + + *opts = tpm2_options_new("c:p:g:d:t:o:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(); + if (rc != tool_rc_success){ + return rc; + } + + rc = process_inputs(ectx); + if (rc != tool_rc_success) { + return rc; + } + + rc = sign_digest(ectx); + if (rc != tool_rc_success) { + return rc; + } + + return process_output(ectx); +} + +static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) { + + UNUSED(ectx); + + free(ctx.signature); + + return tpm2_session_close(&ctx.key.object.session); +} + + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("signdigest", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, NULL) diff --git a/tools/tpm2_signsequenceflow.c b/tools/tpm2_signsequenceflow.c new file mode 100644 index 000000000..f7825230a --- /dev/null +++ b/tools/tpm2_signsequenceflow.c @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_convert.h" +#include "tpm2_tool.h" +#include "object.h" +#include "pcr.h" +#include "tpm2_alg_util.h" +#include "tpm2_auth_util.h" +#include "tpm2_hierarchy.h" +#include "tpm2_options.h" + +typedef struct tpm_signsequenceflow_ctx tpm_signsequenceflow_ctx; +struct tpm_signsequenceflow_ctx { + + char *sequence_auth_arg; + char *key_context_arg; + char *key_auth_arg; + char *input_file; + char *signature_file; + + tpm2_loaded_object key_object; + tpm2_session *sequence_auth_session; + tpm2_session *key_auth_session; + ESYS_TR sequence_handle; +}; + +static tpm_signsequenceflow_ctx ctx = { + .sequence_auth_session = NULL, + .key_auth_session = NULL, + .sequence_handle = ESYS_TR_NONE, +}; + +static bool load_file(const char *path, UINT8 **data, size_t *size){ + + *data = NULL; + *size = 0; + + FILE *f = fopen(path, "rb"); + if (!f){ + LOG_ERR("Cannot open file"); + return false; + } + + if (fseek(f, 0, SEEK_END) != 0){ + fclose(f); + return false; + } + + long sz = ftell(f); + if (sz < 0){ + fclose(f); + return false; + } + + if(fseek(f, 0, SEEK_SET) != 0){ + fclose(f); + return false; + } + + UINT8 *buf = calloc(1, (size_t)sz ? (size_t)sz : 1); + if (!buf){ + fclose(f); + return false; + } + + size_t size_file = fread(buf, 1, (size_t)sz, f); + if (ferror(f)){ + free(buf); + fclose(f); + LOG_ERR("Error while reading file"); + return false; + } + + fclose(f); + + *data = buf; + *size = size_file; + + return true; +} + +static bool save_signature_file(const char *path, TPMT_SIGNATURE *signature){ + + if(!path){ + return true; + } + + UINT8 buffer[8192]; + size_t offset = 0; + + TSS2_RC rval = Tss2_MU_TPMT_SIGNATURE_Marshal(signature, buffer, + sizeof(buffer), &offset); + if (rval != TSS2_RC_SUCCESS){ + return false; + } + + FILE *f = fopen(path, "wb"); + if (!f){ + LOG_ERR("Cannot open signature output file"); + return false; + } + + size_t size_file = fwrite(buffer, 1, offset, f); + fclose(f); + + if(size_file != offset){ + LOG_ERR("Cannot save signature file"); + return false; + } + + return true; + + +} + +static tool_rc check_options(void){ + + if (!ctx.key_context_arg) { + LOG_ERR("Expected options -c for key context"); + return tool_rc_option_error; + } + + if (!ctx.input_file) { + LOG_ERR("Expected options -i for input data"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + +static tool_rc sign_sequence_start(ESYS_CONTEXT *ectx) { + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + TPM2B_SIGNATURE_CTX context = {0}; + + tool_rc rc= tpm2_signsequencestart(ectx, + ctx.key_object.tr_handle, + (TPM2B_AUTH *)seq_auth, + &context, + &ctx.sequence_handle); + if (rc != tool_rc_success){ + return rc; + } + + return tool_rc_success; + +} + +static tool_rc do_sequence_update(ESYS_CONTEXT *ectx, const UINT8 *data, size_t sz) { + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_TR_SetAuth(ectx, ctx.sequence_handle, seq_auth); + if(rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + TPM2B_MAX_BUFFER buffer = {0}; + buffer.size = (UINT16)sz; + memcpy(buffer.buffer, data, sz); + + return tpm2_sequence_update(ectx, ctx.sequence_handle, &buffer); + +} + +static tool_rc sign_sequence_complete(ESYS_CONTEXT *ectx, const UINT8 *data, size_t sz) { + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + const TPM2B_AUTH *key_auth = tpm2_session_get_auth_value(ctx.key_auth_session); + if(!key_auth){ + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_TR_SetAuth(ectx, ctx.sequence_handle, seq_auth); + if(rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + rval = Esys_TR_SetAuth(ectx, ctx.key_object.tr_handle, key_auth); + if(rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + TPM2B_MAX_BUFFER buffer = {0}; + buffer.size = (UINT16)sz; + memcpy(buffer.buffer, data, sz); + + TPMT_SIGNATURE *signature = NULL; + + tool_rc rc = tpm2_signsequencecomplete(ectx, + ctx.sequence_handle, + ctx.key_object.tr_handle, + &buffer, + &signature); + if(rc!=tool_rc_success){ + return rc; + } + + if (!signature){ + LOG_ERR("TPM returned NULL signature"); + return tool_rc_general_error; + } + + bool ok = save_signature_file(ctx.signature_file, signature); + if (!ok){ + Esys_Free(signature); + return tool_rc_general_error; + } + + tpm2_tool_output("signature generated\n"); + Esys_Free(signature); + + ctx.sequence_handle = ESYS_TR_NONE; + + return tool_rc_success; +} + +static tool_rc run_flow(ESYS_CONTEXT *ectx){ + + UINT8 *msg = NULL; + size_t msg_size = 0; + + bool ok = load_file(ctx.input_file, &msg, &msg_size); + if(!ok){ + return tool_rc_general_error; + } + + tool_rc rc = sign_sequence_start(ectx); + if (rc != tool_rc_success){ + free(msg); + return rc; + } + + const size_t max_chunk = sizeof(((TPM2B_MAX_BUFFER *)0)->buffer); + + if (msg_size == 0){ + rc = sign_sequence_complete(ectx, msg, 0); + free(msg); + return rc; + } + + if (msg_size <= max_chunk){ + rc = sign_sequence_complete(ectx, msg, msg_size); + free(msg); + return rc; + } + + size_t offset = 0; + + while ((msg_size - offset) > max_chunk){ + size_t remaining = msg_size - offset; + size_t this_chunk = remaining - offset > max_chunk ? max_chunk : remaining; + + if((msg_size - (offset + this_chunk)) == 0){ + break; + } + + rc = do_sequence_update(ectx, msg + offset, this_chunk); + if (rc != tool_rc_success){ + free(msg); + return rc; + } + + offset += this_chunk; + + if ((msg_size - offset) <= max_chunk){ + break; + } + } + + rc = sign_sequence_complete(ectx, msg + offset, msg_size - offset); + free(msg); + return rc; + +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.key_context_arg = value; + break; + case 'P': + ctx.key_auth_arg = value; + break; + case 'p': + ctx.sequence_auth_arg = value; + break; + case 'i': + ctx.input_file = value; + break; + case 's': + ctx.signature_file = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "key-context", required_argument, NULL, 'c' }, + { "key-auth", required_argument, NULL, 'P' }, + { "sequence-auth", required_argument, NULL, 'p' }, + { "input", required_argument, NULL, 'i' }, + { "signature", required_argument, NULL, 's' } + }; + + + *opts = tpm2_options_new("c:P:p:i:s:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc init(ESYS_CONTEXT *ectx) { + + + tool_rc rc = tpm2_util_object_load(ectx, ctx.key_context_arg, + &ctx.key_object, TPM2_HANDLE_ALL_W_NV); + if (rc != tool_rc_success) { + return rc; + } + + rc = tpm2_auth_util_from_optarg(ectx, ctx.key_auth_arg, &ctx.key_auth_session, false); + if (rc != tool_rc_success) { + return rc; + } + + const TPM2B_AUTH *key_auth = tpm2_session_get_auth_value(ctx.key_auth_session); + if(!key_auth){ + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_TR_SetAuth(ectx, ctx.key_object.tr_handle, key_auth); + + if (rval != TSS2_RC_SUCCESS) { + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + rc = tpm2_auth_util_from_optarg(ectx, ctx.sequence_auth_arg, &ctx.sequence_auth_session, false); + if (rc != tool_rc_success) { + return rc; + } + + return tool_rc_success; + +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(); + if (rc != tool_rc_success){ + return rc; + } + + rc = init(ectx); + if (rc != tool_rc_success) { + return rc; + } + + return run_flow(ectx); +} + +static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) { + + tool_rc rc = tool_rc_success; + tool_rc tmp_rc; + + if (ctx.sequence_handle != ESYS_TR_NONE){ + TSS2_RC rval = Esys_TR_Close(ectx, &ctx.sequence_handle); + if (rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_Close, rval); + rc = tool_rc_general_error; + } + } + + if (ctx.key_object.tr_handle != ESYS_TR_NONE){ + TSS2_RC rval = Esys_TR_Close(ectx, &ctx.key_object.tr_handle); + if (rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_Close, rval); + rc = tool_rc_general_error; + } + } + + tmp_rc = tpm2_session_close(&ctx.sequence_auth_session); + if (tmp_rc != tool_rc_success && rc == tool_rc_success){ + rc = tmp_rc; + } + + tmp_rc = tpm2_session_close(&ctx.key_auth_session); + if (tmp_rc != tool_rc_success && rc == tool_rc_success){ + rc = tmp_rc; + } + + tmp_rc = tpm2_session_close(&ctx.key_object.session); + if (tmp_rc != tool_rc_success && rc == tool_rc_success){ + rc = tmp_rc; + } + + return rc; +} + + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("signsequence", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, NULL) diff --git a/tools/tpm2_verifydigestsignature.c b/tools/tpm2_verifydigestsignature.c new file mode 100644 index 000000000..9180c099d --- /dev/null +++ b/tools/tpm2_verifydigestsignature.c @@ -0,0 +1,320 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_convert.h" +#include "tpm2_tool.h" +#include "object.h" +#include "pcr.h" +#include "tpm2_alg_util.h" +#include "tpm2_auth_util.h" +#include "tpm2_hierarchy.h" +#include "tpm2_options.h" + +typedef struct tpm_verifydigestsignature_ctx tpm_verifydigestsignature_ctx; +struct tpm_verifydigestsignature_ctx { + + struct{ + const char *ctx_path; + tpm2_loaded_object object; + }key; + + const char *context_path; + TPM2B_SIGNATURE_CTX context; + const char *digest_path; + TPM2B_DIGEST digest; + const char *signature_path; + TPMT_SIGNATURE signature; + + const char *validation_path; + TPMT_TK_VERIFIED *validation; +}; + +static tpm_verifydigestsignature_ctx ctx = { + .context ={ + .size = 0, + }, + .digest = { + .size = 0, + }, + .signature = {0} , + .validation = NULL, +}; + +static bool load_signature_context(const char *path, TPM2B_SIGNATURE_CTX *context){ + + if (!path){ + context->size = 0; + return true; + } + + unsigned long size = 0; + bool result = files_get_file_size_path(path, &size); + if (!result){ + LOG_ERR("Unable to get signature context file: %s", path); + return false; + } + + if (size > sizeof(context->buffer)){ + LOG_ERR("Signature context too large: got %lu max %lu", size, sizeof(context->buffer)); + return false; + } + + context->size = size; + return files_load_bytes_from_path(path, context->buffer, &context->size); + +} + +static bool load_digest(const char *path, TPM2B_DIGEST *digest){ + + if (!path){ + LOG_ERR("Expected digest input file"); + return false; + } + + unsigned long size = 0; + bool result = files_get_file_size_path(path, &size); + if (!result){ + LOG_ERR("Unable to get digest file size: %s", path); + return false; + } + + if (size > sizeof(digest->buffer)){ + LOG_ERR("Digest too large: got %lu max %zu", size, sizeof(digest->buffer)); + return false; + } + + digest->size = size; + return files_load_bytes_from_path(path, digest->buffer, &digest->size); + +} + +static bool load_signature_file(const char *path, TPMT_SIGNATURE *signature){ + + FILE *f = fopen(path, "rb"); + if (!f){ + LOG_ERR("Cannot open file"); + return false; + } + + UINT8 buffer[8192]; + size_t size_file = fread(buffer, 1, sizeof(buffer), f); + fclose(f); + + size_t offset = 0; + TSS2_RC rval = Tss2_MU_TPMT_SIGNATURE_Unmarshal(buffer, size_file, &offset, signature); + if (rval != TSS2_RC_SUCCESS){ + return false; + } + return true; + +} + +static bool save_validation(const char *path, TPMT_TK_VERIFIED *validation){ + + if(!path){ + return true; + } + + FILE *f = fopen(path, "wb"); + if (!f){ + LOG_ERR("Cannot open validation output file"); + return false; + } + + bool ok = true; + + if (fwrite(&validation->tag, sizeof(validation->tag), 1, f) != 1){ + ok = false; + } + + if (ok && fwrite(&validation->hierarchy,sizeof(validation->hierarchy), 1, f) != 1) { + ok = false; + } + + if (ok && fwrite(&validation->digest.size,sizeof(validation->digest.size), 1, f) != 1) { + ok = false; + } + + if (ok && validation->digest.size > 0){ + if (fwrite(validation->digest.buffer, 1, validation->digest.size, f) != validation->digest.size) { + ok = false; + } + } + + fclose(f); + + if (!ok){ + LOG_ERR("could not write validation ticket"); + } + + return ok; +} + + +static tool_rc check_options(ESYS_CONTEXT *ectx){ + + UNUSED(ectx); + + if (!ctx.key.ctx_path) { + LOG_ERR("Expected options -c for key object"); + return tool_rc_option_error; + } + + if (!ctx.digest_path) { + LOG_ERR("Expected options -d for input digest"); + return tool_rc_option_error; + } + + if (!ctx.signature_path) { + LOG_ERR("Expected options -s for signature output"); + return tool_rc_option_error; + } + + if (!ctx.validation_path) { + LOG_ERR("Expected options -t for signature output"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + +static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + + tool_rc rc = tpm2_util_object_load_auth(ectx, ctx.key.ctx_path, + NULL, &ctx.key.object, false, + TPM2_HANDLE_ALL_W_NV); + + if (rc != tool_rc_success){ + return rc; + } + + bool result = load_signature_context(ctx.context_path, &ctx.context); + if(!result){ + return tool_rc_general_error; + } + + result = load_digest(ctx.digest_path, &ctx.digest); + if(!result){ + return tool_rc_general_error; + } + + result = load_signature_file(ctx.signature_path, &ctx.signature); + if(!result){ + return tool_rc_general_error; + } + + return tool_rc_success; +} + +static tool_rc verify_digest_signature(ESYS_CONTEXT *ectx){ + + TSS2_RC rval = Esys_VerifyDigestSignature(ectx, ctx.key.object.tr_handle, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + &ctx.context, &ctx.digest, &ctx.signature, &ctx.validation); + + if (rval != TPM2_RC_SUCCESS){ + LOG_PERR(Esys_VerifyDigestSignature, rval); + return tool_rc_from_tpm(rval); + } + + return tool_rc_success; +} + +static tool_rc process_output(ESYS_CONTEXT *ectx){ + + UNUSED(ectx); + + bool result = save_validation(ctx.validation_path, ctx.validation); + if(!result){ + return tool_rc_general_error; + } + + return tool_rc_success; +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.key.ctx_path = value; + break; + case 'g': + ctx.context_path = value; + break; + case 'd': + ctx.digest_path = value; + break; + case 's': + ctx.signature_path = value; + break; + case 't': + ctx.validation_path = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "key-context", required_argument, NULL, 'c' }, + { "context", required_argument, NULL, 'g' }, + { "digest", required_argument, NULL, 'd' }, + { "signature", required_argument, NULL, 's' }, + { "validation", required_argument, NULL, 't' } + }; + + + *opts = tpm2_options_new("c:g:d:s:t:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(ectx); + if (rc != tool_rc_success){ + return rc; + } + + rc = process_inputs(ectx); + if (rc != tool_rc_success) { + return rc; + } + + rc = verify_digest_signature(ectx); + if (rc != tool_rc_success) { + return rc; + } + + return process_output(ectx); +} + +static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) { + + UNUSED(ectx); + + return tpm2_session_close(&ctx.key.object.session); +} + + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("verifydigestsignature", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, NULL) diff --git a/tools/tpm2_verifysequenceflow.c b/tools/tpm2_verifysequenceflow.c new file mode 100644 index 000000000..ab9a6400b --- /dev/null +++ b/tools/tpm2_verifysequenceflow.c @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/***********************************************************************; +* Copyright (c) 2026, STMicroelectronics +* +* All rights reserved. +***********************************************************************/ + +#include +#include +#include +#include + +#include "files.h" +#include "log.h" +#include "tpm2.h" +#include "tpm2_convert.h" +#include "tpm2_tool.h" +#include "object.h" +#include "pcr.h" +#include "tpm2_alg_util.h" +#include "tpm2_auth_util.h" +#include "tpm2_hierarchy.h" +#include "tpm2_options.h" + +typedef struct tpm_verifysequenceflow_ctx tpm_verifysequenceflow_ctx; +struct tpm_verifysequenceflow_ctx { + + char *context_arg; + char *sequence_auth_arg; + char *input_file; + char *signature_file; + char *ticket_file; + char *hint_file; + char *context_file; + + tpm2_loaded_object key_context_object; + tpm2_session *sequence_auth_session; + ESYS_TR sequence_handle; + +}; + +static tpm_verifysequenceflow_ctx ctx = { + .sequence_auth_session = NULL, + .sequence_handle = ESYS_TR_NONE, +}; + +static bool load_file(const char *path, UINT8 **data, size_t *size){ + + *data = NULL; + *size = 0; + + FILE *f = fopen(path, "rb"); + if (!f){ + LOG_ERR("Cannot open file"); + return false; + } + + if (fseek(f, 0, SEEK_END) != 0){ + fclose(f); + return false; + } + + long sz = ftell(f); + if (sz < 0){ + fclose(f); + return false; + } + + if(fseek(f, 0, SEEK_SET) != 0){ + fclose(f); + return false; + } + + UINT8 *buf = calloc(1, (size_t)sz ? (size_t)sz : 1); + if (!buf){ + fclose(f); + return false; + } + + size_t size_file = fread(buf, 1, (size_t)sz, f); + if (ferror(f)){ + free(buf); + fclose(f); + LOG_ERR("Error while reading file"); + return false; + } + + fclose(f); + + *data = buf; + *size = size_file; + + return true; +} + +static bool load_signature_file(const char *path, TPMT_SIGNATURE *signature){ + + FILE *f = fopen(path, "rb"); + if (!f){ + LOG_ERR("Cannot open file"); + return false; + } + + UINT8 buffer[8192]; + size_t size_file = fread(buffer, 1, sizeof(buffer), f); + fclose(f); + + size_t offset = 0; + TSS2_RC rval = Tss2_MU_TPMT_SIGNATURE_Unmarshal(buffer, size_file, &offset, signature); + if (rval != TSS2_RC_SUCCESS){ + return false; + } + return true; + +} + +static bool save_ticket_file(const char *path, TPMT_TK_VERIFIED *ticket){ + + if(!path){ + return true; + } + + FILE *f = fopen(path, "wb"); + if (!f){ + LOG_ERR("Cannot open validation output file"); + return false; + } + + size_t size_file = fwrite(ticket, 1, sizeof(*ticket), f); + fclose(f); + + if(size_file != sizeof(*ticket)){ + LOG_ERR("Cannot save validation file"); + return false; + } + + return true; + +} + +static tool_rc check_options(void){ + + if (!ctx.context_arg) { + LOG_ERR("Expected options -c for key context"); + return tool_rc_option_error; + } + + if (!ctx.input_file) { + LOG_ERR("Expected options -i for input message"); + return tool_rc_option_error; + } + + if (!ctx.signature_file) { + LOG_ERR("Expected options -s for signature input"); + return tool_rc_option_error; + } + + return tool_rc_success; +} + +static tool_rc verify_sequence_start(ESYS_CONTEXT *ectx) { + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + TPM2B_SIGNATURE_HINT hint = {0}; + TPM2B_SIGNATURE_CTX context = {0}; + + if (ctx.hint_file){ + UINT8 *buf = NULL; + size_t size_file = 0; + bool ok = load_file(ctx.hint_file, &buf, &size_file); + if (!ok){ + return tool_rc_general_error; + } + if (size_file > sizeof(hint.buffer)){ + free(buf); + return tool_rc_general_error; + } + hint.size = (UINT16)size_file; + memcpy(hint.buffer, buf, size_file); + free(buf); + } + + if (ctx.context_file){ + UINT8 *buf = NULL; + size_t size_file = 0; + bool ok = load_file(ctx.context_file, &buf, &size_file); + if (!ok){ + return tool_rc_general_error; + } + if (size_file > sizeof(context.buffer)){ + free(buf); + return tool_rc_general_error; + } + context.size = (UINT16)size_file; + memcpy(context.buffer, buf, size_file); + free(buf); + + } + + return tpm2_verifysequencestart(ectx, + ctx.key_context_object.tr_handle, + (TPM2B_AUTH *)seq_auth, + &hint, + &context, + &ctx.sequence_handle); +} + +static tool_rc do_sequence_update(ESYS_CONTEXT *ectx, const UINT8 *data, size_t size_file){ + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_TR_SetAuth(ectx, ctx.sequence_handle, seq_auth); + if(rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + TPM2B_MAX_BUFFER buffer = {0}; + buffer.size = (UINT16)size_file; + memcpy(buffer.buffer, data, size_file); + + return tpm2_sequence_update(ectx, ctx.sequence_handle, &buffer); + +} + +static tool_rc verify_sequence_complete(ESYS_CONTEXT *ectx, TPMT_SIGNATURE *signature) { + + const TPM2B_AUTH *seq_auth = tpm2_session_get_auth_value(ctx.sequence_auth_session); + if(!seq_auth){ + return tool_rc_general_error; + } + + TSS2_RC rval = Esys_TR_SetAuth(ectx, ctx.sequence_handle, seq_auth); + if(rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_SetAuth, rval); + return tool_rc_general_error; + } + + TPMT_TK_VERIFIED *validation = NULL; + + tool_rc rc = tpm2_verifysequencecomplete(ectx, + ctx.sequence_handle, + ctx.key_context_object.tr_handle, + signature, + &validation); + if (rc!= tool_rc_success){ + return rc; + } + + if(!validation){ + return tool_rc_general_error; + } + + bool ok = save_ticket_file(ctx.ticket_file, validation); + if(!ok){ + Esys_Free(validation); + return tool_rc_general_error; + } + + tpm2_tool_output("signature verified\n"); + + Esys_Free(validation); + + ctx.sequence_handle = ESYS_TR_NONE; + + return tool_rc_success; +} + +static tool_rc run_flow(ESYS_CONTEXT *ectx){ + + UINT8 *msg = NULL; + size_t msg_size = 0; + TPMT_SIGNATURE signature; + + bool ok = load_file(ctx.input_file, &msg, &msg_size); + if(!ok){ + return tool_rc_general_error; + } + + ok = load_signature_file(ctx.signature_file, &signature); + if(!ok){ + free(msg); + return tool_rc_general_error; + } + + tool_rc rc = verify_sequence_start(ectx); + if (rc!= tool_rc_success){ + free(msg); + return rc; + } + + const size_t max_chunk = sizeof(((TPM2B_MAX_BUFFER *)0)->buffer); + + if(msg_size == 0){ + rc = verify_sequence_complete(ectx, &signature); + free(msg); + return rc; + } + + size_t offset = 0; + + while (offset < msg_size){ + size_t remaining = msg_size - offset; + size_t this_chunk = remaining > max_chunk ? max_chunk : remaining; + + rc = do_sequence_update(ectx, msg + offset, this_chunk); + if (rc != tool_rc_success){ + free(msg); + return rc; + } + + offset += this_chunk; + + } + + + rc = verify_sequence_complete(ectx, &signature); + free(msg); + return rc; + +} + +static bool on_option(char key, char *value) { + + switch (key) { + case 'c': + ctx.context_arg = value; + break; + case 'p': + ctx.sequence_auth_arg = value; + break; + case 'i': + ctx.input_file = value; + break; + case 's': + ctx.signature_file = value; + break; + case 't': + ctx.ticket_file = value; + break; + case 'h': + ctx.hint_file = value; + break; + case 'C': + ctx.context_file = value; + break; + default: + return false; + } + + return true; +} + +static bool tpm2_tool_onstart(tpm2_options **opts) { + + static const struct option topts[] = { + { "key-context", required_argument, NULL, 'c' }, + { "sequence-auth", required_argument, NULL, 'p' }, + { "input", required_argument, NULL, 'i' }, + { "signature", required_argument, NULL, 's' }, + { "ticket", required_argument, NULL, 't' }, + { "hint", required_argument, NULL, 'h' }, + { "context", required_argument, NULL, 'C' } + }; + + + *opts = tpm2_options_new("c:p:i:s:t:h:C:", ARRAY_LEN(topts), topts, on_option, + NULL, 0); + + return *opts != NULL; +} + +static tool_rc init(ESYS_CONTEXT *ectx) { + + tool_rc rc = tpm2_util_object_load(ectx, ctx.context_arg, + &ctx.key_context_object, TPM2_HANDLE_ALL_W_NV); + if (rc != tool_rc_success) { + return rc; + } + + rc = tpm2_auth_util_from_optarg(ectx, ctx.sequence_auth_arg, + &ctx.sequence_auth_session, false); + if (rc != tool_rc_success) { + return rc; + } + + return tool_rc_success; +} + +static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { + + UNUSED(flags); + + tool_rc rc = check_options(); + if (rc != tool_rc_success){ + return rc; + } + + rc = init(ectx); + if (rc != tool_rc_success) { + return rc; + } + + return run_flow(ectx); +} + +static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) { + + tool_rc rc = tool_rc_success; + tool_rc tmp_rc; + + if (ctx.sequence_handle != ESYS_TR_NONE){ + TSS2_RC rval = Esys_TR_Close(ectx, &ctx.sequence_handle); + if (rval != TSS2_RC_SUCCESS){ + LOG_PERR(Esys_TR_Close, rval); + rc = tool_rc_general_error; + } + } + + tmp_rc = tpm2_session_close(&ctx.sequence_auth_session); + if (tmp_rc != tool_rc_success && rc == tool_rc_success){ + rc = tmp_rc; + } + + tmp_rc = tpm2_session_close(&ctx.key_context_object.session); + if (tmp_rc != tool_rc_success && rc == tool_rc_success){ + rc = tmp_rc; + } + + return rc; +} + + +// Register this tool with tpm2_tool.c +TPM2_TOOL_REGISTER("verifysequence", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, NULL)