انتقل إلى المحتوى الرئيسي

nRF52840 — Rust Embassy USB Honeypot Sentinel

The nRF52840 serves as a USB honeypot sentinel and BLE mesh sensor. It monitors for unauthorized USB device insertions, broadcasts sensor data via BLE, and performs hardware-accelerated attestation using the CryptoCell CC310.

Role in the Architecture

Technical Specifications

FeatureDetail
MCUnRF52840 (ARM Cortex-M4F, 64MHz)
RAM256KB SRAM
Flash1MB
FrameworkEmbassy (async embedded Rust)
LanguageRust (#![no_std], #![no_main])
CryptoCryptoCell CC310 (ECDSA P-256, AES, SHA-256)
ConnectivityBLE 5.0 + USB 2.0
ProtocolMQTT-SN over UDP (via BLE → ESP32-S3)
Key StorageUICR (User Information Configuration Registers)
Heartbeat30 seconds

Why Rust Embassy?

FeatureEmbassyRTICZephyr (C)FreeRTOS (C)
Memory safetyCompile-timeCompile-timeRuntime checksNone
Async/awaitNativeNoNoNo
Zero-cost abstractionsYesYesNoNo
No-allocYes (#![no_std])YesPossiblePossible
Stack overflowImpossible (no threads)PossiblePossiblePossible

Embassy uses Rust's async/await on bare metal — no RTOS, no threads, no heap. Tasks are state machines compiled to static memory, eliminating entire classes of embedded bugs.

Main Entry Point

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_nrf as _;
use embassy_time::{Duration, Timer};

#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_nrf::init(Default::default());

// Spawn concurrent tasks (no threads — cooperative async)
spawner.spawn(usb_sentinel_task(/* ... */)).unwrap();
spawner.spawn(ble_advertiser_task(/* ... */)).unwrap();
spawner.spawn(attestation_task(/* ... */)).unwrap();

// Heartbeat loop
loop {
// Publish heartbeat via MQTT-SN
mqtt_sn_publish("aurora/sensors/nrf52_sentinel_01/status", heartbeat_payload);
Timer::after(Duration::from_secs(30)).await;
}
}

USB Sentinel (Honeypot)

The USB sentinel monitors the USB bus for unauthorized device insertions — a physical attack vector where an attacker plugs in a malicious USB device (Rubber Ducky, BadUSB, etc.).

#[embassy_executor::task]
async fn usb_sentinel_task(usb: embassy_nrf::usb::Driver<'static>) {
loop {
// Wait for USB bus event (device insertion)
let event = usb.wait_for_device().await;

// Extract device descriptor
let vid = event.vendor_id;
let pid = event.product_id;
let class = event.device_class;

// Check against allowlist
if !ALLOWLIST.contains(&(vid, pid)) {
// ALERT: Unauthorized USB device!
let alert = format!(
r#"{{"device_id":"nrf52_sentinel_01","alert_type":"unauthorized_usb","severity":"critical","vid":"0x{:04X}","pid":"0x{:04X}","class":"0x{:02X}"}}"#,
vid, pid, class
);
mqtt_sn_publish("aurora/sensors/nrf52_sentinel_01/alerts", &alert);
}
}
}

Why a USB Honeypot?

Physical access attacks bypass all network security controls. A USB Rubber Ducky can exfiltrate data in seconds. By placing nRF52840 sentinels in server rooms, the SOC gets immediate alerts on physical intrusion attempts.

BLE Sensor Broadcasting

#[derive(Clone, Copy)]
pub struct SensorData {
pub temperature_c: i16, // ×100 fixed-point (e.g., 2350 = 23.50°C)
pub motion_detected: bool,
pub tamper_status: u8, // 0=normal, 1=case_open, 2=wire_cut
pub battery_mv: u16, // Battery voltage in millivolts
pub boot_count: u16, // Monotonic boot counter
}

#[embassy_executor::task]
async fn ble_advertiser_task(/* ... */) {
loop {
let data = SensorData {
temperature_c: read_temperature(),
motion_detected: read_pir_sensor(),
tamper_status: read_tamper_pins(),
battery_mv: read_battery_adc(),
boot_count: get_boot_count(),
};

// Encode as BLE manufacturer-specific advertisement
let adv_data = encode_manufacturer_data(&data);

// Broadcast — ESP32-S3 will pick this up
ble_advertise(&adv_data).await;

Timer::after(Duration::from_secs(5)).await;
}
}

The nRF52840 broadcasts sensor data as BLE advertisements rather than establishing connections. This is more power-efficient and allows multiple ESP32-S3 gateways to receive the data simultaneously.

Hardware Attestation

pub struct AttestationState {
pub boot_count: u32,
pub firmware_hash: [u8; 32], // SHA-256 of firmware
pub last_attestation_ok: bool,
}

#[embassy_executor::task]
async fn attestation_task(/* ... */) {
// Read ECDSA private key from UICR
let private_key = read_uicr_key();

loop {
// 1. Compute firmware hash using CC310 SHA-256
let firmware_hash = cc310_sha256(FIRMWARE_START, FIRMWARE_SIZE);

// 2. Build attestation message
let message = format!("nrf52_sentinel_01{}{}",
hex::encode(&firmware_hash),
get_boot_count()
);

// 3. Sign with CC310 ECDSA P-256
let signature = cc310_ecdsa_sign(&private_key, &message);

// 4. Publish to Rust Core Engine via MQTT-SN
let payload = format!(
r#"{{"device_id":"nrf52_sentinel_01","firmware_hash":"{}","boot_count":{},"signature_hex":"{}"}}"#,
hex::encode(&firmware_hash),
get_boot_count(),
hex::encode(&signature)
);
mqtt_sn_publish("aurora/attestation/nrf52_sentinel_01/response", &payload);

Timer::after(Duration::from_secs(300)).await; // Every 5 minutes
}
}

CryptoCell CC310

The nRF52840 includes a dedicated hardware cryptographic accelerator:

OperationCC310 SpeedSoftware Speed
ECDSA P-256 Sign~50ms~2000ms
SHA-256 (1KB)~0.1ms~5ms
AES-128~0.01ms/block~0.5ms/block

Using the CC310 hardware means attestation completes in ~50ms instead of 2 seconds, critical for a battery-powered device.

MQTT-SN Protocol

The nRF52840 uses MQTT-SN (Sensor Networks) — a lightweight variant of MQTT designed for constrained devices:

pub enum QoS {
AtMostOnce, // Fire and forget
AtLeastOnce, // Acknowledged delivery
ExactlyOnce, // Two-phase delivery
}

pub struct MqttSnClient {
gateway_addr: [u8; 6], // BLE MAC of ESP32-S3 gateway
gateway_port: u16,
connected: bool,
}

impl MqttSnClient {
pub async fn connect(&mut self) -> Result<(), MqttSnError> {
// MQTT-SN CONNECT over UDP-over-BLE
}

pub async fn publish(&self, topic: &str, payload: &[u8], qos: QoS) -> Result<(), MqttSnError> {
// Pre-registered topic IDs for efficiency
// Topic string sent only during registration
}
}

Why MQTT-SN over regular MQTT?

FeatureMQTT-SNMQTT
Header size2 bytes2+ bytes
Topic handlingPre-registered IDs (2 bytes)Full string each time
TransportAny (UDP, BLE, Zigbee)TCP only
Sleep supportBuilt-in (device can sleep)Keep-alive required
GatewayRequired (ESP32-S3)Direct to broker

For a device with 256KB RAM on a coin cell battery, every byte matters.

Memory Layout

Flash (1MB):
├── Bootloader (MCUboot) 16KB
├── Application 512KB
├── OTA staging 512KB (A/B update)
└── UICR 4KB (keys, config)

SRAM (256KB):
├── Stack 8KB
├── Static data 32KB
├── Embassy executor ~4KB
├── BLE stack ~32KB
├── MQTT-SN buffers ~2KB
└── Available ~178KB

Build and Flash

# Build with Embassy
cargo build --release --target thumbv7em-none-eabihf

# Flash via probe-rs
probe-rs run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/release/aurora-nrf52

# Debug
probe-rs attach --chip nRF52840_xxAA