diff --git a/examples/attestation/activate_credential.c b/examples/attestation/activate_credential.c index f5dcd4d8..3c716c81 100644 --- a/examples/attestation/activate_credential.c +++ b/examples/attestation/activate_credential.c @@ -99,6 +99,7 @@ int TPM2_ActivateCredential_Example(void* userCtx, int argc, char *argv[]) XMEMSET(&endorse, 0, sizeof(endorse)); XMEMSET(&storage, 0, sizeof(storage)); XMEMSET(&akKey, 0, sizeof(akKey)); + XMEMSET(&tpmSession, 0, sizeof(tpmSession)); printf("Demo how to create a credential blob for remote attestation\n"); rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); @@ -230,7 +231,8 @@ int TPM2_ActivateCredential_Example(void* userCtx, int argc, char *argv[]) exit: - wolfTPM2_UnloadHandle(&dev, &primary->handle); + if (primary != NULL) + wolfTPM2_UnloadHandle(&dev, &primary->handle); wolfTPM2_UnloadHandle(&dev, &akKey.handle); wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); wolfTPM2_Cleanup(&dev); diff --git a/examples/keygen/keygen.c b/examples/keygen/keygen.c index f54a845e..7ab0fcbd 100644 --- a/examples/keygen/keygen.c +++ b/examples/keygen/keygen.c @@ -481,7 +481,8 @@ int TPM2_Keygen_Example(void* userCtx, int argc, char *argv[]) } /* Close handles */ - wolfTPM2_UnloadHandle(&dev, &primary->handle); + if (primary != NULL) + wolfTPM2_UnloadHandle(&dev, &primary->handle); wolfTPM2_UnloadHandle(&dev, &newKeyBlob.handle); wolfTPM2_UnloadHandle(&dev, &tpmSession.handle); diff --git a/examples/keygen/keyload.c b/examples/keygen/keyload.c index 5c04bbeb..5a280eef 100644 --- a/examples/keygen/keyload.c +++ b/examples/keygen/keyload.c @@ -224,7 +224,8 @@ int TPM2_Keyload_Example(void* userCtx, int argc, char *argv[]) } /* Close key handles */ - wolfTPM2_UnloadHandle(&dev, &primary->handle); + if (primary != NULL) + wolfTPM2_UnloadHandle(&dev, &primary->handle); /* newKey.handle is already flushed by wolfTPM2_NVStoreKey */ if (!persistent) { wolfTPM2_UnloadHandle(&dev, &newKey.handle); diff --git a/examples/management/flush.c b/examples/management/flush.c index f53d42b9..1a55d087 100644 --- a/examples/management/flush.c +++ b/examples/management/flush.c @@ -92,7 +92,7 @@ int TPM2_Flush_Tool(void* userCtx, int argc, char *argv[]) TPM2_FlushContext(&flushCtx); } /* Flush hmac sessions */ - for (handle=0x3000000; handle < 0x3000004; handle++) { + for (handle=0x2000000; handle < 0x2000004; handle++) { flushCtx.flushHandle = handle; printf("Freeing %X object\n", handle); TPM2_FlushContext(&flushCtx); diff --git a/examples/pkcs7/pkcs7.c b/examples/pkcs7/pkcs7.c index d9052ef5..3bdc248b 100644 --- a/examples/pkcs7/pkcs7.c +++ b/examples/pkcs7/pkcs7.c @@ -196,7 +196,8 @@ static int PKCS7_SignVerifyEx(WOLFTPM2_DEV* dev, int tpmDevId, } /* Body - Data */ - do { + offset = 0; + while (1) { dataChunkSz = GetMyData(dataChunk, sizeof(dataChunk), offset); if (dataChunkSz == 0) break; @@ -208,7 +209,7 @@ static int PKCS7_SignVerifyEx(WOLFTPM2_DEV* dev, int tpmDevId, } offset += dataChunkSz; - } while (rc == 0); + } dataChunkSz = GetMyData(NULL, 0, 0); /* get total size */ /* Footer */ diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index f560da7f..b5f852e2 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -5724,6 +5724,17 @@ static TPM_RC FwCmd_Sign(FWTPM_CTX* ctx, TPM2_Packet* cmd, if (rc == 0) TPM2_Packet_ParseU16(cmd, &sigHashAlg); } + /* TPMS_SCHEME_ECDAA carries an additional UINT16 count after + * hashAlg per Part 2 Sec. 11.2.1.5. */ + if (rc == 0 && sigScheme == TPM_ALG_ECDAA) { + UINT16 ecdaaCount; + if (cmd->pos + 2 > cmdSize) + rc = TPM_RC_COMMAND_SIZE; + if (rc == 0) { + TPM2_Packet_ParseU16(cmd, &ecdaaCount); + (void)ecdaaCount; + } + } } if (rc == 0) { @@ -11114,6 +11125,17 @@ static TPM_RC FwParseAttestParams(TPM2_Packet* cmd, int cmdSize, *sigHashAlg = TPM_ALG_NULL; if (*sigScheme != TPM_ALG_NULL) TPM2_Packet_ParseU16(cmd, sigHashAlg); + /* TPMS_SCHEME_ECDAA carries an additional UINT16 count after + * hashAlg per Part 2 Sec. 11.2.1.5. */ + if (*sigScheme == TPM_ALG_ECDAA) { + UINT16 ecdaaCount; + if (cmd->pos + 2 > cmdSize) + rc = TPM_RC_COMMAND_SIZE; + if (rc == 0) { + TPM2_Packet_ParseU16(cmd, &ecdaaCount); + (void)ecdaaCount; + } + } } return rc; @@ -11419,6 +11441,17 @@ static TPM_RC FwCmd_CertifyCreation(FWTPM_CTX* ctx, TPM2_Packet* cmd, sigHashAlg = TPM_ALG_NULL; if (sigScheme != TPM_ALG_NULL) TPM2_Packet_ParseU16(cmd, &sigHashAlg); + /* TPMS_SCHEME_ECDAA carries an additional UINT16 count after + * hashAlg per Part 2 Sec. 11.2.1.5. */ + if (sigScheme == TPM_ALG_ECDAA) { + UINT16 ecdaaCount; + if (cmd->pos + 2 > cmdSize) + rc = TPM_RC_COMMAND_SIZE; + if (rc == 0) { + TPM2_Packet_ParseU16(cmd, &ecdaaCount); + (void)ecdaaCount; + } + } } /* creationTicket verification per TPM 2.0 Part 3 Section 18.3 */ @@ -11581,6 +11614,7 @@ static TPM_RC FwCmd_NV_Certify(FWTPM_CTX* ctx, TPM2_Packet* cmd, TPM2B_DATA qualifyingData; UINT16 sigScheme, sigHashAlg; UINT16 readSize, readOffset; + int digestMode = 0; FWTPM_Object* sigObj; FWTPM_NvIndex* nv; FWTPM_DECLARE_BUF(attestBuf, FWTPM_MAX_ATTEST_BUF); @@ -11639,12 +11673,20 @@ static TPM_RC FwCmd_NV_Certify(FWTPM_CTX* ctx, TPM2_Packet* cmd, } } if (rc == 0) { - if (readSize == 0) + /* Per Part 3 Sec. 31.16.1: when both size and offset are zero, the + * response must contain a TPMS_NV_DIGEST_CERTIFY_INFO with the + * digest of the entire NV index, instead of TPMS_NV_CERTIFY_INFO. */ + if (readSize == 0 && readOffset == 0) { + digestMode = 1; + readSize = nv->nvPublic.dataSize; + } + else if (readSize == 0) { readSize = nv->nvPublic.dataSize - readOffset; + } if ((UINT32)(readOffset + readSize) > nv->nvPublic.dataSize) { rc = TPM_RC_NV_RANGE; } - if (rc == 0 && readSize > FWTPM_MAX_NV_DATA) { + if (rc == 0 && !digestMode && readSize > FWTPM_MAX_NV_DATA) { rc = TPM_RC_SIZE; } } @@ -11680,26 +11722,67 @@ static TPM_RC FwCmd_NV_Certify(FWTPM_CTX* ctx, TPM2_Packet* cmd, /* Build TPMS_ATTEST for NV */ if (rc == 0) { + UINT16 attestType = digestMode ? + TPM_ST_ATTEST_NV_DIGEST : TPM_ST_ATTEST_NV; XMEMSET(attestBuf, 0, FWTPM_MAX_ATTEST_BUF); attestPkt.buf = attestBuf; attestPkt.pos = 0; attestPkt.size = (int)FWTPM_MAX_ATTEST_BUF; - FwAppendAttestCommonHeader(&attestPkt, TPM_ST_ATTEST_NV, + FwAppendAttestCommonHeader(&attestPkt, attestType, &sigObj->name, &qualifyingData); - /* attested.nv: indexName + offset + nvContents */ - TPM2_Packet_AppendU16(&attestPkt, nvName.size); - TPM2_Packet_AppendBytes(&attestPkt, (byte*)nvName.name, - nvName.size); - TPM2_Packet_AppendU16(&attestPkt, readOffset); - TPM2_Packet_AppendU16(&attestPkt, readSize); - TPM2_Packet_AppendBytes(&attestPkt, nv->data + readOffset, - readSize); + if (digestMode) { + /* attested.nvDigest: indexName + nvDigest + * nvDigest is hash of entire NV index using signing scheme hash */ + UINT16 hashAlg = sigHashAlg; + byte nvDigest[TPM_MAX_DIGEST_SIZE]; + int hashSz; + enum wc_HashType wcDigH; + + /* Resolve hash from signing key when scheme/hash is NULL. + * keyScheme is filled in by the by-pointer interface but + * unused here - we only need the resolved hashAlg. */ + if (hashAlg == TPM_ALG_NULL) { + UINT16 keyScheme = TPM_ALG_NULL; + FwResolveSignScheme(sigObj, &keyScheme, &hashAlg); + (void)keyScheme; + } + wcDigH = FwGetWcHashType(hashAlg); + hashSz = TPM2_GetHashDigestSize(hashAlg); + if (wcDigH == WC_HASH_TYPE_NONE || hashSz <= 0) { + rc = TPM_RC_HASH; + } + if (rc == 0) { + if (wc_Hash(wcDigH, nv->data, nv->nvPublic.dataSize, + nvDigest, hashSz) != 0) { + rc = TPM_RC_FAILURE; + } + } + if (rc == 0) { + TPM2_Packet_AppendU16(&attestPkt, nvName.size); + TPM2_Packet_AppendBytes(&attestPkt, (byte*)nvName.name, + nvName.size); + TPM2_Packet_AppendU16(&attestPkt, (UINT16)hashSz); + TPM2_Packet_AppendBytes(&attestPkt, nvDigest, hashSz); + } + } + else { + /* attested.nv: indexName + offset + nvContents */ + TPM2_Packet_AppendU16(&attestPkt, nvName.size); + TPM2_Packet_AppendBytes(&attestPkt, (byte*)nvName.name, + nvName.size); + TPM2_Packet_AppendU16(&attestPkt, readOffset); + TPM2_Packet_AppendU16(&attestPkt, readSize); + TPM2_Packet_AppendBytes(&attestPkt, nv->data + readOffset, + readSize); + } /* Build response */ - rc = FwBuildAttestResponse(ctx, rsp, cmdTag, sigObj, - sigScheme, sigHashAlg, attestBuf, attestPkt.pos); + if (rc == 0) { + rc = FwBuildAttestResponse(ctx, rsp, cmdTag, sigObj, + sigScheme, sigHashAlg, attestBuf, attestPkt.pos); + } } FWTPM_FREE_BUF(attestBuf); return rc; diff --git a/src/tpm2.c b/src/tpm2.c index 1d7e5671..dd427390 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -6488,9 +6488,13 @@ int TPM2_GetCurveSize(TPM_ECC_CURVE curveID) case TPM_ECC_NIST_P256: case TPM_ECC_BN_P256: case TPM_ECC_SM2_P256: + case TPM_ECC_BP_P256_R1: return 32; case TPM_ECC_NIST_P384: + case TPM_ECC_BP_P384_R1: return 48; + case TPM_ECC_BP_P512_R1: + return 64; case TPM_ECC_NIST_P521: return 66; case TPM_ECC_BN_P638: @@ -6520,7 +6524,13 @@ int TPM2_GetTpmCurve(int curve_id) ret = TPM_ECC_NIST_P521; break; case ECC_BRAINPOOLP256R1: - ret = TPM_ECC_BN_P256; + ret = TPM_ECC_BP_P256_R1; + break; + case ECC_BRAINPOOLP384R1: + ret = TPM_ECC_BP_P384_R1; + break; + case ECC_BRAINPOOLP512R1: + ret = TPM_ECC_BP_P512_R1; break; case TPM_ECC_BN_P638: default: @@ -6551,9 +6561,16 @@ int TPM2_GetWolfCurve(int curve_id) case TPM_ECC_NIST_P521: ret = ECC_SECP521R1; break; - case TPM_ECC_BN_P256: + case TPM_ECC_BP_P256_R1: ret = ECC_BRAINPOOLP256R1; break; + case TPM_ECC_BP_P384_R1: + ret = ECC_BRAINPOOLP384R1; + break; + case TPM_ECC_BP_P512_R1: + ret = ECC_BRAINPOOLP512R1; + break; + case TPM_ECC_BN_P256: case TPM_ECC_BN_P638: default: ret = ECC_CURVE_OID_E; diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 97322711..7caa78d2 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -782,8 +782,33 @@ void TPM2_Packet_AppendPoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point) } void TPM2_Packet_ParsePoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point) { + int pointStartPos; + TPM2_Packet_ParseU16(packet, &point->size); - TPM2_Packet_ParseEccPoint(packet, &point->point); + pointStartPos = (packet != NULL) ? packet->pos : 0; + /* Skip the inner ECC point parse when the outer size is zero. A + * malformed blob with size=0 but nonzero inner x.size/y.size would + * otherwise advance packet->pos and desync subsequent fields. */ + if (point->size > 0) { + TPM2_Packet_ParseEccPoint(packet, &point->point); + } + else { + XMEMSET(&point->point, 0, sizeof(point->point)); + } + + /* Resync packet position to end of declared outer size so inner + * x.size / y.size disagreement can't desynchronize subsequent fields. + * If the declared outer size runs past the buffer, clamp to packet end + * so subsequent reads return an out-of-bounds sentinel rather than + * leaving the position wherever the inner parses landed. */ + if (packet != NULL) { + if (pointStartPos + point->size <= packet->size) { + packet->pos = pointStartPos + point->size; + } + else { + packet->pos = packet->size; + } + } } void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) @@ -1095,7 +1120,10 @@ void TPM2_Packet_AppendPublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub) } void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub) { + int pubStartPos; + TPM2_Packet_ParseU16(packet, &pub->size); + pubStartPos = (packet != NULL) ? packet->pos : 0; if (pub->size > 0) { TPM2_Packet_ParseU16(packet, &pub->publicArea.type); TPM2_Packet_ParseU16(packet, &pub->publicArea.nameAlg); @@ -1133,6 +1161,20 @@ void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub) /* TPMS_DERIVE derive; ? */ break; } + + /* Resync packet position to end of declared outer size so inner + * parses can't cause field drift if declared size and actual + * inner consumption disagree. If the declared outer size runs + * past the buffer, clamp to packet end so subsequent reads + * return an out-of-bounds sentinel. */ + if (packet != NULL) { + if (pubStartPos + pub->size <= packet->size) { + packet->pos = pubStartPos + pub->size; + } + else { + packet->pos = packet->size; + } + } } } @@ -1170,7 +1212,13 @@ void TPM2_Packet_AppendSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig) digestSz = TPM2_GetHashDigestSize(sig->signature.hmac.hashAlg); TPM2_Packet_AppendBytes(packet, sig->signature.hmac.digest.H, digestSz); break; + case TPM_ALG_NULL: + /* Legitimate zero-payload signature - nothing to append. */ + break; default: + #ifdef DEBUG_WOLFTPM + printf("AppendSignature: unrecognized sigAlg 0x%x\n", sig->sigAlg); + #endif break; } } @@ -1242,7 +1290,13 @@ void TPM2_Packet_ParseSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig) digestSz = TPM2_GetHashDigestSize(sig->signature.hmac.hashAlg); TPM2_Packet_ParseBytes(packet, sig->signature.hmac.digest.H, digestSz); break; + case TPM_ALG_NULL: + /* Legitimate zero-payload signature - nothing to consume. */ + break; default: + #ifdef DEBUG_WOLFTPM + printf("ParseSignature: unrecognized sigAlg 0x%x\n", sig->sigAlg); + #endif break; } } @@ -1342,6 +1396,16 @@ void TPM2_Packet_ParseAttest(TPM2_Packet* packet, TPMS_ATTEST* out) out->attested.nv.nvContents.buffer, (UINT16)sizeof(out->attested.nv.nvContents.buffer)); break; + case TPM_ST_ATTEST_NV_DIGEST: + TPM2_Packet_ParseU16Buf(packet, + &out->attested.nvDigest.indexName.size, + out->attested.nvDigest.indexName.name, + (UINT16)sizeof(out->attested.nvDigest.indexName.name)); + TPM2_Packet_ParseU16Buf(packet, + &out->attested.nvDigest.nvDigest.size, + out->attested.nvDigest.nvDigest.buffer, + (UINT16)sizeof(out->attested.nvDigest.nvDigest.buffer)); + break; default: /* unknown attestation type */ #ifdef DEBUG_WOLFTPM diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 63ff936c..f53796d3 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2359,10 +2359,10 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, /* set session auth for key */ if (tpmKey) { TPMA_SESSION sessionAttributes = 0; - if (bind != NULL && - (encDecAlg == TPM_ALG_CFB || encDecAlg == TPM_ALG_XOR)) { - /* if parameter encryption is enabled and key bind set, enable - * encrypt/decrypt by default */ + if (encDecAlg == TPM_ALG_CFB || encDecAlg == TPM_ALG_XOR) { + /* if parameter encryption is enabled, enable encrypt/decrypt by + * default. Salted (tpmKey-only) sessions also have valid + * shared-secret state for parameter encryption. */ sessionAttributes |= (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt); } wolfTPM2_SetAuth(dev, 0, tpmKey->handle.hndl, &tpmKey->handle.auth, @@ -3536,8 +3536,11 @@ int wolfTPM2_LoadRsaPrivateKey(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* parentKey, exponent, rsaPriv, rsaPrivSz, TPM_ALG_NULL, TPM_ALG_NULL); } -int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, int curveId, - const byte* eccPubX, word32 eccPubXSz, const byte* eccPubY, word32 eccPubYSz) +int wolfTPM2_LoadEccPublicKey_ex(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, + int curveId, const byte* eccPubX, word32 eccPubXSz, + const byte* eccPubY, word32 eccPubYSz, + TPMI_ALG_ECC_SCHEME scheme, TPMI_ALG_HASH hashAlg, + TPMA_OBJECT objectAttributes) { TPM2B_PUBLIC pub; @@ -3547,16 +3550,23 @@ int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, int curveId, return BUFFER_E; if (eccPubYSz > sizeof(pub.publicArea.unique.ecc.y.buffer)) return BUFFER_E; + /* TPMS_SCHEME_ECDAA requires a 'count' field this helper cannot + * supply. Callers needing ECDAA must build TPM2B_PUBLIC manually and + * use wolfTPM2_LoadPublicKey directly. */ + if (scheme == TPM_ALG_ECDAA) + return BAD_FUNC_ARG; XMEMSET(&pub, 0, sizeof(pub)); pub.publicArea.type = TPM_ALG_ECC; /* make sure nameAlg is set for ticket */ pub.publicArea.nameAlg = WOLFTPM2_WRAP_DIGEST; - pub.publicArea.objectAttributes = TPMA_OBJECT_sign | TPMA_OBJECT_noDA; + pub.publicArea.objectAttributes = objectAttributes; pub.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL; - pub.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_ECDSA; - pub.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg = - WOLFTPM2_WRAP_DIGEST; + pub.publicArea.parameters.eccDetail.scheme.scheme = scheme; + if (scheme != TPM_ALG_NULL) { + pub.publicArea.parameters.eccDetail.scheme.details.any.hashAlg = + hashAlg; + } pub.publicArea.parameters.eccDetail.curveID = curveId; pub.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; pub.publicArea.unique.ecc.x.size = eccPubXSz; @@ -3567,6 +3577,15 @@ int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, int curveId, return wolfTPM2_LoadPublicKey(dev, key, &pub); } +int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, int curveId, + const byte* eccPubX, word32 eccPubXSz, const byte* eccPubY, word32 eccPubYSz) +{ + return wolfTPM2_LoadEccPublicKey_ex(dev, key, curveId, + eccPubX, eccPubXSz, eccPubY, eccPubYSz, + TPM_ALG_ECDSA, WOLFTPM2_WRAP_DIGEST, + TPMA_OBJECT_sign | TPMA_OBJECT_noDA); +} + int wolfTPM2_ImportEccPrivateKeySeed(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* parentKey, WOLFTPM2_KEYBLOB* keyBlob, int curveId, const byte* eccPubX, word32 eccPubXSz, @@ -4941,10 +4960,6 @@ int wolfTPM2_SignHashScheme(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, /* set session auth for key */ wolfTPM2_SetAuthHandle(dev, 0, &key->handle); - /* verify input cannot exceed buffer */ - if (digestSz > (int)sizeof(signIn.digest.buffer)) - digestSz = (int)sizeof(signIn.digest.buffer); - XMEMSET(&signIn, 0, sizeof(signIn)); signIn.keyHandle = key->handle.hndl; signIn.digest.size = (UINT16)TPM2_GetHashDigestSize(hashAlg); @@ -4952,14 +4967,31 @@ int wolfTPM2_SignHashScheme(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, signIn.digest.size > sizeof(signIn.digest.buffer)) { return BUFFER_E; } - /* if digest provided is smaller than key size then zero pad leading */ - if (digestSz < signIn.digest.size) { - XMEMCPY(&signIn.digest.buffer[signIn.digest.size - digestSz], digest, - digestSz); + /* Hard upper bound: digest must fit the message-buffer. */ + if (digestSz < 0 || digestSz > (int)sizeof(signIn.digest.buffer)) { + return BUFFER_E; } - else { + if (key->pub.publicArea.type != TPM_ALG_ECC) { + /* RSA: digest size must match the declared hash algorithm. + * Silently zero-padding produces a signature over crafted + * but incorrect content, so this is a caller error. */ + if (digestSz != (int)signIn.digest.size) { + return BUFFER_E; + } XMEMCPY(signIn.digest.buffer, digest, digestSz); } + else { + /* ECDSA: digests shorter than hashAlg's size are left-padded with + * zeros; longer digests are silently truncated to hashAlg's size + * (TCG Part 1 - ECDSA admits short or long inputs). */ + if (digestSz < (int)signIn.digest.size) { + XMEMCPY(&signIn.digest.buffer[signIn.digest.size - digestSz], + digest, digestSz); + } + else { + XMEMCPY(signIn.digest.buffer, digest, signIn.digest.size); + } + } signIn.inScheme.scheme = sigAlg; signIn.inScheme.details.any.hashAlg = hashAlg; signIn.validation.tag = TPM_ST_HASHCHECK; @@ -5105,10 +5137,6 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return BAD_FUNC_ARG; } - /* verify input cannot exceed buffer */ - if (digestSz > (int)sizeof(verifySigIn.digest.buffer)) - digestSz = (int)sizeof(verifySigIn.digest.buffer); - /* set session auth for key */ wolfTPM2_SetAuthHandle(dev, 0, &key->handle); @@ -5119,13 +5147,27 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, verifySigIn.digest.size > sizeof(verifySigIn.digest.buffer)) { return BUFFER_E; } - /* if digest provided is smaller than key size then zero pad leading */ - if (digestSz < verifySigIn.digest.size) { - XMEMCPY(&verifySigIn.digest.buffer[verifySigIn.digest.size - digestSz], - digest, digestSz); + /* Hard upper bound: digest must fit the message-buffer. */ + if (digestSz < 0 || digestSz > (int)sizeof(verifySigIn.digest.buffer)) { + return BUFFER_E; + } + if (key->pub.publicArea.type != TPM_ALG_ECC) { + /* RSA: digest size must match the declared hash algorithm. */ + if (digestSz != (int)verifySigIn.digest.size) { + return BUFFER_E; + } + XMEMCPY(verifySigIn.digest.buffer, digest, digestSz); } else { - XMEMCPY(verifySigIn.digest.buffer, digest, digestSz); + /* ECDSA: short digests are left-padded with zeros, longer digests + * are silently truncated to hashAlg's size. */ + if (digestSz < (int)verifySigIn.digest.size) { + XMEMCPY(&verifySigIn.digest.buffer[verifySigIn.digest.size - + digestSz], digest, digestSz); + } + else { + XMEMCPY(verifySigIn.digest.buffer, digest, verifySigIn.digest.size); + } } verifySigIn.signature.sigAlg = sigAlg; signature->any.hashAlg = hashAlg; @@ -5451,10 +5493,10 @@ int wolfTPM2_RsaEncrypt(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, /* RSA Encrypt */ XMEMSET(&rsaEncIn, 0, sizeof(rsaEncIn)); rsaEncIn.keyHandle = key->handle.hndl; - rsaEncIn.message.size = msgSz; - if (rsaEncIn.message.size > sizeof(rsaEncIn.message.buffer)) { - rsaEncIn.message.size = sizeof(rsaEncIn.message.buffer); /* truncate */ + if (msgSz < 0 || (size_t)msgSz > sizeof(rsaEncIn.message.buffer)) { + return BUFFER_E; } + rsaEncIn.message.size = (UINT16)msgSz; XMEMCPY(rsaEncIn.message.buffer, msg, rsaEncIn.message.size); /* TPM_ALG_NULL, TPM_ALG_OAEP, TPM_ALG_RSASSA or TPM_ALG_RSAPSS */ rsaEncIn.inScheme.scheme = padScheme; @@ -5472,19 +5514,23 @@ int wolfTPM2_RsaEncrypt(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, printf("TPM2_RSA_Encrypt failed %d: %s\n", rc, wolfTPM2_GetRCString(rc)); #endif - return rc; } - - if (*outSz < rsaEncOut.outData.size) { - return BUFFER_E; + else if (*outSz < rsaEncOut.outData.size) { + rc = BUFFER_E; + } + else { + *outSz = rsaEncOut.outData.size; + XMEMCPY(out, rsaEncOut.outData.buffer, *outSz); + #ifdef DEBUG_WOLFTPM + printf("TPM2_RSA_Encrypt: %d\n", rsaEncOut.outData.size); + #endif } - *outSz = rsaEncOut.outData.size; - XMEMCPY(out, rsaEncOut.outData.buffer, *outSz); - -#ifdef DEBUG_WOLFTPM - printf("TPM2_RSA_Encrypt: %d\n", rsaEncOut.outData.size); -#endif + /* Plaintext copy lingers in rsaEncIn after the XMEMCPY above. Scrub + * before returning so the stack frame doesn't leak it. The early + * BUFFER_E returns above happen before the XMEMCPY so they don't + * need this path. */ + TPM2_ForceZero(&rsaEncIn, sizeof(rsaEncIn)); return rc; } @@ -5506,8 +5552,8 @@ int wolfTPM2_RsaDecrypt(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, /* RSA Decrypt */ XMEMSET(&rsaDecIn, 0, sizeof(rsaDecIn)); rsaDecIn.keyHandle = key->handle.hndl; - if (inSz > (int)sizeof(rsaDecIn.cipherText.buffer)) { - inSz = (int)sizeof(rsaDecIn.cipherText.buffer); /* truncate */ + if (inSz < 0 || (size_t)inSz > sizeof(rsaDecIn.cipherText.buffer)) { + return BUFFER_E; } rsaDecIn.cipherText.size = (UINT16)inSz; XMEMCPY(rsaDecIn.cipherText.buffer, in, inSz); @@ -5686,9 +5732,14 @@ int wolfTPM2_UnloadHandle(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle) /* nv is the populated handle and auth */ /* auth and authSz are optional NV authentication */ /* authPolicy and authPolicySz are optional policy digest */ -int wolfTPM2_NVCreateAuthPolicy(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, +/* nameAlg is the index name hash algorithm; must match the hash that + * produced authPolicy when one is supplied, otherwise the policy can + * never be satisfied. Pass TPM_ALG_NULL to use WOLFTPM2_WRAP_DIGEST + * (default) or auto-infer from authPolicySz for SHA-only policies. */ +int wolfTPM2_NVCreateAuthPolicy_ex(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, WOLFTPM2_NV* nv, word32 nvIndex, word32 nvAttributes, word32 maxSize, - const byte* auth, int authSz, const byte* authPolicy, int authPolicySz) + const byte* auth, int authSz, const byte* authPolicy, int authPolicySz, + TPMI_ALG_HASH nameAlg) { int rc, rctmp; NV_DefineSpace_In in; @@ -5719,7 +5770,43 @@ int wolfTPM2_NVCreateAuthPolicy(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, XMEMCPY(in.auth.buffer, auth, in.auth.size); } in.publicInfo.nvPublic.nvIndex = nvIndex; - in.publicInfo.nvPublic.nameAlg = WOLFTPM2_WRAP_DIGEST; + if (nameAlg != TPM_ALG_NULL) { + /* Caller supplied an explicit nameAlg; use it as-is and only + * sanity-check against the policy digest size. */ + if (authPolicy != NULL && authPolicySz > 0) { + int expectSz = TPM2_GetHashDigestSize(nameAlg); + if (expectSz <= 0 || expectSz != authPolicySz) + return BAD_FUNC_ARG; + } + in.publicInfo.nvPublic.nameAlg = nameAlg; + } + else if (authPolicy != NULL && authPolicySz > 0) { + /* No explicit nameAlg supplied. Infer from policy digest size for + * SHA-only policies. The TPM stores the policy digest verbatim, + * so we don't need wolfCrypt to support the hash here - the + * caller has already computed the digest. SM3-256 / SHA3-256 + * also produce 32 bytes; callers needing those must pass + * nameAlg explicitly via the _ex API. */ + switch (authPolicySz) { + case TPM_SHA_DIGEST_SIZE: + in.publicInfo.nvPublic.nameAlg = TPM_ALG_SHA1; + break; + case TPM_SHA256_DIGEST_SIZE: + in.publicInfo.nvPublic.nameAlg = TPM_ALG_SHA256; + break; + case TPM_SHA384_DIGEST_SIZE: + in.publicInfo.nvPublic.nameAlg = TPM_ALG_SHA384; + break; + case TPM_SHA512_DIGEST_SIZE: + in.publicInfo.nvPublic.nameAlg = TPM_ALG_SHA512; + break; + default: + return BAD_FUNC_ARG; + } + } + else { + in.publicInfo.nvPublic.nameAlg = WOLFTPM2_WRAP_DIGEST; + } in.publicInfo.nvPublic.attributes = nvAttributes; in.publicInfo.nvPublic.dataSize = (UINT16)maxSize; if (authPolicy != NULL && authPolicySz > 0) { @@ -5761,6 +5848,15 @@ int wolfTPM2_NVCreateAuthPolicy(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, return rc; } +int wolfTPM2_NVCreateAuthPolicy(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, + WOLFTPM2_NV* nv, word32 nvIndex, word32 nvAttributes, word32 maxSize, + const byte* auth, int authSz, const byte* authPolicy, int authPolicySz) +{ + return wolfTPM2_NVCreateAuthPolicy_ex(dev, parent, nv, nvIndex, + nvAttributes, maxSize, auth, authSz, authPolicy, authPolicySz, + TPM_ALG_NULL); +} + int wolfTPM2_NVCreateAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, WOLFTPM2_NV* nv, word32 nvIndex, word32 nvAttributes, word32 maxSize, const byte* auth, int authSz) @@ -7303,9 +7399,20 @@ int wolfTPM2_GetKeyTemplate_KeyedHash(TPMT_PUBLIC* publicTemplate, TPMA_OBJECT_noDA | (isSign ? TPMA_OBJECT_sign : 0) | (isDecrypt ? TPMA_OBJECT_decrypt : 0)); - publicTemplate->parameters.keyedHashDetail.scheme.scheme = TPM_ALG_HMAC; - publicTemplate->parameters.keyedHashDetail.scheme.details.hmac.hashAlg = - hashAlg; + /* HMAC scheme requires the sign attribute. When the caller asks for + * neither sign nor decrypt, treat this as a data/seal-style keyed-hash + * object and use TPM_ALG_NULL so the template is loadable and not + * stuck with an unusable HMAC binding. */ + if (isSign || isDecrypt) { + publicTemplate->parameters.keyedHashDetail.scheme.scheme = + TPM_ALG_HMAC; + publicTemplate->parameters.keyedHashDetail.scheme.details.hmac.hashAlg + = hashAlg; + } + else { + publicTemplate->parameters.keyedHashDetail.scheme.scheme = + TPM_ALG_NULL; + } return TPM_RC_SUCCESS; } diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index e3d804a5..36980bb7 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -1096,6 +1096,71 @@ static UINT32 CreatePrimaryHelper(FWTPM_CTX* ctx, TPM_ALG_ID alg) return GetU32BE(gRsp + TPM2_HEADER_SIZE); } +#if defined(HAVE_ECC) && !defined(FWTPM_NO_ATTESTATION) && \ + !defined(FWTPM_NO_NV) +/* Build a non-restricted ECC-P256 sign-capable primary (for tests that + * require TPMA_OBJECT_sign and a key with no scheme bound at create time). + * Only consumed by the attestation tests nested inside the NV-tests + * section, so gated to avoid -Werror=unused-function in + * FWTPM_NO_ATTESTATION or FWTPM_NO_NV builds. */ +static int BuildCreatePrimaryEccSignCmd(byte* buf) +{ + int pos = 0; + int pubAreaStart, pubAreaLen; + int sensStart, sensLen; + + PutU16BE(buf + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(buf + pos, 0); pos += 4; + PutU32BE(buf + pos, TPM_CC_CreatePrimary); pos += 4; + PutU32BE(buf + pos, TPM_RH_OWNER); pos += 4; + PutU32BE(buf + pos, 9); pos += 4; + PutU32BE(buf + pos, TPM_RS_PW); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; + buf[pos++] = 0; + PutU16BE(buf + pos, 0); pos += 2; + + sensStart = pos; + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, 0); pos += 2; + sensLen = pos - sensStart - 2; + PutU16BE(buf + sensStart, (UINT16)sensLen); + + pubAreaStart = pos; + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, TPM_ALG_ECC); pos += 2; + PutU16BE(buf + pos, TPM_ALG_SHA256); pos += 2; + /* fixedTPM | fixedParent | sensitiveDataOrigin | userWithAuth | noDA | + * sign (non-restricted, sign-only) */ + PutU32BE(buf + pos, 0x00040472); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; /* authPolicy */ + PutU16BE(buf + pos, TPM_ALG_NULL); pos += 2; /* sym.algorithm = NULL */ + PutU16BE(buf + pos, TPM_ALG_NULL); pos += 2; /* scheme = NULL */ + PutU16BE(buf + pos, TPM_ECC_NIST_P256); pos += 2; + PutU16BE(buf + pos, TPM_ALG_NULL); pos += 2; /* kdf */ + PutU16BE(buf + pos, 0); pos += 2; /* x */ + PutU16BE(buf + pos, 0); pos += 2; /* y */ + + pubAreaLen = pos - pubAreaStart - 2; + PutU16BE(buf + pubAreaStart, (UINT16)pubAreaLen); + + PutU16BE(buf + pos, 0); pos += 2; /* outsideInfo */ + PutU32BE(buf + pos, 0); pos += 4; /* creationPCR */ + + PutU32BE(buf + 2, (UINT32)pos); + return pos; +} + +static UINT32 CreatePrimaryEccSignHelper(FWTPM_CTX* ctx) +{ + int cmdSz, rspSize = 0; + cmdSz = BuildCreatePrimaryEccSignCmd(gCmd); + FWTPM_ProcessCommand(ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + if (GetRspRC(gRsp) != TPM_RC_SUCCESS) return 0; + return GetU32BE(gRsp + TPM2_HEADER_SIZE); +} +#endif /* HAVE_ECC && !FWTPM_NO_ATTESTATION && !FWTPM_NO_NV */ + /* Helper: flush a handle */ static void FlushHandle(FWTPM_CTX* ctx, UINT32 handle) { @@ -1515,6 +1580,287 @@ static void test_fwtpm_nv_counter(void) FWTPM_Cleanup(&ctx); printf("Test fwTPM:\tNV_Increment (counter):\t\tPassed\n"); } + +#ifndef FWTPM_NO_ATTESTATION +#ifdef HAVE_ECC +/* TPMT_SIG_SCHEME parser must consume the extra UINT16 count field for + * TPMS_SCHEME_ECDAA per Part 2 Sec. 11.2.1.5. Sending a Quote with + * inScheme.scheme = TPM_ALG_ECDAA followed by hashAlg + count + a single + * PCR selection (count=1) verifies that subsequent PCRselect parsing + * is not desynchronized. With the bug, pcrSelectionsCount reads as 0 + * because the count field is interpreted as the high 16 bits of the + * PCR-selection count. ECC-only because the RSA sign path rejects + * TPM_ALG_ECDAA with TPM_RC_SCHEME. */ +static void test_fwtpm_quote_ecdaa_scheme(void) +{ + FWTPM_CTX ctx; + int pos, rspSize; + UINT32 keyH; + UINT16 nameSz; + UINT16 extraSz; + UINT32 pcrSelCount; + int pcrSelOffset; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + keyH = CreatePrimaryHelper(&ctx, TPM_ALG_ECC); + AssertIntNE(keyH, 0); + + /* Build TPM2_Quote with ECDAA inScheme. */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_Quote); pos += 4; + PutU32BE(gCmd + pos, keyH); pos += 4; /* signHandle */ + pos = AppendPwAuth(gCmd, pos, NULL, 0); + /* qualifyingData (TPM2B_DATA) - empty */ + PutU16BE(gCmd + pos, 0); pos += 2; + /* inScheme: ECDAA + hashAlg + count */ + PutU16BE(gCmd + pos, TPM_ALG_ECDAA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU16BE(gCmd + pos, 0); pos += 2; /* ECDAA count */ + /* PCRselect: count=1, hashAlg=SHA256, sizeOfSelect=3, no PCRs */ + PutU32BE(gCmd + pos, 1); pos += 4; + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + gCmd[pos++] = 3; + gCmd[pos++] = 0; + gCmd[pos++] = 0; + gCmd[pos++] = 0; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* Walk response: header(10) + paramSize(4) + TPM2B_ATTEST.size(2) + * + magic(4) + type(2) + qualifiedSigner.size(2) reads N at offset 22 */ + AssertIntGT(rspSize, 24); + nameSz = GetU16BE(gRsp + 22); + AssertIntGT(nameSz, 0); + /* Then qualifiedSigner.name(nameSz) + extraData.size(2) */ + extraSz = GetU16BE(gRsp + 24 + nameSz); + /* Then extraData(extraSz) + clockInfo(17) + firmwareVersion(8) */ + pcrSelOffset = 24 + nameSz + 2 + extraSz + 17 + 8; + AssertIntGT(rspSize, pcrSelOffset + 4); + pcrSelCount = GetU32BE(gRsp + pcrSelOffset); + /* With the ECDAA count consumed correctly, pcrSelCount reflects the + * caller-supplied value of 1. With the bug, the count field is read + * as the high 16 bits of pcrSelCount, yielding 0. */ + AssertIntEQ(pcrSelCount, 1); + + FlushHandle(&ctx, keyH); + FWTPM_Cleanup(&ctx); + printf("Test fwTPM:\tQuote(ECDAA scheme):\t\tPassed\n"); +} +#endif /* HAVE_ECC */ + +#ifdef HAVE_ECC +/* TPM2_CertifyCreation inScheme parser must consume the extra UINT16 + * count for TPMS_SCHEME_ECDAA per Part 2 Sec. 11.2.1.5. With the bug, the + * trailing TPMT_TK_CREATION ticket parses from a wrong wire offset and + * the tag check fails. */ +static void test_fwtpm_certify_creation_ecdaa_scheme(void) +{ + FWTPM_CTX ctx; + int pos, rspSize; + UINT32 keyH; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + keyH = CreatePrimaryEccSignHelper(&ctx); + AssertIntNE(keyH, 0); + + /* Build TPM2_CertifyCreation with ECDAA inScheme. Use the same key as + * both signHandle and objectHandle to avoid additional setup. The + * ticket carries hier=TPM_RH_NULL with a zero digest so HMAC + * validation is skipped (only the tag is checked). */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_CertifyCreation); pos += 4; + PutU32BE(gCmd + pos, keyH); pos += 4; /* signHandle */ + PutU32BE(gCmd + pos, keyH); pos += 4; /* objectHandle */ + pos = AppendPwAuth(gCmd, pos, NULL, 0); + /* qualifyingData (empty) */ + PutU16BE(gCmd + pos, 0); pos += 2; + /* creationHash (empty) */ + PutU16BE(gCmd + pos, 0); pos += 2; + /* inScheme: ECDAA + hashAlg + count */ + PutU16BE(gCmd + pos, TPM_ALG_ECDAA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU16BE(gCmd + pos, 0); pos += 2; /* ECDAA count */ + /* creationTicket (TPMT_TK_CREATION): tag + hier + digest */ + PutU16BE(gCmd + pos, TPM_ST_CREATION); pos += 2; + PutU32BE(gCmd + pos, TPM_RH_NULL); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + FlushHandle(&ctx, keyH); + FWTPM_Cleanup(&ctx); + printf("Test fwTPM:\tCertifyCreation(ECDAA scheme):\tPassed\n"); +} + +/* TPM2_Sign inScheme parser must consume the extra UINT16 count for + * TPMS_SCHEME_ECDAA per Part 2 Sec. 11.2.1.5. With the bug, the trailing + * TPMT_TK_HASHCHECK ticket parses from the wrong wire offset and the + * command fails. */ +static void test_fwtpm_sign_ecdaa_scheme(void) +{ + FWTPM_CTX ctx; + int pos, rspSize; + UINT32 keyH; + byte digest[32]; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + keyH = CreatePrimaryEccSignHelper(&ctx); + AssertIntNE(keyH, 0); + + memset(digest, 0xAA, sizeof(digest)); + + /* Build TPM2_Sign with ECDAA inScheme and an empty ticket + * (TPM_RH_NULL hierarchy + zero size, valid for non-restricted keys). */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_Sign); pos += 4; + PutU32BE(gCmd + pos, keyH); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + /* digest */ + PutU16BE(gCmd + pos, (UINT16)sizeof(digest)); pos += 2; + memcpy(gCmd + pos, digest, sizeof(digest)); pos += sizeof(digest); + /* inScheme: ECDAA + hashAlg + count */ + PutU16BE(gCmd + pos, TPM_ALG_ECDAA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU16BE(gCmd + pos, 0); pos += 2; /* ECDAA count */ + /* validation (TPMT_TK_HASHCHECK): tag + hierarchy + digest */ + PutU16BE(gCmd + pos, TPM_ST_HASHCHECK); pos += 2; + PutU32BE(gCmd + pos, TPM_RH_NULL); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + FlushHandle(&ctx, keyH); + FWTPM_Cleanup(&ctx); + printf("Test fwTPM:\tSign(ECDAA scheme):\t\tPassed\n"); +} +#endif /* HAVE_ECC */ + +/* NV_Certify with size=0 and offset=0 must emit TPMS_NV_DIGEST_CERTIFY_INFO + * inside a TPM_ST_ATTEST_NV_DIGEST (0x801C) attest, not the regular + * TPMS_NV_CERTIFY_INFO inside TPM_ST_ATTEST_NV (0x8014). Per TPM 2.0 + * Part 3 Sec. 31.16.1. */ +static void test_fwtpm_nv_certify_digest_mode(void) +{ + FWTPM_CTX ctx; + int pos, rspSize, cmdSz; + UINT32 nvIdx = 0x01500004; + UINT32 attrs = TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA; + UINT32 keyH; + UINT16 attestType; + byte testData[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + /* Create primary signing key (use existing helper - the FwTPM signer + * does not enforce sign attribute, so a restricted-decrypt key suffices + * to exercise the attest-tag path under test). */ +#ifdef HAVE_ECC + keyH = CreatePrimaryHelper(&ctx, TPM_ALG_ECC); +#else + keyH = CreatePrimaryHelper(&ctx, TPM_ALG_RSA); +#endif + AssertIntNE(keyH, 0); + + /* Define NV index */ + cmdSz = BuildNvDefineCmd(gCmd, nvIdx, 32, attrs); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* Write some data so the index is "written" (required for certify) */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_NV_Write); pos += 4; + PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4; + PutU32BE(gCmd + pos, nvIdx); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU16BE(gCmd + pos, (UINT16)sizeof(testData)); pos += 2; + memcpy(gCmd + pos, testData, sizeof(testData)); pos += sizeof(testData); + PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* NV_Certify with size=0 and offset=0 -> must yield digest-mode attest */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_NV_Certify); pos += 4; + PutU32BE(gCmd + pos, keyH); pos += 4; /* signHandle */ + PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4; /* authHandle */ + PutU32BE(gCmd + pos, nvIdx); pos += 4; /* nvIndex */ + /* Two-session auth area (signHandle + authHandle): 2 * 9 = 18 bytes */ + PutU32BE(gCmd + pos, 18); pos += 4; + PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; /* nonce */ + gCmd[pos++] = 0; /* attrs */ + PutU16BE(gCmd + pos, 0); pos += 2; /* hmac */ + PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + gCmd[pos++] = 0; + PutU16BE(gCmd + pos, 0); pos += 2; + /* qualifyingData (TPM2B_DATA) - empty */ + PutU16BE(gCmd + pos, 0); pos += 2; + /* inScheme: explicit ECDSA/RSASSA + SHA-256 */ +#ifdef HAVE_ECC + PutU16BE(gCmd + pos, TPM_ALG_ECDSA); pos += 2; +#else + PutU16BE(gCmd + pos, TPM_ALG_RSASSA); pos += 2; +#endif + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + /* size = 0, offset = 0 -> digest mode */ + PutU16BE(gCmd + pos, 0); pos += 2; + PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* Response layout (TPM_ST_SESSIONS): + * header(10) + paramSize(4) + TPM2B_ATTEST.size(2) + magic(4) + type(2) + * Attest type lives at offset 20. Spec requires 0x801C (NV_DIGEST). */ + AssertIntGT(rspSize, 22); + attestType = GetU16BE(gRsp + 20); + AssertIntEQ(attestType, 0x801C); + + /* Cleanup: flush key and undefine NV */ + FlushHandle(&ctx, keyH); + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_NV_UndefineSpace); pos += 4; + PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4; + PutU32BE(gCmd + pos, nvIdx); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + + FWTPM_Cleanup(&ctx); + printf("Test fwTPM:\tNV_Certify (digest mode):\tPassed\n"); +} +#endif /* !FWTPM_NO_ATTESTATION */ #endif /* !FWTPM_NO_NV */ /* ================================================================== */ @@ -2195,6 +2541,14 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_nv_define_write_read(); test_fwtpm_nv_read_public(); test_fwtpm_nv_counter(); +#ifndef FWTPM_NO_ATTESTATION + test_fwtpm_nv_certify_digest_mode(); +#ifdef HAVE_ECC + test_fwtpm_quote_ecdaa_scheme(); + test_fwtpm_sign_ecdaa_scheme(); + test_fwtpm_certify_creation_ecdaa_scheme(); +#endif +#endif #endif /* Hierarchy & misc */ diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 413052c7..cdee4c54 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -585,6 +585,47 @@ static void test_wolfTPM2_SetAuthHandle_PolicyAuthOffset(void) #endif } +/* Verify wolfTPM2_StartSession enables encrypt/decrypt attributes for + * salted (tpmKey-only, bind == NULL) sessions when caller selects a + * symmetric algorithm. Per TPM 2.0 spec, salted sessions have valid + * shared-secret state for parameter encryption. */ +static void test_wolfTPM2_StartSession_SaltedEncryptAttrs(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY tpmKey; + WOLFTPM2_SESSION session; + TPMA_SESSION expected = TPMA_SESSION_decrypt | TPMA_SESSION_encrypt; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&tpmKey, 0, sizeof(tpmKey)); + XMEMSET(&session, 0, sizeof(session)); + + /* Initialize so TPM2_GetNonceNoLock and dependent code paths have a + * valid context. Skip if no TPM is reachable. */ + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc != 0) { + printf("Test TPM Wrapper:\tStartSession salted enc attrs:\tSkipped\n"); + return; + } + + /* tpmKey with a non-NULL handle, no auth */ + tpmKey.handle.hndl = 0x80000000; + + /* The call will fail later (no real key with that handle) but the + * SetAuth path that sets sessionAttributes runs first. */ + (void)wolfTPM2_StartSession(&dev, &session, &tpmKey, NULL, + TPM_SE_HMAC, TPM_ALG_CFB); + + AssertIntEQ((int)(dev.session[0].sessionAttributes & expected), + (int)expected); + + wolfTPM2_Cleanup(&dev); + printf("Test TPM Wrapper:\tStartSession salted enc attrs:\tPassed\n"); +#endif +} + static void test_wolfTPM2_PolicyHash(void) { #ifndef WOLFTPM2_NO_WOLFCRYPT @@ -1871,6 +1912,529 @@ static void test_TPM2_ECC_Parameters_EcdaaResponseParse(void) printf("Test TPM Wrapper:\tEcdaaResponseParse:\t\tPassed\n"); } +/* TPM2_Packet_AppendSignature / ParseSignature must explicitly recognize + * TPM_ALG_NULL as a zero-payload signature so subsequent fields stay + * aligned. The previous default-fallthrough lumped TPM_ALG_NULL together + * with unknown algorithms, making the property "Parse(Append(NULL)) + * consumes exactly the sigAlg bytes" depend on undocumented behavior. */ +static void test_TPM2_ParseSignature_NullAlg(void) +{ + TPM2_Packet packet; + byte buf[16]; + TPMT_SIGNATURE sig; + UINT16 sentinel; + int pos = 0; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + + /* sigAlg = TPM_ALG_NULL */ + buf[pos++] = (byte)((TPM_ALG_NULL >> 8) & 0xFF); + buf[pos++] = (byte)(TPM_ALG_NULL & 0xFF); + /* sentinel right after the (zero-length) signature payload */ + buf[pos++] = 0xDE; + buf[pos++] = 0xAD; + + XMEMSET(&sig, 0, sizeof(sig)); + packet.buf = buf; + packet.size = pos; + packet.pos = 0; + + TPM2_Packet_ParseSignature(&packet, &sig); + AssertIntEQ(sig.sigAlg, TPM_ALG_NULL); + AssertIntEQ(packet.pos, 2); + sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]); + AssertIntEQ(sentinel, 0xDEAD); + + /* Round-trip: Append a TPM_ALG_NULL signature into a fresh packet and + * verify only the 2-byte sigAlg was written. A future regression that + * drops the explicit case (defaulting to silent fallthrough) would + * still pass for Parse but the Append side is also locked in here. */ + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + XMEMSET(&sig, 0, sizeof(sig)); + sig.sigAlg = TPM_ALG_NULL; + packet.buf = buf; + packet.size = sizeof(buf); + packet.pos = 0; + TPM2_Packet_AppendSignature(&packet, &sig); + AssertIntEQ(packet.pos, 2); + AssertIntEQ(buf[0], (byte)((TPM_ALG_NULL >> 8) & 0xFF)); + AssertIntEQ(buf[1], (byte)(TPM_ALG_NULL & 0xFF)); + + /* Re-parse confirms the round-trip. */ + XMEMSET(&sig, 0, sizeof(sig)); + packet.pos = 0; + TPM2_Packet_ParseSignature(&packet, &sig); + AssertIntEQ(sig.sigAlg, TPM_ALG_NULL); + AssertIntEQ(packet.pos, 2); + + printf("Test TPM Wrapper:\tParseSignature NULL alg:\tPassed\n"); +} + +/* TPM2_Packet_ParsePoint must resync to outerStart + point->size so a + * malformed wire blob with inner x.size / y.size disagreement can't + * desynchronize subsequent fields. */ +static void test_TPM2_ParsePoint_OuterResync(void) +{ + TPM2_Packet packet; + byte buf[64]; + TPM2B_ECC_POINT point; + UINT16 sentinel; + int outerStart, fakeOuterSz; + int pos = 0; + int innerStart; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + + /* Build TPM2B_ECC_POINT: outer.size + x(2+0) + y(2+0). Declare outer + * size larger than actual inner consumption (4 bytes). */ + outerStart = pos; + pos += 2; /* size placeholder */ + innerStart = pos; + /* x.size = 0 */ + buf[pos++] = 0; buf[pos++] = 0; + /* y.size = 0 */ + buf[pos++] = 0; buf[pos++] = 0; + + fakeOuterSz = (pos - innerStart) + 6; /* 6 padding bytes */ + buf[outerStart] = (byte)((fakeOuterSz >> 8) & 0xFF); + buf[outerStart + 1] = (byte)(fakeOuterSz & 0xFF); + pos += 6; + + /* Sentinel U16 right after outer end */ + buf[pos++] = 0xBE; + buf[pos++] = 0xEF; + + XMEMSET(&point, 0, sizeof(point)); + packet.buf = buf; + packet.size = pos; + packet.pos = 0; + + TPM2_Packet_ParsePoint(&packet, &point); + + /* Position must land at outerStart + 2 + outer.size (= 2 + 10 = 12). + * Read sentinel by hand. */ + AssertIntEQ(packet.pos, 2 + fakeOuterSz); + sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]); + AssertIntEQ(sentinel, 0xBEEF); + + printf("Test TPM Wrapper:\tParsePoint outer resync:\tPassed\n"); +} + +/* TPM2_Packet_ParsePublic must resync the packet position to outerStart + + * pub->size so a malformed wire blob with inner-size disagreement can't + * desynchronize subsequent fields. Pre-fix the parser left the position + * wherever the inner parses ended, drifting from the declared outer size. */ +static void test_TPM2_ParsePublic_OuterResync(void) +{ + TPM2_Packet packet; + byte buf[256]; + TPM2B_PUBLIC pub; + UINT16 sentinel = 0; + int outerStart, fakeOuterSz; + int pos = 0; + int innerStart; + + /* Build a TPM2B_PUBLIC blob by hand with type=RSA, valid inner fields, + * but outer.size declared larger than the actual inner consumption. A + * sentinel is placed at outerStart + 2 + outer.size; only a parser + * that resyncs to that anchor will read the sentinel correctly. */ + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + + outerStart = pos; + pos += 2; /* size placeholder */ + innerStart = pos; + /* type = RSA (0x0001) */ + buf[pos++] = 0x00; buf[pos++] = 0x01; + /* nameAlg = SHA256 (0x000B) */ + buf[pos++] = 0x00; buf[pos++] = 0x0B; + /* objectAttributes = 0 */ + buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; + /* authPolicy: size=0 */ + buf[pos++] = 0; buf[pos++] = 0; + /* RSA params: sym.alg=NULL(2), scheme=NULL(2), keyBits=2048(2), + * exponent=0(4) */ + buf[pos++] = 0x00; buf[pos++] = 0x10; /* TPM_ALG_NULL */ + buf[pos++] = 0x00; buf[pos++] = 0x10; /* scheme NULL */ + buf[pos++] = 0x08; buf[pos++] = 0x00; /* keyBits = 2048 */ + buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; /* exp */ + /* unique.size = 0 */ + buf[pos++] = 0; buf[pos++] = 0; + + /* Declared outer size = actual inner + 8 padding bytes. */ + fakeOuterSz = (pos - innerStart) + 8; + buf[outerStart] = (byte)((fakeOuterSz >> 8) & 0xFF); + buf[outerStart + 1] = (byte)(fakeOuterSz & 0xFF); + /* 8 zero pad bytes */ + pos += 8; + + /* Sentinel U16 at outerStart + 2 + fakeOuterSz */ + buf[pos++] = 0xCA; + buf[pos++] = 0xFE; + + XMEMSET(&pub, 0, sizeof(pub)); + packet.buf = buf; + packet.size = pos; + packet.pos = 0; + + TPM2_Packet_ParsePublic(&packet, &pub); + AssertIntEQ(pub.publicArea.type, TPM_ALG_RSA); + AssertIntEQ(pub.publicArea.nameAlg, TPM_ALG_SHA256); + + /* Position must land at outerStart + 2 + outer.size; the sentinel U16 + * lives at that offset. Read by hand to avoid pulling in WOLFTPM_LOCAL + * parser helpers from the test binary. */ + AssertIntEQ(packet.pos, 2 + fakeOuterSz); + sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]); + AssertIntEQ(sentinel, 0xCAFE); + + printf("Test TPM Wrapper:\tParsePublic outer resync:\tPassed\n"); +} + +/* TPM2_ParseAttest must handle TPM_ST_ATTEST_NV_DIGEST (0x801C) and decode + * TPMS_NV_DIGEST_CERTIFY_INFO. Pre-fix, the switch fell through to default + * and left out->attested zeroed. */ +static void test_TPM2_ParseAttest_NvDigest(void) +{ + TPM2B_ATTEST attestBlob; + TPMS_ATTEST out; + const byte name[] = {0x00, 0x0B, 0x11, 0x22, 0x33, 0x44}; /* alg + 4 bytes */ + const byte digest[] = {0xAA, 0xBB, 0xCC, 0xDD}; + byte* buf; + int pos = 0; + int rc; + + XMEMSET(&attestBlob, 0, sizeof(attestBlob)); + buf = attestBlob.attestationData; + + /* magic */ + buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 24) & 0xFF); + buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 16) & 0xFF); + buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 8) & 0xFF); + buf[pos++] = (byte)(TPM_GENERATED_VALUE & 0xFF); + /* type = TPM_ST_ATTEST_NV_DIGEST (0x801C) */ + buf[pos++] = 0x80; buf[pos++] = 0x1C; + /* qualifiedSigner: empty */ + buf[pos++] = 0; buf[pos++] = 0; + /* extraData: empty */ + buf[pos++] = 0; buf[pos++] = 0; + /* clockInfo: clock(8)+resetCount(4)+restartCount(4)+safe(1) */ + XMEMSET(buf + pos, 0, 17); pos += 17; + /* firmwareVersion */ + XMEMSET(buf + pos, 0, 8); pos += 8; + /* TPMS_NV_DIGEST_CERTIFY_INFO: indexName + nvDigest */ + buf[pos++] = 0; buf[pos++] = (byte)sizeof(name); + XMEMCPY(buf + pos, name, sizeof(name)); pos += sizeof(name); + buf[pos++] = 0; buf[pos++] = (byte)sizeof(digest); + XMEMCPY(buf + pos, digest, sizeof(digest)); pos += sizeof(digest); + + attestBlob.size = (UINT16)pos; + + XMEMSET(&out, 0, sizeof(out)); + rc = TPM2_ParseAttest(&attestBlob, &out); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + AssertIntEQ(out.magic, TPM_GENERATED_VALUE); + AssertIntEQ(out.type, 0x801C); + AssertIntEQ(out.attested.nvDigest.indexName.size, sizeof(name)); + AssertIntEQ(XMEMCMP(out.attested.nvDigest.indexName.name, name, + sizeof(name)), 0); + AssertIntEQ(out.attested.nvDigest.nvDigest.size, sizeof(digest)); + AssertIntEQ(XMEMCMP(out.attested.nvDigest.nvDigest.buffer, digest, + sizeof(digest)), 0); + + printf("Test TPM Wrapper:\tParseAttest NV_DIGEST:\t\tPassed\n"); +} + +/* wolfTPM2_LoadEccPublicKey_ex must honor caller-provided scheme, hashAlg + * and objectAttributes (in particular, allow TPMA_OBJECT_decrypt for ECDH + * peer keys). The legacy wolfTPM2_LoadEccPublicKey must continue to default + * to ECDSA + sign attribute. */ +static void test_wolfTPM2_LoadEccPublicKey_Ex(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY srk; + WOLFTPM2_KEY peer; + TPMT_PUBLIC pub; + byte xBuf[32]; + byte yBuf[32]; + word32 xSz, ySz; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&srk, 0, sizeof(srk)); + XMEMSET(&peer, 0, sizeof(peer)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc != 0) { + printf("Test TPM Wrapper:\tLoadEccPublicKey_ex:\tSkipped\n"); + return; + } + /* Flush any transient objects left by previous tests so CreatePrimary + * does not get TPM_RC_OBJECT_MEMORY on a busy simulator. */ + (void)wolfTPM2_UnloadHandles_AllTransient(&dev); + + /* Create an ECC SRK to harvest valid P-256 X/Y coordinates from. */ + XMEMSET(&pub, 0, sizeof(pub)); + rc = wolfTPM2_GetKeyTemplate_ECC_SRK(&pub); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = wolfTPM2_CreatePrimaryKey(&dev, &srk, TPM_RH_OWNER, &pub, NULL, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + xSz = srk.pub.publicArea.unique.ecc.x.size; + ySz = srk.pub.publicArea.unique.ecc.y.size; + AssertIntGT(xSz, 0); + AssertIntGT(ySz, 0); + XMEMCPY(xBuf, srk.pub.publicArea.unique.ecc.x.buffer, xSz); + XMEMCPY(yBuf, srk.pub.publicArea.unique.ecc.y.buffer, ySz); + + /* Load same coordinates as a peer ECDH key with the decrypt attribute */ + rc = wolfTPM2_LoadEccPublicKey_ex(&dev, &peer, TPM_ECC_NIST_P256, + xBuf, xSz, yBuf, ySz, + TPM_ALG_ECDH, TPM_ALG_SHA256, + TPMA_OBJECT_decrypt | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(peer.pub.publicArea.parameters.eccDetail.scheme.scheme, + TPM_ALG_ECDH); + AssertIntEQ((int)(peer.pub.publicArea.objectAttributes & + TPMA_OBJECT_decrypt), (int)TPMA_OBJECT_decrypt); + AssertIntEQ((int)(peer.pub.publicArea.objectAttributes & + TPMA_OBJECT_sign), 0); + wolfTPM2_UnloadHandle(&dev, &peer.handle); + + /* Legacy wolfTPM2_LoadEccPublicKey: still defaults to ECDSA + sign */ + XMEMSET(&peer, 0, sizeof(peer)); + rc = wolfTPM2_LoadEccPublicKey(&dev, &peer, TPM_ECC_NIST_P256, + xBuf, xSz, yBuf, ySz); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(peer.pub.publicArea.parameters.eccDetail.scheme.scheme, + TPM_ALG_ECDSA); + AssertIntEQ((int)(peer.pub.publicArea.objectAttributes & + TPMA_OBJECT_sign), (int)TPMA_OBJECT_sign); + AssertIntEQ((int)(peer.pub.publicArea.objectAttributes & + TPMA_OBJECT_decrypt), 0); + wolfTPM2_UnloadHandle(&dev, &peer.handle); + + wolfTPM2_UnloadHandle(&dev, &srk.handle); + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tLoadEccPublicKey_ex:\t\tPassed\n"); +#endif +} + +/* wolfTPM2_GetKeyTemplate_KeyedHash must default scheme to TPM_ALG_NULL + * when neither isSign nor isDecrypt is set; an HMAC scheme without the + * sign attribute produces an unusable keyed-hash object. */ +static void test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) + int rc; + TPMT_PUBLIC tpl; + + /* Data/seal-style: isSign=0, isDecrypt=0 -> scheme must be NULL */ + XMEMSET(&tpl, 0, sizeof(tpl)); + rc = wolfTPM2_GetKeyTemplate_KeyedHash(&tpl, TPM_ALG_SHA256, 0, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.scheme, TPM_ALG_NULL); + AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_sign), 0); + AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_decrypt), 0); + + /* HMAC-style: isSign=1 -> scheme HMAC + hashAlg + sign attribute */ + XMEMSET(&tpl, 0, sizeof(tpl)); + rc = wolfTPM2_GetKeyTemplate_KeyedHash(&tpl, TPM_ALG_SHA256, 1, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.scheme, TPM_ALG_HMAC); + AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.details.hmac.hashAlg, + TPM_ALG_SHA256); + AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_sign), + (int)TPMA_OBJECT_sign); + + printf("Test TPM Wrapper:\tKeyedHash template scheme:\tPassed\n"); +#endif +} + +/* wolfTPM2_VerifyHashTicket must apply the same RSA-strict / ECDSA-permissive + * digest size policy as wolfTPM2_SignHashScheme. The bounds check fires + * before any TPM call so this test does not require a working TPM. */ +static void test_wolfTPM2_VerifyHashTicket_DigestSize(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + byte digest[TPM_MAX_DIGEST_SIZE]; + byte sig[MAX_RSA_KEY_BYTES]; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(digest, 0xCC, sizeof(digest)); + XMEMSET(sig, 0, sizeof(sig)); + key.handle.hndl = 0x80000000; + key.pub.publicArea.type = TPM_ALG_RSA; + + /* SHA-256 digest (32) + hashAlg=SHA512 -> RSA mismatch -> BUFFER_E */ + rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, 32, + TPM_ALG_RSASSA, TPM_ALG_SHA512, NULL); + AssertIntEQ(rc, BUFFER_E); + + /* Oversized digest (64) + hashAlg=SHA256 -> BUFFER_E */ + rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, 64, + TPM_ALG_RSASSA, TPM_ALG_SHA256, NULL); + AssertIntEQ(rc, BUFFER_E); + + /* Negative digestSz -> BUFFER_E */ + rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, -1, + TPM_ALG_RSASSA, TPM_ALG_SHA256, NULL); + AssertIntEQ(rc, BUFFER_E); + + printf("Test TPM Wrapper:\tVerifyHashTicket size:\t\tPassed\n"); +#endif +} + +/* wolfTPM2_NVCreateAuthPolicy must derive nameAlg from authPolicySz so + * the policy digest hash matches the index's nameAlg. Bug-mode hardcoded + * SHA-256 nameAlg, which made SHA-384/SHA-512 policies unsatisfiable. + * Mismatched digest sizes must be rejected up front. */ +static void test_wolfTPM2_NVCreateAuthPolicy_NameAlg(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_HANDLE parent; + WOLFTPM2_NV nv; + byte policy[64]; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&parent, 0, sizeof(parent)); + XMEMSET(&nv, 0, sizeof(nv)); + XMEMSET(policy, 0xAB, sizeof(policy)); + + /* No real TPM call required to exercise the new size validation - the + * mismatch check fires before TPM2_NV_DefineSpace is contacted. */ + parent.hndl = TPM_RH_OWNER; + + /* 33 bytes is not a recognized hash digest size -> BAD_FUNC_ARG. */ + rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, 0x01400001, + TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA, 64, + NULL, 0, policy, 33); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* 17 bytes is not a recognized hash digest size -> BAD_FUNC_ARG. */ + rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, 0x01400002, + TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA, 64, + NULL, 0, policy, 17); + AssertIntEQ(rc, BAD_FUNC_ARG); + + printf("Test TPM Wrapper:\tNVCreateAuthPolicy nameAlg:\tPassed\n"); +#endif +} + +/* wolfTPM2_SignHashScheme must reject digest sizes that don't match the + * declared hashAlg for RSA keys, instead of silently zero-padding. The + * pad-to-hash-size convention is preserved for ECDSA per spec. */ +static void test_wolfTPM2_SignHashScheme_DigestSize(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + byte digest[TPM_MAX_DIGEST_SIZE]; + byte sig[MAX_RSA_KEY_BYTES]; + int sigSz = (int)sizeof(sig); + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(digest, 0xCC, sizeof(digest)); + key.handle.hndl = 0x80000000; + key.pub.publicArea.type = TPM_ALG_RSA; + + /* SHA-256 digest (32) but caller declared SHA-512 (64): for RSA this + * was previously silently zero-padded; now must return BUFFER_E. */ + rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 32, sig, &sigSz, + TPM_ALG_RSASSA, TPM_ALG_SHA512); + AssertIntEQ(rc, BUFFER_E); + + /* Oversized digest (larger than declared hashAlg) is also BUFFER_E. */ + sigSz = (int)sizeof(sig); + rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 64, sig, &sigSz, + TPM_ALG_RSASSA, TPM_ALG_SHA256); + AssertIntEQ(rc, BUFFER_E); + + printf("Test TPM Wrapper:\tSignHashScheme size:\t\tPassed\n"); +#endif +} + +/* wolfTPM2_RsaEncrypt and wolfTPM2_RsaDecrypt must reject oversized inputs + * with BUFFER_E rather than silently truncating to the message buffer + * length. The bounds check fires before the TPM is contacted, so this + * test does not require a working TPM connection. */ +static void test_wolfTPM2_RsaEncryptDecrypt_OversizedBufferE(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + byte oversized[MAX_RSA_KEY_BYTES + 16]; + byte out[MAX_RSA_KEY_BYTES]; + int outSz = (int)sizeof(out); + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(oversized, 0xAB, sizeof(oversized)); + key.handle.hndl = 0x80000000; + + rc = wolfTPM2_RsaEncrypt(&dev, &key, TPM_ALG_NULL, + oversized, (int)sizeof(oversized), out, &outSz); + AssertIntEQ(rc, BUFFER_E); + + outSz = (int)sizeof(out); + rc = wolfTPM2_RsaDecrypt(&dev, &key, TPM_ALG_NULL, + oversized, (int)sizeof(oversized), out, &outSz); + AssertIntEQ(rc, BUFFER_E); + + printf("Test TPM Wrapper:\tRsaEncDec oversized:\t\tPassed\n"); +#endif +} + +/* TPM2_GetTpmCurve / TPM2_GetWolfCurve must map wolfCrypt's + * ECC_BRAINPOOLP256R1 to TPM_ECC_BP_P256_R1 (0x0030), not + * TPM_ECC_BN_P256 (0x0010, Barreto-Naehrig). Pre-fix the two were + * conflated, producing an on-the-wire curve ID that is a different + * mathematical curve. */ +static void test_TPM2_BrainpoolCurveMapping(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) + AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP256R1), TPM_ECC_BP_P256_R1); + AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP384R1), TPM_ECC_BP_P384_R1); + AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP512R1), TPM_ECC_BP_P512_R1); + + AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P256_R1), ECC_BRAINPOOLP256R1); + AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P384_R1), ECC_BRAINPOOLP384R1); + AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P512_R1), ECC_BRAINPOOLP512R1); + + /* TPM_ECC_BN_P256 (Barreto-Naehrig pairing curve) has no wolfCrypt + * equivalent and must report ECC_CURVE_OID_E rather than aliasing + * to a Brainpool ID. */ + AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BN_P256), ECC_CURVE_OID_E); + + /* Sanity: NIST mappings still round-trip. */ + AssertIntEQ(TPM2_GetTpmCurve(ECC_SECP256R1), TPM_ECC_NIST_P256); + AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_NIST_P256), ECC_SECP256R1); + + /* TPM2_GetCurveSize must report the correct byte size for the new + * Brainpool curve IDs (32 / 48 / 64). */ + AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P256_R1), 32); + AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P384_R1), 48); + AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P512_R1), 64); + + printf("Test TPM Wrapper:\tBrainpool curve mapping:\tPassed\n"); +#endif +} + static void test_TPM2_KeyedHashScheme_XorSerialize(void) { TPM2_Packet packet; @@ -3229,6 +3793,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_Policy_NULL_Args(); test_wolfTPM2_PolicyAuthValue_AuthOffset(); test_wolfTPM2_SetAuthHandle_PolicyAuthOffset(); + test_wolfTPM2_StartSession_SaltedEncryptAttrs(); test_wolfTPM2_PolicyHash(); test_wolfTPM2_SensitiveToPrivate(); test_TPM2_KDFa(); @@ -3257,6 +3822,17 @@ int unit_tests(int argc, char *argv[]) #endif test_TPM2_SchemeSerialize(); test_TPM2_ECC_Parameters_EcdaaResponseParse(); + test_TPM2_ParseAttest_NvDigest(); + test_TPM2_ParsePublic_OuterResync(); + test_TPM2_ParsePoint_OuterResync(); + test_TPM2_ParseSignature_NullAlg(); + test_TPM2_BrainpoolCurveMapping(); + test_wolfTPM2_RsaEncryptDecrypt_OversizedBufferE(); + test_wolfTPM2_SignHashScheme_DigestSize(); + test_wolfTPM2_VerifyHashTicket_DigestSize(); + test_wolfTPM2_NVCreateAuthPolicy_NameAlg(); + test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(); + test_wolfTPM2_LoadEccPublicKey_Ex(); test_TPM2_KeyedHashScheme_XorSerialize(); test_TPM2_Signature_EcSchnorrSm2Serialize(); test_TPM2_Sensitive_Roundtrip(); diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index ae724e63..545c4ba3 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -130,6 +130,9 @@ typedef enum { TPM_ECC_BN_P256 = 0x0010, TPM_ECC_BN_P638 = 0x0011, TPM_ECC_SM2_P256 = 0x0020, + TPM_ECC_BP_P256_R1 = 0x0030, + TPM_ECC_BP_P384_R1 = 0x0031, + TPM_ECC_BP_P512_R1 = 0x0032, } TPM_ECC_CURVE_T; typedef UINT16 TPM_ECC_CURVE; @@ -466,6 +469,7 @@ typedef enum { TPM_ST_ATTEST_QUOTE = 0x8018, TPM_ST_ATTEST_TIME = 0x8019, TPM_ST_ATTEST_CREATION = 0x801A, + TPM_ST_ATTEST_NV_DIGEST = 0x801C, TPM_ST_CREATION = 0x8021, TPM_ST_VERIFIED = 0x8022, TPM_ST_AUTH_SECRET = 0x8023, @@ -1196,6 +1200,11 @@ typedef struct TPMS_NV_CERTIFY_INFO { TPM2B_MAX_NV_BUFFER nvContents; } TPMS_NV_CERTIFY_INFO; +typedef struct TPMS_NV_DIGEST_CERTIFY_INFO { + TPM2B_NAME indexName; + TPM2B_DIGEST nvDigest; +} TPMS_NV_DIGEST_CERTIFY_INFO; + typedef TPM_ST TPMI_ST_ATTEST; typedef union TPMU_ATTEST { @@ -1206,6 +1215,7 @@ typedef union TPMU_ATTEST { TPMS_SESSION_AUDIT_INFO sessionAudit; /* TPM_ST_ATTEST_SESSION_AUDIT */ TPMS_TIME_ATTEST_INFO time; /* TPM_ST_ATTEST_TIME */ TPMS_NV_CERTIFY_INFO nv; /* TPM_ST_ATTEST_NV */ + TPMS_NV_DIGEST_CERTIFY_INFO nvDigest; /* TPM_ST_ATTEST_NV_DIGEST */ } TPMU_ATTEST; typedef struct TPMS_ATTEST { diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 256df287..88f6a52f 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -195,8 +195,8 @@ WOLFTPM_LOCAL void TPM2_Packet_AppendAsymScheme(TPM2_Packet* packet, TPMT_ASYM_S WOLFTPM_LOCAL void TPM2_Packet_ParseAsymScheme(TPM2_Packet* packet, TPMT_ASYM_SCHEME* scheme); WOLFTPM_LOCAL void TPM2_Packet_AppendEccPoint(TPM2_Packet* packet, TPMS_ECC_POINT* point); WOLFTPM_LOCAL void TPM2_Packet_ParseEccPoint(TPM2_Packet* packet, TPMS_ECC_POINT* point); -WOLFTPM_LOCAL void TPM2_Packet_AppendPoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); -WOLFTPM_LOCAL void TPM2_Packet_ParsePoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); +WOLFTPM_TEST_API void TPM2_Packet_AppendPoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); +WOLFTPM_TEST_API void TPM2_Packet_ParsePoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); WOLFTPM_TEST_API void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive); WOLFTPM_TEST_API void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive); WOLFTPM_LOCAL void TPM2_Packet_AppendSensitiveCreate(TPM2_Packet* packet, TPM2B_SENSITIVE_CREATE* sensitive); @@ -232,8 +232,8 @@ WOLFTPM_LOCAL TPM_RC TPM2_Packet_ParseSensitiveCreate(TPM2_Packet* packet, WOLFTPM_LOCAL void TPM2_Packet_AppendPublicParms(TPM2_Packet* packet, TPMI_ALG_PUBLIC type, TPMU_PUBLIC_PARMS* parameters); WOLFTPM_LOCAL void TPM2_Packet_ParsePublicParms(TPM2_Packet* packet, TPMI_ALG_PUBLIC type, TPMU_PUBLIC_PARMS* parameters); WOLFTPM_LOCAL void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea); -WOLFTPM_LOCAL void TPM2_Packet_AppendPublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); -WOLFTPM_LOCAL void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); +WOLFTPM_TEST_API void TPM2_Packet_AppendPublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); +WOLFTPM_TEST_API void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); WOLFTPM_TEST_API void TPM2_Packet_AppendSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); WOLFTPM_TEST_API void TPM2_Packet_ParseSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); WOLFTPM_LOCAL void TPM2_Packet_ParseAttest(TPM2_Packet* packet, TPMS_ATTEST* out); diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 42a2c996..84d74912 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -1313,6 +1313,43 @@ WOLFTPM_API int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, int curveId, const byte* eccPubX, word32 eccPubXSz, const byte* eccPubY, word32 eccPubYSz); +/*! + \ingroup wolfTPM2_Wrappers + \brief Variant of wolfTPM2_LoadEccPublicKey that lets the caller select + the signing scheme, hash algorithm, and object attributes - required + for loading ECDH peer keys (decrypt attribute) or non-default + scheme/hash combinations. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: check the provided arguments, or scheme is + TPM_ALG_ECDAA (use wolfTPM2_LoadPublicKey directly for ECDAA) + \return BUFFER_E: caller buffer is too small for ECC point coordinates + + \param dev pointer to a TPM2_DEV struct + \param key pointer to an empty struct of WOLFTPM2_KEY type + \param curveId integer value, one of the accepted TPM_ECC_CURVE values + \param eccPubX pointer to a byte buffer containing the public material of point X + \param eccPubXSz integer value of word32 type, specifying the point X buffer size + \param eccPubY pointer to a byte buffer containing the public material of point Y + \param eccPubYSz integer value of word32 type, specifying the point Y buffer size + \param scheme ECC signing scheme (e.g. TPM_ALG_ECDSA, TPM_ALG_ECDH, + TPM_ALG_NULL). Use TPM_ALG_ECDH for ECDH peer keys + \param hashAlg hash algorithm bound to scheme; ignored when scheme is + TPM_ALG_NULL + \param objectAttributes TPMA_OBJECT bitmask. Use TPMA_OBJECT_decrypt for + ECDH peer keys, TPMA_OBJECT_sign for verification keys; typically + OR'd with TPMA_OBJECT_userWithAuth and TPMA_OBJECT_noDA + + \sa wolfTPM2_LoadEccPublicKey + \sa wolfTPM2_LoadRsaPublicKey_ex +*/ +WOLFTPM_API int wolfTPM2_LoadEccPublicKey_ex(WOLFTPM2_DEV* dev, + WOLFTPM2_KEY* key, int curveId, + const byte* eccPubX, word32 eccPubXSz, + const byte* eccPubY, word32 eccPubYSz, + TPMI_ALG_ECC_SCHEME scheme, TPMI_ALG_HASH hashAlg, + TPMA_OBJECT objectAttributes); + /*! \ingroup wolfTPM2_Wrappers \brief Helper function to import the private material of an external ECC key @@ -2318,6 +2355,37 @@ WOLFTPM_API int wolfTPM2_NVCreateAuthPolicy(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* WOLFTPM2_NV* nv, word32 nvIndex, word32 nvAttributes, word32 maxSize, const byte* auth, int authSz, const byte* authPolicy, int authPolicySz); +/*! + \ingroup wolfTPM2_Wrappers + \brief Variant of wolfTPM2_NVCreateAuthPolicy that lets the caller pass + an explicit nameAlg. Required when authPolicy was computed with + SM3-256, SHA3-256, or any other non-SHA hash that the size-based + legacy inference cannot distinguish from SHA-256. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: nameAlg digest size disagrees with authPolicySz, + or other invalid arguments + \return BUFFER_E: auth or authPolicy buffer too large + + \param dev pointer to a TPM2_DEV struct + \param parent pointer to a WOLFTPM2_HANDLE for the hierarchy + \param nv pointer to an empty WOLFTPM2_NV that receives the created index + \param nvIndex NV index handle (e.g. 0x01400001) + \param nvAttributes TPMA_NV bitmask + \param maxSize NV index data size in bytes + \param auth optional NV authValue (NULL/0 for none) + \param authSz optional NV authValue size + \param authPolicy optional policy digest (NULL/0 for none) + \param authPolicySz optional policy digest size + \param nameAlg index name hash algorithm (e.g. TPM_ALG_SHA384). Pass + TPM_ALG_NULL to fall back to size-based inference for SHA-only + policies, or to use WOLFTPM2_WRAP_DIGEST for the no-policy case +*/ +WOLFTPM_API int wolfTPM2_NVCreateAuthPolicy_ex(WOLFTPM2_DEV* dev, + WOLFTPM2_HANDLE* parent, WOLFTPM2_NV* nv, word32 nvIndex, + word32 nvAttributes, word32 maxSize, const byte* auth, int authSz, + const byte* authPolicy, int authPolicySz, TPMI_ALG_HASH nameAlg); + /*! \ingroup wolfTPM2_Wrappers \brief Stores user data to a NV Index, at a given offset