#![allow(dead_code)]
use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter, Value};
use crate::error::{Error, Result};
const EPH_PUB_LEN: usize = 65;
const RANDOM_LEN: usize = 32;
const DEST_ID_LEN: usize = 32;
const RESUMPTION_ID_LEN: usize = 16;
const RESUME_MIC_LEN: usize = 16;
const END_CONTAINER_BYTE: u8 = 0x18;
fn expect_anon_struct_start(reader: &mut TlvReader<'_>) -> Result<()> {
match reader.next()? {
Some(Element::ContainerStart {
tag: Tag::Anonymous,
kind: ContainerKind::Structure,
}) => Ok(()),
_ => Err(Error::InvalidParameter),
}
}
fn collect_structure_body(reader: &mut TlvReader<'_>) -> Result<Value> {
let mut members: Vec<(Tag, Value)> = Vec::new();
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar { tag, value }) => {
members.push((tag, value));
}
Some(Element::ContainerStart { tag, kind }) => {
let inner = match kind {
ContainerKind::Structure => collect_structure_body(reader)?,
ContainerKind::Array => Value::Array(collect_array_body(reader)?),
ContainerKind::List => Value::List(collect_list_body(reader)?),
_ => return Err(Error::InvalidParameter),
};
members.push((tag, inner));
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Value::Structure(members))
}
fn collect_array_body(reader: &mut TlvReader<'_>) -> Result<Vec<Value>> {
let mut elems: Vec<Value> = Vec::new();
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar { value, .. }) => elems.push(value),
Some(Element::ContainerStart { kind, .. }) => {
let inner = match kind {
ContainerKind::Structure => collect_structure_body(reader)?,
ContainerKind::Array => Value::Array(collect_array_body(reader)?),
ContainerKind::List => Value::List(collect_list_body(reader)?),
_ => return Err(Error::InvalidParameter),
};
elems.push(inner);
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(elems)
}
fn collect_list_body(reader: &mut TlvReader<'_>) -> Result<Vec<(Tag, Value)>> {
let mut members: Vec<(Tag, Value)> = Vec::new();
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar { tag, value }) => members.push((tag, value)),
Some(Element::ContainerStart { tag, kind }) => {
let inner = match kind {
ContainerKind::Structure => collect_structure_body(reader)?,
ContainerKind::Array => Value::Array(collect_array_body(reader)?),
ContainerKind::List => Value::List(collect_list_body(reader)?),
_ => return Err(Error::InvalidParameter),
};
members.push((tag, inner));
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(members)
}
fn skip_container_body(reader: &mut TlvReader<'_>) -> Result<()> {
let mut depth: usize = 1;
loop {
match reader.next()? {
None => return Err(Error::InvalidParameter),
Some(Element::ContainerStart { .. }) => depth += 1,
Some(Element::ContainerEnd) => {
depth -= 1;
if depth == 0 {
return Ok(());
}
}
Some(_) => {}
}
}
}
fn skip_unknown_field(reader: &mut TlvReader<'_>, element: Option<&Element>) -> Result<()> {
match element {
None => Err(Error::InvalidParameter),
Some(Element::ContainerStart { .. }) => skip_container_body(reader),
Some(_) => Ok(()),
}
}
fn skip_and_capture_substructure(
full_message: &[u8],
main_reader: &mut TlvReader<'_>,
context_tag: u8,
) -> Result<Vec<u8>> {
let mut depth: usize = 1;
loop {
match main_reader.next()? {
None => return Err(Error::InvalidParameter),
Some(Element::ContainerStart { .. }) => depth += 1,
Some(Element::ContainerEnd) => {
depth -= 1;
if depth == 0 {
break;
}
}
Some(_) => {}
}
}
let mut reader = TlvReader::new(full_message);
match reader.next()? {
Some(Element::ContainerStart {
tag: Tag::Anonymous,
kind: ContainerKind::Structure,
}) => {}
_ => return Err(Error::InvalidParameter),
}
loop {
match reader.next()? {
None | Some(Element::ContainerEnd) => return Err(Error::InvalidParameter),
Some(Element::ContainerStart {
tag: Tag::Context(t),
kind: ContainerKind::Structure,
}) if t == context_tag => {
let child_value = collect_structure_body(&mut reader)?;
let mut raw = Vec::new();
let mut w = TlvWriter::new(&mut raw);
w.write_value(Tag::Context(t), &child_value)?;
return Ok(raw);
}
Some(Element::ContainerStart { .. }) => {
skip_container_body(&mut reader)?;
}
Some(_) => {}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct SessionParams {
pub raw_tlv: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Sigma1 {
pub initiator_random: [u8; RANDOM_LEN],
pub initiator_session_id: u16,
pub dest_id: [u8; DEST_ID_LEN],
pub initiator_eph_pub: [u8; EPH_PUB_LEN],
pub initiator_session_params: Option<SessionParams>,
pub resumption_id: Option<[u8; RESUMPTION_ID_LEN]>,
pub initiator_resume_mic: Option<[u8; RESUME_MIC_LEN]>,
}
impl Sigma1 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::with_capacity(256);
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.initiator_random)?;
w.put_uint(Tag::Context(2), u64::from(self.initiator_session_id))?;
w.put_bytes(Tag::Context(3), &self.dest_id)?;
w.put_bytes(Tag::Context(4), &self.initiator_eph_pub)?;
}
if let Some(sp) = &self.initiator_session_params {
buf.extend_from_slice(&sp.raw_tlv);
}
if let Some(rid) = &self.resumption_id {
let mut w = TlvWriter::new(&mut buf);
w.put_bytes(Tag::Context(6), rid)?;
}
if let Some(mic) = &self.initiator_resume_mic {
let mut w = TlvWriter::new(&mut buf);
w.put_bytes(Tag::Context(7), mic)?;
}
buf.push(END_CONTAINER_BYTE);
Ok(buf)
}
pub(crate) fn decode(bytes: &[u8]) -> Result<Self> {
let mut reader = TlvReader::new(bytes);
expect_anon_struct_start(&mut reader)?;
let mut initiator_random: Option<[u8; RANDOM_LEN]> = None;
let mut initiator_session_id: Option<u16> = None;
let mut dest_id: Option<[u8; DEST_ID_LEN]> = None;
let mut initiator_eph_pub: Option<[u8; EPH_PUB_LEN]> = None;
let mut initiator_session_params: Option<SessionParams> = None;
let mut resumption_id: Option<[u8; RESUMPTION_ID_LEN]> = None;
let mut initiator_resume_mic: Option<[u8; RESUME_MIC_LEN]> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; RANDOM_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
initiator_random = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Uint(v),
}) => {
initiator_session_id =
Some(u16::try_from(v).map_err(|_| Error::InvalidParameter)?);
}
Some(Element::Scalar {
tag: Tag::Context(3),
value: Value::Bytes(b),
}) => {
let arr: [u8; DEST_ID_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
dest_id = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(4),
value: Value::Bytes(b),
}) => {
let arr: [u8; EPH_PUB_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
initiator_eph_pub = Some(arr);
}
Some(Element::ContainerStart {
tag: Tag::Context(5),
kind: ContainerKind::Structure,
}) => {
let raw = skip_and_capture_substructure(bytes, &mut reader, 5)?;
initiator_session_params = Some(SessionParams { raw_tlv: raw });
}
Some(Element::Scalar {
tag: Tag::Context(6),
value: Value::Bytes(b),
}) => {
let arr: [u8; RESUMPTION_ID_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
resumption_id = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(7),
value: Value::Bytes(b),
}) => {
let arr: [u8; RESUME_MIC_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
initiator_resume_mic = Some(arr);
}
other => skip_unknown_field(&mut reader, other.as_ref())?,
}
}
Ok(Self {
initiator_random: initiator_random.ok_or(Error::InvalidParameter)?,
initiator_session_id: initiator_session_id.ok_or(Error::InvalidParameter)?,
dest_id: dest_id.ok_or(Error::InvalidParameter)?,
initiator_eph_pub: initiator_eph_pub.ok_or(Error::InvalidParameter)?,
initiator_session_params,
resumption_id,
initiator_resume_mic,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Sigma2 {
pub responder_random: [u8; RANDOM_LEN],
pub responder_session_id: u16,
pub responder_eph_pub: [u8; EPH_PUB_LEN],
pub encrypted: Vec<u8>,
pub responder_session_params: Option<SessionParams>,
}
impl Sigma2 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::with_capacity(128 + self.encrypted.len());
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.responder_random)?;
w.put_uint(Tag::Context(2), u64::from(self.responder_session_id))?;
w.put_bytes(Tag::Context(3), &self.responder_eph_pub)?;
w.put_bytes(Tag::Context(4), &self.encrypted)?;
}
if let Some(sp) = &self.responder_session_params {
buf.extend_from_slice(&sp.raw_tlv);
}
buf.push(END_CONTAINER_BYTE);
Ok(buf)
}
pub(crate) fn decode(bytes: &[u8]) -> Result<Self> {
let mut reader = TlvReader::new(bytes);
expect_anon_struct_start(&mut reader)?;
let mut responder_random: Option<[u8; RANDOM_LEN]> = None;
let mut responder_session_id: Option<u16> = None;
let mut responder_eph_pub: Option<[u8; EPH_PUB_LEN]> = None;
let mut encrypted: Option<Vec<u8>> = None;
let mut responder_session_params: Option<SessionParams> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; RANDOM_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
responder_random = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Uint(v),
}) => {
responder_session_id =
Some(u16::try_from(v).map_err(|_| Error::InvalidParameter)?);
}
Some(Element::Scalar {
tag: Tag::Context(3),
value: Value::Bytes(b),
}) => {
let arr: [u8; EPH_PUB_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
responder_eph_pub = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(4),
value: Value::Bytes(b),
}) => {
encrypted = Some(b);
}
Some(Element::ContainerStart {
tag: Tag::Context(5),
kind: ContainerKind::Structure,
}) => {
let raw = skip_and_capture_substructure(bytes, &mut reader, 5)?;
responder_session_params = Some(SessionParams { raw_tlv: raw });
}
other => skip_unknown_field(&mut reader, other.as_ref())?,
}
}
Ok(Self {
responder_random: responder_random.ok_or(Error::InvalidParameter)?,
responder_session_id: responder_session_id.ok_or(Error::InvalidParameter)?,
responder_eph_pub: responder_eph_pub.ok_or(Error::InvalidParameter)?,
encrypted: encrypted.ok_or(Error::InvalidParameter)?,
responder_session_params,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Sigma2Resume {
pub resumption_id: [u8; RESUMPTION_ID_LEN],
pub resume_mic: [u8; RESUME_MIC_LEN],
pub responder_session_id: u16,
pub responder_session_params: Option<SessionParams>,
}
impl Sigma2Resume {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::with_capacity(64);
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.resumption_id)?;
w.put_bytes(Tag::Context(2), &self.resume_mic)?;
w.put_uint(Tag::Context(3), u64::from(self.responder_session_id))?;
}
if let Some(sp) = &self.responder_session_params {
buf.extend_from_slice(&sp.raw_tlv);
}
buf.push(END_CONTAINER_BYTE);
Ok(buf)
}
pub(crate) fn decode(bytes: &[u8]) -> Result<Self> {
let mut reader = TlvReader::new(bytes);
expect_anon_struct_start(&mut reader)?;
let mut resumption_id: Option<[u8; RESUMPTION_ID_LEN]> = None;
let mut resume_mic: Option<[u8; RESUME_MIC_LEN]> = None;
let mut responder_session_id: Option<u16> = None;
let mut responder_session_params: Option<SessionParams> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; RESUMPTION_ID_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
resumption_id = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Bytes(b),
}) => {
let arr: [u8; RESUME_MIC_LEN] =
b.try_into().map_err(|_| Error::InvalidParameter)?;
resume_mic = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(3),
value: Value::Uint(v),
}) => {
responder_session_id =
Some(u16::try_from(v).map_err(|_| Error::InvalidParameter)?);
}
Some(Element::ContainerStart {
tag: Tag::Context(4),
kind: ContainerKind::Structure,
}) => {
let raw = skip_and_capture_substructure(bytes, &mut reader, 4)?;
responder_session_params = Some(SessionParams { raw_tlv: raw });
}
other => skip_unknown_field(&mut reader, other.as_ref())?,
}
}
Ok(Self {
resumption_id: resumption_id.ok_or(Error::InvalidParameter)?,
resume_mic: resume_mic.ok_or(Error::InvalidParameter)?,
responder_session_id: responder_session_id.ok_or(Error::InvalidParameter)?,
responder_session_params,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Sigma3 {
pub encrypted: Vec<u8>,
}
impl Sigma3 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::with_capacity(16 + self.encrypted.len());
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.encrypted)?;
w.end_container()?;
Ok(buf)
}
pub(crate) fn decode(bytes: &[u8]) -> Result<Self> {
let mut reader = TlvReader::new(bytes);
expect_anon_struct_start(&mut reader)?;
let mut encrypted: Option<Vec<u8>> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
encrypted = Some(b);
}
other => skip_unknown_field(&mut reader, other.as_ref())?,
}
}
Ok(Self {
encrypted: encrypted.ok_or(Error::InvalidParameter)?,
})
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)] mod tests {
use super::*;
fn minimal_session_params(tag_num: u8) -> SessionParams {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Context(tag_num)).unwrap();
w.put_uint(Tag::Context(1), 500).unwrap();
w.end_container().unwrap();
SessionParams { raw_tlv: buf }
}
#[test]
fn sigma1_roundtrip() {
let msg = Sigma1 {
initiator_random: [0x11; 32],
initiator_session_id: 0x1234,
dest_id: [0x22; 32],
initiator_eph_pub: [0x04; 65],
initiator_session_params: None,
resumption_id: None,
initiator_resume_mic: None,
};
let bytes = msg.encode().unwrap();
let decoded = Sigma1::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma1_with_session_params_roundtrips() {
let msg = Sigma1 {
initiator_random: [0xAA; 32],
initiator_session_id: 0x0001,
dest_id: [0xBB; 32],
initiator_eph_pub: [0x04; 65],
initiator_session_params: Some(minimal_session_params(5)),
resumption_id: None,
initiator_resume_mic: None,
};
let bytes = msg.encode().unwrap();
let decoded = Sigma1::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma1_with_resumption_fields_roundtrips() {
let msg = Sigma1 {
initiator_random: [0x33; 32],
initiator_session_id: 0xFFFF,
dest_id: [0x44; 32],
initiator_eph_pub: [0x04; 65],
initiator_session_params: None,
resumption_id: Some([0x55; 16]),
initiator_resume_mic: Some([0x66; 16]),
};
let bytes = msg.encode().unwrap();
let decoded = Sigma1::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma1_with_all_optional_fields_roundtrips() {
let msg = Sigma1 {
initiator_random: [0x77; 32],
initiator_session_id: 100,
dest_id: [0x88; 32],
initiator_eph_pub: [0x04; 65],
initiator_session_params: Some(minimal_session_params(5)),
resumption_id: Some([0x99; 16]),
initiator_resume_mic: Some([0xAA; 16]),
};
let bytes = msg.encode().unwrap();
let decoded = Sigma1::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma1_rejects_missing_required_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0x11u8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1_u64).unwrap();
w.put_bytes(Tag::Context(4), &[0x04u8; 65]).unwrap();
w.end_container().unwrap();
assert!(matches!(Sigma1::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma1_ignores_unknown_forward_compat_fields() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0x11u8; 32]).unwrap();
w.put_uint(Tag::Context(2), 7_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x22u8; 32]).unwrap();
w.put_bytes(Tag::Context(4), &[0x04u8; 65]).unwrap();
w.put_uint(Tag::Context(8), 42_u64).unwrap(); w.start_structure(Tag::Context(9)).unwrap(); w.put_uint(Tag::Context(0), 1_u64).unwrap();
w.end_container().unwrap();
w.end_container().unwrap();
let decoded = Sigma1::decode(&buf).unwrap();
assert_eq!(decoded.initiator_session_id, 7);
assert_eq!(decoded.initiator_random, [0x11; 32]);
}
#[test]
fn sigma1_rejects_wrong_random_length() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0x11u8; 31]).unwrap(); w.put_uint(Tag::Context(2), 1_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x22u8; 32]).unwrap();
w.put_bytes(Tag::Context(4), &[0x04u8; 65]).unwrap();
w.end_container().unwrap();
assert!(matches!(Sigma1::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma1_rejects_wrong_eph_pub_length() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0x11u8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x22u8; 32]).unwrap();
w.put_bytes(Tag::Context(4), &[0x04u8; 64]).unwrap(); w.end_container().unwrap();
assert!(matches!(Sigma1::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma2_ignores_unknown_forward_compat_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xCCu8; 32]).unwrap();
w.put_uint(Tag::Context(2), 0x5678_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x04u8; 65]).unwrap();
w.put_bytes(Tag::Context(4), &[0xDEu8; 80]).unwrap();
w.put_uint(Tag::Context(9), 42_u64).unwrap(); w.end_container().unwrap();
let decoded = Sigma2::decode(&buf).unwrap();
assert_eq!(decoded.responder_session_id, 0x5678);
}
#[test]
fn sigma2_roundtrip() {
let msg = Sigma2 {
responder_random: [0xCC; 32],
responder_session_id: 0x5678,
responder_eph_pub: [0x04; 65],
encrypted: vec![0xDE; 80], responder_session_params: None,
};
let bytes = msg.encode().unwrap();
let decoded = Sigma2::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma2_rejects_short_eph_pub() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xCCu8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x04u8; 32]).unwrap(); w.put_bytes(Tag::Context(4), &[0xDEu8; 80]).unwrap();
w.end_container().unwrap();
assert!(matches!(Sigma2::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma2_with_session_params_roundtrips() {
let msg = Sigma2 {
responder_random: [0xDD; 32],
responder_session_id: 42,
responder_eph_pub: [0x04; 65],
encrypted: vec![0xEE; 120],
responder_session_params: Some(minimal_session_params(5)),
};
let bytes = msg.encode().unwrap();
let decoded = Sigma2::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma2_rejects_missing_encrypted_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xCCu8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1_u64).unwrap();
w.put_bytes(Tag::Context(3), &[0x04u8; 65]).unwrap();
w.end_container().unwrap();
assert!(matches!(Sigma2::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma2resume_roundtrip() {
let msg = Sigma2Resume {
resumption_id: [0xA1; 16],
resume_mic: [0xB2; 16],
responder_session_id: 0x9900,
responder_session_params: None,
};
let bytes = msg.encode().unwrap();
let decoded = Sigma2Resume::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma2resume_with_session_params_roundtrips() {
let msg = Sigma2Resume {
resumption_id: [0xC3; 16],
resume_mic: [0xD4; 16],
responder_session_id: 0x1234,
responder_session_params: Some(minimal_session_params(4)),
};
let bytes = msg.encode().unwrap();
let decoded = Sigma2Resume::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma2resume_rejects_missing_resumption_id() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(2), &[0xB2u8; 16]).unwrap();
w.put_uint(Tag::Context(3), 0x9900_u64).unwrap();
w.end_container().unwrap();
assert!(matches!(
Sigma2Resume::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn sigma2resume_rejects_missing_resume_mic() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xA1u8; 16]).unwrap();
w.put_uint(Tag::Context(3), 0x9900_u64).unwrap();
w.end_container().unwrap();
assert!(matches!(
Sigma2Resume::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn sigma2resume_rejects_wrong_resumption_id_length() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xA1u8; 15]).unwrap(); w.put_bytes(Tag::Context(2), &[0xB2u8; 16]).unwrap();
w.put_uint(Tag::Context(3), 0x9900_u64).unwrap();
w.end_container().unwrap();
assert!(matches!(
Sigma2Resume::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn sigma2resume_rejects_wrong_resume_mic_length() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xA1u8; 16]).unwrap();
w.put_bytes(Tag::Context(2), &[0xB2u8; 17]).unwrap(); w.put_uint(Tag::Context(3), 0x9900_u64).unwrap();
w.end_container().unwrap();
assert!(matches!(
Sigma2Resume::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn sigma2resume_ignores_unknown_forward_compat_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xA1u8; 16]).unwrap();
w.put_bytes(Tag::Context(2), &[0xB2u8; 16]).unwrap();
w.put_uint(Tag::Context(3), 0x9900_u64).unwrap();
w.put_uint(Tag::Context(5), 42_u64).unwrap(); w.end_container().unwrap();
assert!(
Sigma2Resume::decode(&buf).is_ok(),
"unknown forward-compat field must be ignored"
);
}
#[test]
fn sigma3_roundtrip() {
let msg = Sigma3 {
encrypted: vec![0xFF; 100],
};
let bytes = msg.encode().unwrap();
let decoded = Sigma3::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
#[test]
fn sigma3_rejects_missing_encrypted_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.end_container().unwrap();
assert!(matches!(Sigma3::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn sigma3_ignores_unknown_forward_compat_field() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bytes(Tag::Context(1), &[0xFFu8; 100]).unwrap();
w.put_uint(Tag::Context(2), 99_u64).unwrap(); w.end_container().unwrap();
let decoded = Sigma3::decode(&buf).unwrap();
assert_eq!(decoded.encrypted, vec![0xFF; 100]);
}
#[test]
fn sigma3_empty_encrypted_roundtrips() {
let msg = Sigma3 { encrypted: vec![] };
let bytes = msg.encode().unwrap();
let decoded = Sigma3::decode(&bytes).unwrap();
assert_eq!(decoded, msg);
}
}