Skip to content

Commit

Permalink
feat: build ENC_ID
Browse files Browse the repository at this point in the history
  • Loading branch information
geonnave committed Oct 9, 2023
1 parent c90481e commit c3fcfa6
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 85 deletions.
2 changes: 1 addition & 1 deletion consts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use structs::*;
mod consts {
use super::structs::*;

pub const MAX_MESSAGE_SIZE_LEN: usize = 64;
pub const MAX_MESSAGE_SIZE_LEN: usize = 128; // need 128 to handle EAD fields
pub const MAX_EAD_SIZE_LEN: usize = 64;
pub type EADMessageBuffer = EdhocMessageBuffer; // TODO: make it of size MAX_EAD_SIZE_LEN
pub const EAD_ZEROCONF_LABEL: u8 = 0x1; // NOTE: in lake-authz-draft-02 it is still TBD1
Expand Down
6 changes: 4 additions & 2 deletions crypto/edhoc-crypto-cryptocell310-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,16 @@ pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen
pub fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
ad: &[u8],
plaintext: &BufferPlaintext3,
) -> BufferCiphertext3 {
let mut output: BufferCiphertext3 = BufferCiphertext3::new();
let mut tag: CRYS_AESCCM_Mac_Res_t = Default::default();
let mut aesccm_key: CRYS_AESCCM_Key_t = Default::default();
let mut aesccm_ad = [0x00u8; ENC_STRUCTURE_LEN];

aesccm_key[0..AES_CCM_KEY_LEN].copy_from_slice(&key[..]);
aesccm_ad[0..ad.len()].copy_from_slice(&ad[..]);

let err = unsafe {
CC_AESCCM(
Expand All @@ -86,7 +88,7 @@ pub fn aes_ccm_encrypt_tag_8(
CRYS_AESCCM_KeySize_t_CRYS_AES_Key128BitSize,
iv.clone().as_mut_ptr(),
iv.len() as u8,
ad.clone().as_mut_ptr(),
aesccm_ad.as_mut_ptr(),
ad.len() as u32,
plaintext.content.clone().as_mut_ptr(),
plaintext.len as u32,
Expand Down
8 changes: 2 additions & 6 deletions crypto/edhoc-crypto-hacspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,13 @@ pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen
pub fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
ad: &[u8],
plaintext: &BufferPlaintext3,
) -> BufferCiphertext3 {
let plaintext = BufferPlaintext3Hacspec::from_public_buffer(plaintext);

let output = BufferCiphertext3Hacspec::from_seq(&encrypt_ccm(
ByteSeq::from_slice(
&BytesEncStructureLenHacspec::from_public_slice(ad),
0,
ad.len(),
),
ByteSeq::from_public_slice(ad),
ByteSeq::from_slice(&BytesCcmIvLenHacspec::from_public_slice(iv), 0, iv.len()),
ByteSeq::from_slice(&plaintext.content, 0, plaintext.len),
Key128::from_slice(&BytesCcmKeyLenHacspec::from_public_slice(key), 0, key.len()),
Expand Down
2 changes: 1 addition & 1 deletion crypto/edhoc-crypto-psa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub fn hkdf_extract(salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen
pub fn aes_ccm_encrypt_tag_8(
key: &BytesCcmKeyLen,
iv: &BytesCcmIvLen,
ad: &BytesEncStructureLen,
ad: &[u8],
plaintext: &BufferPlaintext3,
) -> BufferCiphertext3 {
psa_crypto::init().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion ead/edhoc-ead-none/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use edhoc_consts::*;

// initiator side
pub fn i_prepare_ead_1() -> Option<EADItem> {
pub fn i_prepare_ead_1(x: &BytesP256ElemLen, ss: u8) -> Option<EADItem> {
None
}

Expand Down
12 changes: 5 additions & 7 deletions ead/edhoc-ead-zeroconf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ description = "EDHOC EAD zeroconf (draf-lake-authz)"

[dependencies]
edhoc-consts = { path = "../../consts" }
edhoc-crypto = { path = "../../crypto", default-features = false, features = ["psa"] }
edhoc-crypto = { path = "../../crypto", default-features = false }
hacspec-lib = { version = "0.1.0-beta.1", default-features = false, optional = true }
hexlit = "0.5.3"

[features]
# FIXME: allow selection of crypto backend
# default = [ "crypto-psa" ]
# crypto-hacspec = ["hacspec-lib/std", "edhoc-crypto/hacspec" ]
# crypto-psa = [ "edhoc-crypto/psa" ]
# crypto-psa-baremetal = [ "edhoc-crypto/psa-baremetal", "panic-semihosting" ]
# crypto-cryptocell310 = [ "edhoc-crypto/cryptocell310", "panic-semihosting" ]
default = [ "crypto-psa" ]
crypto-psa = [ "edhoc-crypto/psa" ]
crypto-hacspec = ["hacspec-lib/std", "edhoc-crypto/hacspec" ]
124 changes: 61 additions & 63 deletions ead/edhoc-ead-zeroconf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use edhoc_crypto::*;
#[derive(Default, PartialEq, Copy, Clone, Debug)]
pub enum EADInitiatorProtocolState {
#[default]
NonInitialized,
Start,
WaitEAD2,
Completed, // TODO[ead]: check if it is really ok to consider Completed after processing EAD_2
Expand All @@ -15,22 +16,15 @@ pub enum EADInitiatorProtocolState {
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct EADInitiatorState {
pub protocol_state: EADInitiatorProtocolState,
pub(crate) u: BytesP256ElemLen, // private key of the device (U), equivalent to I in EDHOC
pub(crate) id_u: EdhocMessageBuffer, // identifier of the device (U), equivalent to ID_CRED_I in EDHOC
pub(crate) g_w: BytesP256ElemLen, // public key of the enrollment server (W)
pub(crate) g_w: BytesP256ElemLen, // public key of the enrollment server (W)
pub(crate) loc_w: EdhocMessageBuffer, // address of the enrollment server (W)
}

impl EADInitiatorState {
pub fn new(
u: BytesP256ElemLen,
id_u: EdhocMessageBuffer,
g_w: BytesP256ElemLen,
loc_w: EdhocMessageBuffer,
) -> Self {
pub fn new(id_u: EdhocMessageBuffer, g_w: BytesP256ElemLen, loc_w: EdhocMessageBuffer) -> Self {
EADInitiatorState {
protocol_state: EADInitiatorProtocolState::Start,
u,
id_u,
g_w,
loc_w,
Expand All @@ -44,7 +38,6 @@ static mut EAD_INITIATOR_GLOBAL_STATE: EADInitiatorState = EADInitiatorState {
protocol_state: EADInitiatorProtocolState::Start,
// FIXME: lots of wasted bytes in id_u and loc_w.
// they could both be &[u8], but that would require a <'a> lifetime which clashes with the static lifetime of the global state
u: [0u8; P256_ELEM_LEN],
id_u: EdhocMessageBuffer {
content: [0u8; MAX_MESSAGE_SIZE_LEN],
len: 0,
Expand All @@ -67,22 +60,20 @@ pub fn ead_initiator_set_global_state(new_state: EADInitiatorState) {
}
}

pub fn i_prepare_ead_1() -> Option<EADItem> {
// TODO: receive ss as parameter
let ss = EDHOC_SUPPORTED_SUITES[0];
let mut ead_1 = EADItem::new();

// this ead item is critical
ead_1.label = EAD_ZEROCONF_LABEL;
ead_1.is_critical = true;

pub fn i_prepare_ead_1(x: &BytesP256ElemLen, ss: u8) -> Option<EADItem> {
let state = ead_initiator_get_global_state();
if state.protocol_state != EADInitiatorProtocolState::Start {
return None;
}

// UNDER TEST
let _enc_id = build_enc_id(&state.u, &state.id_u, &state.g_w, ss);
let enc_id = build_enc_id(x, &state.id_u, &state.g_w, ss);
let value = Some(encode_ead_1(&state.loc_w, &enc_id));

let enc_id_dummy = build_enc_id_dummy(state.id_u, ss);
ead_1.value = Some(encode_ead_1(state.loc_w, enc_id_dummy));
let ead_1 = EADItem {
label: EAD_ZEROCONF_LABEL,
is_critical: true,
value,
};

ead_initiator_set_global_state(EADInitiatorState {
protocol_state: EADInitiatorProtocolState::WaitEAD2,
Expand All @@ -108,32 +99,19 @@ pub fn i_prepare_ead_3() -> Option<EADItem> {
Some(EADItem::new())
}

// TODO: actually implement this function
use hexlit::hex;
const ENC_ID: &[u8] = &hex!("4545452E4545452E4545452E");
fn build_enc_id_dummy(_id_u: EdhocMessageBuffer, _ss: u8) -> EdhocMessageBuffer {
let enc_id: EdhocMessageBuffer = ENC_ID.try_into().unwrap();
enc_id
}

fn build_enc_id(
u: &BytesP256ElemLen,
x: &BytesP256ElemLen, // ephemeral key of U
id_u: &EdhocMessageBuffer,
g_w: &BytesP256ElemLen,
ss: u8
ss: u8,
) -> EdhocMessageBuffer {

// PRK = EDHOC-Extract(salt, IKM)
// FIXME: salt should be 0x (the zero-length byte string), but crypto backends are hardcoded to salts of size SHA256_DIGEST_LEN.
let salt: BytesHashLen = [0u8; SHA256_DIGEST_LEN];
let g_uw = p256_ecdh(u, g_w);
let prk = hkdf_extract(&salt, &g_uw);

// K_1 = EDHOC-Expand(PRK, info, length)
let k_1 = edhoc_kdf(&prk, 0, &[0x00; MAX_KDF_CONTEXT_LEN], 0, AES_CCM_KEY_LEN);
let g_xw = p256_ecdh(x, g_w);
let prk = hkdf_extract(&salt, &g_xw);

// IV_1 = EDHOC-Expand(PRK, info, length)
let iv_1 = edhoc_kdf(&prk, 1, &[0x00; MAX_KDF_CONTEXT_LEN], 0, AES_CCM_IV_LEN);
let (k_1, iv_1) = compute_k_1_iv_1(&prk);

// plaintext = (ID_U: bstr)
let mut plaintext = EdhocMessageBuffer::new();
Expand All @@ -142,14 +120,26 @@ fn build_enc_id(
plaintext.len = 1 + id_u.len;

// external_aad = (SS: int)
// ENC_ID = 'ciphertext' of COSE_Encrypt0
// DRAFT: in Section 4.4.1. add: "The external_aad is wrapped in an enc_structure as defined in Appendix C3 of draft-ietf-lake-edhoc-22"
let enc_structure = encode_enc_structure(ss);

// let ciphertext = aes_ccm_encrypt_tag_8(&k_1, &iv_1, &enc_structure, plaintext);
// ENC_ID = 'ciphertext' of COSE_Encrypt0
aes_ccm_encrypt_tag_8(&k_1, &iv_1, &enc_structure[..], &plaintext)
}

fn compute_k_1_iv_1(prk: &BytesHashLen) -> (BytesCcmKeyLen, BytesCcmIvLen) {
// K_1 = EDHOC-Expand(PRK, info = (0, h'', 0), length)
let mut k_1: BytesCcmKeyLen = [0x00; AES_CCM_KEY_LEN];
// DRAFT: any reason for context to be 'empty CBOR string' instead of 'context_len 0-filled' ?
let k_1_buf = edhoc_kdf(prk, 0, &[0x00; MAX_KDF_CONTEXT_LEN], 0, AES_CCM_KEY_LEN); // FIXME: context should be h'' (the empty CBOR string)
k_1[..].copy_from_slice(&k_1_buf[..AES_CCM_KEY_LEN]);

// IV_1 = EDHOC-Expand(PRK, info = (1, h'', 0), length)
let mut iv_1: BytesCcmIvLen = [0x00; AES_CCM_IV_LEN];
let iv_1_buf = edhoc_kdf(prk, 1, &[0x00; MAX_KDF_CONTEXT_LEN], 0, AES_CCM_KEY_LEN); // FIXME: context should be h'' (the empty CBOR string)
iv_1[..].copy_from_slice(&iv_1_buf[..AES_CCM_IV_LEN]);

let enc_id: EdhocMessageBuffer = ENC_ID.try_into().unwrap();
enc_id
(k_1, iv_1)
}

const EAD_ENC_STRUCTURE_LEN: usize = 2 + 8 + 1 + 2;
Expand Down Expand Up @@ -186,35 +176,35 @@ fn edhoc_kdf(
length: usize,
) -> BytesMaxBuffer {
let mut info: BytesMaxInfoBuffer = [0x00; MAX_INFO_LEN];
let mut info_len = 0;

// construct info with inline cbor encoding
info[0] = label;
if context_len < 24 {
let mut info_len = if context_len < 24 {
info[1] = context_len as u8 | CBOR_MAJOR_BYTE_STRING;
info[2..2 + context_len].copy_from_slice(&context[..context_len]);
info_len = 2 + context_len;
2 + context_len
} else {
info[1] = CBOR_BYTE_STRING;
info[2] = context_len as u8;
info[3..3 + context_len].copy_from_slice(&context[..context_len]);
info_len = 3 + context_len;
}
if length < 24 {
3 + context_len
};

info_len = if length < 24 {
info[info_len] = length as u8;
info_len = info_len + 1;
info_len + 1
} else {
info[info_len] = CBOR_UINT_1BYTE;
info[info_len + 1] = length as u8;
info_len = info_len + 2;
}
info_len + 2
};

let output = hkdf_expand(prk, &info, info_len, length);

output
}

fn encode_ead_1(loc_w: EdhocMessageBuffer, enc_id: EdhocMessageBuffer) -> EdhocMessageBuffer {
fn encode_ead_1(loc_w: &EdhocMessageBuffer, enc_id: &EdhocMessageBuffer) -> EdhocMessageBuffer {
let mut output = EdhocMessageBuffer::new();

output.content[0] = CBOR_TEXT_STRING;
Expand All @@ -237,38 +227,46 @@ mod test_initiator {
use hexlit::hex;

// U
const U: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b");
const X_TV: BytesP256ElemLen =
hex!("368ec1f69aeb659ba37d5a8d45b21bdc0299dceaa8ef235f3ca42ce3530f9525");
const ID_U: &[u8] = &hex!("a104412b");

// V
// TODO...

// W
const G_W: &[u8] = &hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6"); // TODO: update
const LOC_W: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572");
const G_W: &[u8] = &hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6"); // FIXME: I just copied from G_R
const LOC_W: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572"); // coap://enrollment.server

// voucher_info
// Voucher_Info, FIXME: the ENC_ID is just a mock
const VOUCHER_INFO_TV: &[u8] =
&hex!("7818636F61703A2F2F656E726F6C6C6D656E742E7365727665724C4545452E4545452E4545452E");

#[test]
fn test_prepare_ead_1() {
let u: BytesP256ElemLen = U.try_into().unwrap();
let x: BytesP256ElemLen = X_TV.try_into().unwrap();
let id_u: EdhocMessageBuffer = ID_U.try_into().unwrap();
let g_w: BytesP256ElemLen = G_W.try_into().unwrap();
let loc_w: EdhocMessageBuffer = LOC_W.try_into().unwrap();
let ead_1_value_tv: EdhocMessageBuffer = VOUCHER_INFO_TV.try_into().unwrap();
let ss: u8 = EDHOC_SUPPORTED_SUITES[0];

ead_initiator_set_global_state(EADInitiatorState::new(u, id_u, g_w, loc_w));
ead_initiator_set_global_state(EADInitiatorState::new(id_u, g_w, loc_w));

let ead_1 = i_prepare_ead_1().unwrap();
let ead_1 = i_prepare_ead_1(&x, ss).unwrap();
assert_eq!(
ead_initiator_get_global_state().protocol_state,
EADInitiatorProtocolState::WaitEAD2
);
assert_eq!(ead_1.label, EAD_ZEROCONF_LABEL);
assert_eq!(ead_1.is_critical, true);
assert_eq!(ead_1.value.unwrap().content, ead_1_value_tv.content);

// FIXME: testing only loc_w in the absense of a ENC_ID test vector
assert_eq!(
ead_1.value.unwrap().content[0..loc_w.len + 2],
ead_1_value_tv.content[0..loc_w.len + 2]
);
// assert_eq!(ead_1.value.unwrap().content, ead_1_value_tv.content);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/edhoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ pub fn i_prepare_message_1(
let mut suites_i: BytesSuites = [0x0; SUITES_LEN];
suites_i[0..EDHOC_SUPPORTED_SUITES.len()].copy_from_slice(&EDHOC_SUPPORTED_SUITES[..]);

let ead_1 = i_prepare_ead_1();
let ead_1 = i_prepare_ead_1(&x, suites_i[suites_i.len() - 1]);

// Encode message_1 as a sequence of CBOR encoded data items as specified in Section 5.2.1
message_1 = encode_message_1(
Expand Down Expand Up @@ -1208,7 +1208,7 @@ fn encrypt_message_3(

let (k_3, iv_3) = compute_k_3_iv_3(prk_3e2m, th_3);

let ciphertext_3 = aes_ccm_encrypt_tag_8(&k_3, &iv_3, &enc_structure, plaintext_3);
let ciphertext_3 = aes_ccm_encrypt_tag_8(&k_3, &iv_3, &enc_structure[..], plaintext_3);

output.content[1..output.len].copy_from_slice(&ciphertext_3.content[..ciphertext_3.len]);

Expand Down
20 changes: 18 additions & 2 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,9 +485,20 @@ mod test {
assert_eq!(i_prk_out_new.unwrap(), r_prk_out_new.unwrap());
}

// U
const U: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b");
const ID_U: &[u8] = &hex!("a104412b");

// V
// TODO...

// W
const G_W: &[u8] = &hex!("ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6"); // TODO: update
const LOC_W: &[u8] = &hex!("636F61703A2F2F656E726F6C6C6D656E742E736572766572");

#[cfg(feature = "ead-zeroconf")]
#[test]
fn test_ead() {
fn test_ead_zeroconf() {
let state_initiator: EdhocState = Default::default();
let mut initiator = EdhocInitiator::new(
state_initiator,
Expand All @@ -509,7 +520,12 @@ mod test {
CRED_R,
);

ead_initiator_set_global_state(EADInitiatorState::new(EdhocMessageBuffer::new()));
let u: BytesP256ElemLen = U.try_into().unwrap();
let id_u: EdhocMessageBuffer = ID_U.try_into().unwrap();
let g_w: BytesP256ElemLen = G_W.try_into().unwrap();
let loc_w: EdhocMessageBuffer = LOC_W.try_into().unwrap();
ead_initiator_set_global_state(EADInitiatorState::new(id_u, g_w, loc_w));

let ead_initiator_state = ead_initiator_get_global_state();
assert_eq!(
ead_initiator_state.protocol_state,
Expand Down

0 comments on commit c3fcfa6

Please sign in to comment.