#![cfg_attr(feature = "cargo-clippy", allow(clippy::needless_lifetimes))]
extern crate std;
use rand::{thread_rng, RngCore};
use std::ffi::CString;
use std::io;
use std::io::{Read, Write};
use crate::consts::*;
use crate::u2ftypes::*;
use crate::util::io_err;
pub fn u2f_init_device<T>(dev: &mut T) -> bool
where
T: U2FDevice + Read + Write,
{
let mut nonce = [0u8; 8];
thread_rng().fill_bytes(&mut nonce);
init_device(dev, &nonce).is_ok() && is_v2_device(dev).unwrap_or(false)
}
pub fn u2f_register<T>(dev: &mut T, challenge: &[u8], application: &[u8]) -> io::Result<Vec<u8>>
where
T: U2FDevice + Read + Write,
{
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid parameter sizes",
));
}
let mut register_data = Vec::with_capacity(2 * PARAMETER_SIZE);
register_data.extend(challenge);
register_data.extend(application);
let flags = U2F_REQUEST_USER_PRESENCE;
let (resp, status) = send_apdu(dev, U2F_REGISTER, flags, ®ister_data)?;
status_word_to_result(status, resp)
}
pub fn u2f_sign<T>(
dev: &mut T,
challenge: &[u8],
application: &[u8],
key_handle: &[u8],
) -> io::Result<Vec<u8>>
where
T: U2FDevice + Read + Write,
{
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid parameter sizes",
));
}
if key_handle.len() > 256 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Key handle too large",
));
}
let mut sign_data = Vec::with_capacity(2 * PARAMETER_SIZE + 1 + key_handle.len());
sign_data.extend(challenge);
sign_data.extend(application);
sign_data.push(key_handle.len() as u8);
sign_data.extend(key_handle);
let flags = U2F_REQUEST_USER_PRESENCE;
let (resp, status) = send_apdu(dev, U2F_AUTHENTICATE, flags, &sign_data)?;
status_word_to_result(status, resp)
}
pub fn u2f_is_keyhandle_valid<T>(
dev: &mut T,
challenge: &[u8],
application: &[u8],
key_handle: &[u8],
) -> io::Result<bool>
where
T: U2FDevice + Read + Write,
{
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid parameter sizes",
));
}
if key_handle.len() > 256 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Key handle too large",
));
}
let mut sign_data = Vec::with_capacity(2 * PARAMETER_SIZE + 1 + key_handle.len());
sign_data.extend(challenge);
sign_data.extend(application);
sign_data.push(key_handle.len() as u8);
sign_data.extend(key_handle);
let flags = U2F_CHECK_IS_REGISTERED;
let (_, status) = send_apdu(dev, U2F_AUTHENTICATE, flags, &sign_data)?;
Ok(status == SW_CONDITIONS_NOT_SATISFIED)
}
fn init_device<T>(dev: &mut T, nonce: &[u8]) -> io::Result<()>
where
T: U2FDevice + Read + Write,
{
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
let raw = sendrecv(dev, HIDCmd::Init, nonce)?;
let rsp = U2FHIDInitResp::read(&raw, nonce)?;
dev.set_cid(rsp.cid);
let vendor = dev
.get_property("Manufacturer")
.unwrap_or_else(|_| String::from("Unknown Vendor"));
let product = dev
.get_property("Product")
.unwrap_or_else(|_| String::from("Unknown Device"));
dev.set_device_info(U2FDeviceInfo {
vendor_name: vendor.as_bytes().to_vec(),
device_name: product.as_bytes().to_vec(),
version_interface: rsp.version_interface,
version_major: rsp.version_major,
version_minor: rsp.version_minor,
version_build: rsp.version_build,
cap_flags: rsp.cap_flags,
});
Ok(())
}
fn is_v2_device<T>(dev: &mut T) -> io::Result<bool>
where
T: U2FDevice + Read + Write,
{
let (data, status) = send_apdu(dev, U2F_VERSION, 0x00, &[])?;
let actual = CString::new(data)?;
let expected = CString::new("U2F_V2")?;
status_word_to_result(status, actual == expected)
}
fn status_word_to_result<T>(status: [u8; 2], val: T) -> io::Result<T> {
use self::io::ErrorKind::{InvalidData, InvalidInput};
match status {
SW_NO_ERROR => Ok(val),
SW_WRONG_DATA => Err(io::Error::new(InvalidData, "wrong data")),
SW_WRONG_LENGTH => Err(io::Error::new(InvalidInput, "wrong length")),
SW_CONDITIONS_NOT_SATISFIED => Err(io_err("conditions not satisfied")),
_ => Err(io_err(&format!("failed with status {:?}", status))),
}
}
pub fn sendrecv<T>(dev: &mut T, cmd: HIDCmd, send: &[u8]) -> io::Result<Vec<u8>>
where
T: U2FDevice + Read + Write,
{
let mut count = U2FHIDInit::write(dev, cmd.into(), send)?;
let mut sequence = 0u8;
while count < send.len() {
count += U2FHIDCont::write(dev, sequence, &send[count..])?;
sequence += 1;
}
let (_, mut data) = U2FHIDInit::read(dev)?;
let mut sequence = 0u8;
while data.len() < data.capacity() {
let max = data.capacity() - data.len();
data.extend_from_slice(&U2FHIDCont::read(dev, sequence, max)?);
sequence += 1;
}
Ok(data)
}
fn send_apdu<T>(dev: &mut T, cmd: u8, p1: u8, send: &[u8]) -> io::Result<(Vec<u8>, [u8; 2])>
where
T: U2FDevice + Read + Write,
{
let apdu = U2FAPDUHeader::serialize(cmd, p1, send)?;
let mut data = sendrecv(dev, HIDCmd::Msg, &apdu)?;
if data.len() < 2 {
return Err(io_err("unexpected response"));
}
let split_at = data.len() - 2;
let status = data.split_off(split_at);
Ok((data, [status[0], status[1]]))
}
#[cfg(test)]
pub(crate) mod tests {
use super::{init_device, is_v2_device, send_apdu, sendrecv, U2FDevice};
use crate::consts::{Capability, HIDCmd, CID_BROADCAST, SW_NO_ERROR};
use crate::transport::device_selector::Device;
use crate::transport::hid::HIDDevice;
use crate::u2ftypes::U2FDeviceInfo;
use rand::{thread_rng, RngCore};
#[test]
fn test_init_device() {
let mut device = Device::new("u2fprotocol").unwrap();
let nonce = vec![0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01];
let mut cid = [0u8; 4];
thread_rng().fill_bytes(&mut cid);
let mut msg = CID_BROADCAST.to_vec();
msg.extend(vec![HIDCmd::Init.into(), 0x00, 0x08]); msg.extend_from_slice(&nonce);
device.add_write(&msg, 0);
let mut msg = CID_BROADCAST.to_vec();
msg.extend(vec![HIDCmd::Init.into(), 0x00, 0x11]); msg.extend_from_slice(&nonce);
msg.extend_from_slice(&cid); msg.extend(vec![0x02, 0x04, 0x01, 0x08, 0x01]); device.add_read(&msg, 0);
init_device(&mut device, &nonce).unwrap();
assert_eq!(device.get_cid(), &cid);
let dev_info = device.get_device_info();
assert_eq!(dev_info.version_interface, 0x02);
assert_eq!(dev_info.version_major, 0x04);
assert_eq!(dev_info.version_minor, 0x01);
assert_eq!(dev_info.version_build, 0x08);
assert_eq!(dev_info.cap_flags, Capability::WINK); }
#[test]
fn test_get_version() {
let mut device = Device::new("u2fprotocol").unwrap();
let mut cid = [0u8; 4];
thread_rng().fill_bytes(&mut cid);
device.set_cid(cid.clone());
let info = U2FDeviceInfo {
vendor_name: Vec::new(),
device_name: Vec::new(),
version_interface: 0x02,
version_major: 0x04,
version_minor: 0x01,
version_build: 0x08,
cap_flags: Capability::WINK,
};
device.set_device_info(info);
let mut msg = cid.to_vec();
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x9]); msg.extend(&[0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]);
device.add_write(&msg, 0);
let mut msg = cid.to_vec();
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x08]); msg.extend(&[0x55, 0x32, 0x46, 0x5f, 0x56, 0x32]); msg.extend(&SW_NO_ERROR);
device.add_read(&msg, 0);
let res = is_v2_device(&mut device).expect("Failed to get version");
assert!(res);
}
#[test]
fn test_sendrecv_multiple() {
let mut device = Device::new("u2fprotocol").unwrap();
let cid = [0x01, 0x02, 0x03, 0x04];
device.set_cid(cid);
let mut msg = cid.to_vec();
msg.extend(vec![HIDCmd::Ping.into(), 0x00, 0xe4]); device.add_write(&msg, 1);
device.add_read(&msg, 1);
let mut msg = cid.to_vec();
msg.push(0x00); device.add_write(&msg, 1);
device.add_read(&msg, 1);
let mut msg = cid.to_vec();
msg.push(0x01); device.add_write(&msg, 1);
device.add_read(&msg, 1);
let mut msg = cid.to_vec();
msg.push(0x02); msg.extend_from_slice(&[1u8; 53]);
device.add_write(&msg, 0);
device.add_read(&msg, 0);
let data = [1u8; 228];
let d = sendrecv(&mut device, HIDCmd::Ping, &data).unwrap();
assert_eq!(d.len(), 228);
assert_eq!(d, &data[..]);
}
#[test]
fn test_sendapdu() {
let cid = [0x01, 0x02, 0x03, 0x04];
let data = [0x01, 0x02, 0x03, 0x04, 0x05];
let mut device = Device::new("u2fprotocol").unwrap();
device.set_cid(cid);
let mut msg = cid.to_vec();
msg.extend(vec![HIDCmd::Msg.into(), 0x00, 0x0e]); msg.extend(vec![
0x00,
HIDCmd::Ping.into(),
0xaa,
0x00,
0x00,
0x00,
0x05,
]);
msg.extend_from_slice(&data);
device.add_write(&msg, 0);
let mut msg = cid.to_vec();
msg.extend(vec![HIDCmd::Msg.into(), 0x00, 0x07]);
msg.extend_from_slice(&data);
msg.extend_from_slice(&SW_NO_ERROR);
device.add_read(&msg, 0);
let (result, status) = send_apdu(&mut device, HIDCmd::Ping.into(), 0xaa, &data).unwrap();
assert_eq!(result, &data);
assert_eq!(status, SW_NO_ERROR);
}
#[test]
fn test_get_property() {
let device = Device::new("u2fprotocol").unwrap();
assert_eq!(device.get_property("a").unwrap(), "a not implemented");
}
}