diff --git a/lib/src/edhoc.rs b/lib/src/edhoc.rs index 09554bb3..1437f5c8 100644 --- a/lib/src/edhoc.rs +++ b/lib/src/edhoc.rs @@ -95,7 +95,7 @@ pub fn r_process_message_1( pub fn r_prepare_message_2( state: &ProcessingM1, crypto: &mut impl CryptoTrait, - cred_r: CredentialRPK, + cred_r: Credential, r: &BytesP256ElemLen, // R's static private DH key c_r: ConnId, cred_transfer: CredentialTransfer, @@ -110,12 +110,8 @@ pub fn r_prepare_message_2( let prk_3e2m = compute_prk_3e2m(crypto, &salt_3e2m, r, &state.g_x); let id_cred_r = match cred_transfer { - CredentialTransfer::ByValue => { - IdCred::tmp_from_ccs_or_kid(cred_r.value.as_slice(), IdCredType::KCCS as u8)? - } - CredentialTransfer::ByReference => { - IdCred::tmp_from_ccs_or_kid(&[cred_r.kid], IdCredType::KID as u8)? - } + CredentialTransfer::ByValue => cred_r.by_value()?, + CredentialTransfer::ByReference => cred_r.by_kid()?, }; // compute MAC_2 @@ -124,7 +120,7 @@ pub fn r_prepare_message_2( &prk_3e2m, c_r, id_cred_r.as_full_value(), - cred_r.value.as_slice(), + cred_r.bytes.as_slice(), &th_2, ead_2, ); @@ -134,7 +130,7 @@ pub fn r_prepare_message_2( // step is actually from processing of message_3 // but we do it here to avoid storing plaintext_2 in State - let th_3 = compute_th_3(crypto, &th_2, &plaintext_2, cred_r.value.as_slice()); + let th_3 = compute_th_3(crypto, &th_2, &plaintext_2, cred_r.bytes.as_slice()); let mut ct: BufferCiphertext2 = BufferCiphertext2::new(); ct.fill_with_slice(plaintext_2.as_slice()).unwrap(); // TODO(hax): can we prove with hax that this won't panic since they use the same underlying buffer length? @@ -173,6 +169,7 @@ pub fn r_parse_message_3( y: state.y, prk_3e2m: state.prk_3e2m, th_3: state.th_3, + id_cred_i: id_cred_i.clone(), // needed for compute_mac_3 plaintext_3, // NOTE: this is needed for th_4, which needs valid_cred_i, which is only available at the 'verify' step ead_3: ead_3.clone(), // NOTE: this clone could be avoided by using a reference or an index to the ead_3 item in plaintext_3 }, @@ -191,20 +188,25 @@ pub fn r_parse_message_3( pub fn r_verify_message_3( state: &mut ProcessingM3, crypto: &mut impl CryptoTrait, - valid_cred_i: CredentialRPK, + valid_cred_i: Credential, ) -> Result<(Completed, BytesHashLen), EDHOCError> { // compute salt_4e3m let salt_4e3m = compute_salt_4e3m(crypto, &state.prk_3e2m, &state.th_3); - // TODO compute prk_4e3m - let prk_4e3m = compute_prk_4e3m(crypto, &salt_4e3m, &state.y, &valid_cred_i.public_key); + + let prk_4e3m = match valid_cred_i.key { + CredentialKey::EC2Compact(public_key) => { + compute_prk_4e3m(crypto, &salt_4e3m, &state.y, &public_key) + } + CredentialKey::Symmetric(_psk) => todo!("PSK not implemented"), + }; // compute mac_3 let expected_mac_3 = compute_mac_3( crypto, &prk_4e3m, &state.th_3, - &valid_cred_i.get_id_cred(), - valid_cred_i.value.as_slice(), + state.id_cred_i.as_full_value(), + valid_cred_i.bytes.as_slice(), &state.ead_3, ); @@ -214,7 +216,7 @@ pub fn r_verify_message_3( crypto, &state.th_3, &state.plaintext_3, - valid_cred_i.value.as_slice(), + valid_cred_i.bytes.as_slice(), ); let mut th_4_buf: BytesMaxContextBuffer = [0x00; MAX_KDF_CONTEXT_LEN]; @@ -308,7 +310,8 @@ pub fn i_parse_message_2<'a>( g_y, plaintext_2: plaintext_2, c_r: c_r_2, - ead_2: ead_2.clone(), // needed for compute_mac_2 + id_cred_r: id_cred_r.clone(), // needed for compute_mac_2 + ead_2: ead_2.clone(), // needed for compute_mac_2 }; Ok((state, c_r_2, id_cred_r, ead_2)) @@ -323,20 +326,25 @@ pub fn i_parse_message_2<'a>( pub fn i_verify_message_2( state: &ProcessingM2, crypto: &mut impl CryptoTrait, - valid_cred_r: CredentialRPK, // TODO: have a struct to hold credentials to avoid re-computing - i: &BytesP256ElemLen, // I's static private DH key + valid_cred_r: Credential, + i: &BytesP256ElemLen, // I's static private DH key ) -> Result { // verify mac_2 let salt_3e2m = compute_salt_3e2m(crypto, &state.prk_2e, &state.th_2); - let prk_3e2m = compute_prk_3e2m(crypto, &salt_3e2m, &state.x, &valid_cred_r.public_key); + let prk_3e2m = match valid_cred_r.key { + CredentialKey::EC2Compact(public_key) => { + compute_prk_3e2m(crypto, &salt_3e2m, &state.x, &public_key) + } + CredentialKey::Symmetric(_psk) => todo!("PSK not implemented"), + }; let expected_mac_2 = compute_mac_2( crypto, &prk_3e2m, state.c_r, - &valid_cred_r.get_id_cred(), - valid_cred_r.value.as_slice(), + state.id_cred_r.as_full_value(), + valid_cred_r.bytes.as_slice(), &state.th_2, &state.ead_2, ); @@ -348,7 +356,7 @@ pub fn i_verify_message_2( crypto, &state.th_2, &state.plaintext_2, - valid_cred_r.value.as_slice(), + valid_cred_r.bytes.as_slice(), ); // message 3 processing @@ -371,17 +379,13 @@ pub fn i_verify_message_2( pub fn i_prepare_message_3( state: &ProcessedM2, crypto: &mut impl CryptoTrait, - cred_i: CredentialRPK, + cred_i: Credential, cred_transfer: CredentialTransfer, ead_3: &Option, // FIXME: make it a list of EADItem ) -> Result<(Completed, BufferMessage3, BytesHashLen), EDHOCError> { let id_cred_i = match cred_transfer { - CredentialTransfer::ByValue => { - IdCred::tmp_from_ccs_or_kid(cred_i.value.as_slice(), IdCredType::KCCS as u8)? - } - CredentialTransfer::ByReference => { - IdCred::tmp_from_ccs_or_kid(&[cred_i.kid], IdCredType::KID as u8)? - } + CredentialTransfer::ByValue => cred_i.by_value()?, + CredentialTransfer::ByReference => cred_i.by_kid()?, }; let mac_3 = compute_mac_3( @@ -389,14 +393,14 @@ pub fn i_prepare_message_3( &state.prk_4e3m, &state.th_3, id_cred_i.as_full_value(), - cred_i.value.as_slice(), + cred_i.bytes.as_slice(), ead_3, ); let plaintext_3 = encode_plaintext_3(id_cred_i.as_encoded_value(), &mac_3, &ead_3)?; let message_3 = encrypt_message_3(crypto, &state.prk_3e2m, &state.th_3, &plaintext_3); - let th_4 = compute_th_4(crypto, &state.th_3, &plaintext_3, cred_i.value.as_slice()); + let th_4 = compute_th_4(crypto, &state.th_3, &plaintext_3, cred_i.bytes.as_slice()); let mut th_4_buf: BytesMaxContextBuffer = [0x00; MAX_KDF_CONTEXT_LEN]; th_4_buf[..th_4.len()].copy_from_slice(&th_4[..]); diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 6901915f..caed15c9 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -32,7 +32,7 @@ pub use edhoc::*; pub struct EdhocInitiator { state: InitiatorStart, // opaque state i: Option, // static public key of myself - cred_i: Option, + cred_i: Option, crypto: Crypto, } @@ -40,7 +40,7 @@ pub struct EdhocInitiator { pub struct EdhocInitiatorWaitM2 { state: WaitM2, // opaque state i: Option, - cred_i: Option, + cred_i: Option, crypto: Crypto, } @@ -48,14 +48,14 @@ pub struct EdhocInitiatorWaitM2 { pub struct EdhocInitiatorProcessingM2 { state: ProcessingM2, // opaque state i: Option, - cred_i: Option, + cred_i: Option, crypto: Crypto, } #[derive(Debug)] pub struct EdhocInitiatorProcessedM2 { state: ProcessedM2, // opaque state - cred_i: Option, + cred_i: Option, crypto: Crypto, } @@ -70,15 +70,15 @@ pub struct EdhocInitiatorDone { pub struct EdhocResponder { state: ResponderStart, // opaque state r: BytesP256ElemLen, // private authentication key of R - cred_r: CredentialRPK, // R's full credential + cred_r: Credential, // R's full credential crypto: Crypto, } #[derive(Debug)] pub struct EdhocResponderProcessedM1 { - state: ProcessingM1, // opaque state - r: BytesP256ElemLen, // private authentication key of R - cred_r: CredentialRPK, // R's full credential + state: ProcessingM1, // opaque state + r: BytesP256ElemLen, // private authentication key of R + cred_r: Credential, // R's full credential crypto: Crypto, } @@ -105,7 +105,7 @@ impl EdhocResponder { mut crypto: Crypto, method: EDHOCMethod, r: BytesP256ElemLen, - cred_r: CredentialRPK, + cred_r: Credential, ) -> Self { trace!("Initializing EdhocResponder"); let (y, g_y) = crypto.p256_generate_key_pair(); @@ -199,7 +199,7 @@ impl<'a, Crypto: CryptoTrait> EdhocResponderWaitM3 { impl<'a, Crypto: CryptoTrait> EdhocResponderProcessingM3 { pub fn verify_message_3( mut self, - cred_i: CredentialRPK, + cred_i: Credential, ) -> Result<(EdhocResponderDone, [u8; SHA256_DIGEST_LEN]), EDHOCError> { trace!("Enter verify_message_3"); match r_verify_message_3(&mut self.state, &mut self.crypto, cred_i) { @@ -267,7 +267,7 @@ impl<'a, Crypto: CryptoTrait> EdhocInitiator { } } - pub fn set_identity(&mut self, i: BytesP256ElemLen, cred_i: CredentialRPK) { + pub fn set_identity(&mut self, i: BytesP256ElemLen, cred_i: Credential) { self.i = Some(i); self.cred_i = Some(cred_i); } @@ -341,7 +341,7 @@ impl<'a, Crypto: CryptoTrait> EdhocInitiatorProcessingM2 { pub fn set_identity( &mut self, i: BytesP256ElemLen, - cred_i: CredentialRPK, + cred_i: Credential, ) -> Result<(), EDHOCError> { if self.i.is_some() || self.cred_i.is_some() { return Err(EDHOCError::IdentityAlreadySet); @@ -353,7 +353,7 @@ impl<'a, Crypto: CryptoTrait> EdhocInitiatorProcessingM2 { pub fn verify_message_2( mut self, - valid_cred_r: CredentialRPK, + valid_cred_r: Credential, ) -> Result, EDHOCError> { trace!("Enter verify_message_2"); let Some(i) = self.i else { @@ -463,9 +463,9 @@ pub fn generate_connection_identifier(crypto: &mut Crypto) // Implements auth credential checking according to draft-tiloca-lake-implem-cons pub fn credential_check_or_fetch( - cred_expected: Option, + cred_expected: Option, id_cred_received: IdCred, -) -> Result { +) -> Result { trace!("Enter credential_check_or_fetch"); // Processing of auth credentials according to draft-tiloca-lake-implem-cons // Comments tagged with a number refer to steps in Section 4.3.1. of draft-tiloca-lake-implem-cons @@ -474,10 +474,9 @@ pub fn credential_check_or_fetch( // IMPL: compare cred_i_expected with id_cred // IMPL: assume cred_i_expected is well formed let credentials_match = if id_cred_received.reference_only() { - // FIXME: will be fixed when we update CredentialRPK to Credential - id_cred_received.as_full_value()[3] == cred_expected.kid + id_cred_received.as_full_value() == cred_expected.by_kid()?.as_full_value() } else { - &id_cred_received.as_full_value()[2..] == cred_expected.value.as_slice() + id_cred_received.as_full_value() == cred_expected.by_value()?.as_full_value() }; // 2. Is this authentication credential still valid? @@ -506,8 +505,8 @@ pub fn credential_check_or_fetch( // Pair it with consistent credential identifiers, for each supported type of credential identifier. assert!(!id_cred_received.reference_only()); - // FIXME: will be fixed when we update CredentialRPK to Credential - CredentialRPK::new(id_cred_received.as_full_value()[2..].try_into().unwrap()) + // FIXME: this is not elegant, should be solved at IdCred level + Credential::parse_ccs(id_cred_received.as_full_value()[2..].try_into().unwrap()) .map_err(|_| EDHOCError::ParsingError) } @@ -553,7 +552,7 @@ mod test { default_crypto(), EDHOCMethod::StatStat, R.try_into().expect("Wrong length of responder private key"), - CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap(), + Credential::parse_ccs(CRED_R.try_into().unwrap()).unwrap(), ); } @@ -578,7 +577,7 @@ mod test { default_crypto(), EDHOCMethod::StatStat, R.try_into().expect("Wrong length of responder private key"), - CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap(), + Credential::parse_ccs(CRED_R.try_into().unwrap()).unwrap(), ); // process message_1 first time, when unsupported suite is selected @@ -592,7 +591,7 @@ mod test { default_crypto(), EDHOCMethod::StatStat, R.try_into().expect("Wrong length of responder private key"), - CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap(), + Credential::parse_ccs(CRED_R.try_into().unwrap()).unwrap(), ); // process message_1 second time @@ -609,8 +608,8 @@ mod test { #[cfg(feature = "test-ead-none")] #[test] fn test_handshake() { - let cred_i = CredentialRPK::new(CRED_I.try_into().unwrap()).unwrap(); - let cred_r = CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap(); + let cred_i = Credential::parse_ccs(CRED_I.try_into().unwrap()).unwrap(); + let cred_r = Credential::parse_ccs(CRED_R.try_into().unwrap()).unwrap(); let initiator = EdhocInitiator::new( default_crypto(), @@ -712,11 +711,11 @@ mod test_authz { // TODO: have a setup_test function that prepares the common objects for the ead tests #[test] fn test_handshake_authz() { - let cred_i = CredentialRPK::new(CRED_I.try_into().unwrap()).unwrap(); - let cred_r = CredentialRPK::new(CRED_R.try_into().unwrap()).unwrap(); + let cred_i = Credential::parse_ccs(CRED_I.try_into().unwrap()).unwrap(); + let cred_r = Credential::parse_ccs(CRED_R.try_into().unwrap()).unwrap(); - let mock_fetch_cred_i = |id_cred_i: CredentialRPK| -> Result { - if id_cred_i.kid == cred_i.kid { + let mock_fetch_cred_i = |id_cred_i: IdCred| -> Result { + if id_cred_i.as_full_value() == cred_i.by_kid()?.as_full_value() { Ok(cred_i.clone()) } else { Err(EDHOCError::UnexpectedCredential) @@ -744,7 +743,8 @@ mod test_authz { ); let authenticator = ZeroTouchAuthenticator::default(); - let acl = EdhocMessageBuffer::new_from_slice(&[cred_i.kid]).unwrap(); + let single_byte_kid = cred_i.kid.as_ref().unwrap()[0]; // FIXME: add longer kid support in ACL + let acl = EdhocMessageBuffer::new_from_slice(&[single_byte_kid]).unwrap(); let server = ZeroTouchServer::new( W_TV.try_into().unwrap(), CRED_R.try_into().unwrap(), @@ -804,7 +804,7 @@ mod test_authz { let valid_cred_i = if id_cred_i.reference_only() { mock_fetch_cred_i(id_cred_i).unwrap() } else { - id_cred_i + id_cred_i.get_ccs().unwrap() }; let (mut _responder, r_prk_out) = responder.verify_message_3(valid_cred_i).unwrap(); diff --git a/shared/src/cred_new.rs b/shared/src/cred_new.rs index 44052309..452cedc6 100644 --- a/shared/src/cred_new.rs +++ b/shared/src/cred_new.rs @@ -1,8 +1,8 @@ use super::*; -pub type BufferCred = EdhocBuffer<128>; // arbitrary size +pub type BufferCred = EdhocBuffer<192>; // arbitrary size pub type BufferKid = EdhocBuffer<16>; // variable size, up to 16 bytes -pub type BufferIdCred = EdhocBuffer<128>; // variable size, can contain either the contents of a BufferCred or a BufferKid +pub type BufferIdCred = EdhocBuffer<192>; // variable size, can contain either the contents of a BufferCred or a BufferKid pub type BytesKeyAES128 = [u8; 16]; pub type BytesKeyEC2 = [u8; 32]; pub type BytesKeyOKP = [u8; 32]; @@ -44,7 +44,7 @@ impl From for IdCredType { /// /// Possible values include key IDs, credentials by value and others. // TODO: rename to just IdCred -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct IdCred { /// The value is always stored in the ID_CRED_x form as a serialized one-element dictionary; /// while this technically wastes two bytes, it has the convenient property of having the full @@ -118,6 +118,14 @@ impl IdCred { self.bytes.as_slice()[1].into() } + pub fn get_ccs(&self) -> Option { + if self.item_type() == IdCredType::KCCS { + Credential::parse_ccs(&self.bytes.as_slice()[2..]).ok() + } else { + None + } + } + fn bstr_representable_as_int(value: u8) -> bool { (0x0..=0x17).contains(&value) || (0x20..=0x37).contains(&value) } @@ -296,12 +304,7 @@ impl Credential { CBOR_MAJOR_BYTE_STRING | kid.len() as u8, ]) .map_err(|_| EDHOCError::CredentialTooLongError)?; - if kid.len() == 1 { - id_cred.bytes.extend_from_slice(kid.as_slice()).unwrap(); - } else { - // TODO: this should actually just work, but let's leave it as is for testing later - todo!("Larger kid not supported yet"); - } + id_cred.bytes.extend_from_slice(kid.as_slice()).unwrap(); Ok(id_cred) } } diff --git a/shared/src/lib.rs b/shared/src/lib.rs index f938de6e..223a55f9 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -64,7 +64,7 @@ pub const MAC_LENGTH_3: usize = MAC_LENGTH_2; pub const ENCODED_VOUCHER_LEN: usize = 1 + MAC_LENGTH; // 1 byte for the length of the bstr-encoded voucher // maximum supported length of connection identifier for R -pub const MAX_KDF_CONTEXT_LEN: usize = SCALE_FACTOR * 150; +pub const MAX_KDF_CONTEXT_LEN: usize = SCALE_FACTOR * 256; pub const MAX_KDF_LABEL_LEN: usize = 15; // for "KEYSTREAM_2" pub const MAX_BUFFER_LEN: usize = SCALE_FACTOR * 256; pub const CBOR_BYTE_STRING: u8 = 0x58u8; @@ -367,6 +367,7 @@ pub struct ProcessingM2 { pub g_y: BytesP256ElemLen, pub plaintext_2: EdhocMessageBuffer, pub c_r: ConnId, + pub id_cred_r: IdCred, pub ead_2: Option, } @@ -384,6 +385,7 @@ pub struct ProcessingM3 { pub y: BytesP256ElemLen, // ephemeral private key of the responder pub prk_3e2m: BytesHashLen, pub th_3: BytesHashLen, + pub id_cred_i: IdCred, pub plaintext_3: EdhocMessageBuffer, pub ead_3: Option, }