diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index 155b6144..cb34df1a 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -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" diff --git a/examples/coap/src/bin/coapclient-coaprequest.rs b/examples/coap/src/bin/coapclient-coaprequest.rs new file mode 100644 index 00000000..fe2afd2b --- /dev/null +++ b/examples/coap/src/bin/coapclient-coaprequest.rs @@ -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( + 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(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 +} diff --git a/examples/coap/src/bin/coapserver-coaphandler.rs b/examples/coap/src/bin/coapserver-coaphandler.rs index 1925c490..30e42dc1 100644 --- a/examples/coap/src/bin/coapserver-coaphandler.rs +++ b/examples/coap/src/bin/coapserver-coaphandler.rs @@ -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"); @@ -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)>, + mock_server: ZeroTouchServer, } /// Render a MessageBufferError into the common Error type. @@ -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, }, Message3Processed, } @@ -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)?; @@ -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 // @@ -178,9 +213,13 @@ impl coap_handler::Handler for EdhocHandler { ) -> Result<(), Self::BuildResponseError> { 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())?; @@ -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)