diff --git a/benchmarks.md b/benchmarks.md new file mode 100644 index 0000000..544ce4a --- /dev/null +++ b/benchmarks.md @@ -0,0 +1,115 @@ +# Benchmark Results + +All numbers reported here refer to cycle counts. + +## ML-KEM + +### ML-KEM 512 + +| Device | KeyGen[Debug] | KeyGen[Release] | Encaps[Debug] | Encaps[Release] | Decaps[Debug] | Decaps[Release] | +|---------------------------------|---------------|-----------------|---------------|-----------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 4445020 | 771501 | 5044052 | 839800 | 6134089 | 839800 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | 5968523 | 764636 | 6806307 | 839812 | 8316331 | 1011669 | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | 5789064 | 762595 | 6579357 | 838838 | 7998480 | 1010106 | +| - nRF52810 | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | + + +### ML-KEM 768 + +| Device | KeyGen[Debug] | KeyGen[Release] | Encaps[Debug] | Encaps[Release] | Decaps[Debug] | Decaps[Release] | +|---------------------------------|---------------|-----------------|---------------|-----------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 7085443 | 1273471 | 7996675 | 1395000 | 9421249 | 1591459 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | 9315152 | 1257679 | 10418524 | 1381088 | 12319554 | 1607698 | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | 9434994 | 1276466 | 10691113 | 1401091 | 12626256 | 1630202 | +| - nRF52810 | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | + + +### ML-KEM 1024 + +| Device | KeyGen[Debug] | KeyGen[Release] | Encaps[Debug] | Encaps[Release] | Decaps[Debug] | Decaps[Release] | +|---------------------------------|---------------|-----------------|---------------|-----------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 10981246 | 1991691 | 11988292 | 2129442 | 13756274 | 2371826 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | 14_910_284 | 1_959_916 | 16_318_665 | 2_103_371 | 18_830_628 | 2_391_463 | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | 14886756 | 1951395 | 16021505 | 2094650 | 18385162 | 2381655 | +| - nRF52810 | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | + + +## ML-DSA +### ML-DSA 44 + +| Device | KeyGen[Debug] | KeyGen[Release] | Sign[Debug] | Sign[Release] | Verify[Debug] | Verify[Release] | +|---------------------------------|---------------|-----------------|-------------|---------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 19524681 | 3476142 | 28743483 | 4825636 | 20929483 | 3695919 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | 25565997 | 3565042 | 37809350 | 4987219 | 27441288 | 3788007 | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | +| - nRF52810 | | | | | | | + +### ML-DSA 65 + +| Device | KeyGen[Debug] | KeyGen[Release] | Sign[Debug] | Sign[Release] | Verify[Debug] | Verify[Release] | +|---------------------------------|---------------|-----------------|-------------|---------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 36271715 | 6382246 | 47606417 | 7981373 | 37005196 | 6501876 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | 47073665 | 6638977 | 62247859 | 8364565 | 48188918 | 6767475 | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | ❌ [^5] | ❌ [^6] | ❌ [^5] | ❌ [^6] | ❌ [^5] | ❌ [^6] | +| - nRF52810 | | | | | | | + + +### ML-DSA 87 + +| Device | KeyGen[Debug] | KeyGen[Release] | Sign[Debug] | Sign[Release] | Verify[Debug] | Verify[Release] | +|---------------------------------|---------------|-----------------|-------------|---------------|---------------|-----------------| +| Raspberry Pi 3 | | | | | | | +| Raspberry Pi 4 | | | | | | | +| [ESP32-S3] [^1] [^2] | | | | | | | +| [STM32-L4R5xx] (our Nucleo-144) | 60053867 | 10633875 | 137335754 | 21909905 | 61676143 | 10794553 | +| [ESP32-C6] [^3] | | | | | | | +| [nRF52840-DK] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | ❌ [^5] | ❌ [^4] | +| [nRF5340-DK] | | | | | | | +| [nRF52-DK] | | | | | | | +| - nRF52832 | ❌ [^5] | ❌ [^6] | ❌ [^5] | ❌ [^6] | ❌ [^5] | ❌ [^6] | +| - nRF52810 | | | | | | | + + +[STM32-L4R5xx]: https://www.st.com/en/microcontrollers-microprocessors/stm32l4r5zi.html?rt=db&id=DB3171 +[ESP32-C6]: https://www.espressif.com/en/products/socs/esp32-c6 +[STM32-L476RG]: https://www.st.com/en/microcontrollers-microprocessors/stm32l476rg.html?rt=db&id=DB2196 +[nRF52840-DK]: https://www.nordicsemi.com/Products/nRF52840 +[nRF5340-DK]: https://www.nordicsemi.com/Products/nRF5340 +[nRF52-DK]: https://www.nordicsemi.com/Products/Development-hardware/nRF52-DK +[esp32-s3]: https://www.espressif.com/en/products/socs/esp32-s3 +[^1]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html +[^2]: https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf +[^3]: https://www.espressif.com/sites/default/files/documentation/esp32-c6-wroom-1_wroom-1u_datasheet_en.pdf +[^4]: The benchmark crashses due to insufficient stack space. +[^5]: Core locks up. +[^6]: Benchmark binary does not fit available flash space. diff --git a/libcrux-nrf52810/.gitignore b/libcrux-nrf52810/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/libcrux-nrf52810/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/libcrux-nrf52810/Cargo.toml b/libcrux-nrf52810/Cargo.toml new file mode 100644 index 0000000..49d8de3 --- /dev/null +++ b/libcrux-nrf52810/Cargo.toml @@ -0,0 +1,76 @@ +[package] +authors = ["Jonas Schneider-Bensch "] +name = "libcrux-nrf52810" +edition = "2021" +version = "0.1.0" + +[lib] +harness = false + +# needed for each integration test +[[test]] +name = "integration" +harness = false + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7" +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m-semihosting = "0.5.0" +libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } +libcrux-testbench = { path = "../libcrux-testbench" } +embassy-nrf = { version = "0.1.0", features = [ "nrf52810", "defmt", ] } +embedded-alloc = "0.6.0" + +[dev-dependencies] +defmt-test = "0.3" + +[features] +default = ["mldsa87", "mlkem1024"] # We want to benchmark the most expensive variants by default +mldsa44 = ["libcrux-testbench/mldsa44"] +mldsa65 = ["libcrux-testbench/mldsa65"] +mldsa87 = ["libcrux-testbench/mldsa87"] +mlkem512 = ["libcrux-testbench/mlkem512"] +mlkem768 = ["libcrux-testbench/mlkem768"] +mlkem1024 = ["libcrux-testbench/mlkem1024"] + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 'z' # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + diff --git a/libcrux-nrf52810/README.md b/libcrux-nrf52810/README.md new file mode 100644 index 0000000..d86fd3e --- /dev/null +++ b/libcrux-nrf52810/README.md @@ -0,0 +1,32 @@ +# Libcrux ML-KEM / DSA on nRF52480 + +This setup is based on the app template found at [https://github.com/knurling-rs/app-template]. + +## Dependencies + +#### 1. `flip-link`: + +```console +$ cargo install flip-link +``` + +#### 2. `probe-rs`: + +``` console +$ # make sure to install v0.2.0 or later +$ cargo install probe-rs --features cli +``` + + +## Running Benchmarks + +With the device attached, run +```console +$ cargo rb mlkem --release +``` +for a crude benchmark of ML-KEM 1024 and + +```console +$ cargo rb mldsa_65 --release +``` +for a crude benchmark of ML-DSA 65. diff --git a/libcrux-nrf52810/memory.x b/libcrux-nrf52810/memory.x new file mode 100644 index 0000000..1be105b --- /dev/null +++ b/libcrux-nrf52810/memory.x @@ -0,0 +1,23 @@ +/* Linker script for the nRF52 - WITHOUT SOFT DEVICE */ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 192K + RAM : ORIGIN = 0x20000000, LENGTH = 24K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Size of the heap (in bytes) */ +/* _heap_size = 1024; */ diff --git a/libcrux-nrf52810/src/bin/mldsa.rs b/libcrux-nrf52810/src/bin/mldsa.rs new file mode 100644 index 0000000..4838699 --- /dev/null +++ b/libcrux-nrf52810/src/bin/mldsa.rs @@ -0,0 +1,42 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52810 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mldsa::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf52810/src/bin/mlkem.rs b/libcrux-nrf52810/src/bin/mlkem.rs new file mode 100644 index 0000000..57a36e4 --- /dev/null +++ b/libcrux-nrf52810/src/bin/mlkem.rs @@ -0,0 +1,41 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52810 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mlkem::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf52810/src/lib.rs b/libcrux-nrf52810/src/lib.rs new file mode 100644 index 0000000..10cc03c --- /dev/null +++ b/libcrux-nrf52810/src/lib.rs @@ -0,0 +1,53 @@ +#![no_main] +#![no_std] + +use cortex_m_semihosting::debug; + +use defmt_rtt as _; // global logger + +use embassy_nrf as _; // memory layout + +use panic_probe as _; + +pub const COREFREQ: u32 = 4_000_000; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with status code 0. +pub fn exit() -> ! { + loop { + debug::exit(debug::EXIT_SUCCESS); + } +} + +/// Hardfault handler. +/// +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with an error. This seems better than the default, which is to spin in a +/// loop. +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + loop { + debug::exit(debug::EXIT_FAILURE); + } +} + +// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used +// once within a crate. the module can be in any file but there can only be at most +// one `#[tests]` module in this library crate +#[cfg(test)] +#[defmt_test::tests] +mod unit_tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nrf52810/tests/integration.rs b/libcrux-nrf52810/tests/integration.rs new file mode 100644 index 0000000..013a8a4 --- /dev/null +++ b/libcrux-nrf52810/tests/integration.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +use libcrux_nrf52840 as _; // memory layout + panic handler + +// See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' +// feature) +#[defmt_test::tests] +mod tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nrf52832/.gitignore b/libcrux-nrf52832/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/libcrux-nrf52832/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/libcrux-nrf52832/Cargo.toml b/libcrux-nrf52832/Cargo.toml new file mode 100644 index 0000000..2813266 --- /dev/null +++ b/libcrux-nrf52832/Cargo.toml @@ -0,0 +1,76 @@ +[package] +authors = ["Jonas Schneider-Bensch "] +name = "libcrux-nrf52832" +edition = "2021" +version = "0.1.0" + +[lib] +harness = false + +# needed for each integration test +[[test]] +name = "integration" +harness = false + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7" +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m-semihosting = "0.5.0" +libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } +libcrux-testbench = { path = "../libcrux-testbench" } +embassy-nrf = { version = "0.1.0", features = [ "nrf52832", "defmt", ] } +embedded-alloc = "0.6.0" + +[dev-dependencies] +defmt-test = "0.3" + +[features] +default = ["mldsa87", "mlkem1024"] # We want to benchmark the most expensive variants by default +mldsa44 = ["libcrux-testbench/mldsa44"] +mldsa65 = ["libcrux-testbench/mldsa65"] +mldsa87 = ["libcrux-testbench/mldsa87"] +mlkem512 = ["libcrux-testbench/mlkem512"] +mlkem768 = ["libcrux-testbench/mlkem768"] +mlkem1024 = ["libcrux-testbench/mlkem1024"] + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 'z' # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + diff --git a/libcrux-nrf52832/README.md b/libcrux-nrf52832/README.md new file mode 100644 index 0000000..d86fd3e --- /dev/null +++ b/libcrux-nrf52832/README.md @@ -0,0 +1,32 @@ +# Libcrux ML-KEM / DSA on nRF52480 + +This setup is based on the app template found at [https://github.com/knurling-rs/app-template]. + +## Dependencies + +#### 1. `flip-link`: + +```console +$ cargo install flip-link +``` + +#### 2. `probe-rs`: + +``` console +$ # make sure to install v0.2.0 or later +$ cargo install probe-rs --features cli +``` + + +## Running Benchmarks + +With the device attached, run +```console +$ cargo rb mlkem --release +``` +for a crude benchmark of ML-KEM 1024 and + +```console +$ cargo rb mldsa_65 --release +``` +for a crude benchmark of ML-DSA 65. diff --git a/libcrux-nrf52832/memory.x b/libcrux-nrf52832/memory.x new file mode 100644 index 0000000..917f43d --- /dev/null +++ b/libcrux-nrf52832/memory.x @@ -0,0 +1,23 @@ +/* Linker script for the nRF52 - WITHOUT SOFT DEVICE */ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 512K + RAM : ORIGIN = 0x20000000, LENGTH = 64K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Size of the heap (in bytes) */ +/* _heap_size = 1024; */ diff --git a/libcrux-nrf52832/src/bin/mldsa.rs b/libcrux-nrf52832/src/bin/mldsa.rs new file mode 100644 index 0000000..a2534f6 --- /dev/null +++ b/libcrux-nrf52832/src/bin/mldsa.rs @@ -0,0 +1,42 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52832 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mldsa::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf52832/src/bin/mlkem.rs b/libcrux-nrf52832/src/bin/mlkem.rs new file mode 100644 index 0000000..159bca6 --- /dev/null +++ b/libcrux-nrf52832/src/bin/mlkem.rs @@ -0,0 +1,41 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52832 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mlkem::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf52832/src/lib.rs b/libcrux-nrf52832/src/lib.rs new file mode 100644 index 0000000..10cc03c --- /dev/null +++ b/libcrux-nrf52832/src/lib.rs @@ -0,0 +1,53 @@ +#![no_main] +#![no_std] + +use cortex_m_semihosting::debug; + +use defmt_rtt as _; // global logger + +use embassy_nrf as _; // memory layout + +use panic_probe as _; + +pub const COREFREQ: u32 = 4_000_000; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with status code 0. +pub fn exit() -> ! { + loop { + debug::exit(debug::EXIT_SUCCESS); + } +} + +/// Hardfault handler. +/// +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with an error. This seems better than the default, which is to spin in a +/// loop. +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + loop { + debug::exit(debug::EXIT_FAILURE); + } +} + +// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used +// once within a crate. the module can be in any file but there can only be at most +// one `#[tests]` module in this library crate +#[cfg(test)] +#[defmt_test::tests] +mod unit_tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nrf52832/tests/integration.rs b/libcrux-nrf52832/tests/integration.rs new file mode 100644 index 0000000..013a8a4 --- /dev/null +++ b/libcrux-nrf52832/tests/integration.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +use libcrux_nrf52840 as _; // memory layout + panic handler + +// See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' +// feature) +#[defmt_test::tests] +mod tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nrf52840/.cargo/config.toml b/libcrux-nrf52840/.cargo/config.toml index 7da95f4..3ed569f 100644 --- a/libcrux-nrf52840/.cargo/config.toml +++ b/libcrux-nrf52840/.cargo/config.toml @@ -1,6 +1,7 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs run --chip nRF52840_xxAA" -# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"] +# runner = "probe-rs run --chip nRF52840_xxAA" +runner = ["probe-rs", "run", "--chip", "nRF52840_xxAA", "--log-format", "{s}"] + rustflags = [ "-C", "linker=flip-link", diff --git a/libcrux-nrf52840/Cargo.toml b/libcrux-nrf52840/Cargo.toml index e42d437..3957715 100644 --- a/libcrux-nrf52840/Cargo.toml +++ b/libcrux-nrf52840/Cargo.toml @@ -19,13 +19,23 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m-semihosting = "0.5.0" -nrf52840-hal = "0.18.0" -libcrux-ml-kem = { path = "../libcrux/libcrux-ml-kem", default-features = false, features = ["pre-verification", "mlkem512", "mlkem768", "mlkem1024"] } -libcrux-ml-dsa = { path = "../libcrux/libcrux-ml-dsa" } +libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } +libcrux-testbench = { path = "../libcrux-testbench" } +embassy-nrf = { version = "0.1.0", features = [ "nrf52840", "defmt", ] } +embedded-alloc = "0.6.0" [dev-dependencies] defmt-test = "0.3" +[features] +default = ["mldsa87", "mlkem1024"] # We want to benchmark the most expensive variants by default +mldsa44 = ["libcrux-testbench/mldsa44"] +mldsa65 = ["libcrux-testbench/mldsa65"] +mldsa87 = ["libcrux-testbench/mldsa87"] +mlkem512 = ["libcrux-testbench/mlkem512"] +mlkem768 = ["libcrux-testbench/mlkem768"] +mlkem1024 = ["libcrux-testbench/mlkem1024"] + # cargo build/run [profile.dev] codegen-units = 1 diff --git a/libcrux-nrf52840/memory.x b/libcrux-nrf52840/memory.x new file mode 100644 index 0000000..70e5006 --- /dev/null +++ b/libcrux-nrf52840/memory.x @@ -0,0 +1,23 @@ +/* Linker script for the nRF52 - WITHOUT SOFT DEVICE */ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Size of the heap (in bytes) */ +/* _heap_size = 1024; */ diff --git a/libcrux-nrf52840/src/bin/mldsa.rs b/libcrux-nrf52840/src/bin/mldsa.rs new file mode 100644 index 0000000..b9d9633 --- /dev/null +++ b/libcrux-nrf52840/src/bin/mldsa.rs @@ -0,0 +1,42 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52840 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mldsa::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf52840/src/bin/mldsa_65.rs b/libcrux-nrf52840/src/bin/mldsa_65.rs deleted file mode 100644 index f0299b2..0000000 --- a/libcrux-nrf52840/src/bin/mldsa_65.rs +++ /dev/null @@ -1,109 +0,0 @@ -#![no_main] -#![no_std] - -use libcrux_ml_dsa::ml_dsa_65; -use libcrux_nrf52840 as _; // global logger + panicking-behavior + memory layout - -use cortex_m::peripheral::{Peripherals, DWT}; - -const KEYGEN_ITERATIONS: usize = 10; -const SIGN_ITERATIONS: usize = 10; -const VERIFY_ITERATIONS: usize = 10; - -fn init_cycle_counter() -> Peripherals { - // Enable tracing - let mut peripherals = Peripherals::take().unwrap(); - peripherals.DCB.enable_trace(); - peripherals.DWT.enable_cycle_counter(); - - peripherals -} - -fn count_cycles( - description: &str, - setup: SetupF, - operation: OpF, - iterations: usize, -) where - SetupF: FnOnce() -> Input, - OpF: Fn(&Input) -> (), -{ - defmt::println!("{=str} ({=usize} times)", description, iterations); - let input = setup(); - let start_measuring = DWT::cycle_count(); - for _ in 0..iterations { - let _ = operation(&input); - } - let end_measuring = DWT::cycle_count(); - let time_avg = (end_measuring - start_measuring) / (iterations as u32); - defmt::println!("Took {=u32} cycles on average", time_avg); -} - -#[cortex_m_rt::entry] -fn main() -> ! { - let _peripherals = init_cycle_counter(); - defmt::println!("Testing that everything works"); - let randomness_gen = [1u8; 32]; - let keypair = ml_dsa_65::generate_key_pair(randomness_gen); - defmt::println!("\tKey Generation OK"); - - let signing_randomness = [4u8; 32]; - let message = [5u8; 1024]; - - let signature = ml_dsa_65::sign(&keypair.signing_key, &message, b"", signing_randomness).unwrap(); - defmt::println!("\tSigning OK"); - - let result = ml_dsa_65::verify(&keypair.verification_key, &message, b"", &signature); - defmt::println!("\tVerification OK"); - - assert!(result.is_ok()); - defmt::println!("\tSuccess!"); - - defmt::println!("Benchmarking"); - count_cycles( - "\tKey Generation", - || { - let randomness_gen = [1u8; 32]; - randomness_gen - }, - |randomness| { - let _pair = ml_dsa_65::generate_key_pair(*randomness); - }, - KEYGEN_ITERATIONS, - ); - - count_cycles( - "\tSigning", - || { - let randomness_gen = [1u8; 32]; - let signing_randomness = [4u8; 32]; - let message = [5u8; 1024]; - - let keypair = ml_dsa_65::generate_key_pair(randomness_gen); - (message, keypair, signing_randomness) - }, - |(message, keypair, signing_randomness)| { - let _ = ml_dsa_65::sign(&keypair.signing_key, message, b"", *signing_randomness); - }, - SIGN_ITERATIONS, - ); - - count_cycles( - "\tVerification", - || { - let randomness_gen = [1u8; 32]; - let signing_randomness = [4u8; 32]; - let message = [5u8; 1024]; - - let keypair = ml_dsa_65::generate_key_pair(randomness_gen); - let signature = ml_dsa_65::sign(&keypair.signing_key, &message, b"", signing_randomness).unwrap(); - (message, keypair, signature) - }, - |(message, keypair, signature)| { - ml_dsa_65::verify(&keypair.verification_key, message, b"", signature).unwrap(); - }, - VERIFY_ITERATIONS, - ); - - libcrux_nrf52840::exit() -} diff --git a/libcrux-nrf52840/src/bin/mlkem.rs b/libcrux-nrf52840/src/bin/mlkem.rs index feadff3..cefd045 100644 --- a/libcrux-nrf52840/src/bin/mlkem.rs +++ b/libcrux-nrf52840/src/bin/mlkem.rs @@ -1,70 +1,41 @@ #![no_main] #![no_std] -use libcrux_ml_kem::mlkem1024 as mlkem; -use libcrux_nrf52840 as _; // global logger + panicking-behavior + memory layout -use nrf52840_hal as hal; +use cortex_m::peripheral::Peripherals; +use libcrux_nrf52840 as board; // global logger + panicking-behavior + memory layout -// Resolution in µs -const RTC_RESOLUTION: f32 = 30.517; +extern crate alloc; -#[cortex_m_rt::entry] -fn main() -> ! { - let board = hal::pac::Peripherals::take().unwrap(); - defmt::println!("Starting main!"); - - let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; - let clock = nrf52840_hal::clocks::Clocks::new(board.CLOCK); - clock.start_lfclk(); - - let rtc = nrf52840_hal::rtc::Rtc::new(board.RTC0, 0).unwrap(); - - defmt::println!("Generating 100 key pairs"); - let counter_start = rtc.get_counter(); - rtc.enable_counter(); - for _ in 0..100 { - let _ = mlkem::generate_key_pair(randomness_gen); - } - rtc.disable_counter(); - let counter_end = rtc.get_counter(); - let counter_keygen_total = counter_end - counter_start; - let counter_keygen_avg = counter_keygen_total as f32/ 100.0; - let time_keygen_avg = counter_keygen_avg * RTC_RESOLUTION; - defmt::println!("Took {=f32} µs to generate key pair on average", time_keygen_avg); +#[global_allocator] +static HEAP: Heap = Heap::empty(); - let pair = mlkem::generate_key_pair(randomness_gen); - let randomness_encaps = [2u8; libcrux_ml_kem::ENCAPS_SEED_SIZE]; - defmt::println!("Encapsulating 100 times."); - let counter_start = rtc.get_counter(); - rtc.enable_counter(); - for _ in 0..100 { - let _ = mlkem::encapsulate(pair.public_key(), randomness_encaps); - } - rtc.disable_counter(); - let counter_end = rtc.get_counter(); - let counter_encaps_total = counter_end - counter_start; - let counter_encaps_avg = counter_encaps_total as f32/ 100.0; - let time_encaps_avg = counter_encaps_avg * RTC_RESOLUTION; - defmt::println!("Took {=f32} µs to encapsulate on average", time_encaps_avg); - - let (ct, ss) = mlkem::encapsulate(pair.public_key(), randomness_encaps); - - defmt::println!("Decapsulating 100 times."); - let counter_start = rtc.get_counter(); - rtc.enable_counter(); - for _ in 0..100 { - let _ = mlkem::decapsulate(pair.private_key(), &ct); +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); } - let counter_end = rtc.get_counter(); - let counter_decaps_total = counter_end - counter_start; - let counter_decaps_avg = counter_decaps_total as f32/ 100.0; - let time_decaps_avg = counter_decaps_avg * RTC_RESOLUTION; - defmt::println!("Took {=f32} µs to decapsulate on average", time_decaps_avg); + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; - let ss_decaps = mlkem::decapsulate(pair.private_key(), &ct); - assert_eq!(ss, ss_decaps); + libcrux_testbench::mlkem::run_benchmarks(test_config); - libcrux_nrf52840::exit() + board::exit() } diff --git a/libcrux-nrf52840/src/lib.rs b/libcrux-nrf52840/src/lib.rs index fd3a0e5..10cc03c 100644 --- a/libcrux-nrf52840/src/lib.rs +++ b/libcrux-nrf52840/src/lib.rs @@ -5,10 +5,12 @@ use cortex_m_semihosting::debug; use defmt_rtt as _; // global logger -use nrf52840_hal as _; // memory layout +use embassy_nrf as _; // memory layout use panic_probe as _; +pub const COREFREQ: u32 = 4_000_000; + // same panicking *behavior* as `panic-probe` but doesn't print a panic message // this prevents the panic message being printed *twice* when `defmt::panic` is invoked #[defmt::panic_handler] diff --git a/libcrux-nrf5340/.gitignore b/libcrux-nrf5340/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/libcrux-nrf5340/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/libcrux-nrf5340/Cargo.toml b/libcrux-nrf5340/Cargo.toml new file mode 100644 index 0000000..0bf1ba6 --- /dev/null +++ b/libcrux-nrf5340/Cargo.toml @@ -0,0 +1,76 @@ +[package] +authors = ["Jonas Schneider-Bensch "] +name = "libcrux-nrf5340" +edition = "2021" +version = "0.1.0" + +[lib] +harness = false + +# needed for each integration test +[[test]] +name = "integration" +harness = false + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7" +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m-semihosting = "0.5.0" +libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } +libcrux-testbench = { path = "../libcrux-testbench" } +embassy-nrf = { version = "0.1.0", features = [ "nrf5340-app-ns", "defmt", ] } +embedded-alloc = "0.6.0" + +[dev-dependencies] +defmt-test = "0.3" + +[features] +default = ["mldsa87", "mlkem1024"] # We want to benchmark the most expensive variants by default +mldsa44 = ["libcrux-testbench/mldsa44"] +mldsa65 = ["libcrux-testbench/mldsa65"] +mldsa87 = ["libcrux-testbench/mldsa87"] +mlkem512 = ["libcrux-testbench/mlkem512"] +mlkem768 = ["libcrux-testbench/mlkem768"] +mlkem1024 = ["libcrux-testbench/mlkem1024"] + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 'z' # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + diff --git a/libcrux-nrf5340/README.md b/libcrux-nrf5340/README.md new file mode 100644 index 0000000..d86fd3e --- /dev/null +++ b/libcrux-nrf5340/README.md @@ -0,0 +1,32 @@ +# Libcrux ML-KEM / DSA on nRF52480 + +This setup is based on the app template found at [https://github.com/knurling-rs/app-template]. + +## Dependencies + +#### 1. `flip-link`: + +```console +$ cargo install flip-link +``` + +#### 2. `probe-rs`: + +``` console +$ # make sure to install v0.2.0 or later +$ cargo install probe-rs --features cli +``` + + +## Running Benchmarks + +With the device attached, run +```console +$ cargo rb mlkem --release +``` +for a crude benchmark of ML-KEM 1024 and + +```console +$ cargo rb mldsa_65 --release +``` +for a crude benchmark of ML-DSA 65. diff --git a/libcrux-nrf5340/memory.x b/libcrux-nrf5340/memory.x new file mode 100644 index 0000000..58900a7 --- /dev/null +++ b/libcrux-nrf5340/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/libcrux-nrf5340/src/bin/mldsa.rs b/libcrux-nrf5340/src/bin/mldsa.rs new file mode 100644 index 0000000..a348af4 --- /dev/null +++ b/libcrux-nrf5340/src/bin/mldsa.rs @@ -0,0 +1,42 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf5340 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mldsa::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf5340/src/bin/mlkem.rs b/libcrux-nrf5340/src/bin/mlkem.rs new file mode 100644 index 0000000..2b739c6 --- /dev/null +++ b/libcrux-nrf5340/src/bin/mlkem.rs @@ -0,0 +1,41 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nrf5340 as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mlkem::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nrf5340/src/lib.rs b/libcrux-nrf5340/src/lib.rs new file mode 100644 index 0000000..10cc03c --- /dev/null +++ b/libcrux-nrf5340/src/lib.rs @@ -0,0 +1,53 @@ +#![no_main] +#![no_std] + +use cortex_m_semihosting::debug; + +use defmt_rtt as _; // global logger + +use embassy_nrf as _; // memory layout + +use panic_probe as _; + +pub const COREFREQ: u32 = 4_000_000; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with status code 0. +pub fn exit() -> ! { + loop { + debug::exit(debug::EXIT_SUCCESS); + } +} + +/// Hardfault handler. +/// +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with an error. This seems better than the default, which is to spin in a +/// loop. +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + loop { + debug::exit(debug::EXIT_FAILURE); + } +} + +// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used +// once within a crate. the module can be in any file but there can only be at most +// one `#[tests]` module in this library crate +#[cfg(test)] +#[defmt_test::tests] +mod unit_tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nrf5340/tests/integration.rs b/libcrux-nrf5340/tests/integration.rs new file mode 100644 index 0000000..013a8a4 --- /dev/null +++ b/libcrux-nrf5340/tests/integration.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +use libcrux_nrf52840 as _; // memory layout + panic handler + +// See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' +// feature) +#[defmt_test::tests] +mod tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/libcrux-nucleo-l4r5zi/Cargo.toml b/libcrux-nucleo-l4r5zi/Cargo.toml index 5ebc337..eedd7fe 100644 --- a/libcrux-nucleo-l4r5zi/Cargo.toml +++ b/libcrux-nucleo-l4r5zi/Cargo.toml @@ -19,16 +19,23 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m-semihosting = "0.5.0" -libcrux-ml-kem = { path = "../libcrux/libcrux-ml-kem", default-features = false, features = [ "pre-verification", "mlkem512", "mlkem768", "mlkem1024", ] } -libcrux-ml-dsa = { path = "../libcrux/libcrux-ml-dsa" } libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } -embassy-time = "0.3.2" -embassy-stm32 = { version = "0.1.0", features = [ "stm32l4r5zi", "defmt", "time", "time-driver-any", ] } +libcrux-testbench = { path = "../libcrux-testbench" } +embassy-stm32 = { version = "0.1.0", features = [ "stm32l4r5zi", "defmt", ] } embedded-alloc = "0.6.0" [dev-dependencies] defmt-test = "0.3" +[features] +default = ["mldsa87", "mlkem1024"] # We want to benchmark the most expensive variants by default +mldsa44 = ["libcrux-testbench/mldsa44"] +mldsa65 = ["libcrux-testbench/mldsa65"] +mldsa87 = ["libcrux-testbench/mldsa87"] +mlkem512 = ["libcrux-testbench/mlkem512"] +mlkem768 = ["libcrux-testbench/mlkem768"] +mlkem1024 = ["libcrux-testbench/mlkem1024"] + # cargo build/run [profile.dev] codegen-units = 1 diff --git a/libcrux-nucleo-l4r5zi/src/bin/mldsa.rs b/libcrux-nucleo-l4r5zi/src/bin/mldsa.rs new file mode 100644 index 0000000..bb32d73 --- /dev/null +++ b/libcrux-nucleo-l4r5zi/src/bin/mldsa.rs @@ -0,0 +1,40 @@ +#![no_main] +#![no_std] + +use cortex_m::peripheral::Peripherals; +use libcrux_nucleo_l4r5zi as board; // global logger + panicking-behavior + memory layout + +extern crate alloc; + +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[cortex_m_rt::entry] +fn main() -> ! { + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mldsa::run_benchmarks(test_config); + + board::exit() +} diff --git a/libcrux-nucleo-l4r5zi/src/bin/mldsa_65.rs b/libcrux-nucleo-l4r5zi/src/bin/mldsa_65.rs deleted file mode 100644 index b8ce00e..0000000 --- a/libcrux-nucleo-l4r5zi/src/bin/mldsa_65.rs +++ /dev/null @@ -1,106 +0,0 @@ -#![no_main] -#![no_std] - -use libcrux_iot_testutil::DefmtInfoLogger; -use libcrux_iot_testutil::*; -extern crate alloc; -use alloc::string::String; -use alloc::vec; - -use cortex_m::peripheral::Peripherals; -use libcrux_ml_dsa::ml_dsa_65; -use libcrux_nucleo_l4r5zi as _; // global logger + panicking-behavior + memory layout - -use core::ptr::addr_of_mut; -use embedded_alloc::LlffHeap as Heap; - -#[global_allocator] -static HEAP: Heap = Heap::empty(); - -struct MLDSABenchState { - randomness_gen: [u8; 32], - keypair: ml_dsa_65::MLDSA65KeyPair, - signing_randomness: [u8; 32], - message: [u8; 1024], - signature: ml_dsa_65::MLDSA65Signature, -} - -fn bench_keygen(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { - let _pair = ml_dsa_65::generate_key_pair(state.randomness_gen); - Ok(()) -} - -fn bench_sign(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { - let _signature = ml_dsa_65::sign( - &state.keypair.signing_key, - &state.message, - b"", - state.signing_randomness, - ); - Ok(()) -} - -fn bench_verify(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { - let _ = ml_dsa_65::verify( - &state.keypair.verification_key, - &state.message, - b"", - &state.signature, - ); - - Ok(()) -} - -#[cortex_m_rt::entry] -fn main() -> ! { - // Initialize the allocator BEFORE you use it - { - use core::mem::MaybeUninit; - const HEAP_SIZE: usize = 1024; - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } - - let mut peripherals = Peripherals::take().unwrap(); - peripherals.DCB.enable_trace(); - peripherals.DWT.enable_cycle_counter(); - } - - // set up the test suite - let test_cases = [ - TestCase::new("bench_keygen", bench_keygen), - TestCase::new("bench_sign", bench_sign), - TestCase::new("bench_verify", bench_verify), - ]; - - let test_suite = TestSuite::new("ml_dsa65", &test_cases); - - // set up the test config - let test_config = TestConfig { - core_freq: 4_000_000, - only_names: vec!["bench_keygen", "bench_sign", "bench_verify"], - early_abort: false, - benchmark_runs: 5, - }; - - // prepare the state for the benchmarked functions - let randomness_gen = [1u8; 32]; - let keypair = ml_dsa_65::generate_key_pair(randomness_gen); - let signing_randomness = [4u8; 32]; - let message = [5u8; 1024]; - let signature = - ml_dsa_65::sign(&keypair.signing_key, &message, b"", signing_randomness).unwrap(); - - let state = MLDSABenchState { - randomness_gen, - keypair, - signing_randomness, - message, - signature, - }; - - // run the benchmark - let mut logger = DefmtInfoLogger; - let _ = test_suite.benchmark(&mut logger, &test_config, &state); - - libcrux_nucleo_l4r5zi::exit() -} diff --git a/libcrux-nucleo-l4r5zi/src/bin/mlkem.rs b/libcrux-nucleo-l4r5zi/src/bin/mlkem.rs index ed8c5ee..c9ffe93 100644 --- a/libcrux-nucleo-l4r5zi/src/bin/mlkem.rs +++ b/libcrux-nucleo-l4r5zi/src/bin/mlkem.rs @@ -1,96 +1,41 @@ #![no_main] #![no_std] -use embassy_stm32::Config; -use embassy_time::{self, Instant}; -use libcrux_ml_kem::mlkem1024 as mlkem; -use libcrux_nucleo_l4r5zi as _; // global logger + panicking-behavior + memory layout +use cortex_m::peripheral::Peripherals; +use libcrux_nucleo_l4r5zi as board; // global logger + panicking-behavior + memory layout -const KEYGEN_ITERATIONS: usize = 100; -const ENCAPS_ITERATIONS: usize = 100; -const DECAPS_ITERATIONS: usize = 100; +extern crate alloc; -fn time_operation( - description: &str, - setup: SetupF, - operation: OpF, - iterations: usize, -) where - SetupF: FnOnce() -> Input, - OpF: Fn(&Input) -> (), -{ - defmt::println!("{=str} ({=usize} times)", description, iterations); - let input = setup(); - let start_measuring = Instant::now(); - for _ in 0..iterations { - let _ = operation(&input); - } - let end_measuring = Instant::now(); - let time_avg = (end_measuring.as_micros() - start_measuring.as_micros()) / (iterations as u64); - defmt::println!("Took {=u64} µs on average", time_avg); -} +use core::ptr::addr_of_mut; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); #[cortex_m_rt::entry] fn main() -> ! { - // Need to initialize this, otherwise we have no timers - let _peripherals = embassy_stm32::init(Config::default()); - - defmt::println!("Testing that everything works"); - let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; - let pair = mlkem::generate_key_pair(randomness_gen); - defmt::println!("\tKey Generation OK"); - - let randomness_encaps = [2u8; libcrux_ml_kem::ENCAPS_SEED_SIZE]; - let (ct, ss_enc) = mlkem::encapsulate(pair.public_key(), randomness_encaps); - defmt::println!("\tEncapsulation OK"); - - let ss_dec = mlkem::decapsulate(pair.private_key(), &ct); - defmt::println!("\tDecapsulation OK"); - - assert_eq!(ss_enc, ss_dec); - defmt::println!("\tSuccess!"); - - defmt::println!("Benchmarking"); - time_operation( - "\tKey Generation", - || { - let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; - randomness_gen - }, - |randomness| { - let _ = mlkem::generate_key_pair(*randomness); - }, - KEYGEN_ITERATIONS, - ); - - time_operation( - "\tEncapsulation", - || { - let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; - let pair = mlkem::generate_key_pair(randomness_gen); - let randomness_encaps = [2u8; libcrux_ml_kem::ENCAPS_SEED_SIZE]; - (randomness_encaps, pair) - }, - |(randomness, pair)| { - mlkem::encapsulate(pair.public_key(), *randomness); - }, - ENCAPS_ITERATIONS, - ); - - time_operation( - "\tDecapsulation", - || { - let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; - let pair = mlkem::generate_key_pair(randomness_gen); - let randomness_encaps = [2u8; libcrux_ml_kem::ENCAPS_SEED_SIZE]; - let (ct, _ss) = mlkem::encapsulate(pair.public_key(), randomness_encaps); - (ct, pair) - }, - |(ct, pair)| { - mlkem::decapsulate(pair.private_key(), ct); - }, - DECAPS_ITERATIONS, - ); + use libcrux_iot_testutil::*; + // Initialize the allocator BEFORE you use it + { + use core::mem::MaybeUninit; + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + + let mut peripherals = Peripherals::take().unwrap(); + peripherals.DCB.enable_trace(); + peripherals.DWT.enable_cycle_counter(); + } - libcrux_nucleo_l4r5zi::exit() + // set up the test config + let test_config = TestConfig { + core_freq: board::COREFREQ, + only_names: alloc::vec![], + early_abort: false, + benchmark_runs: 5, + }; + + libcrux_testbench::mlkem::run_benchmarks(test_config); + + board::exit() } diff --git a/libcrux-nucleo-l4r5zi/src/lib.rs b/libcrux-nucleo-l4r5zi/src/lib.rs index 4d97cee..17629b4 100644 --- a/libcrux-nucleo-l4r5zi/src/lib.rs +++ b/libcrux-nucleo-l4r5zi/src/lib.rs @@ -9,6 +9,8 @@ use embassy_stm32 as _; // memory layout use panic_probe as _; +pub const COREFREQ: u32 = 4_000_000; + // same panicking *behavior* as `panic-probe` but doesn't print a panic message // this prevents the panic message being printed *twice* when `defmt::panic` is invoked #[defmt::panic_handler] diff --git a/libcrux-testbench/Cargo.toml b/libcrux-testbench/Cargo.toml new file mode 100644 index 0000000..f90354f --- /dev/null +++ b/libcrux-testbench/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "libcrux-testbench" +version = "0.1.0" +edition = "2021" + +[dependencies] +libcrux-ml-dsa = { path = "../libcrux/libcrux-ml-dsa" } +libcrux-ml-kem = { path = "../libcrux/libcrux-ml-kem", default-features = false, features = [ "pre-verification", "mlkem512", "mlkem768", "mlkem1024",] } +libcrux-iot-testutil = { path = "../libcrux-iot-testutil" } + +[features] +mldsa44 = [] +mldsa65 = [] +mldsa87 = [] +mlkem512 = [] +mlkem768 = [] +mlkem1024 = [] diff --git a/libcrux-testbench/src/lib.rs b/libcrux-testbench/src/lib.rs new file mode 100644 index 0000000..6faa4be --- /dev/null +++ b/libcrux-testbench/src/lib.rs @@ -0,0 +1,4 @@ +#![no_std] + +pub mod mldsa; +pub mod mlkem; diff --git a/libcrux-testbench/src/mldsa.rs b/libcrux-testbench/src/mldsa.rs new file mode 100644 index 0000000..9c09d5f --- /dev/null +++ b/libcrux-testbench/src/mldsa.rs @@ -0,0 +1,90 @@ +use libcrux_iot_testutil::DefmtInfoLogger; +use libcrux_iot_testutil::*; +extern crate alloc; +use alloc::string::String; + +#[cfg(feature = "mldsa44")] +use libcrux_ml_dsa::ml_dsa_44 as mldsa; +#[cfg(feature = "mldsa44")] +type MLDSASignature = mldsa::MLDSA44Signature; +#[cfg(feature = "mldsa44")] +type MLDSAKeyPair = mldsa::MLDSA44KeyPair; + +#[cfg(feature = "mldsa65")] +use libcrux_ml_dsa::ml_dsa_65 as mldsa; +#[cfg(feature = "mldsa65")] +type MLDSASignature = mldsa::MLDSA65Signature; +#[cfg(feature = "mldsa65")] +type MLDSAKeyPair = mldsa::MLDSA65KeyPair; + +#[cfg(feature = "mldsa87")] +use libcrux_ml_dsa::ml_dsa_87 as mldsa; +#[cfg(feature = "mldsa87")] +type MLDSASignature = mldsa::MLDSA87Signature; +#[cfg(feature = "mldsa87")] +type MLDSAKeyPair = mldsa::MLDSA87KeyPair; + +struct MLDSABenchState { + randomness_gen: [u8; 32], + keypair: MLDSAKeyPair, + signing_randomness: [u8; 32], + message: [u8; 1024], + signature: MLDSASignature, +} + +fn bench_keygen(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { + let _pair = mldsa::generate_key_pair(state.randomness_gen); + Ok(()) +} + +fn bench_sign(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { + let _signature = mldsa::sign( + &state.keypair.signing_key, + &state.message, + b"", + state.signing_randomness, + ); + Ok(()) +} + +fn bench_verify(_l: &mut L, state: &MLDSABenchState) -> Result<(), String> { + let _ = mldsa::verify( + &state.keypair.verification_key, + &state.message, + b"", + &state.signature, + ); + + Ok(()) +} + +pub fn run_benchmarks(test_config: TestConfig) { + // set up the test suite + let test_cases = [ + TestCase::new("bench_keygen", bench_keygen), + TestCase::new("bench_sign", bench_sign), + TestCase::new("bench_verify", bench_verify), + ]; + + let test_suite = TestSuite::new("ML-DSA Benchmark", &test_cases); + + + // prepare the state for the benchmarked functions + let randomness_gen = [1u8; 32]; + let keypair = mldsa::generate_key_pair(randomness_gen); + let signing_randomness = [4u8; 32]; + let message = [5u8; 1024]; + let signature = mldsa::sign(&keypair.signing_key, &message, b"", signing_randomness).unwrap(); + + let state = MLDSABenchState { + randomness_gen, + keypair, + signing_randomness, + message, + signature, + }; + + // run the benchmark + let mut logger = DefmtInfoLogger; + let _ = test_suite.benchmark(&mut logger, &test_config, &state); +} diff --git a/libcrux-testbench/src/mlkem.rs b/libcrux-testbench/src/mlkem.rs new file mode 100644 index 0000000..e86fb85 --- /dev/null +++ b/libcrux-testbench/src/mlkem.rs @@ -0,0 +1,83 @@ +use libcrux_iot_testutil::DefmtInfoLogger; +use libcrux_iot_testutil::*; +extern crate alloc; +use alloc::string::String; + +#[cfg(feature = "mlkem512")] +use libcrux_ml_kem::mlkem512 as mlkem; +#[cfg(feature = "mlkem512")] +type MlKemPublicKey = mlkem::MlKem512PublicKey; +#[cfg(feature = "mlkem512")] +type MlKemPrivateKey = mlkem::MlKem512PrivateKey; +#[cfg(feature = "mlkem512")] +type MlKemCiphertext = mlkem::MlKem512Ciphertext; + +#[cfg(feature = "mlkem768")] +use libcrux_ml_kem::mlkem768 as mlkem; +#[cfg(feature = "mlkem768")] +type MlKemPublicKey = mlkem::MlKem768PublicKey; +#[cfg(feature = "mlkem768")] +type MlKemPrivateKey = mlkem::MlKem768PrivateKey; +#[cfg(feature = "mlkem768")] +type MlKemCiphertext = mlkem::MlKem768Ciphertext; + +#[cfg(feature = "mlkem1024")] +use libcrux_ml_kem::mlkem1024 as mlkem; +#[cfg(feature = "mlkem1024")] +type MlKemPublicKey = mlkem::MlKem1024PublicKey; +#[cfg(feature = "mlkem1024")] +type MlKemPrivateKey = mlkem::MlKem1024PrivateKey; +#[cfg(feature = "mlkem1024")] +type MlKemCiphertext = mlkem::MlKem1024Ciphertext; + +struct MlKemBenchState<'a> { + randomness_gen: [u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE], + public_key: &'a MlKemPublicKey, + private_key: &'a MlKemPrivateKey, + randomness_encaps: [u8; libcrux_ml_kem::ENCAPS_SEED_SIZE], + ciphertext: MlKemCiphertext, +} + +fn bench_keygen(_l: &mut L, state: &MlKemBenchState) -> Result<(), String> { + let _pair = mlkem::generate_key_pair(state.randomness_gen); + Ok(()) +} + +fn bench_encaps(_l: &mut L, state: &MlKemBenchState) -> Result<(), String> { + let _ = mlkem::encapsulate(&state.public_key, state.randomness_encaps); + Ok(()) +} + +fn bench_decaps(_l: &mut L, state: &MlKemBenchState) -> Result<(), String> { + let _ = mlkem::decapsulate(&state.private_key, &state.ciphertext); + + Ok(()) +} + +pub fn run_benchmarks(test_config: TestConfig) { + // set up the test suite + let test_cases = [ + TestCase::new("bench_keygen", bench_keygen), + TestCase::new("bench_encaps", bench_encaps), + TestCase::new("bench_decaps", bench_decaps), + ]; + + let test_suite = TestSuite::new("ML-KEM Benchmark", &test_cases); + + // prepare the state for the benchmarked functions + let randomness_gen = [1u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE]; + let pair = mlkem::generate_key_pair(randomness_gen); + let randomness_encaps = [2u8; libcrux_ml_kem::ENCAPS_SEED_SIZE]; + let (ciphertext, _shared_secret) = mlkem::encapsulate(pair.public_key(), randomness_encaps); + let state = MlKemBenchState { + randomness_gen, + public_key: pair.public_key(), + private_key: pair.private_key(), + randomness_encaps, + ciphertext, + }; + + // run the benchmark + let mut logger = DefmtInfoLogger; + let _ = test_suite.benchmark(&mut logger, &test_config, &state); +} diff --git a/libcrux/libcrux-platform/src/lib.rs b/libcrux/libcrux-platform/src/lib.rs index c83769b..40adfc6 100644 --- a/libcrux/libcrux-platform/src/lib.rs +++ b/libcrux/libcrux-platform/src/lib.rs @@ -54,6 +54,9 @@ mod platform { #[cfg(not(hax))] mod platform { + #[cfg(not(target_os = "none"))] + use super::*; + // TODO: Check for z14 or z15 pub fn simd128_support() -> bool { #[cfg(all(target_arch = "aarch64", target_os = "macos"))]