use serde_indexed::{DeserializeIndexed, SerializeIndexed};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::Vec;
pub const MAX_MIN_PIN_LENGTH_RP_IDS: usize = 4;
pub const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
pub const MAX_RP_ID_LENGTH: usize = 253;
pub const MAX_SUBCOMMAND_PARAMS_CBOR_LEN: usize = 1100;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
#[repr(u8)]
pub enum Subcommand {
EnableEnterpriseAttestation = 0x01,
ToggleAlwaysUv = 0x02,
SetMinPINLength = 0x03,
EnableLongTouchForReset = 0x04,
VendorPrototype = 0xff,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
#[non_exhaustive]
#[serde_indexed(offset = 1)]
pub struct SubcommandParameters<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub new_min_pin_length: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_pin_length_rp_ids: Option<Vec<&'a str, MAX_MIN_PIN_LENGTH_RP_IDS>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub force_change_pin: Option<bool>,
}
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
#[non_exhaustive]
#[serde_indexed(offset = 1)]
pub struct Request<'a> {
pub sub_command: Subcommand,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_command_params: Option<SubcommandParameters<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pin_protocol: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pin_auth: Option<&'a serde_bytes::Bytes>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_test::{assert_de_tokens, assert_ser_tokens, assert_tokens, Token};
#[test]
fn test_serde_subcommand() {
for (sub, byte) in [
(Subcommand::EnableEnterpriseAttestation, 0x01),
(Subcommand::ToggleAlwaysUv, 0x02),
(Subcommand::SetMinPINLength, 0x03),
(Subcommand::EnableLongTouchForReset, 0x04),
(Subcommand::VendorPrototype, 0xff),
] {
assert_tokens(&sub, &[Token::U8(byte)]);
}
}
#[test]
fn test_de_request_toggle_always_uv() {
let req = Request {
sub_command: Subcommand::ToggleAlwaysUv,
sub_command_params: None,
pin_protocol: None,
pin_auth: None,
};
assert_de_tokens(
&req,
&[
Token::Map { len: Some(1) },
Token::U64(1),
Token::U8(0x02),
Token::MapEnd,
],
);
}
#[test]
fn test_de_request_set_min_pin_length() {
let mut rp_ids = Vec::new();
rp_ids.push("login.example.com").unwrap();
let req = Request {
sub_command: Subcommand::SetMinPINLength,
sub_command_params: Some(SubcommandParameters {
new_min_pin_length: Some(6),
min_pin_length_rp_ids: Some(rp_ids),
force_change_pin: Some(true),
}),
pin_protocol: Some(2),
pin_auth: None,
};
assert_de_tokens(
&req,
&[
Token::Map { len: Some(3) },
Token::U64(1),
Token::U8(0x03),
Token::U64(2),
Token::Map { len: Some(3) },
Token::U64(1),
Token::U8(6),
Token::U64(2),
Token::Seq { len: Some(1) },
Token::BorrowedStr("login.example.com"),
Token::SeqEnd,
Token::U64(3),
Token::Bool(true),
Token::MapEnd,
Token::U64(3),
Token::U8(2),
Token::MapEnd,
],
);
}
#[test]
fn test_ser_request_set_min_pin_length() {
let mut rp_ids = Vec::new();
rp_ids.push("login.example.com").unwrap();
let req = Request {
sub_command: Subcommand::SetMinPINLength,
sub_command_params: Some(SubcommandParameters {
new_min_pin_length: Some(6),
min_pin_length_rp_ids: Some(rp_ids),
force_change_pin: Some(true),
}),
pin_protocol: Some(2),
pin_auth: None,
};
assert_ser_tokens(
&req,
&[
Token::Map { len: Some(3) },
Token::U64(1),
Token::U8(0x03),
Token::U64(2),
Token::Some,
Token::Map { len: Some(3) },
Token::U64(1),
Token::Some,
Token::U8(6),
Token::U64(2),
Token::Some,
Token::Seq { len: Some(1) },
Token::BorrowedStr("login.example.com"),
Token::SeqEnd,
Token::U64(3),
Token::Some,
Token::Bool(true),
Token::MapEnd,
Token::U64(3),
Token::Some,
Token::U8(2),
Token::MapEnd,
],
);
}
#[test]
fn test_max_subcommand_params_cbor_len() {
let rp_id = "a".repeat(253);
let mut rp_ids = Vec::new();
rp_ids.resize(4, rp_id.as_str()).unwrap();
let params = SubcommandParameters {
new_min_pin_length: Some(u8::MAX),
min_pin_length_rp_ids: Some(rp_ids),
force_change_pin: Some(true),
};
let mut buffer = [0; MAX_SUBCOMMAND_PARAMS_CBOR_LEN];
let result = cbor_smol::cbor_serialize(¶ms, &mut buffer);
assert_eq!(result.err(), None);
}
}