Skip to content

Commit

Permalink
Merge pull request openwsn-berkeley#281 from chrysn-pull-requests/ski…
Browse files Browse the repository at this point in the history
…p-for-ccs

CCS: Process as map rather than CBOR encoded map
  • Loading branch information
geonnave authored May 23, 2024
2 parents 76ae15b + 67e099b commit ba48ed2
Showing 1 changed file with 82 additions and 10 deletions.
92 changes: 82 additions & 10 deletions shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ impl EADItem {
#[derive(Debug, Clone, Copy)]
pub enum IdCred<'a> {
CompactKid(u8),
/// Credential by value. It is required that the credential is a valid deterministic encoding
/// of a CCS.
FullCredential(&'a [u8]),
}

Expand All @@ -507,16 +509,9 @@ impl<'a> IdCred<'a> {
match self {
IdCred::CompactKid(kid) => message.extend_from_slice(&[*kid]),
IdCred::FullCredential(cred) => {
let len =
u8::try_from(cred.len()).map_err(|_| EDHOCError::CredentialTooLongError)?;
let kccs_map_len = 1;
message
.extend_from_slice(&[
CBOR_MAJOR_MAP + kccs_map_len,
KCSS_LABEL,
CBOR_BYTE_STRING,
len,
])
.extend_from_slice(&[CBOR_MAJOR_MAP + kccs_map_len, KCSS_LABEL])
.map_err(|_| EDHOCError::CredentialTooLongError)?;
message.extend_from_slice(cred)
}
Expand Down Expand Up @@ -719,7 +714,7 @@ mod edhoc_parser {
// NOTE: if len of bstr is 1, it is a compact kid and therefore should have been encoded as int
let id_cred_r = if CBOR_MAJOR_MAP == CBORDecoder::type_of(decoder.current()?) {
if decoder.map()? == 1 && decoder.u8()? == KCSS_LABEL {
IdCred::FullCredential(decoder.bytes()?)
IdCred::FullCredential(decoder.any_as_encoded()?)
} else {
return Err(EDHOCError::ParsingError);
}
Expand Down Expand Up @@ -755,7 +750,7 @@ mod edhoc_parser {
// NOTE: if len of bstr is 1, it is a compact kid and therefore should have been encoded as int
let id_cred_i = if CBOR_MAJOR_MAP == CBORDecoder::type_of(decoder.current()?) {
if decoder.map()? == 1 && decoder.u8()? == KCSS_LABEL {
IdCred::FullCredential(decoder.bytes()?)
IdCred::FullCredential(decoder.any_as_encoded()?)
} else {
return Err(EDHOCError::ParsingError);
}
Expand Down Expand Up @@ -993,12 +988,77 @@ mod cbor_decoder {
pub fn is_i8(byte: u8) -> bool {
byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END
}

/// Decode any (supported) CBOR item, but ignore its internal structure and just return the
/// encoded data.
///
/// To have bound memory requirements, this depends on the encoded data to be in
/// deterministic encoding, thus not having any indeterminate length items.
pub fn any_as_encoded(&mut self) -> Result<&'a [u8], CBORError> {
let mut remaining_items = 1;
let start = self.position();

// Instead of `while remaining_items > 0`, this loop helps hax to see that the loop
// terminates. As every loop iteration advances the cursor by at least 1, the iteration
// bound introduced by the for loop will never be reached, and the loop only terminates
// through the remaining_items condition or a failure to read.
//
// I trust (but did not verify) that the Rust compiler can make something sensible out
// of this (especially not keep looping needlessly) and doesn't do anything worse than
// keep a limited loop counter.
for _ in self.buf.iter() {
if remaining_items > 0 {
remaining_items -= 1;
let head = self.read()?;
let major = head >> 5;
let minor = head & 0x1f;
let argument = match minor {
0..=23 => minor,
24 => self.read()?,
// We do not support values outside the range -256..256.
// FIXME: Sooner or later we should. There is probably an upper bound on
// lengths we need to support (we don't need to support 32bit integer decoding
// for map keys when our maximum buffers are 256 long); will split things up
// here into major-0/1/6/7 where we can just skip 1/2/4/8 bytes vs. the other
// majors where this is an out-of-bounds error anyway, or just have up to 64bit
// decoding available consistently for all?
25 | 26 | 27 => return Err(CBORError::DecodingError),
// Reserved, not well-formed
28 | 29 | 30 => return Err(CBORError::DecodingError),
// Indefinite length markers are forbidden in deterministic CBOR (or it's one
// of the major types where this is just not well-formed)
31 => return Err(CBORError::DecodingError),
_ => unreachable!("Value was masked to 5 bits"),
};
match major {
0..=1 => (), // Argument consumed, remaining items were already decremented
7 => (), // Same, but in separate line due to Hax FStar backend limitations
6 => {
remaining_items += 1;
}
2..=3 => {
self.read_slice(argument.into())?;
}
4 => {
remaining_items += argument;
}
5 => {
remaining_items += argument * 2;
}
_ => unreachable!("Value is result of a right shift trimming it to 3 bits"),
}
}
}

Ok(&self.buf[start..self.position()])
}
}
}

#[cfg(test)]
mod test_cbor_decoder {
use super::cbor_decoder::*;
use hexlit::hex;

#[test]
fn test_cbor_decoder() {
Expand All @@ -1011,4 +1071,16 @@ mod test_cbor_decoder {
assert_eq!([0x68, 0x69], decoder.str().unwrap()); // "hi"
assert_eq!([0xFE, 0xFE], decoder.bytes().unwrap());
}

#[test]
fn test_cbor_decoder_any_as_decoded() {
// {"bytes": 'val', "n": 123, "tagged": 255(["a", -1]), "deep": [[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]], {1: {2: {3: {4: [simple(0), true, null, simple(128)]}}}}]}
// Note we can't have floats b/c we don't skip long arguments yet (and all floats have
// minor 25 or longer).
let input = hex!("A46562797465734376616C616E187B66746167676564D8FF82616120646465657082818181818181818181818181818181818181818180A101A102A103A10484E0F5F6F880");
let mut decoder = CBORDecoder::new(&input);

assert_eq!(input, decoder.any_as_encoded().unwrap());
assert!(decoder.finished())
}
}

0 comments on commit ba48ed2

Please sign in to comment.