1#![no_std]
2
3use lakers_shared::{Crypto as CryptoTrait, *};
4
5#[derive(Default, PartialEq, Copy, Clone, Debug)]
8pub enum EADInitiatorProtocolState {
9 #[default]
10 NonInitialized,
11 Start,
12 WaitEAD2,
13 Completed, Error,
15}
16
17#[derive(PartialEq, Debug, Copy, Clone)]
18pub struct EADInitiatorState {
19 pub protocol_state: EADInitiatorProtocolState,
20 pub(crate) id_u: EdhocMessageBuffer, pub(crate) g_w: BytesP256ElemLen, pub(crate) loc_w: EdhocMessageBuffer, pub(crate) prk: BytesHashLen,
24 pub(crate) voucher: BytesMac,
25}
26
27impl EADInitiatorState {
28 pub fn new(id_u: EdhocMessageBuffer, g_w: BytesP256ElemLen, loc_w: EdhocMessageBuffer) -> Self {
29 EADInitiatorState {
30 protocol_state: EADInitiatorProtocolState::Start,
31 id_u,
32 g_w,
33 loc_w,
34 prk: [0u8; SHA256_DIGEST_LEN],
35 voucher: [0u8; MAC_LENGTH],
36 }
37 }
38}
39
40static mut EAD_INITIATOR_GLOBAL_STATE: EADInitiatorState = EADInitiatorState {
43 protocol_state: EADInitiatorProtocolState::Start,
44 id_u: EdhocMessageBuffer {
47 content: [0u8; MAX_MESSAGE_SIZE_LEN],
48 len: 0,
49 },
50 g_w: [0u8; P256_ELEM_LEN],
51 loc_w: EdhocMessageBuffer {
52 content: [0u8; MAX_MESSAGE_SIZE_LEN],
53 len: 0,
54 },
55 prk: [0u8; SHA256_DIGEST_LEN],
56 voucher: [0u8; MAC_LENGTH],
57};
58pub fn ead_initiator_get_global_state() -> &'static EADInitiatorState {
59 unsafe { &EAD_INITIATOR_GLOBAL_STATE }
60}
61pub fn ead_initiator_get_global_state_own() -> EADInitiatorState {
62 unsafe { EAD_INITIATOR_GLOBAL_STATE }
63}
64pub fn ead_initiator_set_global_state(new_state: EADInitiatorState) {
65 unsafe {
66 EAD_INITIATOR_GLOBAL_STATE = new_state;
67 }
68}
69
70pub fn i_prepare_ead_1<Crypto: CryptoTrait>(
71 crypto: &mut Crypto,
72 x: &BytesP256ElemLen,
73 ss: u8,
74) -> Option<EADItem> {
75 let state = ead_initiator_get_global_state();
76 if state.protocol_state != EADInitiatorProtocolState::Start {
77 return None;
78 }
79
80 let prk = compute_prk(crypto, x, &state.g_w);
82
83 let encoded_id_u = encode_id_u(&state.id_u);
85 let enc_id = encrypt_enc_id(crypto, &prk, &encoded_id_u, ss);
86 let value = Some(encode_ead_1_value(&state.loc_w, &enc_id));
87
88 let ead_1 = EADItem {
89 label: EAD_ZEROCONF_LABEL,
90 is_critical: true,
91 value,
92 };
93
94 ead_initiator_set_global_state(EADInitiatorState {
95 protocol_state: EADInitiatorProtocolState::WaitEAD2,
96 prk,
97 ..ead_initiator_get_global_state_own()
98 });
99
100 Some(ead_1)
101}
102
103pub fn i_process_ead_2<Crypto: CryptoTrait>(
104 crypto: &mut Crypto,
105 ead_2: EADItem,
106 cred_v_u8: &[u8],
107 h_message_1: &BytesHashLen,
108) -> Result<(), ()> {
109 if ead_2.label != EAD_ZEROCONF_LABEL || ead_2.value.is_none() {
110 return Err(());
111 }
112 let mut ead_2_value: BytesEncodedVoucher = Default::default();
113 ead_2_value[..].copy_from_slice(&ead_2.value.unwrap().content[..ENCODED_VOUCHER_LEN]);
114
115 let state = ead_initiator_get_global_state();
116
117 let mut cred_v = EdhocMessageBuffer::new();
119 cred_v.fill_with_slice(cred_v_u8).unwrap();
120
121 match verify_voucher(crypto, &ead_2_value, h_message_1, &cred_v, &state.prk) {
122 Ok(voucher) => {
123 ead_initiator_set_global_state(EADInitiatorState {
124 protocol_state: EADInitiatorProtocolState::Completed,
125 voucher,
126 ..ead_initiator_get_global_state_own()
127 });
128 Ok(())
129 }
130 Err(_) => {
131 ead_initiator_set_global_state(EADInitiatorState {
132 protocol_state: EADInitiatorProtocolState::Error,
133 ..ead_initiator_get_global_state_own()
134 });
135 Err(())
136 }
137 }
138}
139
140pub fn i_prepare_ead_3() -> Option<EADItem> {
141 Some(EADItem::new())
142}
143
144fn verify_voucher<Crypto: CryptoTrait>(
145 crypto: &mut Crypto,
146 received_voucher: &BytesEncodedVoucher,
147 h_message_1: &BytesHashLen,
148 cred_v: &EdhocMessageBuffer,
149 prk: &BytesHashLen,
150) -> Result<BytesMac, ()> {
151 let prepared_voucher = &prepare_voucher(crypto, h_message_1, cred_v, prk);
152 if received_voucher == prepared_voucher {
153 let mut voucher_mac: BytesMac = Default::default();
154 voucher_mac[..MAC_LENGTH].copy_from_slice(&prepared_voucher[1..1 + MAC_LENGTH]);
155 return Ok(voucher_mac);
156 } else {
157 return Err(());
158 }
159}
160
161fn encode_id_u(id_u: &EdhocMessageBuffer) -> EdhocMessageBuffer {
162 let mut plaintext = EdhocMessageBuffer::new();
164 plaintext.content[0] = CBOR_MAJOR_BYTE_STRING + id_u.len as u8;
165 plaintext.content[1..1 + id_u.len].copy_from_slice(id_u.as_slice());
166 plaintext.len = 1 + id_u.len;
167
168 plaintext
169}
170
171fn encrypt_enc_id<Crypto: CryptoTrait>(
172 crypto: &mut Crypto,
173 prk: &BytesHashLen,
174 plaintext: &EdhocMessageBuffer,
175 ss: u8,
176) -> EdhocMessageBuffer {
177 let (k_1, iv_1) = compute_k_1_iv_1(crypto, &prk);
178
179 let enc_structure = encode_enc_structure(ss);
181
182 crypto.aes_ccm_encrypt_tag_8(&k_1, &iv_1, &enc_structure[..], plaintext)
184}
185
186fn compute_prk<Crypto: CryptoTrait>(
187 crypto: &mut Crypto,
188 a: &BytesP256ElemLen,
189 g_b: &BytesP256ElemLen,
190) -> BytesHashLen {
191 let salt: BytesHashLen = [0u8; SHA256_DIGEST_LEN];
194 let g_ab = crypto.p256_ecdh(a, g_b);
195 crypto.hkdf_extract(&salt, &g_ab)
196}
197
198fn compute_k_1_iv_1<Crypto: CryptoTrait>(
199 crypto: &mut Crypto,
200 prk: &BytesHashLen,
201) -> (BytesCcmKeyLen, BytesCcmIvLen) {
202 let mut k_1: BytesCcmKeyLen = [0x00; AES_CCM_KEY_LEN];
204 let k_1_buf = edhoc_kdf_expand(
205 crypto,
206 prk,
207 EAD_ZEROCONF_INFO_K_1_LABEL,
208 &[0x00; MAX_KDF_CONTEXT_LEN],
209 0,
210 AES_CCM_KEY_LEN,
211 );
212 k_1[..].copy_from_slice(&k_1_buf[..AES_CCM_KEY_LEN]);
213
214 let mut iv_1: BytesCcmIvLen = [0x00; AES_CCM_IV_LEN];
216 let iv_1_buf = edhoc_kdf_expand(
217 crypto,
218 prk,
219 EAD_ZEROCONF_INFO_IV_1_LABEL,
220 &[0x00; MAX_KDF_CONTEXT_LEN],
221 0,
222 AES_CCM_IV_LEN,
223 );
224 iv_1[..].copy_from_slice(&iv_1_buf[..AES_CCM_IV_LEN]);
225
226 (k_1, iv_1)
227}
228
229fn encode_enc_structure(ss: u8) -> [u8; EAD_ZEROCONF_ENC_STRUCTURE_LEN] {
230 let mut encrypt0: Bytes8 = [0x00; 8];
231 encrypt0[0] = 0x45u8; encrypt0[1] = 0x6eu8; encrypt0[2] = 0x63u8; encrypt0[3] = 0x72u8; encrypt0[4] = 0x79u8; encrypt0[5] = 0x70u8; encrypt0[6] = 0x74u8; encrypt0[7] = 0x30u8; let mut enc_structure: [u8; EAD_ZEROCONF_ENC_STRUCTURE_LEN] =
241 [0x00; EAD_ZEROCONF_ENC_STRUCTURE_LEN];
242
243 enc_structure[0] = CBOR_MAJOR_ARRAY | 3 as u8; enc_structure[1] = CBOR_MAJOR_TEXT_STRING | encrypt0.len() as u8;
246 enc_structure[2..2 + encrypt0.len()].copy_from_slice(&encrypt0[..]);
247 enc_structure[encrypt0.len() + 2] = CBOR_MAJOR_BYTE_STRING | 0x00 as u8; enc_structure[encrypt0.len() + 3] = CBOR_MAJOR_BYTE_STRING | 0x01 as u8; enc_structure[encrypt0.len() + 4] = ss;
250
251 enc_structure
252}
253
254fn edhoc_kdf_expand<Crypto: CryptoTrait>(
256 crypto: &mut Crypto,
257 prk: &BytesHashLen,
258 label: u8,
259 context: &BytesMaxContextBuffer,
260 context_len: usize,
261 length: usize,
262) -> BytesMaxBuffer {
263 let (info, info_len) = encode_info(label, context, context_len, length);
264 let output = crypto.hkdf_expand(prk, &info, info_len, length);
265 output
266}
267
268fn encode_ead_1_value(
269 loc_w: &EdhocMessageBuffer,
270 enc_id: &EdhocMessageBuffer,
271) -> EdhocMessageBuffer {
272 let mut output = EdhocMessageBuffer::new();
273
274 output.content[0] = CBOR_BYTE_STRING;
275 output.content[2] = CBOR_TEXT_STRING;
278 output.content[3] = loc_w.len as u8;
279 output.content[4..4 + loc_w.len].copy_from_slice(loc_w.as_slice());
280
281 output.content[4 + loc_w.len] = CBOR_MAJOR_BYTE_STRING + enc_id.len as u8;
282 output.content[5 + loc_w.len..5 + loc_w.len + enc_id.len].copy_from_slice(enc_id.as_slice());
283
284 output.len = 5 + loc_w.len + enc_id.len;
285 output.content[1] = (output.len - 2) as u8;
286
287 output
288}
289
290#[derive(Default, PartialEq, Copy, Clone, Debug)]
293pub enum EADResponderProtocolState {
294 #[default]
295 Start,
296 ProcessedEAD1,
297 WaitEAD3,
298 Completed,
299 Error,
300}
301
302pub struct EADResponderState {
303 pub protocol_state: EADResponderProtocolState,
304 pub(crate) voucher_response: Option<EdhocMessageBuffer>,
305}
306
307impl EADResponderState {
308 pub fn new() -> Self {
309 EADResponderState {
310 protocol_state: EADResponderProtocolState::Start,
311 voucher_response: None,
312 }
313 }
314}
315
316static mut EAD_RESPONDER_GLOBAL_STATE: EADResponderState = EADResponderState {
319 protocol_state: EADResponderProtocolState::Start,
320 voucher_response: None,
321};
322pub fn ead_responder_get_global_state() -> &'static EADResponderState {
323 unsafe { &EAD_RESPONDER_GLOBAL_STATE }
324}
325pub fn ead_responder_set_global_state(new_state: EADResponderState) {
326 unsafe {
327 EAD_RESPONDER_GLOBAL_STATE = new_state;
328 }
329}
330
331pub fn r_process_ead_1<Crypto: CryptoTrait>(
332 crypto: &mut Crypto,
333 ead_1: &EADItem,
334 message_1: &EdhocMessageBuffer,
335) -> Result<(), EDHOCError> {
336 let opaque_state: Option<EdhocMessageBuffer> = None; if ead_1.label != EAD_ZEROCONF_LABEL || ead_1.value.is_none() {
339 return Err(EDHOCError::EADError);
340 }
341
342 let (loc_w, _enc_id) = parse_ead_1_value(&ead_1.value.unwrap())?;
343 let voucher_request = encode_voucher_request(message_1, &opaque_state);
344
345 let voucher_response = mock_send_voucher_request(crypto, &loc_w, &voucher_request);
349
350 if let Ok(voucher_response) = voucher_response {
351 ead_responder_set_global_state(EADResponderState {
352 protocol_state: EADResponderProtocolState::ProcessedEAD1,
353 voucher_response: Some(voucher_response),
354 });
355 return Ok(());
356 } else {
357 ead_responder_set_global_state(EADResponderState {
358 protocol_state: EADResponderProtocolState::Error,
359 voucher_response: None,
360 });
361 return Err(EDHOCError::EADError);
362 }
363}
364
365pub fn r_prepare_ead_2() -> Option<EADItem> {
369 let state = ead_responder_get_global_state();
370
371 if let Some(voucher_response) = state.voucher_response {
372 let (_message_1, voucher, _opaque_state) =
374 parse_voucher_response(&voucher_response).unwrap();
375
376 let voucher_value = voucher[..].try_into().unwrap();
377
378 ead_responder_set_global_state(EADResponderState {
379 protocol_state: EADResponderProtocolState::Completed,
380 voucher_response: None,
381 });
382
383 Some(EADItem {
384 label: EAD_ZEROCONF_LABEL,
385 is_critical: true,
386 value: Some(voucher_value),
387 })
388 } else {
389 ead_responder_set_global_state(EADResponderState {
390 protocol_state: EADResponderProtocolState::Error,
391 voucher_response: None,
392 });
393
394 None
395 }
396}
397
398pub fn r_process_ead_3(_ead_3: EADItem) -> Result<(), ()> {
399 Ok(())
404}
405
406fn parse_voucher_response(
407 voucher_response: &EdhocMessageBuffer,
408) -> Result<
409 (
410 EdhocMessageBuffer,
411 BytesEncodedVoucher,
412 Option<EdhocMessageBuffer>,
413 ),
414 EDHOCError,
415> {
416 let mut decoder = CBORDecoder::new(voucher_response.as_slice());
417
418 let array_size = decoder.array()?;
419 if !(2..=3).contains(&array_size) {
420 return Err(EDHOCError::EADError);
421 }
422
423 let message_1: EdhocMessageBuffer = decoder.bytes()?.try_into().unwrap();
424 let voucher: BytesEncodedVoucher = decoder
425 .bytes_sized(ENCODED_VOUCHER_LEN)?
426 .try_into()
427 .unwrap();
428
429 if array_size == 3 {
430 let opaque_state: EdhocMessageBuffer = decoder.bytes()?.try_into().unwrap();
431 return Ok((message_1, voucher, Some(opaque_state)));
432 } else {
433 return Ok((message_1, voucher, None));
434 }
435}
436
437fn parse_ead_1_value(
438 value: &EdhocMessageBuffer,
439) -> Result<(EdhocMessageBuffer, EdhocMessageBuffer), EDHOCError> {
440 let mut outer_decoder = CBORDecoder::new(value.as_slice());
441 let voucher_info_seq = outer_decoder.bytes()?;
442 let mut seq_decoder = CBORDecoder::new(voucher_info_seq);
443 Ok((
444 seq_decoder.str()?.try_into().unwrap(),
445 seq_decoder.bytes()?.try_into().unwrap(),
446 ))
447}
448
449pub fn encode_voucher_request(
450 message_1: &EdhocMessageBuffer,
451 opaque_state: &Option<EdhocMessageBuffer>,
452) -> EdhocMessageBuffer {
453 let mut output = EdhocMessageBuffer::new();
454
455 output.content[1] = CBOR_BYTE_STRING;
456 output.content[2] = message_1.len as u8;
457 output.content[3..3 + message_1.len].copy_from_slice(message_1.as_slice());
458
459 if let Some(opaque_state) = opaque_state {
460 output.content[0] = CBOR_MAJOR_ARRAY | 2;
461
462 output.content[3 + message_1.len] = CBOR_BYTE_STRING;
463 output.content[4 + message_1.len] = opaque_state.len as u8;
464 output.content[5 + message_1.len..5 + message_1.len + opaque_state.len]
465 .copy_from_slice(opaque_state.as_slice());
466
467 output.len = 5 + message_1.len + opaque_state.len;
468 } else {
469 output.content[0] = CBOR_MAJOR_ARRAY | 1;
470 output.len = 3 + message_1.len;
471 }
472
473 output
474}
475
476#[derive(PartialEq, Debug, Copy, Clone)]
480pub struct MockEADServerState {
481 pub(crate) cred_v: EdhocMessageBuffer, pub(crate) w: BytesP256ElemLen, pub acl: Option<EdhocMessageBuffer>, }
485impl MockEADServerState {
486 pub fn new(cred_v: &[u8], w: BytesP256ElemLen, acl: Option<EdhocMessageBuffer>) -> Self {
487 let cred_v: EdhocMessageBuffer = cred_v.try_into().unwrap();
488 MockEADServerState { cred_v, w, acl }
489 }
490 pub fn authorized(self, kid: u8) -> bool {
491 if let Some(acl) = self.acl {
492 acl.content.contains(&kid)
493 } else {
494 true
496 }
497 }
498}
499static mut MOCK_EAD_SERVER_GLOBAL_STATE: MockEADServerState = MockEADServerState {
500 cred_v: EdhocMessageBuffer {
501 content: [0u8; MAX_MESSAGE_SIZE_LEN],
502 len: 0,
503 },
504 w: [0; P256_ELEM_LEN],
505 acl: None,
506};
507pub fn mock_ead_server_get_global_state() -> &'static MockEADServerState {
508 unsafe { &MOCK_EAD_SERVER_GLOBAL_STATE }
509}
510pub fn mock_ead_server_get_global_state_own() -> MockEADServerState {
511 unsafe { MOCK_EAD_SERVER_GLOBAL_STATE }
512}
513pub fn mock_ead_server_set_global_state(new_state: MockEADServerState) {
514 unsafe {
515 MOCK_EAD_SERVER_GLOBAL_STATE = new_state;
516 }
517}
518
519fn mock_send_voucher_request<Crypto: CryptoTrait>(
520 crypto: &mut Crypto,
521 _loc_w: &EdhocMessageBuffer,
522 voucher_request: &EdhocMessageBuffer,
523) -> Result<EdhocMessageBuffer, EDHOCError> {
524 let server_state = mock_ead_server_get_global_state();
525
526 handle_voucher_request(
527 crypto,
528 voucher_request,
529 &server_state.cred_v,
530 &server_state.w,
531 )
532}
533
534fn handle_voucher_request<Crypto: CryptoTrait>(
535 crypto: &mut Crypto,
536 vreq: &EdhocMessageBuffer,
537 cred_v: &EdhocMessageBuffer,
538 w: &BytesP256ElemLen, ) -> Result<EdhocMessageBuffer, EDHOCError> {
540 let (message_1, opaque_state) = parse_voucher_request(vreq)?;
541
542 let (_method, _suites_i, _suites_i_len, g_x, _c_i, ead_1) = parse_message_1(&message_1)?;
543
544 let mut message_1_buf: BytesMaxBuffer = [0x00; MAX_BUFFER_LEN];
546 message_1_buf[..message_1.len].copy_from_slice(message_1.as_slice());
547 let h_message_1 = crypto.sha256_digest(&message_1_buf, message_1.len);
548
549 let prk = compute_prk(crypto, w, &g_x);
550
551 let (_loc_w, enc_id) = parse_ead_1_value(&ead_1.unwrap().value.unwrap())?;
552 let id_u_encoded = decrypt_enc_id(crypto, &prk, &enc_id, EDHOC_SUPPORTED_SUITES[0])?;
553 let id_u = decode_id_u(id_u_encoded)?;
554
555 let server_state = mock_ead_server_get_global_state();
556 if server_state.acl.is_none() || server_state.authorized(id_u.content[3]) {
557 let voucher = prepare_voucher(crypto, &h_message_1, cred_v, &prk);
558 let voucher_response = encode_voucher_response(&message_1, &voucher, &opaque_state);
559 Ok(voucher_response)
560 } else {
561 Err(EDHOCError::EADError)
562 }
563}
564
565fn decode_id_u(id_u_bstr: EdhocMessageBuffer) -> Result<EdhocMessageBuffer, EDHOCError> {
566 let mut decoder = CBORDecoder::new(id_u_bstr.as_slice());
568 let id_u: EdhocMessageBuffer = decoder.bytes()?.try_into().unwrap();
569 Ok(id_u)
570}
571
572fn decrypt_enc_id<Crypto: CryptoTrait>(
573 crypto: &mut Crypto,
574 prk: &BytesHashLen,
575 enc_id: &EdhocMessageBuffer,
576 ss: u8,
577) -> Result<EdhocMessageBuffer, EDHOCError> {
578 let (k_1, iv_1) = compute_k_1_iv_1(crypto, &prk);
579
580 let enc_structure = encode_enc_structure(ss);
582
583 crypto.aes_ccm_decrypt_tag_8(&k_1, &iv_1, &enc_structure[..], &enc_id)
585}
586
587fn prepare_voucher<Crypto: CryptoTrait>(
588 crypto: &mut Crypto,
589 h_message_1: &BytesHashLen,
590 cred_v: &EdhocMessageBuffer,
591 prk: &BytesP256ElemLen,
592) -> BytesEncodedVoucher {
593 let voucher_input = encode_voucher_input(&h_message_1, &cred_v);
594 let voucher_mac = compute_voucher_mac(crypto, &prk, &voucher_input);
595 encode_voucher(&voucher_mac)
596}
597
598fn parse_voucher_request(
599 vreq: &EdhocMessageBuffer,
600) -> Result<(EdhocMessageBuffer, Option<EdhocMessageBuffer>), EDHOCError> {
601 let mut decoder = CBORDecoder::new(vreq.as_slice());
602 let array_size = decoder.array()?;
603 if array_size != 1 && array_size != 2 {
604 return Err(EDHOCError::EADError);
605 }
606
607 let message_1: EdhocMessageBuffer = decoder.bytes()?.try_into().unwrap();
608
609 if array_size == 2 {
610 let opaque_state: EdhocMessageBuffer = decoder.bytes()?.try_into().unwrap();
611 Ok((message_1, Some(opaque_state)))
612 } else {
613 Ok((message_1, None))
614 }
615}
616
617fn encode_voucher_input(
618 h_message_1: &BytesHashLen,
619 cred_v: &EdhocMessageBuffer,
620) -> EdhocMessageBuffer {
621 let mut voucher_input = EdhocMessageBuffer::new();
622
623 voucher_input.content[0] = CBOR_BYTE_STRING;
624 voucher_input.content[1] = SHA256_DIGEST_LEN as u8;
625 voucher_input.content[2..2 + SHA256_DIGEST_LEN]
626 .copy_from_slice(&h_message_1[..SHA256_DIGEST_LEN]);
627
628 voucher_input.content[2 + SHA256_DIGEST_LEN] = CBOR_BYTE_STRING;
629 voucher_input.content[3 + SHA256_DIGEST_LEN] = cred_v.len as u8;
630 voucher_input.content[4 + SHA256_DIGEST_LEN..4 + SHA256_DIGEST_LEN + cred_v.len]
631 .copy_from_slice(cred_v.as_slice());
632
633 voucher_input.len = 4 + SHA256_DIGEST_LEN + cred_v.len;
634
635 voucher_input
636}
637
638fn compute_voucher_mac<Crypto: CryptoTrait>(
639 crypto: &mut Crypto,
640 prk: &BytesHashLen,
641 voucher_input: &EdhocMessageBuffer,
642) -> BytesMac {
643 let mut voucher_mac: BytesMac = [0x00; MAC_LENGTH];
644
645 let mut context = [0x00; MAX_KDF_CONTEXT_LEN];
646 context[..voucher_input.len].copy_from_slice(voucher_input.as_slice());
647
648 let voucher_mac_buf = edhoc_kdf_expand(crypto, prk, 2, &context, voucher_input.len, MAC_LENGTH);
649 voucher_mac[..MAC_LENGTH].copy_from_slice(&voucher_mac_buf[..MAC_LENGTH]);
650
651 voucher_mac
652}
653
654fn encode_voucher(voucher_mac: &BytesMac) -> BytesEncodedVoucher {
655 let mut voucher: BytesEncodedVoucher = Default::default();
656 voucher[0] = CBOR_MAJOR_BYTE_STRING + MAC_LENGTH as u8;
657 voucher[1..1 + MAC_LENGTH].copy_from_slice(&voucher_mac[..MAC_LENGTH]);
658
659 voucher
660}
661
662fn encode_voucher_response(
663 message_1: &EdhocMessageBuffer,
664 voucher: &BytesEncodedVoucher,
665 opaque_state: &Option<EdhocMessageBuffer>,
666) -> EdhocMessageBuffer {
667 let mut output = EdhocMessageBuffer::new();
668
669 output.content[1] = CBOR_BYTE_STRING;
670 output.content[2] = message_1.len as u8;
671 output.content[3..3 + message_1.len].copy_from_slice(message_1.as_slice());
672
673 output.content[3 + message_1.len] = CBOR_MAJOR_BYTE_STRING + ENCODED_VOUCHER_LEN as u8;
674 output.content[4 + message_1.len..4 + message_1.len + ENCODED_VOUCHER_LEN]
675 .copy_from_slice(&voucher[..]);
676
677 if let Some(opaque_state) = opaque_state {
678 output.content[0] = CBOR_MAJOR_ARRAY | 3;
679
680 output.content[4 + message_1.len + ENCODED_VOUCHER_LEN] = CBOR_BYTE_STRING;
681 output.content[5 + message_1.len + ENCODED_VOUCHER_LEN] = opaque_state.len as u8;
682 output.content[6 + message_1.len + ENCODED_VOUCHER_LEN
683 ..6 + message_1.len + ENCODED_VOUCHER_LEN + opaque_state.len]
684 .copy_from_slice(opaque_state.as_slice());
685
686 output.len = 6 + message_1.len + ENCODED_VOUCHER_LEN + opaque_state.len;
687 } else {
688 output.content[0] = CBOR_MAJOR_ARRAY | 2;
689 output.len = 4 + message_1.len + ENCODED_VOUCHER_LEN;
690 }
691
692 output
693}
694
695#[cfg(test)]
696mod test_vectors {
697 use hexlit::hex;
698 use lakers_shared::*;
699
700 pub const ID_U_TV: &[u8] = &hex!("a104412b");
703 pub const ID_U_ENCODED_TV: &[u8] = &hex!("44a104412b");
704 pub const X_TV: BytesP256ElemLen =
705 hex!("368ec1f69aeb659ba37d5a8d45b21bdc0299dceaa8ef235f3ca42ce3530f9525");
706 pub const G_X_TV: &[u8] =
707 &hex!("8af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6");
708
709 pub const CRED_V_TV: &[u8] = &hex!("a2026b6578616d706c652e65647508a101a501020241322001215820bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f02258204519e257236b2a0ce2023f0931f1f386ca7afda64fcde0108c224c51eabf6072");
711
712 pub const W_TV: &[u8] =
714 &hex!("4E5E15AB35008C15B89E91F9F329164D4AACD53D9923672CE0019F9ACD98573F");
715 pub const G_W_TV: &[u8] =
716 &hex!("FFA4F102134029B3B156890B88C9D9619501196574174DCB68A07DB0588E4D41");
717 pub const LOC_W_TV: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572"); pub const SS_TV: u8 = 2;
722 pub const ENC_ID_TV: &[u8] = &hex!("da9784962883c96ed01ff122c3");
723 pub const PRK_TV: &[u8] =
724 &hex!("d40f1601b577dbe7827bb3a20e0d16f7231c3a25225c1ed733f9094050d59666");
725 pub const K_1_TV: &[u8] = &hex!("6f2a9112801a5011aa33576b5c7862ad");
726 pub const IV_1_TV: &[u8] = &hex!("d31bc0d128349f290e79f0bde3");
727 pub const EAD1_VALUE_TV: &[u8] = &hex!(
728 "58287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c3"
729 );
730 pub const MESSAGE_1_WITH_EAD_TV: &[u8] = &hex!("0382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c3");
731
732 pub const VOUCHER_REQUEST_TV: &[u8] = &hex!("8158520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c3");
734
735 pub const VOUCHER_RESPONSE_TV: &[u8] = &hex!("8258520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c34948c783671337f75bd5");
737 pub const H_MESSAGE_1_TV: &[u8] =
738 &hex!("a1004dfd2c64777980d9c84f100f93a9cac511ae38f56b2210530c945d186c24");
739 pub const VOUCHER_INPUT_TV: &[u8] = &hex!("5820a1004dfd2c64777980d9c84f100f93a9cac511ae38f56b2210530c945d186c24585fa2026b6578616d706c652e65647508a101a501020241322001215820bbc34960526ea4d32e940cad2a234148ddc21791a12afbcbac93622046dd44f02258204519e257236b2a0ce2023f0931f1f386ca7afda64fcde0108c224c51eabf6072");
740 pub const VOUCHER_MAC_TV: &[u8] = &hex!("c783671337f75bd5");
741 pub const VOUCHER_TV: &[u8] = &hex!("48c783671337f75bd5");
742
743 pub const EAD2_VALUE_TV: &[u8] = &hex!("48c783671337f75bd5");
745
746 pub const SLO_OPAQUE_STATE_TV: &[u8] =
749 &hex!("827819666538303a3a623833343a643630623a373936663a38646530198bed");
750 pub const SLO_VOUCHER_REQUEST_TV: &[u8] = &hex!("8258520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c3581f827819666538303a3a623833343a643630623a373936663a38646530198bed");
751
752 pub const SLO_VOUCHER_RESPONSE_TV: &[u8] = &hex!("8358520382060258208af6f430ebe18d34184017a9a11bf511c8dff8f834730b96c1b7c8dbca2fc3b6370158287818636f61703a2f2f656e726f6c6c6d656e742e7365727665724dda9784962883c96ed01ff122c34948c783671337f75bd5581f827819666538303a3a623833343a643630623a373936663a38646530198bed");
754}
755
756#[cfg(test)]
757mod test_initiator {
758 use super::*;
759 use test_vectors::*;
760
761 use lakers_crypto::default_crypto;
762
763 #[test]
764 fn test_compute_keys() {
765 let k_1_tv: BytesCcmKeyLen = K_1_TV.try_into().unwrap();
766 let iv_1_tv: BytesCcmIvLen = IV_1_TV.try_into().unwrap();
767 let prk_tv: BytesHashLen = PRK_TV.try_into().unwrap();
768
769 let prk_xw = compute_prk(
770 &mut default_crypto(),
771 &X_TV.try_into().unwrap(),
772 &G_W_TV.try_into().unwrap(),
773 );
774 let prk_wx = compute_prk(
775 &mut default_crypto(),
776 &W_TV.try_into().unwrap(),
777 &G_X_TV.try_into().unwrap(),
778 );
779 assert_eq!(prk_xw, prk_tv);
780 assert_eq!(prk_xw, prk_wx);
781
782 let (k_1, iv_1) = compute_k_1_iv_1(&mut default_crypto(), &prk_xw);
783 assert_eq!(k_1, k_1_tv);
784 assert_eq!(iv_1, iv_1_tv);
785 }
786
787 #[test]
788 fn test_encrypt_enc_id() {
789 let enc_id_tv: EdhocMessageBuffer = ENC_ID_TV.try_into().unwrap();
790
791 let enc_id = encrypt_enc_id(
792 &mut default_crypto(),
793 &PRK_TV.try_into().unwrap(),
794 &ID_U_ENCODED_TV.try_into().unwrap(),
795 SS_TV,
796 );
797 assert_eq!(enc_id.content, enc_id_tv.content);
798 }
799
800 #[test]
801 fn test_decrypt_enc_id() {
802 let enc_id_tv: EdhocMessageBuffer = ENC_ID_TV.try_into().unwrap();
803 let mut prk_tv: BytesHashLen = Default::default();
804 prk_tv[..].copy_from_slice(PRK_TV);
805 let id_u_encoded_tv: EdhocMessageBuffer = ID_U_ENCODED_TV.try_into().unwrap();
806
807 let id_u_res = decrypt_enc_id(
808 &mut default_crypto(),
809 &prk_tv,
810 &ENC_ID_TV.try_into().unwrap(),
811 SS_TV,
812 );
813 assert!(id_u_res.is_ok());
814 assert_eq!(id_u_res.unwrap().content, id_u_encoded_tv.content);
815 }
816
817 #[test]
818 fn test_prepare_ead_1() {
819 let ead_1_value_tv: EdhocMessageBuffer = EAD1_VALUE_TV.try_into().unwrap();
820
821 ead_initiator_set_global_state(EADInitiatorState::new(
822 ID_U_TV.try_into().unwrap(),
823 G_W_TV.try_into().unwrap(),
824 LOC_W_TV.try_into().unwrap(),
825 ));
826
827 let ead_1 =
828 i_prepare_ead_1(&mut default_crypto(), &X_TV.try_into().unwrap(), SS_TV).unwrap();
829 assert_eq!(
830 ead_initiator_get_global_state().protocol_state,
831 EADInitiatorProtocolState::WaitEAD2
832 );
833 assert_eq!(ead_1.label, EAD_ZEROCONF_LABEL);
834 assert_eq!(ead_1.is_critical, true);
835 assert_eq!(ead_1.value.unwrap().content, ead_1_value_tv.content);
836 }
837
838 #[test]
839 fn test_verify_voucher() {
840 let voucher_tv = VOUCHER_TV.try_into().unwrap();
841 let h_message_1_tv = H_MESSAGE_1_TV.try_into().unwrap();
842 let cred_v_tv = CRED_V_TV.try_into().unwrap();
843 let prk_tv = PRK_TV.try_into().unwrap();
844 let voucher_mac_tv: BytesMac = VOUCHER_MAC_TV.try_into().unwrap();
845
846 let res = verify_voucher(
847 &mut default_crypto(),
848 &voucher_tv,
849 &h_message_1_tv,
850 &cred_v_tv,
851 &prk_tv,
852 );
853 assert!(res.is_ok());
854 assert_eq!(res.unwrap(), voucher_mac_tv);
855 }
856
857 #[test]
858 fn test_process_ead_2() {
859 let ead_2_value_tv: EdhocMessageBuffer = EAD2_VALUE_TV.try_into().unwrap();
860 let cred_v_tv: &[u8] = CRED_V_TV.try_into().unwrap();
861 let h_message_1_tv = H_MESSAGE_1_TV.try_into().unwrap();
862
863 let ead_2_tv = EADItem {
864 label: EAD_ZEROCONF_LABEL,
865 is_critical: true,
866 value: Some(ead_2_value_tv),
867 };
868
869 let mut state = EADInitiatorState::new(
870 ID_U_TV.try_into().unwrap(),
871 G_W_TV.try_into().unwrap(),
872 LOC_W_TV.try_into().unwrap(),
873 );
874 state.prk = PRK_TV.try_into().unwrap();
875 ead_initiator_set_global_state(state);
876
877 let res = i_process_ead_2(&mut default_crypto(), ead_2_tv, cred_v_tv, &h_message_1_tv);
878 assert!(res.is_ok());
879 assert_eq!(
880 ead_initiator_get_global_state().protocol_state,
881 EADInitiatorProtocolState::Completed
882 );
883 }
884}
885
886#[cfg(test)]
887mod test_responder {
888 use super::*;
889 use test_vectors::*;
890
891 use lakers_crypto::default_crypto;
892
893 #[test]
894 fn test_parse_ead_1_value() {
895 let ead_1_value_tv: EdhocMessageBuffer = EAD1_VALUE_TV.try_into().unwrap();
896 let loc_w_tv: EdhocMessageBuffer = LOC_W_TV.try_into().unwrap();
897 let enc_id_tv: EdhocMessageBuffer = ENC_ID_TV.try_into().unwrap();
898
899 let res = parse_ead_1_value(&ead_1_value_tv);
900 assert!(res.is_ok());
901 let (loc_w, enc_id) = res.unwrap();
902 assert_eq!(loc_w.content, loc_w_tv.content);
903 assert_eq!(enc_id.content, enc_id_tv.content);
904 }
905
906 #[test]
907 fn test_encode_voucher_request() {
908 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
909 let voucher_request_tv: EdhocMessageBuffer = VOUCHER_REQUEST_TV.try_into().unwrap();
910
911 let voucher_request = encode_voucher_request(&message_1_tv, &None);
912 assert_eq!(voucher_request.content, voucher_request_tv.content);
913 }
914
915 #[test]
916 fn test_process_ead_1() {
917 let ead_1_value_tv: EdhocMessageBuffer = EAD1_VALUE_TV.try_into().unwrap();
918 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
919
920 let ead_1 = EADItem {
921 label: EAD_ZEROCONF_LABEL,
922 is_critical: true,
923 value: Some(ead_1_value_tv),
924 };
925
926 ead_responder_set_global_state(EADResponderState::new());
927
928 mock_ead_server_set_global_state(MockEADServerState::new(
929 CRED_V_TV,
930 W_TV.try_into().unwrap(),
931 None,
932 ));
933
934 let res = r_process_ead_1(&mut default_crypto(), &ead_1, &message_1_tv);
935 assert!(res.is_ok());
936 assert_eq!(
937 ead_responder_get_global_state().protocol_state,
938 EADResponderProtocolState::ProcessedEAD1
939 );
940 }
941
942 #[test]
943 fn test_parse_voucher_response() {
944 let voucher_response_tv: EdhocMessageBuffer = VOUCHER_RESPONSE_TV.try_into().unwrap();
945 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
946 let voucher_tv: BytesEncodedVoucher = VOUCHER_TV.try_into().unwrap();
947
948 let res = parse_voucher_response(&voucher_response_tv);
949 assert!(res.is_ok());
950 let (message_1, voucher, opaque_state) = res.unwrap();
951 assert_eq!(message_1.content, message_1_tv.content);
952 assert_eq!(voucher, voucher_tv);
953 assert!(opaque_state.is_none());
954 }
955
956 #[test]
957 fn test_r_prepare_ead_2() {
958 let voucher_response_tv: EdhocMessageBuffer = VOUCHER_RESPONSE_TV.try_into().unwrap();
959 let ead_2_value_tv: EdhocMessageBuffer = EAD2_VALUE_TV.try_into().unwrap();
960
961 ead_responder_set_global_state(EADResponderState {
962 voucher_response: Some(voucher_response_tv),
963 ..EADResponderState::new()
964 });
965
966 let ead_2 = r_prepare_ead_2().unwrap();
967 assert_eq!(
968 ead_responder_get_global_state().protocol_state,
969 EADResponderProtocolState::Completed
970 );
971 assert_eq!(ead_2.label, EAD_ZEROCONF_LABEL);
972 assert_eq!(ead_2.is_critical, true);
973 assert_eq!(ead_2.value.unwrap().content, ead_2_value_tv.content);
974 }
975}
976
977#[cfg(test)]
978mod test_enrollment_server {
979 use super::*;
980 use test_vectors::*;
981
982 use lakers_crypto::default_crypto;
983
984 #[test]
985 fn test_encode_voucher_input() {
986 let h_message_1_tv: BytesHashLen = H_MESSAGE_1_TV.try_into().unwrap();
987 let cred_v_tv: EdhocMessageBuffer = CRED_V_TV.try_into().unwrap();
988 let voucher_input_tv: EdhocMessageBuffer = VOUCHER_INPUT_TV.try_into().unwrap();
989
990 let voucher_input = encode_voucher_input(&h_message_1_tv, &cred_v_tv);
991 assert_eq!(voucher_input.content, voucher_input_tv.content);
992 }
993
994 #[test]
995 fn test_compute_voucher_mac() {
996 let prk_tv: BytesHashLen = PRK_TV.try_into().unwrap();
997 let voucher_input_tv: EdhocMessageBuffer = VOUCHER_INPUT_TV.try_into().unwrap();
998 let voucher_mac_tv: BytesMac = VOUCHER_MAC_TV.try_into().unwrap();
999
1000 let voucher_mac = compute_voucher_mac(&mut default_crypto(), &prk_tv, &voucher_input_tv);
1001 assert_eq!(voucher_mac, voucher_mac_tv);
1002 }
1003
1004 #[test]
1005 fn test_prepare_voucher() {
1006 let h_message_1: BytesHashLen = H_MESSAGE_1_TV.try_into().unwrap();
1007 let cred_v: EdhocMessageBuffer = CRED_V_TV.try_into().unwrap();
1008 let prk: BytesHashLen = PRK_TV.try_into().unwrap();
1009 let voucher_tv: BytesEncodedVoucher = VOUCHER_TV.try_into().unwrap();
1010
1011 let voucher = prepare_voucher(&mut default_crypto(), &h_message_1, &cred_v, &prk);
1012 assert_eq!(voucher, voucher_tv);
1013 }
1014
1015 #[test]
1016 fn test_encode_voucher_response() {
1017 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
1018 let voucher_tv: BytesEncodedVoucher = VOUCHER_TV.try_into().unwrap();
1019 let opaque_state_tv: EdhocMessageBuffer = SLO_OPAQUE_STATE_TV.try_into().unwrap();
1020 let voucher_response_tv: EdhocMessageBuffer = SLO_VOUCHER_RESPONSE_TV.try_into().unwrap();
1021
1022 let voucher_response =
1023 encode_voucher_response(&message_1_tv, &voucher_tv, &Some(opaque_state_tv));
1024 assert_eq!(voucher_response.content, voucher_response_tv.content);
1025 }
1026
1027 #[test]
1028 fn test_parse_voucher_request() {
1029 let voucher_request_tv: EdhocMessageBuffer = VOUCHER_REQUEST_TV.try_into().unwrap();
1030 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
1031
1032 let voucher_request = parse_voucher_request(&voucher_request_tv);
1033 assert!(voucher_request.is_ok());
1034 let (message_1, opaque_state) = voucher_request.unwrap();
1035 assert_eq!(message_1.content, message_1_tv.content);
1036 assert!(opaque_state.is_none());
1037 }
1038
1039 #[test]
1040 fn test_handle_voucher_request() {
1041 let voucher_request_tv: EdhocMessageBuffer = VOUCHER_REQUEST_TV.try_into().unwrap();
1042 let cred_v_tv: EdhocMessageBuffer = CRED_V_TV.try_into().unwrap();
1043 let w_tv: BytesP256ElemLen = W_TV.try_into().unwrap();
1044 let g_x_tv: BytesP256ElemLen = G_X_TV.try_into().unwrap();
1045 let voucher_response_tv: EdhocMessageBuffer = VOUCHER_RESPONSE_TV.try_into().unwrap();
1046
1047 mock_ead_server_set_global_state(MockEADServerState::new(
1048 CRED_V_TV,
1049 W_TV.try_into().unwrap(),
1050 None,
1051 ));
1052
1053 let res = handle_voucher_request(
1054 &mut default_crypto(),
1055 &voucher_request_tv,
1056 &cred_v_tv,
1057 &w_tv,
1058 );
1059 assert!(res.is_ok());
1060 let voucher_response = res.unwrap();
1061 assert_eq!(voucher_response.content, voucher_response_tv.content);
1062 }
1063}
1064
1065#[cfg(test)]
1066mod test_stateless_operation {
1067 use super::*;
1068 use test_vectors::*;
1069
1070 use lakers_crypto::default_crypto;
1071
1072 #[test]
1073 fn slo_test_encode_voucher_request() {
1074 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
1075 let opaque_state_tv: EdhocMessageBuffer = SLO_OPAQUE_STATE_TV.try_into().unwrap();
1076 let voucher_request_tv: EdhocMessageBuffer = SLO_VOUCHER_REQUEST_TV.try_into().unwrap();
1077
1078 let voucher_request = encode_voucher_request(&message_1_tv, &Some(opaque_state_tv));
1079 assert_eq!(voucher_request.content, voucher_request_tv.content);
1080 }
1081
1082 #[test]
1083 fn slo_test_parse_voucher_response() {
1084 let voucher_response_tv: EdhocMessageBuffer = SLO_VOUCHER_RESPONSE_TV.try_into().unwrap();
1085 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
1086 let voucher_tv: BytesEncodedVoucher = VOUCHER_TV.try_into().unwrap();
1087 let opaque_state_tv: EdhocMessageBuffer = SLO_OPAQUE_STATE_TV.try_into().unwrap();
1088
1089 let res = parse_voucher_response(&voucher_response_tv);
1090 assert!(res.is_ok());
1091 let (message_1, voucher, opaque_state) = res.unwrap();
1092 assert_eq!(message_1.content, message_1_tv.content);
1093 assert_eq!(voucher, voucher_tv);
1094 assert_eq!(opaque_state.unwrap().content, opaque_state_tv.content);
1095 }
1096
1097 #[test]
1098 fn slo_test_parse_voucher_request() {
1099 let voucher_request_tv: EdhocMessageBuffer = SLO_VOUCHER_REQUEST_TV.try_into().unwrap();
1100 let message_1_tv: EdhocMessageBuffer = MESSAGE_1_WITH_EAD_TV.try_into().unwrap();
1101 let opaque_state_tv: EdhocMessageBuffer = SLO_OPAQUE_STATE_TV.try_into().unwrap();
1102
1103 let voucher_request = parse_voucher_request(&voucher_request_tv);
1104 assert!(voucher_request.is_ok());
1105 let (message_1, opaque_state) = voucher_request.unwrap();
1106 assert_eq!(message_1.content, message_1_tv.content);
1107 assert_eq!(opaque_state.unwrap().content, opaque_state_tv.content);
1108 }
1109
1110 #[test]
1111 fn slo_test_handle_voucher_request() {
1112 let voucher_request_tv: EdhocMessageBuffer = SLO_VOUCHER_REQUEST_TV.try_into().unwrap();
1113 let cred_v_tv: EdhocMessageBuffer = CRED_V_TV.try_into().unwrap();
1114 let w_tv: BytesP256ElemLen = W_TV.try_into().unwrap();
1115 let g_x_tv: BytesP256ElemLen = G_X_TV.try_into().unwrap();
1116 let voucher_response_tv: EdhocMessageBuffer = SLO_VOUCHER_RESPONSE_TV.try_into().unwrap();
1117
1118 mock_ead_server_set_global_state(MockEADServerState::new(
1119 CRED_V_TV,
1120 W_TV.try_into().unwrap(),
1121 None,
1122 ));
1123
1124 let res = handle_voucher_request(
1125 &mut default_crypto(),
1126 &voucher_request_tv,
1127 &cred_v_tv,
1128 &w_tv,
1129 );
1130 assert!(res.is_ok());
1131 let voucher_response = res.unwrap();
1132 assert_eq!(voucher_response.content, voucher_response_tv.content);
1133 }
1134}