Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/fwtpm-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ jobs:
build_only: true
extra_cflags: -DDEBUG_WOLFTPM

# v1.85 PQC without HKDF, so ECC DHKEM is unavailable and
# Encapsulate/Decapsulate must reject an ECC key while the ML-KEM
# path still works. TLS 1.3 turns HKDF on regardless of
# --disable-hkdf, so it is disabled too.
- name: fwtpm-v185-no-hkdf
os: ubuntu-latest
wolftpm_config: --enable-fwtpm --enable-v185
wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --enable-dilithium --enable-mlkem --enable-experimental --enable-harden --disable-tls13 --disable-hkdf
build_only: false
Comment thread
dgarske marked this conversation as resolved.
Resolved

# Build-only: fwTPM with RSA disabled
- name: fwtpm-no-rsa
os: ubuntu-latest
Expand Down
16 changes: 11 additions & 5 deletions examples/wrap/wrap_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,17 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[])
cipher.size = sizeof(cipher.buffer); /* signature */
rc = wolfTPM2_SignHashScheme(&dev, &rsaKey, message.buffer, message.size,
cipher.buffer, &cipher.size, TPM_ALG_RSAPSS, TPM_ALG_SHA256);
if (rc != 0) goto exit;
rc = wolfTPM2_VerifyHashScheme(&dev, &rsaKey, cipher.buffer, cipher.size,
message.buffer, message.size, TPM_ALG_RSAPSS, TPM_ALG_SHA256);
if (rc != 0) goto exit;
printf("RSA Sign/Verify using RSA PSS padding\n");
if ((rc & RC_MAX_FMT1) == TPM_RC_SCHEME) {
printf("RSA PSS padding not supported by this TPM\n");
}
else {
if (rc != 0) goto exit;
rc = wolfTPM2_VerifyHashScheme(&dev, &rsaKey, cipher.buffer,
cipher.size, message.buffer, message.size, TPM_ALG_RSAPSS,
TPM_ALG_SHA256);
if (rc != 0) goto exit;
printf("RSA Sign/Verify using RSA PSS padding\n");
}

rc = wolfTPM2_UnloadHandle(&dev, &rsaKey.handle);
if (rc != 0) goto exit;
Expand Down
15 changes: 12 additions & 3 deletions src/fwtpm/fwtpm_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -1855,9 +1855,11 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
#ifndef NO_RSA
{ TPM_ALG_RSASSA, TPMA_ALGORITHM_asymmetric |
TPMA_ALGORITHM_signing },
#ifdef WC_RSA_PSS
{ TPM_ALG_RSAPSS, TPMA_ALGORITHM_asymmetric |
TPMA_ALGORITHM_signing },
#endif
#endif
#ifdef HAVE_ECC
{ TPM_ALG_ECDSA, TPMA_ALGORITHM_asymmetric |
TPMA_ALGORITHM_signing },
Expand Down Expand Up @@ -2450,7 +2452,10 @@ static TPM_RC FwTestRsaScheme(const TPMT_RSA_SCHEME* scheme)
if (alg == TPM_ALG_NULL || alg == TPM_ALG_RSAES) {
rc = TPM_RC_SUCCESS;
}
else if (alg == TPM_ALG_RSASSA || alg == TPM_ALG_RSAPSS ||
else if (alg == TPM_ALG_RSASSA ||
#ifdef WC_RSA_PSS
alg == TPM_ALG_RSAPSS ||
#endif
alg == TPM_ALG_OAEP) {
rc = FwTestHashAlg(scheme->details.anySig.hashAlg);
}
Expand Down Expand Up @@ -18138,7 +18143,7 @@ static TPM_RC FwCmd_Encapsulate(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
rc = FwEncapsulateMlkem(&ctx->rng, ps, &obj->pub.unique.mlkem,
&sharedSecret, ciphertext);
}
#ifdef HAVE_ECC
#ifdef FWTPM_HAVE_DHKEM
else if (obj->pub.type == TPM_ALG_ECC &&
obj->pub.parameters.eccDetail.kdf.scheme == TPM_ALG_HKDF) {
rc = FwEncapsulateEcdhDhkem(&ctx->rng, &obj->pub,
Expand Down Expand Up @@ -18258,12 +18263,16 @@ static TPM_RC FwCmd_Decapsulate(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
rc = FwDecapsulateMlkem(ps, obj->privKey,
ciphertext->buffer, ciphertext->size, &sharedSecret);
}
#ifdef HAVE_ECC
#ifdef FWTPM_HAVE_DHKEM
else {
rc = FwDecapsulateEcdhDhkem(&ctx->rng, obj,
obj->pub.parameters.eccDetail.kdf.details.any.hashAlg,
ciphertext->buffer, ciphertext->size, &sharedSecret);
}
#else
else {
rc = TPM_RC_KEY;
}
#endif
}

Expand Down
16 changes: 12 additions & 4 deletions src/fwtpm/fwtpm_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ TPM_RC FwDecapsulateMlkem(TPMI_MLKEM_PARAMETER_SET parameterSet,
return rc;
}

#ifdef HAVE_ECC
#ifdef FWTPM_HAVE_DHKEM
/* RFC 9180 Sec.7 kem_id mapping for the curve+hash pairings the TPM accepts.
* Returns 0 on a supported pairing; -1 otherwise (caller maps to TPM_RC_KDF). */
static int FwDhkemParamsLookup(int wcCurve, TPMI_ALG_HASH kdfHash,
Expand Down Expand Up @@ -1519,7 +1519,7 @@ TPM_RC FwDecapsulateEcdhDhkem(WC_RNG* rng, const FWTPM_Object* recipObj,
FWTPM_FREE_VAR(ephKey);
return rc;
}
#endif /* HAVE_ECC */
#endif /* FWTPM_HAVE_DHKEM */

#endif /* WOLFTPM_MLKEM */

Expand Down Expand Up @@ -3770,7 +3770,9 @@ int FwGetRsaPadding(UINT16 scheme)
{
switch (scheme) {
case TPM_ALG_RSASSA: return WC_RSA_PKCSV15_PAD;
#ifdef WC_RSA_PSS
case TPM_ALG_RSAPSS: return WC_RSA_PSS_PAD;
#endif
default: return -1;
}
}
Expand Down Expand Up @@ -3897,13 +3899,16 @@ TPM_RC FwSignDigestAndAppend(FWTPM_CTX* ctx, FWTPM_Object* obj,
}

if (rc == 0) {
#ifdef WC_RSA_PSS
if (pad == WC_RSA_PSS_PAD) {
int mgf = FwGetMgfType(sigHashAlg);
wcRc = wc_RsaPSS_Sign_ex(digest, digestSz,
sigBuf, sigSz, (enum wc_HashType)wcHashType, mgf,
RSA_PSS_SALT_LEN_DEFAULT, rsaKey, &ctx->rng);
}
else {
else
#endif
{
/* RSASSA PKCS#1 v1.5: wrap in ASN.1 DigestInfo */
byte encHash[WOLFTPM_MAX_DER_DIGEST_SZ];
int encSz;
Expand Down Expand Up @@ -4043,6 +4048,7 @@ TPM_RC FwVerifySignatureCore(FWTPM_Object* obj,
}

if (rc == 0) {
#ifdef WC_RSA_PSS
if (pad == WC_RSA_PSS_PAD) {
FWTPM_DECLARE_BUF(decSig, FWTPM_MAX_PUB_BUF);
int wcHashType = FwGetRsaHashOid(
Expand All @@ -4061,7 +4067,9 @@ TPM_RC FwVerifySignatureCore(FWTPM_Object* obj,
rsaKey);
FWTPM_FREE_BUF(decSig);
}
else {
else
#endif
{
FWTPM_DECLARE_BUF(decSig, FWTPM_MAX_PUB_BUF);
byte expDI[WOLFTPM_MAX_DER_DIGEST_SZ];
int wcHash = FwGetRsaHashOid(
Expand Down
110 changes: 110 additions & 0 deletions tests/fwtpm_unit_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -3553,6 +3553,8 @@ static void test_fwtpm_mlkem_roundtrip(void)
fwtpm_pass("MLKEM Encap/Decap Roundtrip:", 1);
}

#if defined(FWTPM_HAVE_DHKEM) && defined(WOLFTPM_MLKEM_ENCAP) && \
defined(WOLFTPM_MLKEM_DECAP)
/* ECC DHKEM Encap/Decap roundtrip per Part 2 Sec.12.2.3.5 + RFC 9180 Sec.4.1.
* An unrestricted-decryption ECC key with the matching HKDF kdf qualifies for
* Encap/Decap. Encap and Decap MUST produce identical sharedSecrets. */
Expand Down Expand Up @@ -3680,6 +3682,104 @@ static void test_fwtpm_ecc_dhkem_p521_roundtrip(void)
"Encap/Decap ECC DHKEM (P-521/HKDF-SHA512) Roundtrip:");
}
#endif
#endif /* FWTPM_HAVE_DHKEM && WOLFTPM_MLKEM_ENCAP && WOLFTPM_MLKEM_DECAP */

#if !defined(FWTPM_HAVE_DHKEM) && defined(HAVE_ECC) && \
defined(WOLFTPM_MLKEM_ENCAP) && defined(WOLFTPM_MLKEM_DECAP)
/* Without HKDF there is no ECC DHKEM, so an ECC key is not a usable KEM key
* even though it still passes the Part 2 Sec.10.3.13 template checks that
* admit it. Encap and Decap MUST report TPM_RC_KEY rather than dispatch into
* the absent DHKEM path. ML-KEM remains the one supported KEM type here and
* is covered by test_fwtpm_mlkem_roundtrip. */
static void test_fwtpm_ecc_dhkem_unsupported(void)
{
FWTPM_CTX ctx;
int rc, rspSize;
int pos, pubAreaStart, pubAreaLen;
UINT32 keyHandle;
byte ct[32];

memset(&ctx, 0, sizeof(ctx));
XMEMSET(ct, 0, sizeof(ct));
AssertIntEQ(fwtpm_test_startup(&ctx), 0);

/* Same key the DHKEM roundtrip uses: ECC unrestricted decrypt with an
* HKDF kdf. FwTestKdfScheme accepts HKDF whether or not wolfCrypt has
* it, so creation still succeeds in this build. */
pos = 0;
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
PutU32BE(gCmd + pos, 0); pos += 4;
PutU32BE(gCmd + pos, TPM_CC_CreatePrimary); pos += 4;
PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4;
PutU32BE(gCmd + pos, 9); pos += 4;
PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4;
PutU16BE(gCmd + pos, 0); pos += 2;
gCmd[pos++] = 0;
PutU16BE(gCmd + pos, 0); pos += 2;
PutU16BE(gCmd + pos, 4); pos += 2;
PutU16BE(gCmd + pos, 0); pos += 2;
PutU16BE(gCmd + pos, 0); pos += 2;
pubAreaStart = pos;
PutU16BE(gCmd + pos, 0); pos += 2;
PutU16BE(gCmd + pos, TPM_ALG_ECC); pos += 2;
PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2;
/* fixedTPM|fixedParent|sensitiveDataOrigin|userWithAuth|decrypt */
PutU32BE(gCmd + pos, 0x00020072); pos += 4;
PutU16BE(gCmd + pos, 0); pos += 2;
PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* sym */
PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* scheme */
PutU16BE(gCmd + pos, TPM_ECC_NIST_P256); pos += 2;
PutU16BE(gCmd + pos, TPM_ALG_HKDF); pos += 2; /* kdf scheme */
PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; /* kdf hash */
PutU16BE(gCmd + pos, 0); pos += 2; /* unique.x */
PutU16BE(gCmd + pos, 0); pos += 2; /* unique.y */
pubAreaLen = pos - pubAreaStart - 2;
PutU16BE(gCmd + pubAreaStart, (UINT16)pubAreaLen);
PutU16BE(gCmd + pos, 0); pos += 2;
PutU32BE(gCmd + pos, 0); pos += 4;
PutU32BE(gCmd + 2, (UINT32)pos);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
keyHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE);
AssertIntNE(keyHandle, 0);

/* Encapsulate falls through to the unsupported-type branch */
pos = 0;
PutU16BE(gCmd + pos, TPM_ST_NO_SESSIONS); pos += 2;
PutU32BE(gCmd + pos, 0); pos += 4;
PutU32BE(gCmd + pos, TPM_CC_Encapsulate); pos += 4;
PutU32BE(gCmd + pos, keyHandle); pos += 4;
PutU32BE(gCmd + 2, (UINT32)pos);
rspSize = 0;
AssertIntEQ(FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0),
TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_KEY);

/* Decapsulate passes every template check and is rejected at dispatch */
pos = 0;
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
PutU32BE(gCmd + pos, 0); pos += 4;
PutU32BE(gCmd + pos, TPM_CC_Decapsulate); pos += 4;
PutU32BE(gCmd + pos, keyHandle); pos += 4;
PutU32BE(gCmd + pos, 9); pos += 4;
PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4;
PutU16BE(gCmd + pos, 0); pos += 2;
gCmd[pos++] = 0;
PutU16BE(gCmd + pos, 0); pos += 2;
PutU16BE(gCmd + pos, (UINT16)sizeof(ct)); pos += 2;
memcpy(gCmd + pos, ct, sizeof(ct)); pos += (int)sizeof(ct);
PutU32BE(gCmd + 2, (UINT32)pos);
rspSize = 0;
AssertIntEQ(FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0),
TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_KEY);

FWTPM_Cleanup(&ctx);
fwtpm_pass("Encap/Decap ECC DHKEM Unsupported (no HKDF):", 1);
}
#endif /* !FWTPM_HAVE_DHKEM && HAVE_ECC && MLKEM_ENCAP && MLKEM_DECAP */

/* ML-KEM Labeled KEM seed roundtrip per Part 1 Sec.47.4 Eq.66:
* seed = KDFa(nameAlg, K, label, ciphertext, publicKey, bits).
Expand Down Expand Up @@ -19477,7 +19577,14 @@ int fwtpm_unit_tests(int argc, char *argv[])
test_fwtpm_create_loaded_mldsa();
Comment thread
dgarske marked this conversation as resolved.
Resolved
test_fwtpm_create_loaded_mlkem();
test_fwtpm_mlkem_roundtrip();
#if defined(FWTPM_HAVE_DHKEM) && defined(WOLFTPM_MLKEM_ENCAP) && \
defined(WOLFTPM_MLKEM_DECAP)
test_fwtpm_ecc_dhkem_roundtrip();
#endif
#if !defined(FWTPM_HAVE_DHKEM) && defined(HAVE_ECC) && \
defined(WOLFTPM_MLKEM_ENCAP) && defined(WOLFTPM_MLKEM_DECAP)
test_fwtpm_ecc_dhkem_unsupported();
#endif
test_fwtpm_encseed_decseed_mlkem_roundtrip();
#if defined(WOLFTPM_MLKEM_ENCAP) && defined(WOLFTPM_MLKEM_DECAP) && \
!defined(FWTPM_NO_CREDENTIAL)
Expand Down Expand Up @@ -19521,10 +19628,13 @@ int fwtpm_unit_tests(int argc, char *argv[])
test_fwtpm_signsequence_hmac_roundtrip();
test_fwtpm_signsequence_handle_auth_required();
test_fwtpm_verifysequence_long_message();
#if defined(FWTPM_HAVE_DHKEM) && defined(WOLFTPM_MLKEM_ENCAP) && \
defined(WOLFTPM_MLKEM_DECAP)
test_fwtpm_ecc_dhkem_p384_roundtrip();
#ifdef FWTPM_HAVE_ECC521
test_fwtpm_ecc_dhkem_p521_roundtrip();
#endif
#endif
#ifdef WOLFTPM_HASH_MLDSA
test_fwtpm_mldsa_digest_roundtrip();
#endif /* WOLFTPM_HASH_MLDSA */
Expand Down
7 changes: 7 additions & 0 deletions wolftpm/fwtpm/fwtpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
#endif
#endif

/* v1.85 ECC DHKEM (RFC 9180) derives its shared secret with HKDF. Reachable
* only through TPM2_Encapsulate/Decapsulate, which are gated on ML-KEM, so
* the capability has to match that gate as well. */
#if defined(WOLFTPM_MLKEM) && defined(HAVE_ECC) && defined(HAVE_HKDF)
#define FWTPM_HAVE_DHKEM
#endif

/* Endian byte-array helpers - use shared TPM2_Packet helpers.
* Note: argument order differs (Fw: buf,val; TPM2_Packet: val,buf) */
#define FwStoreU16BE(buf, val) TPM2_Packet_U16ToByteArray((val), (buf))
Expand Down
Loading