use hex;
use hidapi::HidDevice;
use log;
use std::{cmp::min, mem::size_of_val, slice};
use crate::errors::HWKeyError;
use crate::ledger::apdu::APDU;
pub const HID_RPT_SIZE: usize = 64;
pub const INIT_HEADER_SIZE: usize = 7;
const INIT_DATA_SIZE: usize = HID_RPT_SIZE - 12;
const CONT_DATA_SIZE: usize = HID_RPT_SIZE - 5;
pub const SW_NO_ERROR: [u8; 2] = [0x90, 0x00];
pub const SW_CONDITIONS_NOT_SATISFIED: [u8; 2] = [0x69, 0x85];
pub const SW_WRONG_DATA: [u8; 2] = [0x6A, 0x80];
pub const SW_WRONG_LENGTH: [u8; 2] = [0x67, 0x00];
pub const SW_INCORRECT_PARAMETERS: [u8; 2] = [0x6b, 0x00];
pub const SW_USER_CANCEL: [u8; 2] = [0x6A, 0x85];
fn get_hid_header(channel: u16, index: usize) -> [u8; 5] {
[
(channel >> 8) as u8,
(channel & 0xff) as u8, 0x05, (index >> 8) as u8,
(index & 0xff) as u8, ]
}
fn check_recv_frame(frame: &[u8], channel: u16, index: usize) -> Result<(), HWKeyError> {
if size_of_val(frame) < 5
|| frame[0] != (channel >> 8) as u8
|| frame[1] != (channel & 0xff) as u8
|| frame[2] != 0x05
{
return Err(HWKeyError::CommError("Invalid frame header size".to_string()));
}
let seq = (frame[3] as usize) << 8 | (frame[4] as usize);
if seq != index {
return Err(HWKeyError::CommError(format!(
"Invalid sequence. {:?} != {:?} (act != exp) of {:}",
seq, index, hex::encode(&frame)
)));
}
if index == 0 && size_of_val(frame) < 7 {
return Err(HWKeyError::CommError(format!("Invalid frame size: {:}", size_of_val(frame))));
}
Ok(())
}
fn get_init_header(apdu: &APDU) -> [u8; INIT_HEADER_SIZE] {
let mut buf = [0u8; INIT_HEADER_SIZE];
buf[0] = (apdu.len() >> 8) as u8;
buf[1] = (apdu.len() & 0xff) as u8;
buf[2..].clone_from_slice(apdu.raw_header().as_slice());
buf
}
fn set_data(data: &mut [u8], itr: &mut slice::Iter<u8>, max: usize) {
let available = itr.size_hint().0;
for i in 0..min(max, available) {
data[i] = *itr.next().unwrap();
}
}
pub fn sw_to_error(sw_h: u8, sw_l: u8) -> Result<(), HWKeyError> {
match [sw_l, sw_h] {
SW_NO_ERROR => Ok(()),
SW_WRONG_LENGTH => Err(HWKeyError::CommError("Incorrect length".to_string())),
SW_WRONG_DATA => Err(HWKeyError::CommError("Invalid data".to_string())),
SW_INCORRECT_PARAMETERS => Err(HWKeyError::CommError("Incorrect parameters".to_string())),
SW_USER_CANCEL => Err(HWKeyError::CommError("Canceled by user".to_string())),
SW_CONDITIONS_NOT_SATISFIED => {
Err(HWKeyError::CommError("Conditions not satisfied()".to_string()))
}
v => Err(HWKeyError::CommError(format!(
"Internal communication error: {:?}",
v
))),
}
}
pub fn send(dev: &HidDevice, apdu: &APDU) -> Result<(), HWKeyError> {
let mut frame_index: usize = 0;
let mut data_itr = apdu.data.iter();
let mut init_sent = false;
let channel = 0x101;
debug!(">> senrecv input: {:?}", &apdu);
while !init_sent || data_itr.size_hint().0 != 0 {
let mut frame: [u8; (HID_RPT_SIZE + 1) as usize] = [0; (HID_RPT_SIZE + 1) as usize];
frame[1..6].clone_from_slice(&get_hid_header(channel, frame_index));
if !init_sent {
frame[6..13].clone_from_slice(&get_init_header(&apdu));
init_sent = true;
set_data(&mut frame[13..], &mut data_itr, INIT_DATA_SIZE);
} else {
set_data(&mut frame[6..], &mut data_itr, CONT_DATA_SIZE);
}
if log_enabled!(log::Level::Trace) {
trace!(">> USB send: {}", hex::encode(frame.to_vec()));
}
if let Err(err) = dev.write(&frame) {
return Err(err.into());
};
frame_index += 1;
}
Ok(())
}
pub fn recv_direct(dev: &HidDevice) -> Result<Vec<u8>, HWKeyError> {
let mut frame_index: usize = 0;
let channel = 0x101;
debug!("<< read response");
let mut data: Vec<u8> = Vec::new();
let datalen: usize;
let mut recvlen: usize = 0;
let mut frame: [u8; HID_RPT_SIZE] = [0u8; HID_RPT_SIZE];
let frame_size = dev.read(&mut frame)?;
check_recv_frame(&frame, channel, frame_index)?;
datalen = (frame[5] as usize) << 8 | (frame[6] as usize);
data.extend_from_slice(&frame[7..frame_size]);
recvlen += frame_size;
frame_index += 1;
debug!(
"<<\t|-- recvlen: {}, datalen: {}",
recvlen,
datalen
);
debug!(
"<<\t|-- init data: {:?}",
hex::encode(&data)
);
while recvlen < datalen {
frame = [0u8; HID_RPT_SIZE];
let frame_size = dev.read(&mut frame)?;
check_recv_frame(&frame, channel, frame_index)?;
data.extend_from_slice(&frame[5..frame_size]);
recvlen += frame_size;
frame_index += 1;
debug!(
"<<\t|-- cont_{:?} size:{:?}, data: {:?}",
frame_index,
data.len(),
hex::encode(&data)
);
}
data.truncate(datalen);
Ok(data)
}
pub fn recv(dev: &HidDevice) -> Result<Vec<u8>, HWKeyError> {
let mut data = recv_direct(dev)?;
match sw_to_error(data.pop().unwrap(), data.pop().unwrap()) {
Ok(_) => Ok(data),
Err(e) => Err(e),
}
}
pub fn sendrecv(dev: &HidDevice, apdu: &APDU) -> Result<Vec<u8>, HWKeyError> {
send(dev, apdu)?;
recv(dev)
}
pub fn ping(dev: &HidDevice) -> Result<bool, HWKeyError> {
let mut frame: [u8; (HID_RPT_SIZE + 1) as usize] = [0; (HID_RPT_SIZE + 1) as usize];
let channel: u16 = 0x101;
frame[1] = (channel >> 8) as u8;
frame[2] = (channel & 0xff) as u8;
frame[3] = 2;
if log_enabled!(log::Level::Trace) {
let parts: Vec<String> = frame.iter().map(|byte| format!("{:02x}", byte)).collect();
trace!(">> PING USB send: {}", parts.join(""));
}
if let Err(err) = dev.write(&frame) {
return Err(err.into());
};
let mut frame: [u8; HID_RPT_SIZE] = [0u8; HID_RPT_SIZE];
let frame_size = dev.read(&mut frame)?;
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&frame[7..frame_size]);
if log_enabled!(log::Level::Trace) {
trace!("\t\t|-- PING response: {:?}", hex::encode(&data));
}
Ok(data[0] == 0)
}