TI C2000 example: MLDSA=1 octet-boundary KATs for ML-DSA-44/65/87, pre-hash and packed input
parent
a4c09c7f40
commit
104cab75a0
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue