use secrecy::ExposeSecret as _;
use secrecy::SecretBox;
use crate::Error;
#[derive(Clone, Copy)]
pub enum Expect {
Empty,
Some,
Short(u8),
}
#[derive(Debug)]
pub(crate) struct Command {
cla: u8,
ins: u8,
p1: u8,
p2: u8,
data: Data,
}
impl Command {
pub fn new(cla: u8, ins: u8, p1: u8, p2: u8, data: impl Into<Data>) -> Result<Self, Error> {
let data = data.into();
if data.len() > u16::MAX as usize {
Err(Error::InternalError(
"Command data too large, must be <64 kbyte".to_string(),
))
} else {
Ok(Command {
cla,
ins,
p1,
p2,
data,
})
}
}
pub(crate) fn ins(&self) -> u8 {
self.ins
}
pub(crate) fn p1(&self) -> u8 {
self.p1
}
pub(crate) fn p2(&self) -> u8 {
self.p2
}
pub(crate) fn data(&self) -> &Data {
&self.data
}
pub(crate) fn serialize(
&self,
ext_len: bool,
expect_response: Expect,
) -> Result<Vec<u8>, Error> {
debug_assert!(self.data.len() <= u16::MAX as usize);
let nc = self.data.len() as u16;
let mut buf = vec![self.cla, self.ins, self.p1, self.p2];
buf.extend(Self::make_lc(nc, ext_len)?);
match &self.data {
Data::Plain(data) => buf.extend(data),
Data::Sensitive(sensitive) => buf.extend(sensitive.expose_secret()),
}
buf.extend(Self::make_le(nc, ext_len, expect_response));
Ok(buf)
}
fn make_lc(len: u16, ext_len: bool) -> Result<Vec<u8>, crate::Error> {
if !ext_len && len > 0xff {
return Err(crate::Error::InternalError(format!(
"Command len = {len:x?}, but extended length is unsupported by backend"
)));
}
if len == 0 {
Ok(vec![])
} else if !ext_len {
Ok(vec![len as u8])
} else {
Ok(vec![0, (len >> 8) as u8, (len & 255) as u8])
}
}
fn make_le(nc: u16, ext_len: bool, expect_response: Expect) -> Vec<u8> {
match (ext_len, expect_response) {
(_, Expect::Empty) => {
vec![]
}
(false, Expect::Some) => {
vec![0]
}
(false, Expect::Short(size)) => {
vec![size]
}
(true, Expect::Some) => {
if nc == 0 {
vec![0, 0, 0]
} else {
vec![0, 0]
}
}
_ => {
unreachable!("This should not happen")
}
}
}
}
pub(crate) enum Data {
Plain(Vec<u8>),
Sensitive(SecretBox<[u8]>),
}
impl std::fmt::Debug for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Data::Plain(vec) => write!(f, "{vec:02x?}"),
Data::Sensitive(_) => write!(f, "REDACTED"),
}
}
}
impl Data {
#[must_use]
fn len(&self) -> usize {
match self {
Data::Plain(vec) => vec.len(),
Data::Sensitive(secret_vec) => secret_vec.expose_secret().len(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl From<Vec<u8>> for Data {
fn from(value: Vec<u8>) -> Self {
Data::Plain(value)
}
}
impl From<SecretBox<[u8]>> for Data {
fn from(value: SecretBox<[u8]>) -> Self {
Data::Sensitive(value)
}
}