From 1413ac6b0b19a191df40c06011e36c66397d5b3f Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Fri, 15 Sep 2023 17:24:53 +0200 Subject: [PATCH] refactor: use dedicated inlined functions for cbor --- consts/src/lib.rs | 2 + hacspec/src/lib.rs | 218 +++++++++++++++++++++++++++------------------ 2 files changed, 132 insertions(+), 88 deletions(-) diff --git a/consts/src/lib.rs b/consts/src/lib.rs index 872f2a61..fb48fc74 100644 --- a/consts/src/lib.rs +++ b/consts/src/lib.rs @@ -137,7 +137,9 @@ mod common { pub const CBOR_UINT_1BYTE_END: u8 = 0x17u8; pub const CBOR_MAJOR_TEXT_STRING: u8 = 0x60u8; pub const CBOR_MAJOR_BYTE_STRING: u8 = 0x40u8; + pub const CBOR_MAJOR_BYTE_STRING_MAX: u8 = 0x57u8; pub const CBOR_MAJOR_ARRAY: u8 = 0x80u8; + pub const CBOR_MAJOR_ARRAY_MAX: u8 = 0x97u8; pub const MAX_INFO_LEN: usize = 2 + SHA256_DIGEST_LEN + // 32-byte digest as bstr 1 + MAX_KDF_LABEL_LEN + // label <24 bytes as tstr 1 + MAX_KDF_CONTEXT_LEN + // context <24 bytes as bstr diff --git a/hacspec/src/lib.rs b/hacspec/src/lib.rs index 6ccb8d16..a543d314 100644 --- a/hacspec/src/lib.rs +++ b/hacspec/src/lib.rs @@ -733,6 +733,46 @@ pub fn construct_state( ) } +/// the unsigned integer is encoded as a single byte +#[inline(always)] +fn is_cbor_uint_1byte(byte: U8) -> bool { + let byte = byte.declassify(); + return byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END; +} + +/// the unsigned integer is encoded as two bytes +#[inline(always)] +fn is_cbor_uint_2bytes(byte: U8) -> bool { + return byte.declassify() == CBOR_UINT_1BYTE; +} + +/// the negative integer is encoded as a single byte +#[inline(always)] +fn is_cbor_neg_int_1byte(byte: U8) -> bool { + let byte = byte.declassify(); + return byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END; +} + +/// a single byte signals both bstr type and content length +#[inline(always)] +fn is_cbor_bstr_1byte_prefix(byte: U8) -> bool { + let byte = byte.declassify(); + return byte >= CBOR_MAJOR_BYTE_STRING && byte <= CBOR_MAJOR_BYTE_STRING_MAX; +} + +/// two bytes are used to signal bstr type and content length +#[inline(always)] +fn is_cbor_bstr_2bytes_prefix(byte: U8) -> bool { + return byte.declassify() == CBOR_BYTE_STRING; +} + +/// a single byte signals both array type and content length +#[inline(always)] +fn is_cbor_array_1byte_prefix(byte: U8) -> bool { + let byte = byte.declassify(); + return byte >= CBOR_MAJOR_ARRAY && byte <= CBOR_MAJOR_ARRAY_MAX; +} + fn parse_suites_i( rcvd_message_1: &BufferMessage1, ) -> Result<(BytesSuites, usize, usize), EDHOCError> { @@ -742,54 +782,48 @@ fn parse_suites_i( let mut suites_i_len: usize = 0; // match based on first byte of SUITES_I, which can be either an int or an array - let suites_i_first = (rcvd_message_1.content[1] as U8).declassify(); - if suites_i_first >= 0x00 && suites_i_first <= 0x17 { + if is_cbor_uint_1byte(rcvd_message_1.content[1]) { // CBOR unsigned integer (0..=23) suites_i[0] = rcvd_message_1.content[1]; suites_i_len = 1; raw_suites_len = 1; error = EDHOCError::Success; - } else if suites_i_first == 0x18 { + } else if is_cbor_uint_2bytes(rcvd_message_1.content[1]) { // CBOR unsigned integer (one-byte uint8_t follows) suites_i[0] = rcvd_message_1.content[2]; suites_i_len = 1; raw_suites_len = 2; error = EDHOCError::Success; - } else if suites_i_first >= 0x80 && suites_i_first <= 0x97 { + } else if is_cbor_array_1byte_prefix(rcvd_message_1.content[1]) { // CBOR array (0..=23 data items follow) // the CBOR array length is encoded in the first byte, so we extract it let suites_len: U8 = rcvd_message_1.content[1] - U8(CBOR_MAJOR_ARRAY); let suites_len: usize = suites_len.declassify().into(); - // check surplus array encoding of ciphersuite - if suites_len > 1 { - raw_suites_len = 1; // account for the CBOR_MAJOR_ARRAY byte - if suites_len <= EDHOC_SUITES.len() { - let mut error_occurred = false; - for j in 0..suites_len { - raw_suites_len += 1; - if !error_occurred { - // parse based on cipher suite identifier - let cs_id = (rcvd_message_1.content[raw_suites_len] as U8).declassify(); - if cs_id >= 0x00 && cs_id <= 0x17 { - // CBOR unsigned integer (0..23) - suites_i[j] = rcvd_message_1.content[raw_suites_len]; - suites_i_len += 1; - } else if cs_id == 0x18 { - // CBOR unsigned integer (one-byte uint8_t follows) - raw_suites_len += 1; // account for the 0x18 tag byte - suites_i[j] = rcvd_message_1.content[raw_suites_len]; - suites_i_len += 1; - } else { - error = EDHOCError::ParsingError; - error_occurred = true; - } - } - } + raw_suites_len = 1; // account for the CBOR_MAJOR_ARRAY byte + if suites_len > 1 && suites_len <= EDHOC_SUITES.len() { + // cipher suite array must be at least 2 elements long, but not longer than the defined cipher suites + let mut error_occurred = false; + for j in 0..suites_len { + raw_suites_len += 1; if !error_occurred { - error = EDHOCError::Success; + // parse based on cipher suite identifier + if is_cbor_uint_1byte(rcvd_message_1.content[raw_suites_len]) { + // CBOR unsigned integer (0..23) + suites_i[j] = rcvd_message_1.content[raw_suites_len]; + suites_i_len += 1; + } else if is_cbor_uint_2bytes(rcvd_message_1.content[raw_suites_len]) { + // CBOR unsigned integer (one-byte uint8_t follows) + raw_suites_len += 1; // account for the 0x18 tag byte + suites_i[j] = rcvd_message_1.content[raw_suites_len]; + suites_i_len += 1; + } else { + error = EDHOCError::ParsingError; + error_occurred = true; + } } - } else { - error = EDHOCError::ParsingError; + } + if !error_occurred { + error = EDHOCError::Success; } } else { error = EDHOCError::ParsingError; @@ -812,14 +846,14 @@ fn parse_ead( let mut ead_item = None::; let mut ead_value = None::; - // assume label is either a single byte integer (negative or positive) - let label = message.content[offset].declassify(); - let res_label = if label >= 0x00 && label <= 0x17 { + // assume label is a single byte integer (negative or positive) + let label = message.content[offset]; + let res_label = if is_cbor_uint_1byte(label) { // CBOR unsigned integer (0..=23) - Ok((label as u8, false)) - } else if label >= 0x20 && label <= 0x37 { + Ok((label.declassify() as u8, false)) + } else if is_cbor_neg_int_1byte(label) { // CBOR negative integer (-1..=-24) - Ok((label - (CBOR_NEG_INT_1BYTE_START - 1), true)) + Ok((label.declassify() - (CBOR_NEG_INT_1BYTE_START - 1), true)) } else { Err(EDHOCError::ParsingError) }; @@ -851,18 +885,6 @@ fn parse_ead( } } -#[inline(always)] -fn is_cbor_uint8(byte: U8) -> bool { - let byte = byte.declassify(); - return byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END; -} - -#[inline(always)] -fn is_cbor_int8(byte: U8) -> bool { - let byte = byte.declassify(); - return byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END; -} - fn parse_message_1( rcvd_message_1: &BufferMessage1, ) -> Result< @@ -886,7 +908,7 @@ fn parse_message_1( let mut ead_1 = None::; // first element of CBOR sequence must be an integer - if is_cbor_uint8(rcvd_message_1.content[0]) { + if is_cbor_uint_1byte(rcvd_message_1.content[0]) { method = rcvd_message_1.content[0]; let res_suites = parse_suites_i(rcvd_message_1); @@ -894,8 +916,7 @@ fn parse_message_1( if res_suites.is_ok() { (suites_i, suites_i_len, raw_suites_len) = res_suites.unwrap(); - let g_x_type = rcvd_message_1.content[1 + raw_suites_len].declassify(); - if g_x_type == CBOR_BYTE_STRING { + if is_cbor_bstr_2bytes_prefix(rcvd_message_1.content[1 + raw_suites_len]) { g_x = BytesP256ElemLen::from_slice( &rcvd_message_1.content, 3 + raw_suites_len, @@ -904,7 +925,7 @@ fn parse_message_1( c_i = rcvd_message_1.content[3 + raw_suites_len + P256_ELEM_LEN]; // check that c_i is encoded as single-byte int (we still do not support bstr encoding) - if is_cbor_int8(c_i) || is_cbor_uint8(c_i) { + if is_cbor_neg_int_1byte(c_i) || is_cbor_uint_1byte(c_i) { // if there is still more to parse, the rest will be the EAD_1 if rcvd_message_1.len > (4 + raw_suites_len + P256_ELEM_LEN) { // NOTE: since the current implementation only supports one EAD handler, @@ -1027,7 +1048,7 @@ fn parse_message_2( let mut ciphertext_2: BufferCiphertext2 = BufferCiphertext2::new(); // ensure the whole message is a single CBOR sequence - if (rcvd_message_2.content[0] as U8).declassify() == CBOR_BYTE_STRING + if is_cbor_bstr_2bytes_prefix(rcvd_message_2.content[0]) && (rcvd_message_2.content[1] as U8).declassify() == (rcvd_message_2.len as u8 - 2) { g_y = BytesP256ElemLen::from_slice(&rcvd_message_2.content, 2, P256_ELEM_LEN); @@ -1160,24 +1181,34 @@ fn decode_plaintext_3( ) -> Result<(U8, BytesMac3, Option), EDHOCError> { let mut ead_3 = None::; let mut error = EDHOCError::UnknownError; - - let kid = plaintext_3.content[0usize]; + let mut kid = U8(0xff); // skip the CBOR magic byte as we know how long the MAC is - let mac_3 = BytesMac3::from_slice(&plaintext_3.content, 2, MAC_LENGTH_3); - - // if there is still more to parse, the rest will be the EAD_3 - if plaintext_3.len > (2 + MAC_LENGTH_3) { - // NOTE: since the current implementation only supports one EAD handler, - // we assume only one EAD item - let ead_res = parse_ead(plaintext_3, 2 + MAC_LENGTH_3); - if ead_res.is_ok() { - ead_3 = ead_res.unwrap(); + let mut mac_3 = BytesMac3::new(); + + // check ID_CRED_I and MAC_3 + if (is_cbor_neg_int_1byte(plaintext_3.content[0]) || is_cbor_uint_1byte(plaintext_3.content[0])) + && (is_cbor_bstr_1byte_prefix(plaintext_3.content[1])) + { + kid = plaintext_3.content[0]; + // skip the CBOR magic byte as we know how long the MAC is + mac_3 = BytesMac3::from_slice(&plaintext_3.content, 2, MAC_LENGTH_3); + + // if there is still more to parse, the rest will be the EAD_3 + if plaintext_3.len > (2 + MAC_LENGTH_3) { + // NOTE: since the current implementation only supports one EAD handler, + // we assume only one EAD item + let ead_res = parse_ead(plaintext_3, 2 + MAC_LENGTH_3); + if ead_res.is_ok() { + ead_3 = ead_res.unwrap(); + error = EDHOCError::Success; + } else { + error = ead_res.unwrap_err(); + } + } else if plaintext_3.len == (2 + MAC_LENGTH_3) { error = EDHOCError::Success; } else { - error = ead_res.unwrap_err(); + error = EDHOCError::ParsingError; } - } else if plaintext_3.len == (2 + MAC_LENGTH_3) { - error = EDHOCError::Success; } else { error = EDHOCError::ParsingError; } @@ -1397,28 +1428,39 @@ fn decode_plaintext_2( ) -> Result<(U8, U8, BytesMac2, Option), EDHOCError> { let mut error = EDHOCError::UnknownError; let mut ead_2 = None::; - - let c_r = plaintext_2[0]; - let id_cred_r = plaintext_2[1]; - // NOTE: skipping cbor byte string byte as we know how long the string is - let mac_2 = BytesMac2::from_slice(plaintext_2, 3, MAC_LENGTH_2); - - // if there is still more to parse, the rest will be the EAD_2 - if plaintext_2_len > (3 + MAC_LENGTH_2) { - // NOTE: since the current implementation only supports one EAD handler, - // we assume only one EAD item - let ead_res = parse_ead( - &EdhocMessageBufferHacspec::from_slice(plaintext_2, 0, plaintext_2_len), - 3 + MAC_LENGTH_2, - ); - if ead_res.is_ok() { - ead_2 = ead_res.unwrap(); + let mut c_r: U8 = U8(0xff); + let mut id_cred_r: U8 = U8(0xff); + let mut mac_2 = BytesMac2::new(); + + if (is_cbor_neg_int_1byte(plaintext_2[0]) || is_cbor_uint_1byte(plaintext_2[0])) + && (is_cbor_neg_int_1byte(plaintext_2[1]) || is_cbor_uint_1byte(plaintext_2[1])) + && (is_cbor_bstr_1byte_prefix(plaintext_2[2])) + // TODO: check mac length as well + { + c_r = plaintext_2[0]; + id_cred_r = plaintext_2[1]; + // NOTE: skipping cbor byte string byte as we know how long the string is + mac_2 = BytesMac2::from_slice(plaintext_2, 3, MAC_LENGTH_2); + + // if there is still more to parse, the rest will be the EAD_2 + if plaintext_2_len > (3 + MAC_LENGTH_2) { + // NOTE: since the current implementation only supports one EAD handler, + // we assume only one EAD item + let ead_res = parse_ead( + &EdhocMessageBufferHacspec::from_slice(plaintext_2, 0, plaintext_2_len), + 3 + MAC_LENGTH_2, + ); + if ead_res.is_ok() { + ead_2 = ead_res.unwrap(); + error = EDHOCError::Success; + } else { + error = ead_res.unwrap_err(); + } + } else if plaintext_2_len == (3 + MAC_LENGTH_2) { error = EDHOCError::Success; } else { - error = ead_res.unwrap_err(); + error = EDHOCError::ParsingError; } - } else if plaintext_2_len == (3 + MAC_LENGTH_2) { - error = EDHOCError::Success; } else { error = EDHOCError::ParsingError; }