mirror of https://github.com/wolfSSL/wolfTPM.git
Add MLKEM arm to wolfTPM2_EncryptSecret + TPMU_ENCRYPTED_SECRET
Per TCG TPM 2.0 Library v1.85 Part 1 §24 (p.316) and Part 2 Table 222,
v1.85 adds ML-KEM as a valid key-exchange type for encryptedSalt
(TPM2_StartAuthSession), inSymSeed (Duplicate/Import/Rewrap), and
credentialBlob (ActivateCredential/MakeCredential). The caller
encapsulates under the TPM's ML-KEM public key: the 32-byte shared
secret becomes the session salt, the ciphertext goes on the wire.
Changes:
- wolftpm/tpm2.h: add mlkem[MAX_MLKEM_CT_SIZE] arm to
TPMU_ENCRYPTED_SECRET union (gated on WOLFTPM_V185). Without this the
union sized at MAX_RSA_KEY_BYTES (512) could not hold an ML-KEM-768
ciphertext (1088 bytes) let alone ML-KEM-1024 (1568 bytes).
- src/tpm2_wrap.c: new static wolfTPM2_EncryptSecret_MLKEM helper that
inits an MlKemKey from tpmKey->pub.unique.mlkem, calls
wc_MlKemKey_Encapsulate, writes shared secret to data->buffer and
ciphertext to secret->secret. Dispatch switch in wolfTPM2_EncryptSecret
gains a TPM_ALG_MLKEM case. ML-DSA intentionally omitted — signing
keys have no encrypt operation (spec Table 222 has no mldsa arm).
- tests/unit_tests.c: test_wolfTPM2_EncryptSecret now creates a real
MLKEM-768 primary and asserts data.size == 32 and secret.size == 1088.
pull/445/head
parent
c2b631ebf5
commit
e86b8971df
|
|
@ -2282,6 +2282,77 @@ static int wolfTPM2_EncryptSecret_RSA(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpm
|
|||
}
|
||||
#endif /* !WOLFTPM2_NO_WOLFCRYPT && !NO_RSA && !WC_NO_RNG */
|
||||
|
||||
#if defined(WOLFTPM_V185) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
||||
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
||||
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
||||
/* ML-KEM session-salt path per TCG TPM 2.0 Library v1.85 Part 1 §24
|
||||
* (p.316): caller encapsulates under the TPM's ML-KEM public key; the
|
||||
* ML-KEM shared secret becomes the session salt; the ML-KEM ciphertext
|
||||
* is carried on the wire as encryptedSalt.bytes. The TPM decapsulates
|
||||
* internally to recover the same shared secret. Unlike RSA-OAEP /
|
||||
* ECDH, ML-KEM generates the shared secret as part of encapsulation —
|
||||
* the caller does not supply their own salt.
|
||||
*/
|
||||
static int wolfTPM2_EncryptSecret_MLKEM(const WOLFTPM2_KEY* tpmKey,
|
||||
TPM2B_DATA* data, TPM2B_ENCRYPTED_SECRET* secret)
|
||||
{
|
||||
int rc;
|
||||
int wcType;
|
||||
WC_RNG rng;
|
||||
MlKemKey mlkemKey;
|
||||
const TPMS_MLKEM_PARMS* parms;
|
||||
const TPM2B_PUBLIC_KEY_MLKEM* pubIn;
|
||||
word32 ctSz = 0, ssSz = 0;
|
||||
|
||||
parms = &tpmKey->pub.publicArea.parameters.mlkemDetail;
|
||||
pubIn = &tpmKey->pub.publicArea.unique.mlkem;
|
||||
|
||||
switch (parms->parameterSet) {
|
||||
case TPM_MLKEM_512: wcType = WC_ML_KEM_512; break;
|
||||
case TPM_MLKEM_768: wcType = WC_ML_KEM_768; break;
|
||||
case TPM_MLKEM_1024: wcType = WC_ML_KEM_1024; break;
|
||||
default: return TPM_RC_VALUE;
|
||||
}
|
||||
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
XMEMSET(&mlkemKey, 0, sizeof(mlkemKey));
|
||||
|
||||
rc = wc_InitRng_ex(&rng, NULL, INVALID_DEVID);
|
||||
if (rc == 0) {
|
||||
rc = wc_MlKemKey_Init(&mlkemKey, wcType, NULL, INVALID_DEVID);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_MlKemKey_DecodePublicKey(&mlkemKey, pubIn->buffer,
|
||||
pubIn->size);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_MlKemKey_CipherTextSize(&mlkemKey, &ctSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_MlKemKey_SharedSecretSize(&mlkemKey, &ssSz);
|
||||
}
|
||||
if (rc == 0 &&
|
||||
(ctSz > sizeof(secret->secret) || ssSz > sizeof(data->buffer))) {
|
||||
rc = BUFFER_E;
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_MlKemKey_Encapsulate(&mlkemKey, secret->secret,
|
||||
data->buffer, &rng);
|
||||
}
|
||||
if (rc == 0) {
|
||||
secret->size = (UINT16)ctSz;
|
||||
data->size = (UINT16)ssSz;
|
||||
}
|
||||
|
||||
wc_MlKemKey_Free(&mlkemKey);
|
||||
TPM2_ForceZero(&mlkemKey, sizeof(mlkemKey));
|
||||
wc_FreeRng(&rng);
|
||||
TPM2_ForceZero(&rng, sizeof(rng));
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* WOLFTPM_V185 && ML-KEM enabled */
|
||||
|
||||
int wolfTPM2_EncryptSecret(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpmKey,
|
||||
TPM2B_DATA *data, TPM2B_ENCRYPTED_SECRET *secret,
|
||||
const char* label)
|
||||
|
|
@ -2313,6 +2384,14 @@ int wolfTPM2_EncryptSecret(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpmKey,
|
|||
case TPM_ALG_RSA:
|
||||
rc = wolfTPM2_EncryptSecret_RSA(dev, tpmKey, data, secret, label);
|
||||
break;
|
||||
#endif
|
||||
#if defined(WOLFTPM_V185) && \
|
||||
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
||||
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
||||
case TPM_ALG_MLKEM:
|
||||
rc = wolfTPM2_EncryptSecret_MLKEM(tpmKey, data, secret);
|
||||
(void)label; /* ML-KEM.Encaps does not take a KDF label */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rc = NOT_COMPILED_IN;
|
||||
|
|
|
|||
|
|
@ -816,6 +816,12 @@ static void test_wolfTPM2_EncryptSecret(void)
|
|||
WOLFTPM2_KEY tpmKey;
|
||||
TPM2B_DATA data;
|
||||
TPM2B_ENCRYPTED_SECRET secret;
|
||||
#if defined(WOLFTPM_V185) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
||||
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
||||
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
||||
WOLFTPM2_KEY mlkemKey;
|
||||
TPMT_PUBLIC mlkemPub;
|
||||
#endif
|
||||
|
||||
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
||||
XMEMSET(&data, 0, sizeof(data));
|
||||
|
|
@ -840,10 +846,48 @@ static void test_wolfTPM2_EncryptSecret(void)
|
|||
rc = wolfTPM2_EncryptSecret(&dev, &tpmKey, &data, NULL, "SECRET");
|
||||
AssertIntEQ(rc, BAD_FUNC_ARG);
|
||||
|
||||
#if defined(WOLFTPM_V185) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
||||
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
||||
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
||||
/* MLKEM path (v1.85 Part 1 §24): caller encapsulates under the TPM's
|
||||
* ML-KEM public key; the shared secret (32 bytes) becomes the session
|
||||
* salt, the ciphertext (1088 bytes for MLKEM-768) goes on the wire. */
|
||||
XMEMSET(&mlkemKey, 0, sizeof(mlkemKey));
|
||||
XMEMSET(&mlkemPub, 0, sizeof(mlkemPub));
|
||||
XMEMSET(&data, 0, sizeof(data));
|
||||
XMEMSET(&secret, 0, sizeof(secret));
|
||||
|
||||
rc = wolfTPM2_GetKeyTemplate_MLKEM(&mlkemPub,
|
||||
TPMA_OBJECT_decrypt | TPMA_OBJECT_fixedTPM |
|
||||
TPMA_OBJECT_fixedParent | TPMA_OBJECT_sensitiveDataOrigin |
|
||||
TPMA_OBJECT_userWithAuth, TPM_MLKEM_768);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
rc = wolfTPM2_CreatePrimaryKey(&dev, &mlkemKey, TPM_RH_OWNER,
|
||||
&mlkemPub, NULL, 0);
|
||||
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
||||
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
||||
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
||||
"EncryptSecret MLKEM:");
|
||||
}
|
||||
else {
|
||||
AssertIntEQ(rc, 0);
|
||||
|
||||
rc = wolfTPM2_EncryptSecret(&dev, &mlkemKey, &data, &secret,
|
||||
"SECRET");
|
||||
AssertIntEQ(rc, 0);
|
||||
AssertIntEQ(data.size, 32); /* MLKEM shared secret */
|
||||
AssertIntEQ(secret.size, 1088); /* MLKEM-768 ciphertext */
|
||||
printf("Test TPM Wrapper: %-40s Passed\n",
|
||||
"EncryptSecret MLKEM:");
|
||||
|
||||
wolfTPM2_UnloadHandle(&dev, &mlkemKey.handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
|
||||
printf("Test TPM Wrapper: %-40s %s\n", "EncryptSecret:",
|
||||
rc == BAD_FUNC_ARG ? "Passed" : "Failed");
|
||||
rc == 0 || rc == BAD_FUNC_ARG ? "Passed" : "Failed");
|
||||
}
|
||||
|
||||
static void test_wolfTPM2_Cleanup(void)
|
||||
|
|
|
|||
|
|
@ -1647,6 +1647,9 @@ typedef union TPMU_ENCRYPTED_SECRET {
|
|||
BYTE rsa[MAX_RSA_KEY_BYTES]; /* TPM_ALG_RSA */
|
||||
BYTE symmetric[sizeof(TPM2B_DIGEST)]; /* TPM_ALG_SYMCIPHER */
|
||||
BYTE keyedHash[sizeof(TPM2B_DIGEST)]; /* TPM_ALG_KEYEDHASH */
|
||||
#ifdef WOLFTPM_V185
|
||||
BYTE mlkem[MAX_MLKEM_CT_SIZE]; /* TPM_ALG_MLKEM (v1.85 T222) */
|
||||
#endif
|
||||
} TPMU_ENCRYPTED_SECRET;
|
||||
|
||||
typedef struct TPM2B_ENCRYPTED_SECRET {
|
||||
|
|
|
|||
Loading…
Reference in New Issue