Add ML-DSA support.

pull/508/head
jordan 2024-10-11 15:38:40 -05:00 committed by Daniele Lacamera
parent 8bdb0507c0
commit b97abd4ace
18 changed files with 711 additions and 19 deletions

View File

@ -80,6 +80,10 @@ jobs:
- name: Renode Tests ext_XMSS-SHA2_10_256
run: ./tools/renode/docker-test.sh "SIGN=ext_XMSS XMSS_PARAMS='XMSS-SHA2_10_256' WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE=5000"
# ML-DSA TEST
- name: Renode Tests ML-DSA-44
run: ./tools/renode/docker-test.sh "SIGN=ML_DSA ML_DSA_LEVEL=2 WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE=4840"
- name: Upload Output Dir
uses: actions/upload-artifact@v3

View File

@ -546,3 +546,15 @@ jobs:
run: |
tools/scripts/sim-sunnyday-update.sh
- name: Run sunny day LMS update test
run: |
tools/scripts/sim-pq-sunnyday-update.sh config/examples/sim-lms.config
- name: Run sunny day XMSS update test
run: |
tools/scripts/sim-pq-sunnyday-update.sh config/examples/sim-xmss.config
- name: Run sunny day ML-DSA update test
run: |
tools/scripts/sim-pq-sunnyday-update.sh config/examples/sim-ml-dsa.config

View File

@ -27,15 +27,15 @@ ARCH=sim
TARGET=sim
SIGN?=LMS
HASH?=SHA256
LMS_LEVELS=2
LMS_HEIGHT=5
LMS_LEVELS=1
LMS_HEIGHT=10
LMS_WINTERNITZ=8
IMAGE_SIGNATURE_SIZE=1456
IMAGE_HEADER_SIZE=4096
WOLFBOOT_SMALL_STACK=0
SPI_FLASH=0
DEBUG=0
DELTA_UPDATES=0
IMAGE_SIGNATURE_SIZE=2644
IMAGE_HEADER_SIZE?=5288
# sizes should be multiple of system page size
WOLFBOOT_PARTITION_SIZE=0x40000

View File

@ -0,0 +1,65 @@
# ML-DSA signature example, based on sim.config example.
#
# The acceptable parameter values are those in FIPS 204:
#
# ML_DSA_LEVEL = {2, 3, 5}
#
# This corresponds to these security levels (from FIPS 204, Table 1.):
#
# Claimed Security Strength
# ML-DSA-44 Category 2
# ML-DSA-65 Category 3
# ML-DSA-87 Category 5
#
# The signature, pub key, and priv key lengths are all a function
# of this parameter. Refer to this table (from FIPS 204, Table 2.)
# to configure your IMAGE_SIGNATURE_SIZE:
#
# Table 2. Sizes (in bytes) of keys and signatures of ML-DSA
#
# Private Key Public Key Signature Size
# ML-DSA-44 2560 1312 2420
# ML-DSA-65 4032 1952 3309
# ML-DSA-87 4896 2592 4627
#
ARCH=sim
TARGET=sim
SIGN?=ML_DSA
HASH?=SHA256
WOLFBOOT_SMALL_STACK=0
SPI_FLASH=0
DEBUG=0
DELTA_UPDATES=0
#
# ML-DSA config examples:
#
# Category 2:
ML_DSA_LEVEL=2
IMAGE_SIGNATURE_SIZE=2420
IMAGE_HEADER_SIZE?=8192
#
# Category 3:
# ML_DSA_LEVEL=3
# IMAGE_SIGNATURE_SIZE=3309
# IMAGE_HEADER_SIZE?=8192
#
# Category 5:
# ML_DSA_LEVEL=5
# IMAGE_SIGNATURE_SIZE=4627
# IMAGE_HEADER_SIZE?=12288
# This example needsd larger sector size.
# WOLFBOOT_SECTOR_SIZE=0x3000
#
# sizes should be multiple of system page size
WOLFBOOT_PARTITION_SIZE=0x40000
WOLFBOOT_SECTOR_SIZE=0x2000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x20000
# if on external flash, it should be multiple of system page size
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x60000
WOLFBOOT_PARTITION_SWAP_ADDRESS=0xA0000
# required for keytools
WOLFBOOT_FIXED_PARTITIONS=1

View File

@ -17,11 +17,11 @@ SPI_FLASH=0
DEBUG=0
DELTA_UPDATES=0
IMAGE_SIGNATURE_SIZE=2500
IMAGE_HEADER_SIZE?=5000
IMAGE_HEADER_SIZE?=8192
# sizes should be multiple of system page size
WOLFBOOT_PARTITION_SIZE=0x40000
WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_SECTOR_SIZE=0x2000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x20000
# if on external flash, it should be multiple of system page size
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x60000

View File

@ -1,8 +1,90 @@
# Post-Quantum Signatures
wolfBoot is adding support for post-quantum signatures. At present, support
for LMS/HSS (https://www.rfc-editor.org/rfc/rfc8554.html), and XMSS/XMSS^MT
(https://www.rfc-editor.org/rfc/rfc8391.html) has been added.
wolfBoot is continuously adding support for post-quantum (PQ) signature
algorithms as they mature. At present, support has been added for three NIST
approved PQ signature algorithms:
- ML-DSA: https://csrc.nist.gov/pubs/fips/204/final
- LMS/HSS: https://csrc.nist.gov/projects/stateful-hash-based-signatures
- XMSS/XMSS^MT: https://csrc.nist.gov/projects/stateful-hash-based-signatures
ML-DSA is a PQ lattice-based algorithm, derived from
CRYSTALS-DILITHIUM (a round three NIST finalist).
LMS/HSS and XMSS/XMSS^MT are both PQ stateful hash-based signature (HBS)
schemes, recommended in NIST SP 800-208.
In terms of relative tradeoffs:
- All three methods have fast verifying operations.
- All three methods have variable length signature sizes.
- ML-DSA key generation is much faster than LMS/HSS and XMSS/XMSS^MT.
- ML-DSA public keys are larger than LMS/HSS and XMSS/XMSS^MT, and
variable sized.
- LMS/HSS and XMSS/XMSS^MT have stateful private keys, which requires
more care with key generation and signing operations.
See these config files for simulated target examples:
- `config/examples/sim-ml-dsa.config`
- `config/examples/sim-lms.config`
- `config/examples/sim-xmss.config`
## Lattice Based Signature Methods
### ML-DSA
ML-DSA (Module-Lattice Digital Signature Algorithm) was standardized in
FIPS 204 (https://csrc.nist.gov/pubs/fips/204/final), based on its
round 3 predecessor CRYSTALS-DILITHIUM.
ML-DSA has three standardized parameter sets:
- `ML-DSA-44`
- `ML-DSA-65`
- `ML-DSA-87`
The numerical suffix (44, 65, 87) denotes the dimension of the matrix used
in the underlying lattice construction.
The private key, public key, signature size, and overall security strength
all depend on the parameter set:
```
#
# Private Key Public Key Signature Size Security Strength
# ML-DSA-44 2560 1312 2420 Category 2
# ML-DSA-65 4032 1952 3309 Category 3
# ML-DSA-87 4896 2592 4627 Category 5
#
```
### ML-DSA Config
A new ML-DSA sim example has been added here:
```
config/examples/sim-ml-dsa.config
```
The security category level is configured with `ML_DSA_LEVEL=<num>`, where
num = 2, 3, 5. Here is an example from the `sim-ml-dsa.config` for category
2:
```
# ML-DSA config examples:
#
# Category 2:
ML_DSA_LEVEL=2
IMAGE_SIGNATURE_SIZE=2420
IMAGE_HEADER_SIZE?=4840
```
Note: The wolfcrypt implementation of ML-DSA (dilithium) builds to the
FIPS 204 final standard by default. If you wish to conform to the older
FIPS 204 draft standard, then build with `WOLFSSL_DILITHIUM_FIPS204_DRAFT`
instead.
## Stateful Hash-Based Signature Methods
LMS/HSS and XMSS/XMSS^MT are both post-quantum stateful hash-based signature
(HBS) schemes. They are known for having small public keys, relatively fast
@ -19,7 +101,7 @@ See these links for more info on stateful HBS support and wolfSSL/wolfCrypt:
- https://www.wolfssl.com/documentation/manuals/wolfssl/appendix07.html#post-quantum-stateful-hash-based-signatures
- https://github.com/wolfSSL/wolfssl-examples/tree/master/pq/stateful_hash_sig
## Supported PQ Signature Methods
### Supported PQ HBS Options
These four PQ signature options are supported:
- LMS: uses wolfcrypt implementation from `wc_lms.c`, and `wc_lms_impl.c`.

View File

@ -61,15 +61,18 @@ extern "C" {
extern const unsigned char rsa4096_pub_key[];
extern unsigned int rsa4096_pub_key_len;
# define IMAGE_SIGNATURE_SIZE (512)
/* In PQC methods the signature size is a function of
* the parameters. Therefore IMAGE_SIGNATURE_SIZE is
* set in options.mk from the .config file. */
#elif defined(WOLFBOOT_SIGN_LMS)
/* Because signature size in LMS is function of
* LMS variables, IMAGE_SIGNATURE_SIZE is set in
* options.mk from the .config file. */
extern const unsigned char lms_pub_key[];
extern unsigned int lms_pub_key_len;
#elif defined(WOLFBOOT_SIGN_XMSS)
extern const unsigned char xmss_pub_key[];
extern unsigned int xmss_pub_key_len;
#elif defined(WOLFBOOT_SIGN_ML_DSA)
extern const unsigned char ml_dsa_pub_key[];
extern unsigned int ml_dsa_pub_key_len;
#elif !defined(WOLFBOOT_NO_SIGN)
# error "No public key available for given signing algorithm."
#endif /* Algorithm selection */

View File

@ -227,6 +227,29 @@ extern int tolower(int c);
# define NO_RSA
#endif /* RSA */
/* ML-DSA (dilithium) */
#if defined(WOLFBOOT_SIGN_ML_DSA)
# define HAVE_DILITHIUM
# define WOLFSSL_WC_DILITHIUM
# define WOLFSSL_EXPERIMENTAL_SETTINGS
/* Wolfcrypt builds ML-DSA (dilithium) to the FIPS 204 final
* standard by default. Uncomment this if you want the draft
* version instead. */
#if 0
#define WOLFSSL_DILITHIUM_FIPS204_DRAFT
#endif
# define WOLFSSL_DILITHIUM_VERIFY_ONLY
# define WOLFSSL_DILITHIUM_NO_LARGE_CODE
# define WOLFSSL_DILITHIUM_SMALL
# define WOLFSSL_DILITHIUM_VERIFY_SMALL_MEM
# define WOLFSSL_DILITHIUM_VERIFY_NO_MALLOC
# define WOLFSSL_DILITHIUM_NO_ASN1
/* dilithium needs these sha functions. */
# define WOLFSSL_SHA3
# define WOLFSSL_SHAKE256
# define WOLFSSL_SHAKE128
#endif /* WOLFBOOT_SIGN_ML_DSA */
#ifdef WOLFBOOT_HASH_SHA3_384
# define WOLFSSL_SHA3
# if defined(NO_RSA) && !defined(WOLFBOOT_TPM) && \

View File

@ -86,6 +86,7 @@ extern "C" {
#define AUTH_KEY_RSA3072 0x08
#define AUTH_KEY_LMS 0x09
#define AUTH_KEY_XMSS 0x10
#define AUTH_KEY_ML_DSA 0x11
@ -107,6 +108,7 @@ extern "C" {
#define HDR_IMG_TYPE_AUTH_RSA3072 (AUTH_KEY_RSA3072 << 8)
#define HDR_IMG_TYPE_AUTH_LMS (AUTH_KEY_LMS << 8)
#define HDR_IMG_TYPE_AUTH_XMSS (AUTH_KEY_XMSS << 8)
#define HDR_IMG_TYPE_AUTH_ML_DSA (AUTH_KEY_ML_DSA << 8)
#define HDR_IMG_TYPE_DIFF 0x00D0
@ -127,6 +129,22 @@ extern "C" {
#define KEYSTORE_PUBKEY_SIZE_RSA4096 576
#define KEYSTORE_PUBKEY_SIZE_LMS 60
#define KEYSTORE_PUBKEY_SIZE_XMSS 68
/* ML-DSA pub key size is a function of parameters.
* This needs to be configurable. Default to security
* category 2. */
#ifdef ML_DSA_LEVEL
#if ML_DSA_LEVEL == 2
#define KEYSTORE_PUBKEY_SIZE_ML_DSA 1312
#elif ML_DSA_LEVEL == 3
#define KEYSTORE_PUBKEY_SIZE_ML_DSA 1952
#elif ML_DSA_LEVEL == 5
#define KEYSTORE_PUBKEY_SIZE_ML_DSA 2592
#else
#error "Invalid ML_DSA_LEVEL!"
#endif
#else
#define KEYSTORE_PUBKEY_SIZE_ML_DSA 1312
#endif /* ML_DSA_LEVEL */
/* Mask for key permissions */
#define KEY_VERIFY_ALL (0xFFFFFFFFU)
@ -237,6 +255,11 @@ extern "C" {
# ifndef WOLFBOOT_UNIVERSAL_KEYSTORE
# define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_XMSS
# endif
#elif defined(WOLFBOOT_SIGN_ML_DSA)
# define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_ML_DSA
# ifndef WOLFBOOT_UNIVERSAL_KEYSTORE
# define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_ML_DSA
# endif
#else
# error "No valid authentication mechanism selected. " \
"Please select a valid SIGN= option."

View File

@ -487,6 +487,26 @@ ifneq (,$(filter $(SIGN), ext_LMS ext_XMSS))
CFLAGS +=-DWOLFSSL_EXPERIMENTAL_SETTINGS
endif
ifeq ($(SIGN),ML_DSA)
# Use wolfcrypt ML-DSA dilithium implementation.
KEYGEN_OPTIONS+=--ml_dsa
SIGN_OPTIONS+=--ml_dsa
WOLFCRYPT_OBJS+= \
./lib/wolfssl/wolfcrypt/src/dilithium.o \
./lib/wolfssl/wolfcrypt/src/memory.o \
./lib/wolfssl/wolfcrypt/src/sha3.o \
./lib/wolfssl/wolfcrypt/src/wc_port.o \
./lib/wolfssl/wolfcrypt/src/hash.o
CFLAGS+=-D"WOLFBOOT_SIGN_ML_DSA" \
-D"IMAGE_SIGNATURE_SIZE"=$(IMAGE_SIGNATURE_SIZE) \
-D"ML_DSA_LEVEL"=$(ML_DSA_LEVEL)
ifeq ($(WOLFBOOT_SMALL_STACK),1)
$(error WOLFBOOT_SMALL_STACK with ML-DSA not supported yet)
else
STACK_USAGE=19544
endif
endif
ifeq ($(RAM_CODE),1)
CFLAGS+= -D"RAM_CODE"
endif

View File

@ -472,6 +472,106 @@ static void wolfBoot_verify_signature(uint8_t key_slot,
}
#endif /* WOLFBOOT_SIGN_XMSS */
#ifdef WOLFBOOT_SIGN_ML_DSA
#include <wolfssl/wolfcrypt/dilithium.h>
static void wolfBoot_verify_signature(uint8_t key_slot,
struct wolfBoot_image *img, uint8_t *sig)
{
int ret = 0;
MlDsaKey ml_dsa;
uint8_t * pubkey = NULL;
int pub_len = 0;
int sig_len = 0;
int verify_res = 0;
wolfBoot_printf("info: ML-DSA wolfBoot_verify_signature\n");
pubkey = keystore_get_buffer(key_slot);
if (pubkey == NULL) {
wolfBoot_printf("error: ML-DSA pubkey not found\n");
return;
}
ret = wc_MlDsaKey_Init(&ml_dsa, NULL, INVALID_DEVID);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_Init returned %d\n", ret);
}
if (ret == 0) {
/* Set the ML-DSA security level. */
ret = wc_MlDsaKey_SetParams(&ml_dsa, ML_DSA_LEVEL);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_SetParams(%d)" \
" returned %d\n", ML_DSA_LEVEL, ret);
}
}
/* Make sure pub key matches parameters. */
if (ret == 0) {
ret = wc_MlDsaKey_GetPubLen(&ml_dsa, &pub_len);
if (ret != 0 || pub_len <= 0) {
wolfBoot_printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret);
ret = -1;
}
else if (pub_len != KEYSTORE_PUBKEY_SIZE_ML_DSA) {
wolfBoot_printf("error: ML-DSA pub key mismatch: got %d bytes " \
"expected %d\n", pub_len, KEYSTORE_PUBKEY_SIZE_ML_DSA);
ret = -1;
}
}
/* Make sure sig len matches parameters. */
if (ret == 0) {
ret = wc_MlDsaKey_GetSigLen(&ml_dsa, &sig_len);
if (ret != 0 || sig_len <= 0) {
wolfBoot_printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret);
ret = -1;
}
else if (sig_len != IMAGE_SIGNATURE_SIZE) {
wolfBoot_printf("error: ML-DSA sig len mismatch: got %d bytes " \
"expected %d\n", sig_len, IMAGE_SIGNATURE_SIZE);
ret = -1;
}
}
if (ret == 0) {
/* Now import pub key. */
ret = wc_MlDsaKey_ImportPubRaw(&ml_dsa, pubkey, pub_len);
if (ret != 0) {
wolfBoot_printf("error: wc_MlDsaKey_ImportPubRaw returned: %d\n",
ret);
}
}
if (ret == 0) {
wolfBoot_printf("info: using ML-DSA security level: %d\n",
ML_DSA_LEVEL);
/* Finally verify signagure. */
ret = wc_MlDsaKey_Verify(&ml_dsa, sig, IMAGE_SIGNATURE_SIZE,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE,
&verify_res);
if (ret == 0 && verify_res == 1) {
wolfBoot_printf("info: wc_MlDsaKey_Verify returned OK\n");
wolfBoot_image_confirm_signature_ok(img);
}
else {
wolfBoot_printf("error: wc_MlDsaKey_Verify returned: ret=%d, "
"res=%d\n", ret, verify_res);
}
}
wc_MlDsaKey_Free(&ml_dsa);
}
#endif /* WOLFBOOT_SIGN_ML_DSA */
#endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY */

View File

@ -162,6 +162,15 @@ OBJS_REAL+=\
$(XMSSDIR)/utils.o
endif
# Add wolfcrypt ML-DSA (dilithium) implementation.
ifeq ($(SIGN),ML_DSA)
OBJS_REAL+=$(WOLFDIR)/wolfcrypt/src/dilithium.o
CFLAGS += -D"WOLFBOOT_SIGN_ML_DSA" \
-D"IMAGE_SIGNATURE_SIZE"=$(IMAGE_SIGNATURE_SIZE) \
-D"ML_DSA_LEVEL"=$(ML_DSA_LEVEL)
endif
OBJS_VIRT=$(addprefix $(OBJDIR), $(notdir $(OBJS_REAL)))
vpath %.c $(WOLFDIR)/wolfcrypt/src/
vpath %.c $(WOLFBOOTDIR)/src/

View File

@ -79,6 +79,10 @@
#endif
#endif
#ifdef WOLFSSL_WC_DILITHIUM
#include <wolfssl/wolfcrypt/dilithium.h>
#endif
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#ifdef DEBUG_SIGNTOOL
@ -103,6 +107,7 @@
#define KEYGEN_RSA3072 8
#define KEYGEN_LMS 9
#define KEYGEN_XMSS 10
#define KEYGEN_ML_DSA 11
/* Globals */
static FILE *fpub, *fpub_image;
@ -117,7 +122,12 @@ static int saveAsDer = 0;
static WC_RNG rng;
#ifndef KEYSLOT_MAX_PUBKEY_SIZE
#define KEYSLOT_MAX_PUBKEY_SIZE 576
#if defined(WOLFSSL_WC_DILITHIUM)
/* ML-DSA pub keys are big. */
#define KEYSLOT_MAX_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_ML_DSA
#else
#define KEYSLOT_MAX_PUBKEY_SIZE 576
#endif
#endif
struct keystore_slot {
@ -300,7 +310,8 @@ const char KType[][17] = {
"AUTH_KEY_ECC521",
"AUTH_KEY_RSA3072",
"AUTH_KEY_LMS",
"AUTH_KEY_XMSS"
"AUTH_KEY_XMSS",
"AUTH_KEY_ML_DSA"
};
const char KSize[][29] = {
@ -314,7 +325,8 @@ const char KSize[][29] = {
"KEYSTORE_PUBKEY_SIZE_ECC521",
"KEYSTORE_PUBKEY_SIZE_RSA3072",
"KEYSTORE_PUBKEY_SIZE_LMS",
"KEYSTORE_PUBKEY_SIZE_XMSS"
"KEYSTORE_PUBKEY_SIZE_XMSS",
"KEYSTORE_PUBKEY_SIZE_ML_DSA"
};
const char KName[][8] = {
@ -328,7 +340,8 @@ const char KName[][8] = {
"ECC521",
"RSA3072",
"LMS",
"XMSS"
"XMSS",
"ML_DSA"
};
#define MAX_PUBKEYS 64
@ -375,6 +388,9 @@ static uint32_t get_pubkey_size(uint32_t keyType)
case KEYGEN_XMSS:
size = KEYSTORE_PUBKEY_SIZE_XMSS;
break;
case KEYGEN_ML_DSA:
size = KEYSTORE_PUBKEY_SIZE_ML_DSA;
break;
default:
size = 0;
}
@ -404,6 +420,13 @@ void keystore_add(uint32_t ktype, uint8_t *key, uint32_t sz, const char *keyfile
sl.part_id_mask = id_mask;
sl.pubkey_size = get_pubkey_size(ktype);
if (sl.pubkey_size > sizeof(sl.pubkey)){
printf("error: %s pubkey larger than keystore: %d > %zu\n",
KName[ktype], sl.pubkey_size, sizeof(sl.pubkey));
exit(1);
}
memcpy(sl.pubkey, key, sl.pubkey_size);
#ifdef WOLFBOOT_UNIVERSAL_KEYSTORE
slot_size = sizeof(struct keystore_slot);
@ -773,6 +796,120 @@ static void keygen_xmss(const char *priv_fname, uint32_t id_mask)
}
#endif /* if defined(WOLFSSL_HAVE_XMSS) */
#if defined(WOLFSSL_WC_DILITHIUM)
static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask)
{
FILE * fpriv = NULL;
MlDsaKey key;
int ret;
byte * priv = NULL;
byte pub[KEYSTORE_PUBKEY_SIZE_ML_DSA];
word32 priv_len = 0;
word32 pub_len = 0;
int ml_dsa_priv_len = 0;
int ml_dsa_pub_len = 0;
ret = wc_MlDsaKey_Init(&key, NULL, INVALID_DEVID);
if (ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_Init returned %d\n", ret);
exit(1);
}
ret = wc_MlDsaKey_SetParams(&key, ML_DSA_LEVEL);
if (ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_SetParams(%d) returned %d\n",
ML_DSA_LEVEL, ret);
exit(1);
}
/* Make the key pair. */
ret = wc_MlDsaKey_MakeKey(&key, &rng);
if (ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_MakeKey returned %d\n", ret);
exit(1);
}
/* Get the ML-DSA public key length. */
ret = wc_MlDsaKey_GetPubLen(&key, &ml_dsa_pub_len);
if (ret != 0 || ml_dsa_pub_len <= 0) {
printf("error: wc_MlDsaKey_GetPrivLen returned %d\n",
ret);
exit(1);
}
/* Get the ML-DSA private key length. This API returns
* the public + private length. */
ret = wc_MlDsaKey_GetPrivLen(&key, &ml_dsa_priv_len);
if (ret != 0 || ml_dsa_priv_len <= 0) {
printf("error: wc_MlDsaKey_GetPrivLen returned %d\n",
ret);
exit(1);
}
if (ml_dsa_priv_len <= ml_dsa_pub_len) {
printf("error: ml-dsa: unexpected key lengths: %d, %d",
ml_dsa_priv_len, ml_dsa_pub_len);
exit(1);
}
else {
ml_dsa_priv_len -= ml_dsa_pub_len;
}
priv = malloc(ml_dsa_priv_len);
if (priv == NULL) {
fprintf(stderr, "error: malloc(%d) failed\n", ml_dsa_priv_len);
exit(1);
}
/* Set the expected key lengths. */
pub_len = (word32) ml_dsa_pub_len;
priv_len = (word32) ml_dsa_priv_len;
ret = wc_MlDsaKey_ExportPubRaw(&key, pub, &pub_len);
if (ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_ExportPubRaw returned %d\n", ret);
exit(1);
}
ret = wc_MlDsaKey_ExportPrivRaw(&key, priv, &priv_len);
if (ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_ExportPrivRaw returned %d\n", ret);
exit(1);
}
if (pub_len != sizeof(pub)) {
fprintf(stderr, "error: wc_MlDsaKey_ExportPubRaw returned pub_len=%d, " \
"expected %zu\n", pub_len, sizeof(pub));
exit(1);
}
if ((int) priv_len != ml_dsa_priv_len) {
fprintf(stderr, "error: ml_dsa priv key mismatch: got %d " \
"bytes, expected %d\n", priv_len, ml_dsa_priv_len);
exit(1);
}
fpriv = fopen(priv_fname, "wb");
if (fpriv == NULL) {
fprintf(stderr, "error: fopen(%s) failed: %s",
priv_fname, strerror(errno));
exit(1);
}
fwrite(priv, priv_len, 1, fpriv);
fwrite(pub, pub_len, 1, fpriv);
fclose(fpriv);
keystore_add(KEYGEN_ML_DSA, pub, KEYSTORE_PUBKEY_SIZE_ML_DSA,
priv_fname, id_mask);
wc_MlDsaKey_Free(&key);
free(priv);
priv = NULL;
}
#endif /* if defined(WOLFSSL_WC_DILITHIUM) */
static void key_gen_check(const char *kfilename)
{
@ -848,6 +985,13 @@ static void key_generate(uint32_t ktype, const char *kfilename, uint32_t id_mask
keygen_xmss(kfilename, id_mask);
break;
#endif
#ifdef WOLFSSL_WC_DILITHIUM
case KEYGEN_ML_DSA:
keygen_ml_dsa(kfilename, id_mask);
break;
#endif
} /* end switch */
}
@ -1009,6 +1153,11 @@ int main(int argc, char** argv)
else if (strcmp(argv[i], "--xmss") == 0) {
keytype = KEYGEN_XMSS;
}
#endif
#if defined(WOLFSSL_WC_DILITHIUM)
else if (strcmp(argv[i], "--ml_dsa") == 0) {
keytype = KEYGEN_ML_DSA;
}
#endif
else if (strcmp(argv[i], "--force") == 0) {
force = 1;

View File

@ -124,6 +124,10 @@ static inline int fp_truncate(FILE *f, size_t len)
#endif
#endif
#ifdef WOLFSSL_WC_DILITHIUM
#include <wolfssl/wolfcrypt/dilithium.h>
#endif
#ifdef DEBUG_SIGNTOOL
#include <wolfssl/wolfcrypt/logging.h>
#endif
@ -186,6 +190,7 @@ static inline int fp_truncate(FILE *f, size_t len)
#define SIGN_ECC521 HDR_IMG_TYPE_AUTH_ECC521
#define SIGN_LMS HDR_IMG_TYPE_AUTH_LMS
#define SIGN_XMSS HDR_IMG_TYPE_AUTH_XMSS
#define SIGN_ML_DSA HDR_IMG_TYPE_AUTH_ML_DSA
#define ENC_OFF 0
@ -246,6 +251,9 @@ static union {
#ifdef WOLFSSL_HAVE_XMSS
XmssKey xmss;
#endif
#ifdef WOLFSSL_WC_DILITHIUM
MlDsaKey ml_dsa;
#endif
} key;
struct cmd_options {
@ -467,9 +475,13 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
uint32_t idx = 0;
int io_sz;
FILE *f;
#ifdef WOLFSSL_HAVE_XMSS
#if defined(WOLFSSL_HAVE_XMSS)
word32 priv_sz = 0;
#endif
#if defined(WOLFSSL_WC_DILITHIUM)
int priv_sz = 0;
int pub_sz = 0;
#endif
/* open and load key buffer */
*key_buffer = NULL;
@ -746,6 +758,63 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
}
#endif /* WOLFSSL_HAVE_XMSS */
#ifdef WOLFSSL_WC_DILITHIUM
FALL_THROUGH; /* we didn't solve the key, keep trying */
case SIGN_ML_DSA:
ret = wc_MlDsaKey_GetPubLen(&key.ml_dsa, &pub_sz);
if (ret != 0 || pub_sz <= 0) {
printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret);
break;
}
/* Get the ML-DSA private key length. This API returns
* the public + private length. */
ret = wc_MlDsaKey_GetPrivLen(&key.ml_dsa, &priv_sz);
if (ret != 0 || priv_sz <= 0) {
printf("error: wc_MlDsaKey_GetPrivLen returned %d\n", ret);
break;
}
if (priv_sz <= pub_sz) {
printf("error: ml-dsa: unexpected key lengths: %d, %d",
priv_sz, pub_sz);
break;
}
else {
priv_sz -= pub_sz;
}
printf("info: ml-dsa priv len: %d\n", priv_sz);
printf("info: ml-dsa pub len: %d\n", pub_sz);
if ((int)*key_buffer_sz == (priv_sz + pub_sz)) {
/* priv + pub */
ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer,
priv_sz);
*pubkey = (*key_buffer) + priv_sz;
*pubkey_sz = (*key_buffer_sz) - priv_sz;
ret = 0;
printf("Found ml-dsa key\n");
break;
}
else if ((int)*key_buffer_sz == pub_sz) {
/* pub only */
*pubkey = (*key_buffer);
*pubkey_sz = pub_sz;
ret = 0;
printf("Found ml-dsa public only key\n");
break;
}
else {
/* We don't recognize this as an ML-DSA pub or private key. */
printf("error: unrecognized ml-dsa key size: %d\n",
*key_buffer_sz);
ret = -1;
}
#endif /* WOLFSSL_WC_DILITHIUM */
break;
} /* end switch (CMD.sign) */
@ -915,6 +984,19 @@ static int sign_digest(int sign, int hash_algo,
}
else
#endif /* WOLFSSL_HAVE_XMSS */
#ifdef WOLFSSL_WC_DILITHIUM
if (sign == SIGN_ML_DSA) {
/* Nothing else to do, ready to sign. */
if (ret == 0) {
ret = wc_MlDsaKey_Sign(&key.ml_dsa, signature, signature_sz,
digest, digest_sz, &rng);
}
if (ret != 0) {
fprintf(stderr, "error signing with XMSS: %d\n", ret);
}
}
else
#endif /* WOLFSSL_WC_DILITHIUM */
{
ret = NOT_COMPILED_IN;
}
@ -1871,6 +1953,12 @@ int main(int argc, char** argv)
CMD.sign = SIGN_XMSS;
sign_str = "XMSS";
}
#endif
#ifdef HAVE_DILITHIUM
else if (strcmp(argv[i], "--ml_dsa") == 0) {
CMD.sign = SIGN_ML_DSA;
sign_str = "ML-DSA";
}
#endif
else if (strcmp(argv[i], "--sha256") == 0) {
CMD.hash_algo = HASH_SHA256;
@ -2250,10 +2338,44 @@ int main(int argc, char** argv)
CMD.signature_sz = sig_sz;
}
#endif /* WOLFSSL_HAVE_XMSS */
#ifdef WOLFSSL_WC_DILITHIUM
else if (CMD.sign == SIGN_ML_DSA) {
int ml_dsa_ret = 0;
int sig_sz = 0;
ml_dsa_ret = wc_MlDsaKey_Init(&key.ml_dsa, NULL, INVALID_DEVID);
if (ml_dsa_ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_Init returned %d\n", ml_dsa_ret);
exit(1);
}
ml_dsa_ret = wc_MlDsaKey_SetParams(&key.ml_dsa, ML_DSA_LEVEL);
if (ml_dsa_ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_SetParamStr(%d)" \
" returned %d\n", ML_DSA_LEVEL, ret);
exit(1);
}
printf("info: using ML-DSA parameters: %d\n", ML_DSA_LEVEL);
ml_dsa_ret = wc_MlDsaKey_GetSigLen(&key.ml_dsa, &sig_sz);
if (ml_dsa_ret != 0) {
fprintf(stderr, "error: wc_MlDsaKey_GetSigLen returned %d\n",
ml_dsa_ret);
exit(1);
}
printf("info: ML-DSA signature size: %d\n", sig_sz);
CMD.header_sz = 2 * sig_sz;
CMD.signature_sz = sig_sz;
}
#endif /* WOLFSSL_WC_DILITHIUM */
if (((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) ||
CMD.header_sz == 0) {
printf("Invalid hash or signature type! %d\n", CMD.sign);
printf("Invalid hash or signature type! %d, %d, %d\n", CMD.sign,
CMD.signature_sz, CMD.header_sz);
exit(2);
}
@ -2311,6 +2433,11 @@ int main(int argc, char** argv)
else if (CMD.sign == SIGN_XMSS) {
#ifdef WOLFSSL_HAVE_XMSS
wc_XmssKey_Free(&key.xmss);
#endif
}
else if (CMD.sign == SIGN_ML_DSA) {
#ifdef WOLFSSL_WC_DILITHIUM
wc_MlDsaKey_Free(&key.ml_dsa);
#endif
}
return ret;

View File

@ -73,6 +73,21 @@
#define WOLFSSL_SHA3
#undef NO_SHA256
/* ML-DSA (dilithium) */
#if defined(WOLFBOOT_SIGN_ML_DSA)
# define HAVE_DILITHIUM
# define WOLFSSL_WC_DILITHIUM
# define WOLFSSL_EXPERIMENTAL_SETTINGS
/* Wolfcrypt builds ML-DSA (dilithium) to the FIPS 204 final
* standard by default. Uncomment this if you want the draft
* version instead. */
#if 0
#define WOLFSSL_DILITHIUM_FIPS204_DRAFT
#endif
/* dilithium needs these sha functions. */
# define WOLFSSL_SHAKE128
#endif /* WOLFBOOT_SIGN_ML_DSA */
/* ASN */
#define WOLFSSL_ASN_TEMPLATE

View File

@ -0,0 +1,53 @@
#!/bin/bash
#
# For PQ methods the keytools must be built to match the alg.
#
print_usage_and_die() {
echo "usage:"
echo " ./tools/scripts/sim-pq-sunnyday-update.sh <path to sim-pq dot config>"
echo ""
echo "example:"
echo " ./tools/scripts/sim-pq-sunnyday-update.sh config/examples/sim-ml-dsa.config"
exit 1
}
err_and_die() {
echo "error: $1"
exit 1
}
if [ $# -ne 1 ]; then
print_usage_and_die
fi
sim_pq=$1
if [ ! -f $sim_pq ]; then
err_and_die "file not found: $sim_pq"
fi
cp $sim_pq .config || err_and_die "cp $sim_pq"
make distclean; make clean;
make keytools || err_and_die "keytools build failed"
make test-sim-internal-flash-with-update || err_and_die "make sim failed"
V=`./wolfboot.elf update_trigger get_version 2>/dev/null`
if [ "x$V" != "x1" ]; then
echo "Failed first boot with update_trigger"
exit 1
fi
V=`./wolfboot.elf success get_version 2>/dev/null`
if [ "x$V" != "x2" ]; then
echo "Failed update (V: $V)"
exit 1
fi
echo Test successful.
exit 0

View File

@ -102,6 +102,10 @@ ifeq ($(SIGN),XMSS)
SIGN_ARGS+= --xmss
endif
ifeq ($(SIGN),ML_DSA)
SIGN_ARGS+= --ml_dsa
endif
ifeq ($(HASH),SHA256)
SIGN_ARGS+= --sha256
endif

View File

@ -57,6 +57,9 @@ endif
ifneq (,$(filter $(SIGN), XMSS ext_XMSS))
SIGN_ARGS+= --xmss
endif
ifeq ($(SIGN),ML_DSA)
SIGN_ARGS+= --ml_dsa
endif
# Make sign hash argument
ifeq ($(HASH),SHA256)