use crate::consts::*;
use crate::transport::hid::HIDDevice;
use crate::util::io_err;
use serde::Serialize;
use std::{cmp, fmt, io, str};
pub fn to_hex(data: &[u8], joiner: &str) -> String {
let parts: Vec<String> = data.iter().map(|byte| format!("{byte:02x}")).collect();
parts.join(joiner)
}
pub fn trace_hex(data: &[u8]) {
if log_enabled!(log::Level::Trace) {
trace!("USB send: {}", to_hex(data, ""));
}
}
pub struct U2FHIDInit {}
impl U2FHIDInit {
pub fn read<T: HIDDevice>(dev: &mut T) -> io::Result<(HIDCmd, Vec<u8>)> {
let mut frame = vec![0u8; dev.in_rpt_size()];
let mut count = dev.read(&mut frame)?;
while dev.get_cid() != &frame[..4] {
count = dev.read(&mut frame)?;
}
if count != dev.in_rpt_size() {
return Err(io_err("invalid init packet"));
}
let cmd = HIDCmd::from(frame[4] | TYPE_INIT);
let cap = (frame[5] as usize) << 8 | (frame[6] as usize);
let mut data = Vec::with_capacity(cap);
let len = if dev.in_rpt_size() >= INIT_HEADER_SIZE {
cmp::min(cap, dev.in_rpt_size() - INIT_HEADER_SIZE)
} else {
cap
};
data.extend_from_slice(&frame[7..7 + len]);
Ok((cmd, data))
}
pub fn write<T: HIDDevice>(dev: &mut T, cmd: u8, data: &[u8]) -> io::Result<usize> {
if data.len() > 0xffff {
return Err(io_err("payload length > 2^16"));
}
let mut frame = vec![0u8; dev.out_rpt_size() + 1];
frame[1..5].copy_from_slice(dev.get_cid());
frame[5] = cmd;
frame[6] = (data.len() >> 8) as u8;
frame[7] = data.len() as u8;
let count = if dev.out_rpt_size() >= INIT_HEADER_SIZE {
cmp::min(data.len(), dev.out_rpt_size() - INIT_HEADER_SIZE)
} else {
data.len()
};
frame[8..8 + count].copy_from_slice(&data[..count]);
trace_hex(&frame);
if dev.write(&frame)? != frame.len() {
return Err(io_err("device write failed"));
}
Ok(count)
}
}
pub struct U2FHIDCont {}
impl U2FHIDCont {
pub fn read<T: HIDDevice>(dev: &mut T, seq: u8, max: usize) -> io::Result<Vec<u8>> {
let mut frame = vec![0u8; dev.in_rpt_size()];
let mut count = dev.read(&mut frame)?;
while dev.get_cid() != &frame[..4] {
count = dev.read(&mut frame)?;
}
if count != dev.in_rpt_size() {
return Err(io_err("invalid cont packet"));
}
if seq != frame[4] {
return Err(io_err("invalid sequence number"));
}
let max = if dev.in_rpt_size() >= CONT_HEADER_SIZE {
cmp::min(max, dev.in_rpt_size() - CONT_HEADER_SIZE)
} else {
max
};
Ok(frame[5..5 + max].to_vec())
}
pub fn write<T: HIDDevice>(dev: &mut T, seq: u8, data: &[u8]) -> io::Result<usize> {
let mut frame = vec![0u8; dev.out_rpt_size() + 1];
frame[1..5].copy_from_slice(dev.get_cid());
frame[5] = seq;
let count = if dev.out_rpt_size() >= CONT_HEADER_SIZE {
cmp::min(data.len(), dev.out_rpt_size() - CONT_HEADER_SIZE)
} else {
data.len()
};
frame[6..6 + count].copy_from_slice(&data[..count]);
trace_hex(&frame);
if dev.write(&frame)? != frame.len() {
return Err(io_err("device write failed"));
}
Ok(count)
}
}
pub struct U2FHIDInitResp {
pub cid: [u8; 4],
pub version_interface: u8,
pub version_major: u8,
pub version_minor: u8,
pub version_build: u8,
pub cap_flags: Capability,
}
impl U2FHIDInitResp {
pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<U2FHIDInitResp> {
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
if data.len() < INIT_NONCE_SIZE + 9 {
return Err(io_err("invalid init response"));
}
if nonce != &data[..INIT_NONCE_SIZE] {
return Err(io_err("invalid nonce"));
}
let rsp = U2FHIDInitResp {
cid: [
data[INIT_NONCE_SIZE],
data[INIT_NONCE_SIZE + 1],
data[INIT_NONCE_SIZE + 2],
data[INIT_NONCE_SIZE + 3],
],
version_interface: data[INIT_NONCE_SIZE + 4],
version_major: data[INIT_NONCE_SIZE + 5],
version_minor: data[INIT_NONCE_SIZE + 6],
version_build: data[INIT_NONCE_SIZE + 7],
cap_flags: Capability::from_bits_truncate(data[INIT_NONCE_SIZE + 8]),
};
Ok(rsp)
}
}
pub struct CTAP1RequestAPDU {}
impl CTAP1RequestAPDU {
pub fn serialize(ins: u8, p1: u8, data: &[u8]) -> io::Result<Vec<u8>> {
if data.len() > 0xffff {
return Err(io_err("payload length > 2^16"));
}
let data_size = if data.is_empty() { 0 } else { 2 + data.len() };
let mut bytes = vec![0u8; U2FAPDUHEADER_SIZE + data_size];
bytes[1] = ins;
bytes[2] = p1;
if !data.is_empty() {
bytes[5] = (data.len() >> 8) as u8; bytes[6] = data.len() as u8;
bytes[7..7 + data.len()].copy_from_slice(data);
}
Ok(bytes)
}
}
#[derive(Clone, Debug, Serialize)]
pub struct U2FDeviceInfo {
pub vendor_name: Vec<u8>,
pub device_name: Vec<u8>,
pub version_interface: u8,
pub version_major: u8,
pub version_minor: u8,
pub version_build: u8,
pub cap_flags: Capability,
}
impl fmt::Display for U2FDeviceInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Vendor: {}, Device: {}, Interface: {}, Firmware: v{}.{}.{}, Capabilities: {}",
str::from_utf8(&self.vendor_name).unwrap(),
str::from_utf8(&self.device_name).unwrap(),
&self.version_interface,
&self.version_major,
&self.version_minor,
&self.version_build,
to_hex(&[self.cap_flags.bits()], ":"),
)
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::CTAP1RequestAPDU;
#[test]
fn test_ctap1_serialize() {
assert_eq!(
vec![0, 1, 2, 0, 0, 0, 0],
CTAP1RequestAPDU::serialize(1, 2, &[]).unwrap()
);
assert_eq!(
vec![0, 1, 2, 0, 0, 0, 1, 42, 0, 0],
CTAP1RequestAPDU::serialize(1, 2, &[42]).unwrap()
);
let d = [0xFF; 300];
let mut expected = vec![0, 1, 2, 0, 0, 0x1, 0x2c];
expected.extend_from_slice(&d);
expected.extend_from_slice(&[0, 0]); assert_eq!(expected, CTAP1RequestAPDU::serialize(1, 2, &d).unwrap());
let big = [0xFF; 65536];
assert!(CTAP1RequestAPDU::serialize(1, 2, &big).is_err());
}
}