Skip to content

Commit

Permalink
Merge pull request #107 from chrysn-pull-requests/coap-example-using-…
Browse files Browse the repository at this point in the history
…coap-handler

Add example / implementation for coap-handler
  • Loading branch information
malishav authored Nov 4, 2023
2 parents d9eafa2 + 5d048be commit ae2012f
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ jobs:
- name: Build client
run: cargo build --bin coapclient

- name: Build coap-handler based server
run: cargo build --bin coapserver-coaphandler


release:
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions examples/coap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ 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 = "0.3.1"
embedded-nal = "0.6"
coap-message = "0.2.3"
coap-handler = "0.1.5"
coap-handler-implementations = "0.4.2"
coap-numbers = "0.2.3"
159 changes: 159 additions & 0 deletions examples/coap/src/bin/coapserver-coaphandler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
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, &CRED_R, Some(&CRED_I));

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<S: UdpFullStack>(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 <https://github.com/rust-embedded-community/embedded-nal/issues/47>
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);
}

0 comments on commit ae2012f

Please sign in to comment.