diff --git a/config/examples/polarfire_mpfs250.config b/config/examples/polarfire_mpfs250.config index 448191d789..9211942242 100644 --- a/config/examples/polarfire_mpfs250.config +++ b/config/examples/polarfire_mpfs250.config @@ -1,8 +1,22 @@ ARCH?=RISCV64 TARGET?=mpfs250 + +# ECC P384 + SHA384 SIGN?=ECC384 HASH?=SHA384 IMAGE_HEADER_SIZE=512 +# Flash sector size (4KB typical) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# ML-DSA 87 +#SIGN=ML_DSA +#HASH=SHA256 +#ML_DSA_LEVEL=5 +#IMAGE_SIGNATURE_SIZE=4627 +#IMAGE_HEADER_SIZE=12288 +# Flash sector size (16KB) +#WOLFBOOT_SECTOR_SIZE?=0x4000 + WOLFBOOT_VERSION?=1 ARMORED?=0 DEBUG?=0 @@ -27,7 +41,6 @@ ELF?=1 # Use RISC-V assembly version of ECDSA and SHA NO_ASM?=0 -NO_ARM_ASM?=0 # Optional: Use smaller SHA512 #CFLAGS_EXTRA+=-DUSE_SLOW_SHA512 @@ -38,9 +51,6 @@ DISK_EMMC?=0 # DDR Address for wolfBoot to start from WOLFBOOT_ORIGIN?=0x80000000 -# Flash sector size (4KB typical) -WOLFBOOT_SECTOR_SIZE?=0x1000 - # Load Partition to RAM Address WOLFBOOT_LOAD_ADDRESS?=0x8E000000 @@ -56,10 +66,10 @@ CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000 # Optional Encryption -#CUSTOM_ENCRYPT_KEY=1 -#ENCRYPT=1 -#ENCRYPT_WITH_AES256=1 -#OBJS_EXTRA=src/my_custom_encrypt_key.o +CUSTOM_ENCRYPT_KEY=1 +ENCRYPT=1 +ENCRYPT_WITH_AES256=1 +OBJS_EXTRA=src/my_custom_encrypt_key.o # Optional EMMC_SD debugging logs #CFLAGS_EXTRA+=-DDEBUG_MMC diff --git a/docs/Targets.md b/docs/Targets.md index 49d4c1e453..43a18f2496 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -796,7 +796,7 @@ The PolarFire SoC is a 64-bit RISC-V SoC featuring a five-core CPU cluster (1× `hal/mpfs250.c` - Hardware abstraction layer implementation (UART and uSD) `hal/mpfs250.h` - Register definitions and hardware interfaces -`hal/mpfs250.ld` - Linker script for the platform +`hal/mpfs250.ld` - Linker script fopolarr the platform `hal/mpfs.dts` - Device tree source `hal/mpfs.yaml` - HSS payload generator configuration `hal/mpfs250.its` - Example FIT image creation template @@ -982,7 +982,7 @@ sudo dd if=wolfboot.bin of=/dev/sdc1 bs=512 && sudo cmp wolfboot.bin /dev/sdc1 # Extract GZIP compressed linux kernel to wolfboot root gzip -cdvk ../yocto-dev-polarfire/build/tmp-glibc/work/mpfs_video_kit-oe-linux/linux-mchp/6.12.22+git/build/linux.bin > kernel.bin -# Create custom FIT image +# Create custom FIT ima.itsge mkimage -f hal/mpfs250.its fitImage FIT description: PolarFire SoC MPFS250T Created: Tue Dec 23 11:29:02 2025 @@ -1062,10 +1062,89 @@ printf "0123456789abcdef0123456789abcdef0123456789abcdef" > /tmp/enc_key.der The result is `fitImage_v1_signed_and_encrypted.bin`, which gets placed into your OFP_A or OFP_B partitions. +```sh +sudo dd if=fitImage_v1_signed_and_encrypted.bin of=/dev/sdd2 bs=512 status=progress && sudo cmp fitImage_v1_signed_and_encrypted.bin /dev/sdd2 +sudo dd if=fitImage_v1_signed_and_encrypted.bin of=/dev/sdd3 bs=512 status=progress && sudo cmp fitImage_v1_signed_and_encrypted.bin /dev/sdd3 +``` + During boot, wolfBoot decrypts the image headers from disk to select the best candidate, loads and decrypts the full image to RAM, then verifies integrity and authenticity before booting. On successful boot, `wolfBoot_success()` clears the key from RAM. See the [Encrypted Partitions](encrypted_partitions.md) documentation for additional details. +### PolarFire SoC with PQC (ML-DSA) + +#### Configuration + +Update your `.config` file with the following ML-DSA settings: + +```makefile +# ML-DSA 87 (Category 5) +SIGN=ML_DSA +HASH=SHA256 +ML_DSA_LEVEL=5 +IMAGE_SIGNATURE_SIZE=4627 +IMAGE_HEADER_SIZE=12288 +WOLFBOOT_SECTOR_SIZE?=0x4000 +``` + +**Important:** The `sign` tool requires `IMAGE_HEADER_SIZE` to be set as an environment variable, even if it's already configured in `.config`. This is because the sign tool reads the environment variable separately to determine the header size for padding. Without this, the sign tool may use a smaller default header size, causing a mismatch with wolfBoot's expected header size. + +#### Signing and Encryption + +```sh +# Sign and Encrypt with PQ ML-DSA 5 (87) +# NOTE: IMAGE_HEADER_SIZE must match the value in .config +IMAGE_HEADER_SIZE=12288 ML_DSA_LEVEL=5 ./tools/keytools/sign --ml_dsa --sha256 --aes256 --encrypt /tmp/enc_key.der \ + fitImage wolfboot_signing_private_key.der 1 +``` + +**ML-DSA Parameter Reference:** + +| ML_DSA_LEVEL | Security Category | Signature Size | Private Key | Public Key | Recommended IMAGE_HEADER_SIZE | +|--------------|-------------------|----------------|-------------|------------|------------------------------| +| 2 | Category 2 | 2420 | 2560 | 1312 | 8192 | +| 3 | Category 3 | 3309 | 4032 | 1952 | 8192 | +| 5 | Category 5 | 4627 | 4896 | 2592 | 12288 | + +For other ML-DSA levels, adjust `ML_DSA_LEVEL`, `IMAGE_SIGNATURE_SIZE`, and `IMAGE_HEADER_SIZE` accordingly in both `.config` and the signing command. + +### PolarFire Performance Comparison + +#### Binary Size Comparison + +The following table compares wolfBoot binary sizes for different signature algorithms on PolarFire SoC (MPFS250): + +| Algorithm | Hash | Text | Data | BSS | Total | Binary Size | +|-----------|------|------|------|-----|-------|-------------| +| ECC384 | SHA384 | 67.1 KB | 8 B | 3.0 KB | 70.2 KB | 68 KB | +| ML-DSA 87 | SHA256 | 63.9 KB | 0 B | 14.5 KB | 78.4 KB | 64 KB | + +**Observations:** +- **Binary Size:** ML-DSA 87 produces a smaller binary (64 KB vs 68 KB) due to optimized post-quantum code paths +- **Memory Usage:** ML-DSA 87 uses more BSS (14.5 KB vs 3.0 KB) for signature verification buffers +- **Total Size:** ML-DSA 87 has a larger total memory footprint (78.4 KB vs 70.2 KB) primarily due to BSS requirements +- **Header Size:** ML-DSA 87 requires `IMAGE_HEADER_SIZE=12288` compared to ECC384's `IMAGE_HEADER_SIZE=512` + +#### Boot Time Comparison + +Boot time measurements on PolarFire SoC (RISC-V 64-bit U54 @ 625 MHz) for a 19MB encrypted FIT image: + +| Algorithm | Hash | Load Time | Decrypt Time | Integrity Check | Signature Verify | Total Boot Time | +|-----------|------|-----------|--------------|-----------------|------------------|-----------------| +| ECC384 | SHA384 | ~800 ms | ~2900 ms | ~1500 ms | ~70 ms | ~5.3 seconds | +| ML-DSA 87 | SHA256 | ~835 ms | ~2900 ms | ~2100 ms | ~22 ms | ~5.9 seconds | + +**Observations:** +- **Loading and Decryption:** Similar performance (~835 ms load, ~2900 ms decrypt) as these operations are independent of the signature algorithm. +- **Integrity Check:** ML-DSA 87 takes longer (~2100 ms vs ~1500 ms) due to SHA256 processing a larger header (12288 bytes vs 512 bytes). +- **Signature Verification:** ML-DSA 87 is significantly faster (~22 ms vs ~70 ms) due to efficient post-quantum signature verification. +- **Overall:** ML-DSA 87 adds approximately 600 ms to total boot time, primarily due to the larger header size requiring more hashing. + +**Configuration Notes:** +- ML-DSA 87 requires `IMAGE_HEADER_SIZE=12288` and `WOLFBOOT_SECTOR_SIZE=0x4000` +- ECC384 uses `IMAGE_HEADER_SIZE=512` and `WOLFBOOT_SECTOR_SIZE=0x1000` +- Both configurations support AES-256 encryption with similar decryption performance + ### PolarFire Soc Debugging Start GDB server: @@ -1153,48 +1232,80 @@ FDT: MAC1 = 00:04:A3:5B:22:89 RISC-V 64-bit U54 (RV64GC1) 625 MHz ``` +./configure --enable-riscv-asm --enable-dilithium --enable-mlkem --enable-sp=yes +make +./wolfcrypt/benchmark/benchmark ------------------------------------------------------------------------------ wolfSSL version 5.8.4 ------------------------------------------------------------------------------ Math: Multi-Precision: Wolf(SP) word-size=64 bits=3072 sp_int.c + Single Precision: ecc 256 rsa/dh 2048 3072 sp_c64.c Assembly Speedups: RISCVASM ALIGN wolfCrypt Benchmark (block bytes 1048576, min 1.0 sec each) -RNG 5 MiB took 1.232 seconds, 4.058 MiB/s -AES-128-CBC-enc 10 MiB took 1.182 seconds, 8.457 MiB/s -AES-128-CBC-dec 10 MiB took 1.166 seconds, 8.573 MiB/s -AES-192-CBC-enc 10 MiB took 1.378 seconds, 7.257 MiB/s -AES-192-CBC-dec 10 MiB took 1.362 seconds, 7.344 MiB/s -AES-256-CBC-enc 10 MiB took 1.569 seconds, 6.373 MiB/s -AES-256-CBC-dec 10 MiB took 1.556 seconds, 6.426 MiB/s -AES-128-GCM-enc 10 MiB took 1.956 seconds, 5.113 MiB/s -AES-128-GCM-dec 10 MiB took 1.955 seconds, 5.115 MiB/s -AES-192-GCM-enc 5 MiB took 1.075 seconds, 4.650 MiB/s -AES-192-GCM-dec 5 MiB took 1.074 seconds, 4.654 MiB/s -AES-256-GCM-enc 5 MiB took 1.172 seconds, 4.268 MiB/s -AES-256-GCM-dec 5 MiB took 1.170 seconds, 4.275 MiB/s -GMAC Table 4-bit 15 MiB took 1.133 seconds, 13.245 MiB/s -CHACHA 20 MiB took 1.107 seconds, 18.064 MiB/s -CHA-POLY 15 MiB took 1.060 seconds, 14.152 MiB/s -POLY1305 75 MiB took 1.044 seconds, 71.812 MiB/s -SHA 20 MiB took 1.139 seconds, 17.561 MiB/s -SHA-256 10 MiB took 1.069 seconds, 9.350 MiB/s -SHA-384 15 MiB took 1.072 seconds, 13.994 MiB/s -SHA-512 15 MiB took 1.072 seconds, 13.990 MiB/s -SHA-512/224 15 MiB took 1.068 seconds, 14.041 MiB/s -SHA-512/256 15 MiB took 1.066 seconds, 14.070 MiB/s -HMAC-SHA 20 MiB took 1.140 seconds, 17.542 MiB/s -HMAC-SHA256 10 MiB took 1.068 seconds, 9.366 MiB/s -HMAC-SHA384 15 MiB took 1.066 seconds, 14.076 MiB/s -HMAC-SHA512 15 MiB took 1.066 seconds, 14.077 MiB/s -PBKDF2 1 KiB took 1.024 seconds, 1.129 KiB/s -RSA 2048 public 800 ops took 1.142 sec, avg 1.427 ms, 700.575 ops/sec -RSA 2048 private 100 ops took 8.450 sec, avg 84.504 ms, 11.834 ops/sec -DH 2048 key gen 60 ops took 1.010 sec, avg 16.841 ms, 59.379 ops/sec -DH 2048 agree 100 ops took 3.421 sec, avg 34.211 ms, 29.231 ops/sec -ECC [ SECP256R1] 256 key gen 100 ops took 1.304 sec, avg 13.039 ms, 76.691 ops/sec -ECDHE [ SECP256R1] 256 agree 100 ops took 1.299 sec, avg 12.992 ms, 76.970 ops/sec -ECDSA [ SECP256R1] 256 sign 100 ops took 1.338 sec, avg 13.383 ms, 74.723 ops/sec -ECDSA [ SECP256R1] 256 verify 200 ops took 1.846 sec, avg 9.231 ms, 108.333 ops/sec +RNG 5 MiB took 1.225 seconds, 4.081 MiB/s +AES-128-CBC-enc 10 MiB took 1.179 seconds, 8.478 MiB/s +AES-128-CBC-dec 10 MiB took 1.164 seconds, 8.589 MiB/s +AES-192-CBC-enc 10 MiB took 1.373 seconds, 7.281 MiB/s +AES-192-CBC-dec 10 MiB took 1.360 seconds, 7.354 MiB/s +AES-256-CBC-enc 10 MiB took 1.565 seconds, 6.389 MiB/s +AES-256-CBC-dec 10 MiB took 1.550 seconds, 6.451 MiB/s +AES-128-GCM-enc 10 MiB took 1.940 seconds, 5.156 MiB/s +AES-128-GCM-dec 10 MiB took 1.938 seconds, 5.159 MiB/s +AES-192-GCM-enc 5 MiB took 1.068 seconds, 4.680 MiB/s +AES-192-GCM-dec 5 MiB took 1.066 seconds, 4.689 MiB/s +AES-256-GCM-enc 5 MiB took 1.163 seconds, 4.298 MiB/s +AES-256-GCM-dec 5 MiB took 1.163 seconds, 4.301 MiB/s +GMAC Table 4-bit 15 MiB took 1.106 seconds, 13.566 MiB/s +CHACHA 20 MiB took 1.107 seconds, 18.068 MiB/s +CHA-POLY 15 MiB took 1.058 seconds, 14.178 MiB/s +POLY1305 75 MiB took 1.036 seconds, 72.387 MiB/s +SHA 20 MiB took 1.141 seconds, 17.535 MiB/s +SHA-256 10 MiB took 1.071 seconds, 9.336 MiB/s +SHA-384 15 MiB took 1.066 seconds, 14.068 MiB/s +SHA-512 15 MiB took 1.066 seconds, 14.070 MiB/s +SHA-512/224 15 MiB took 1.067 seconds, 14.060 MiB/s +SHA-512/256 15 MiB took 1.070 seconds, 14.023 MiB/s +SHA3-224 15 MiB took 1.328 seconds, 11.292 MiB/s +SHA3-256 15 MiB took 1.398 seconds, 10.731 MiB/s +SHA3-384 10 MiB took 1.206 seconds, 8.291 MiB/s +SHA3-512 10 MiB took 1.729 seconds, 5.785 MiB/s +SHAKE128 15 MiB took 1.142 seconds, 13.135 MiB/s +SHAKE256 15 MiB took 1.402 seconds, 10.699 MiB/s +HMAC-SHA 20 MiB took 1.145 seconds, 17.470 MiB/s +HMAC-SHA256 10 MiB took 1.074 seconds, 9.310 MiB/s +HMAC-SHA384 15 MiB took 1.076 seconds, 13.944 MiB/s +HMAC-SHA512 15 MiB took 1.069 seconds, 14.036 MiB/s +PBKDF2 1 KiB took 1.023 seconds, 1.130 KiB/s +RSA 2048 public 1000 ops took 1.087 sec, avg 1.087 ms, 920.244 ops/sec +RSA 2048 private 100 ops took 5.410 sec, avg 54.100 ms, 18.484 ops/sec +DH 2048 key gen 48 ops took 1.004 sec, avg 20.920 ms, 47.801 ops/sec +DH 2048 agree 100 ops took 2.087 sec, avg 20.873 ms, 47.909 ops/sec +ECC [ SECP256R1] 256 key gen 800 ops took 1.100 sec, avg 1.375 ms, 727.248 ops/sec +ECDHE [ SECP256R1] 256 agree 300 ops took 1.041 sec, avg 3.470 ms, 288.152 ops/sec +ECDSA [ SECP256R1] 256 sign 600 ops took 1.144 sec, avg 1.907 ms, 524.370 ops/sec +ECDSA [ SECP256R1] 256 verify 300 ops took 1.173 sec, avg 3.909 ms, 255.844 ops/sec +ECC [ SECP384R1] 384 key gen 100 ops took 3.887 sec, avg 38.867 ms, 25.729 ops/sec +ECDHE [ SECP384R1] 384 agree 100 ops took 3.883 sec, avg 38.827 ms, 25.755 ops/sec +ECDSA [ SECP384R1] 384 sign 100 ops took 3.948 sec, avg 39.485 ms, 25.326 ops/sec +ECDSA [ SECP384R1] 384 verify 100 ops took 2.619 sec, avg 26.190 ms, 38.183 ops/sec +ML-KEM 512 128 key gen 2000 ops took 1.021 sec, avg 0.511 ms, 1958.111 ops/sec +ML-KEM 512 128 encap 1700 ops took 1.006 sec, avg 0.592 ms, 1690.275 ops/sec +ML-KEM 512 128 decap 1300 ops took 1.075 sec, avg 0.827 ms, 1209.214 ops/sec +ML-KEM 768 192 key gen 1200 ops took 1.035 sec, avg 0.863 ms, 1158.970 ops/sec +ML-KEM 768 192 encap 1100 ops took 1.092 sec, avg 0.993 ms, 1006.925 ops/sec +ML-KEM 768 192 decap 800 ops took 1.055 sec, avg 1.319 ms, 758.026 ops/sec +ML-KEM 1024 256 key gen 800 ops took 1.124 sec, avg 1.405 ms, 711.862 ops/sec +ML-KEM 1024 256 encap 700 ops took 1.090 sec, avg 1.557 ms, 642.343 ops/sec +ML-KEM 1024 256 decap 600 ops took 1.181 sec, avg 1.968 ms, 508.073 ops/sec +ML-DSA 44 key gen 600 ops took 1.107 sec, avg 1.844 ms, 542.217 ops/sec +ML-DSA 44 sign 200 ops took 1.144 sec, avg 5.719 ms, 174.842 ops/sec +ML-DSA 44 verify 600 ops took 1.146 sec, avg 1.910 ms, 523.569 ops/sec +ML-DSA 65 key gen 400 ops took 1.267 sec, avg 3.167 ms, 315.744 ops/sec +ML-DSA 65 sign 200 ops took 1.687 sec, avg 8.436 ms, 118.543 ops/sec +ML-DSA 65 verify 400 ops took 1.272 sec, avg 3.180 ms, 314.428 ops/sec +ML-DSA 87 key gen 200 ops took 1.066 sec, avg 5.331 ms, 187.588 ops/sec +ML-DSA 87 sign 100 ops took 1.162 sec, avg 11.617 ms, 86.084 ops/sec +ML-DSA 87 verify 200 ops took 1.077 sec, avg 5.385 ms, 185.704 ops/sec Benchmark complete ``` diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 2137cebf7f..a759467ebf 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -54,6 +54,19 @@ void hal_init(void) { wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); + +#if defined(DISK_SDCARD) || defined(DISK_EMMC) + /* Initialize SDHCI for SDCard or eMMC */ + if (sdhci_init() != 0) { + wolfBoot_printf("SDHCI initialization failed\n"); + } else { +#ifdef DISK_EMMC + wolfBoot_printf("eMMC initialized successfully\n"); +#else + wolfBoot_printf("SDCard initialized successfully\n"); +#endif + } +#endif } /* ============================================================================ diff --git a/include/user_settings.h b/include/user_settings.h index 26a08f276b..7d63ff38a2 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -296,6 +296,7 @@ extern int tolower(int c); # define WOLFSSL_SHA3 # define WOLFSSL_SHAKE256 # define WOLFSSL_SHAKE128 +# define WOLFSSL_SP_NO_DYN_STACK #endif /* WOLFBOOT_SIGN_ML_DSA || WOLFBOOT_SIGN_SECONDARY_ML_DSA */ #ifdef WOLFBOOT_HASH_SHA3_384 diff --git a/options.mk b/options.mk index d55dd160c4..50225e2793 100644 --- a/options.mk +++ b/options.mk @@ -409,6 +409,7 @@ ifeq ($(SIGN),ML_DSA) KEYGEN_OPTIONS+=--ml_dsa SIGN_OPTIONS+=--ml_dsa WOLFCRYPT_OBJS+= $(ML_DSA_OBJS) + WOLFCRYPT_OBJS+=$(MATH_OBJS) CFLAGS+=-D"WOLFBOOT_SIGN_ML_DSA" $(ML_DSA_EXTRA) ifneq ($(HASH),SHA3) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o @@ -417,7 +418,7 @@ ifeq ($(SIGN),ML_DSA) ifeq ($(WOLFBOOT_SMALL_STACK),1) $(error WOLFBOOT_SMALL_STACK with ML-DSA not supported yet) else - STACK_USAGE=19544 + STACK_USAGE=25000 endif endif diff --git a/src/image.c b/src/image.c index 7e301e59f3..b1bc5383b3 100644 --- a/src/image.c +++ b/src/image.c @@ -697,6 +697,7 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot, int verify_res = 0; wolfBoot_printf("info: ML-DSA wolfBoot_verify_signature\n"); + wolfBoot_printf("info: using ML-DSA parameters: %d\n", ML_DSA_LEVEL); #if !defined WOLFBOOT_ENABLE_WOLFHSM_CLIENT || \ (defined WOLFBOOT_ENABLE_WOLFHSM_CLIENT && \ @@ -726,6 +727,11 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot, if (ret != 0) { wolfBoot_printf("error: wc_MlDsaKey_SetParams(%d)" \ " returned %d\n", ML_DSA_LEVEL, ret); + } else { + ret = wc_MlDsaKey_GetSigLen(&ml_dsa, &sig_len); + if (ret == 0 && sig_len > 0) { + wolfBoot_printf("info: ML-DSA signature size: %d\n", sig_len); + } } } @@ -766,6 +772,9 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot, "max %d\n", pub_len, KEYSTORE_PUBKEY_SIZE); ret = -1; } + else { + wolfBoot_printf("info: ml-dsa pub len: %d\n", pub_len); + } } if (ret == 0) { @@ -784,7 +793,7 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot, ret = wc_MlDsaKey_GetSigLen(&ml_dsa, &sig_len); if (ret != 0 || sig_len <= 0) { - wolfBoot_printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret); + wolfBoot_printf("error: wc_MlDsaKey_GetSigLen returned %d\n", ret); ret = -1; } else if (sig_len != ML_DSA_IMAGE_SIGNATURE_SIZE) { @@ -795,13 +804,15 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot, } 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, ML_DSA_IMAGE_SIGNATURE_SIZE, - img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, - &verify_res); + if (img->sha_hash == NULL) { + wolfBoot_printf("error: Image hash not available\n"); + ret = -1; + } else { + /* Finally verify signagure. */ + ret = wc_MlDsaKey_Verify(&ml_dsa, sig, ML_DSA_IMAGE_SIGNATURE_SIZE, + img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, + &verify_res); + } #ifdef WOLFBOOT_ARMORED if (ret == 0) { @@ -958,13 +969,17 @@ static int header_sha256(wc_Sha256 *sha256_ctx, struct wolfBoot_image *img) uint16_t stored_sha_len; uint8_t *p; int blksz; + uint32_t header_bytes_hashed = 0; if (!img) return -1; p = get_img_hdr(img); stored_sha_len = get_header(img, HDR_SHA256, &stored_sha); - if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) + if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) { + wolfBoot_printf("error: header_sha256: stored_sha_len=%d, expected=%d\n", + stored_sha_len, WOLFBOOT_SHA_DIGEST_SIZE); return -1; + } #ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT (void)wc_InitSha256_ex(sha256_ctx, NULL, hsmDevIdHash); #else @@ -976,6 +991,7 @@ static int header_sha256(wc_Sha256 *sha256_ctx, struct wolfBoot_image *img) if (end_sha - p < blksz) blksz = end_sha - p; wc_Sha256Update(sha256_ctx, p, blksz); + header_bytes_hashed += blksz; p += blksz; } return 0; @@ -999,8 +1015,11 @@ static int image_sha256(struct wolfBoot_image *img, uint8_t *hash) return -1; do { p = get_sha_block(img, position); - if (p == NULL) + if (p == NULL) { + wolfBoot_printf("error: image_sha256: get_sha_block returned NULL at position %lu\n", + (unsigned long)position); break; + } blksz = WOLFBOOT_SHA_BLOCK_SIZE; if (position + blksz > img->fw_size) blksz = img->fw_size - position; @@ -1448,13 +1467,23 @@ int wolfBoot_verify_integrity(struct wolfBoot_image *img) { uint8_t *stored_sha; uint16_t stored_sha_len; + + stored_sha_len = get_header(img, WOLFBOOT_SHA_HDR, &stored_sha); - if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) + if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) { + wolfBoot_printf("error: stored SHA length mismatch: got %d, expected %d\n", + stored_sha_len, WOLFBOOT_SHA_DIGEST_SIZE); return -1; - if (image_hash(img, digest) != 0) + } + + if (image_hash(img, digest) != 0) { + wolfBoot_printf("error: failed to calculate image hash\n"); return -1; - if (memcmp(digest, stored_sha, stored_sha_len) != 0) + } + if (memcmp(digest, stored_sha, stored_sha_len) != 0) { + wolfBoot_printf("error: Image hash mismatch - integrity check failed\n"); return -1; + } img->sha_ok = 1; img->sha_hash = stored_sha; return 0; diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 6f573d8271..bbe7d96712 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -62,7 +62,7 @@ #include /* for size_t */ -#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) +#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST) || defined(MMU)) #include "encrypt.h" static int encrypt_initialized = 0; @@ -1376,7 +1376,7 @@ int wolfBoot_fallback_is_possible(void) static uint8_t ENCRYPT_KEY[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; #endif -#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) +#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST) || defined(MMU)) int RAMFUNCTION wolfBoot_enable_fallback_iv(int enable) { int prev = 0; @@ -1408,7 +1408,7 @@ void RAMFUNCTION wolfBoot_crypto_set_iv(const uint8_t *nonce, uint32_t iv_counte /* Fallback IV offset is single-use; clear it once applied. */ encrypt_iv_offset = 0; } -#endif /* EXT_ENCRYPTED && (__WOLFBOOT || UNIT_TEST) */ +#endif /* EXT_ENCRYPTED && (__WOLFBOOT || UNIT_TEST || MMU) */ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) { diff --git a/src/update_disk.c b/src/update_disk.c index 614eedf0e7..90b1267014 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -41,6 +41,7 @@ #include "hal.h" #include "spi_flash.h" #include "printf.h" + #include "wolfboot/wolfboot.h" #include "disk.h" #ifdef WOLFBOOT_ELF @@ -93,11 +94,64 @@ static uint8_t disk_encrypt_nonce[ENCRYPT_NONCE_SIZE]; #define DISK_BLOCK_SIZE 512 #endif +#ifdef DEBUG_UART +/** + * Print the signing algorithm and hash algorithm used in this build + */ +static inline void print_signing_hash_info(void) +{ + const char *alg_name = "Unknown"; + const char *hash_name = "Unknown"; + + #ifdef WOLFBOOT_SIGN_ED25519 + alg_name = "ED25519"; + #elif defined(WOLFBOOT_SIGN_ED448) + alg_name = "ED448"; + #elif defined(WOLFBOOT_SIGN_ECC256) + alg_name = "ECC256"; + #elif defined(WOLFBOOT_SIGN_ECC384) + alg_name = "ECC384"; + #elif defined(WOLFBOOT_SIGN_ECC521) + alg_name = "ECC521"; + #elif defined(WOLFBOOT_SIGN_RSA2048) + alg_name = "RSA2048"; + #elif defined(WOLFBOOT_SIGN_RSA3072) + alg_name = "RSA3072"; + #elif defined(WOLFBOOT_SIGN_RSA4096) + alg_name = "RSA4096"; + #elif defined(WOLFBOOT_SIGN_LMS) + alg_name = "LMS"; + #elif defined(WOLFBOOT_SIGN_XMSS) + alg_name = "XMSS"; + #elif defined(WOLFBOOT_SIGN_ML_DSA) + alg_name = "ML-DSA"; + #endif + + #ifdef WOLFBOOT_HASH_SHA256 + hash_name = "SHA256"; + #elif defined(WOLFBOOT_HASH_SHA384) + hash_name = "SHA384"; + #elif defined(WOLFBOOT_HASH_SHA512) + hash_name = "SHA512"; + #elif defined(WOLFBOOT_HASH_SHA3_384) + hash_name = "SHA3-384"; + #endif + + wolfBoot_printf("Signing algorithm: %s, Hash algorithm: %s\n", alg_name, hash_name); +} +#endif /* DEBUG_UART */ + #ifdef DISK_ENCRYPT /* Module-level storage for encryption key */ static uint8_t disk_encrypt_key[ENCRYPT_KEY_SIZE]; +/* AES context for disk encryption/decryption */ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) +static Aes disk_aes_dec; +static int disk_aes_initialized = 0; +#endif + /** * @brief Get the version from an already-decrypted header. * @@ -151,32 +205,39 @@ static uint32_t get_decrypted_blob_version(uint8_t *hdr) * * @param block_offset Block offset for IV counter (0 = start of image). */ +static int disk_crypto_init(void) +{ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + if (disk_aes_initialized) + return 0; + + wc_AesInit(&disk_aes_dec, NULL, INVALID_DEVID); + /* Set key with IV directly - matches sign.c behavior. + * sign.c reads the IV from the key file and passes it directly to + * wc_AesSetKeyDirect. The IV is already in the correct format (16 bytes). + * wc_AesCtrEncrypt will handle counter increment internally. */ + wc_AesSetKeyDirect(&disk_aes_dec, disk_encrypt_key, ENCRYPT_KEY_SIZE, + disk_encrypt_nonce, AES_ENCRYPTION); + disk_aes_initialized = 1; + return 0; +#elif defined(ENCRYPT_WITH_CHACHA) + /* ChaCha initialization handled elsewhere */ + return 0; +#else + return -1; +#endif +} + static void disk_crypto_set_iv(uint32_t block_offset) { #if defined(ENCRYPT_WITH_CHACHA) wc_Chacha_SetIV(&chacha, disk_encrypt_nonce, block_offset); #elif defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) - /* For AES CTR, we need to construct the IV with the counter. - * The sign tool uses the IV directly without byte-reversal, - * so we must match that behavior here. */ - uint8_t iv[ENCRYPT_BLOCK_SIZE]; - uint32_t ctr; - - /* Copy nonce/IV (first 12 bytes for CTR nonce, last 4 for counter) */ - memcpy(iv, disk_encrypt_nonce, ENCRYPT_NONCE_SIZE); - - /* Add block offset to the counter portion (last 4 bytes, big-endian) */ - /* The IV from sign.c is already in the correct format, we just need - * to add the block offset to the counter portion */ - ctr = ((uint32_t)iv[12] << 24) | ((uint32_t)iv[13] << 16) | - ((uint32_t)iv[14] << 8) | (uint32_t)iv[15]; - ctr += block_offset; - iv[12] = (uint8_t)(ctr >> 24); - iv[13] = (uint8_t)(ctr >> 16); - iv[14] = (uint8_t)(ctr >> 8); - iv[15] = (uint8_t)(ctr); - - wc_AesSetIV(&aes_dec, iv); + /* For AES CTR mode, we don't need to reset the IV here. + * The IV was already set in disk_crypto_init() via wc_AesSetKeyDirect. + * wc_AesCtrEncrypt handles counter increment internally. + * This function is kept for compatibility but does nothing for AES. */ + (void)block_offset; #endif } @@ -195,11 +256,27 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst) { uint32_t magic; - /* Reset IV to start of image (block 0) */ + /* Reset AES context for header decryption. + * Each partition's header is encrypted starting from counter 0, + * so we need to reset the context before each header decryption. */ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + /* Reinitialize AES context to reset counter to IV from key file */ + wc_AesInit(&disk_aes_dec, NULL, INVALID_DEVID); + wc_AesSetKeyDirect(&disk_aes_dec, disk_encrypt_key, ENCRYPT_KEY_SIZE, + disk_encrypt_nonce, AES_ENCRYPTION); +#elif defined(ENCRYPT_WITH_CHACHA) + /* Initialize disk crypto if not already done */ + if (disk_crypto_init() != 0) + return -1; disk_crypto_set_iv(0); +#endif /* Decrypt header - CTR mode handles counter increment internally */ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + wc_AesCtrEncrypt(&disk_aes_dec, dst, src, IMAGE_HEADER_SIZE); +#elif defined(ENCRYPT_WITH_CHACHA) crypto_decrypt(dst, src, IMAGE_HEADER_SIZE); +#endif magic = *((uint32_t*)dst); if (magic != WOLFBOOT_MAGIC) @@ -220,11 +297,37 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst) */ static int decrypt_image(uint8_t *data, uint32_t size) { - /* Reset IV to start of image (block 0) */ + uint32_t firmware_size = size - IMAGE_HEADER_SIZE; + + /* Reset AES context for full image decryption. + * sign.c encrypts the entire file (header + firmware) as one continuous stream + * starting from counter 0. We need to decrypt the header first to advance + * the counter, then decrypt the firmware. */ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + /* Reinitialize AES context to reset counter to IV from key file */ + wc_AesInit(&disk_aes_dec, NULL, INVALID_DEVID); + wc_AesSetKeyDirect(&disk_aes_dec, disk_encrypt_key, ENCRYPT_KEY_SIZE, + disk_encrypt_nonce, AES_ENCRYPTION); +#elif defined(ENCRYPT_WITH_CHACHA) + /* Initialize disk crypto if not already done */ + if (disk_crypto_init() != 0) + return -1; disk_crypto_set_iv(0); +#endif - /* Decrypt entire image - CTR mode handles counter increment internally */ + /* Decrypt header first to advance counter (even though we already have it decrypted, + * we need to advance the counter to the correct position for firmware) */ +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + /* Decrypt header to advance counter - we'll overwrite with correct decrypted header later */ + wc_AesCtrEncrypt(&disk_aes_dec, data, data, IMAGE_HEADER_SIZE); + + /* Now decrypt firmware starting from where counter left off */ + wc_AesCtrEncrypt(&disk_aes_dec, data + IMAGE_HEADER_SIZE, + data + IMAGE_HEADER_SIZE, firmware_size); +#elif defined(ENCRYPT_WITH_CHACHA) + /* Decrypt entire image */ crypto_decrypt(data, data, size); +#endif return 0; } @@ -292,6 +395,11 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_panic(); } +#ifdef DEBUG_UART + /* Print signing and hash algorithm before loading from disk */ + print_signing_hash_info(); +#endif + wolfBoot_printf("Checking primary OS image in %d,%d...\r\n", BOOT_DISK, BOOT_PART_A); if (disk_part_read(BOOT_DISK, BOOT_PART_A, 0, IMAGE_HEADER_SIZE, p_hdr) diff --git a/test-app/RISCV64-mpfs250.ld b/test-app/RISCV64-mpfs250.ld index 7c6d6104d1..1b3eb7d99f 100644 --- a/test-app/RISCV64-mpfs250.ld +++ b/test-app/RISCV64-mpfs250.ld @@ -67,8 +67,9 @@ SECTIONS } >LSRAM AT> IRAM /* Uninitialized data section */ + /* Mark as NOLOAD so it's not included in binary (zero-initialized at runtime) */ . = ALIGN(8); - .bss : + .bss (NOLOAD) : { /* This is used by the startup in order to initialize the .bss section */ _start_bss = .; /* define a global symbol at bss start */ @@ -83,7 +84,8 @@ SECTIONS } >LSRAM /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack : + /* Mark as NOLOAD so it's not included in binary (zero-initialized at runtime) */ + ._user_heap_stack (NOLOAD) : { . = ALIGN(8); PROVIDE ( end = . );