Bug 1468349 - Web Authentication - Add FreeBSD Support r=jcj

Summary:
Upstream PR: https://github.com/jcjones/u2f-hid-rs/pull/62

* Extract hidproto module from linux::hidraw
Make the protocol parts independent of Linux code, in preparation for
adding FreeBSD support.

* Add FreeBSD (uhid + devd) support
Tested with a YubiKey 4.

Tags: #secure-revision

Bug #: 1468349

Differential Revision: https://phabricator.services.mozilla.com/D1636

MozReview-Commit-ID: 8NNWRgTEMn2

--HG--
extra : rebase_source : edf774f0a993a18b59b5f8aa10e0977d94ea1de8
This commit is contained in:
Greg V 2018-06-12 09:55:30 -07:00
Родитель bcc5036fe6
Коммит 6efd24a566
15 изменённых файлов: 584 добавлений и 165 удалений

Просмотреть файл

@ -1,11 +1,14 @@
[package]
name = "u2fhid"
version = "0.1.0"
version = "0.2.0"
authors = ["Kyle Machulis <kyle@nonpolynomial.com>", "J.C. Jones <jc@mozilla.com>", "Tim Taubert <ttaubert@mozilla.com>"]
[target.'cfg(target_os = "linux")'.dependencies]
libudev = "^0.2"
[target.'cfg(target_os = "freebsd")'.dependencies]
devd-rs = "0.2.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation-sys = "0.6.0"
core-foundation = "0.6.0"

Просмотреть файл

@ -5,7 +5,7 @@
This is a cross-platform library for interacting with U2F Security Key-type devices via Rust.
* **Supported Platforms**: Windows, Linux, and Mac OS X.
* **Supported Platforms**: Windows, Linux, FreeBSD, and macOS.
* **Supported HID Transports**: USB.
* **Supported Protocols**: [FIDO U2F over USB](https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html).

Просмотреть файл

@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
extern crate libc;
use std::ffi::{CString, OsString};
use std::io;
use std::io::{Read, Write};
use std::os::unix::prelude::*;
use consts::CID_BROADCAST;
use platform::uhid;
use u2ftypes::U2FDevice;
use util::from_unix_result;
#[derive(Debug)]
pub struct Device {
path: OsString,
fd: libc::c_int,
cid: [u8; 4],
}
impl Device {
pub fn new(path: OsString) -> io::Result<Self> {
let cstr = CString::new(path.as_bytes())?;
let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) };
let fd = from_unix_result(fd)?;
Ok(Self {
path,
fd,
cid: CID_BROADCAST,
})
}
pub fn is_u2f(&self) -> bool {
uhid::is_u2f_device(self.fd)
}
}
impl Drop for Device {
fn drop(&mut self) {
// Close the fd, ignore any errors.
let _ = unsafe { libc::close(self.fd) };
}
}
impl PartialEq for Device {
fn eq(&self, other: &Device) -> bool {
self.path == other.path
}
}
impl Read for Device {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let bufp = buf.as_mut_ptr() as *mut libc::c_void;
let rv = unsafe { libc::read(self.fd, bufp, buf.len()) };
from_unix_result(rv as usize)
}
}
impl Write for Device {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let report_id = buf[0] as i64;
// Skip report number when not using numbered reports.
let start = if report_id == 0x0 { 1 } else { 0 };
let data = &buf[start..];
let data_ptr = data.as_ptr() as *const libc::c_void;
let rv = unsafe { libc::write(self.fd, data_ptr, data.len()) };
from_unix_result(rv as usize + 1)
}
// USB HID writes don't buffer, so this will be a nop.
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl U2FDevice for Device {
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
&self.cid
}
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}
}

Просмотреть файл

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
pub mod device;
pub mod transaction;
mod monitor;
mod uhid;

Просмотреть файл

@ -0,0 +1,139 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 devd_rs;
use std::collections::HashMap;
use std::ffi::OsString;
use std::sync::Arc;
use std::{fs, io};
use runloop::RunLoop;
const POLL_TIMEOUT: usize = 100;
pub enum Event {
Add(OsString),
Remove(OsString),
}
impl Event {
fn from_devd(event: devd_rs::Event) -> Option<Self> {
match event {
devd_rs::Event::Attach {
ref dev,
parent: _,
location: _,
} if dev.starts_with("uhid") =>
{
Some(Event::Add(("/dev/".to_owned() + dev).into()))
}
devd_rs::Event::Detach {
ref dev,
parent: _,
location: _,
} if dev.starts_with("uhid") =>
{
Some(Event::Remove(("/dev/".to_owned() + dev).into()))
}
_ => None,
}
}
}
fn convert_error(e: devd_rs::Error) -> io::Error {
e.into()
}
pub struct Monitor<F>
where
F: Fn(OsString, &Fn() -> bool) + Sync,
{
runloops: HashMap<OsString, RunLoop>,
new_device_cb: Arc<F>,
}
impl<F> Monitor<F>
where
F: Fn(OsString, &Fn() -> bool) + Send + Sync + 'static,
{
pub fn new(new_device_cb: F) -> Self {
Self {
runloops: HashMap::new(),
new_device_cb: Arc::new(new_device_cb),
}
}
pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
let mut ctx = devd_rs::Context::new().map_err(convert_error)?;
// Iterate all existing devices.
for dev in fs::read_dir("/dev")? {
if let Ok(dev) = dev {
let filename_ = dev.file_name();
let filename = filename_.to_str().unwrap_or("");
if filename.starts_with("uhid") {
self.add_device(("/dev/".to_owned() + filename).into());
}
}
}
// Loop until we're stopped by the controlling thread, or fail.
while alive() {
// Wait for new events, break on failure.
match ctx.wait_for_event(POLL_TIMEOUT) {
Err(devd_rs::Error::Timeout) => (),
Err(e) => return Err(e.into()),
Ok(event) => {
if let Some(event) = Event::from_devd(event) {
self.process_event(event);
}
}
}
}
// Remove all tracked devices.
self.remove_all_devices();
Ok(())
}
fn process_event(&mut self, event: Event) {
match event {
Event::Add(path) => {
self.add_device(path);
}
Event::Remove(path) => {
self.remove_device(path);
}
}
}
fn add_device(&mut self, path: OsString) {
let f = self.new_device_cb.clone();
let key = path.clone();
let runloop = RunLoop::new(move |alive| {
if alive() {
f(path, alive);
}
});
if let Ok(runloop) = runloop {
self.runloops.insert(key, runloop);
}
}
fn remove_device(&mut self, path: OsString) {
if let Some(runloop) = self.runloops.remove(&path) {
runloop.cancel();
}
}
fn remove_all_devices(&mut self) {
while !self.runloops.is_empty() {
let path = self.runloops.keys().next().unwrap().clone();
self.remove_device(path);
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 platform::monitor::Monitor;
use runloop::RunLoop;
use std::ffi::OsString;
use util::OnceCallback;
pub struct Transaction {
// Handle to the thread loop.
thread: Option<RunLoop>,
}
impl Transaction {
pub fn new<F, T>(
timeout: u64,
callback: OnceCallback<T>,
new_device_cb: F,
) -> Result<Self, ::Error>
where
F: Fn(OsString, &Fn() -> bool) + Sync + Send + 'static,
T: 'static,
{
let thread = RunLoop::new_with_timeout(
move |alive| {
// Create a new device monitor.
let mut monitor = Monitor::new(new_device_cb);
// Start polling for new devices.
try_or!(monitor.run(alive), |_| callback.call(Err(::Error::Unknown)));
// Send an error, if the callback wasn't called already.
callback.call(Err(::Error::NotAllowed));
},
timeout,
).map_err(|_| ::Error::Unknown)?;
Ok(Self {
thread: Some(thread),
})
}
pub fn cancel(&mut self) {
// This must never be None.
self.thread.take().unwrap().cancel();
}
}

Просмотреть файл

@ -0,0 +1,92 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
extern crate libc;
use std::io;
use std::os::unix::io::RawFd;
use std::ptr;
use hidproto::*;
use util::from_unix_result;
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct GenDescriptor {
ugd_data: *mut u8,
ugd_lang_id: u16,
ugd_maxlen: u16,
ugd_actlen: u16,
ugd_offset: u16,
ugd_config_index: u8,
ugd_string_index: u8,
ugd_iface_index: u8,
ugd_altif_index: u8,
ugd_endpt_index: u8,
ugd_report_index: u8,
reserved: [u8; 16],
}
impl Default for GenDescriptor {
fn default() -> GenDescriptor {
GenDescriptor {
ugd_data: ptr::null_mut(),
ugd_lang_id: 0,
ugd_maxlen: 65535,
ugd_actlen: 0,
ugd_offset: 0,
ugd_config_index: 0,
ugd_string_index: 0,
ugd_iface_index: 0,
ugd_altif_index: 0,
ugd_endpt_index: 0,
ugd_report_index: 0,
reserved: [0; 16],
}
}
}
const IOWR: u32 = 0x40000000 | 0x80000000;
const IOCPARM_SHIFT: u32 = 13;
const IOCPARM_MASK: u32 = ((1 << IOCPARM_SHIFT) - 1);
const TYPESHIFT: u32 = 8;
const SIZESHIFT: u32 = 16;
macro_rules! ioctl {
($dir:expr, $name:ident, $ioty:expr, $nr:expr, $size:expr; $ty:ty) => {
pub unsafe fn $name(fd: libc::c_int, val: *mut $ty) -> io::Result<libc::c_int> {
let ioc = ($dir as u32)
| (($size as u32 & IOCPARM_MASK) << SIZESHIFT)
| (($ioty as u32) << TYPESHIFT)
| ($nr as u32);
from_unix_result(libc::ioctl(fd, ioc as libc::c_ulong, val))
}
};
}
// https://github.com/freebsd/freebsd/blob/master/sys/dev/usb/usb_ioctl.h
ioctl!(IOWR, usb_get_report_desc, b'U', 21, 32; /*struct*/ GenDescriptor);
fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
let mut desc = GenDescriptor::default();
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
desc.ugd_maxlen = desc.ugd_actlen;
let mut value = Vec::with_capacity(desc.ugd_actlen as usize);
unsafe {
value.set_len(desc.ugd_actlen as usize);
}
desc.ugd_data = value.as_mut_ptr();
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
Ok(ReportDescriptor { value })
}
pub fn is_u2f_device(fd: RawFd) -> bool {
match read_report_descriptor(fd) {
Ok(desc) => has_fido_usage(desc),
Err(_) => false, // Upon failure, just say it's not a U2F device.
}
}

Просмотреть файл

@ -0,0 +1,158 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
// Shared code for platforms that use raw HID access (Linux, FreeBSD, etc.)
use std::mem;
use consts::{FIDO_USAGE_U2FHID, FIDO_USAGE_PAGE};
// The 4 MSBs (the tag) are set when it's a long item.
const HID_MASK_LONG_ITEM_TAG: u8 = 0b11110000;
// The 2 LSBs denote the size of a short item.
const HID_MASK_SHORT_ITEM_SIZE: u8 = 0b00000011;
// The 6 MSBs denote the tag (4) and type (2).
const HID_MASK_ITEM_TAGTYPE: u8 = 0b11111100;
// tag=0000, type=10 (local)
const HID_ITEM_TAGTYPE_USAGE: u8 = 0b00001000;
// tag=0000, type=01 (global)
const HID_ITEM_TAGTYPE_USAGE_PAGE: u8 = 0b00000100;
pub struct ReportDescriptor {
pub value: Vec<u8>,
}
impl ReportDescriptor {
fn iter(self) -> ReportDescriptorIterator {
ReportDescriptorIterator::new(self)
}
}
#[derive(Debug)]
pub enum Data {
UsagePage { data: u32 },
Usage { data: u32 },
}
pub struct ReportDescriptorIterator {
desc: ReportDescriptor,
pos: usize,
}
impl ReportDescriptorIterator {
fn new(desc: ReportDescriptor) -> Self {
Self { desc, pos: 0 }
}
fn next_item(&mut self) -> Option<Data> {
let item = get_hid_item(&self.desc.value[self.pos..]);
if item.is_none() {
self.pos = self.desc.value.len(); // Close, invalid data.
return None;
}
let (tag_type, key_len, data) = item.unwrap();
// Advance if we have a valid item.
self.pos += key_len + data.len();
// We only check short items.
if key_len > 1 {
return None; // Check next item.
}
// Short items have max. length of 4 bytes.
assert!(data.len() <= mem::size_of::<u32>());
// Convert data bytes to a uint.
let data = read_uint_le(data);
match tag_type {
HID_ITEM_TAGTYPE_USAGE_PAGE => Some(Data::UsagePage { data }),
HID_ITEM_TAGTYPE_USAGE => Some(Data::Usage { data }),
_ => None,
}
}
}
impl Iterator for ReportDescriptorIterator {
type Item = Data;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.desc.value.len() {
return None;
}
self.next_item().or_else(|| self.next())
}
}
fn get_hid_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
if (buf[0] & HID_MASK_LONG_ITEM_TAG) == HID_MASK_LONG_ITEM_TAG {
get_hid_long_item(buf)
} else {
get_hid_short_item(buf)
}
}
fn get_hid_long_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
// A valid long item has at least three bytes.
if buf.len() < 3 {
return None;
}
let len = buf[1] as usize;
// Ensure that there are enough bytes left in the buffer.
if len > buf.len() - 3 {
return None;
}
Some((buf[2], 3 /* key length */, &buf[3..]))
}
fn get_hid_short_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
// This is a short item. The bottom two bits of the key
// contain the length of the data section (value) for this key.
let len = match buf[0] & HID_MASK_SHORT_ITEM_SIZE {
s @ 0...2 => s as usize,
_ => 4, /* _ == 3 */
};
// Ensure that there are enough bytes left in the buffer.
if len > buf.len() - 1 {
return None;
}
Some((
buf[0] & HID_MASK_ITEM_TAGTYPE,
1, /* key length */
&buf[1..1 + len],
))
}
fn read_uint_le(buf: &[u8]) -> u32 {
assert!(buf.len() <= 4);
// Parse the number in little endian byte order.
buf.iter().rev().fold(0, |num, b| (num << 8) | (*b as u32))
}
pub fn has_fido_usage(desc: ReportDescriptor) -> bool {
let mut usage_page = None;
let mut usage = None;
for data in desc.iter() {
match data {
Data::UsagePage { data } => usage_page = Some(data),
Data::Usage { data } => usage = Some(data),
}
// Check the values we found.
if let (Some(usage_page), Some(usage)) = (usage_page, usage) {
return usage_page == FIDO_USAGE_PAGE as u32 && usage == FIDO_USAGE_U2FHID as u32;
}
}
false
}

Просмотреть файл

@ -5,6 +5,9 @@
#[macro_use]
mod util;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub mod hidproto;
#[cfg(any(target_os = "linux"))]
extern crate libudev;
@ -12,6 +15,13 @@ extern crate libudev;
#[path = "linux/mod.rs"]
pub mod platform;
#[cfg(any(target_os = "freebsd"))]
extern crate devd_rs;
#[cfg(any(target_os = "freebsd"))]
#[path = "freebsd/mod.rs"]
pub mod platform;
#[cfg(any(target_os = "macos"))]
extern crate core_foundation_sys;
@ -26,7 +36,11 @@ pub mod platform;
#[path = "windows/mod.rs"]
pub mod platform;
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
#[cfg(
not(
any(target_os = "linux", target_os = "freebsd", target_os = "macos", target_os = "windows")
)
)]
#[path = "stub/mod.rs"]
pub mod platform;

Просмотреть файл

@ -8,22 +8,16 @@ use std::io;
use std::mem;
use std::os::unix::io::RawFd;
use consts::{FIDO_USAGE_U2FHID, FIDO_USAGE_PAGE};
use hidproto::*;
use util::{from_unix_result, io_err};
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct ReportDescriptor {
pub struct LinuxReportDescriptor {
size: ::libc::c_int,
value: [u8; 4096],
}
impl ReportDescriptor {
fn iter(self) -> ReportDescriptorIterator {
ReportDescriptorIterator::new(self)
}
}
const NRBITS: u32 = 8;
const TYPEBITS: u32 = 8;
@ -35,17 +29,6 @@ const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;
// The 4 MSBs (the tag) are set when it's a long item.
const HID_MASK_LONG_ITEM_TAG: u8 = 0b11110000;
// The 2 LSBs denote the size of a short item.
const HID_MASK_SHORT_ITEM_SIZE: u8 = 0b00000011;
// The 6 MSBs denote the tag (4) and type (2).
const HID_MASK_ITEM_TAGTYPE: u8 = 0b11111100;
// tag=0000, type=10 (local)
const HID_ITEM_TAGTYPE_USAGE: u8 = 0b00001000;
// tag=0000, type=01 (global)
const HID_ITEM_TAGTYPE_USAGE_PAGE: u8 = 0b00000100;
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/hid.h
const HID_MAX_DESCRIPTOR_SIZE: usize = 4096;
@ -53,8 +36,10 @@ macro_rules! ioctl {
($dir:expr, $name:ident, $ioty:expr, $nr:expr; $ty:ty) => {
pub unsafe fn $name(fd: libc::c_int, val: *mut $ty) -> io::Result<libc::c_int> {
let size = mem::size_of::<$ty>();
let ioc = (($dir as u32) << DIRSHIFT) | (($ioty as u32) << TYPESHIFT)
| (($nr as u32) << NRSHIFT) | ((size as u32) << SIZESHIFT);
let ioc = (($dir as u32) << DIRSHIFT)
| (($ioty as u32) << TYPESHIFT)
| (($nr as u32) << NRSHIFT)
| ((size as u32) << SIZESHIFT);
#[cfg(not(target_env = "musl"))]
type IocType = libc::c_ulong;
@ -68,115 +53,7 @@ macro_rules! ioctl {
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/hidraw.h
ioctl!(READ, hidiocgrdescsize, b'H', 0x01; ::libc::c_int);
ioctl!(READ, hidiocgrdesc, b'H', 0x02; /*struct*/ ReportDescriptor);
enum Data {
UsagePage { data: u32 },
Usage { data: u32 },
}
struct ReportDescriptorIterator {
desc: ReportDescriptor,
pos: usize,
}
impl ReportDescriptorIterator {
fn new(desc: ReportDescriptor) -> Self {
Self { desc, pos: 0 }
}
fn next_item(&mut self) -> Option<Data> {
let item = get_hid_item(&self.desc.value[self.pos..]);
if item.is_none() {
self.pos = self.desc.size as usize; // Close, invalid data.
return None;
}
let (tag_type, key_len, data) = item.unwrap();
// Advance if we have a valid item.
self.pos += key_len + data.len();
// We only check short items.
if key_len > 1 {
return None; // Check next item.
}
// Short items have max. length of 4 bytes.
assert!(data.len() <= mem::size_of::<u32>());
// Convert data bytes to a uint.
let data = read_uint_le(data);
match tag_type {
HID_ITEM_TAGTYPE_USAGE_PAGE => Some(Data::UsagePage { data }),
HID_ITEM_TAGTYPE_USAGE => Some(Data::Usage { data }),
_ => None,
}
}
}
impl Iterator for ReportDescriptorIterator {
type Item = Data;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.desc.size as usize {
return None;
}
self.next_item().or_else(|| self.next())
}
}
fn get_hid_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
if (buf[0] & HID_MASK_LONG_ITEM_TAG) == HID_MASK_LONG_ITEM_TAG {
get_hid_long_item(buf)
} else {
get_hid_short_item(buf)
}
}
fn get_hid_long_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
// A valid long item has at least three bytes.
if buf.len() < 3 {
return None;
}
let len = buf[1] as usize;
// Ensure that there are enough bytes left in the buffer.
if len > buf.len() - 3 {
return None;
}
Some((buf[2], 3 /* key length */, &buf[3..]))
}
fn get_hid_short_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
// This is a short item. The bottom two bits of the key
// contain the length of the data section (value) for this key.
let len = match buf[0] & HID_MASK_SHORT_ITEM_SIZE {
s @ 0...2 => s as usize,
_ => 4, /* _ == 3 */
};
// Ensure that there are enough bytes left in the buffer.
if len > buf.len() - 1 {
return None;
}
Some((
buf[0] & HID_MASK_ITEM_TAGTYPE,
1, /* key length */
&buf[1..1 + len],
))
}
fn read_uint_le(buf: &[u8]) -> u32 {
assert!(buf.len() <= 4);
// Parse the number in little endian byte order.
buf.iter().rev().fold(0, |num, b| (num << 8) | (*b as u32))
}
ioctl!(READ, hidiocgrdesc, b'H', 0x02; /*struct*/ LinuxReportDescriptor);
pub fn is_u2f_device(fd: RawFd) -> bool {
match read_report_descriptor(fd) {
@ -186,7 +63,7 @@ pub fn is_u2f_device(fd: RawFd) -> bool {
}
fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
let mut desc = ReportDescriptor {
let mut desc = LinuxReportDescriptor {
size: 0,
value: [0; HID_MAX_DESCRIPTOR_SIZE],
};
@ -197,24 +74,7 @@ fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
}
let _ = unsafe { hidiocgrdesc(fd, &mut desc)? };
Ok(desc)
}
fn has_fido_usage(desc: ReportDescriptor) -> bool {
let mut usage_page = None;
let mut usage = None;
for data in desc.iter() {
match data {
Data::UsagePage { data } => usage_page = Some(data),
Data::Usage { data } => usage = Some(data),
}
// Check the values we found.
if let (Some(usage_page), Some(usage)) = (usage_page, usage) {
return usage_page == FIDO_USAGE_PAGE as u32 && usage == FIDO_USAGE_U2FHID as u32;
}
}
false
let mut value = Vec::from(&desc.value[..]);
value.truncate(desc.size as usize);
Ok(ReportDescriptor { value })
}

Просмотреть файл

@ -8,15 +8,15 @@ extern crate core_foundation_sys;
extern crate libc;
use consts::{FIDO_USAGE_U2FHID, FIDO_USAGE_PAGE};
use core_foundation::dictionary::*;
use core_foundation::number::*;
use core_foundation::string::*;
use core_foundation_sys::base::*;
use core_foundation_sys::dictionary::*;
use core_foundation_sys::runloop::*;
use core_foundation_sys::string::*;
use core_foundation::dictionary::*;
use core_foundation::string::*;
use core_foundation::number::*;
use std::os::raw::c_void;
use std::ops::Deref;
use std::os::raw::c_void;
type IOOptionBits = u32;
@ -168,7 +168,7 @@ impl IOHIDDeviceMatcher {
CFString::from_static_string("DeviceUsagePage"),
CFNumber::from(FIDO_USAGE_PAGE as i32),
),
]);
]);
Self { dict }
}
}

Просмотреть файл

@ -5,13 +5,13 @@
extern crate libc;
extern crate log;
use core_foundation::base::TCFType;
use core_foundation_sys::base::*;
use core_foundation_sys::runloop::*;
use core_foundation::base::TCFType;
use std::os::raw::c_void;
use platform::iokit::*;
use runloop::RunLoop;
use std::collections::HashMap;
use std::os::raw::c_void;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{io, slice};
use util::io_err;

Просмотреть файл

@ -5,9 +5,9 @@
extern crate libc;
use core_foundation_sys::runloop::*;
use std::os::raw::c_void;
use platform::iokit::{CFRunLoopEntryObserver, IOHIDDeviceRef, SendableRunLoop};
use platform::monitor::Monitor;
use std::os::raw::c_void;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use util::OnceCallback;

Просмотреть файл

@ -165,9 +165,7 @@ impl StateMachine {
// Aggregate distinct transports from all given credentials.
let transports = key_handles
.iter()
.fold(::AuthenticatorTransports::empty(), |t, k| {
t | k.transports
});
.fold(::AuthenticatorTransports::empty(), |t, k| t | k.transports);
// We currently only support USB. If the RP specifies transports
// and doesn't include USB it's probably lying.

Просмотреть файл

@ -46,6 +46,16 @@ pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
}
}
#[cfg(any(target_os = "freebsd"))]
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
if rv.is_negative() {
let errno = unsafe { *libc::__error() };
Err(io::Error::from_raw_os_error(errno))
} else {
Ok(rv)
}
}
pub fn io_err(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, msg)
}