diff --git a/ead/edhoc-ead-zeroconf/src/lib.rs b/ead/edhoc-ead-zeroconf/src/lib.rs index 8a56677a..58450897 100644 --- a/ead/edhoc-ead-zeroconf/src/lib.rs +++ b/ead/edhoc-ead-zeroconf/src/lib.rs @@ -224,34 +224,163 @@ fn encode_ead_1(loc_w: &EdhocMessageBuffer, enc_id: &EdhocMessageBuffer) -> Edho output } +// responder side +#[derive(Default, PartialEq, Copy, Clone, Debug)] +pub enum EADResponderProtocolState { + #[default] + Start, + ProcessedEAD1, + WaitEAD3, + Completed, +} + +pub struct EADResponderState { + pub protocol_state: EADResponderProtocolState, +} + +impl EADResponderState { + pub fn new() -> Self { + EADResponderState { + protocol_state: EADResponderProtocolState::Start, + } + } +} + +// shared mutable global state for EAD +// NOTE: this is not thread-safe +static mut EAD_RESPONDER_GLOBAL_STATE: EADResponderState = EADResponderState { + protocol_state: EADResponderProtocolState::Start, +}; +pub fn ead_responder_get_global_state() -> &'static EADResponderState { + unsafe { &EAD_RESPONDER_GLOBAL_STATE } +} +pub fn ead_responder_set_global_state(new_state: EADResponderState) { + unsafe { + EAD_RESPONDER_GLOBAL_STATE = new_state; + } +} + +pub fn encode_voucher_request( + message_1: &EdhocMessageBuffer, + opaque_state: &EdhocMessageBuffer, +) -> EdhocMessageBuffer { + let mut output = EdhocMessageBuffer::new(); + + output.content[0] = CBOR_MAJOR_ARRAY | 2; + + output.content[1] = CBOR_BYTE_STRING; + output.content[2] = message_1.len as u8; + output.content[3..3 + message_1.len].copy_from_slice(&message_1.content[..message_1.len]); + + output.content[3 + message_1.len] = CBOR_BYTE_STRING; + output.content[4 + message_1.len] = opaque_state.len as u8; + output.content[5 + message_1.len..5 + message_1.len + opaque_state.len] + .copy_from_slice(&opaque_state.content[..opaque_state.len]); + + output.len = 5 + message_1.len + opaque_state.len; + + output +} + +// FIXME: receive opaque_state as parameter, but that requires changing the r_process_message_1 function signature +use hexlit::hex; +const OPAQUE_STATE_TV: &[u8] = + &hex!("827819666538303a3a623833343a643630623a373936663a38646530198bed"); +pub fn r_process_ead_1(ead_1: &EADItem, message_1: &BufferMessage1) -> Result<(), ()> { + let opaque_state: EdhocMessageBuffer = OPAQUE_STATE_TV.try_into().unwrap(); + + if ead_1.label != EAD_ZEROCONF_LABEL { + return Err(()); + } + let (loc_w, _enc_id) = parse_ead_1_value(&ead_1.value)?; + let voucher_request = encode_voucher_request(message_1, &opaque_state); + // TODO: implement send_voucher_request(&loc_w, &voucher_request); + + ead_responder_set_global_state(EADResponderState { + protocol_state: EADResponderProtocolState::ProcessedEAD1, + }); + + Ok(()) +} + +pub fn r_prepare_ead_2() -> Option { + let mut ead_2 = EADItem::new(); + + // add the label to the buffer (non-critical) + ead_2.label = EAD_ZEROCONF_LABEL; + ead_2.is_critical = true; + + // TODO: append Voucher (H(message_1), CRED_V) to the buffer + + // NOTE: see the note in lib.rs::test_ead + // state.protocol_state = EADResponderProtocolState::WaitMessage3; + ead_responder_set_global_state(EADResponderState { + protocol_state: EADResponderProtocolState::Completed, + }); + + Some(ead_2) +} + +pub fn r_process_ead_3(_ead_3: EADItem) -> Result<(), ()> { + // TODO: maybe retrive CRED_U from a Credential Database + + // state.protocol_state = EADResponderProtocolState::Completed; + + Ok(()) +} + +fn parse_ead_1_value( + ead_1_value: &Option, +) -> Result<(EdhocMessageBuffer, EdhocMessageBuffer), ()> { + let value = ead_1_value.unwrap(); + let loc_w: EdhocMessageBuffer = value.content[4..4 + value.content[3] as usize] + .try_into() + .unwrap(); + + let enc_id: EdhocMessageBuffer = EdhocMessageBuffer::new(); + + Ok((loc_w, enc_id)) +} + #[cfg(test)] -mod test_initiator { - use super::*; +mod test_vectors { use edhoc_consts::*; use hexlit::hex; + // common + pub const MESSAGE_1_WITH_EAD_TV: &[u8] = &hex!("0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711"); + // U - const X_TV: BytesP256ElemLen = + pub const X_TV: BytesP256ElemLen = hex!("A0C71BDBA570FFD270D90BDF416C142921F214406271FCF55B8567F079B50DA0"); - const ID_U_TV: &[u8] = &hex!("a104412b"); + pub const ID_U_TV: &[u8] = &hex!("a104412b"); // V - // TODO... + pub const VOUCHER_REQUEST_TV: &[u8] = &hex!("8258520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711581f827819666538303a3a623833343a643630623a373936663a38646530198bed"); // W - const G_W_TV: &[u8] = &hex!("FFA4F102134029B3B156890B88C9D9619501196574174DCB68A07DB0588E4D41"); - const LOC_W_TV: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572"); // coap://enrollment.server + pub const G_W_TV: &[u8] = + &hex!("FFA4F102134029B3B156890B88C9D9619501196574174DCB68A07DB0588E4D41"); + pub const LOC_W_TV: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572"); // coap://enrollment.server - const ENC_ID_TV: &[u8] = &hex!("71fb72788b180ebe332697d711"); - const PRK_TV: &[u8] = &hex!("04da32d221db25db701667f9d3903374a45a9b04f25d1cb481b099a480cece04"); - const K_1_TV: &[u8] = &hex!("95a90f115d8fc5252849a25ba5225575"); - const IV_1_TV: &[u8] = &hex!("083cb9a00da66af4f56877fcda"); + pub const ENC_ID_TV: &[u8] = &hex!("71fb72788b180ebe332697d711"); + pub const PRK_TV: &[u8] = + &hex!("04da32d221db25db701667f9d3903374a45a9b04f25d1cb481b099a480cece04"); + pub const K_1_TV: &[u8] = &hex!("95a90f115d8fc5252849a25ba5225575"); + pub const IV_1_TV: &[u8] = &hex!("083cb9a00da66af4f56877fcda"); - const EAD1_VALUE_TV: &[u8] = &hex!( + pub const EAD1_VALUE_TV: &[u8] = &hex!( "58287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711" ); - const SS_TV: u8 = 2; + pub const SS_TV: u8 = 2; +} + +#[cfg(test)] +mod test_initiator { + use super::*; + use edhoc_consts::*; + use test_vectors::*; #[test] fn test_compute_keys() { @@ -304,75 +433,52 @@ mod test_initiator { } } -// responder side -#[derive(Default, PartialEq, Copy, Clone, Debug)] -pub enum EADResponderProtocolState { - #[default] - Start, - ProcessedEAD1, - WaitEAD3, - Completed, -} +#[cfg(test)] +mod test_responder { + use super::*; + use edhoc_consts::*; + use hexlit::hex; + use test_vectors::*; -pub struct EADResponderState { - pub protocol_state: EADResponderProtocolState, -} + #[test] + fn test_parse_ead_1_value() { + let ead_1_value_tv: EdhocMessageBuffer = EAD1_VALUE_TV.try_into().unwrap(); + let loc_w_tv: EdhocMessageBuffer = LOC_W_TV.try_into().unwrap(); -impl EADResponderState { - pub fn new() -> Self { - EADResponderState { - protocol_state: EADResponderProtocolState::Start, - } + let res = parse_ead_1_value(&Some(ead_1_value_tv)); + assert!(res.is_ok()); + let (loc_w, enc_id) = res.unwrap(); + assert_eq!(loc_w.content, loc_w_tv.content); } -} -// shared mutable global state for EAD -// NOTE: this is not thread-safe -static mut EAD_RESPONDER_GLOBAL_STATE: EADResponderState = EADResponderState { - protocol_state: EADResponderProtocolState::Start, -}; -pub fn ead_responder_get_global_state() -> &'static EADResponderState { - unsafe { &EAD_RESPONDER_GLOBAL_STATE } -} -pub fn ead_responder_set_global_state(new_state: EADResponderState) { - unsafe { - EAD_RESPONDER_GLOBAL_STATE = new_state; - } -} - -pub fn r_process_ead_1(_ead_1: EADItem) -> Result<(), ()> { - // TODO: parse and verify the label - // TODO: trigger the voucher request to W - - ead_responder_set_global_state(EADResponderState { - protocol_state: EADResponderProtocolState::ProcessedEAD1, - }); - - Ok(()) -} - -pub fn r_prepare_ead_2() -> Option { - let mut ead_2 = EADItem::new(); - - // add the label to the buffer (non-critical) - ead_2.label = EAD_ZEROCONF_LABEL; - ead_2.is_critical = true; - - // TODO: append Voucher (H(message_1), CRED_V) to the buffer + #[test] + fn test_encode_voucher_request() { + let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap(); + let opaque_state_tv: EdhocMessageBuffer = OPAQUE_STATE_TV.try_into().unwrap(); + let voucher_request_tv: EdhocMessageBuffer = VOUCHER_REQUEST_TV.try_into().unwrap(); - // NOTE: see the note in lib.rs::test_ead - // state.protocol_state = EADResponderProtocolState::WaitMessage3; - ead_responder_set_global_state(EADResponderState { - protocol_state: EADResponderProtocolState::Completed, - }); + let voucher_request = encode_voucher_request(&message_1_tv, &opaque_state_tv); + assert_eq!(voucher_request.content, voucher_request_tv.content); + } - Some(ead_2) -} + #[test] + fn test_process_ead_1() { + let ead_1_value_tv: EdhocMessageBuffer = EAD1_VALUE_TV.try_into().unwrap(); + let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap(); -pub fn r_process_ead_3(_ead_3: EADItem) -> Result<(), ()> { - // TODO: maybe retrive CRED_U from a Credential Database + let ead_1 = EADItem { + label: EAD_ZEROCONF_LABEL, + is_critical: true, + value: Some(ead_1_value_tv), + }; - // state.protocol_state = EADResponderProtocolState::Completed; + ead_responder_set_global_state(EADResponderState::new()); - Ok(()) + let res = r_process_ead_1(&ead_1, &message_1_tv); + assert!(res.is_ok()); + assert_eq!( + ead_responder_get_global_state().protocol_state, + EADResponderProtocolState::ProcessedEAD1 + ); + } } diff --git a/examples/test-vectors-playground.ipynb b/examples/traces-zeroconf.ipynb similarity index 83% rename from examples/test-vectors-playground.ipynb rename to examples/traces-zeroconf.ipynb index df805e10..c11f2127 100644 --- a/examples/test-vectors-playground.ipynb +++ b/examples/traces-zeroconf.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -38,9 +38,32 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "# static_keys\n", + "U = \"555C89F41EA42D458F0B4D74499E1C177BA9AD910F525BACF3D64D35E8568DEC\"\n", + "G_U = \"AC69E4299F79FAED612E37C37F99D2B3939B142A8E8E65B90FAB5001F7F2CF56\"\n", + "V = \"1DAF151B30F0F247AEB5598C1EEE8664384166BBC37F262DC6581A67486BCF3C\"\n", + "G_V = \"188B3E5A62352FFCDE66894FCDBBCB33D243A045BAA99357A72012A6AF3A33AD\"\n", + "W = \"4E5E15AB35008C15B89E91F9F329164D4AACD53D9923672CE0019F9ACD98573F\"\n", + "G_W = \"FFA4F102134029B3B156890B88C9D9619501196574174DCB68A07DB0588E4D41\"\n", + "\n", + "# ephemeral_keys\n", + "X = \"A0C71BDBA570FFD270D90BDF416C142921F214406271FCF55B8567F079B50DA0\"\n", + "G_X = \"FF14FB42677CE9D016907F571E5E1CD4E815F098AA37084063A0C34570F6F7F5\"\n", + "Y = \"A1D1A1C084AB0D912CC7A15B7F252FABCA252FAD4CAA8E5D569C94578B52A047\"\n", + "G_Y = \"9F69C52FAE8F7EA9194022C70B238FCBF4AFFFDFFC8341EEC85BA68E2F9BB744\"\n", + "Z = \"644658D815CBCA8EA863090A2D498990B5C75357A729231EC3DE7DF5A7AFE49E\"\n", + "G_Z = \"6B67C90638924C4AE8472CA6FB9A90BE5F43132753346379C672972D323F7A41\"\n" + ] + } + ], "source": [ "def format_tv(tv, fmt, nokeys=False):\n", " for k, v in tv.items():\n", @@ -103,7 +126,9 @@ " '_G_Z_y': 'FA1EFAD24A287B1FEF04683B5B24963A107067541B2E4766088552EE11337D87'\n", " },\n", "}\n", - "# keys_tv = add_new_keys(keys_tv) # uncomment to generate a new set of keys" + "# keys_tv = add_new_keys(keys_tv) # uncomment to generate a new set of keys\n", + "\n", + "format_tv(keys_tv, \"python\")" ] }, { @@ -115,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -150,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -163,22 +188,6 @@ "ID_U = \"a104412b\"\n", "SS = 2\n", "\n", - "# static_keys\n", - "U = \"555C89F41EA42D458F0B4D74499E1C177BA9AD910F525BACF3D64D35E8568DEC\"\n", - "G_U = \"AC69E4299F79FAED612E37C37F99D2B3939B142A8E8E65B90FAB5001F7F2CF56\"\n", - "V = \"1DAF151B30F0F247AEB5598C1EEE8664384166BBC37F262DC6581A67486BCF3C\"\n", - "G_V = \"188B3E5A62352FFCDE66894FCDBBCB33D243A045BAA99357A72012A6AF3A33AD\"\n", - "W = \"4E5E15AB35008C15B89E91F9F329164D4AACD53D9923672CE0019F9ACD98573F\"\n", - "G_W = \"FFA4F102134029B3B156890B88C9D9619501196574174DCB68A07DB0588E4D41\"\n", - "\n", - "# ephemeral_keys\n", - "X = \"A0C71BDBA570FFD270D90BDF416C142921F214406271FCF55B8567F079B50DA0\"\n", - "G_X = \"FF14FB42677CE9D016907F571E5E1CD4E815F098AA37084063A0C34570F6F7F5\"\n", - "Y = \"A1D1A1C084AB0D912CC7A15B7F252FABCA252FAD4CAA8E5D569C94578B52A047\"\n", - "G_Y = \"9F69C52FAE8F7EA9194022C70B238FCBF4AFFFDFFC8341EEC85BA68E2F9BB744\"\n", - "Z = \"644658D815CBCA8EA863090A2D498990B5C75357A729231EC3DE7DF5A7AFE49E\"\n", - "G_Z = \"6B67C90638924C4AE8472CA6FB9A90BE5F43132753346379C672972D323F7A41\"\n", - "\n", "# enc_id\n", "enc_id = \"71fb72788b180ebe332697d711\"\n", "salt = \"\"\n", @@ -272,9 +281,63 @@ "ead1_tv = add_voucher_info(ead1_tv)\n", "ead1_tv = add_ead1(ead1_tv)\n", "\n", - "# rich.print(ead1_tv)\n", - "format_tv(ead1_tv, \"python\")\n", - "# format_tv(ead1_tv, \"rust\", nokeys=True)" + "format_tv(ead1_tv, \"python\", nokeys=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Traces for Voucher_Request\n", + "\n", + "See https://www.ietf.org/archive/id/draft-selander-lake-authz-03.html#name-authenticator-enrollment-se" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "# input\n", + "EAD_1_VALUE = \"58287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711\"\n", + "MESSAGE_1_WITH_EAD = \"0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711\"\n", + "OPAQUE_STATE = \"827819666538303a3a623833343a643630623a373936663a38646530198bed\"\n", + "\n", + "# voucher_request\n", + "voucher_request = \"8258520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724d71fb72788b180ebe332697d711581f827819666538303a3a623833343a643630623a373936663a38646530198bed\"\n" + ] + } + ], + "source": [ + "def add_voucher_request(tv):\n", + " voucher_request = cbor2.dumps([\n", + " unhexlify(tv[\"input\"][\"MESSAGE_1_WITH_EAD\"]),\n", + " unhexlify(tv[\"input\"][\"OPAQUE_STATE\"]),\n", + " ]).hex()\n", + " tv.update({\n", + " \"voucher_request\": {\n", + " \"voucher_request\": voucher_request,\n", + " }\n", + " })\n", + " return tv\n", + "\n", + "voucher_request_tv = {\n", + " \"input\": {\n", + " \"EAD_1_VALUE\": ead1_tv[\"ead1\"][\"ead1_value\"],\n", + " \"MESSAGE_1_WITH_EAD\": \"0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b637\" + ead1_tv[\"ead1\"][\"ead1\"],\n", + " \"OPAQUE_STATE\": cbor2.dumps([\"fe80::b834:d60b:796f:8de0\", 35821]).hex(), # [ORIGIN_IPADDR, PORT]\n", + " }\n", + "}\n", + "# voucher_request_tv.update(keys_tv) # using existing keys\n", + "\n", + "voucher_request_tv = add_voucher_request(voucher_request_tv)\n", + "\n", + "format_tv(voucher_request_tv, \"python\")" ] }, { diff --git a/lib/src/edhoc.rs b/lib/src/edhoc.rs index d9a1c5c1..42e8113a 100644 --- a/lib/src/edhoc.rs +++ b/lib/src/edhoc.rs @@ -122,7 +122,7 @@ pub fn r_process_message_1( if suites_i[suites_i_len - 1] == EDHOC_SUPPORTED_SUITES[0] { // Step 3: If EAD is present make it available to the application let ead_success = if let Some(ead_1) = ead_1 { - r_process_ead_1(ead_1).is_ok() + r_process_ead_1(&ead_1, message_1).is_ok() } else { true };