@@ -667,6 +667,277 @@ static void test_wolfTPM2_StartSession_SaltedEncryptAttrs(void)
667667#endif
668668}
669669
670+ /* Bind an AES-CFB param-enc session to an EmptyAuth SRK and create a child
671+ * under it. The pre-fix code left a bound EmptyAuth sessionKey empty, breaking
672+ * the HMAC; this command fails pre-fix. */
673+ static void test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc (void )
674+ {
675+ #if !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
676+ (!defined(NO_RSA ) || defined(HAVE_ECC ))
677+ int rc ;
678+ WOLFTPM2_DEV dev ;
679+ WOLFTPM2_KEY srk ;
680+ WOLFTPM2_KEY child ;
681+ WOLFTPM2_SESSION session ;
682+ TPMT_PUBLIC publicTemplate ;
683+ #if !defined(NO_RSA )
684+ TPM_ALG_ID srkAlg = TPM_ALG_RSA ;
685+ #else
686+ TPM_ALG_ID srkAlg = TPM_ALG_ECC ;
687+ #endif
688+
689+ XMEMSET (& dev , 0 , sizeof (dev ));
690+ XMEMSET (& srk , 0 , sizeof (srk ));
691+ XMEMSET (& child , 0 , sizeof (child ));
692+ XMEMSET (& session , 0 , sizeof (session ));
693+ XMEMSET (& publicTemplate , 0 , sizeof (publicTemplate ));
694+
695+ /* Skip cleanly when no TPM is reachable. */
696+ rc = wolfTPM2_Init (& dev , TPM2_IoCb , NULL );
697+ if (rc != 0 ) {
698+ printf ("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tSkipped\n" );
699+ return ;
700+ }
701+
702+ /* Storage root key with an EmptyAuth (auth NULL, authSz 0). */
703+ rc = wolfTPM2_CreateSRK (& dev , & srk , srkAlg , NULL , 0 );
704+ if (rc != 0 ) {
705+ /* Environmental (TPM busy / unsupported). Treat as skip. */
706+ wolfTPM2_Cleanup (& dev );
707+ printf ("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tSkipped\n" );
708+ return ;
709+ }
710+
711+ /* Bind an HMAC session to the EmptyAuth SRK with AES-CFB param enc. */
712+ rc = wolfTPM2_StartSession (& dev , & session , NULL , & srk .handle ,
713+ TPM_SE_HMAC , TPM_ALG_CFB );
714+ AssertIntEQ (rc , TPM_RC_SUCCESS );
715+
716+ /* Slot 1: the create/load wrappers own slot 0 (parent auth), so the
717+ * param-enc session lives in slot 1 to survive into the command. */
718+ rc = wolfTPM2_SetAuthSession (& dev , 1 , & session ,
719+ (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
720+ TPMA_SESSION_continueSession ));
721+ AssertIntEQ (rc , TPM_RC_SUCCESS );
722+
723+ /* Create+load a child under the EmptyAuth SRK; fails pre-fix. */
724+ #if !defined(NO_RSA )
725+ rc = wolfTPM2_GetKeyTemplate_RSA (& publicTemplate ,
726+ TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
727+ TPMA_OBJECT_sign | TPMA_OBJECT_noDA );
728+ #else
729+ rc = wolfTPM2_GetKeyTemplate_ECC (& publicTemplate ,
730+ TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
731+ TPMA_OBJECT_sign | TPMA_OBJECT_noDA ,
732+ TPM_ECC_NIST_P256 , TPM_ALG_ECDSA );
733+ #endif
734+ AssertIntEQ (rc , TPM_RC_SUCCESS );
735+
736+ rc = wolfTPM2_CreateAndLoadKey (& dev , & child , & srk .handle ,
737+ & publicTemplate , NULL , 0 );
738+ AssertIntEQ (rc , TPM_RC_SUCCESS );
739+
740+ /* Clear the session slot, then release handles. */
741+ wolfTPM2_SetAuthSession (& dev , 1 , NULL , 0 );
742+ wolfTPM2_UnloadHandle (& dev , & child .handle );
743+ wolfTPM2_UnloadHandle (& dev , & session .handle );
744+ wolfTPM2_UnloadHandle (& dev , & srk .handle );
745+ wolfTPM2_Cleanup (& dev );
746+ printf ("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tPassed\n" );
747+ #endif
748+ }
749+
750+ /* Run TPM2_CreateLoaded under a salted AES-CFB param-enc session. Pre-fix the
751+ * missing response outHandleCnt mis-parsed the rpHash offset and the reply was
752+ * rejected with TPM_RC_HMAC. */
753+ static void test_wolfTPM2_CreateLoaded_ParamEnc (void )
754+ {
755+ #if !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
756+ (!defined(NO_RSA ) || defined(HAVE_ECC ))
757+ int rc ;
758+ WOLFTPM2_DEV dev ;
759+ WOLFTPM2_KEY srk ;
760+ WOLFTPM2_KEYBLOB child ;
761+ WOLFTPM2_SESSION session ;
762+ TPMT_PUBLIC publicTemplate ;
763+ #if !defined(NO_RSA )
764+ TPM_ALG_ID srkAlg = TPM_ALG_RSA ;
765+ #else
766+ TPM_ALG_ID srkAlg = TPM_ALG_ECC ;
767+ #endif
768+
769+ XMEMSET (& dev , 0 , sizeof (dev ));
770+ XMEMSET (& srk , 0 , sizeof (srk ));
771+ XMEMSET (& child , 0 , sizeof (child ));
772+ XMEMSET (& session , 0 , sizeof (session ));
773+ XMEMSET (& publicTemplate , 0 , sizeof (publicTemplate ));
774+
775+ /* Skip cleanly when no TPM is reachable. */
776+ rc = wolfTPM2_Init (& dev , TPM2_IoCb , NULL );
777+ if (rc != 0 ) {
778+ printf ("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n" );
779+ return ;
780+ }
781+
782+ rc = wolfTPM2_CreateSRK (& dev , & srk , srkAlg , NULL , 0 );
783+ if (rc != 0 ) {
784+ /* Environmental (TPM busy / unsupported). Treat as skip. */
785+ wolfTPM2_Cleanup (& dev );
786+ printf ("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n" );
787+ return ;
788+ }
789+
790+ /* Salted AES-CFB parameter-encryption session in slot 1; slot 0 is left
791+ * for the parent auth that wolfTPM2_CreateLoadedKey sets internally. */
792+ rc = wolfTPM2_StartSession (& dev , & session , & srk , NULL ,
793+ TPM_SE_HMAC , TPM_ALG_CFB );
794+ AssertIntEQ (rc , TPM_RC_SUCCESS );
795+ rc = wolfTPM2_SetAuthSession (& dev , 1 , & session ,
796+ (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
797+ TPMA_SESSION_continueSession ));
798+ AssertIntEQ (rc , TPM_RC_SUCCESS );
799+
800+ #if !defined(NO_RSA )
801+ rc = wolfTPM2_GetKeyTemplate_RSA (& publicTemplate ,
802+ TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
803+ TPMA_OBJECT_sign | TPMA_OBJECT_noDA );
804+ #else
805+ rc = wolfTPM2_GetKeyTemplate_ECC (& publicTemplate ,
806+ TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
807+ TPMA_OBJECT_sign | TPMA_OBJECT_noDA ,
808+ TPM_ECC_NIST_P256 , TPM_ALG_ECDSA );
809+ #endif
810+ AssertIntEQ (rc , TPM_RC_SUCCESS );
811+
812+ /* CreateLoaded under the param-enc session. Fails pre-fix with
813+ * TPM_RC_HMAC; a TPM that does not implement CreateLoaded is a skip. */
814+ rc = wolfTPM2_CreateLoadedKey (& dev , & child , & srk .handle ,
815+ & publicTemplate , NULL , 0 );
816+ if (WOLFTPM_IS_COMMAND_UNAVAILABLE (rc )) {
817+ printf ("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n" );
818+ }
819+ else {
820+ AssertIntEQ (rc , TPM_RC_SUCCESS );
821+ wolfTPM2_UnloadHandle (& dev , & child .handle );
822+ printf ("Test TPM Wrapper:\tCreateLoaded param-enc:\tPassed\n" );
823+ }
824+
825+ /* Clear the session slot, then release handles. */
826+ wolfTPM2_SetAuthSession (& dev , 1 , NULL , 0 );
827+ wolfTPM2_UnloadHandle (& dev , & session .handle );
828+ wolfTPM2_UnloadHandle (& dev , & srk .handle );
829+ wolfTPM2_Cleanup (& dev );
830+ #else
831+ printf ("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n" );
832+ #endif
833+ }
834+
835+ /* Exercise the bound-own-entity branch of TPM2_ParamEncBindKey: the
836+ * parameter-encryption key for a session that authorizes its own bind entity
837+ * is sessionKey || authValue. An HMAC session cannot authorize in slot 0, so
838+ * this uses a bound policy session (the examples/nvram/extend.c pattern): write
839+ * a POLICYWRITE NV index (policy PolicyPCR(16), auth "cpusecret") under a bound
840+ * AES-CFB policy session, then read it back. A wrong param-enc key corrupts the
841+ * stored data even though the write command itself succeeds. */
842+ static void test_wolfTPM2_BoundOwnEntity_ParamEnc (void )
843+ {
844+ #if !defined(WOLFTPM2_NO_WOLFCRYPT ) && !defined(WOLFTPM_WINAPI )
845+ int rc ;
846+ WOLFTPM2_DEV dev ;
847+ WOLFTPM2_SESSION session ;
848+ WOLFTPM2_SESSION trial ;
849+ WOLFTPM2_NV nv ;
850+ WOLFTPM2_HANDLE parent ;
851+ const word32 nvIndex = TPM2_DEMO_NV_TEST_AUTH_INDEX ;
852+ const byte nvAuth [] = "cpusecret" ;
853+ const int nvAuthSz = (int )sizeof (nvAuth ) - 1 ;
854+ word32 nvAttributes ;
855+ byte policyDigest [TPM_SHA256_DIGEST_SIZE ];
856+ word32 policyDigestSz = (word32 )sizeof (policyDigest );
857+ byte pcrArray [1 ];
858+ byte buf [8 ];
859+ byte readBuf [8 ];
860+ word32 readSz ;
861+
862+ XMEMSET (& dev , 0 , sizeof (dev ));
863+ XMEMSET (& session , 0 , sizeof (session ));
864+ XMEMSET (& trial , 0 , sizeof (trial ));
865+ XMEMSET (& nv , 0 , sizeof (nv ));
866+ XMEMSET (& parent , 0 , sizeof (parent ));
867+ XMEMSET (policyDigest , 0 , sizeof (policyDigest ));
868+ XMEMSET (buf , 0x11 , sizeof (buf ));
869+ XMEMSET (readBuf , 0 , sizeof (readBuf ));
870+ pcrArray [0 ] = 16 ; /* resettable debug PCR */
871+
872+ rc = wolfTPM2_Init (& dev , TPM2_IoCb , NULL );
873+ if (rc != 0 ) {
874+ printf ("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n" );
875+ return ;
876+ }
877+
878+ /* Compute the index authPolicy = PolicyPCR(16) with a trial session
879+ * (the write helper re-runs PolicyPCR with the same selection). */
880+ rc = wolfTPM2_StartSession (& dev , & trial , NULL , NULL , TPM_SE_TRIAL ,
881+ TPM_ALG_NULL );
882+ AssertIntEQ (rc , TPM_RC_SUCCESS );
883+ rc = wolfTPM2_PolicyPCR (& dev , trial .handle .hndl , TPM_ALG_SHA256 ,
884+ pcrArray , 1 );
885+ AssertIntEQ (rc , TPM_RC_SUCCESS );
886+ rc = wolfTPM2_GetPolicyDigest (& dev , trial .handle .hndl , policyDigest ,
887+ & policyDigestSz );
888+ AssertIntEQ (rc , TPM_RC_SUCCESS );
889+ wolfTPM2_UnloadHandle (& dev , & trial .handle );
890+
891+ parent .hndl = TPM_RH_OWNER ;
892+ nvAttributes = TPMA_NV_POLICYWRITE | TPMA_NV_AUTHREAD | TPMA_NV_NO_DA ;
893+ rc = wolfTPM2_NVCreateAuthPolicy (& dev , & parent , & nv , nvIndex , nvAttributes ,
894+ (word32 )sizeof (buf ), (byte * )nvAuth , nvAuthSz ,
895+ policyDigest , (int )policyDigestSz );
896+ if (rc != 0 && rc != TPM_RC_NV_DEFINED ) {
897+ /* Environmental (NV space / unsupported). Treat as skip. */
898+ wolfTPM2_Cleanup (& dev );
899+ printf ("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n" );
900+ return ;
901+ }
902+ /* Load the NV handle's auth and Name for the bind. */
903+ rc = wolfTPM2_NVOpen (& dev , & nv , nvIndex , (byte * )nvAuth , nvAuthSz );
904+ AssertIntEQ (rc , TPM_RC_SUCCESS );
905+
906+ /* Bound AES-CFB policy session (slot 0) authorizing its own bind entity. */
907+ rc = wolfTPM2_StartSession (& dev , & session , NULL , & nv .handle ,
908+ TPM_SE_POLICY , TPM_ALG_CFB );
909+ AssertIntEQ (rc , TPM_RC_SUCCESS );
910+ rc = wolfTPM2_SetAuthSession (& dev , 0 , & session ,
911+ (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
912+ TPMA_SESSION_continueSession ));
913+ AssertIntEQ (rc , TPM_RC_SUCCESS );
914+
915+ /* Write under the bound policy session - the data is parameter-encrypted
916+ * with the folded key. */
917+ rc = wolfTPM2_NVWriteAuthPolicy (& dev , & session , TPM_ALG_SHA256 , pcrArray , 1 ,
918+ & nv , nvIndex , buf , (word32 )sizeof (buf ), 0 );
919+ AssertIntEQ (rc , TPM_RC_SUCCESS );
920+
921+ /* Read back with plain password auth (no param enc) and verify the data
922+ * round-tripped. A doubled or dropped bind authValue would have stored
923+ * garbage even though the write command itself succeeded. */
924+ wolfTPM2_SetAuthSession (& dev , 0 , NULL , 0 );
925+ wolfTPM2_UnloadHandle (& dev , & session .handle );
926+ wolfTPM2_SetAuthHandle (& dev , 0 , & nv .handle );
927+ readSz = (word32 )sizeof (readBuf );
928+ rc = wolfTPM2_NVReadAuth (& dev , & nv , nvIndex , readBuf , & readSz , 0 );
929+ AssertIntEQ (rc , TPM_RC_SUCCESS );
930+ AssertIntEQ ((int )readSz , (int )sizeof (buf ));
931+ AssertIntEQ (XMEMCMP (readBuf , buf , sizeof (buf )), 0 );
932+
933+ wolfTPM2_NVDeleteAuth (& dev , & parent , nvIndex );
934+ wolfTPM2_Cleanup (& dev );
935+ printf ("Test TPM Wrapper:\tBound own-entity param-enc:\tPassed\n" );
936+ #else
937+ printf ("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n" );
938+ #endif
939+ }
940+
670941static void test_wolfTPM2_PolicyHash (void )
671942{
672943#ifndef WOLFTPM2_NO_WOLFCRYPT
@@ -851,7 +1122,7 @@ static void test_wolfTPM2_EncryptSecret(void)
8511122 WOLFTPM2_KEY tpmKey ;
8521123 TPM2B_DATA data ;
8531124 TPM2B_ENCRYPTED_SECRET secret ;
854- #if defined(WOLFTPM_PQC ) && !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
1125+ #if defined(WOLFTPM_MLKEM ) && !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
8551126 (defined(WOLFSSL_HAVE_MLKEM ) || defined(WOLFSSL_KYBER512 ) || \
8561127 defined(WOLFSSL_KYBER768 ) || defined(WOLFSSL_KYBER1024 ))
8571128 WOLFTPM2_KEY mlkemKey ;
@@ -881,7 +1152,7 @@ static void test_wolfTPM2_EncryptSecret(void)
8811152 rc = wolfTPM2_EncryptSecret (& dev , & tpmKey , & data , NULL , "SECRET" );
8821153 AssertIntEQ (rc , BAD_FUNC_ARG );
8831154
884- #if defined(WOLFTPM_PQC ) && !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
1155+ #if defined(WOLFTPM_MLKEM ) && !defined(WOLFTPM2_NO_WOLFCRYPT ) && \
8851156 (defined(WOLFSSL_HAVE_MLKEM ) || defined(WOLFSSL_KYBER512 ) || \
8861157 defined(WOLFSSL_KYBER768 ) || defined(WOLFSSL_KYBER1024 ))
8871158 /* MLKEM path (v1.85 Part 1 Sec.24): caller encapsulates under the TPM's
@@ -4691,7 +4962,11 @@ static void test_TPM2_GetHashDigestSize_AllAlgs(void)
46914962 printf ("Test TPM2:\t\tGetHashDigestSize all algs:\tPassed\n" );
46924963}
46934964
4694- #ifdef WOLFTPM_PQC
4965+ /* These PQC unit tests call both ML-DSA and ML-KEM wrappers, so they compile
4966+ * only when both families are present (a WOLFTPM_NO_MLDSA or WOLFTPM_NO_MLKEM
4967+ * build excludes the matching wrapper definitions). CI always builds full
4968+ * PQC, so coverage is unchanged there. */
4969+ #if defined(WOLFTPM_MLDSA ) && defined(WOLFTPM_MLKEM )
46954970/* Post-Quantum Cryptography (PQC) Unit Tests - TPM 2.0 v185 */
46964971
46974972/* TODO: Remove TPM_RC_COMMAND_CODE skip logic once we have a TPM simulator
@@ -5451,7 +5726,7 @@ static void test_wolfTPM2_PQC_Sizes(void)
54515726
54525727 printf ("Test TPM Wrapper: %-40s Passed\n" , "PQC Sizes:" );
54535728}
5454- #endif /* WOLFTPM_PQC */
5729+ #endif /* WOLFTPM_MLDSA && WOLFTPM_MLKEM */
54555730
54565731#endif /* !WOLFTPM2_NO_WRAPPER */
54575732
@@ -5475,6 +5750,9 @@ int unit_tests(int argc, char *argv[])
54755750 test_wolfTPM2_PolicyAuthValue_AuthOffset ();
54765751 test_wolfTPM2_SetAuthHandle_PolicyAuthOffset ();
54775752 test_wolfTPM2_StartSession_SaltedEncryptAttrs ();
5753+ test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc ();
5754+ test_wolfTPM2_CreateLoaded_ParamEnc ();
5755+ test_wolfTPM2_BoundOwnEntity_ParamEnc ();
54785756 test_wolfTPM2_PolicyHash ();
54795757 test_wolfTPM2_SensitiveToPrivate ();
54805758 test_TPM2_KDFa ();
@@ -5569,7 +5847,7 @@ int unit_tests(int argc, char *argv[])
55695847 test_wolfTPM2_ST33_FirmwareUpgrade ();
55705848 #endif
55715849 #endif
5572- #ifdef WOLFTPM_PQC
5850+ #if defined( WOLFTPM_MLDSA ) && defined( WOLFTPM_MLKEM )
55735851 /* Run non-TPM-dependent tests first */
55745852 test_wolfTPM2_PQC_KeyTemplates ();
55755853 test_wolfTPM2_PQC_Sizes ();
0 commit comments