Skip to content

Commit

Permalink
refactor: use dedicated inlined functions for cbor
Browse files Browse the repository at this point in the history
  • Loading branch information
geonnave committed Sep 15, 2023
1 parent e1a36d0 commit 30b7f87
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 88 deletions.
39 changes: 39 additions & 0 deletions consts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -307,6 +309,43 @@ mod hacspec {
}
}

#[inline(always)]
pub fn is_cbor_uint_1byte(byte: U8) -> bool {
let byte = byte.declassify();
return byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END;
}

#[inline(always)]
pub fn is_cbor_uint_2bytes(byte: U8) -> bool {
return byte.declassify() == CBOR_UINT_1BYTE;
}

#[inline(always)]
pub 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)]
pub 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)]
pub 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)]
pub fn is_cbor_array_1byte_prefix(byte: U8) -> bool {
let byte = byte.declassify();
return byte >= CBOR_MAJOR_ARRAY && byte <= CBOR_MAJOR_ARRAY_MAX;
}

array!(BytesIdCred, ID_CRED_LEN, U8);
array!(BytesSuites, SUITES_LEN, U8);
array!(BytesSupportedSuites, SUPPORTED_SUITES_LEN, U8);
Expand Down
178 changes: 90 additions & 88 deletions hacspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,54 +742,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;
Expand All @@ -812,14 +806,14 @@ fn parse_ead(
let mut ead_item = None::<EADItemHacspec>;
let mut ead_value = None::<EdhocMessageBufferHacspec>;

// 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)
};
Expand Down Expand Up @@ -851,18 +845,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<
Expand All @@ -886,16 +868,15 @@ fn parse_message_1(
let mut ead_1 = None::<EADItemHacspec>;

// 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);

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,
Expand All @@ -904,7 +885,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,
Expand Down Expand Up @@ -1027,7 +1008,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);
Expand Down Expand Up @@ -1160,24 +1141,34 @@ fn decode_plaintext_3(
) -> Result<(U8, BytesMac3, Option<EADItemHacspec>), EDHOCError> {
let mut ead_3 = None::<EADItemHacspec>;
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;
}
Expand Down Expand Up @@ -1397,28 +1388,39 @@ fn decode_plaintext_2(
) -> Result<(U8, U8, BytesMac2, Option<EADItemHacspec>), EDHOCError> {
let mut error = EDHOCError::UnknownError;
let mut ead_2 = None::<EADItemHacspec>;

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;
}
Expand Down

0 comments on commit 30b7f87

Please sign in to comment.