diff --git a/Cargo.toml b/Cargo.toml index 01fa2c0b..9e6a6d82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "examples/rtfm-demo", "examples/spi-demo", "examples/twi-ssd1306", + "examples/ecb-demo", ] [profile.dev] diff --git a/examples/ecb-demo/Cargo.toml b/examples/ecb-demo/Cargo.toml new file mode 100644 index 00000000..9845417f --- /dev/null +++ b/examples/ecb-demo/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ecb-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"] } + +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 = "ecb-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/ecb-demo/Embed.toml b/examples/ecb-demo/Embed.toml new file mode 100644 index 00000000..5da61018 --- /dev/null +++ b/examples/ecb-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/ecb-demo/README.md b/examples/ecb-demo/README.md new file mode 100644 index 00000000..af4c3e76 --- /dev/null +++ b/examples/ecb-demo/README.md @@ -0,0 +1,19 @@ +# AES electronic codebook mode encryption 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/ecb-demo/src/main.rs b/examples/ecb-demo/src/main.rs new file mode 100644 index 00000000..9a9f7dc3 --- /dev/null +++ b/examples/ecb-demo/src/main.rs @@ -0,0 +1,64 @@ +#![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::{Clocks, Ecb}, + rtt_target::{rprint, rprintln, rtt_init_print}, +}; + +const MSG: [u8; 16] = *b"Message to encry"; +const KEY: [u8; 16] = *b"aaaaaaaaaaaaaaaa"; +const CIPHER_MSG: [u8; 16] = [ + 0xFE, 0xF1, 0x63, 0x82, 0xB4, 0x54, 0x6B, 0xE4, 0xEB, 0x9A, 0x5C, 0x0E, 0xB6, 0x0E, 0x49, 0x2F, +]; + +#[entry] +fn main() -> ! { + let p = hal::pac::Peripherals::take().unwrap(); + + let _clocks = Clocks::new(p.CLOCK).enable_ext_hfosc(); + rtt_init_print!(); + + let mut ecb = Ecb::init(p.ECB); + + loop { + rprintln!("Starting Encryption\n"); + rprintln!("Clear text: {}", core::str::from_utf8(&MSG[..]).unwrap()); + + let cipher_text = ecb.encrypt_block(MSG, KEY).unwrap(); + rprint!("Cipher Text: "); + for number in cipher_text.iter() { + rprint!("{:x} ", *number); + } + assert_eq!(cipher_text, CIPHER_MSG); + rprintln!("\r\n Encryption Done\n"); + + cortex_m::asm::delay(136_000_000); + } +} + +#[inline(never)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} diff --git a/nrf-hal-common/src/ecb.rs b/nrf-hal-common/src/ecb.rs new file mode 100644 index 00000000..4c675a73 --- /dev/null +++ b/nrf-hal-common/src/ecb.rs @@ -0,0 +1,98 @@ +//! HAL interface to the AES electronic codebook mode encryption. +//! +//! The ECB encryption block supports 128 bit AES encryption (encryption only, not decryption). + +use crate::target::ECB; +use core::sync::atomic::{compiler_fence, Ordering}; + +/// Error type to represent a sharing conflict during encryption. +#[derive(Debug, Copy, Clone)] +pub struct EncryptionError {} + +/// A safe, blocking wrapper around the AES-ECB peripheral. +/// +/// It's really just blockwise AES and not an ECB stream cipher. Blocks can be +/// encrypted by calling `crypt_block`. +pub struct Ecb { + regs: ECB, +} + +impl Ecb { + /// Takes ownership of the `ECB` peripheral, returning a safe wrapper. + pub fn init(regs: ECB) -> Self { + // Disable all interrupts + regs.intenclr + .write(|w| w.endecb().clear().errorecb().clear()); + + // NOTE(unsafe) 1 is a valid pattern to write to this register + regs.tasks_stopecb.write(|w| unsafe { w.bits(1) }); + Self { regs } + } + + /// Destroys `self`, giving the `ECB` peripheral back. + pub fn into_inner(self) -> ECB { + // Clear all events + self.regs.events_endecb.reset(); + self.regs.events_errorecb.reset(); + + self.regs + } + + /// Blocking encryption. + /// + /// Encrypts a `block` with `key`. + /// + /// # Errors + /// + /// An error will be returned when the AES hardware raises an `ERRORECB` + /// event. This can happen when an operation is started that shares the AES + /// hardware resources with the AES ECB peripheral while an encryption + /// operation is running. + pub fn encrypt_block( + &mut self, + block: [u8; 16], + key: [u8; 16], + ) -> Result<[u8; 16], EncryptionError> { + #[repr(C)] + struct EcbData { + key: [u8; 16], + clear_text: [u8; 16], + cipher_text: [u8; 16], + } + + // We allocate the DMA'd buffer on the stack, which means that we must + // not panic or return before the AES operation is finished. + let mut buf = EcbData { + key, + clear_text: block, + cipher_text: [0; 16], + }; + + // NOTE(unsafe) Any 32bits pattern is safe to write to this register + self.regs + .ecbdataptr + .write(|w| unsafe { w.bits(&mut buf as *mut _ as u32) }); + + // Clear all events + self.regs.events_endecb.reset(); + self.regs.events_errorecb.reset(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + compiler_fence(Ordering::Release); + // NOTE(unsafe) 1 is a valid pattern to write to this register + self.regs.tasks_startecb.write(|w| unsafe { w.bits(1) }); + + while self.regs.events_endecb.read().bits() == 0 + && self.regs.events_errorecb.read().bits() == 0 + {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + compiler_fence(Ordering::Acquire); + + if self.regs.events_errorecb.read().bits() == 1 { + // It's ok to return here, the events will be cleared before the next encryption + return Err(EncryptionError {}); + } + Ok(buf.cipher_text) + } +} diff --git a/nrf-hal-common/src/lib.rs b/nrf-hal-common/src/lib.rs index b93b1643..2befd62a 100644 --- a/nrf-hal-common/src/lib.rs +++ b/nrf-hal-common/src/lib.rs @@ -25,6 +25,8 @@ pub mod adc; pub mod clocks; #[cfg(not(feature = "51"))] pub mod delay; +#[cfg(not(feature = "9160"))] +pub mod ecb; pub mod gpio; #[cfg(not(feature = "9160"))] pub mod rng; diff --git a/nrf51-hal/src/lib.rs b/nrf51-hal/src/lib.rs index 2d2b0181..861b8364 100644 --- a/nrf51-hal/src/lib.rs +++ b/nrf51-hal/src/lib.rs @@ -11,6 +11,7 @@ pub mod prelude { pub use crate::adc::Adc; pub use crate::clocks::Clocks; +pub use crate::ecb::Ecb; pub use crate::rtc::Rtc; pub use crate::spi::Spi; pub use crate::temp::Temp; diff --git a/nrf52810-hal/src/lib.rs b/nrf52810-hal/src/lib.rs index 8ea2dea6..dd36a252 100644 --- a/nrf52810-hal/src/lib.rs +++ b/nrf52810-hal/src/lib.rs @@ -13,6 +13,7 @@ pub mod prelude { pub use crate::clocks::Clocks; pub use crate::delay::Delay; +pub use crate::ecb::Ecb; pub use crate::saadc::Saadc; pub use crate::spim::Spim; pub use crate::temp::Temp; diff --git a/nrf52832-hal/src/lib.rs b/nrf52832-hal/src/lib.rs index 43f6eb9c..b0aaceae 100644 --- a/nrf52832-hal/src/lib.rs +++ b/nrf52832-hal/src/lib.rs @@ -11,6 +11,7 @@ pub mod prelude { pub use crate::clocks::Clocks; pub use crate::delay::Delay; +pub use crate::ecb::Ecb; pub use crate::rtc::Rtc; pub use crate::saadc::Saadc; pub use crate::spim::Spim; diff --git a/nrf52833-hal/src/lib.rs b/nrf52833-hal/src/lib.rs index cbf21491..af84f376 100644 --- a/nrf52833-hal/src/lib.rs +++ b/nrf52833-hal/src/lib.rs @@ -13,6 +13,7 @@ pub mod prelude { pub use crate::clocks::Clocks; pub use crate::delay::Delay; +pub use crate::ecb::Ecb; pub use crate::saadc::Saadc; pub use crate::spim::Spim; pub use crate::temp::Temp; diff --git a/nrf52840-hal/src/lib.rs b/nrf52840-hal/src/lib.rs index f5969422..952b1b41 100644 --- a/nrf52840-hal/src/lib.rs +++ b/nrf52840-hal/src/lib.rs @@ -13,6 +13,7 @@ pub mod prelude { pub use crate::clocks::Clocks; pub use crate::delay::Delay; +pub use crate::ecb::Ecb; pub use crate::saadc::Saadc; pub use crate::spim::Spim; pub use crate::temp::Temp; diff --git a/scripts/build.sh b/scripts/build.sh index b010fb52..2ec90b26 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -43,5 +43,7 @@ echo Building examples/twi-ssd1306... cargo build --manifest-path examples/twi-ssd1306/Cargo.toml 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 Checking source code formatting... cargo +stable fmt -- --check