mirror of https://github.com/wolfSSL/wolfTPM.git
fwTPM PQC: finish v1.85 protocol wiring for Sign/Verify over mssim
Server-side handler fixes:
- FwCmd_SignSequenceStart / VerifySequenceStart: call FwSkipAuthArea when
cmdTag == TPM_ST_SESSIONS. Without it, the 4-byte authAreaSize prefix
was mis-parsed as the auth / context TPM2B size fields, producing
mis-aligned context bytes (ctxSz=9 on sign, 0 on verify) so the μ fed
into FIPS 204 differed between the two handlers — verify always failed.
- FwCmd_SignSequenceStart / VerifySequenceStart: emit the output
sequenceHandle before FwRspParamsBegin, matching TPM 2.0 response
framing (handles precede the SESSIONS paramSize).
- FwCmd_CreatePrimary: add MLDSA / HASH_MLDSA / MLKEM arms to the
hashUnique switch so the unique template actually binds into KDFa
derivation.
- FwCmd_TestParms: accept PQC algs (MLKEM / MLDSA / HASH_MLDSA).
Client-side wrapper fixes:
- wolfTPM2_CopyPubT: add MLDSA / HASH_MLDSA / MLKEM cases. Previous
switch fell through, leaving unique.mlkem / .mldsa as zero-filled
buffers after a successful CreatePrimary (Jay's reported bug).
- GetKeyTemplateSize: add PQC parameter-set-aware sizes.
- wolfTPM2_SetKeyTemplate_Unique: add PQC arms.
- TPM2_SignSequenceComplete: add CMD_FLAG_AUTH_USER2 (Table 124 requires
USER auth on both @seq and @key handles).
- TPM2_VerifySequenceComplete: remove extra buffer field (Table 118 has
no buffer parameter); add CMD_FLAG_AUTH_USER1.
examples/pqc/pqc_mssim_e2e.c: tighten validation — check_pub_populated
catches CopyPubT-class regressions, MLKEM-768 Encap/Decap secrets must
match, HashMLDSA-65 SignDigest emits a DIGEST_VERIFIED ticket.
pull/445/head
parent
552032d357
commit
4ead816713
|
|
@ -36,6 +36,25 @@
|
|||
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_V185)
|
||||
|
||||
/* Guard against the CopyPubT-class bug where the server-side key exists
|
||||
* and the handle works, but the client-side TPM2B buffer is zero-filled
|
||||
* (Part 2 Table 225 unique arm never copied). */
|
||||
static int check_pub_populated(const char* label, const byte* buf,
|
||||
UINT16 gotSize, UINT16 wantSize)
|
||||
{
|
||||
int i;
|
||||
if (gotSize != wantSize) {
|
||||
printf("%s.size = %u (expected %u)\n", label, gotSize, wantSize);
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < wantSize; i++) {
|
||||
if (buf[i] != 0) return 0;
|
||||
}
|
||||
printf("%s.buffer is all zero (client-side unique-arm copy dropped)\n",
|
||||
label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int test_mlkem_roundtrip(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
WOLFTPM2_KEY mlkem;
|
||||
|
|
@ -64,6 +83,11 @@ static int test_mlkem_roundtrip(WOLFTPM2_DEV* dev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
rc = check_pub_populated("mlkem.unique",
|
||||
mlkem.pub.publicArea.unique.mlkem.buffer,
|
||||
mlkem.pub.publicArea.unique.mlkem.size, 1184);
|
||||
if (rc != 0) goto cleanup;
|
||||
|
||||
rc = wolfTPM2_Encapsulate(dev, &mlkem, ct, &ctSz, ss1, &ss1Sz);
|
||||
if (rc != 0) {
|
||||
printf("Encapsulate rc=%d\n", rc);
|
||||
|
|
@ -126,6 +150,12 @@ static int test_hash_mldsa_digest_roundtrip(WOLFTPM2_DEV* dev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* HashMLDSA shares the mldsa arm of TPMU_PUBLIC_ID. */
|
||||
rc = check_pub_populated("mldsa.unique",
|
||||
mldsa.pub.publicArea.unique.mldsa.buffer,
|
||||
mldsa.pub.publicArea.unique.mldsa.size, 1952);
|
||||
if (rc != 0) goto cleanup;
|
||||
|
||||
rc = wolfTPM2_SignDigest(dev, &mldsa,
|
||||
digest, sizeof(digest),
|
||||
NULL, 0,
|
||||
|
|
|
|||
|
|
@ -1312,6 +1312,11 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
|
|||
#endif
|
||||
case TPM_ALG_HMAC:
|
||||
case TPM_ALG_NULL:
|
||||
#ifdef WOLFTPM_V185
|
||||
case TPM_ALG_MLDSA:
|
||||
case TPM_ALG_HASH_MLDSA:
|
||||
case TPM_ALG_MLKEM:
|
||||
#endif
|
||||
/* Supported - skip remaining type-specific params */
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2289,6 +2294,24 @@ static TPM_RC FwCmd_CreatePrimary(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
uBuf = inPublic->publicArea.unique.sym.buffer;
|
||||
uSz = (int)inPublic->publicArea.unique.sym.size;
|
||||
break;
|
||||
#ifdef WOLFTPM_V185
|
||||
/* MLDSA / HASH_MLDSA / MLKEM: only feed user-supplied unique
|
||||
* bytes into hashUnique, not the raw buffer. A size==0 arm
|
||||
* must not read the uninitialized buffer pointer. */
|
||||
case TPM_ALG_MLDSA:
|
||||
case TPM_ALG_HASH_MLDSA:
|
||||
if (inPublic->publicArea.unique.mldsa.size > 0) {
|
||||
uBuf = inPublic->publicArea.unique.mldsa.buffer;
|
||||
uSz = (int)inPublic->publicArea.unique.mldsa.size;
|
||||
}
|
||||
break;
|
||||
case TPM_ALG_MLKEM:
|
||||
if (inPublic->publicArea.unique.mlkem.size > 0) {
|
||||
uBuf = inPublic->publicArea.unique.mlkem.buffer;
|
||||
uSz = (int)inPublic->publicArea.unique.mlkem.size;
|
||||
}
|
||||
break;
|
||||
#endif /* WOLFTPM_V185 */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -13022,6 +13045,15 @@ static TPM_RC FwCmd_SignSequenceStart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
}
|
||||
|
||||
/* Skip auth area when the client used ST_SESSIONS. SignSequenceStart
|
||||
* has no mandatory auth (Table 89 Auth Index: None) but clients may
|
||||
* still emit a password session. Without skipping, the 4-byte
|
||||
* authAreaSize prefix gets mis-parsed as the TPM2B_AUTH and
|
||||
* TPM2B_SIGNATURE_CTX size fields. */
|
||||
if (rc == 0 && cmdTag == TPM_ST_SESSIONS) {
|
||||
rc = FwSkipAuthArea(cmd, cmdSize);
|
||||
}
|
||||
|
||||
/* Parse auth (TPM2B_AUTH) */
|
||||
if (rc == 0) {
|
||||
TPM2_Packet_ParseU16(cmd, &authSz);
|
||||
|
|
@ -13067,8 +13099,11 @@ static TPM_RC FwCmd_SignSequenceStart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
|
||||
if (rc == 0) {
|
||||
paramStart = FwRspParamsBegin(rsp, cmdTag, ¶mSzPos);
|
||||
/* sequenceHandle is an output handle per Table 89 — must be
|
||||
* emitted BEFORE the parameterSize field, not inside the
|
||||
* parameter area. */
|
||||
TPM2_Packet_AppendU32(rsp, seqHandle);
|
||||
paramStart = FwRspParamsBegin(rsp, cmdTag, ¶mSzPos);
|
||||
FwRspParamsEnd(rsp, cmdTag, paramSzPos, paramStart);
|
||||
}
|
||||
else if (seq != NULL) {
|
||||
|
|
@ -13110,6 +13145,13 @@ static TPM_RC FwCmd_VerifySequenceStart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
}
|
||||
|
||||
/* Skip auth area when tag is ST_SESSIONS — Table 87 Auth Index: None,
|
||||
* but clients may still emit a password session that otherwise
|
||||
* desynchronises the auth / hint / context TPM2B parse. */
|
||||
if (rc == 0 && cmdTag == TPM_ST_SESSIONS) {
|
||||
rc = FwSkipAuthArea(cmd, cmdSize);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
TPM2_Packet_ParseU16(cmd, &authSz);
|
||||
if (authSz > sizeof(((TPM2B_AUTH*)0)->buffer)) {
|
||||
|
|
@ -13158,8 +13200,10 @@ static TPM_RC FwCmd_VerifySequenceStart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
|
||||
if (rc == 0) {
|
||||
paramStart = FwRspParamsBegin(rsp, cmdTag, ¶mSzPos);
|
||||
/* sequenceHandle is an output handle per Table 87 — emitted
|
||||
* before parameterSize. */
|
||||
TPM2_Packet_AppendU32(rsp, seqHandle);
|
||||
paramStart = FwRspParamsBegin(rsp, cmdTag, ¶mSzPos);
|
||||
FwRspParamsEnd(rsp, cmdTag, paramSzPos, paramStart);
|
||||
}
|
||||
else if (seq != NULL) {
|
||||
|
|
|
|||
40
src/tpm2.c
40
src/tpm2.c
|
|
@ -3414,7 +3414,10 @@ TPM_RC TPM2_SignSequenceComplete(SignSequenceComplete_In* in,
|
|||
TPM2_Packet packet;
|
||||
CmdInfo_t info = {0,0,0,0};
|
||||
info.inHandleCnt = 2;
|
||||
info.flags = (CMD_FLAG_ENC2 | CMD_FLAG_AUTH_USER1);
|
||||
/* Part 3 §20.6 Table 124: both @sequenceHandle and @keyHandle
|
||||
* require USER authorization. */
|
||||
info.flags = (CMD_FLAG_ENC2 | CMD_FLAG_AUTH_USER1 |
|
||||
CMD_FLAG_AUTH_USER2);
|
||||
|
||||
TPM2_Packet_Init(ctx, &packet);
|
||||
|
||||
|
|
@ -3457,7 +3460,10 @@ TPM_RC TPM2_VerifySequenceComplete(VerifySequenceComplete_In* in,
|
|||
TPM2_Packet packet;
|
||||
CmdInfo_t info = {0,0,0,0};
|
||||
info.inHandleCnt = 2;
|
||||
info.flags = (CMD_FLAG_ENC2);
|
||||
/* Part 3 §20.3 Table 118: @sequenceHandle requires USER auth;
|
||||
* keyHandle has no auth. The framework needs the USER1 flag so
|
||||
* the auth area matches what the server parses under ST_SESSIONS. */
|
||||
info.flags = (CMD_FLAG_ENC2 | CMD_FLAG_AUTH_USER1);
|
||||
|
||||
TPM2_Packet_Init(ctx, &packet);
|
||||
|
||||
|
|
@ -3466,9 +3472,8 @@ TPM_RC TPM2_VerifySequenceComplete(VerifySequenceComplete_In* in,
|
|||
|
||||
st = TPM2_Packet_AppendAuth(&packet, ctx, &info);
|
||||
|
||||
TPM2_Packet_AppendU16(&packet, in->buffer.size);
|
||||
TPM2_Packet_AppendBytes(&packet, in->buffer.buffer, in->buffer.size);
|
||||
|
||||
/* Part 3 §20.3 Table 118: parameters are {signature} only — no
|
||||
* buffer field. Message was accumulated via SequenceUpdate. */
|
||||
TPM2_Packet_AppendSignature(&packet, &in->signature);
|
||||
|
||||
TPM2_Packet_Finalize(&packet, st, TPM_CC_VerifySequenceComplete);
|
||||
|
|
@ -7326,6 +7331,31 @@ void TPM2_PrintPublicArea(const TPM2B_PUBLIC* pub)
|
|||
TPM2_PrintBin(pub->publicArea.unique.ecc.y.buffer, pub->publicArea.unique.ecc.y.size);
|
||||
#endif
|
||||
break;
|
||||
#ifdef WOLFTPM_V185
|
||||
case TPM_ALG_MLDSA:
|
||||
case TPM_ALG_HASH_MLDSA:
|
||||
printf(" %s: parameterSet 0x%X, unique size %d\n",
|
||||
(pub->publicArea.type == TPM_ALG_MLDSA)
|
||||
? "ML-DSA" : "Hash-ML-DSA",
|
||||
(pub->publicArea.type == TPM_ALG_MLDSA)
|
||||
? pub->publicArea.parameters.mldsaDetail.parameterSet
|
||||
: pub->publicArea.parameters.hash_mldsaDetail.parameterSet,
|
||||
pub->publicArea.unique.mldsa.size);
|
||||
#ifdef WOLFTPM_DEBUG_VERBOSE
|
||||
TPM2_PrintBin(pub->publicArea.unique.mldsa.buffer,
|
||||
pub->publicArea.unique.mldsa.size);
|
||||
#endif
|
||||
break;
|
||||
case TPM_ALG_MLKEM:
|
||||
printf(" ML-KEM: parameterSet 0x%X, unique size %d\n",
|
||||
pub->publicArea.parameters.mlkemDetail.parameterSet,
|
||||
pub->publicArea.unique.mlkem.size);
|
||||
#ifdef WOLFTPM_DEBUG_VERBOSE
|
||||
TPM2_PrintBin(pub->publicArea.unique.mlkem.buffer,
|
||||
pub->publicArea.unique.mlkem.size);
|
||||
#endif
|
||||
break;
|
||||
#endif /* WOLFTPM_V185 */
|
||||
default:
|
||||
/* derive does not seem to have specific fields in the parameters struct */
|
||||
printf("Derive Type: unique label size %d, context size %d\n",
|
||||
|
|
|
|||
108
src/tpm2_wrap.c
108
src/tpm2_wrap.c
|
|
@ -8482,6 +8482,31 @@ static int GetKeyTemplateSize(TPMT_PUBLIC* publicTemplate)
|
|||
case TPM_ALG_SYMCIPHER:
|
||||
ret = publicTemplate->parameters.symDetail.sym.keyBits.sym / 8;
|
||||
break;
|
||||
#ifdef WOLFTPM_V185
|
||||
case TPM_ALG_MLDSA:
|
||||
case TPM_ALG_HASH_MLDSA: {
|
||||
TPMI_MLDSA_PARAMETER_SET ps =
|
||||
(publicTemplate->type == TPM_ALG_MLDSA)
|
||||
? publicTemplate->parameters.mldsaDetail.parameterSet
|
||||
: publicTemplate->parameters.hash_mldsaDetail.parameterSet;
|
||||
/* Per Part 2 Table 207 MLDSA public-key sizes. */
|
||||
if (ps == TPM_MLDSA_44) ret = 1312;
|
||||
else if (ps == TPM_MLDSA_65) ret = 1952;
|
||||
else if (ps == TPM_MLDSA_87) ret = 2592;
|
||||
else ret = BAD_FUNC_ARG;
|
||||
break;
|
||||
}
|
||||
case TPM_ALG_MLKEM: {
|
||||
TPMI_MLKEM_PARAMETER_SET ps =
|
||||
publicTemplate->parameters.mlkemDetail.parameterSet;
|
||||
/* Per Part 2 Table 204 MLKEM public-key sizes. */
|
||||
if (ps == TPM_MLKEM_512) ret = 800;
|
||||
else if (ps == TPM_MLKEM_768) ret = 1184;
|
||||
else if (ps == TPM_MLKEM_1024) ret = 1568;
|
||||
else ret = BAD_FUNC_ARG;
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFTPM_V185 */
|
||||
case TPM_ALG_KEYEDHASH:
|
||||
default:
|
||||
ret = BAD_FUNC_ARG;
|
||||
|
|
@ -8565,6 +8590,49 @@ int wolfTPM2_SetKeyTemplate_Unique(TPMT_PUBLIC* publicTemplate,
|
|||
}
|
||||
publicTemplate->unique.sym.size = uniqueSz;
|
||||
break;
|
||||
#ifdef WOLFTPM_V185
|
||||
/* TPMU_PUBLIC_ID shares the mldsa arm between MLDSA and HASH_MLDSA
|
||||
* (Part 2 Table 225 note — one union field, two selectors). */
|
||||
case TPM_ALG_MLDSA:
|
||||
case TPM_ALG_HASH_MLDSA:
|
||||
if (uniqueSz == 0) {
|
||||
uniqueSz = keySz;
|
||||
}
|
||||
else if (uniqueSz > keySz) {
|
||||
uniqueSz = keySz;
|
||||
}
|
||||
if (uniqueSz > (int)sizeof(publicTemplate->unique.mldsa.buffer)) {
|
||||
uniqueSz =
|
||||
(int)sizeof(publicTemplate->unique.mldsa.buffer); /* truncate */
|
||||
}
|
||||
if (unique == NULL) {
|
||||
XMEMSET(publicTemplate->unique.mldsa.buffer, 0, uniqueSz);
|
||||
}
|
||||
else {
|
||||
XMEMCPY(publicTemplate->unique.mldsa.buffer, unique, uniqueSz);
|
||||
}
|
||||
publicTemplate->unique.mldsa.size = uniqueSz;
|
||||
break;
|
||||
case TPM_ALG_MLKEM:
|
||||
if (uniqueSz == 0) {
|
||||
uniqueSz = keySz;
|
||||
}
|
||||
else if (uniqueSz > keySz) {
|
||||
uniqueSz = keySz;
|
||||
}
|
||||
if (uniqueSz > (int)sizeof(publicTemplate->unique.mlkem.buffer)) {
|
||||
uniqueSz =
|
||||
(int)sizeof(publicTemplate->unique.mlkem.buffer); /* truncate */
|
||||
}
|
||||
if (unique == NULL) {
|
||||
XMEMSET(publicTemplate->unique.mlkem.buffer, 0, uniqueSz);
|
||||
}
|
||||
else {
|
||||
XMEMCPY(publicTemplate->unique.mlkem.buffer, unique, uniqueSz);
|
||||
}
|
||||
publicTemplate->unique.mlkem.size = uniqueSz;
|
||||
break;
|
||||
#endif /* WOLFTPM_V185 */
|
||||
case TPM_ALG_KEYEDHASH:
|
||||
/* not supported */
|
||||
ret = BAD_FUNC_ARG;
|
||||
|
|
@ -8946,6 +9014,46 @@ static void wolfTPM2_CopyPubT(TPMT_PUBLIC* out, const TPMT_PUBLIC* in)
|
|||
wolfTPM2_CopyEccParam(&out->unique.ecc.y,
|
||||
&in->unique.ecc.y);
|
||||
break;
|
||||
#ifdef WOLFTPM_V185
|
||||
case TPM_ALG_MLDSA:
|
||||
out->parameters.mldsaDetail.parameterSet =
|
||||
in->parameters.mldsaDetail.parameterSet;
|
||||
out->parameters.mldsaDetail.allowExternalMu =
|
||||
in->parameters.mldsaDetail.allowExternalMu;
|
||||
out->unique.mldsa.size = in->unique.mldsa.size;
|
||||
if (out->unique.mldsa.size > (UINT16)sizeof(out->unique.mldsa.buffer)) {
|
||||
out->unique.mldsa.size = (UINT16)sizeof(out->unique.mldsa.buffer);
|
||||
}
|
||||
XMEMCPY(out->unique.mldsa.buffer, in->unique.mldsa.buffer,
|
||||
out->unique.mldsa.size);
|
||||
break;
|
||||
case TPM_ALG_HASH_MLDSA:
|
||||
out->parameters.hash_mldsaDetail.parameterSet =
|
||||
in->parameters.hash_mldsaDetail.parameterSet;
|
||||
out->parameters.hash_mldsaDetail.hashAlg =
|
||||
in->parameters.hash_mldsaDetail.hashAlg;
|
||||
/* TPMU_PUBLIC_ID shares the mldsa arm between MLDSA and HASH_MLDSA
|
||||
* (Part 2 Table 225 note — one union field, two selectors). */
|
||||
out->unique.mldsa.size = in->unique.mldsa.size;
|
||||
if (out->unique.mldsa.size > (UINT16)sizeof(out->unique.mldsa.buffer)) {
|
||||
out->unique.mldsa.size = (UINT16)sizeof(out->unique.mldsa.buffer);
|
||||
}
|
||||
XMEMCPY(out->unique.mldsa.buffer, in->unique.mldsa.buffer,
|
||||
out->unique.mldsa.size);
|
||||
break;
|
||||
case TPM_ALG_MLKEM:
|
||||
wolfTPM2_CopySymmetric(&out->parameters.mlkemDetail.symmetric,
|
||||
&in->parameters.mlkemDetail.symmetric);
|
||||
out->parameters.mlkemDetail.parameterSet =
|
||||
in->parameters.mlkemDetail.parameterSet;
|
||||
out->unique.mlkem.size = in->unique.mlkem.size;
|
||||
if (out->unique.mlkem.size > (UINT16)sizeof(out->unique.mlkem.buffer)) {
|
||||
out->unique.mlkem.size = (UINT16)sizeof(out->unique.mlkem.buffer);
|
||||
}
|
||||
XMEMCPY(out->unique.mlkem.buffer, in->unique.mlkem.buffer,
|
||||
out->unique.mlkem.size);
|
||||
break;
|
||||
#endif /* WOLFTPM_V185 */
|
||||
default:
|
||||
wolfTPM2_CopySymmetric(&out->parameters.asymDetail.symmetric,
|
||||
&in->parameters.asymDetail.symmetric);
|
||||
|
|
|
|||
Loading…
Reference in New Issue