From 34b712b2cedb924e73aa99a78abcf15bfa529554 Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 5 Oct 2023 10:24:36 +0200 Subject: [PATCH] WIP: Add coapserver example that implements a coap-handler --- examples/coap/Cargo.toml | 10 +- .../coap/src/bin/coapserver-coaphandler.rs | 161 ++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 examples/coap/src/bin/coapserver-coaphandler.rs diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index 6269e354..738b0d54 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -4,7 +4,15 @@ version = "0.1.0" edition = "2021" [dependencies] -edhoc-rs = { path = "../../lib", features = [ "crypto-psa" ] } +edhoc-rs = { path = "../../lib", features = [ "crypto-hacspec" ] } hexlit = "0.5.3" coap = { version = "0.13" } coap-lite = { version = "0.11.3" } + +std-embedded-nal = "^0.1.2" +embedded-nal-minimal-coapserver = "*" +embedded-nal = "0.6" +coap-message = "*" +coap-handler = "*" +coap-handler-implementations = "*" +coap-numbers = "*" diff --git a/examples/coap/src/bin/coapserver-coaphandler.rs b/examples/coap/src/bin/coapserver-coaphandler.rs new file mode 100644 index 00000000..0015e33f --- /dev/null +++ b/examples/coap/src/bin/coapserver-coaphandler.rs @@ -0,0 +1,161 @@ +use coap_lite::{CoapRequest, Packet, ResponseType}; +use edhoc_rs::*; +use hexlit::hex; +use std::net::UdpSocket; + +use embedded_nal::UdpFullStack; + +const ID_CRED_I: &[u8] = &hex!("a104412b"); +const ID_CRED_R: &[u8] = &hex!("a104410a"); +const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8"); +const G_I: &[u8] = &hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6"); // not used +const _G_I_Y_COORD: &[u8] = + &hex!("6e5de611388a4b8a8211334ac7d37ecb52a387d257e6db3c2a93df21ff3affc8"); // not used +const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072"); +const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac"); + +#[derive(Default, Debug)] +struct EdhocHandler { + connections: Vec<(u8, EdhocResponder<'static>)>, +} + +impl EdhocHandler { + fn connection_by_c_r(&mut self, c_r: u8) -> Option<&mut EdhocResponder<'static>> { + self.connections + .iter_mut() + .filter(|(current_c_r, _)| current_c_r == &c_r) + .map(|(_, responder)| responder) + .next() + } + + fn new_c_r(&self) -> u8 { + // FIXME: We'll need to do better, but a) that'll be more practical when we can do more + // than u8, and b) that'll best be coordinated with a storage that not only stores EDHOC + // contexts but also OSCORE ones. + let result = self.connections.len(); + if result >= 24 { + panic!("Contexts exceeded"); + } + result as _ + } +} + +enum EdhocResponse { + OkSend2 { c_r: u8 }, + Message3Processed, +} + +impl coap_handler::Handler for EdhocHandler { + type RequestData = EdhocResponse; + fn extract_request_data( + &mut self, + request: &impl coap_message::ReadableMessage, + ) -> Self::RequestData { + let starts_with_true = request.payload().get(0) == Some(&0xf5); + + if starts_with_true { + let state = EdhocState::default(); + let mut responder = + EdhocResponder::new(state, &R, &G_I, &ID_CRED_I, &CRED_I, &ID_CRED_R, &CRED_R); + + let error = responder + .process_message_1(&request.payload()[1..].try_into().expect("wrong length")); + + if error.is_ok() { + let c_r = self.new_c_r(); + // save edhoc connection + self.connections.push((c_r, responder)); + EdhocResponse::OkSend2 { c_r } + } else { + panic!("How to respond to non-OK?") + } + } else { + // potentially message 3 + let c_r_rcvd = request.payload()[0]; + let mut responder = self.connection_by_c_r(c_r_rcvd).expect("No such C_R found"); + + println!("Found state with connection identifier {:?}", c_r_rcvd); + let prk_out = responder + .process_message_3(&request.payload()[1..].try_into().expect("wrong length")); + + if prk_out.is_err() { + println!("EDHOC processing error: {:?}", prk_out); + // FIXME remove state from edhoc_connections + panic!("Handler can't just not respond"); + } + + println!("EDHOC exchange successfully completed"); + println!("PRK_out: {:02x?}", prk_out); + + let mut _oscore_secret = responder.edhoc_exporter(0u8, &[], 16).unwrap(); // label is 0 + println!("OSCORE secret: {:02x?}", _oscore_secret); + let mut _oscore_salt = responder.edhoc_exporter(1u8, &[], 8).unwrap(); // label is 1 + println!("OSCORE salt: {:02x?}", _oscore_salt); + + // context of key update is a test vector from draft-ietf-lake-traces + let prk_out_new = responder.edhoc_key_update(&[ + 0xa0, 0x11, 0x58, 0xfd, 0xb8, 0x20, 0x89, 0x0c, 0xd6, 0xbe, 0x16, 0x96, 0x02, 0xb8, + 0xbc, 0xea, + ]); + println!("PRK_out after key update: {:02x?}?", prk_out_new); + + _oscore_secret = responder.edhoc_exporter(0u8, &[], 16).unwrap(); // label is 0 + println!("OSCORE secret after key update: {:02x?}", _oscore_secret); + _oscore_salt = responder.edhoc_exporter(1u8, &[], 8).unwrap(); // label is 1 + println!("OSCORE salt after key update: {:02x?}", _oscore_salt); + + EdhocResponse::Message3Processed + } + } + fn estimate_length(&mut self, _: &Self::RequestData) -> usize { + 200 + } + fn build_response( + &mut self, + response: &mut impl coap_message::MutableWritableMessage, + req: Self::RequestData, + ) { + response.set_code(coap_numbers::code::CHANGED.try_into().ok().unwrap()); + match req { + EdhocResponse::OkSend2 { c_r } => { + let responder = self.connection_by_c_r(c_r).unwrap(); + let message_2 = responder.prepare_message_2(c_r).unwrap(); + response.set_payload(&message_2.content[..message_2.len]); + } + EdhocResponse::Message3Processed => (), // "send empty ack back"? + }; + } +} + +fn build_handler() -> impl coap_handler::Handler { + use coap_handler_implementations::{HandlerBuilder, ReportingHandlerBuilder}; + + let mut edhoc: EdhocHandler = Default::default(); + + coap_handler_implementations::new_dispatcher() + .at_with_attributes(&[".well-known", "edhoc"], &[], edhoc) + .with_wkc() +} + +fn main_on_stack(stack: &mut S) { + let mut sock = stack.socket().expect("Can't create a socket"); + + let mut handler = build_handler(); + + stack.bind(&mut sock, 5683).expect("Can't bind to port"); + + loop { + match embedded_nal_minimal_coapserver::poll(stack, &mut sock, &mut handler) { + Err(embedded_nal::nb::Error::WouldBlock) => { + // See + std::thread::sleep(std::time::Duration::from_millis(50)); + } + e => e.expect("UDP error during send/receive"), + } + } +} + +fn main() { + let mut stack = std_embedded_nal::Stack::default(); + main_on_stack(&mut stack); +}