Skip to content

Commit 0a00b47

Browse files
committed
Fix ML-KEM ARM64 NEON ciphertext comparison reduction
ZD#21457 (30)
1 parent 9c304bd commit 0a00b47

4 files changed

Lines changed: 73 additions & 7 deletions

File tree

tests/api/test_mlkem.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3950,3 +3950,67 @@ int test_wc_mlkem_decapsulate_pubonly_fails(void)
39503950
return EXPECT_RESULT();
39513951
} /* END test_wc_mlkem_decapsulate_pubonly_fails */
39523952

3953+
/* Verify that the FO re-encryption check catches ciphertext tampering
3954+
* at various byte offsets and falls back to implicit rejection. */
3955+
int test_wc_mlkem_decap_fo_reject(void)
3956+
{
3957+
EXPECT_DECLS;
3958+
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
3959+
#if defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_WC_MLKEM) && \
3960+
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
3961+
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
3962+
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
3963+
MlKemKey* key = NULL;
3964+
WC_RNG rng;
3965+
byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
3966+
byte ctTampered[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
3967+
byte ss[WC_ML_KEM_SS_SZ];
3968+
byte ssDec[WC_ML_KEM_SS_SZ];
3969+
byte ssTampered[WC_ML_KEM_SS_SZ];
3970+
word32 ctLen = 0;
3971+
3972+
key = (MlKemKey*)XMALLOC(sizeof(*key), NULL, DYNAMIC_TYPE_TMP_BUFFER);
3973+
ExpectNotNull(key);
3974+
3975+
XMEMSET(&rng, 0, sizeof(rng));
3976+
ExpectIntEQ(wc_InitRng(&rng), 0);
3977+
3978+
#ifndef WOLFSSL_NO_ML_KEM_768
3979+
ExpectIntEQ(wc_MlKemKey_Init(key, WC_ML_KEM_768, NULL, INVALID_DEVID), 0);
3980+
#elif !defined(WOLFSSL_NO_ML_KEM_512)
3981+
ExpectIntEQ(wc_MlKemKey_Init(key, WC_ML_KEM_512, NULL, INVALID_DEVID), 0);
3982+
#else
3983+
ExpectIntEQ(wc_MlKemKey_Init(key, WC_ML_KEM_1024, NULL, INVALID_DEVID), 0);
3984+
#endif
3985+
3986+
ExpectIntEQ(wc_MlKemKey_CipherTextSize(key, &ctLen), 0);
3987+
ExpectIntEQ(wc_MlKemKey_MakeKey(key, &rng), 0);
3988+
ExpectIntEQ(wc_MlKemKey_Encapsulate(key, ct, ss, &rng), 0);
3989+
3990+
/* Untampered ciphertext recovers the original ss. */
3991+
XMEMSET(ssDec, 0, sizeof(ssDec));
3992+
ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ssDec, ct, ctLen), 0);
3993+
ExpectIntEQ(XMEMCMP(ssDec, ss, WC_ML_KEM_SS_SZ), 0);
3994+
3995+
/* Tamper at byte 32: implicit rejection must fire. */
3996+
XMEMCPY(ctTampered, ct, ctLen);
3997+
ctTampered[32] ^= 0x01;
3998+
XMEMSET(ssTampered, 0, sizeof(ssTampered));
3999+
ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ssTampered, ctTampered, ctLen), 0);
4000+
ExpectIntNE(XMEMCMP(ssTampered, ss, WC_ML_KEM_SS_SZ), 0);
4001+
4002+
/* Tamper at byte 0: also must be rejected. */
4003+
XMEMCPY(ctTampered, ct, ctLen);
4004+
ctTampered[0] ^= 0x01;
4005+
XMEMSET(ssTampered, 0, sizeof(ssTampered));
4006+
ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ssTampered, ctTampered, ctLen), 0);
4007+
ExpectIntNE(XMEMCMP(ssTampered, ss, WC_ML_KEM_SS_SZ), 0);
4008+
4009+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
4010+
wc_MlKemKey_Free(key);
4011+
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4012+
#endif
4013+
#endif
4014+
return EXPECT_RESULT();
4015+
} /* END test_wc_mlkem_decap_fo_reject */
4016+

tests/api/test_mlkem.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ int test_wc_mlkem_make_key_kats(void);
2828
int test_wc_mlkem_encapsulate_kats(void);
2929
int test_wc_mlkem_decapsulate_kats(void);
3030
int test_wc_mlkem_decapsulate_pubonly_fails(void);
31+
int test_wc_mlkem_decap_fo_reject(void);
3132

32-
#define TEST_MLKEM_DECLS \
33-
TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \
34-
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encapsulate_kats), \
35-
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats), \
36-
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_pubonly_fails)
33+
#define TEST_MLKEM_DECLS \
34+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \
35+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encapsulate_kats), \
36+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats), \
37+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_pubonly_fails), \
38+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decap_fo_reject)
3739

3840
#endif /* WOLFCRYPT_TEST_MLKEM_H */

wolfcrypt/src/port/arm/armv8-mlkem-asm.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8927,7 +8927,7 @@ L_mlkem_aarch64_cmp_neon_done:
89278927
orr v8.16b, v8.16b, v9.16b
89288928
orr v10.16b, v10.16b, v11.16b
89298929
orr v8.16b, v8.16b, v10.16b
8930-
ins v9.b[0], v8.b[1]
8930+
ext v9.16b, v8.16b, v8.16b, #8
89318931
orr v8.16b, v8.16b, v9.16b
89328932
mov x0, v8.d[0]
89338933
subs x0, x0, xzr

wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8404,7 +8404,7 @@ int mlkem_cmp_neon(const byte* a, const byte* b, int sz)
84048404
"orr v8.16b, v8.16b, v9.16b\n\t"
84058405
"orr v10.16b, v10.16b, v11.16b\n\t"
84068406
"orr v8.16b, v8.16b, v10.16b\n\t"
8407-
"ins v9.b[0], v8.b[1]\n\t"
8407+
"ext v9.16b, v8.16b, v8.16b, #8\n\t"
84088408
"orr v8.16b, v8.16b, v9.16b\n\t"
84098409
"mov x0, v8.d[0]\n\t"
84108410
"subs x0, x0, xzr\n\t"

0 commit comments

Comments
 (0)