#![allow(dead_code)]
use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter, Value};
use crate::error::{Error, Result};
const POINT_LEN: usize = 65;
const HASH_LEN: usize = 32;
const RANDOM_LEN: usize = 32;
const SALT_MIN_LEN: usize = 16;
const SALT_MAX_LEN: usize = 32;
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 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(_) => {}
}
}
}
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 => {
let elems = collect_array_body(reader)?;
Value::Array(elems)
}
ContainerKind::List => {
let inner_list = collect_list_body(reader)?;
Value::List(inner_list)
}
_ => 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(_) => {}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct SessionParams {
pub raw_tlv: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PbkdfParamsInner {
pub iterations: u32,
pub salt: Vec<u8>,
}
impl PbkdfParamsInner {
pub(crate) fn encode(&self, outer_tag: Tag) -> Result<Vec<u8>> {
if self.salt.len() < SALT_MIN_LEN || self.salt.len() > SALT_MAX_LEN {
return Err(Error::InvalidParameter);
}
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(outer_tag)?;
w.put_uint(Tag::Context(1), u64::from(self.iterations))?;
w.put_bytes(Tag::Context(2), &self.salt)?;
w.end_container()?;
Ok(buf)
}
fn decode_body(reader: &mut TlvReader<'_>) -> Result<Self> {
let mut iterations: Option<u32> = None;
let mut salt: Option<Vec<u8>> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Uint(v),
}) => {
iterations = Some(u32::try_from(v).map_err(|_| Error::InvalidParameter)?);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Bytes(b),
}) => {
if b.len() < SALT_MIN_LEN || b.len() > SALT_MAX_LEN {
return Err(Error::InvalidParameter);
}
salt = Some(b);
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
iterations: iterations.ok_or(Error::InvalidParameter)?,
salt: salt.ok_or(Error::InvalidParameter)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PbkdfParamRequest {
pub initiator_random: [u8; RANDOM_LEN],
pub initiator_session_id: u16,
pub passcode_id: u16,
pub has_pbkdf_parameters: bool,
pub initiator_session_params: Option<SessionParams>,
}
impl PbkdfParamRequest {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
{
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_uint(Tag::Context(3), u64::from(self.passcode_id))?;
w.put_bool(Tag::Context(4), self.has_pbkdf_parameters)?;
}
if let Some(sp) = &self.initiator_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 initiator_random: Option<[u8; RANDOM_LEN]> = None;
let mut initiator_session_id: Option<u16> = None;
let mut passcode_id: Option<u16> = None;
let mut has_pbkdf_parameters: Option<bool> = None;
let mut initiator_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)?;
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::Uint(v),
}) => {
passcode_id = Some(u16::try_from(v).map_err(|_| Error::InvalidParameter)?);
}
Some(Element::Scalar {
tag: Tag::Context(4),
value: Value::Bool(b),
}) => {
has_pbkdf_parameters = Some(b);
}
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 });
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
initiator_random: initiator_random.ok_or(Error::InvalidParameter)?,
initiator_session_id: initiator_session_id.ok_or(Error::InvalidParameter)?,
passcode_id: passcode_id.ok_or(Error::InvalidParameter)?,
has_pbkdf_parameters: has_pbkdf_parameters.ok_or(Error::InvalidParameter)?,
initiator_session_params,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PbkdfParamResponse {
pub initiator_random: [u8; RANDOM_LEN],
pub responder_random: [u8; RANDOM_LEN],
pub responder_session_id: u16,
pub pbkdf_parameters: Option<PbkdfParamsInner>,
pub responder_session_params: Option<SessionParams>,
}
impl PbkdfParamResponse {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.initiator_random)?;
w.put_bytes(Tag::Context(2), &self.responder_random)?;
w.put_uint(Tag::Context(3), u64::from(self.responder_session_id))?;
}
if let Some(pbkdf) = &self.pbkdf_parameters {
let inner_bytes = pbkdf.encode(Tag::Context(4))?;
buf.extend_from_slice(&inner_bytes);
}
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 initiator_random: Option<[u8; RANDOM_LEN]> = None;
let mut responder_random: Option<[u8; RANDOM_LEN]> = None;
let mut responder_session_id: Option<u16> = None;
let mut pbkdf_parameters: Option<PbkdfParamsInner> = 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)?;
initiator_random = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(2),
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(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 inner = PbkdfParamsInner::decode_body(&mut reader)?;
pbkdf_parameters = Some(inner);
}
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 });
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
initiator_random: initiator_random.ok_or(Error::InvalidParameter)?,
responder_random: responder_random.ok_or(Error::InvalidParameter)?,
responder_session_id: responder_session_id.ok_or(Error::InvalidParameter)?,
pbkdf_parameters,
responder_session_params,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Pake1 {
pub x: [u8; POINT_LEN],
}
impl Pake1 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.x)?;
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 x: Option<[u8; POINT_LEN]> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; POINT_LEN] = b.try_into().map_err(|_| Error::InvalidParameter)?;
x = Some(arr);
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
x: x.ok_or(Error::InvalidParameter)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Pake2 {
pub y: [u8; POINT_LEN],
pub verifier: [u8; HASH_LEN],
}
impl Pake2 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.y)?;
w.put_bytes(Tag::Context(2), &self.verifier)?;
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 y: Option<[u8; POINT_LEN]> = None;
let mut verifier: Option<[u8; HASH_LEN]> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; POINT_LEN] = b.try_into().map_err(|_| Error::InvalidParameter)?;
y = Some(arr);
}
Some(Element::Scalar {
tag: Tag::Context(2),
value: Value::Bytes(b),
}) => {
let arr: [u8; HASH_LEN] = b.try_into().map_err(|_| Error::InvalidParameter)?;
verifier = Some(arr);
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
y: y.ok_or(Error::InvalidParameter)?,
verifier: verifier.ok_or(Error::InvalidParameter)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Pake3 {
pub verifier: [u8; HASH_LEN],
}
impl Pake3 {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.put_bytes(Tag::Context(1), &self.verifier)?;
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 verifier: Option<[u8; HASH_LEN]> = None;
loop {
match reader.next()? {
Some(Element::ContainerEnd) => break,
Some(Element::Scalar {
tag: Tag::Context(1),
value: Value::Bytes(b),
}) => {
let arr: [u8; HASH_LEN] = b.try_into().map_err(|_| Error::InvalidParameter)?;
verifier = Some(arr);
}
None | Some(_) => return Err(Error::InvalidParameter),
}
}
Ok(Self {
verifier: verifier.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 pbkdf_param_request_roundtrip_no_session_params() {
let req = PbkdfParamRequest {
initiator_random: [0x42; 32],
initiator_session_id: 0x1234,
passcode_id: 0,
has_pbkdf_parameters: false,
initiator_session_params: None,
};
let bytes = req.encode().unwrap();
let decoded = PbkdfParamRequest::decode(&bytes).unwrap();
assert_eq!(decoded, req);
}
#[test]
fn pbkdf_param_request_has_pbkdf_parameters_true_roundtrips() {
let req = PbkdfParamRequest {
initiator_random: [0xAB; 32],
initiator_session_id: 0xFFFF,
passcode_id: 0x8000,
has_pbkdf_parameters: true,
initiator_session_params: None,
};
let bytes = req.encode().unwrap();
let decoded = PbkdfParamRequest::decode(&bytes).unwrap();
assert_eq!(decoded, req);
}
#[test]
fn pbkdf_param_request_with_session_params_roundtrips() {
let req = PbkdfParamRequest {
initiator_random: [0x11; 32],
initiator_session_id: 0x0001,
passcode_id: 0,
has_pbkdf_parameters: false,
initiator_session_params: Some(minimal_session_params(5)),
};
let bytes = req.encode().unwrap();
let decoded = PbkdfParamRequest::decode(&bytes).unwrap();
assert_eq!(decoded, req);
}
#[test]
fn pbkdf_param_request_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), &[0u8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1).unwrap();
w.put_uint(Tag::Context(3), 0).unwrap();
w.end_container().unwrap();
assert!(matches!(
PbkdfParamRequest::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn pbkdf_param_request_rejects_extra_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), &[0u8; 32]).unwrap();
w.put_uint(Tag::Context(2), 1).unwrap();
w.put_uint(Tag::Context(3), 0).unwrap();
w.put_bool(Tag::Context(4), false).unwrap();
w.put_uint(Tag::Context(6), 99).unwrap(); w.end_container().unwrap();
assert!(matches!(
PbkdfParamRequest::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn pbkdf_param_request_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), &[0u8; 31]).unwrap(); w.put_uint(Tag::Context(2), 1).unwrap();
w.put_uint(Tag::Context(3), 0).unwrap();
w.put_bool(Tag::Context(4), false).unwrap();
w.end_container().unwrap();
assert!(matches!(
PbkdfParamRequest::decode(&buf),
Err(Error::InvalidParameter)
));
}
#[test]
fn pbkdf_param_response_with_pbkdf_params_roundtrips() {
let resp = PbkdfParamResponse {
initiator_random: [0x11; 32],
responder_random: [0x22; 32],
responder_session_id: 0xABCD,
pbkdf_parameters: Some(PbkdfParamsInner {
iterations: 1000,
salt: vec![0x55; 16],
}),
responder_session_params: None,
};
let bytes = resp.encode().unwrap();
let decoded = PbkdfParamResponse::decode(&bytes).unwrap();
assert_eq!(decoded, resp);
}
#[test]
fn pbkdf_param_response_without_pbkdf_params_roundtrips() {
let resp = PbkdfParamResponse {
initiator_random: [0xAA; 32],
responder_random: [0xBB; 32],
responder_session_id: 0x0001,
pbkdf_parameters: None,
responder_session_params: None,
};
let bytes = resp.encode().unwrap();
let decoded = PbkdfParamResponse::decode(&bytes).unwrap();
assert_eq!(decoded, resp);
}
#[test]
fn pbkdf_param_response_with_session_params_roundtrips() {
let resp = PbkdfParamResponse {
initiator_random: [0x33; 32],
responder_random: [0x44; 32],
responder_session_id: 7,
pbkdf_parameters: Some(PbkdfParamsInner {
iterations: 2000,
salt: vec![0xFF; 32],
}),
responder_session_params: Some(minimal_session_params(5)),
};
let bytes = resp.encode().unwrap();
let decoded = PbkdfParamResponse::decode(&bytes).unwrap();
assert_eq!(decoded, resp);
}
#[test]
fn pbkdf_params_inner_rejects_short_salt_on_encode() {
let inner = PbkdfParamsInner {
iterations: 1000,
salt: vec![0u8; 15], };
assert!(matches!(
inner.encode(Tag::Context(4)),
Err(Error::InvalidParameter)
));
}
#[test]
fn pbkdf_params_inner_rejects_long_salt_on_encode() {
let inner = PbkdfParamsInner {
iterations: 1000,
salt: vec![0u8; 33], };
assert!(matches!(
inner.encode(Tag::Context(4)),
Err(Error::InvalidParameter)
));
}
#[test]
fn pake1_roundtrip() {
let p = Pake1 { x: [0x04; 65] };
let bytes = p.encode().unwrap();
let decoded = Pake1::decode(&bytes).unwrap();
assert_eq!(decoded, p);
}
#[test]
fn pake1_rejects_wrong_x_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), &[0x04u8; 64]).unwrap();
w.end_container().unwrap();
assert!(matches!(Pake1::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn pake1_rejects_extra_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), &[0x04u8; 65]).unwrap();
w.put_uint(Tag::Context(2), 0).unwrap(); w.end_container().unwrap();
assert!(matches!(Pake1::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn pake2_roundtrip() {
let p = Pake2 {
y: [0x04; 65],
verifier: [0xCC; 32],
};
let bytes = p.encode().unwrap();
let decoded = Pake2::decode(&bytes).unwrap();
assert_eq!(decoded, p);
}
#[test]
fn pake2_rejects_wrong_verifier_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), &[0x04u8; 65]).unwrap();
w.put_bytes(Tag::Context(2), &[0xCCu8; 31]).unwrap(); w.end_container().unwrap();
assert!(matches!(Pake2::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn pake3_roundtrip() {
let p = Pake3 {
verifier: [0xDE; 32],
};
let bytes = p.encode().unwrap();
let decoded = Pake3::decode(&bytes).unwrap();
assert_eq!(decoded, p);
}
#[test]
fn pake3_rejects_missing_verifier() {
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.end_container().unwrap();
assert!(matches!(Pake3::decode(&buf), Err(Error::InvalidParameter)));
}
#[test]
fn pake3_rejects_extra_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), &[0xDEu8; 32]).unwrap();
w.put_uint(Tag::Context(2), 42).unwrap(); w.end_container().unwrap();
assert!(matches!(Pake3::decode(&buf), Err(Error::InvalidParameter)));
}
}