Skip to content

Commit

Permalink
Merge pull request #126 from geonnave/improve-credential-handling
Browse files Browse the repository at this point in the history
Refactor: credential handling improvements
  • Loading branch information
malishav authored Nov 4, 2023
2 parents ae2012f + 4ca0fb5 commit 0dd92f5
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 75 deletions.
22 changes: 17 additions & 5 deletions consts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,26 +304,36 @@ mod helpers {
(info, info_len)
}

pub fn parse_cred<'a>(cred: &'a [u8]) -> (BytesP256ElemLen, u8) {
pub fn parse_cred<'a>(cred: &'a [u8]) -> Result<(BytesP256ElemLen, u8), EDHOCError> {
// NOTE: this routine is only guaranteed to work with credentials from lake-traces
const CCS_PREFIX_LEN: usize = 3;
const CNF_AND_COSE_KEY_PREFIX_LEN: usize = 8;
const COSE_KEY_FIRST_ITEMS_LEN: usize = 6;

if cred.len()
< 3 + CCS_PREFIX_LEN
+ 1
+ CNF_AND_COSE_KEY_PREFIX_LEN
+ COSE_KEY_FIRST_ITEMS_LEN
+ P256_ELEM_LEN
{
return Err(EDHOCError::ParsingError);
}

let subject_len = (cred[2] - CBOR_MAJOR_TEXT_STRING) as usize;
let id_cred_offset: usize = CCS_PREFIX_LEN + subject_len + CNF_AND_COSE_KEY_PREFIX_LEN;
let g_a_x_offset: usize = id_cred_offset + COSE_KEY_FIRST_ITEMS_LEN;

(
Ok((
cred[g_a_x_offset..g_a_x_offset + P256_ELEM_LEN]
.try_into()
.expect("Wrong key length"),
cred[id_cred_offset],
)
))
}

pub fn get_id_cred<'a>(cred: &'a [u8]) -> BytesIdCred {
let (_g, kid) = parse_cred(cred);
let (_g, kid) = parse_cred(cred).unwrap();
[0xa1, 0x04, 0x41, kid]
}
}
Expand Down Expand Up @@ -530,7 +540,9 @@ mod test {

#[test]
fn test_parse_cred() {
let (g_a, kid) = parse_cred(CRED_TV);
let res = parse_cred(CRED_TV);
assert!(res.is_ok());
let (g_a, kid) = res.unwrap();
assert_eq!(g_a, G_A_TV);
assert_eq!(kid, ID_CRED_TV[3]);
assert_eq!(get_id_cred(CRED_TV), ID_CRED_TV);
Expand Down
27 changes: 18 additions & 9 deletions ead/edhoc-ead-zeroconf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum EADInitiatorProtocolState {
Start,
WaitEAD2,
Completed, // TODO[ead]: check if it is really ok to consider Completed after processing EAD_2
Error,
}

#[derive(PartialEq, Debug, Copy, Clone)]
Expand Down Expand Up @@ -112,15 +113,23 @@ pub fn i_process_ead_2(
cred_v.len = cred_v_u8.len();
cred_v.content[..cred_v.len].copy_from_slice(cred_v_u8);

let voucher = verify_voucher(&ead_2_value, h_message_1, &cred_v, &state.prk)?;

ead_initiator_set_global_state(EADInitiatorState {
protocol_state: EADInitiatorProtocolState::Completed,
voucher,
..ead_initiator_get_global_state_own()
});

Ok(())
match verify_voucher(&ead_2_value, h_message_1, &cred_v, &state.prk) {
Ok(voucher) => {
ead_initiator_set_global_state(EADInitiatorState {
protocol_state: EADInitiatorProtocolState::Completed,
voucher,
..ead_initiator_get_global_state_own()
});
Ok(())
}
Err(_) => {
ead_initiator_set_global_state(EADInitiatorState {
protocol_state: EADInitiatorProtocolState::Error,
..ead_initiator_get_global_state_own()
});
Err(())
}
}
}

pub fn i_prepare_ead_3() -> Option<EADItem> {
Expand Down
168 changes: 107 additions & 61 deletions lib/src/edhoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ pub fn r_prepare_message_2(
// NOTE: assume EAD_2 is for zeroconf
IdCred::FullCredential(cred_r)
} else {
let (_g_r, kid) = parse_cred(cred_r);
let (_g_r, kid) = parse_cred(cred_r).unwrap(); // FIXME
IdCred::CompactKid(kid)
};

Expand Down Expand Up @@ -296,7 +296,7 @@ pub fn r_process_message_3(
true
};
if ead_success {
let (g_i, kid_i) = parse_cred(cred_i_expected);
let (g_i, kid_i) = parse_cred(cred_i_expected).unwrap(); // FIXME

// compare the kid received with the kid expected in id_cred_i
if kid == kid_i {
Expand Down Expand Up @@ -474,6 +474,8 @@ pub fn i_process_message_2(
let mut error = EDHOCError::UnknownError;
let mut c_r = 0xffu8; // invalidate c_r
let mut kid = 0xffu8; // invalidate kid
let mut g_r: BytesP256ElemLen = Default::default();
let mut cred_r = None;

if current_state == EDHOCState::WaitMessage2 {
let res = parse_message_2(message_2);
Expand All @@ -495,87 +497,131 @@ pub fn i_process_message_2(
let (c_r_2, id_cred, mac_2, ead_2) = plaintext_2_decoded.unwrap();
c_r = c_r_2;

// Step 3: If EAD is present make it available to the application
// TODO: rewrite so that the logic is more clear
let (ead_ok, r_authenticated_via_ead, cred_r) = if let Some(ead_2) = ead_2 {
// if EAD-zeroconf is present, then id_cred must contain a full credential
if let IdCred::FullCredential(full_cred) = id_cred {
let ead_ok = i_process_ead_2(ead_2, full_cred, &h_message_1).is_ok();
// at this point, in case of EAD = zeroconf, if it works it means that:
// - the Voucher has been verified
// - the received cred_r (aka cred_v) has been authenticated
(ead_ok, ead_ok, Some(full_cred))
cred_r = if let Some(cred_r_expected) = cred_r_expected {
// 1. Does ID_CRED_X point to a stored authentication credential? YES
// IMPL: compare cred_r_expected with id_cred
// IMPL: assume cred_r_expected is well formed
let (g_r_expected, kid_expected) = parse_cred(cred_r_expected).unwrap();
g_r = g_r_expected;
let credentials_match = match id_cred {
IdCred::CompactKid(kid) => kid == kid_expected,
IdCred::FullCredential(cred_r_received) => {
cred_r_expected == cred_r_received
}
};

// 2. Is this authentication credential still valid?
// IMPL,TODO: check cred_r_expected is still valid

// Continue by considering CRED_X as the authentication credential of the other peer.
// IMPL: ready to proceed, including process ead_2

if credentials_match {
Some(cred_r_expected)
} else {
(false, false, None)
None
}
} else {
if cred_r_expected.is_some() {
if let IdCred::CompactKid(compact_kid) = id_cred {
kid = compact_kid;
(true, false, cred_r_expected)
} else {
(true, false, None)
// 1. Does ID_CRED_X point to a stored authentication credential? NO
// IMPL: cred_r_expected provided by application is None
// id_cred must be a full credential
if let IdCred::FullCredential(cred_r_received) = id_cred {
// 3. Is the trust model Pre-knowledge-only? NO (hardcoded to NO for now)

// 4. Is the trust model Pre-knowledge + TOFU? YES (hardcoded to YES for now)

// 6. Validate CRED_X. Generally a CCS has to be validated only syntactically and semantically, unlike a certificate or a CWT.
// Is the validation successful?
// IMPL: parse_cred(cred_r) and check it is valid
match parse_cred(cred_r_received) {
Ok((g_r_received, _kid_received)) => {
// 5. Is the authentication credential authorized for use in the context of this EDHOC session?
// IMPL,TODO: we just skip this step for now

// 7. Store CRED_X as valid and trusted.
// Pair it with consistent credential identifiers, for each supported type of credential identifier.
// IMPL: cred_r = id_cred
g_r = g_r_received;
Some(cred_r_received)
}
Err(_) => None,
}
} else {
(true, false, None)
// IMPL: should have gotten a full credential
None
}
};

if ead_ok {
if cred_r.is_some() {
let cred_r = cred_r.unwrap();
let (g_r, kid_r) = parse_cred(cred_r);
// 8. Is this authentication credential good to use in the context of this EDHOC session?
// IMPL,TODO: we just skip this step for now

// IMPL: stop if cred_r is None
if let Some(valid_cred_r) = cred_r {
// Phase 2:
// - Process EAD_X items that have not been processed yet, and that can be processed before message verification
// IMPL: we are sure valid_cred_r is a full credential

// Step 3: If EAD is present make it available to the application
let ead_res = if let Some(ead_2) = ead_2 {
// IMPL: if EAD-zeroconf is present, then id_cred must contain a full credential
// at this point, in case of EAD = zeroconf, if it works it means that:
// - the Voucher has been verified
// - the received valid_cred_r (aka cred_v) has been authenticated
i_process_ead_2(ead_2, valid_cred_r, &h_message_1)
} else {
Ok(())
};

if ead_res.is_ok() {
// verify mac_2
let salt_3e2m = compute_salt_3e2m(&prk_2e, &th_2);

prk_3e2m = compute_prk_3e2m(&salt_3e2m, &x, &g_r);

let expected_mac_2 =
compute_mac_2(&prk_3e2m, &get_id_cred(cred_r), &cred_r, &th_2);
let expected_mac_2 = compute_mac_2(
&prk_3e2m,
&get_id_cred(valid_cred_r),
&valid_cred_r,
&th_2,
);

if mac_2 == expected_mac_2 {
if r_authenticated_via_ead || kid == kid_r {
// step is actually from processing of message_3
// but we do it here to avoid storing plaintext_2 in State
let mut pt2: BufferPlaintext2 = BufferPlaintext2::new();
pt2.content[..plaintext_2_len]
.copy_from_slice(&plaintext_2[..plaintext_2_len]);
pt2.len = plaintext_2_len;
th_3 = compute_th_3(&th_2, &pt2, &cred_r);
// message 3 processing

let salt_4e3m = compute_salt_4e3m(&prk_3e2m, &th_3);

prk_4e3m = compute_prk_4e3m(&salt_4e3m, i, &g_y);

error = EDHOCError::Success;
current_state = EDHOCState::ProcessedMessage2;

state = construct_state(
current_state,
x,
_c_i,
g_y,
prk_3e2m,
prk_4e3m,
_prk_out,
_prk_exporter,
h_message_1,
th_3,
);
} else {
// Unknown peer
error = EDHOCError::UnknownPeer;
}
// step is actually from processing of message_3
// but we do it here to avoid storing plaintext_2 in State
let mut pt2: BufferPlaintext2 = BufferPlaintext2::new();
pt2.content[..plaintext_2_len]
.copy_from_slice(&plaintext_2[..plaintext_2_len]);
pt2.len = plaintext_2_len;
th_3 = compute_th_3(&th_2, &pt2, &valid_cred_r);
// message 3 processing

let salt_4e3m = compute_salt_4e3m(&prk_3e2m, &th_3);

prk_4e3m = compute_prk_4e3m(&salt_4e3m, i, &g_y);

error = EDHOCError::Success;
current_state = EDHOCState::ProcessedMessage2;

state = construct_state(
current_state,
x,
_c_i,
g_y,
prk_3e2m,
prk_4e3m,
_prk_out,
_prk_exporter,
h_message_1,
th_3,
);
} else {
error = EDHOCError::MacVerificationFailed;
}
} else {
error = EDHOCError::UnknownPeer;
error = EDHOCError::EADError;
}
} else {
error = EDHOCError::EADError;
error = EDHOCError::UnknownPeer;
}
} else {
error = EDHOCError::ParsingError;
Expand Down

0 comments on commit 0dd92f5

Please sign in to comment.