mirror of
https://github.com/librekeys/authenticator-rs.git
synced 2026-07-28 08:01:16 -07:00
+13
-5
@@ -69,15 +69,19 @@ fn main() {
|
||||
app_bytes.clone(),
|
||||
vec![],
|
||||
move |rv| {
|
||||
tx.send(rv.unwrap()).unwrap();
|
||||
tx.send(rv).unwrap();
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let register_data = try_or!(rx.recv(), |_| {
|
||||
let register_result = try_or!(rx.recv(), |_| {
|
||||
panic!("Problem receiving, unable to continue");
|
||||
});
|
||||
let (register_data, device_info) =
|
||||
register_result.unwrap_or_else(|e| panic!("Registration failed: {:?}", e));
|
||||
|
||||
println!("Register result: {}", base64::encode(®ister_data));
|
||||
println!("Device info: {}", &device_info);
|
||||
println!("Asking a security key to sign now, with the data from the register...");
|
||||
let credential = u2f_get_key_handle_from_register_response(®ister_data).unwrap();
|
||||
let key_handle = KeyHandle {
|
||||
@@ -95,15 +99,19 @@ fn main() {
|
||||
vec![app_bytes],
|
||||
vec![key_handle],
|
||||
move |rv| {
|
||||
tx.send(rv.unwrap()).unwrap();
|
||||
tx.send(rv).unwrap();
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (_, handle_used, sign_data) = try_or!(rx.recv(), |_| {
|
||||
println!("Problem receiving");
|
||||
let sign_result = try_or!(rx.recv(), |_| {
|
||||
panic!("Problem receiving, unable to continue");
|
||||
});
|
||||
let (_, handle_used, sign_data, device_info) =
|
||||
sign_result.unwrap_or_else(|e| panic!("Sign failed: {:?}", e));
|
||||
|
||||
println!("Sign result: {}", base64::encode(&sign_data));
|
||||
println!("Key handle used: {}", base64::encode(&handle_used));
|
||||
println!("Device info: {}", &device_info);
|
||||
println!("Done.");
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ extern crate authenticator;
|
||||
|
||||
use std::{cmp, io};
|
||||
|
||||
use authenticator::{sendrecv, U2FDevice};
|
||||
use authenticator::{sendrecv, U2FDevice, U2FDeviceInfo};
|
||||
use authenticator::{CID_BROADCAST, MAX_HID_RPT_SIZE};
|
||||
|
||||
struct TestDevice<'a> {
|
||||
cid: [u8; 4],
|
||||
data: &'a [u8],
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl<'a> TestDevice<'a> {
|
||||
@@ -22,6 +23,7 @@ impl<'a> TestDevice<'a> {
|
||||
TestDevice {
|
||||
cid: CID_BROADCAST,
|
||||
data,
|
||||
dev_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +65,20 @@ impl<'a> U2FDevice for TestDevice<'a> {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
|
||||
@@ -9,12 +9,13 @@ extern crate authenticator;
|
||||
|
||||
use std::{cmp, io};
|
||||
|
||||
use authenticator::{sendrecv, U2FDevice};
|
||||
use authenticator::{sendrecv, U2FDevice, U2FDeviceInfo};
|
||||
use authenticator::{CID_BROADCAST, MAX_HID_RPT_SIZE};
|
||||
|
||||
struct TestDevice {
|
||||
cid: [u8; 4],
|
||||
data: Vec<u8>,
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl TestDevice {
|
||||
@@ -22,6 +23,7 @@ impl TestDevice {
|
||||
TestDevice {
|
||||
cid: CID_BROADCAST,
|
||||
data: vec![],
|
||||
dev_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +66,20 @@ impl U2FDevice for TestDevice {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
|
||||
+17
-2
@@ -22,6 +22,11 @@ const RESBUF_ID_REGISTRATION: u8 = 0;
|
||||
const RESBUF_ID_KEYHANDLE: u8 = 1;
|
||||
const RESBUF_ID_SIGNATURE: u8 = 2;
|
||||
const RESBUF_ID_APPID: u8 = 3;
|
||||
const RESBUF_ID_VENDOR_NAME: u8 = 4;
|
||||
const RESBUF_ID_DEVICE_NAME: u8 = 5;
|
||||
const RESBUF_ID_FIRMWARE_MAJOR: u8 = 6;
|
||||
const RESBUF_ID_FIRMWARE_MINOR: u8 = 7;
|
||||
const RESBUF_ID_FIRMWARE_BUILD: u8 = 8;
|
||||
|
||||
// Generates a new 64-bit transaction id with collision probability 2^-32.
|
||||
fn new_tid() -> u64 {
|
||||
@@ -233,9 +238,14 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(
|
||||
key_handles,
|
||||
move |rv| {
|
||||
let result = match rv {
|
||||
Ok(registration) => {
|
||||
Ok((registration, dev_info)) => {
|
||||
let mut bufs = HashMap::new();
|
||||
bufs.insert(RESBUF_ID_REGISTRATION, registration);
|
||||
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
|
||||
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]);
|
||||
U2FResult::Success(bufs)
|
||||
}
|
||||
Err(e) => U2FResult::Error(e),
|
||||
@@ -288,11 +298,16 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
|
||||
let tid = new_tid();
|
||||
let res = (*mgr).sign(flags, timeout, challenge, app_ids, key_handles, move |rv| {
|
||||
let result = match rv {
|
||||
Ok((app_id, key_handle, signature)) => {
|
||||
Ok((app_id, key_handle, signature, dev_info)) => {
|
||||
let mut bufs = HashMap::new();
|
||||
bufs.insert(RESBUF_ID_KEYHANDLE, key_handle);
|
||||
bufs.insert(RESBUF_ID_SIGNATURE, signature);
|
||||
bufs.insert(RESBUF_ID_APPID, app_id);
|
||||
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
|
||||
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]);
|
||||
bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]);
|
||||
U2FResult::Success(bufs)
|
||||
}
|
||||
Err(e) => U2FResult::Error(e),
|
||||
|
||||
@@ -45,6 +45,8 @@ pub const U2F_REGISTER: u8 = 0x01; // Registration command
|
||||
pub const U2F_AUTHENTICATE: u8 = 0x02; // Authenticate/sign command
|
||||
pub const U2F_VERSION: u8 = 0x03; // Read version string command
|
||||
|
||||
pub const YKPIV_INS_GET_VERSION: u8 = 0xfd; // Get firmware version, yubico ext
|
||||
|
||||
// U2F_REGISTER command defines
|
||||
pub const U2F_REGISTER_ID: u8 = 0x05; // Version 2 registration identifier
|
||||
pub const U2F_REGISTER_HASH_ID: u8 = 0x00; // Version 2 hash identintifier
|
||||
|
||||
+17
-1
@@ -11,7 +11,7 @@ use std::os::unix::prelude::*;
|
||||
|
||||
use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
|
||||
use crate::platform::uhid;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use crate::util::from_unix_result;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -19,6 +19,7 @@ pub struct Device {
|
||||
path: OsString,
|
||||
fd: libc::c_int,
|
||||
cid: [u8; 4],
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -30,6 +31,7 @@ impl Device {
|
||||
path,
|
||||
fd,
|
||||
cid: CID_BROADCAST,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,4 +95,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -100,8 +100,8 @@ pub struct KeyHandle {
|
||||
}
|
||||
|
||||
pub type AppId = Vec<u8>;
|
||||
pub type RegisterResult = Vec<u8>;
|
||||
pub type SignResult = (AppId, Vec<u8>, Vec<u8>);
|
||||
pub type RegisterResult = (Vec<u8>, u2ftypes::U2FDeviceInfo);
|
||||
pub type SignResult = (AppId, Vec<u8>, Vec<u8>, u2ftypes::U2FDeviceInfo);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Error {
|
||||
|
||||
+18
-2
@@ -10,8 +10,8 @@ use std::io::{Read, Write};
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
use crate::consts::CID_BROADCAST;
|
||||
use crate::platform::hidraw;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::platform::{hidraw, monitor};
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use crate::util::from_unix_result;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -21,6 +21,7 @@ pub struct Device {
|
||||
in_rpt_size: usize,
|
||||
out_rpt_size: usize,
|
||||
cid: [u8; 4],
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -35,6 +36,7 @@ impl Device {
|
||||
in_rpt_size,
|
||||
out_rpt_size,
|
||||
cid: CID_BROADCAST,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,4 +95,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
self.out_rpt_size
|
||||
}
|
||||
|
||||
fn get_property(&self, prop_name: &str) -> io::Result<String> {
|
||||
monitor::get_property_linux(&self.path, prop_name)
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,8 @@ where
|
||||
let f = self.new_device_cb.clone();
|
||||
let key = path.clone();
|
||||
|
||||
debug!("Adding device {}", path.to_string_lossy());
|
||||
|
||||
let runloop = RunLoop::new(move |alive| {
|
||||
if alive() {
|
||||
f(path, alive);
|
||||
@@ -119,6 +121,8 @@ where
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, path: &OsString) {
|
||||
debug!("Removing device {}", path.to_string_lossy());
|
||||
|
||||
if let Some(runloop) = self.runloops.remove(path) {
|
||||
runloop.cancel();
|
||||
}
|
||||
@@ -131,3 +135,35 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_property_linux(path: &OsString, prop_name: &str) -> io::Result<String> {
|
||||
let ctx = libudev::Context::new()?;
|
||||
|
||||
let mut enumerator = libudev::Enumerator::new(&ctx)?;
|
||||
enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
|
||||
// Iterate all existing devices, since we don't have a syspath
|
||||
// and libudev-rs doesn't implement opening by devnode.
|
||||
for dev in enumerator.scan_devices()? {
|
||||
if dev.devnode().is_some() && dev.devnode().unwrap() == path {
|
||||
debug!(
|
||||
"get_property_linux Querying property {} from {}",
|
||||
prop_name,
|
||||
dev.syspath().display()
|
||||
);
|
||||
|
||||
let value = dev
|
||||
.attribute_value(prop_name)
|
||||
.ok_or(io::ErrorKind::Other)?
|
||||
.to_string_lossy();
|
||||
|
||||
debug!("get_property_linux Fetched Result, {}={}", prop_name, value);
|
||||
return Ok(value.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Unable to find device",
|
||||
))
|
||||
}
|
||||
|
||||
+38
-3
@@ -6,8 +6,9 @@ extern crate log;
|
||||
|
||||
use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
|
||||
use crate::platform::iokit::*;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use core_foundation::base::*;
|
||||
use core_foundation::string::*;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
@@ -20,21 +21,41 @@ pub struct Device {
|
||||
device_ref: IOHIDDeviceRef,
|
||||
cid: [u8; 4],
|
||||
report_rx: Receiver<Vec<u8>>,
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(dev_info: (IOHIDDeviceRef, Receiver<Vec<u8>>)) -> io::Result<Self> {
|
||||
let (device_ref, report_rx) = dev_info;
|
||||
pub fn new(dev_ids: (IOHIDDeviceRef, Receiver<Vec<u8>>)) -> io::Result<Self> {
|
||||
let (device_ref, report_rx) = dev_ids;
|
||||
Ok(Self {
|
||||
device_ref,
|
||||
cid: CID_BROADCAST,
|
||||
report_rx,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn get_property_macos(&self, prop_name: &str) -> io::Result<String> {
|
||||
let prop_ref = IOHIDDeviceGetProperty(
|
||||
self.device_ref,
|
||||
CFString::new(prop_name).as_concrete_TypeRef(),
|
||||
);
|
||||
if prop_ref.is_null() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"IOHIDDeviceGetProperty received nullptr for property {}",
|
||||
prop_name
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(CFString::from_void(prop_ref).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Device {
|
||||
@@ -108,4 +129,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, prop_name: &str) -> io::Result<String> {
|
||||
unsafe { self.get_property_macos(prop_name) }
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +220,7 @@ extern "C" {
|
||||
report: *const u8,
|
||||
reportLength: CFIndex,
|
||||
) -> IOReturn;
|
||||
pub fn IOHIDDeviceGetProperty(device: IOHIDDeviceRef, key: CFStringRef) -> CFTypeRef;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+17
-1
@@ -13,13 +13,14 @@ use crate::consts::CID_BROADCAST;
|
||||
use crate::consts::MAX_HID_RPT_SIZE;
|
||||
use crate::platform::fd::Fd;
|
||||
use crate::platform::uhid;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use crate::util::io_err;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
fd: Fd,
|
||||
cid: [u8; 4],
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -27,6 +28,7 @@ impl Device {
|
||||
Ok(Self {
|
||||
fd,
|
||||
cid: CID_BROADCAST,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -140,4 +142,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -10,7 +10,7 @@ use std::mem;
|
||||
|
||||
use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
|
||||
use crate::platform::monitor::FidoDev;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use crate::util::{from_unix_result, io_err};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -19,6 +19,7 @@ pub struct Device {
|
||||
fd: libc::c_int,
|
||||
cid: [u8; 4],
|
||||
out_len: usize,
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -29,6 +30,7 @@ impl Device {
|
||||
fd: fido.fd,
|
||||
cid: CID_BROADCAST,
|
||||
out_len: 64,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -128,4 +130,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -6,6 +6,7 @@ use crate::consts::PARAMETER_SIZE;
|
||||
use crate::platform::device::Device;
|
||||
use crate::platform::transaction::Transaction;
|
||||
use crate::u2fprotocol::{u2f_init_device, u2f_is_keyhandle_valid, u2f_register, u2f_sign};
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::util::StateCallback;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -103,7 +104,8 @@ impl StateMachine {
|
||||
break;
|
||||
}
|
||||
} else if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
|
||||
callback.call(Ok(bytes));
|
||||
let dev_info = dev.get_device_info();
|
||||
callback.call(Ok((bytes, dev_info)));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -187,10 +189,12 @@ impl StateMachine {
|
||||
for key_handle in &valid_handles {
|
||||
if let Ok(bytes) = u2f_sign(dev, &challenge, app_id, &key_handle.credential)
|
||||
{
|
||||
let dev_info = dev.get_device_info();
|
||||
callback.call(Ok((
|
||||
app_id.clone(),
|
||||
key_handle.credential.clone(),
|
||||
bytes,
|
||||
dev_info,
|
||||
)));
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
+13
-1
@@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
@@ -50,4 +50,16 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
panic!("not implemented");
|
||||
}
|
||||
|
||||
fn get_property(&self, prop_name: &str) -> io::Result<String> {
|
||||
panic!("not implemented")
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
panic!("not implemented")
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
panic!("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ const uint8_t U2F_RESBUF_ID_REGISTRATION = 0;
|
||||
const uint8_t U2F_RESBUF_ID_KEYHANDLE = 1;
|
||||
const uint8_t U2F_RESBUF_ID_SIGNATURE = 2;
|
||||
const uint8_t U2F_RESBUF_ID_APPID = 3;
|
||||
const uint8_t U2F_RESBUF_ID_VENDOR_NAME = 4;
|
||||
const uint8_t U2F_RESBUF_ID_DEVICE_NAME = 5;
|
||||
const uint8_t U2F_RESBUF_ID_FIRMWARE_MAJOR = 6;
|
||||
const uint8_t U2F_RESBUF_ID_FIRMWARE_MINOR = 7;
|
||||
const uint8_t U2F_RESBUF_ID_FIRMWARE_BUILD = 8;
|
||||
|
||||
const uint64_t U2F_FLAG_REQUIRE_RESIDENT_KEY = 1;
|
||||
const uint64_t U2F_FLAG_REQUIRE_USER_VERIFICATION = 2;
|
||||
|
||||
+47
-2
@@ -128,7 +128,25 @@ where
|
||||
{
|
||||
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
|
||||
let raw = sendrecv(dev, U2FHID_INIT, nonce)?;
|
||||
dev.set_cid(U2FHIDInitResp::read(&raw, 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(())
|
||||
}
|
||||
@@ -224,7 +242,7 @@ mod tests {
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::consts::CID_BROADCAST;
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
|
||||
const IN_HID_RPT_SIZE: usize = 64;
|
||||
const OUT_HID_RPT_SIZE: usize = 64;
|
||||
@@ -233,6 +251,7 @@ mod tests {
|
||||
cid: [u8; 4],
|
||||
reads: Vec<[u8; IN_HID_RPT_SIZE]>,
|
||||
writes: Vec<[u8; OUT_HID_RPT_SIZE + 1]>,
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl TestDevice {
|
||||
@@ -241,6 +260,7 @@ mod tests {
|
||||
cid: CID_BROADCAST,
|
||||
reads: vec![],
|
||||
writes: vec![],
|
||||
dev_info: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +331,17 @@ mod tests {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
OUT_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, prop_name: &str) -> io::Result<String> {
|
||||
Ok(format!("{} not implemented", prop_name))
|
||||
}
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +370,13 @@ mod tests {
|
||||
|
||||
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, 0x01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -409,4 +447,11 @@ mod tests {
|
||||
assert_eq!(result, &data);
|
||||
assert_eq!(status, SW_NO_ERROR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_property() {
|
||||
let device = platform::TestDevice::new();
|
||||
|
||||
assert_eq!(device.get_property("a").unwrap(), "a not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
+64
-9
@@ -2,17 +2,21 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::{cmp, io};
|
||||
use std::{cmp, fmt, io, str};
|
||||
|
||||
use crate::consts::*;
|
||||
use crate::util::io_err;
|
||||
|
||||
use log;
|
||||
|
||||
fn trace_hex(data: &[u8]) {
|
||||
pub fn to_hex(data: &[u8], joiner: &str) -> String {
|
||||
let parts: Vec<String> = data.iter().map(|byte| format!("{:02x}", byte)).collect();
|
||||
parts.join(joiner)
|
||||
}
|
||||
|
||||
pub fn trace_hex(data: &[u8]) {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
let parts: Vec<String> = data.iter().map(|byte| format!("{:02x}", byte)).collect();
|
||||
trace!("USB send: {}", parts.join(""));
|
||||
trace!("USB send: {}", to_hex(data, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +41,10 @@ pub trait U2FDevice {
|
||||
fn out_cont_data_size(&self) -> usize {
|
||||
self.out_rpt_size() - CONT_HEADER_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, prop_name: &str) -> io::Result<String>;
|
||||
fn get_device_info(&self) -> U2FDeviceInfo;
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo);
|
||||
}
|
||||
|
||||
// Init structure for U2F Communications. Tells the receiver what channel
|
||||
@@ -157,10 +165,17 @@ impl U2FHIDCont {
|
||||
//
|
||||
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
|
||||
// html#u2fhid_init
|
||||
pub struct U2FHIDInitResp {}
|
||||
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: u8,
|
||||
}
|
||||
|
||||
impl U2FHIDInitResp {
|
||||
pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<[u8; 4]> {
|
||||
pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<U2FHIDInitResp> {
|
||||
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
|
||||
|
||||
if data.len() != INIT_NONCE_SIZE + 9 {
|
||||
@@ -171,9 +186,21 @@ impl U2FHIDInitResp {
|
||||
return Err(io_err("invalid nonce"));
|
||||
}
|
||||
|
||||
let mut cid = [0u8; 4];
|
||||
cid.copy_from_slice(&data[INIT_NONCE_SIZE..INIT_NONCE_SIZE + 4]);
|
||||
Ok(cid)
|
||||
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: data[INIT_NONCE_SIZE + 8],
|
||||
};
|
||||
|
||||
Ok(rsp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +217,7 @@ impl U2FAPDUHeader {
|
||||
|
||||
// Size of header + data + 2 zero bytes for maximum return size.
|
||||
let mut bytes = vec![0u8; U2FAPDUHEADER_SIZE + data.len() + 2];
|
||||
// cla is always 0 for our requirements
|
||||
bytes[1] = ins;
|
||||
bytes[2] = p1;
|
||||
// p2 is always 0, at least, for our requirements.
|
||||
@@ -201,3 +229,30 @@ impl U2FAPDUHeader {
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
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: u8,
|
||||
}
|
||||
|
||||
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], ":"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -9,14 +9,14 @@ use std::os::windows::io::AsRawHandle;
|
||||
|
||||
use super::winapi::DeviceCapabilities;
|
||||
use crate::consts::{CID_BROADCAST, FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID, MAX_HID_RPT_SIZE};
|
||||
|
||||
use crate::u2ftypes::U2FDevice;
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: String,
|
||||
file: File,
|
||||
cid: [u8; 4],
|
||||
dev_info: Option<U2FDeviceInfo>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -26,6 +26,7 @@ impl Device {
|
||||
path,
|
||||
file,
|
||||
cid: CID_BROADCAST,
|
||||
dev_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,4 +80,18 @@ impl U2FDevice for Device {
|
||||
fn out_rpt_size(&self) -> usize {
|
||||
MAX_HID_RPT_SIZE
|
||||
}
|
||||
|
||||
fn get_property(&self, _prop_name: &str) -> io::Result<String> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
|
||||
fn get_device_info(&self) -> U2FDeviceInfo {
|
||||
// unwrap is okay, as dev_info must have already been set, else
|
||||
// a programmer error
|
||||
self.dev_info.clone().unwrap()
|
||||
}
|
||||
|
||||
fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
|
||||
self.dev_info = Some(dev_info);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user