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

Split message processing #174

Merged
merged 21 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5dd6c72
refactor: split prepare_message_1
geonnave Dec 12, 2023
461ed07
wip: use specialized structs on initiator
geonnave Dec 12, 2023
a8437cf
refactor: split message 2 processing
geonnave Dec 18, 2023
bfdf6d6
refactor: split initiator message 3
geonnave Dec 19, 2023
81a039a
refactor: update no_std example with split api (initiator)
geonnave Dec 19, 2023
6770265
refactor: rename initiator states for consistency
geonnave Dec 19, 2023
8bbd5c3
refactor: split responder side (missing cred handling)
geonnave Dec 19, 2023
ea1923e
fix: return id_cred_i from message 3 processing
geonnave Dec 19, 2023
2201d8b
CI: separate unit test step with default features
geonnave Dec 19, 2023
a80adeb
refactor(api)!: improve api plus some cleanup
geonnave Dec 20, 2023
3d1e047
reafactor: adjust test for ead-authz with new split api
geonnave Dec 20, 2023
fa8b65c
refactor: move unauthorized test to ead-authz
geonnave Dec 20, 2023
49cff1a
refactor: improve names and remove warnings
geonnave Dec 20, 2023
85a4d01
refactor: pass cred_i from application after message 2
geonnave Dec 20, 2023
41c9a7d
refactor: update tests with split api
geonnave Dec 21, 2023
863001f
Merge branch 'main' into split-message-processing
geonnave Dec 21, 2023
b721416
fix: fstar generation, and call it early on CI
geonnave Dec 21, 2023
a42b196
fix: adjust no_std example to split api
geonnave Dec 21, 2023
5f421be
refactor: only split calls that require credential processing
geonnave Dec 26, 2023
b75d40a
refactor: rename splitted functions
geonnave Dec 26, 2023
df6b9b9
Merge branch 'main' into split-message-processing
geonnave Dec 26, 2023
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
21 changes: 16 additions & 5 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ jobs:
- name: Check if code is well formatted
run: cargo fmt --check

unit-tests: # run before build because it is faster
unit-tests-default:
needs: check-style
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
crypto_backend: [lakers-crypto/hacspec, lakers-crypto/psa, lakers-crypto/rustcrypto]
ead: [ead-none, ead-zeroconf]

steps:
- name: Checkout repo
Expand All @@ -38,6 +35,21 @@ jobs:
- name: Run unit tests with default features
run: RUST_BACKTRACE=1 cargo test


unit-tests:
needs: check-style
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
crypto_backend: [lakers-crypto/hacspec, lakers-crypto/psa, lakers-crypto/rustcrypto]
ead: [ead-none, ead-zeroconf]

steps:
- name: Checkout repo
uses: actions/checkout@v3

- name: Run unit tests with feature matrix # note that we only add `--package lakers-ead-zeroconf` when testing with that config
run: RUST_BACKTRACE=1 cargo test -p lakers -p lakers-crypto -p lakers-shared ${{ matrix.ead == 'ead-zeroconf' && '-p lakers-ead-zeroconf' || '' }} --no-default-features --features="${{ matrix.crypto_backend }}, ${{ matrix.ead }}" --no-fail-fast -- --test-threads 1

Expand Down Expand Up @@ -66,7 +78,6 @@ jobs:


generate-fstar:
needs: unit-tests
runs-on: ubuntu-latest

steps:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ lakers-shared = { package = "lakers-shared", path = "shared/", version = "^0.4.1
lakers-ead = { package = "lakers-ead-dispatch", path = "ead/", version = "^0.4.1", default-features = false }
lakers-ead-none = { package = "lakers-ead-none", path = "ead/lakers-ead-none/", version = "^0.4.1" }
lakers-ead-zeroconf = { package = "lakers-ead-zeroconf", path = "ead/lakers-ead-zeroconf/", version = "^0.4.1" }
lakers-ead-authz = { package = "lakers-ead-authz", path = "ead/lakers-ead-authz/", version = "^0.4.1" }
lakers-crypto = { path = "crypto/" }

lakers-crypto-cc2538 = { path = "crypto/lakers-crypto-cc2538/" }
Expand Down
2 changes: 2 additions & 0 deletions ead/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ lakers-shared.workspace = true

lakers-ead-none = { workspace = true, optional = true }
lakers-ead-zeroconf = { workspace = true, optional = true }
lakers-ead-authz = { workspace = true, optional = true }

[features]
default = [ "ead-none" ]
ead-none = [ "lakers-ead-none" ]
ead-zeroconf = [ "lakers-ead-zeroconf" ]
ead-authz = [ "lakers-ead-authz" ]
4 changes: 1 addition & 3 deletions ead/lakers-ead-authz/src/authenticator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::shared::*;
use lakers_shared::{Crypto as CryptoTrait, *};
use lakers_shared::*;

#[derive(Debug, Default)]
pub struct ZeroTouchAuthenticator;
Expand Down Expand Up @@ -113,7 +113,6 @@ fn parse_voucher_response(
mod test_authenticator {
use super::*;
use crate::test_vectors::*;
use lakers_crypto::default_crypto;

#[test]
fn test_parse_ead_1_value() {
Expand Down Expand Up @@ -184,7 +183,6 @@ mod test_authenticator {
mod test_responder_stateless_operation {
use super::*;
use crate::test_vectors::*;
use lakers_crypto::default_crypto;

#[test]
fn test_slo_encode_voucher_request() {
Expand Down
18 changes: 14 additions & 4 deletions ead/lakers-ead-authz/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct ZeroTouchDevice {
#[derive(Debug)]
pub struct ZeroTouchDeviceWaitEAD2 {
prk: BytesHashLen,
pub h_message_1: BytesHashLen,
}

#[derive(Debug)]
Expand Down Expand Up @@ -43,25 +44,34 @@ impl ZeroTouchDevice {
value,
};

(ead_1, ZeroTouchDeviceWaitEAD2 { prk })
(
ead_1,
ZeroTouchDeviceWaitEAD2 {
prk,
h_message_1: [0; SHA256_DIGEST_LEN],
},
)
}
}

impl ZeroTouchDeviceWaitEAD2 {
pub fn set_h_message_1(&mut self, h_message_1: BytesHashLen) {
self.h_message_1 = h_message_1;
}

pub fn process_ead_2<Crypto: CryptoTrait>(
&self,
crypto: &mut Crypto,
ead_2: EADItem,
cred_v: &[u8],
h_message_1: &BytesHashLen,
) -> Result<ZeroTouchDeviceDone, ()> {
if ead_2.label != EAD_ZEROCONF_LABEL || ead_2.value.is_none() {
return Err(());
}
let mut ead_2_value: BytesEncodedVoucher = Default::default();
ead_2_value[..].copy_from_slice(&ead_2.value.unwrap().content[..ENCODED_VOUCHER_LEN]);

match verify_voucher(crypto, &ead_2_value, h_message_1, cred_v, &self.prk) {
match verify_voucher(crypto, &ead_2_value, &self.h_message_1, cred_v, &self.prk) {
Ok(voucher) => Ok(ZeroTouchDeviceDone { voucher }),
Err(_) => Err(()),
}
Expand Down Expand Up @@ -179,13 +189,13 @@ mod test_device {

let ead_device = ZeroTouchDeviceWaitEAD2 {
prk: PRK_TV.try_into().unwrap(),
h_message_1: H_MESSAGE_1_TV.try_into().unwrap(),
};

let res = ead_device.process_ead_2(
&mut default_crypto(),
ead_2_tv,
CRED_V_TV.try_into().unwrap(),
&H_MESSAGE_1_TV.try_into().unwrap(),
);
assert!(res.is_ok());
let ead_device = res.unwrap();
Expand Down
38 changes: 31 additions & 7 deletions ead/lakers-ead-authz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod test_authz {
test_vectors::*,
};
use lakers_crypto::default_crypto;
use lakers_shared::EDHOCError;

#[test]
fn test_complete_flow() {
Expand All @@ -35,8 +36,9 @@ mod test_authz {

// using .unwrap below since detailed errors are tested in each entity's tests

let (ead_1, device) =
let (ead_1, mut device) =
device.prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV);
device.set_h_message_1(H_MESSAGE_1_TV.try_into().unwrap());

// ead_1 will be transported within message_1

Expand All @@ -54,12 +56,34 @@ mod test_authz {

// ead_2 will be transported within message_2

let result = device.process_ead_2(
&mut default_crypto(),
ead_2,
CRED_V_TV,
H_MESSAGE_1_TV.try_into().unwrap(),
);
let result = device.process_ead_2(&mut default_crypto(), ead_2, CRED_V_TV);
assert!(result.is_ok());
}

#[test]
fn test_complete_flow_unauthorized() {
let device = ZeroTouchDevice::new(
ID_U_TV.try_into().unwrap(),
G_W_TV.try_into().unwrap(),
LOC_W_TV.try_into().unwrap(),
);
let authenticator = ZeroTouchAuthenticator::default();
let server = ZeroTouchServer::new(
W_TV.try_into().unwrap(),
CRED_V_TV.try_into().unwrap(),
Some(ACL_INVALID_TV.try_into().unwrap()),
);

let (ead_1, mut device) =
device.prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV);
device.set_h_message_1(H_MESSAGE_1_TV.try_into().unwrap());

let (_loc_w, voucher_request, _authenticator) = authenticator
.process_ead_1(&ead_1, &MESSAGE_1_WITH_EAD_TV.try_into().unwrap())
.unwrap();

let voucher_response =
server.handle_voucher_request(&mut default_crypto(), &voucher_request);
assert_eq!(voucher_response.unwrap_err(), EDHOCError::EADError);
}
}
3 changes: 3 additions & 0 deletions ead/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ pub use lakers_ead_none::*;

#[cfg(feature = "ead-zeroconf")]
pub use lakers_ead_zeroconf::*;

#[cfg(feature = "ead-authz")]
pub use lakers_ead_authz::*;
81 changes: 40 additions & 41 deletions examples/coap/src/bin/coapclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@ const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32
const _G_R: &[u8] = &hex!("bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f0");

fn main() {
match client_handshake() {
Ok(_) => println!("Handshake completed"),
Err(e) => panic!("Handshake failed with error: {:?}", e),
}
}

fn client_handshake() -> Result<(), EDHOCError> {
let url = "coap://127.0.0.1:5683/.well-known/edhoc";
let timeout = Duration::new(5, 0);
println!("Client request: {}", url);

let state = Default::default();
let initiator = EdhocInitiator::new(
state,
lakers_crypto::default_crypto(),
&I,
&CRED_I,
Some(&CRED_R),
);
let initiator =
EdhocInitiator::new(lakers_crypto::default_crypto(), &I, &CRED_I, Some(&CRED_R));

// 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 (initiator, message_1) = initiator.prepare_message_1(c_i).unwrap();
let (initiator, message_1) = initiator.prepare_message_1(Some(c_i), &None)?;
msg_1_buf.extend_from_slice(message_1.as_slice());
println!("message_1 len = {}", msg_1_buf.len());

Expand All @@ -43,45 +44,43 @@ fn main() {
println!("response_vec = {:02x?}", response.message.payload);
println!("message_2 len = {}", response.message.payload.len());

let m2result = initiator.process_message_2(
&response.message.payload[..]
.try_into()
.expect("wrong length"),
);
let message_2 = EdhocMessageBuffer::new_from_slice(&response.message.payload[..]).unwrap();
let (initiator, c_r, id_cred_r, _ead_2) = initiator.parse_message_2(&message_2)?;
let (valid_cred_r, _g_r) =
credential_check_or_fetch(Some(CRED_R.try_into().unwrap()), id_cred_r).unwrap();
let initiator = initiator.verify_message_2(valid_cred_r.as_slice())?;

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

let _response = CoAPClient::post_with_timeout(url, msg_3, timeout).unwrap();
// we don't care about the response to message_3 for now
let _response = CoAPClient::post_with_timeout(url, msg_3, timeout).unwrap();
// we don't care about the response to message_3 for now

println!("EDHOC exchange successfully completed");
println!("PRK_out: {:02x?}", prk_out);
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
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);
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,
]);
// 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);
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
// 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);
} else {
panic!("Message 2 processing error: {:#?}", m2result);
}
println!("OSCORE secret after key update: {:02x?}", oscore_secret);
println!("OSCORE salt after key update: {:02x?}", oscore_salt);

Ok(())
}
33 changes: 18 additions & 15 deletions examples/coap/src/bin/coapserver-coaphandler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use lakers_crypto::Crypto;
use embedded_nal::UdpFullStack;

const _ID_CRED_I: &[u8] = &hex!("a104412b");
const _ID_CRED_R: &[u8] = &hex!("a104410a");
const ID_CRED_R: &[u8] = &hex!("a104410a");
const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8");
const _G_I: &[u8] = &hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6");
const _G_I_Y_COORD: &[u8] =
Expand Down Expand Up @@ -46,7 +46,7 @@ enum EdhocResponse {
// take up a slot there anyway) if we make it an enum.
OkSend2 {
c_r: u8,
responder: EdhocResponderBuildM2<'static, Crypto>,
responder: EdhocResponderProcessedM1<'static, Crypto>,
},
Message3Processed,
}
Expand All @@ -60,20 +60,13 @@ impl coap_handler::Handler for EdhocHandler {
let starts_with_true = request.payload().get(0) == Some(&0xf5);

if starts_with_true {
let state = EdhocState::default();

let responder = EdhocResponder::new(
state,
lakers_crypto::default_crypto(),
&R,
&CRED_R,
Some(&CRED_I),
);
let responder =
EdhocResponder::new(lakers_crypto::default_crypto(), &R, &CRED_R, Some(&CRED_I));

let response = responder
.process_message_1(&request.payload()[1..].try_into().expect("wrong length"));

if let Ok(responder) = response {
if let Ok((responder, _ead_1)) = response {
let c_r = self.new_c_r();
EdhocResponse::OkSend2 { c_r, responder }
} else {
Expand All @@ -87,9 +80,17 @@ impl coap_handler::Handler for EdhocHandler {
.expect("No such C_R found");

println!("Found state with connection identifier {:?}", c_r_rcvd);
let result = responder
.process_message_3(&request.payload()[1..].try_into().expect("wrong length"));

let message_3 = EdhocMessageBuffer::new_from_slice(&request.payload()[1..]).unwrap();
let result = responder.parse_message_3(&message_3);
let Ok((responder, id_cred_i, _ead_3)) = result else {
println!("EDHOC processing error: {:?}", result);
// FIXME remove state from edhoc_connections
panic!("Handler can't just not respond");
};
let (valid_cred_i, _g_i) =
credential_check_or_fetch(Some(CRED_I.try_into().unwrap()), id_cred_i).unwrap();
let result = responder.verify_message_3(valid_cred_i.as_slice());
let Ok((mut responder, prk_out)) = result else {
println!("EDHOC processing error: {:?}", result);
// FIXME remove state from edhoc_connections
Expand Down Expand Up @@ -130,7 +131,9 @@ impl coap_handler::Handler for EdhocHandler {
response.set_code(coap_numbers::code::CHANGED.try_into().ok().unwrap());
match req {
EdhocResponse::OkSend2 { c_r, responder } => {
let (responder, message_2) = responder.prepare_message_2(c_r).unwrap();
let kid = IdCred::CompactKid(ID_CRED_R[3]);
let (responder, message_2) =
responder.prepare_message_2(&kid, Some(c_r), &None).unwrap();
self.connections.push((c_r, responder));
response.set_payload(message_2.as_slice());
}
Expand Down
Loading