Skip to content
Open
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
24 changes: 24 additions & 0 deletions .github/configs/pq-all.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
"configure": ["--enable-dilithium=yes,small", "--enable-mlkem",
"--enable-experimental",
"CPPFLAGS=-pedantic -Wdeclaration-after-statement -Wnull-dereference -DWOLFSSL_MLDSA_SIGN_SMALL_MEM -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM -DWOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM -DWOLFSSL_MLDSA_NO_LARGE_CODE -DWC_MLDSA_NONBLOCK -DWOLFSSL_MLKEM_MAKEKEY_SMALL_MEM -DWOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM -DWC_MLKEM_NONBLOCK"]},
{"name": "pq-nonblock-smallest", "minutes": 2.0,
"comment": "Non-blocking ML-DSA on the smallest memory signer and verifier: mldsa_nonblock_test resumes the column walk and compares seeded output with the blocking path",
"configure": ["--enable-cryptonly", "--enable-mldsa",
"CPPFLAGS=-pedantic -Wdeclaration-after-statement -DWOLFSSL_MLDSA_SIGN_SMALLEST_MEM -DWOLFSSL_MLDSA_VERIFY_SMALLEST_MEM -DWOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM -DWC_MLDSA_NONBLOCK"]},
{"name": "pq-nonblock-smallest-checks", "minutes": 2.0,
"comment": "Non-blocking smallest memory signer with the early y and w0 checks and fault hardening: the y check result is carried across yields",
"configure": ["--enable-cryptonly", "--enable-mldsa=yes,small",
"CPPFLAGS=-DWOLFSSL_MLDSA_SIGN_SMALLEST_MEM -DWOLFSSL_MLDSA_VERIFY_SMALLEST_MEM -DWOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM -DWOLFSSL_MLDSA_SIGN_CHECK_Y -DWOLFSSL_MLDSA_SIGN_CHECK_W0 -DWC_MLDSA_FAULT_HARDEN -DWC_MLDSA_NONBLOCK"]},
{"name": "pq-nonblock-smallmem-dynkeys", "minutes": 2.5,
"comment": "Non-blocking ML-KEM with the cached matrix A and dynamic key buffers: a resumed keygen must keep the vectors its first slice allocated, and encapsulation must read the cached matrix across yields",
"configure": ["--enable-cryptonly", "--enable-mlkem", "--enable-smallstack",
Expand Down Expand Up @@ -361,6 +369,14 @@
"--disable-examples"],
"run": [["env", "QEMU_LD_PREFIX=/usr/aarch64-linux-gnu", "QEMU_CPU=max",
"./wolfcrypt/test/testwolfcrypt"]]},
{"name": "mldsa-aarch64-armasm-smallest", "minutes": 0.5, "check": false,
"cc": "ccache aarch64-linux-gnu-gcc",
"comment": "AArch64 NEON with the smallest memory signer and verifier and the small memory key generation, which leave the batched gen_y and gen_s lanes out. sha3-crypto builds the SHA-3 instruction wrappers as functions, and without the key check matrix A expansion does not use them either",
"configure": ["--host=aarch64-linux-gnu", "--enable-cryptonly",
"--enable-armasm=sha3-crypto", "--enable-mldsa", "--disable-examples",
"CPPFLAGS=-DWOLFSSL_MLDSA_SIGN_SMALLEST_MEM -DWOLFSSL_MLDSA_VERIFY_SMALLEST_MEM -DWOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM -DWOLFSSL_MLDSA_NO_CHECK_KEY"],
"run": [["env", "QEMU_LD_PREFIX=/usr/aarch64-linux-gnu", "QEMU_CPU=max",
"./wolfcrypt/test/testwolfcrypt"]]},
{"name": "mldsa-mlkem-aarch64-armasm-inline", "minutes": 0.5, "check": false,
"cc": "ccache aarch64-linux-gnu-gcc",
"comment": "Same, through the inline-asm backend.",
Expand Down Expand Up @@ -413,6 +429,14 @@
"comment": "Cached public vectors alongside the pinned verify buffers, the combination whose key structure member clash stopped it building; the clashing member only exists when the small memory verify is selected too",
"configure": ["--enable-cryptonly", "--enable-mldsa",
"CPPFLAGS=-DWC_MLDSA_CACHE_PUB_VECTORS -DWOLFSSL_MLDSA_VERIFY_NO_MALLOC -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM"]},
{"name": "mldsa-verify-no-malloc-sparse-c", "minutes": 2,
"comment": "Pinned verify buffers without cached public vectors, the only run of the sparse c verify that keeps c in key->cSparse and samples it with key->h as the block",
"configure": ["--enable-cryptonly", "--enable-mldsa",
"CPPFLAGS=-DWOLFSSL_MLDSA_VERIFY_NO_MALLOC -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM"]},
{"name": "mldsa-44-verify-no-malloc-sparse-c", "minutes": 2,
"comment": "As mldsa-verify-no-malloc-sparse-c with ML-DSA-44 only, so key->cSparse is sized by the ML-DSA-44 tau",
"configure": ["--enable-cryptonly", "--enable-mldsa",
"CPPFLAGS=-DWOLFSSL_MLDSA_VERIFY_NO_MALLOC -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM -DWOLFSSL_NO_ML_DSA_65 -DWOLFSSL_NO_ML_DSA_87"]},
{"name": "mldsa-cache-matrix", "minutes": 2,
"comment": "Cached matrix A on the default full vector signer, the only build where the signing cache allocation test's key->a and aSet assertions are compiled",
"configure": ["--enable-cryptonly", "--enable-mldsa",
Expand Down
146 changes: 146 additions & 0 deletions tests/api/test_mldsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -31684,6 +31684,152 @@ int test_mldsa_pkcs8_export_import_wolfSSL_form(void)
return EXPECT_RESULT();
}

#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
#define MLDSA_TEST_ENC_MAX_K 8
#define MLDSA_TEST_ENC_GUARD 32
#define MLDSA_TEST_ENC_SZ \
(MLDSA_TEST_ENC_MAX_K * MLDSA_N * MLDSA_D / 8 + MLDSA_TEST_ENC_GUARD)

/* Pack n values of bits bits each, little-endian, as FIPS 204 does. */
static void test_mldsa_pack_bits(const sword32* v, word32 n, word32 bits,
byte* out)
{
word32 i;
word32 accBits = 0;
word64 acc = 0;

for (i = 0; i < n; i++) {
acc |= (word64)(word32)v[i] << accBits;
accBits += bits;
while (accBits >= 8) {
*(out++) = (byte)acc;
acc >>= 8;
accBits -= 8;
}
}
}

static int test_mldsa_guard_intact(const byte* p)
{
int i;

for (i = 0; i < MLDSA_TEST_ENC_GUARD; i++) {
if (p[i] != 0xa5) {
return 0;
}
}
return 1;
}

static word32 test_mldsa_rand(word32* state)
{
*state = *state * 1103515245U + 12345U;
return *state >> 8;
}
#endif

/* The vector encoders write exactly the encoded length, for every count. */
int test_mldsa_vec_encode_exact_len(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
static const byte etas[] = {
#if !defined(WOLFSSL_NO_ML_DSA_44) || !defined(WOLFSSL_NO_ML_DSA_87)
2,
#endif
#ifndef WOLFSSL_NO_ML_DSA_65
4,
#endif
};
sword32* s = NULL;
sword32* v = NULL;
byte* enc = NULL;
byte* enc1 = NULL;
byte* exp = NULL;
word32 rnd = 1;
word32 len;
word32 len1;
word32 i;
word32 e;
byte d;

ExpectNotNull(s = (sword32*)XMALLOC(MLDSA_TEST_ENC_MAX_K * MLDSA_N *
sizeof(sword32), NULL, DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(v = (sword32*)XMALLOC(MLDSA_TEST_ENC_MAX_K * MLDSA_N *
sizeof(sword32), NULL, DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(enc = (byte*)XMALLOC(MLDSA_TEST_ENC_SZ, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(enc1 = (byte*)XMALLOC(MLDSA_TEST_ENC_SZ, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(exp = (byte*)XMALLOC(MLDSA_TEST_ENC_SZ, NULL,
DYNAMIC_TYPE_TMP_BUFFER));

#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && \
defined(DEBUG_VECTOR_REGISTER_ACCESS_FUZZING)
WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(0);
#endif

/* Every count, so the last store of each vector length is checked. */
for (e = 0; EXPECT_SUCCESS() && (e < sizeof(etas)); e++) {
byte eta = etas[e];
word32 bits = (eta == 2) ? 3 : 4;

for (d = 1; EXPECT_SUCCESS() && (d <= MLDSA_TEST_ENC_MAX_K); d++) {
len = (word32)d * MLDSA_N * bits / 8;
for (i = 0; i < (word32)d * MLDSA_N; i++) {
s[i] = (sword32)(test_mldsa_rand(&rnd) % (2U * eta + 1)) -
eta;
v[i] = eta - s[i];
}
test_mldsa_pack_bits(v, (word32)d * MLDSA_N, bits, exp);
XMEMSET(enc, 0xa5, MLDSA_TEST_ENC_SZ);
ExpectIntEQ(wc_mldsa_vec_encode_eta(s, d, eta, enc), 0);
ExpectBufEQ(enc, exp, len);
ExpectIntEQ(test_mldsa_guard_intact(enc + len), 1);
}
}

for (d = 1; EXPECT_SUCCESS() && (d <= MLDSA_TEST_ENC_MAX_K); d++) {
len = (word32)d * MLDSA_N * MLDSA_D / 8;
len1 = (word32)d * MLDSA_N * MLDSA_U / 8;
for (i = 0; i < (word32)d * MLDSA_N; i++) {
s[i] = (sword32)(test_mldsa_rand(&rnd) % MLDSA_Q);
}
/* Power2Round: t1 in the top bits, 2^(d-1) - t0 in the low ones. */
for (i = 0; i < (word32)d * MLDSA_N; i++) {
sword32 t1 = (s[i] + (1 << (MLDSA_D - 1)) - 1) >> MLDSA_D;
v[i] = (1 << (MLDSA_D - 1)) - (s[i] - (t1 << MLDSA_D));
}
test_mldsa_pack_bits(v, (word32)d * MLDSA_N, MLDSA_D, exp);
XMEMSET(enc, 0xa5, MLDSA_TEST_ENC_SZ);
XMEMSET(enc1, 0xa5, MLDSA_TEST_ENC_SZ);
ExpectIntEQ(wc_mldsa_vec_encode_t0_t1(s, d, enc, enc1), 0);
ExpectBufEQ(enc, exp, len);
ExpectIntEQ(test_mldsa_guard_intact(enc + len), 1);
for (i = 0; i < (word32)d * MLDSA_N; i++) {
v[i] = (s[i] + (1 << (MLDSA_D - 1)) - 1) >> MLDSA_D;
}
test_mldsa_pack_bits(v, (word32)d * MLDSA_N, MLDSA_U, exp);
ExpectBufEQ(enc1, exp, len1);
ExpectIntEQ(test_mldsa_guard_intact(enc1 + len1), 1);
}

#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && \
defined(DEBUG_VECTOR_REGISTER_ACCESS_FUZZING)
WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(0);
#endif

XFREE(exp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(enc1, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(enc, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(v, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(s, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}

/* Exercise w1 encoding with adversarial inputs to catch undefined behavior.
*
* The word32-optimized paths in mldsa_encode_w1_88_c and
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_mldsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ int test_mldsa_verify_mu_kats(void);
int test_mldsa_PrivateKeyDecode_OpenSSL_form(void);
int test_mldsa_pkcs8_import_OpenSSL_form(void);
int test_mldsa_pkcs8_export_import_wolfSSL_form(void);
int test_mldsa_vec_encode_exact_len(void);
int test_mldsa_encode_w1_large_values(void);
int test_mldsa_pkcs12(void);
int test_mldsa_x509_pubkey_sigtype(void);
Expand Down Expand Up @@ -132,6 +133,7 @@ int test_mldsa_make_key_id_dev(void);
TEST_DECL_GROUP("mldsa", test_mldsa_PrivateKeyDecode_OpenSSL_form), \
TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_import_OpenSSL_form), \
TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_export_import_wolfSSL_form), \
TEST_DECL_GROUP("mldsa", test_mldsa_vec_encode_exact_len), \
TEST_DECL_GROUP("mldsa", test_mldsa_encode_w1_large_values), \
TEST_DECL_GROUP("mldsa", test_mldsa_pkcs12), \
TEST_DECL_GROUP("mldsa", test_mldsa_x509_pubkey_sigtype), \
Expand Down
Loading
Loading