Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples: Add traits based CoAP example #241

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/coap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ coap-handler = "0.2"
coap-handler-implementations = "0.5"
coap-numbers = "0.2.3"
coap-message-utils = "0.3.1"

std-embedded-nal-async = "^0.2"
embedded-nal-async = "0.7"
embedded-nal-coap = "0.1.0-alpha.2"
async-std = { version = "1.12", features = [ "attributes" ] }
rand = "0.8"
coap-request = "0.2.0-alpha.2"
embassy-futures = "0.1.1"
coap-request-implementations = "0.1.0-alpha.4"
161 changes: 161 additions & 0 deletions examples/coap/src/bin/coapclient-coaprequest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use hexlit::hex;
use lakers::*;
use lakers_crypto::Crypto;
use lakers_ead::ZeroTouchDevice;

use embedded_nal_async::UdpStack;

const ID_CRED_I: &[u8] = &hex!("a104412b");
const _ID_CRED_R: &[u8] = &hex!("a104410a");
const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8");
const I: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b");
const _G_I_X_COORD: &[u8] =
&hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6");
const _G_I_Y_COORD: &[u8] =
&hex!("6e5de611388a4b8a8211334ac7d37ecb52a387d257e6db3c2a93df21ff3affc8");
const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072");
const _G_R: &[u8] = &hex!("bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f0");

async fn run_client_operations<const N: usize>(
client: embedded_nal_coap::CoAPRuntimeClient<'_, N>,
) -> Result<(), EDHOCError> {
let demoserver = "[::1]:5683".parse().unwrap();
let path = "/.well-known/edhoc";

use coap_request::Stack;

println!("Client request: to {:?}, path {}", demoserver, path);

let cred_i = CredentialRPK::new(CRED_I.try_into().unwrap()).unwrap();
let cred_r = CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap();

let initiator = EdhocInitiator::new(lakers_crypto::default_crypto());

// Send Message 1 over CoAP and convert the response to byte
let mut msg_1_buf = Vec::from([0xf5u8]); // EDHOC message_1 when transported over CoAP is prepended with CBOR true
let c_i = generate_connection_identifier_cbor(&mut lakers_crypto::default_crypto());
let ead_1 = None;
let (initiator, message_1) = initiator.prepare_message_1(Some(c_i), &ead_1)?;
msg_1_buf.extend_from_slice(message_1.as_slice());
println!("message_1 len = {}", msg_1_buf.len());

let mut message_2 = None;

client
.to(demoserver)
.request(
coap_request_implementations::Code::post()
.with_path(path)
.with_request_payload_slice(&msg_1_buf)
.processing_response_payload_through(|response| {
println!("response = {:02x?}", response);
message_2 = Some(EdhocMessageBuffer::new_from_slice(response));
}),
)
.await
.expect("Message 1 request failed")
.expect("Message 1 response was not successful");

let message_2 = message_2.expect("Successful processsing on the client, but no response seen?");
let message_2 = message_2.expect("Failed to create an EdhocMessageBuffer from the response");

let (initiator, c_r, id_cred_r, _ead_2) = initiator.parse_message_2(&message_2)?;
let valid_cred_r = credential_check_or_fetch(Some(cred_r), id_cred_r).unwrap();
let initiator = initiator.verify_message_2(&I, cred_i, valid_cred_r)?;

let mut msg_3 = Vec::from([c_r]);
let (mut initiator, message_3, prk_out) =
initiator.prepare_message_3(CredentialTransfer::ByReference, &None)?;
msg_3.extend_from_slice(message_3.as_slice());
println!("message_3 len = {}", msg_3.len());

client
.to(demoserver)
.request(
coap_request_implementations::Code::post()
.with_path(path)
.with_request_payload_slice(&msg_3),
)
.await
.expect("Message 3 request failed")
.expect("Message 3 response was not successful");

// we don't care about the response to message_3 for now

println!("EDHOC exchange successfully completed");
println!("PRK_out: {:02x?}", prk_out);

let mut oscore_secret = initiator.edhoc_exporter(0u8, &[], 16); // label is 0
let mut oscore_salt = initiator.edhoc_exporter(1u8, &[], 8); // label is 1

println!("OSCORE secret: {:02x?}", oscore_secret);
println!("OSCORE salt: {:02x?}", oscore_salt);

// context of key update is a test vector from draft-ietf-lake-traces
let prk_out_new = initiator.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);

// compute OSCORE secret and salt after key update
oscore_secret = initiator.edhoc_exporter(0u8, &[], 16); // label is 0
oscore_salt = initiator.edhoc_exporter(1u8, &[], 8); // label is 1

println!("OSCORE secret after key update: {:02x?}", oscore_secret);
println!("OSCORE salt after key update: {:02x?}", oscore_salt);

Ok(())
}

async fn main_on_stack<S: UdpStack>(stack: &mut S) {
// FIXME this needs coordination btwn embedded-nal-async and -coap
// let (_localaddr, mut sock) = stack
// .bind_single(embedded_nal_async::SocketAddr::new(
// "::".parse().unwrap(),
// 0,
// ))
// .await
// .expect("Can't create a socket");
let mut sock = stack
.bind_multiple(embedded_nal_async::SocketAddr::new(
"::".parse().unwrap(),
1234,
))
.await
.expect("Can't create a socket");

// Shortest way to 4.04 all requests
let mut handler = coap_handler_implementations::new_dispatcher();

let coap = embedded_nal_coap::CoAPShared::<1>::new();
let (client, server) = coap.split();

use rand::SeedableRng;

match embassy_futures::select::select(
server.run(
&mut sock,
&mut handler,
&mut rand::rngs::StdRng::from_entropy(),
),
run_client_operations(client),
)
.await
{
embassy_futures::select::Either::First(r) => {
panic!("CoAP task terminated unexpectedly: {:?}", r)
}
embassy_futures::select::Either::Second(result) => {
result.expect("Client operation was not successful")
}
}
}

#[async_std::main]
async fn main() {
let mut stack = std_embedded_nal_async::Stack::default();

main_on_stack(&mut stack).await
}
76 changes: 63 additions & 13 deletions examples/coap/src/bin/coapserver-coaphandler.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use hexlit::hex;
use lakers::*;
use lakers_crypto::Crypto;
use lakers_ead::{ZeroTouchAuthenticator, ZeroTouchServer};

use coap_message::{Code, MinimalWritableMessage, MutableWritableMessage, ReadableMessage};
use coap_message_utils::{Error, OptionsExt as _};
use embedded_nal::UdpFullStack;

const _ID_CRED_I: &[u8] = &hex!("a104412b");
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");
Expand All @@ -15,9 +16,13 @@ const _G_I_Y_COORD: &[u8] =
const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072");
const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac");

#[derive(Default, Debug)]
// authz server
const W_TV: &[u8] = &hex!("4E5E15AB35008C15B89E91F9F329164D4AACD53D9923672CE0019F9ACD98573F");

#[derive(Debug)]
struct EdhocHandler {
connections: Vec<(u8, EdhocResponderWaitM3<Crypto>)>,
mock_server: ZeroTouchServer,
}

/// Render a MessageBufferError into the common Error type.
Expand Down Expand Up @@ -71,6 +76,12 @@ enum EdhocResponse {
OkSend2 {
c_r: u8,
responder: EdhocResponderProcessedM1<'static, Crypto>,
// FIXME: Is the ead_2 the most practical data to store here? An easy alternative is the
// voucher_response; ideal would be the voucher, bu that is only internal to prepare_ead_2.
//
// Also, we'll want to carry around the set of actually authenticated claims (right now
// it's just "if something is here, our single W completed authz")
ead_2: Option<EADItem>,
},
Message3Processed,
}
Expand All @@ -89,8 +100,6 @@ impl coap_handler::Handler for EdhocHandler {
return Err(Error::method_not_allowed());
}

println!("{:?}", self);

request.options().ignore_elective_others()?;

let first_byte = request.payload().get(0).ok_or_else(Error::bad_request)?;
Expand All @@ -101,16 +110,42 @@ impl coap_handler::Handler for EdhocHandler {
CredentialRPK::new(CRED_R.try_into().expect("Static credential is too large"))
.expect("Static credential is not processable");

let (responder, _ead_1) =
let message_1 =
&EdhocMessageBuffer::new_from_slice(&request.payload()[1..]).map_err(too_small)?;

let (responder, ead_1) =
EdhocResponder::new(lakers_crypto::default_crypto(), &R, cred_r)
.process_message_1(
&EdhocMessageBuffer::new_from_slice(&request.payload()[1..])
.map_err(too_small)?,
)
.process_message_1(message_1)
.map_err(render_error)?;

let ead_2 = if let Some(ead_1) = ead_1 {
let authenticator = ZeroTouchAuthenticator::default();
let (authenticator, _loc_w, voucher_request) = authenticator
.process_ead_1(&ead_1, &message_1)
.map_err(render_error)?;

// mock a request to the server
let voucher_response = self
.mock_server
.handle_voucher_request(&mut lakers_crypto::default_crypto(), &voucher_request)
.map_err(render_error)?;

let ead_2 = authenticator
.prepare_ead_2(&voucher_response)
.map_err(render_error)?;

println!("Authenticator confirmed authz");
Some(ead_2)
} else {
None
};

let c_r = self.new_c_r();
Ok(EdhocResponse::OkSend2 { c_r, responder })
Ok(EdhocResponse::OkSend2 {
c_r,
responder,
ead_2,
})
} else {
// potentially message 3
//
Expand Down Expand Up @@ -178,9 +213,13 @@ impl coap_handler::Handler for EdhocHandler {
) -> Result<(), Self::BuildResponseError<M>> {
response.set_code(M::Code::new(coap_numbers::code::CHANGED)?);
match req {
EdhocResponse::OkSend2 { c_r, responder } => {
EdhocResponse::OkSend2 {
c_r,
responder,
ead_2,
} => {
let (responder, message_2) = responder
.prepare_message_2(CredentialTransfer::ByReference, Some(c_r), &None)
.prepare_message_2(CredentialTransfer::ByReference, Some(c_r), &ead_2)
.unwrap();
self.connections.push((c_r, responder));
response.set_payload(message_2.as_slice())?;
Expand All @@ -194,7 +233,18 @@ impl coap_handler::Handler for EdhocHandler {
fn build_handler() -> impl coap_handler::Handler {
use coap_handler_implementations::{HandlerBuilder, ReportingHandlerBuilder};

let edhoc: EdhocHandler = Default::default();
// ead authz server (W)
let acl = EdhocMessageBuffer::new_from_slice(&[ID_CRED_I[3]]).unwrap(); // [kid]
let mock_server = ZeroTouchServer::new(
W_TV.try_into().unwrap(),
CRED_R.try_into().unwrap(),
Some(acl),
);

let edhoc = EdhocHandler {
connections: Vec::with_capacity(3),
mock_server,
};

coap_handler_implementations::new_dispatcher()
.at_with_attributes(&[".well-known", "edhoc"], &[], edhoc)
Expand Down
Loading