TI C2000 example: MLDSA=1 octet-boundary KATs for ML-DSA-44/65/87, pre-hash and packed input

pull/617/head
David Garske 2026-08-28 18:16:01 -07:00
parent a4c09c7f40
commit 104cab75a0
6 changed files with 3407 additions and 4 deletions

File diff suppressed because it is too large Load Diff

View File

@ -153,6 +153,16 @@ extern "C" {
#undef WOLFSSL_NO_ML_DSA_65
#define WOLFSSL_NO_ML_DSA_65
#ifdef WOLF_MLDSA_ALL_LEVELS
/* The octet-boundary KAT image (make MLDSA=1) verifies at all three parameter
* sets. Level 44 matters disproportionately here: it is the only one whose w1
* commitment encoder packs 6-bit values (mldsa_encode_w1_88_c), so it is the
* only one that exercises that packer's octet masking at CHAR_BIT == 16.
* Levels 65 and 87 pack 4-bit values, which cannot overflow an octet. */
#undef WOLFSSL_NO_ML_DSA_44
#undef WOLFSSL_NO_ML_DSA_65
#endif
/* Raw key/sig import - no ASN.1 (both modes). */
#undef WOLFSSL_MLDSA_NO_ASN1
#define WOLFSSL_MLDSA_NO_ASN1

View File

@ -111,6 +111,15 @@ ifeq ($(MLKEM),1)
$(WOLFROOT)/wolfcrypt/src/wc_mlkem_poly.c
endif
# MLDSA=1 builds the octet-boundary KAT image: ML-DSA-44/65/87 verify,
# wc_MlDsaKey_VerifyCtxHash() over SHA-256/SHA-512, and a verify from a PACKED
# key and signature via wc_UnpackOctets(). All three parameter sets are
# compiled in, so the test/benchmark harness is dropped to make room.
MLDSA ?= 0
ifeq ($(MLDSA),1)
CFLAGS += --define=WOLF_MLDSA_ALL_LEVELS
endif
# aes.c + wc_encrypt.c are wanted by AES=1, AESEXTRA=1 and HWAES=1. Each sets
# NEED_AES_CORE and the pair is appended once below: the build is a single
# cl2000 invocation, so a source listed twice multiply-defines at link.
@ -235,6 +244,8 @@ ifeq ($(DH),1)
$(WOLFROOT)/wolfcrypt/src/sp_c32.c
endif
HARNESS_EXTRA :=
HARNESS_SRCS := \
$(WOLFROOT)/wolfcrypt/test/test.c \
$(WOLFROOT)/wolfcrypt/benchmark/benchmark.c
@ -261,6 +272,14 @@ ifeq ($(SIGN),1)
--define=NO_CRYPT_BENCHMARK
LNKCMD := $(CURDIR)/28p55x_wolf_sign_lnk.cmd
HARNESS_SRCS :=
else ifeq ($(MLDSA),1)
ifeq ($(BENCH),1)
$(error MLDSA=1 and BENCH=1 are separate images - build them one at a time)
endif
CFLAGS += --define=WOLF_MLDSA_OCTETS --define=NO_CRYPT_TEST \
--define=NO_CRYPT_BENCHMARK
LNKCMD := $(CURDIR)/28p55x_wolf_flash_lnk.cmd
HARNESS_SRCS :=
else
LNKCMD := $(CURDIR)/28p55x_wolf_flash_lnk.cmd
ifeq ($(BENCH),1)

View File

@ -9,6 +9,7 @@ The default build runs a KAT suite plus `wolfcrypt_test` and (optionally) `bench
- SHA-224/256, SHA-384/512, SHA-512/224, SHA-512/256
- SHA3-224/256/384/512, SHAKE128, SHAKE256 (split-64 Keccak permutation, ~53% faster than the generic C path on C28x)
- ML-DSA-87 (Dilithium level 5) verify, and the full keygen+sign+verify round-trip (`SIGN=1`)
- ML-DSA-44/65/87 verify, HashML-DSA (`wc_MlDsaKey_VerifyCtxHash`) over SHA-256/SHA-512, and a packed-octet-stream verify (`MLDSA=1`)
- ML-KEM-768 (FIPS 203) keygen/encap/decap round-trip (`MLKEM=1`)
- AES-128/192/256 CBC/CTR/CFB/GCM (`AES=1`); AES-CMAC, AES-CCM, AES-GMAC (`AESEXTRA=1`); hardware-accelerated AES-ECB/CBC/CTR on the on-chip AESA block (`HWAES=1`)
- HMAC-SHA256 + HKDF (`HKDF=1`)
@ -17,6 +18,14 @@ The default build runs a KAT suite plus `wolfcrypt_test` and (optionally) `bench
- ECDSA + ECDH over SECP256R1 via SP math (`ECC=1`)
- RSA-2048 PKCS#1 v1.5 verify via SP math (`RSA=1`)
## Octet representation at the API boundary (`MLDSA=1`)
Every wolfCrypt `byte*` buffer holds one octet per `byte` cell. On this part a cell is 16 bits, so a buffer costs twice the RAM and each cell reads as `0x00nn` -- an ML-DSA-65 signature is 3309 octets = 3309 cells = 6618 bytes of RAM. The octet values are exact, so this is a footprint property, not a signature-integrity one, and `sigLen` stays an octet count.
Data arriving from flash, SCI, CAN or a host tool is **packed**, two octets per cell, low octet first. Convert it with `wc_UnpackOctets()` before calling wolfCrypt (and `wc_PackOctets()` on the way out). Both are declared only under `WOLFSSL_WIDE_BYTE` -- on an 8-bit-byte host the two layouts are the same buffer, so there is nothing to convert.
`make MLDSA=1` proves this on hardware: ML-DSA-44/65/87 verify, `wc_MlDsaKey_VerifyCtxHash()` over SHA-256/SHA-512 with a non-empty context, and a verify from a packed key and signature. Vectors are in `Header/mldsa_octet_kat.h`, regenerated by `tools/gen_kat.c` (build it against a host wolfSSL, run it, and move the header into `Header/`). Full write-up: `IDE/C2000/README.md` in the wolfSSL tree.
## The 16-bit-byte (CHAR_BIT == 16) story
On the C28x a `char`/`unsigned char` (wolfSSL's `byte`) holds 16 bits, the minimum addressable unit is 16 bits, and `(byte)x` masks to 16 bits, not 8. wolfCrypt octet handling that assumed an 8-bit byte was made `CHAR_BIT`-agnostic behind the `WOLFSSL_WIDE_BYTE` gate (auto-enabled for `CHAR_BIT != 8` and known 16-bit-char TI toolchains), with zero change on 8-bit targets. The fixes fall into a few recurring classes:
@ -62,6 +71,7 @@ Each is `make <NAME>=1` (default 0 unless noted), additive on top of the default
| `ENTROPY_PROBE=1` | Raw entropy characterization image: dumps unconditioned samples over SCI for host analysis, runs no crypto |
| `RSA=1` | RSA-2048 verify (SP math, 2048-only, verify/public-only) |
| `SIGN=1` | Full ML-DSA-87 keygen+sign+verify demo (dedicated linker script, 32 KW heap, no test/bench harness) |
| `MLDSA=1` | ML-DSA octet-boundary KATs: verify at all three parameter sets, `wc_MlDsaKey_VerifyCtxHash()` over SHA-256/SHA-512, and a verify from a PACKED key/signature via `wc_UnpackOctets()`. No test/bench harness. See "Octet representation" below |
| `BENCH=1` | Run only `benchmark` instead of `wolfcrypt_test` (they need separate images on this RAM-limited part) |
Other knobs: `CGT_ROOT` (required), `C2000WARE`, `WOLFROOT`, and `CIO=1` (route `printf` to the CCS/JTAG console via CIO instead of the default SCI/UART).

View File

@ -101,7 +101,16 @@
#endif
#include <wolfcrypt/test/test.h>
#include <wolfcrypt/benchmark/benchmark.h>
#ifdef WOLF_MLDSA_OCTETS
/* The octet-boundary image carries its own seed-derived vectors for all three
* parameter sets (it needs the matching private key to produce the pre-hash
* signatures), and reuses the same kat_mldsa87_* names. The canonical FIPS 204
* ML-DSA-87 vectors from wolfcrypt/test/test.c stay in the default image. */
#include <wolfssl/wolfcrypt/hash.h>
#include "mldsa_octet_kat.h"
#else
#include "mldsa87_kat.h"
#endif
#ifdef WOLF_ECC
#include "ecc_p256_kat.h"
#endif
@ -870,7 +879,7 @@ static void wolf_sha1_test(void)
}
#endif /* WOLF_SHA1 */
#ifndef WOLF_MLDSA_SIGN
#if !defined(WOLF_MLDSA_SIGN) && !defined(WOLF_MLDSA_OCTETS)
/* ML-DSA-87 verify known-answer test (real pk/msg/sig from test.c). This
* is the primary deliverable: a deterministic, RNG-free verify on HW.
* Key struct is static (large; WOLFSSL_MLDSA_VERIFY_NO_MALLOC pins buffers
@ -956,7 +965,178 @@ static void wolf_mldsa87_verify_test(void)
#endif
wc_MlDsaKey_Free(&mldsa_key);
}
#endif /* !WOLF_MLDSA_SIGN */
#endif /* !WOLF_MLDSA_SIGN && !WOLF_MLDSA_OCTETS */
#ifdef WOLF_MLDSA_OCTETS
/* ------------------------------------------------------------------------- */
/* ML-DSA octet-boundary tests (make MLDSA=1) */
/* ------------------------------------------------------------------------- */
/* Proves three things on 16-bit-byte silicon: verify is correct at all three
* parameter sets (level 44 matters most - its w1 encoder packs 6-bit values, so
* it is the only one that can push a cell above 255); the HashML-DSA path
* wc_MlDsaKey_VerifyCtxHash() works with a non-empty context; and a key and
* signature that arrive PACKED verify once wc_UnpackOctets() expands them.
* One key struct is shared - WOLFSSL_MLDSA_VERIFY_NO_MALLOC pins the verify
* workspace inside it, far too big for the 16 KW C28x stack. */
/* Static: an ML-DSA-65 signature is 3309 cells = 6618 octets of C28x RAM. */
static byte mo_pub[WC_MLDSA_65_PUB_KEY_SIZE];
static byte mo_sig[WC_MLDSA_65_SIG_SIZE];
static wc_MlDsaKey mo_key;
static byte mo_msg[512];
/* hashAlg < 0 selects plain verify over the message; otherwise pre-hash. */
static void mo_verify(const char* what, int type, const byte* pub,
word32 pubLen, const byte* sig, word32 sigLen, int hashAlg,
const byte* hash, word32 hashLen)
{
int res = 0;
int ret;
ret = wc_MlDsaKey_Init(&mo_key, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_MlDsaKey_SetParams(&mo_key, type);
}
if (ret == 0) {
ret = wc_MlDsaKey_ImportPubRaw(&mo_key, pub, pubLen);
}
if (ret == 0) {
if (hashAlg < 0) {
ret = wc_MlDsaKey_VerifyCtx(&mo_key, sig, sigLen, NULL, 0, mo_msg,
(word32)sizeof(mo_msg), &res);
}
else {
ret = wc_MlDsaKey_VerifyCtxHash(&mo_key, sig, sigLen,
(const byte*)KAT_MLDSA_CTX,
(byte)(sizeof(KAT_MLDSA_CTX) - 1), hash, hashLen, hashAlg,
&res);
}
}
printf("%s %s (ret=%d res=%d)\r\n", what,
((ret == 0) && (res == 1)) ? "PASS" : "FAIL", ret, res);
wc_MlDsaKey_Free(&mo_key);
}
/* Helpers on their own, odd length so the partial trailing cell is covered. */
static void mo_pack_roundtrip(void)
{
byte src[65];
byte packed[65];
byte back[65];
int i;
int ret;
int ok;
for (i = 0; i < (int)sizeof(src); i++) {
src[i] = (byte)((i * 7 + 1) & 0xFF);
}
ret = wc_PackOctets(packed, (word32)sizeof(packed), src,
(word32)sizeof(src));
if (ret == 0) {
ret = wc_UnpackOctets(back, (word32)sizeof(back), packed,
(word32)sizeof(src));
}
ok = (ret == 0) && (XMEMCMP(src, back, sizeof(src)) == 0);
printf("wc_Pack/UnpackOctets round-trip: %s (ret=%d)\r\n",
ok ? "PASS" : "FAIL", ret);
}
/* Key and signature stored PACKED, expanded before use. */
static void mo_packed_verify(void)
{
int res = 0;
int ret;
ret = wc_UnpackOctets(mo_pub, (word32)sizeof(mo_pub),
(const byte*)kat_mldsa65_pub_packed, (word32)sizeof(mo_pub));
if (ret == 0) {
ret = wc_UnpackOctets(mo_sig, (word32)sizeof(mo_sig),
(const byte*)kat_mldsa65_sig_packed, (word32)sizeof(mo_sig));
}
/* Expanded buffers must match the plain arrays octet for octet. */
if (ret == 0) {
if ((XMEMCMP(mo_pub, kat_mldsa65_pub, sizeof(mo_pub)) != 0) ||
(XMEMCMP(mo_sig, kat_mldsa65_sig, sizeof(mo_sig)) != 0)) {
ret = -1;
}
}
if (ret == 0) {
ret = wc_MlDsaKey_Init(&mo_key, NULL, INVALID_DEVID);
}
if (ret == 0) {
ret = wc_MlDsaKey_SetParams(&mo_key, WC_ML_DSA_65);
}
if (ret == 0) {
ret = wc_MlDsaKey_ImportPubRaw(&mo_key, mo_pub, (word32)sizeof(mo_pub));
}
if (ret == 0) {
ret = wc_MlDsaKey_VerifyCtx(&mo_key, mo_sig, (word32)sizeof(mo_sig),
NULL, 0, mo_msg, (word32)sizeof(mo_msg), &res);
}
printf("ML-DSA-65 packed-signature verify: %s (ret=%d res=%d)\r\n",
((ret == 0) && (res == 1)) ? "PASS" : "FAIL", ret, res);
wc_MlDsaKey_Free(&mo_key);
}
static void wolf_mldsa_octet_test(void)
{
byte sha256[WC_SHA256_DIGEST_SIZE];
byte sha512[WC_SHA512_DIGEST_SIZE];
int i;
int ret;
/* msg[i] = i & 0xFF. The mask is not cosmetic here: a C28x cell would
* otherwise store 256..511 verbatim and corrupt the hash input. */
for (i = 0; i < (int)sizeof(mo_msg); i++) {
mo_msg[i] = (byte)(i & 0xFF);
}
ret = wc_Sha256Hash(mo_msg, (word32)sizeof(mo_msg), sha256);
if (ret == 0) {
ret = wc_Sha512Hash(mo_msg, (word32)sizeof(mo_msg), sha512);
}
if (ret != 0) {
printf("ML-DSA octet tests: SKIP (digest failed ret=%d)\r\n", ret);
return;
}
printf("octet model: CHAR_BIT=%d, %lu octet(s) per byte cell; "
"ML-DSA-65 sig = %lu octets = %lu cells = %lu bytes of RAM\r\n",
(int)CHAR_BIT, (unsigned long)WC_OCTETS_PER_BYTE,
(unsigned long)WC_MLDSA_65_SIG_SIZE,
(unsigned long)WC_MLDSA_65_SIG_SIZE,
(unsigned long)WC_MLDSA_65_SIG_SIZE * (CHAR_BIT / 8));
mo_pack_roundtrip();
mo_verify("ML-DSA-44 verify KAT:", WC_ML_DSA_44, kat_mldsa44_pub,
(word32)sizeof(kat_mldsa44_pub), kat_mldsa44_sig,
(word32)sizeof(kat_mldsa44_sig), -1, NULL, 0);
mo_verify("ML-DSA-65 verify KAT:", WC_ML_DSA_65, kat_mldsa65_pub,
(word32)sizeof(kat_mldsa65_pub), kat_mldsa65_sig,
(word32)sizeof(kat_mldsa65_sig), -1, NULL, 0);
mo_verify("ML-DSA-87 verify KAT:", WC_ML_DSA_87, kat_mldsa87_pub,
(word32)sizeof(kat_mldsa87_pub), kat_mldsa87_sig,
(word32)sizeof(kat_mldsa87_sig), -1, NULL, 0);
mo_verify("ML-DSA-44 VerifyCtxHash SHA-256 KAT:", WC_ML_DSA_44,
kat_mldsa44_pub, (word32)sizeof(kat_mldsa44_pub),
kat_mldsa44_sig_ph256, (word32)sizeof(kat_mldsa44_sig_ph256),
WC_HASH_TYPE_SHA256, sha256, (word32)sizeof(sha256));
mo_verify("ML-DSA-65 VerifyCtxHash SHA-256 KAT:", WC_ML_DSA_65,
kat_mldsa65_pub, (word32)sizeof(kat_mldsa65_pub),
kat_mldsa65_sig_ph256, (word32)sizeof(kat_mldsa65_sig_ph256),
WC_HASH_TYPE_SHA256, sha256, (word32)sizeof(sha256));
mo_verify("ML-DSA-87 VerifyCtxHash SHA-256 KAT:", WC_ML_DSA_87,
kat_mldsa87_pub, (word32)sizeof(kat_mldsa87_pub),
kat_mldsa87_sig_ph256, (word32)sizeof(kat_mldsa87_sig_ph256),
WC_HASH_TYPE_SHA256, sha256, (word32)sizeof(sha256));
mo_verify("ML-DSA-87 VerifyCtxHash SHA-512 KAT:", WC_ML_DSA_87,
kat_mldsa87_pub, (word32)sizeof(kat_mldsa87_pub),
kat_mldsa87_sig_ph512, (word32)sizeof(kat_mldsa87_sig_ph512),
WC_HASH_TYPE_SHA512, sha512, (word32)sizeof(sha512));
mo_packed_verify();
}
#endif /* WOLF_MLDSA_OCTETS */
#ifdef WOLF_MLDSA_SIGN
/* ML-DSA-87 sign+verify round-trip (keygen -> sign -> verify). Exercises
@ -2007,7 +2187,10 @@ int main(void)
wolf_sha1_test();
#endif
#ifndef WOLF_MLDSA_SIGN
#if defined(WOLF_MLDSA_OCTETS)
printf("\r\n--- ML-DSA octet boundary (44/65/87, pre-hash, packed) ---\r\n");
wolf_mldsa_octet_test();
#elif !defined(WOLF_MLDSA_SIGN)
wolf_mldsa87_verify_test();
#else
wolf_mldsa87_sign_test();
@ -2017,7 +2200,7 @@ int main(void)
#ifndef WOLFSSL_NO_ML_DSA_65
mldsa_sign_roundtrip(WC_ML_DSA_65, "ML-DSA-65");
#endif
#endif /* !WOLF_MLDSA_SIGN */
#endif /* ML-DSA test selection */
#ifdef WOLF_ECC
printf("\r\n--- ECDSA/ECDH P-256 (SP) ---\r\n");

View File

@ -0,0 +1,211 @@
/* gen_kat.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Generate the deterministic ML-DSA KAT vectors in Header/mldsa_octet_kat.h.
*
* gcc -o gen_kat gen_kat.c -lwolfssl && ./gen_kat && mv mldsa_octet_kat.h ../Header/
*
* Everything is seed driven, so a regeneration reproduces the file exactly. */
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/wc_mldsa.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/sha512.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <stdio.h>
#include <string.h>
#define MSG_SZ 512
#define OUT_NAME "mldsa_octet_kat.h"
static const byte kSeed[MLDSA_SEED_SZ] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
};
static const byte kRnd[MLDSA_RND_SZ] = {
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,
0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf
};
/* FIPS 204 application context string; deliberately non-empty so the
* 0x01 || ctxLen || ctx domain separator is actually exercised. */
static const char kCtx[] = "wolfSSL-C28x";
static FILE* out;
static void emit_bytes(const char* name, const byte* b, word32 len)
{
word32 i;
fprintf(out, "static const unsigned char %s[] = {", name);
for (i = 0; i < len; i++) {
fprintf(out, "%s 0x%02x,", ((i % 12) == 0) ? "\n " : "", b[i]);
}
fprintf(out, "\n};\n");
}
/* Same octets packed two per 16-bit cell, low octet first. Identical storage
* on a little-endian 8-bit host, so the same test code runs on both. */
static void emit_packed(const char* name, const byte* b, word32 len)
{
word32 cells = (len + 1) / 2;
word32 i;
fprintf(out, "static const unsigned short %s[] = {", name);
for (i = 0; i < cells; i++) {
unsigned lo = b[i * 2];
unsigned hi = ((i * 2 + 1) < len) ? b[i * 2 + 1] : 0;
fprintf(out, "%s 0x%04x,", ((i % 8) == 0) ? "\n " : "",
lo | (hi << 8));
}
fprintf(out, "\n};\n");
}
static int do_level(int type, const char* tag, const byte* msg,
const byte* sha256, const byte* sha512, int wantPacked, int wantPh512)
{
wc_MlDsaKey key;
byte pub[MLDSA_MAX_PUB_KEY_SIZE];
byte sig[MLDSA_MAX_SIG_SIZE];
word32 pubLen = (word32)sizeof(pub);
word32 sigLen;
char name[64];
int ret;
ret = wc_MlDsaKey_Init(&key, NULL, INVALID_DEVID);
if (ret != 0) {
fprintf(stderr, "%s init failed: %d\n", tag, ret);
return ret;
}
ret = wc_MlDsaKey_SetParams(&key, type);
if (ret == 0)
ret = wc_MlDsaKey_MakeKeyFromSeed(&key, kSeed);
if (ret == 0)
ret = wc_MlDsaKey_ExportPubRaw(&key, pub, &pubLen);
if (ret == 0) {
snprintf(name, sizeof(name), "kat_mldsa%s_pub", tag);
emit_bytes(name, pub, pubLen);
}
/* Plain (non pre-hash) signature over the whole message. */
if (ret == 0) {
sigLen = (word32)sizeof(sig);
ret = wc_MlDsaKey_SignCtxWithSeed(&key, NULL, 0, sig, &sigLen,
msg, MSG_SZ, kRnd);
}
if (ret == 0) {
snprintf(name, sizeof(name), "kat_mldsa%s_sig", tag);
emit_bytes(name, sig, sigLen);
if (wantPacked) {
snprintf(name, sizeof(name), "kat_mldsa%s_sig_packed", tag);
emit_packed(name, sig, sigLen);
snprintf(name, sizeof(name), "kat_mldsa%s_pub_packed", tag);
emit_packed(name, pub, pubLen);
}
}
/* HashML-DSA over SHA-256(msg) with a non-empty context. */
if (ret == 0) {
sigLen = (word32)sizeof(sig);
ret = wc_MlDsaKey_SignCtxHashWithSeed(&key, (const byte*)kCtx,
(byte)(sizeof(kCtx) - 1), sig, &sigLen, sha256,
WC_SHA256_DIGEST_SIZE, WC_HASH_TYPE_SHA256, kRnd);
}
if (ret == 0) {
snprintf(name, sizeof(name), "kat_mldsa%s_sig_ph256", tag);
emit_bytes(name, sig, sigLen);
}
/* Same again over SHA-512, so the OID table is exercised at two lengths. */
if ((ret == 0) && wantPh512) {
sigLen = (word32)sizeof(sig);
ret = wc_MlDsaKey_SignCtxHashWithSeed(&key, (const byte*)kCtx,
(byte)(sizeof(kCtx) - 1), sig, &sigLen, sha512,
WC_SHA512_DIGEST_SIZE, WC_HASH_TYPE_SHA512, kRnd);
if (ret == 0) {
snprintf(name, sizeof(name), "kat_mldsa%s_sig_ph512", tag);
emit_bytes(name, sig, sigLen);
}
}
if (ret != 0)
fprintf(stderr, "%s vector generation failed: %d\n", tag, ret);
wc_MlDsaKey_Free(&key);
return ret;
}
int main(void)
{
byte msg[MSG_SZ];
byte sha256[WC_SHA256_DIGEST_SIZE];
byte sha512[WC_SHA512_DIGEST_SIZE];
int i;
int ret;
for (i = 0; i < MSG_SZ; i++)
msg[i] = (byte)(i & 0xFF);
ret = wc_Sha256Hash(msg, MSG_SZ, sha256);
if (ret == 0)
ret = wc_Sha512Hash(msg, MSG_SZ, sha512);
if (ret != 0) {
fprintf(stderr, "hash failed: %d\n", ret);
return 1;
}
out = fopen(OUT_NAME, "w");
if (out == NULL) {
fprintf(stderr, "cannot open output\n");
return 1;
}
fprintf(out,
"/* ML-DSA octet-boundary KAT vectors for the TI C2000 C28x example.\n"
" *\n"
" * GENERATED by tools/gen_kat.c - do not edit. Seed driven, so a\n"
" * regeneration reproduces this file exactly. Message is\n"
" * msg[i] = i & 0xFF, 512 octets. The _packed arrays hold the same\n"
" * octets two per 16-bit cell, low octet first - the layout an octet\n"
" * stream has off flash on a CHAR_BIT == 16 target. */\n"
"#ifndef MLDSA_OCTET_KAT_H\n"
"#define MLDSA_OCTET_KAT_H\n\n"
"#define KAT_MLDSA_CTX \"%s\"\n\n", kCtx);
ret = do_level(WC_ML_DSA_44, "44", msg, sha256, sha512, 0, 0);
if (ret == 0)
ret = do_level(WC_ML_DSA_65, "65", msg, sha256, sha512, 1, 0);
if (ret == 0)
ret = do_level(WC_ML_DSA_87, "87", msg, sha256, sha512, 0, 1);
if (ret == 0)
fprintf(out, "\n#endif /* MLDSA_OCTET_KAT_H */\n");
fclose(out);
if (ret != 0) {
/* Do not leave a truncated header behind for the build to pick up. */
remove(OUT_NAME);
return 1;
}
return 0;
}