#![cfg_attr(not(feature = "python-bindings"), no_std)]
pub use cbor_decoder::*;
pub use edhoc_parser::*;
pub use helpers::*;
use core::num::NonZeroI16;
use defmt_or_log::trace;
mod crypto;
pub use crypto::*;
mod cred;
pub use cred::*;
mod buffer;
pub use buffer::*;
#[cfg(feature = "python-bindings")]
use pyo3::prelude::*;
#[cfg(feature = "python-bindings")]
mod python_bindings;
pub const MAX_MESSAGE_SIZE_LEN: usize = if cfg!(feature = "max_message_size_len_1024") {
1024
} else if cfg!(feature = "max_message_size_len_512") {
512
} else if cfg!(feature = "max_message_size_len_448") {
448
} else if cfg!(feature = "max_message_size_len_384") {
384
} else if cfg!(feature = "max_message_size_len_320") {
320
} else if cfg!(feature = "max_message_size_len_256") {
256
} else {
128 + 64
};
pub const ID_CRED_LEN: usize = 4;
pub const SUITES_LEN: usize = 9;
pub const SUPPORTED_SUITES_LEN: usize = 1;
pub const EDHOC_METHOD: u8 = 3u8; pub const P256_ELEM_LEN: usize = 32;
pub const SHA256_DIGEST_LEN: usize = 32;
pub const AES_CCM_KEY_LEN: usize = 16;
pub const AES_CCM_IV_LEN: usize = 13;
pub const AES_CCM_TAG_LEN: usize = 8;
pub const MAC_LENGTH: usize = 8; pub const MAC_LENGTH_2: usize = MAC_LENGTH;
pub const MAC_LENGTH_3: usize = MAC_LENGTH_2;
pub const ENCODED_VOUCHER_LEN: usize = 1 + MAC_LENGTH;
pub const MAX_KDF_CONTEXT_LEN: usize = if cfg!(feature = "max_kdf_content_len_1024") {
1024
} else if cfg!(feature = "max_kdf_content_len_512") {
512
} else if cfg!(feature = "max_kdf_content_len_448") {
448
} else if cfg!(feature = "max_kdf_content_len_384") {
384
} else if cfg!(feature = "max_kdf_content_len_320") {
320
} else {
256
};
pub const MAX_KDF_LABEL_LEN: usize = 15;
pub const MAX_BUFFER_LEN: usize = if cfg!(feature = "max_buffer_len_1024") {
1024
} else if cfg!(feature = "max_buffer_len_512") {
512
} else if cfg!(feature = "max_buffer_len_448") {
448
} else if cfg!(feature = "max_buffer_len_384") {
384
} else {
256 + 64
};
pub const CBOR_BYTE_STRING: u8 = 0x58u8;
pub const CBOR_TEXT_STRING: u8 = 0x78u8;
pub const CBOR_UINT_1BYTE: u8 = 0x18u8;
pub const CBOR_NEG_INT_1BYTE_START: u8 = 0x20u8;
pub const CBOR_NEG_INT_1BYTE_END: u8 = 0x37u8;
pub const CBOR_UINT_1BYTE_START: u8 = 0x0u8;
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 CBOR_MAJOR_MAP: u8 = 0xA0;
pub const MAX_INFO_LEN: usize = 2 + SHA256_DIGEST_LEN + 1 + MAX_KDF_LABEL_LEN + 1 + MAX_KDF_CONTEXT_LEN + 1;
pub const KCCS_LABEL: u8 = 14;
#[deprecated(note = "Typo for KCCS_LABEL")]
pub const KCSS_LABEL: u8 = KCCS_LABEL;
pub const KID_LABEL: u8 = 4;
pub const ENC_STRUCTURE_LEN: usize = 8 + 5 + SHA256_DIGEST_LEN;
pub const MAX_EAD_SIZE_LEN: usize = 64;
const MAX_CONNID_ENCODED_LEN: usize = if cfg!(feature = "max_connid_encoded_len_24") {
24
} else {
8
};
pub type BytesSuites = [u8; SUITES_LEN];
pub type BytesSupportedSuites = [u8; SUPPORTED_SUITES_LEN];
pub const EDHOC_SUITES: BytesSuites = [0, 1, 2, 3, 4, 5, 6, 24, 25]; pub const EDHOC_SUPPORTED_SUITES: BytesSupportedSuites = [0x2u8];
pub type BytesEad2 = [u8; 0];
pub type BytesIdCred = [u8; ID_CRED_LEN];
pub type Bytes8 = [u8; 8];
pub type BytesCcmKeyLen = [u8; AES_CCM_KEY_LEN];
pub type BytesCcmIvLen = [u8; AES_CCM_IV_LEN];
pub type BufferPlaintext2 = EdhocMessageBuffer;
pub type BufferPlaintext3 = EdhocMessageBuffer;
pub type BufferPlaintext4 = EdhocMessageBuffer;
pub type BytesMac2 = [u8; MAC_LENGTH_2];
pub type BytesMac3 = [u8; MAC_LENGTH_3];
pub type BufferMessage1 = EdhocMessageBuffer;
pub type BufferMessage3 = EdhocMessageBuffer;
pub type BufferMessage4 = EdhocMessageBuffer;
pub type BufferCiphertext2 = EdhocMessageBuffer;
pub type BufferCiphertext3 = EdhocMessageBuffer;
pub type BufferCiphertext4 = EdhocMessageBuffer;
pub type BytesHashLen = [u8; SHA256_DIGEST_LEN];
pub type BytesP256ElemLen = [u8; P256_ELEM_LEN];
pub type BufferMessage2 = EdhocMessageBuffer;
pub type BytesMaxBuffer = [u8; MAX_BUFFER_LEN];
pub type BytesMaxContextBuffer = [u8; MAX_KDF_CONTEXT_LEN];
pub type BytesMaxInfoBuffer = [u8; MAX_INFO_LEN];
pub type BytesMaxLabelBuffeer = [u8; MAX_KDF_LABEL_LEN];
pub type BytesEncStructureLen = [u8; ENC_STRUCTURE_LEN];
pub type BytesMac = [u8; MAC_LENGTH];
pub type BytesEncodedVoucher = [u8; ENCODED_VOUCHER_LEN];
pub type EADMessageBuffer = EdhocMessageBuffer;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Default)]
pub struct ConnId([u8; MAX_CONNID_ENCODED_LEN]);
enum ConnIdType {
SingleByte,
ByteString(u8),
}
impl ConnIdType {
const _IMPL_CONSTRAINTS: () = assert!(
MAX_CONNID_ENCODED_LEN <= 1 + 23,
"Longer connection IDs require more elaborate decoding here"
);
fn classify(byte: u8) -> Option<Self> {
if byte >> 5 <= 1 && byte & 0x1f < 24 {
return Some(ConnIdType::SingleByte);
} else if byte >> 5 == 2 && byte & 0x1f < 24 {
return Some(ConnIdType::ByteString(byte & 0x1f));
}
None
}
fn length(&self) -> usize {
match self {
ConnIdType::SingleByte => 1,
ConnIdType::ByteString(n) => (1 + n).into(),
}
}
}
impl ConnId {
#[deprecated(
note = "This API is only capable of generating a limited sub-set of the supported identifiers."
)]
pub const fn from_int_raw(raw: u8) -> Self {
debug_assert!(raw >> 5 <= 1, "Major type is not an integer");
debug_assert!(raw & 0x1f < 24, "Value is not immediate");
let mut s = [0; MAX_CONNID_ENCODED_LEN];
s[0] = raw;
Self(s)
}
fn classify(&self) -> ConnIdType {
let Some(t) = ConnIdType::classify(self.0[0]) else {
unreachable!("Type invariant requires valid classification")
};
t
}
pub fn from_decoder(decoder: &mut CBORDecoder<'_>) -> Result<Self, CBORError> {
let mut s = [0; MAX_CONNID_ENCODED_LEN];
let len = ConnIdType::classify(decoder.current()?)
.ok_or(CBORError::DecodingError)?
.length();
s[..len].copy_from_slice(decoder.read_slice(len)?);
Ok(Self(s))
}
pub fn as_slice(&self) -> &[u8] {
match self.classify() {
ConnIdType::SingleByte => &self.0[..1],
ConnIdType::ByteString(n) => &self.0[1..1 + usize::from(n)],
}
}
pub fn as_cbor(&self) -> &[u8] {
&self.0[..self.classify().length()]
}
pub fn from_slice(input: &[u8]) -> Option<Self> {
if input.len() > MAX_CONNID_ENCODED_LEN - 1 {
None
} else {
let mut s = [0; MAX_CONNID_ENCODED_LEN];
if input.len() == 1
&& matches!(ConnIdType::classify(input[0]), Some(ConnIdType::SingleByte))
{
s[0] = input[0];
} else {
s[0] = input.len() as u8 | 0x40;
s[1..1 + input.len()].copy_from_slice(input);
}
Some(Self(s))
}
}
}
#[derive(PartialEq, Debug)]
pub enum EDHOCMethod {
StatStat = 3,
}
impl From<EDHOCMethod> for u8 {
fn from(method: EDHOCMethod) -> u8 {
method as u8
}
}
#[derive(PartialEq, Debug)]
pub enum EDHOCSuite {
CipherSuite2 = 2,
}
impl From<EDHOCSuite> for u8 {
fn from(suite: EDHOCSuite) -> u8 {
suite as u8
}
}
#[derive(PartialEq, Debug)]
#[non_exhaustive]
pub enum EDHOCError {
UnexpectedCredential,
MissingIdentity,
IdentityAlreadySet,
MacVerificationFailed,
UnsupportedMethod,
UnsupportedCipherSuite,
ParsingError,
EncodingError,
CredentialTooLongError,
EadLabelTooLongError,
EadTooLongError,
EADUnprocessable,
AccessDenied,
}
impl EDHOCError {
pub fn err_code(&self) -> ErrCode {
use EDHOCError::*;
match self {
UnexpectedCredential => ErrCode::UNSPECIFIED,
MissingIdentity => ErrCode::UNSPECIFIED,
IdentityAlreadySet => ErrCode::UNSPECIFIED,
MacVerificationFailed => ErrCode::UNSPECIFIED,
UnsupportedMethod => ErrCode::UNSPECIFIED,
UnsupportedCipherSuite => ErrCode::WRONG_SELECTED_CIPHER_SUITE,
ParsingError => ErrCode::UNSPECIFIED,
EncodingError => ErrCode::UNSPECIFIED,
CredentialTooLongError => ErrCode::UNSPECIFIED,
EadLabelTooLongError => ErrCode::UNSPECIFIED,
EadTooLongError => ErrCode::UNSPECIFIED,
EADUnprocessable => ErrCode::UNSPECIFIED,
AccessDenied => ErrCode::ACCESS_DENIED,
}
}
}
#[repr(C)]
pub struct ErrCode(pub NonZeroI16);
impl ErrCode {
pub const UNSPECIFIED: Self = ErrCode(match NonZeroI16::new(1) {
Some(v) => v,
_ => unreachable!(),
});
pub const WRONG_SELECTED_CIPHER_SUITE: Self = ErrCode(match NonZeroI16::new(2) {
Some(v) => v,
_ => unreachable!(),
});
pub const UNKNOWN_CREDENTIAL: Self = ErrCode(match NonZeroI16::new(3) {
Some(v) => v,
_ => unreachable!(),
});
pub const ACCESS_DENIED: Self = ErrCode(match NonZeroI16::new(3333) {
Some(v) => v,
_ => unreachable!(),
});
}
#[derive(Debug)]
#[repr(C)]
pub struct InitiatorStart {
pub suites_i: EdhocBuffer<MAX_SUITES_LEN>,
pub method: u8,
pub x: BytesP256ElemLen, pub g_x: BytesP256ElemLen, }
#[derive(Debug)]
pub struct ResponderStart {
pub method: u8,
pub y: BytesP256ElemLen, pub g_y: BytesP256ElemLen, }
#[derive(Default, Debug)]
pub struct ProcessingM1 {
pub y: BytesP256ElemLen,
pub g_y: BytesP256ElemLen,
pub c_i: ConnId,
pub g_x: BytesP256ElemLen, pub h_message_1: BytesHashLen,
}
#[derive(Default, Clone, Debug)]
#[repr(C)]
pub struct WaitM2 {
pub x: BytesP256ElemLen, pub h_message_1: BytesHashLen,
}
#[derive(Default, Debug)]
pub struct WaitM3 {
pub y: BytesP256ElemLen, pub prk_3e2m: BytesHashLen,
pub th_3: BytesHashLen,
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct ProcessingM2 {
pub mac_2: BytesMac2,
pub prk_2e: BytesHashLen,
pub th_2: BytesHashLen,
pub x: BytesP256ElemLen,
pub g_y: BytesP256ElemLen,
pub plaintext_2: EdhocMessageBuffer,
pub c_r: ConnId,
pub id_cred_r: IdCred,
pub ead_2: Option<EADItem>,
}
#[derive(Default, Debug)]
#[repr(C)]
pub struct ProcessedM2 {
pub prk_3e2m: BytesHashLen,
pub prk_4e3m: BytesHashLen,
pub th_3: BytesHashLen,
}
#[derive(Default, Debug)]
pub struct ProcessingM3 {
pub mac_3: BytesMac3,
pub y: BytesP256ElemLen, pub prk_3e2m: BytesHashLen,
pub th_3: BytesHashLen,
pub id_cred_i: IdCred,
pub plaintext_3: EdhocMessageBuffer,
pub ead_3: Option<EADItem>,
}
#[derive(Default, Debug)]
pub struct PreparingM3 {
pub prk_3e2m: BytesHashLen,
pub prk_4e3m: BytesHashLen,
pub th_3: BytesHashLen,
pub mac_3: BytesMac3,
}
#[derive(Default, Debug)]
pub struct ProcessedM3 {
pub prk_4e3m: BytesHashLen,
pub th_4: BytesHashLen,
pub prk_out: BytesHashLen,
pub prk_exporter: BytesHashLen,
}
#[derive(Default, Debug)]
#[repr(C)]
pub struct WaitM4 {
pub prk_4e3m: BytesHashLen,
pub th_4: BytesHashLen,
pub prk_out: BytesHashLen,
pub prk_exporter: BytesHashLen,
}
#[derive(Default, Debug)]
#[repr(C)]
pub struct Completed {
pub prk_out: BytesHashLen,
pub prk_exporter: BytesHashLen,
}
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub enum CredentialTransfer {
ByReference,
ByValue,
}
#[derive(PartialEq, Debug)]
#[repr(C)]
pub enum MessageBufferError {
BufferAlreadyFull,
SliceTooLong,
}
#[repr(C)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct EdhocMessageBuffer {
pub content: [u8; MAX_MESSAGE_SIZE_LEN],
pub len: usize,
}
impl Default for EdhocMessageBuffer {
fn default() -> Self {
EdhocMessageBuffer {
content: [0; MAX_MESSAGE_SIZE_LEN],
len: 0,
}
}
}
impl EdhocMessageBuffer {
pub fn new() -> Self {
EdhocMessageBuffer {
content: [0u8; MAX_MESSAGE_SIZE_LEN],
len: 0,
}
}
pub fn new_from_slice(slice: &[u8]) -> Result<Self, MessageBufferError> {
let mut buffer = Self::new();
if buffer.fill_with_slice(slice).is_ok() {
Ok(buffer)
} else {
Err(MessageBufferError::SliceTooLong)
}
}
pub fn get(self, index: usize) -> Option<u8> {
self.content.get(index).copied()
}
pub fn push(&mut self, item: u8) -> Result<(), MessageBufferError> {
if self.len < self.content.len() {
self.content[self.len] = item;
self.len += 1;
Ok(())
} else {
Err(MessageBufferError::BufferAlreadyFull)
}
}
pub fn get_slice(&self, start: usize, len: usize) -> Option<&[u8]> {
self.content.get(start..start + len)
}
pub fn as_slice(&self) -> &[u8] {
&self.content[0..self.len]
}
pub fn fill_with_slice(&mut self, slice: &[u8]) -> Result<(), MessageBufferError> {
if slice.len() <= self.content.len() {
self.len = slice.len();
self.content[..self.len].copy_from_slice(slice);
Ok(())
} else {
Err(MessageBufferError::SliceTooLong)
}
}
pub fn extend_from_slice(&mut self, slice: &[u8]) -> Result<(), MessageBufferError> {
if self.len + slice.len() <= self.content.len() {
self.content[self.len..self.len + slice.len()].copy_from_slice(slice);
self.len += slice.len();
Ok(())
} else {
Err(MessageBufferError::SliceTooLong)
}
}
pub fn from_hex(hex: &str) -> Self {
let mut buffer = EdhocMessageBuffer::new();
buffer.len = hex.len() / 2;
for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
let chunk_str = core::str::from_utf8(chunk).unwrap();
buffer.content[i] = u8::from_str_radix(chunk_str, 16).unwrap();
}
buffer
}
}
impl TryInto<EdhocMessageBuffer> for &[u8] {
type Error = ();
fn try_into(self) -> Result<EdhocMessageBuffer, Self::Error> {
let mut buffer = [0u8; MAX_MESSAGE_SIZE_LEN];
if self.len() <= buffer.len() {
buffer[..self.len()].copy_from_slice(self);
Ok(EdhocMessageBuffer {
content: buffer,
len: self.len(),
})
} else {
Err(())
}
}
}
#[cfg_attr(feature = "python-bindings", pyclass)]
#[derive(Clone, Debug)]
pub struct EADItem {
pub label: u16,
pub is_critical: bool,
pub value: Option<EdhocMessageBuffer>,
}
impl EADItem {
pub fn new() -> Self {
EADItem {
label: 0,
is_critical: false,
value: None,
}
}
}
mod helpers {
use super::*;
pub fn encode_info(
label: u8,
context: &BytesMaxContextBuffer,
context_len: usize,
length: usize,
) -> (BytesMaxInfoBuffer, usize) {
let mut info: BytesMaxInfoBuffer = [0x00; MAX_INFO_LEN];
info[0] = label;
let mut info_len = if context_len < 24 {
info[1] = context_len as u8 | CBOR_MAJOR_BYTE_STRING;
info[2..2 + context_len].copy_from_slice(&context[..context_len]);
2 + context_len
} else {
info[1] = CBOR_BYTE_STRING;
info[2] = context_len as u8;
info[3..3 + context_len].copy_from_slice(&context[..context_len]);
3 + context_len
};
info_len = if length < 24 {
info[info_len] = length as u8;
info_len + 1
} else {
info[info_len] = CBOR_UINT_1BYTE;
info[info_len + 1] = length as u8;
info_len + 2
};
(info, info_len)
}
}
mod edhoc_parser {
use super::*;
pub fn parse_ead(buffer: &[u8]) -> Result<Option<EADItem>, EDHOCError> {
trace!("Enter parse_ead");
if let Some((&label, tail)) = buffer.split_first() {
let label_res = if CBORDecoder::is_u8(label) {
Ok((label, false))
} else if CBORDecoder::is_i8(label) {
Ok((label - (CBOR_NEG_INT_1BYTE_START - 1), true))
} else {
Err(EDHOCError::ParsingError)
};
if let Ok((label, is_critical)) = label_res {
let ead_value = if tail.len() > 0 {
let mut buffer = EdhocMessageBuffer::new();
buffer.fill_with_slice(tail).unwrap(); buffer.len = tail.len();
Some(buffer)
} else {
None
};
let ead_item = Some(EADItem {
label: label.into(),
is_critical,
value: ead_value,
});
Ok(ead_item)
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn parse_suites_i(
mut decoder: CBORDecoder,
) -> Result<(EdhocBuffer<MAX_SUITES_LEN>, CBORDecoder), EDHOCError> {
trace!("Enter parse_suites_i");
let mut suites_i: EdhocBuffer<MAX_SUITES_LEN> = Default::default();
if let Ok(curr) = decoder.current() {
if CBOR_UINT_1BYTE_START == CBORDecoder::type_of(curr) {
let Ok(_) = suites_i.push(decoder.u8()?) else {
return Err(EDHOCError::ParsingError);
};
Ok((suites_i, decoder))
} else if CBOR_MAJOR_ARRAY == CBORDecoder::type_of(curr)
&& CBORDecoder::info_of(curr) >= 2
{
let received_suites_i_len = decoder.array()?;
if received_suites_i_len <= suites_i.capacity() {
for i in 0..received_suites_i_len {
suites_i.content[i] = decoder.u8()?;
}
suites_i.len = received_suites_i_len;
Ok((suites_i, decoder))
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn parse_message_1(
rcvd_message_1: &BufferMessage1,
) -> Result<
(
u8,
EdhocBuffer<MAX_SUITES_LEN>,
BytesP256ElemLen,
ConnId,
Option<EADItem>,
),
EDHOCError,
> {
trace!("Enter parse_message_1");
let mut decoder = CBORDecoder::new(rcvd_message_1.as_slice());
let method = decoder.u8()?;
if let Ok((suites_i, mut decoder)) = parse_suites_i(decoder) {
let mut g_x: BytesP256ElemLen = [0x00; P256_ELEM_LEN];
g_x.copy_from_slice(decoder.bytes_sized(P256_ELEM_LEN)?);
let c_i = ConnId::from_decoder(&mut decoder)?;
if rcvd_message_1.len > decoder.position() {
let ead_res = parse_ead(decoder.remaining_buffer()?);
if let Ok(ead_1) = ead_res {
Ok((method, suites_i, g_x, c_i, ead_1))
} else {
Err(ead_res.unwrap_err())
}
} else if decoder.finished() {
Ok((method, suites_i, g_x, c_i, None))
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn parse_message_2(
rcvd_message_2: &BufferMessage2,
) -> Result<(BytesP256ElemLen, BufferCiphertext2), EDHOCError> {
trace!("Enter parse_message_2");
let mut ciphertext_2: BufferCiphertext2 = BufferCiphertext2::new();
let mut decoder = CBORDecoder::new(rcvd_message_2.as_slice());
let decoded = decoder.bytes()?;
if decoder.finished() {
if let Some(key) = decoded.get(0..P256_ELEM_LEN) {
let mut g_y: BytesP256ElemLen = [0x00; P256_ELEM_LEN];
g_y.copy_from_slice(key);
if let Some(c2) = decoded.get(P256_ELEM_LEN..) {
if ciphertext_2.fill_with_slice(c2).is_ok() {
Ok((g_y, ciphertext_2))
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn decode_plaintext_2(
plaintext_2: &BufferCiphertext2,
) -> Result<(ConnId, IdCred, BytesMac2, Option<EADItem>), EDHOCError> {
trace!("Enter decode_plaintext_2");
let mut mac_2: BytesMac2 = [0x00; MAC_LENGTH_2];
let mut decoder = CBORDecoder::new(plaintext_2.as_slice());
let c_r = ConnId::from_decoder(&mut decoder)?;
let id_cred_r = IdCred::from_encoded_value(decoder.any_as_encoded()?)?;
mac_2[..].copy_from_slice(decoder.bytes_sized(MAC_LENGTH_2)?);
if plaintext_2.len > decoder.position() {
let ead_res = parse_ead(decoder.remaining_buffer()?);
if let Ok(ead_2) = ead_res {
Ok((c_r, id_cred_r, mac_2, ead_2))
} else {
Err(ead_res.unwrap_err())
}
} else if decoder.finished() {
Ok((c_r, id_cred_r, mac_2, None))
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn decode_plaintext_3(
plaintext_3: &BufferPlaintext3,
) -> Result<(IdCred, BytesMac3, Option<EADItem>), EDHOCError> {
trace!("Enter decode_plaintext_3");
let mut mac_3: BytesMac3 = [0x00; MAC_LENGTH_3];
let mut decoder = CBORDecoder::new(plaintext_3.as_slice());
let id_cred_i = IdCred::from_encoded_value(decoder.any_as_encoded()?)?;
mac_3[..].copy_from_slice(decoder.bytes_sized(MAC_LENGTH_3)?);
if plaintext_3.len > decoder.position() {
let ead_res = parse_ead(decoder.remaining_buffer()?);
if let Ok(ead_3) = ead_res {
Ok((id_cred_i, mac_3, ead_3))
} else {
Err(ead_res.unwrap_err())
}
} else if decoder.finished() {
Ok((id_cred_i, mac_3, None))
} else {
Err(EDHOCError::ParsingError)
}
}
pub fn decode_plaintext_4(
plaintext_4: &BufferPlaintext4,
) -> Result<Option<EADItem>, EDHOCError> {
trace!("Enter decode_plaintext_4");
let decoder = CBORDecoder::new(plaintext_4.as_slice());
if plaintext_4.len > decoder.position() {
let ead_res = parse_ead(decoder.remaining_buffer()?);
if let Ok(ead_4) = ead_res {
Ok(ead_4)
} else {
Err(ead_res.unwrap_err())
}
} else if decoder.finished() {
Ok(None)
} else {
Err(EDHOCError::ParsingError)
}
}
}
mod cbor_decoder {
use super::*;
#[derive(Debug)]
pub enum CBORError {
DecodingError,
}
impl From<CBORError> for EDHOCError {
fn from(error: CBORError) -> Self {
match error {
CBORError::DecodingError => EDHOCError::ParsingError,
}
}
}
#[derive(Debug)]
pub struct CBORDecoder<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> CBORDecoder<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
CBORDecoder { buf: bytes, pos: 0 }
}
fn read(&mut self) -> Result<u8, CBORError> {
if let Some(b) = self.buf.get(self.pos) {
self.pos += 1;
Ok(*b)
} else {
Err(CBORError::DecodingError)
}
}
pub fn read_slice(&mut self, n: usize) -> Result<&'a [u8], CBORError> {
if let Some(b) = self
.pos
.checked_add(n)
.and_then(|end| self.buf.get(self.pos..end))
{
self.pos += n;
Ok(b)
} else {
Err(CBORError::DecodingError)
}
}
pub fn position(&self) -> usize {
self.pos
}
pub fn finished(&self) -> bool {
self.pos == self.buf.len()
}
pub fn ensure_finished(&self) -> Result<(), CBORError> {
if self.finished() {
Ok(())
} else {
Err(CBORError::DecodingError)
}
}
pub fn remaining_buffer(&self) -> Result<&[u8], CBORError> {
if let Some(buffer) = self.buf.get(self.pos..) {
Ok(buffer)
} else {
Err(CBORError::DecodingError)
}
}
pub fn current(&self) -> Result<u8, CBORError> {
if let Some(b) = self.buf.get(self.pos) {
Ok(*b)
} else {
Err(CBORError::DecodingError)
}
}
pub fn u8(&mut self) -> Result<u8, CBORError> {
let n = self.read()?;
if (0..=0x17).contains(&n) {
Ok(n)
} else if 0x18 == n {
self.read()
} else {
Err(CBORError::DecodingError)
}
}
pub fn i8(&mut self) -> Result<i8, CBORError> {
let n = self.read()?;
if (0..=0x17).contains(&n) {
Ok(n as i8)
} else if (0x20..=0x37).contains(&n) {
Ok(-1 - (n - 0x20) as i8)
} else if 0x18 == n {
Ok(self.read()? as i8)
} else if 0x38 == n {
Ok(-1 - (self.read()? - 0x20) as i8)
} else {
Err(CBORError::DecodingError)
}
}
pub fn int_raw(&mut self) -> Result<u8, CBORError> {
let n = self.read()?;
if (0..=0x17).contains(&n) || (0x20..=0x37).contains(&n) {
Ok(n)
} else {
Err(CBORError::DecodingError)
}
}
pub fn str(&mut self) -> Result<&'a [u8], CBORError> {
let b = self.read()?;
if CBOR_MAJOR_TEXT_STRING != Self::type_of(b) || Self::info_of(b) == 31 {
Err(CBORError::DecodingError)
} else {
let n = self.as_usize(Self::info_of(b))?;
self.read_slice(n)
}
}
pub fn bytes(&mut self) -> Result<&'a [u8], CBORError> {
let b = self.read()?;
if CBOR_MAJOR_BYTE_STRING != Self::type_of(b) || Self::info_of(b) == 31 {
Err(CBORError::DecodingError)
} else {
let n = self.as_usize(Self::info_of(b))?;
self.read_slice(n)
}
}
pub fn bytes_sized(&mut self, expected_size: usize) -> Result<&'a [u8], CBORError> {
let res = self.bytes()?;
if res.len() == expected_size {
Ok(res)
} else {
Err(CBORError::DecodingError)
}
}
pub fn array(&mut self) -> Result<usize, CBORError> {
let b = self.read()?;
if CBOR_MAJOR_ARRAY != Self::type_of(b) {
Err(CBORError::DecodingError)
} else {
match Self::info_of(b) {
31 => Err(CBORError::DecodingError), n => Ok(self.as_usize(n)?),
}
}
}
pub fn map(&mut self) -> Result<usize, CBORError> {
let b = self.read()?;
if CBOR_MAJOR_MAP != Self::type_of(b) {
Err(CBORError::DecodingError)
} else {
match Self::info_of(b) {
n if n < 24 => Ok(self.as_usize(n)?),
_ => Err(CBORError::DecodingError), }
}
}
pub fn as_usize(&mut self, b: u8) -> Result<usize, CBORError> {
if (0..=0x17).contains(&b) {
Ok(usize::from(b))
} else if 0x18 == b {
self.read().map(usize::from)
} else {
Err(CBORError::DecodingError)
}
}
pub fn type_of(b: u8) -> u8 {
b & 0b111_00000
}
pub fn info_of(b: u8) -> u8 {
b & 0b000_11111
}
pub fn is_u8(byte: u8) -> bool {
byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END
}
pub fn is_i8(byte: u8) -> bool {
byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END
}
pub fn any_as_encoded(&mut self) -> Result<&'a [u8], CBORError> {
let mut remaining_items = 1;
let start = self.position();
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()?,
25 | 26 | 27 => return Err(CBORError::DecodingError),
28 | 29 | 30 => return Err(CBORError::DecodingError),
31 => return Err(CBORError::DecodingError),
_ => unreachable!("Value was masked to 5 bits"),
};
match major {
0..=1 => (), 7 => (), 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() {
let input = [0x01, 0x20, 0x62, 0x68, 0x69, 0x42, 0xFE, 0xFE];
let mut decoder = CBORDecoder::new(&input);
assert_eq!(1, decoder.u8().unwrap());
assert_eq!(-1, decoder.i8().unwrap());
assert_eq!([0x68, 0x69], decoder.str().unwrap()); assert_eq!([0xFE, 0xFE], decoder.bytes().unwrap());
}
#[test]
fn test_cbor_decoder_any_as_decoded() {
let input = hex!("A46562797465734376616C616E187B66746167676564D8FF82616120646465657082818181818181818181818181818181818181818180A101A102A103A10484E0F5F6F880");
let mut decoder = CBORDecoder::new(&input);
assert_eq!(input, decoder.any_as_encoded().unwrap());
assert!(decoder.finished())
}
}