diff --git a/Cargo.toml b/Cargo.toml index 9e6a6d82..013152be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "examples/spi-demo", "examples/twi-ssd1306", "examples/ecb-demo", + "examples/ccm-demo", ] [profile.dev] diff --git a/examples/ccm-demo/Cargo.toml b/examples/ccm-demo/Cargo.toml new file mode 100644 index 00000000..1f31d752 --- /dev/null +++ b/examples/ccm-demo/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ccm-demo" +version = "0.0.1" +edition = "2018" +authors = [ "Thales Fragoso "] + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rt = "0.6.12" +rtt-target = {version = "0.2.0", features = ["cortex-m"] } +rand_core = "0.5.1" + +nrf52810-hal = { path = "../../nrf52810-hal", features = ["rt"], optional = true } +nrf52832-hal = { path = "../../nrf52832-hal", features = ["rt"], optional = true } +nrf52840-hal = { path = "../../nrf52840-hal", features = ["rt"], optional = true } +nrf52833-hal = { path = "../../nrf52833-hal", features = ["rt"], optional = true } +nrf51-hal = { path = "../../nrf51-hal", features = ["rt"], optional = true} + +[[bin]] +name = "ccm-demo" +doc = false +test = false + +[features] +51 = ["nrf51-hal"] +52810 = ["nrf52810-hal"] +52832 = ["nrf52832-hal"] +52840 = ["nrf52840-hal"] +52833 = ["nrf52833-hal"] diff --git a/examples/ccm-demo/Embed.toml b/examples/ccm-demo/Embed.toml new file mode 100644 index 00000000..d64fb342 --- /dev/null +++ b/examples/ccm-demo/Embed.toml @@ -0,0 +1,46 @@ +[probe] +# The index of the probe in the connected probe list. +# probe_index = 0 +# The protocol to be used for communicating with the target. +protocol = "Swd" +# The speed in kHz of the data link to the target. +# speed = 1337 + +[flashing] +# Whether or not the target should be flashed. +enabled = true +# Whether or not the target should be halted after flashing. +halt_afterwards = false +# Whether or not bytes erased but not rewritten with data from the ELF +# should be restored with their contents before erasing. +restore_unwritten_bytes = false +# The path where an SVG of the assembled flash layout should be written to. +# flash_layout_output_path = "out.svg" + +[general] +# The chip name of the chip to be debugged. +chip = "nRF52832" +# A list of chip descriptions to be loaded during runtime. +chip_descriptions = [] +# The default log level to be used. +log_level = "Warn" + +[rtt] +# Whether or not an RTTUI should be opened after flashing. +# This is exclusive and cannot be used with GDB at the moment. +enabled = true +# A list of channel associations to be displayed. If left empty, all channels are displayed. +channels = [ + # { up = 0, down = 0, name = "name" } +] +# The duration in ms for which the logger should retry to attach to RTT. +timeout = 3000 +# Whether timestamps in the RTTUI are enabled +show_timestamps = true + +[gdb] +# Whether or not a GDB server should be opened after flashing. +# This is exclusive and cannot be used with RTT at the moment. +enabled = false +# The connection string in host:port format wher the GDB server will open a socket. +# gdb_connection_string diff --git a/examples/ccm-demo/README.md b/examples/ccm-demo/README.md new file mode 100644 index 00000000..7651c3bd --- /dev/null +++ b/examples/ccm-demo/README.md @@ -0,0 +1,19 @@ +# AES-CCM demo + +Choose the microcontroller with one of the following features: +- 51 +- 52810 +- 52832 +- 52840 + +Also, if using `cargo-embed`, change the `chip` and `protocol` fields in [Embed.toml](Embed.toml). + +This demo uses the [rtt-target](https://crates.io/crates/rtt-target) crate for communication. + +If using `cargo-embed`, just run + +```console +$ cargo embed --release --features=52832 --target=thumbv7em-none-eabihf +``` + +Replace `52832` and `thumbv7em-none-eabihf` with the correct feature and target for your microcontroller. diff --git a/examples/ccm-demo/src/main.rs b/examples/ccm-demo/src/main.rs new file mode 100644 index 00000000..86bae00c --- /dev/null +++ b/examples/ccm-demo/src/main.rs @@ -0,0 +1,146 @@ +#![no_std] +#![no_main] + +// Import the right HAL/PAC crate, depending on the target chip +#[cfg(feature = "51")] +pub use nrf51_hal as hal; +#[cfg(feature = "52810")] +pub use nrf52810_hal as hal; +#[cfg(feature = "52832")] +pub use nrf52832_hal as hal; +#[cfg(feature = "52833")] +pub use nrf52833_hal as hal; +#[cfg(feature = "52840")] +pub use nrf52840_hal as hal; + +use { + core::{ + panic::PanicInfo, + sync::atomic::{compiler_fence, Ordering}, + }, + cortex_m_rt::entry, + hal::{ + ccm::{CcmData, DataRate}, + rng::Rng, + Ccm, Clocks, + }, + rand_core::RngCore, + rtt_target::{rprintln, rtt_init_print}, +}; + +mod stopwatch; +use stopwatch::StopWatch; + +const MSG: [u8; 251] = *b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis justo +libero, commodo eget tincidunt quis, elementum at ipsum. Praesent pharetra imperdiet eros, at +vestibulum diam mattis ac. Nunc viverra cursus justo, sollicitudin placerat justo lectus."; + +const KEY: [u8; 16] = *b"aaaaaaaaaaaaaaaa"; +const HEADER_SIZE: usize = 3; +const MIC_SIZE: usize = 4; +const LENGTH_INDEX: usize = 1; + +#[entry] +fn main() -> ! { + let p = hal::pac::Peripherals::take().unwrap(); + + let _clocks = Clocks::new(p.CLOCK).enable_ext_hfosc(); + rtt_init_print!(); + + let mut rng = Rng::new(p.RNG); + let mut iv = [0u8; 8]; + rng.fill_bytes(&mut iv); + + let mut ccm_data_enc = CcmData::new(KEY, iv); + let mut ccm_data_dec = CcmData::new(KEY, iv); + let mut ccm = Ccm::init(p.CCM, p.AAR, DataRate::_1Mbit); + + let mut clear_buffer = [0u8; 254]; + let mut cipher_buffer = [0u8; 258]; + let mut strach_area = [0u8; 271]; + + (&mut clear_buffer[HEADER_SIZE..]).copy_from_slice(&MSG[..]); + + let mut stop_watch = StopWatch::new(p.TIMER0); + + let payload_lengths: [usize; 5] = [251, 128, 64, 32, 16]; + + for &length in payload_lengths.iter() { + // Adjust payload length + clear_buffer[LENGTH_INDEX] = length as u8; + + rprintln!("Starting Encryption of {} bytes", length); + stop_watch.start(); + + ccm.encrypt_packet( + &mut ccm_data_enc, + &clear_buffer[..], + &mut cipher_buffer[..], + &mut strach_area[..], + ) + .unwrap(); + + let now = stop_watch.now(); + stop_watch.stop(); + + assert_eq!(cipher_buffer[LENGTH_INDEX], (length + MIC_SIZE) as u8); + + rprintln!("Encryption Took: {} us", now); + + //rprint!("Cipher Packet: "); + //for number in cipher_buffer.iter().take(length + MIC_SIZE + HEADER_SIZE) { + // rprint!("{:x} ", *number); + //} + + // Since we're both encrypting and decrypting, we need to decrement the counter to have the + // same counter that encrypted the message. `encrypt_packet` and `decrypt_packet` + // automatically increments the counter when the operation succeeds. + //ccm_data.decrement_counter(); + + // Clears the buffer, so we can inspect the decrypted text + clear_buffer = [0u8; 254]; + + rprintln!("\r\nStarting Decryption of {} bytes", length + MIC_SIZE); + stop_watch.start(); + + ccm.decrypt_packet( + &mut ccm_data_dec, + &mut clear_buffer[..], + &cipher_buffer[..], + &mut strach_area[..], + ) + .unwrap(); + + let now = stop_watch.now(); + stop_watch.stop(); + + rprintln!("Decryption Took: {} us\n\n", now); + + assert_eq!(clear_buffer[LENGTH_INDEX], length as u8); + assert_eq!( + &clear_buffer[HEADER_SIZE..length + HEADER_SIZE], + &MSG[..length] + ); + + //let msg = core::str::from_utf8(&clear_buffer[HEADER_SIZE..length]).unwrap(); + //rprintln!("Clear text: {}\n", msg); + + // Clears the cipher text for next round + cipher_buffer = [0u8; 258]; + } + + rprintln!("Done"); + + loop { + compiler_fence(Ordering::SeqCst); + } +} + +#[inline(never)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} diff --git a/examples/ccm-demo/src/stopwatch.rs b/examples/ccm-demo/src/stopwatch.rs new file mode 100644 index 00000000..d4995169 --- /dev/null +++ b/examples/ccm-demo/src/stopwatch.rs @@ -0,0 +1,50 @@ +use super::hal::pac::TIMER0; + +pub struct StopWatch { + regs: TIMER0, +} + +impl StopWatch { + pub fn new(regs: TIMER0) -> Self { + // NOTE(unsafe) 1 is a valid pattern to write to this register + regs.tasks_stop.write(|w| unsafe { w.bits(1) }); + + regs.bitmode.write(|w| w.bitmode()._32bit()); + + // 16 Mhz / 2**4 = 1 Mhz = µs resolution + // NOTE(unsafe) 4 is a valid pattern to write to this register + regs.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); + // NOTE(unsafe) 1 is a valid pattern to write to this register + regs.tasks_clear.write(|w| unsafe { w.bits(1) }); + + Self { regs } + } + + #[inline(always)] + pub fn start(&mut self) { + // NOTE(unsafe) 1 is a valid pattern to write to this register + self.regs.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + #[inline(always)] + pub fn now(&self) -> u32 { + // NOTE(unsafe) 1 is a valid pattern to write to this register + self.regs.tasks_capture[0].write(|w| unsafe { w.bits(1) }); + self.regs.cc[0].read().bits() + } + + //#[inline(always)] + //pub fn pause(&mut self) { + // // NOTE(unsafe) 1 is a valid pattern to write to this register + // self.regs.tasks_stop.write(|w| unsafe { w.bits(1) }); + //} + + #[inline(always)] + pub fn stop(&mut self) { + // NOTE(unsafe) 1 is a valid pattern to write to this register + self.regs.tasks_stop.write(|w| unsafe { w.bits(1) }); + + // NOTE(unsafe) 1 is a valid pattern to write to this register + self.regs.tasks_clear.write(|w| unsafe { w.bits(1) }); + } +} diff --git a/nrf-hal-common/src/ccm.rs b/nrf-hal-common/src/ccm.rs index 8db9011a..a1681465 100644 --- a/nrf-hal-common/src/ccm.rs +++ b/nrf-hal-common/src/ccm.rs @@ -101,6 +101,7 @@ pub enum CcmError { /// /// The NONCE vector (as specified by the Bluetooth Core Specification) will be generated by /// hardware based on this information. +#[derive(Debug, PartialEq)] #[repr(C)] pub struct CcmData { key: [u8; 16], @@ -188,7 +189,8 @@ impl Ccm { /// /// The generated MIC will be appended to after the payload in the `cipher_packet`. The slices /// passed to this method must have the correct size, for more information refer to the module - /// level documentation. + /// level documentation. The counter in `ccm_data` will be incremented if the operation + /// succeeds. pub fn encrypt_packet( &mut self, ccm_data: &mut CcmData, @@ -223,7 +225,7 @@ impl Ccm { return Err(CcmError::InsufficientScratchArea); } - let length_variant = if payload_len > MAXIMUM_LENGTH_5BITS { + let length_variant = if payload_len <= MAXIMUM_LENGTH_5BITS { LENGTH_A::DEFAULT } else { #[cfg(any(feature = "52840", feature = "52833"))] @@ -260,6 +262,7 @@ impl Ccm { // Clear events self.regs.events_endcrypt.reset(); self.regs.events_error.reset(); + self.regs.events_endksgen.reset(); // "Preceding reads and writes cannot be moved past subsequent writes." compiler_fence(Ordering::Release); @@ -289,7 +292,7 @@ impl Ccm { /// /// This method will return an error if the MIC verification fails. The slices passed to this /// method must have the correct size, for more information refer to the module level - /// documentation. + /// documentation. The counter in `ccm_data` will be incremented if the operation succeeds. pub fn decrypt_packet( &mut self, ccm_data: &mut CcmData, @@ -329,7 +332,7 @@ impl Ccm { return Err(CcmError::InsufficientScratchArea); } - let length_variant = if payload_len > MAXIMUM_LENGTH_5BITS { + let length_variant = if payload_len <= MAXIMUM_LENGTH_5BITS { LENGTH_A::DEFAULT } else { #[cfg(any(feature = "52840", feature = "52833"))] @@ -366,11 +369,12 @@ impl Ccm { // Clear events self.regs.events_endcrypt.reset(); self.regs.events_error.reset(); + self.regs.events_endksgen.reset(); // "Preceding reads and writes cannot be moved past subsequent writes." compiler_fence(Ordering::Release); - // Start key generation, encryption will start automatically because of the enabled short + // Start key generation, decryption will start automatically because of the enabled short // in init // NOTE(unsafe) 1 is a valid pattern to write to this register self.regs.tasks_ksgen.write(|w| unsafe { w.bits(1) }); diff --git a/nrf51-hal/src/lib.rs b/nrf51-hal/src/lib.rs index 861b8364..c456d7dd 100644 --- a/nrf51-hal/src/lib.rs +++ b/nrf51-hal/src/lib.rs @@ -10,6 +10,7 @@ pub mod prelude { } pub use crate::adc::Adc; +pub use crate::ccm::Ccm; pub use crate::clocks::Clocks; pub use crate::ecb::Ecb; pub use crate::rtc::Rtc; diff --git a/nrf52810-hal/src/lib.rs b/nrf52810-hal/src/lib.rs index dd36a252..5f45851b 100644 --- a/nrf52810-hal/src/lib.rs +++ b/nrf52810-hal/src/lib.rs @@ -11,6 +11,7 @@ pub mod prelude { pub use crate::time::U32Ext; } +pub use crate::ccm::Ccm; pub use crate::clocks::Clocks; pub use crate::delay::Delay; pub use crate::ecb::Ecb; diff --git a/nrf52832-hal/src/lib.rs b/nrf52832-hal/src/lib.rs index b0aaceae..bb430b35 100644 --- a/nrf52832-hal/src/lib.rs +++ b/nrf52832-hal/src/lib.rs @@ -9,6 +9,7 @@ pub mod prelude { pub use nrf_hal_common::prelude::*; } +pub use crate::ccm::Ccm; pub use crate::clocks::Clocks; pub use crate::delay::Delay; pub use crate::ecb::Ecb; diff --git a/nrf52833-hal/src/lib.rs b/nrf52833-hal/src/lib.rs index af84f376..3730ef63 100644 --- a/nrf52833-hal/src/lib.rs +++ b/nrf52833-hal/src/lib.rs @@ -11,6 +11,7 @@ pub mod prelude { pub use crate::time::U32Ext; } +pub use crate::ccm::Ccm; pub use crate::clocks::Clocks; pub use crate::delay::Delay; pub use crate::ecb::Ecb; diff --git a/nrf52840-hal/src/lib.rs b/nrf52840-hal/src/lib.rs index 952b1b41..50c41320 100644 --- a/nrf52840-hal/src/lib.rs +++ b/nrf52840-hal/src/lib.rs @@ -11,6 +11,7 @@ pub mod prelude { pub use crate::time::U32Ext; } +pub use crate::ccm::Ccm; pub use crate::clocks::Clocks; pub use crate::delay::Delay; pub use crate::ecb::Ecb; diff --git a/scripts/build.sh b/scripts/build.sh index 2ec90b26..06ff8739 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -45,5 +45,7 @@ echo Building examples/twi-ssd1306... cargo build --manifest-path examples/twi-ssd1306/Cargo.toml --no-default-features --features="52840" --target thumbv7em-none-eabi echo Building examples/ecb-demo... cargo build --manifest-path examples/ecb-demo/Cargo.toml --features=52832 +echo Building examples/ccm-demo... +cargo build --manifest-path examples/ccm-demo/Cargo.toml --features=52832 echo Checking source code formatting... cargo +stable fmt -- --check