From d1888773b7022270d649c124472369386b4eee08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Fri, 12 Jul 2024 11:12:01 +0200 Subject: [PATCH 01/10] example: add minimal radio tx-rx functionality on nRF52840 --- Cargo.toml | 1 + examples/lakers-nrf52840/.cargo/config.toml | 9 +++ examples/lakers-nrf52840/Cargo.toml | 44 +++++++++++ examples/lakers-nrf52840/build.rs | 35 +++++++++ examples/lakers-nrf52840/memory.x | 12 +++ .../lakers-nrf52840/src/bin/radio_common.rs | 77 +++++++++++++++++++ examples/lakers-nrf52840/src/bin/radio_rx.rs | 65 ++++++++++++++++ examples/lakers-nrf52840/src/bin/radio_tx.rs | 66 ++++++++++++++++ 8 files changed, 309 insertions(+) create mode 100644 examples/lakers-nrf52840/.cargo/config.toml create mode 100644 examples/lakers-nrf52840/Cargo.toml create mode 100644 examples/lakers-nrf52840/build.rs create mode 100644 examples/lakers-nrf52840/memory.x create mode 100644 examples/lakers-nrf52840/src/bin/radio_common.rs create mode 100644 examples/lakers-nrf52840/src/bin/radio_rx.rs create mode 100644 examples/lakers-nrf52840/src/bin/radio_tx.rs diff --git a/Cargo.toml b/Cargo.toml index 688ed202..c59e6625 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crypto/lakers-crypto-cryptocell310-sys", "examples/coap", "examples/lakers-no_std", + "examples/lakers-nrf52840", "lakers-c", "lakers-python", ] diff --git a/examples/lakers-nrf52840/.cargo/config.toml b/examples/lakers-nrf52840/.cargo/config.toml new file mode 100644 index 00000000..17616a05 --- /dev/null +++ b/examples/lakers-nrf52840/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/lakers-nrf52840/Cargo.toml b/examples/lakers-nrf52840/Cargo.toml new file mode 100644 index 00000000..df8d7aba --- /dev/null +++ b/examples/lakers-nrf52840/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "lakers-nrf52840" +edition = "2021" +version.workspace = true +repository.workspace = true +license.workspace = true +readme.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +embassy-futures = { git = "https://github.com/embassy-rs/embassy", branch = "main" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-net = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-usb = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } +embassy-net-esp-hosted = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } +embassy-net-enc28j60 = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } + +embedded-io = { version = "0.6.0", features = ["defmt-03"] } +embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +fixed = "1.10.0" +static_cell = { version = "2" } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +rand = { version = "0.8.4", default-features = false } +embedded-storage = "0.3.1" +usbd-hid = "0.7.0" +serde = { version = "1.0.136", default-features = false } +embedded-hal = { version = "1.0" } +embedded-hal-async = { version = "1.0" } +embedded-hal-bus = { version = "0.1", features = ["async"] } +num-integer = { version = "0.1.45", default-features = false } +microfft = "0.5.0" + +[profile.release] +debug = 2 diff --git a/examples/lakers-nrf52840/build.rs b/examples/lakers-nrf52840/build.rs new file mode 100644 index 00000000..30691aa9 --- /dev/null +++ b/examples/lakers-nrf52840/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/lakers-nrf52840/memory.x b/examples/lakers-nrf52840/memory.x new file mode 100644 index 00000000..15b492bc --- /dev/null +++ b/examples/lakers-nrf52840/memory.x @@ -0,0 +1,12 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K + + /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */ + /* + FLASH : ORIGIN = 0x00027000, LENGTH = 868K + RAM : ORIGIN = 0x20020000, LENGTH = 128K + */ +} diff --git a/examples/lakers-nrf52840/src/bin/radio_common.rs b/examples/lakers-nrf52840/src/bin/radio_common.rs new file mode 100644 index 00000000..f154d58d --- /dev/null +++ b/examples/lakers-nrf52840/src/bin/radio_common.rs @@ -0,0 +1,77 @@ +pub const MAX_PDU: usize = 258; +pub const FREQ: u32 = 2408; +pub const ADV_ADDRESS: u32 = 0x12345678; +pub const ADV_CRC_INIT: u32 = 0xffff; +pub const CRC_POLY: u32 = 0x00065b; + +#[derive(Debug)] +pub enum PacketError { + SliceTooLong, + SliceTooShort, + ParsingError, +} +pub struct Packet { + pub len: usize, + pub pdu: [u8; MAX_PDU], +} + +impl Default for Packet { + fn default() -> Self { + Packet { + len: 0, + pdu: [0u8; MAX_PDU], + } + } +} + +impl Packet { + pub fn new() -> Self { + Packet { + len: 0, + pdu: [0u8; MAX_PDU], + } + } + + pub fn new_from_slice(slice: &[u8]) -> Result { + let mut buffer = Self::new(); + if buffer.fill_with_slice(slice).is_ok() { + Ok(buffer) + } else { + Err(PacketError::SliceTooLong) + } + } + + pub fn fill_with_slice(&mut self, slice: &[u8]) -> Result<(), PacketError> { + if slice.len() <= self.pdu.len() { + self.len = slice.len(); + self.pdu[..self.len].copy_from_slice(slice); + Ok(()) + } else { + Err(PacketError::SliceTooLong) + } + } + + pub fn as_bytes(&mut self) -> &[u8] { + self.pdu.copy_within(..self.len, 2); + self.pdu[0] = 0x00; + self.pdu[1] = self.len as u8; + + &self.pdu[..self.len] + } +} + +impl TryInto for &[u8] { + type Error = (); + + fn try_into(self) -> Result { + let mut packet: Packet = Default::default(); + + if self.len() > 1 { + packet.len = self[1] as usize; + packet.pdu[..packet.len].copy_from_slice(&self[2..2 + packet.len]); + Ok(packet) + } else { + Err(()) + } + } +} diff --git a/examples/lakers-nrf52840/src/bin/radio_rx.rs b/examples/lakers-nrf52840/src/bin/radio_rx.rs new file mode 100644 index 00000000..3ecb71e5 --- /dev/null +++ b/examples/lakers-nrf52840/src/bin/radio_rx.rs @@ -0,0 +1,65 @@ +#![no_std] +#![no_main] + +use defmt::info; +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::radio::ble::Mode; +use embassy_nrf::radio::ble::Radio; +use embassy_nrf::radio::TxPower; +use embassy_nrf::{bind_interrupts, peripherals, radio}; +use embassy_time::{Duration, Timer}; +use radio_common::{Packet, PacketError, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; +use {defmt_rtt as _, panic_probe as _}; + +mod radio_common; + +bind_interrupts!(struct Irqs { + RADIO => radio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = embassy_nrf::config::Config::default(); + config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + info!("Starting BLE radio"); + let mut radio = Radio::new(p.RADIO, Irqs); + + let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + led.set_high(); + + radio.set_mode(Mode::BLE_1MBIT); + radio.set_tx_power(TxPower::_0D_BM); + radio.set_frequency(FREQ); + + radio.set_access_address(ADV_ADDRESS); + radio.set_header_expansion(false); + radio.set_crc_init(ADV_CRC_INIT); + radio.set_crc_poly(CRC_POLY); + + unwrap!(spawner.spawn(receive_and_blink(radio, led))); +} + +#[embassy_executor::task] +async fn receive_and_blink( + mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>, + mut led: Output<'static>, +) { + info!("Hello from receive_and_blink"); + + loop { + let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; + let res = radio.receive(&mut buffer).await.unwrap(); + let packet: Packet = buffer[..].try_into().unwrap(); + + info!("Packet received: {:X}", packet.pdu[..packet.len]); + + // blink the LED + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + led.set_high(); + } +} diff --git a/examples/lakers-nrf52840/src/bin/radio_tx.rs b/examples/lakers-nrf52840/src/bin/radio_tx.rs new file mode 100644 index 00000000..766cce27 --- /dev/null +++ b/examples/lakers-nrf52840/src/bin/radio_tx.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] + +use defmt::info; +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::radio::ble::Mode; +use embassy_nrf::radio::ble::Radio; +use embassy_nrf::radio::TxPower; +use embassy_nrf::{bind_interrupts, peripherals, radio}; +use embassy_time::{Duration, Timer}; +use radio_common::{Packet, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; +use {defmt_rtt as _, panic_probe as _}; + +mod radio_common; + +bind_interrupts!(struct Irqs { + RADIO => radio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello world!"); + let mut config = embassy_nrf::config::Config::default(); + config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + info!("Starting BLE radio"); + let mut radio: Radio<'_, _> = Radio::new(p.RADIO, Irqs).into(); + + let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + led.set_high(); + + radio.set_mode(Mode::BLE_1MBIT); + radio.set_tx_power(TxPower::_0D_BM); + radio.set_frequency(FREQ); + + radio.set_access_address(ADV_ADDRESS); + radio.set_header_expansion(false); + radio.set_crc_init(ADV_CRC_INIT); + radio.set_crc_poly(CRC_POLY); + + unwrap!(spawner.spawn(transmit_and_blink(radio, led, Duration::from_millis(100)))); +} + +#[embassy_executor::task] +async fn transmit_and_blink( + mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>, + mut led: Output<'static>, + period: Duration, +) { + loop { + let mut packet: radio_common::Packet = Default::default(); + packet.fill_with_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); + radio.transmit(packet.as_bytes()).await.unwrap(); + + // blink the LED + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + led.set_high(); + + // wait for period before continuing + Timer::after(period).await; + } +} From 37539ffaed3fd9bf25c67f7ba13865c45cccfaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Mon, 15 Jul 2024 12:06:15 +0200 Subject: [PATCH 02/10] example: clean up deps and add lakers --- examples/lakers-nrf52840/Cargo.toml | 34 +++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/examples/lakers-nrf52840/Cargo.toml b/examples/lakers-nrf52840/Cargo.toml index df8d7aba..d47db556 100644 --- a/examples/lakers-nrf52840/Cargo.toml +++ b/examples/lakers-nrf52840/Cargo.toml @@ -9,36 +9,32 @@ keywords.workspace = true categories.workspace = true [dependencies] -embassy-futures = { git = "https://github.com/embassy-rs/embassy", branch = "main" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } + +# embassy deps embassy-executor = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -embassy-net = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } -embassy-usb = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } -embassy-net-esp-hosted = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } -embassy-net-enc28j60 = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt"] } -embedded-io = { version = "0.6.0", features = ["defmt-03"] } -embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } +# lakers +lakers = { package = "lakers", path = "../../lib", default-features = false } +lakers-crypto = { path = "../../crypto", default-features = false } + +# misc +hexlit = "0.5.3" +# defmt defmt = "0.3" defmt-rtt = "0.4" -fixed = "1.10.0" -static_cell = { version = "2" } +# arm cortex m3 deps cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } -rand = { version = "0.8.4", default-features = false } -embedded-storage = "0.3.1" -usbd-hid = "0.7.0" -serde = { version = "1.0.136", default-features = false } -embedded-hal = { version = "1.0" } -embedded-hal-async = { version = "1.0" } -embedded-hal-bus = { version = "0.1", features = ["async"] } -num-integer = { version = "0.1.45", default-features = false } -microfft = "0.5.0" + +[features] +default = [ "crypto-cryptocell310", "ead-none" ] +crypto-cryptocell310 = [ "lakers-crypto/cryptocell310" ] +ead-none = [ ] [profile.release] debug = 2 From 6945af6c72ee95fd0af74a069f95b245f3a610cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Mon, 15 Jul 2024 16:51:49 +0200 Subject: [PATCH 03/10] example: implement basic ping pong mechanism with timeout --- .../lakers-nrf52840/src/bin/radio_common.rs | 45 +++++++++++++++++++ examples/lakers-nrf52840/src/bin/radio_rx.rs | 16 ++++--- examples/lakers-nrf52840/src/bin/radio_tx.rs | 31 ++++++++----- 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/examples/lakers-nrf52840/src/bin/radio_common.rs b/examples/lakers-nrf52840/src/bin/radio_common.rs index f154d58d..05d53caf 100644 --- a/examples/lakers-nrf52840/src/bin/radio_common.rs +++ b/examples/lakers-nrf52840/src/bin/radio_common.rs @@ -1,3 +1,10 @@ +use embassy_nrf::radio::ble::Radio; +use embassy_nrf::saadc::Time; +use embassy_nrf::{peripherals, radio}; +use embassy_time::Duration; +use embassy_time::TimeoutError; +use embassy_time::WithTimeout; + pub const MAX_PDU: usize = 258; pub const FREQ: u32 = 2408; pub const ADV_ADDRESS: u32 = 0x12345678; @@ -9,6 +16,8 @@ pub enum PacketError { SliceTooLong, SliceTooShort, ParsingError, + TimeoutError, + RadioError, } pub struct Packet { pub len: usize, @@ -75,3 +84,39 @@ impl TryInto for &[u8] { } } } + +impl From for PacketError { + fn from(error: TimeoutError) -> Self { + PacketError::TimeoutError + } +} + +impl From for PacketError { + fn from(error: embassy_nrf::radio::Error) -> Self { + match error { + _ => PacketError::RadioError, + } + } +} + +pub async fn transmit_and_wait_response( + radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>, + mut packet: Packet, + timeout: Duration, +) -> Result { + let mut rcvd_packet: Packet = Default::default(); + let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; + + radio.transmit(packet.as_bytes()).await?; + radio.receive(&mut buffer).with_timeout(timeout).await?; + + Ok(buffer[..].try_into().unwrap()) +} + +pub async fn transmit_without_response( + radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>, + mut packet: Packet, +) -> Result<(), PacketError> { + radio.transmit(packet.as_bytes()).await?; + Ok(()) +} diff --git a/examples/lakers-nrf52840/src/bin/radio_rx.rs b/examples/lakers-nrf52840/src/bin/radio_rx.rs index 3ecb71e5..793d6b56 100644 --- a/examples/lakers-nrf52840/src/bin/radio_rx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_rx.rs @@ -55,11 +55,17 @@ async fn receive_and_blink( let res = radio.receive(&mut buffer).await.unwrap(); let packet: Packet = buffer[..].try_into().unwrap(); - info!("Packet received: {:X}", packet.pdu[..packet.len]); + if packet.pdu[..packet.len] == [0xDEu8, 0xAD, 0xBE, 0xEF] { + info!("Received a ping. Pong'ing..."); + let pong = Packet::new_from_slice(&[0xCA, 0xFE, 0xCA, 0xFE]).unwrap(); + radio_common::transmit_without_response(&mut radio, pong) + .await + .unwrap(); - // blink the LED - led.set_low(); - Timer::after(Duration::from_millis(50)).await; - led.set_high(); + // blink the LED + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + led.set_high(); + } } } diff --git a/examples/lakers-nrf52840/src/bin/radio_tx.rs b/examples/lakers-nrf52840/src/bin/radio_tx.rs index 766cce27..cab2697b 100644 --- a/examples/lakers-nrf52840/src/bin/radio_tx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_tx.rs @@ -9,8 +9,8 @@ use embassy_nrf::radio::ble::Mode; use embassy_nrf::radio::ble::Radio; use embassy_nrf::radio::TxPower; use embassy_nrf::{bind_interrupts, peripherals, radio}; +use embassy_time::WithTimeout; use embassy_time::{Duration, Timer}; -use radio_common::{Packet, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; use {defmt_rtt as _, panic_probe as _}; mod radio_common; @@ -34,12 +34,12 @@ async fn main(spawner: Spawner) { radio.set_mode(Mode::BLE_1MBIT); radio.set_tx_power(TxPower::_0D_BM); - radio.set_frequency(FREQ); + radio.set_frequency(radio_common::FREQ); - radio.set_access_address(ADV_ADDRESS); + radio.set_access_address(radio_common::ADV_ADDRESS); radio.set_header_expansion(false); - radio.set_crc_init(ADV_CRC_INIT); - radio.set_crc_poly(CRC_POLY); + radio.set_crc_init(radio_common::ADV_CRC_INIT); + radio.set_crc_poly(radio_common::CRC_POLY); unwrap!(spawner.spawn(transmit_and_blink(radio, led, Duration::from_millis(100)))); } @@ -51,14 +51,21 @@ async fn transmit_and_blink( period: Duration, ) { loop { - let mut packet: radio_common::Packet = Default::default(); - packet.fill_with_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); - radio.transmit(packet.as_bytes()).await.unwrap(); + let mut packet_to_transmit = + radio_common::Packet::new_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]).unwrap(); - // blink the LED - led.set_low(); - Timer::after(Duration::from_millis(50)).await; - led.set_high(); + let rcvd = radio_common::transmit_and_wait_response( + &mut radio, + packet_to_transmit, + Duration::from_secs(1), + ) + .await; + + match rcvd { + Ok(packet) => info!("Packet received: {:X}", packet.pdu[..packet.len]), + Err(radio_common::PacketError::TimeoutError) => info!("Timeout!"), + _ => info!("Unhandled error!"), + } // wait for period before continuing Timer::after(period).await; From fed960cd5bfaa116f3bda7bfbad5c99ef014297b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Tue, 16 Jul 2024 10:51:30 +0200 Subject: [PATCH 04/10] example, radio: allow pdu header to be sent prepended to the pdu --- .../lakers-nrf52840/src/bin/radio_common.rs | 29 +++++++++++++++---- examples/lakers-nrf52840/src/bin/radio_rx.rs | 4 +-- examples/lakers-nrf52840/src/bin/radio_tx.rs | 2 +- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/examples/lakers-nrf52840/src/bin/radio_common.rs b/examples/lakers-nrf52840/src/bin/radio_common.rs index 05d53caf..5f144a5e 100644 --- a/examples/lakers-nrf52840/src/bin/radio_common.rs +++ b/examples/lakers-nrf52840/src/bin/radio_common.rs @@ -21,6 +21,7 @@ pub enum PacketError { } pub struct Packet { pub len: usize, + pub pdu_header: Option, pub pdu: [u8; MAX_PDU], } @@ -28,6 +29,7 @@ impl Default for Packet { fn default() -> Self { Packet { len: 0, + pdu_header: None, pdu: [0u8; MAX_PDU], } } @@ -37,22 +39,24 @@ impl Packet { pub fn new() -> Self { Packet { len: 0, + pdu_header: None, pdu: [0u8; MAX_PDU], } } - pub fn new_from_slice(slice: &[u8]) -> Result { + pub fn new_from_slice(slice: &[u8], header: Option) -> Result { let mut buffer = Self::new(); - if buffer.fill_with_slice(slice).is_ok() { + if buffer.fill_with_slice(slice, header).is_ok() { Ok(buffer) } else { Err(PacketError::SliceTooLong) } } - pub fn fill_with_slice(&mut self, slice: &[u8]) -> Result<(), PacketError> { + pub fn fill_with_slice(&mut self, slice: &[u8], header: Option) -> Result<(), PacketError> { if slice.len() <= self.pdu.len() { self.len = slice.len(); + self.pdu_header = header; self.pdu[..self.len].copy_from_slice(slice); Ok(()) } else { @@ -61,11 +65,24 @@ impl Packet { } pub fn as_bytes(&mut self) -> &[u8] { - self.pdu.copy_within(..self.len, 2); + let mut offset = 0; + let mut len: usize = 0; + + if let Some(header) = self.pdu_header { + offset = 3; + len = self.len + 1; + } else { + offset = 2; + len = self.len; + } + self.pdu.copy_within(..self.len, offset); self.pdu[0] = 0x00; - self.pdu[1] = self.len as u8; + self.pdu[1] = len as u8; - &self.pdu[..self.len] + if let Some(header) = self.pdu_header { + self.pdu[2] = header; + } + &self.pdu[..len] } } diff --git a/examples/lakers-nrf52840/src/bin/radio_rx.rs b/examples/lakers-nrf52840/src/bin/radio_rx.rs index 793d6b56..19c2d9f8 100644 --- a/examples/lakers-nrf52840/src/bin/radio_rx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_rx.rs @@ -55,9 +55,9 @@ async fn receive_and_blink( let res = radio.receive(&mut buffer).await.unwrap(); let packet: Packet = buffer[..].try_into().unwrap(); - if packet.pdu[..packet.len] == [0xDEu8, 0xAD, 0xBE, 0xEF] { + if packet.pdu[..packet.len] == [0xf5u8, 0xDEu8, 0xAD, 0xBE, 0xEF] { info!("Received a ping. Pong'ing..."); - let pong = Packet::new_from_slice(&[0xCA, 0xFE, 0xCA, 0xFE]).unwrap(); + let pong = Packet::new_from_slice(&[0xCA, 0xFE, 0xCA, 0xFE], None).unwrap(); radio_common::transmit_without_response(&mut radio, pong) .await .unwrap(); diff --git a/examples/lakers-nrf52840/src/bin/radio_tx.rs b/examples/lakers-nrf52840/src/bin/radio_tx.rs index cab2697b..3655c004 100644 --- a/examples/lakers-nrf52840/src/bin/radio_tx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_tx.rs @@ -52,7 +52,7 @@ async fn transmit_and_blink( ) { loop { let mut packet_to_transmit = - radio_common::Packet::new_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]).unwrap(); + radio_common::Packet::new_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF], Some(0xf5)).unwrap(); let rcvd = radio_common::transmit_and_wait_response( &mut radio, From 8793ba1d7161350581d8ea9f060643b681947a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 10:50:53 +0200 Subject: [PATCH 05/10] example: two nRF52840-DKs complete the handshake --- examples/lakers-nrf52840/Cargo.toml | 6 +- .../lakers-nrf52840/src/bin/radio_common.rs | 39 +++++- examples/lakers-nrf52840/src/bin/radio_rx.rs | 123 +++++++++++++++--- examples/lakers-nrf52840/src/bin/radio_tx.rs | 79 ++++++++--- 4 files changed, 204 insertions(+), 43 deletions(-) diff --git a/examples/lakers-nrf52840/Cargo.toml b/examples/lakers-nrf52840/Cargo.toml index d47db556..119f06ca 100644 --- a/examples/lakers-nrf52840/Cargo.toml +++ b/examples/lakers-nrf52840/Cargo.toml @@ -26,14 +26,18 @@ hexlit = "0.5.3" defmt = "0.3" defmt-rtt = "0.4" +# depend on an allocator +embedded-alloc = "0.5.0" + # arm cortex m3 deps cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } [features] -default = [ "crypto-cryptocell310", "ead-none" ] +default = [ "crypto-psa", "ead-none" ] crypto-cryptocell310 = [ "lakers-crypto/cryptocell310" ] +crypto-psa = [ "lakers-crypto/psa-baremetal" ] ead-none = [ ] [profile.release] diff --git a/examples/lakers-nrf52840/src/bin/radio_common.rs b/examples/lakers-nrf52840/src/bin/radio_common.rs index 5f144a5e..69a94742 100644 --- a/examples/lakers-nrf52840/src/bin/radio_common.rs +++ b/examples/lakers-nrf52840/src/bin/radio_common.rs @@ -4,6 +4,9 @@ use embassy_nrf::{peripherals, radio}; use embassy_time::Duration; use embassy_time::TimeoutError; use embassy_time::WithTimeout; +use hexlit::hex; + +use defmt::info; pub const MAX_PDU: usize = 258; pub const FREQ: u32 = 2408; @@ -11,6 +14,13 @@ pub const ADV_ADDRESS: u32 = 0x12345678; pub const ADV_CRC_INIT: u32 = 0xffff; pub const CRC_POLY: u32 = 0x00065b; +pub const _ID_CRED_I: &[u8] = &hex!("a104412b"); +pub const _ID_CRED_R: &[u8] = &hex!("a104410a"); +pub const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8"); +pub const I: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b"); +pub const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac"); +pub const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072"); + #[derive(Debug)] pub enum PacketError { SliceTooLong, @@ -116,18 +126,34 @@ impl From for PacketError { } } +pub async fn receive_and_filter( + radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>, + header: Option, +) -> Result { + let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; + loop { + radio.receive(&mut buffer).await?; + let pckt: Packet = buffer[..].try_into().unwrap(); + if header == None || pckt.pdu[0] == header.unwrap() { + return Ok(pckt); + } else { + continue; + } + } +} + pub async fn transmit_and_wait_response( radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>, mut packet: Packet, - timeout: Duration, + filter: Option, ) -> Result { let mut rcvd_packet: Packet = Default::default(); let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; radio.transmit(packet.as_bytes()).await?; - radio.receive(&mut buffer).with_timeout(timeout).await?; + let resp = receive_and_filter(radio, filter).await?; - Ok(buffer[..].try_into().unwrap()) + Ok(resp) } pub async fn transmit_without_response( @@ -137,3 +163,10 @@ pub async fn transmit_without_response( radio.transmit(packet.as_bytes()).await?; Ok(()) } + +use core::ffi::{c_char, c_void}; +#[no_mangle] +pub extern "C" fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char { + panic!("strstr handler!"); + core::ptr::null_mut() +} diff --git a/examples/lakers-nrf52840/src/bin/radio_rx.rs b/examples/lakers-nrf52840/src/bin/radio_rx.rs index 19c2d9f8..80a08664 100644 --- a/examples/lakers-nrf52840/src/bin/radio_rx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_rx.rs @@ -13,6 +13,21 @@ use embassy_time::{Duration, Timer}; use radio_common::{Packet, PacketError, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; use {defmt_rtt as _, panic_probe as _}; +use lakers::*; + +use core::ffi::c_char; + +extern crate alloc; + +use embedded_alloc::Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +extern "C" { + pub fn mbedtls_memory_buffer_alloc_init(buf: *mut c_char, len: usize); +} + mod radio_common; bind_interrupts!(struct Irqs { @@ -40,32 +55,98 @@ async fn main(spawner: Spawner) { radio.set_crc_init(ADV_CRC_INIT); radio.set_crc_poly(CRC_POLY); - unwrap!(spawner.spawn(receive_and_blink(radio, led))); -} - -#[embassy_executor::task] -async fn receive_and_blink( - mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>, - mut led: Output<'static>, -) { - info!("Hello from receive_and_blink"); + // Memory buffer for mbedtls + #[cfg(feature = "crypto-psa")] + let mut buffer: [c_char; 4096 * 2] = [0; 4096 * 2]; + #[cfg(feature = "crypto-psa")] + unsafe { + mbedtls_memory_buffer_alloc_init(buffer.as_mut_ptr(), buffer.len()); + } loop { let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; - let res = radio.receive(&mut buffer).await.unwrap(); - let packet: Packet = buffer[..].try_into().unwrap(); - - if packet.pdu[..packet.len] == [0xf5u8, 0xDEu8, 0xAD, 0xBE, 0xEF] { - info!("Received a ping. Pong'ing..."); - let pong = Packet::new_from_slice(&[0xCA, 0xFE, 0xCA, 0xFE], None).unwrap(); - radio_common::transmit_without_response(&mut radio, pong) - .await + let mut c_r: Option = None; + let pckt = radio_common::receive_and_filter(&mut radio, Some(0xf5)) // filter all incoming packets waiting for CBOR TRUE (0xf5) + .await + .unwrap(); + + info!("Received message_1"); + + let cred_r = CredentialRPK::new(radio_common::CRED_R.try_into().unwrap()).unwrap(); + let responder = + EdhocResponder::new(lakers_crypto::default_crypto(), &radio_common::R, cred_r); + + let message_1: EdhocMessageBuffer = pckt.pdu[1..pckt.len].try_into().expect("wrong length"); // get rid of the TRUE byte + + let result = responder.process_message_1(&message_1); + + if let Ok((responder, _c_i, ead_1)) = result { + c_r = Some(generate_connection_identifier_cbor( + &mut lakers_crypto::default_crypto(), + )); + let ead_2 = None; + + let (responder, message_2) = responder + .prepare_message_2(CredentialTransfer::ByReference, c_r, &ead_2) .unwrap(); - // blink the LED - led.set_low(); - Timer::after(Duration::from_millis(50)).await; - led.set_high(); + // prepend 0xf5 also to message_2 in order to allow the Initiator filter out from other BLE packets + let message_3 = radio_common::transmit_and_wait_response( + &mut radio, + Packet::new_from_slice(message_2.as_slice(), Some(0xf5u8)).expect("wrong length"), + Some(c_r.unwrap().as_slice()[0]), + ) + .await; + + match message_3 { + Ok(message_3) => { + info!("Received message_3"); + + let rcvd_c_r: ConnId = ConnId::from_int_raw(message_3.pdu[0] as u8); + + if rcvd_c_r == c_r.unwrap() { + let message_3: EdhocMessageBuffer = message_3.pdu[1..message_3.len] + .try_into() + .expect("wrong length"); + let Ok((responder, id_cred_i, _ead_3)) = + responder.parse_message_3(&message_3) + else { + info!("EDHOC error at parse_message_3"); + // We don't get another chance, it's popped and can't be used any further + // anyway legally + continue; + }; + + let cred_i = + CredentialRPK::new(radio_common::CRED_I.try_into().unwrap()).unwrap(); + let valid_cred_i = + credential_check_or_fetch(Some(cred_i), id_cred_i).unwrap(); + + let Ok((mut responder, prk_out)) = responder.verify_message_3(valid_cred_i) + else { + info!("EDHOC error at verify_message_3"); + continue; + }; + + info!("Handshake completed. prk_out: {:X}", prk_out); + + unwrap!(spawner.spawn(application_task_1(prk_out))); + } else { + info!("Another packet interrupted the handshake."); + continue; + } + } + Err(PacketError::TimeoutError) => info!("Timeout while waiting for message_3!"), + Err(_) => panic!("Unexpected error"), + } } } } + +#[embassy_executor::task] +async fn application_task_1(secret: BytesHashLen) { + info!( + "Successfully spawned an application task. EDHOC prk_out: {:X}", + secret + ); +} diff --git a/examples/lakers-nrf52840/src/bin/radio_tx.rs b/examples/lakers-nrf52840/src/bin/radio_tx.rs index 3655c004..8df8b82e 100644 --- a/examples/lakers-nrf52840/src/bin/radio_tx.rs +++ b/examples/lakers-nrf52840/src/bin/radio_tx.rs @@ -13,6 +13,22 @@ use embassy_time::WithTimeout; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +use lakers::*; +use lakers_crypto::{default_crypto, CryptoTrait}; + +extern crate alloc; + +use embedded_alloc::Heap; + +use core::ffi::c_char; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +extern "C" { + pub fn mbedtls_memory_buffer_alloc_init(buf: *mut c_char, len: usize); +} + mod radio_common; bind_interrupts!(struct Irqs { @@ -50,24 +66,51 @@ async fn transmit_and_blink( mut led: Output<'static>, period: Duration, ) { - loop { - let mut packet_to_transmit = - radio_common::Packet::new_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF], Some(0xf5)).unwrap(); - - let rcvd = radio_common::transmit_and_wait_response( - &mut radio, - packet_to_transmit, - Duration::from_secs(1), - ) - .await; - - match rcvd { - Ok(packet) => info!("Packet received: {:X}", packet.pdu[..packet.len]), - Err(radio_common::PacketError::TimeoutError) => info!("Timeout!"), - _ => info!("Unhandled error!"), - } + info!("transmit_and_blink"); + + // Memory buffer for mbedtls + #[cfg(feature = "crypto-psa")] + let mut buffer: [c_char; 4096 * 2] = [0; 4096 * 2]; + #[cfg(feature = "crypto-psa")] + unsafe { + mbedtls_memory_buffer_alloc_init(buffer.as_mut_ptr(), buffer.len()); + } + + let cred_i = CredentialRPK::new(radio_common::CRED_I.try_into().unwrap()).unwrap(); + let cred_r = CredentialRPK::new(radio_common::CRED_R.try_into().unwrap()).unwrap(); + + let mut initiator = EdhocInitiator::new(lakers_crypto::default_crypto()); + + // Send Message 1 over CoAP and convert the response to byte + let c_i = generate_connection_identifier_cbor(&mut lakers_crypto::default_crypto()); + let (initiator, message_1) = initiator.prepare_message_1(Some(c_i), &None).unwrap(); + let pckt_1 = radio_common::Packet::new_from_slice(message_1.as_slice(), Some(0xf5u8)) + .expect("Buffer not long enough"); + + let rcvd = radio_common::transmit_and_wait_response(&mut radio, pckt_1, Some(0xf5u8)).await; - // wait for period before continuing - Timer::after(period).await; + match rcvd { + Ok(pckt_2) => { + let message_2 = EdhocMessageBuffer::new_from_slice(&pckt_2.pdu[1..pckt_2.len]).unwrap(); + let (initiator, c_r, id_cred_r, ead_2) = initiator.parse_message_2(&message_2).unwrap(); + let valid_cred_r = credential_check_or_fetch(Some(cred_r), id_cred_r).unwrap(); + let initiator = initiator + .verify_message_2(radio_common::I, cred_i, valid_cred_r) + .unwrap(); + + let (mut initiator, message_3, i_prk_out) = initiator + .prepare_message_3(CredentialTransfer::ByReference, &None) + .unwrap(); + + radio_common::transmit_without_response( + &mut radio, + radio_common::Packet::new_from_slice(message_3.as_slice(), Some(c_r.as_slice()[0])) + .unwrap(), + ) + .await; + + info!("Handshake completed. prk_out = {:X}", i_prk_out); + } + Err(_) => panic!("parsing error"), } } From 3ae5258ad868090d5a1bfe059bc14d11cc6b9d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 11:32:19 +0200 Subject: [PATCH 06/10] example: rename files to initiator, responder and common --- .../src/bin/{radio_common.rs => common.rs} | 0 .../src/bin/{radio_tx.rs => initiator.rs} | 34 ++++++++----------- .../src/bin/{radio_rx.rs => responder.rs} | 15 ++++---- 3 files changed, 22 insertions(+), 27 deletions(-) rename examples/lakers-nrf52840/src/bin/{radio_common.rs => common.rs} (100%) rename examples/lakers-nrf52840/src/bin/{radio_tx.rs => initiator.rs} (72%) rename examples/lakers-nrf52840/src/bin/{radio_rx.rs => responder.rs} (88%) diff --git a/examples/lakers-nrf52840/src/bin/radio_common.rs b/examples/lakers-nrf52840/src/bin/common.rs similarity index 100% rename from examples/lakers-nrf52840/src/bin/radio_common.rs rename to examples/lakers-nrf52840/src/bin/common.rs diff --git a/examples/lakers-nrf52840/src/bin/radio_tx.rs b/examples/lakers-nrf52840/src/bin/initiator.rs similarity index 72% rename from examples/lakers-nrf52840/src/bin/radio_tx.rs rename to examples/lakers-nrf52840/src/bin/initiator.rs index 8df8b82e..01f7f6ce 100644 --- a/examples/lakers-nrf52840/src/bin/radio_tx.rs +++ b/examples/lakers-nrf52840/src/bin/initiator.rs @@ -29,7 +29,7 @@ extern "C" { pub fn mbedtls_memory_buffer_alloc_init(buf: *mut c_char, len: usize); } -mod radio_common; +mod common; bind_interrupts!(struct Irqs { RADIO => radio::InterruptHandler; @@ -50,23 +50,19 @@ async fn main(spawner: Spawner) { radio.set_mode(Mode::BLE_1MBIT); radio.set_tx_power(TxPower::_0D_BM); - radio.set_frequency(radio_common::FREQ); + radio.set_frequency(common::FREQ); - radio.set_access_address(radio_common::ADV_ADDRESS); + radio.set_access_address(common::ADV_ADDRESS); radio.set_header_expansion(false); - radio.set_crc_init(radio_common::ADV_CRC_INIT); - radio.set_crc_poly(radio_common::CRC_POLY); + radio.set_crc_init(common::ADV_CRC_INIT); + radio.set_crc_poly(common::CRC_POLY); - unwrap!(spawner.spawn(transmit_and_blink(radio, led, Duration::from_millis(100)))); + unwrap!(spawner.spawn(init_handshake(radio))); } #[embassy_executor::task] -async fn transmit_and_blink( - mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>, - mut led: Output<'static>, - period: Duration, -) { - info!("transmit_and_blink"); +async fn init_handshake(mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>) { + info!("init_handshake"); // Memory buffer for mbedtls #[cfg(feature = "crypto-psa")] @@ -76,18 +72,18 @@ async fn transmit_and_blink( mbedtls_memory_buffer_alloc_init(buffer.as_mut_ptr(), buffer.len()); } - let cred_i = CredentialRPK::new(radio_common::CRED_I.try_into().unwrap()).unwrap(); - let cred_r = CredentialRPK::new(radio_common::CRED_R.try_into().unwrap()).unwrap(); + let cred_i = CredentialRPK::new(common::CRED_I.try_into().unwrap()).unwrap(); + let cred_r = CredentialRPK::new(common::CRED_R.try_into().unwrap()).unwrap(); let mut initiator = EdhocInitiator::new(lakers_crypto::default_crypto()); // Send Message 1 over CoAP and convert the response to byte let c_i = generate_connection_identifier_cbor(&mut lakers_crypto::default_crypto()); let (initiator, message_1) = initiator.prepare_message_1(Some(c_i), &None).unwrap(); - let pckt_1 = radio_common::Packet::new_from_slice(message_1.as_slice(), Some(0xf5u8)) + let pckt_1 = common::Packet::new_from_slice(message_1.as_slice(), Some(0xf5u8)) .expect("Buffer not long enough"); - let rcvd = radio_common::transmit_and_wait_response(&mut radio, pckt_1, Some(0xf5u8)).await; + let rcvd = common::transmit_and_wait_response(&mut radio, pckt_1, Some(0xf5u8)).await; match rcvd { Ok(pckt_2) => { @@ -95,16 +91,16 @@ async fn transmit_and_blink( let (initiator, c_r, id_cred_r, ead_2) = initiator.parse_message_2(&message_2).unwrap(); let valid_cred_r = credential_check_or_fetch(Some(cred_r), id_cred_r).unwrap(); let initiator = initiator - .verify_message_2(radio_common::I, cred_i, valid_cred_r) + .verify_message_2(common::I, cred_i, valid_cred_r) .unwrap(); let (mut initiator, message_3, i_prk_out) = initiator .prepare_message_3(CredentialTransfer::ByReference, &None) .unwrap(); - radio_common::transmit_without_response( + common::transmit_without_response( &mut radio, - radio_common::Packet::new_from_slice(message_3.as_slice(), Some(c_r.as_slice()[0])) + common::Packet::new_from_slice(message_3.as_slice(), Some(c_r.as_slice()[0])) .unwrap(), ) .await; diff --git a/examples/lakers-nrf52840/src/bin/radio_rx.rs b/examples/lakers-nrf52840/src/bin/responder.rs similarity index 88% rename from examples/lakers-nrf52840/src/bin/radio_rx.rs rename to examples/lakers-nrf52840/src/bin/responder.rs index 80a08664..07022c95 100644 --- a/examples/lakers-nrf52840/src/bin/radio_rx.rs +++ b/examples/lakers-nrf52840/src/bin/responder.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use common::{Packet, PacketError, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; use defmt::info; use defmt::unwrap; use embassy_executor::Spawner; @@ -10,7 +11,6 @@ use embassy_nrf::radio::ble::Radio; use embassy_nrf::radio::TxPower; use embassy_nrf::{bind_interrupts, peripherals, radio}; use embassy_time::{Duration, Timer}; -use radio_common::{Packet, PacketError, ADV_ADDRESS, ADV_CRC_INIT, CRC_POLY, FREQ, MAX_PDU}; use {defmt_rtt as _, panic_probe as _}; use lakers::*; @@ -28,7 +28,7 @@ extern "C" { pub fn mbedtls_memory_buffer_alloc_init(buf: *mut c_char, len: usize); } -mod radio_common; +mod common; bind_interrupts!(struct Irqs { RADIO => radio::InterruptHandler; @@ -66,15 +66,14 @@ async fn main(spawner: Spawner) { loop { let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; let mut c_r: Option = None; - let pckt = radio_common::receive_and_filter(&mut radio, Some(0xf5)) // filter all incoming packets waiting for CBOR TRUE (0xf5) + let pckt = common::receive_and_filter(&mut radio, Some(0xf5)) // filter all incoming packets waiting for CBOR TRUE (0xf5) .await .unwrap(); info!("Received message_1"); - let cred_r = CredentialRPK::new(radio_common::CRED_R.try_into().unwrap()).unwrap(); - let responder = - EdhocResponder::new(lakers_crypto::default_crypto(), &radio_common::R, cred_r); + let cred_r = CredentialRPK::new(common::CRED_R.try_into().unwrap()).unwrap(); + let responder = EdhocResponder::new(lakers_crypto::default_crypto(), &common::R, cred_r); let message_1: EdhocMessageBuffer = pckt.pdu[1..pckt.len].try_into().expect("wrong length"); // get rid of the TRUE byte @@ -91,7 +90,7 @@ async fn main(spawner: Spawner) { .unwrap(); // prepend 0xf5 also to message_2 in order to allow the Initiator filter out from other BLE packets - let message_3 = radio_common::transmit_and_wait_response( + let message_3 = common::transmit_and_wait_response( &mut radio, Packet::new_from_slice(message_2.as_slice(), Some(0xf5u8)).expect("wrong length"), Some(c_r.unwrap().as_slice()[0]), @@ -118,7 +117,7 @@ async fn main(spawner: Spawner) { }; let cred_i = - CredentialRPK::new(radio_common::CRED_I.try_into().unwrap()).unwrap(); + CredentialRPK::new(common::CRED_I.try_into().unwrap()).unwrap(); let valid_cred_i = credential_check_or_fetch(Some(cred_i), id_cred_i).unwrap(); From b4e4e16144a8ceac2f32996a98a9808f1b79e512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 15:33:32 +0200 Subject: [PATCH 07/10] example: Add README.md --- examples/lakers-nrf52840/Cargo.toml | 3 +-- examples/lakers-nrf52840/README.md | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 examples/lakers-nrf52840/README.md diff --git a/examples/lakers-nrf52840/Cargo.toml b/examples/lakers-nrf52840/Cargo.toml index 119f06ca..045b1d33 100644 --- a/examples/lakers-nrf52840/Cargo.toml +++ b/examples/lakers-nrf52840/Cargo.toml @@ -4,7 +4,6 @@ edition = "2021" version.workspace = true repository.workspace = true license.workspace = true -readme.workspace = true keywords.workspace = true categories.workspace = true @@ -15,7 +14,7 @@ embassy-executor = { git = "https://github.com/embassy-rs/embassy", branch = "m embassy-time = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -# lakers +# lakers lakers = { package = "lakers", path = "../../lib", default-features = false } lakers-crypto = { path = "../../crypto", default-features = false } diff --git a/examples/lakers-nrf52840/README.md b/examples/lakers-nrf52840/README.md new file mode 100644 index 00000000..5d0d4cb6 --- /dev/null +++ b/examples/lakers-nrf52840/README.md @@ -0,0 +1,23 @@ +# Introduction + +This folder contains a bare metal (no_std) example of EDHOC Initiator and EDHOC Responder for nRF52840-DK. +The Initiator and Responder communicate over a raw BLE radio. + +The example is configured to be ran on nRF52840-DK board. + +## Prerequisites + +- install probe-rs +- `rustup target add thumbv7m-none-eabi` + +## How to use + +This folder's `.cargo/config.toml` configures the target (`thumbv7m-none-eabi`) and the probe-rs runner so the things should just work: + + cargo run --bin initiator + cargo run --bin responder + +You may want to prefix the commands above with e.g. PROBE_RS_PROBE=1366:1051:001050288491 in order to specify which board you want to connect to. +You can get the name of your probes by running: + + probe-rs list From a153fb100f69446d517b927e8323931104cfc8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 16:32:28 +0200 Subject: [PATCH 08/10] example: rewrite receive_and_filter in a safe manner --- examples/lakers-nrf52840/src/bin/common.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/lakers-nrf52840/src/bin/common.rs b/examples/lakers-nrf52840/src/bin/common.rs index 69a94742..0e01c6a6 100644 --- a/examples/lakers-nrf52840/src/bin/common.rs +++ b/examples/lakers-nrf52840/src/bin/common.rs @@ -133,9 +133,17 @@ pub async fn receive_and_filter( let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU]; loop { radio.receive(&mut buffer).await?; - let pckt: Packet = buffer[..].try_into().unwrap(); - if header == None || pckt.pdu[0] == header.unwrap() { - return Ok(pckt); + if let Ok(pckt) = <&[u8] as TryInto>::try_into(&(buffer[..])) { + if let Some(header) = header { + if pckt.pdu[0] == header { + return Ok(pckt); + } else { + continue; + } + } else { + // header is None + return Ok(pckt); + } } else { continue; } From 22af7680cb7af2151cc4b2de3c29adb4462d25dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 16:44:58 +0200 Subject: [PATCH 09/10] example: address review comments --- examples/lakers-nrf52840/src/bin/common.rs | 2 -- examples/lakers-nrf52840/src/bin/initiator.rs | 18 ++++++------------ examples/lakers-nrf52840/src/bin/responder.rs | 13 +++++-------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/lakers-nrf52840/src/bin/common.rs b/examples/lakers-nrf52840/src/bin/common.rs index 0e01c6a6..c804987c 100644 --- a/examples/lakers-nrf52840/src/bin/common.rs +++ b/examples/lakers-nrf52840/src/bin/common.rs @@ -14,8 +14,6 @@ pub const ADV_ADDRESS: u32 = 0x12345678; pub const ADV_CRC_INIT: u32 = 0xffff; pub const CRC_POLY: u32 = 0x00065b; -pub const _ID_CRED_I: &[u8] = &hex!("a104412b"); -pub const _ID_CRED_R: &[u8] = &hex!("a104410a"); pub const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8"); pub const I: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b"); pub const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac"); diff --git a/examples/lakers-nrf52840/src/bin/initiator.rs b/examples/lakers-nrf52840/src/bin/initiator.rs index 01f7f6ce..ea11a4b4 100644 --- a/examples/lakers-nrf52840/src/bin/initiator.rs +++ b/examples/lakers-nrf52840/src/bin/initiator.rs @@ -37,15 +37,14 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(spawner: Spawner) { - info!("Hello world!"); let mut config = embassy_nrf::config::Config::default(); config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; - let p = embassy_nrf::init(config); + let peripherals = embassy_nrf::init(config); info!("Starting BLE radio"); - let mut radio: Radio<'_, _> = Radio::new(p.RADIO, Irqs).into(); + let mut radio: Radio<'_, _> = Radio::new(peripherals.RADIO, Irqs).into(); - let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + let mut led = Output::new(peripherals.P0_13, Level::Low, OutputDrive::Standard); led.set_high(); radio.set_mode(Mode::BLE_1MBIT); @@ -57,11 +56,6 @@ async fn main(spawner: Spawner) { radio.set_crc_init(common::ADV_CRC_INIT); radio.set_crc_poly(common::CRC_POLY); - unwrap!(spawner.spawn(init_handshake(radio))); -} - -#[embassy_executor::task] -async fn init_handshake(mut radio: Radio<'static, embassy_nrf::peripherals::RADIO>) { info!("init_handshake"); // Memory buffer for mbedtls @@ -77,13 +71,13 @@ async fn init_handshake(mut radio: Radio<'static, embassy_nrf::peripherals::RADI let mut initiator = EdhocInitiator::new(lakers_crypto::default_crypto()); - // Send Message 1 over CoAP and convert the response to byte + // Send Message 1 over raw BLE and convert the response to byte let c_i = generate_connection_identifier_cbor(&mut lakers_crypto::default_crypto()); let (initiator, message_1) = initiator.prepare_message_1(Some(c_i), &None).unwrap(); - let pckt_1 = common::Packet::new_from_slice(message_1.as_slice(), Some(0xf5u8)) + let pckt_1 = common::Packet::new_from_slice(message_1.as_slice(), Some(0xf5)) .expect("Buffer not long enough"); - let rcvd = common::transmit_and_wait_response(&mut radio, pckt_1, Some(0xf5u8)).await; + let rcvd = common::transmit_and_wait_response(&mut radio, pckt_1, Some(0xf5)).await; match rcvd { Ok(pckt_2) => { diff --git a/examples/lakers-nrf52840/src/bin/responder.rs b/examples/lakers-nrf52840/src/bin/responder.rs index 07022c95..d79ec382 100644 --- a/examples/lakers-nrf52840/src/bin/responder.rs +++ b/examples/lakers-nrf52840/src/bin/responder.rs @@ -38,13 +38,10 @@ bind_interrupts!(struct Irqs { async fn main(spawner: Spawner) { let mut config = embassy_nrf::config::Config::default(); config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; - let p = embassy_nrf::init(config); + let peripherals: embassy_nrf::Peripherals = embassy_nrf::init(config); info!("Starting BLE radio"); - let mut radio = Radio::new(p.RADIO, Irqs); - - let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); - led.set_high(); + let mut radio = Radio::new(peripherals.RADIO, Irqs); radio.set_mode(Mode::BLE_1MBIT); radio.set_tx_power(TxPower::_0D_BM); @@ -92,7 +89,7 @@ async fn main(spawner: Spawner) { // prepend 0xf5 also to message_2 in order to allow the Initiator filter out from other BLE packets let message_3 = common::transmit_and_wait_response( &mut radio, - Packet::new_from_slice(message_2.as_slice(), Some(0xf5u8)).expect("wrong length"), + Packet::new_from_slice(message_2.as_slice(), Some(0xf5)).expect("wrong length"), Some(c_r.unwrap().as_slice()[0]), ) .await; @@ -129,7 +126,7 @@ async fn main(spawner: Spawner) { info!("Handshake completed. prk_out: {:X}", prk_out); - unwrap!(spawner.spawn(application_task_1(prk_out))); + unwrap!(spawner.spawn(example_application_task(prk_out))); } else { info!("Another packet interrupted the handshake."); continue; @@ -143,7 +140,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn application_task_1(secret: BytesHashLen) { +async fn example_application_task(secret: BytesHashLen) { info!( "Successfully spawned an application task. EDHOC prk_out: {:X}", secret From c2e14e73aa86aa0fa2bc2e460c393688d1b95815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mali=C5=A1a=20Vu=C4=8Dini=C4=87?= Date: Wed, 17 Jul 2024 16:59:41 +0200 Subject: [PATCH 10/10] example: address review comments --- examples/lakers-nrf52840/src/bin/common.rs | 6 +++--- examples/lakers-nrf52840/src/bin/initiator.rs | 3 ++- examples/lakers-nrf52840/src/bin/responder.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/lakers-nrf52840/src/bin/common.rs b/examples/lakers-nrf52840/src/bin/common.rs index c804987c..dff31ff4 100644 --- a/examples/lakers-nrf52840/src/bin/common.rs +++ b/examples/lakers-nrf52840/src/bin/common.rs @@ -28,9 +28,9 @@ pub enum PacketError { RadioError, } pub struct Packet { - pub len: usize, - pub pdu_header: Option, - pub pdu: [u8; MAX_PDU], + pub len: usize, // total length that gets transmitted over the air, equals length of pdu + 1, for pdu_header + pub pdu_header: Option, // 1-byte application-level header, used for filtering the packets + pub pdu: [u8; MAX_PDU], // application-level payload } impl Default for Packet { diff --git a/examples/lakers-nrf52840/src/bin/initiator.rs b/examples/lakers-nrf52840/src/bin/initiator.rs index ea11a4b4..6efc6156 100644 --- a/examples/lakers-nrf52840/src/bin/initiator.rs +++ b/examples/lakers-nrf52840/src/bin/initiator.rs @@ -81,7 +81,8 @@ async fn main(spawner: Spawner) { match rcvd { Ok(pckt_2) => { - let message_2 = EdhocMessageBuffer::new_from_slice(&pckt_2.pdu[1..pckt_2.len]).unwrap(); + let message_2: EdhocMessageBuffer = + pckt_2.pdu[1..pckt_2.len].try_into().expect("wrong length"); let (initiator, c_r, id_cred_r, ead_2) = initiator.parse_message_2(&message_2).unwrap(); let valid_cred_r = credential_check_or_fetch(Some(cred_r), id_cred_r).unwrap(); let initiator = initiator diff --git a/examples/lakers-nrf52840/src/bin/responder.rs b/examples/lakers-nrf52840/src/bin/responder.rs index d79ec382..de8b8518 100644 --- a/examples/lakers-nrf52840/src/bin/responder.rs +++ b/examples/lakers-nrf52840/src/bin/responder.rs @@ -118,7 +118,7 @@ async fn main(spawner: Spawner) { let valid_cred_i = credential_check_or_fetch(Some(cred_i), id_cred_i).unwrap(); - let Ok((mut responder, prk_out)) = responder.verify_message_3(valid_cred_i) + let Ok((responder, prk_out)) = responder.verify_message_3(valid_cred_i) else { info!("EDHOC error at verify_message_3"); continue;