TI C2000: add PRECOMPA build using a flash-resident ML-DSA-87 matrix A
parent
fd1f216859
commit
9046a9548f
File diff suppressed because it is too large
Load Diff
|
|
@ -130,6 +130,13 @@ ifeq ($(SECUREBOOT),1)
|
|||
CFLAGS += --define=WOLF_SECUREBOOT --define=WOLF_MLDSA_ALL_LEVELS
|
||||
endif
|
||||
|
||||
# PRECOMPA=1 stores matrix A (expanded at build time from the fixed
|
||||
# verification key) in flash, so verify skips the SHAKE128 rejection sampling.
|
||||
PRECOMPA ?= 0
|
||||
ifeq ($(PRECOMPA),1)
|
||||
CFLAGS += --define=WOLFSSL_MLDSA_VERIFY_PRECOMP_A
|
||||
endif
|
||||
|
||||
MLDSA ?= 0
|
||||
ifeq ($(MLDSA),1)
|
||||
CFLAGS += --define=WOLF_MLDSA_ALL_LEVELS
|
||||
|
|
|
|||
|
|
@ -110,6 +110,9 @@
|
|||
* ML-DSA-87 vectors from wolfcrypt/test/test.c stay in the default image. */
|
||||
#include <wolfssl/wolfcrypt/hash.h>
|
||||
#include "mldsa_octet_kat.h"
|
||||
#ifdef WOLFSSL_MLDSA_VERIFY_PRECOMP_A
|
||||
#include "mldsa87_precomp_a.h"
|
||||
#endif
|
||||
#else
|
||||
#include "mldsa87_kat.h"
|
||||
#endif
|
||||
|
|
@ -1283,6 +1286,14 @@ static void sb_verify(const char* what, long flip, int want)
|
|||
ret = wc_MlDsaKey_ImportPubRaw(&sb_key, sb_mldsa87_pub,
|
||||
(word32)sizeof(sb_mldsa87_pub));
|
||||
}
|
||||
#ifdef WOLFSSL_MLDSA_VERIFY_PRECOMP_A
|
||||
if (ret == 0) {
|
||||
/* A was expanded at build time and lives in flash: verify skips the
|
||||
* SHAKE128 rejection sampling entirely. */
|
||||
ret = wc_MlDsaKey_SetPrecompA(&sb_key, sb_mldsa87_A, SB_A_LEN,
|
||||
sb_rho, (word32)sizeof(sb_rho));
|
||||
}
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
ret = wc_MlDsaKey_VerifyMu(&sb_key, sb_mldsa87_sig,
|
||||
(word32)sizeof(sb_mldsa87_sig), mu, MLDSA_MU_SZ, &res);
|
||||
|
|
@ -1297,8 +1308,64 @@ static void wolf_secureboot_test(void)
|
|||
printf("secure boot: %lu-octet packed image, %u-octet chunks, "
|
||||
"image never resident\r\n",
|
||||
(unsigned long)SB_IMG_SZ, (unsigned)SB_CHUNK);
|
||||
#ifdef WOLFSSL_MLDSA_VERIFY_PRECOMP_A
|
||||
printf("matrix A: precomputed in flash (%ld coefficients)\r\n",
|
||||
(long)SB_A_LEN);
|
||||
#else
|
||||
printf("matrix A: expanded at run time (SHAKE128 rejection sampling)\r\n");
|
||||
#endif
|
||||
sb_verify("ML-DSA-87 pure-mode packed-image verify:", -1, 1);
|
||||
sb_verify("ML-DSA-87 corrupted-image reject:", SB_IMG_SZ / 2, 0);
|
||||
|
||||
#ifdef WOLF_MLDSA_VERIFY_BENCH
|
||||
/* Time the ML-DSA verify alone (mu is already built), which is the figure
|
||||
* a bootloader budget cares about. */
|
||||
{
|
||||
byte mu[MLDSA_MU_SZ];
|
||||
int res = 0;
|
||||
int bret;
|
||||
int bi;
|
||||
double t;
|
||||
|
||||
/* Time mu construction too: for a real firmware image the streamed
|
||||
* SHAKE-256 hashing, not the ML-DSA verify, sets the boot budget. */
|
||||
(void)current_time(1);
|
||||
bret = sb_build_mu(mu, -1);
|
||||
t = current_time(0);
|
||||
if (bret == 0) {
|
||||
printf("mu build (%lu octets streamed): %.2f ms = %.1f KiB/s\r\n",
|
||||
(unsigned long)SB_IMG_SZ, t * 1000.0,
|
||||
((double)SB_IMG_SZ / 1024.0) / t);
|
||||
}
|
||||
if (bret == 0) {
|
||||
bret = wc_MlDsaKey_Init(&sb_key, NULL, INVALID_DEVID);
|
||||
}
|
||||
if (bret == 0) {
|
||||
bret = wc_MlDsaKey_SetParams(&sb_key, WC_ML_DSA_87);
|
||||
}
|
||||
if (bret == 0) {
|
||||
bret = wc_MlDsaKey_ImportPubRaw(&sb_key, sb_mldsa87_pub,
|
||||
(word32)sizeof(sb_mldsa87_pub));
|
||||
}
|
||||
#ifdef WOLFSSL_MLDSA_VERIFY_PRECOMP_A
|
||||
if (bret == 0) {
|
||||
bret = wc_MlDsaKey_SetPrecompA(&sb_key, sb_mldsa87_A, SB_A_LEN,
|
||||
sb_rho, (word32)sizeof(sb_rho));
|
||||
}
|
||||
#endif
|
||||
if (bret == 0) {
|
||||
(void)current_time(1);
|
||||
for (bi = 0; bi < 10; bi++) {
|
||||
(void)wc_MlDsaKey_VerifyMu(&sb_key, sb_mldsa87_sig,
|
||||
(word32)sizeof(sb_mldsa87_sig), mu, MLDSA_MU_SZ, &res);
|
||||
}
|
||||
t = current_time(0);
|
||||
printf("ML-DSA-87 VerifyMu bench: 10 ops in %.3f s = %.2f ms/op\r\n",
|
||||
t, (t * 1000.0) / 10.0);
|
||||
}
|
||||
wc_MlDsaKey_Free(&sb_key);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* WOLF_SECUREBOOT */
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,15 @@
|
|||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
#include <wolfssl/wolfcrypt/sha512.h>
|
||||
#include <wolfssl/wolfcrypt/hash.h>
|
||||
#include <wolfssl/wolfcrypt/sha3.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MSG_SZ 512
|
||||
#define OUT_NAME "mldsa_octet_kat.h"
|
||||
/* Second output: matrix A for the secure-boot key, expanded off target. */
|
||||
#define PRECOMP_OUT_NAME "mldsa87_precomp_a.h"
|
||||
/* SHAKE-128 rate, and the 3-byte triplets RejNTTPoly consumes from it. */
|
||||
#define SHAKE128_BLOCK_SZ 168
|
||||
|
||||
/* A stand-in firmware image for the secure-boot demo. Deliberately larger
|
||||
* than anything a bootloader would buffer, to show the image is never
|
||||
|
|
@ -160,6 +165,113 @@ static int do_level(int type, const char* tag, const byte* msg,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Expand matrix A for the secure-boot public key and write it as a flash
|
||||
* resident C array, so verify on target can skip the SHAKE128 rejection
|
||||
* sampling that otherwise dominates it.
|
||||
*
|
||||
* FIPS 204 Algorithm 32 ExpandA / Algorithm 30 RejNTTPoly, over the public
|
||||
* SHAKE-128 API only. A is a function of rho alone, so a fixed verification
|
||||
* key makes it a build-time constant. Coefficients come out in the NTT
|
||||
* domain and in the same row-major (r, s) order wc_mldsa.c indexes. */
|
||||
static int do_precomp_a(const byte* pub)
|
||||
{
|
||||
FILE* pf;
|
||||
wc_Shake shake;
|
||||
byte seed[MLDSA_PUB_SEED_SZ + 2];
|
||||
byte h[SHAKE128_BLOCK_SZ];
|
||||
long total = (long)PARAMS_ML_DSA_87_K * PARAMS_ML_DSA_87_L * MLDSA_N;
|
||||
long n = 0;
|
||||
int r;
|
||||
int s;
|
||||
int c;
|
||||
int j;
|
||||
int ret = 0;
|
||||
|
||||
pf = fopen(PRECOMP_OUT_NAME, "w");
|
||||
if (pf == NULL) {
|
||||
fprintf(stderr, "cannot open %s\n", PRECOMP_OUT_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(pf,
|
||||
"/* Precomputed ML-DSA-87 matrix A for the secure-boot key.\n"
|
||||
" *\n"
|
||||
" * GENERATED by tools/gen_kat.c - do not edit. A = ExpandA(rho)\n"
|
||||
" * and depends only on the public key, so a fixed verification\n"
|
||||
" * key lets it be built once and stored in flash, removing the\n"
|
||||
" * SHAKE128 rejection sampling from every verify.\n"
|
||||
" *\n"
|
||||
" * SECURITY: protect this exactly as you protect the public key.\n"
|
||||
" * Substituting it can influence verification. */\n"
|
||||
"#ifndef MLDSA87_PRECOMP_A_H\n"
|
||||
"#define MLDSA87_PRECOMP_A_H\n\n"
|
||||
"#define SB_A_LEN %ldL\n\n"
|
||||
"static const sword32 sb_mldsa87_A[SB_A_LEN] = {\n", total);
|
||||
|
||||
/* rho is the leading MLDSA_PUB_SEED_SZ octets of the raw public key. */
|
||||
XMEMCPY(seed, pub, MLDSA_PUB_SEED_SZ);
|
||||
|
||||
for (r = 0; (ret == 0) && (r < PARAMS_ML_DSA_87_K); r++) {
|
||||
/* Seed layout is rho || s || r, matching mldsa_verify_with_mu(). */
|
||||
seed[MLDSA_PUB_SEED_SZ + 1] = (byte)r;
|
||||
for (s = 0; (ret == 0) && (s < PARAMS_ML_DSA_87_L); s++) {
|
||||
seed[MLDSA_PUB_SEED_SZ + 0] = (byte)s;
|
||||
|
||||
ret = wc_InitShake128(&shake, NULL, INVALID_DEVID);
|
||||
if (ret == 0) {
|
||||
ret = wc_Shake128_Absorb(&shake, seed, (word32)sizeof(seed));
|
||||
}
|
||||
/* Rejection sample 256 coefficients from squeezed blocks. */
|
||||
for (j = 0; (ret == 0) && (j < MLDSA_N); ) {
|
||||
ret = wc_Shake128_SqueezeBlocks(&shake, h, 1);
|
||||
for (c = 0; (ret == 0) && (c < SHAKE128_BLOCK_SZ) &&
|
||||
(j < MLDSA_N); c += 3) {
|
||||
/* CoeffFromThreeBytes: 23-bit little-endian, drop >= q. */
|
||||
sword32 t = (sword32)h[c] + ((sword32)h[c + 1] << 8) +
|
||||
((sword32)h[c + 2] << 16);
|
||||
t &= 0x7fffff;
|
||||
if (t < MLDSA_Q) {
|
||||
fprintf(pf, "%s%ldL,", ((n % 10) == 0) ? " " : " ",
|
||||
(long)t);
|
||||
n++;
|
||||
if ((n % 10) == 0) {
|
||||
fprintf(pf, "\n");
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
wc_Shake128_Free(&shake);
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret == 0) && ((n % 10) != 0)) {
|
||||
fprintf(pf, "\n");
|
||||
}
|
||||
if (ret == 0) {
|
||||
fprintf(pf, "};\n\nstatic const unsigned char sb_rho[%d] = {\n",
|
||||
MLDSA_PUB_SEED_SZ);
|
||||
for (j = 0; j < MLDSA_PUB_SEED_SZ; j++) {
|
||||
fprintf(pf, "%s0x%02x,", ((j % 12) == 0) ? " " : " ", pub[j]);
|
||||
if (((j + 1) % 12) == 0) {
|
||||
fprintf(pf, "\n");
|
||||
}
|
||||
}
|
||||
if ((MLDSA_PUB_SEED_SZ % 12) != 0) {
|
||||
fprintf(pf, "\n");
|
||||
}
|
||||
fprintf(pf, "};\n\n#endif /* MLDSA87_PRECOMP_A_H */\n");
|
||||
}
|
||||
|
||||
fclose(pf);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "matrix A generation failed: %d\n", ret);
|
||||
/* Never leave a truncated header for the build to pick up. */
|
||||
remove(PRECOMP_OUT_NAME);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Secure-boot vectors: an ML-DSA-87 key, a packed "firmware image", and a
|
||||
* PURE (non pre-hash) signature over that image. */
|
||||
static int do_secureboot(byte* img)
|
||||
|
|
@ -188,6 +300,7 @@ static int do_secureboot(byte* img)
|
|||
emit_bytes("sb_mldsa87_pub", pub, pubLen);
|
||||
emit_packed("sb_image_packed", img, IMG_SZ);
|
||||
emit_bytes("sb_mldsa87_sig", sig, sigLen);
|
||||
ret = do_precomp_a(pub);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "secure-boot vector generation failed: %d\n", ret);
|
||||
|
|
|
|||
Loading…
Reference in New Issue