Skip to content

Extract in/out HID report sizes from descriptors #112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
// Allow dead code in this module, since it's all packet consts anyways.
#![allow(dead_code)]

pub const HID_RPT_SIZE: usize = 64;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used by all the other platforms as well, so we'll have to find/replace across the repo.

pub const MAX_HID_RPT_SIZE: usize = 64;
pub const U2FAPDUHEADER_SIZE: usize = 7;
pub const CID_BROADCAST: [u8; 4] = [0xff, 0xff, 0xff, 0xff];
pub const TYPE_MASK: u8 = 0x80;
pub const TYPE_INIT: u8 = 0x80;
pub const TYPE_CONT: u8 = 0x80;

// Size of data chunk expected in U2F Init USB HID Packets
pub const INIT_DATA_SIZE: usize = HID_RPT_SIZE - 7;
// Size of data chunk expected in U2F Cont USB HID Packets
pub const CONT_DATA_SIZE: usize = HID_RPT_SIZE - 5;
// Size of header in U2F Init USB HID Packets
pub const INIT_HEADER_SIZE: usize = 7;
// Size of header in U2F Cont USB HID Packets
pub const CONT_HEADER_SIZE: usize = 5;

pub const PARAMETER_SIZE: usize = 32;

Expand All @@ -31,16 +31,16 @@ pub const U2FHID_FRAME_TIMEOUT: u32 = 500; // Default frame timeout in ms
pub const U2FHID_TRANS_TIMEOUT: u32 = 3000; // Default message timeout in ms

// U2FHID native commands
pub const U2FHID_PING: u8 = (TYPE_INIT | 0x01); // Echo data through local processor only
pub const U2FHID_MSG: u8 = (TYPE_INIT | 0x03); // Send U2F message frame
pub const U2FHID_LOCK: u8 = (TYPE_INIT | 0x04); // Send lock channel command
pub const U2FHID_INIT: u8 = (TYPE_INIT | 0x06); // Channel initialization
pub const U2FHID_WINK: u8 = (TYPE_INIT | 0x08); // Send device identification wink
pub const U2FHID_ERROR: u8 = (TYPE_INIT | 0x3f); // Error response
pub const U2FHID_PING: u8 = TYPE_INIT | 0x01; // Echo data through local processor only
pub const U2FHID_MSG: u8 = TYPE_INIT | 0x03; // Send U2F message frame
pub const U2FHID_LOCK: u8 = TYPE_INIT | 0x04; // Send lock channel command
pub const U2FHID_INIT: u8 = TYPE_INIT | 0x06; // Channel initialization
pub const U2FHID_WINK: u8 = TYPE_INIT | 0x08; // Send device identification wink
pub const U2FHID_ERROR: u8 = TYPE_INIT | 0x3f; // Error response

// U2FHID_MSG commands
pub const U2F_VENDOR_FIRST: u8 = (TYPE_INIT | 0x40); // First vendor defined command
pub const U2F_VENDOR_LAST: u8 = (TYPE_INIT | 0x7f); // Last vendor defined command
pub const U2F_VENDOR_FIRST: u8 = TYPE_INIT | 0x40; // First vendor defined command
pub const U2F_VENDOR_LAST: u8 = TYPE_INIT | 0x7f; // Last vendor defined command
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
Expand Down
10 changes: 9 additions & 1 deletion src/freebsd/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::io;
use std::io::{Read, Write};
use std::os::unix::prelude::*;

use consts::CID_BROADCAST;
use consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
use platform::uhid;
use u2ftypes::U2FDevice;
use util::from_unix_result;
Expand Down Expand Up @@ -85,4 +85,12 @@ impl U2FDevice for Device {
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}

fn in_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}

fn out_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}
}
91 changes: 89 additions & 2 deletions src/hidproto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless, needless_lifetimes))]

use std::io;
use std::mem;

use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID, INIT_HEADER_SIZE, MAX_HID_RPT_SIZE};

// The 4 MSBs (the tag) are set when it's a long item.
const HID_MASK_LONG_ITEM_TAG: u8 = 0b1111_0000;
Expand All @@ -20,6 +21,12 @@ const HID_MASK_ITEM_TAGTYPE: u8 = 0b1111_1100;
const HID_ITEM_TAGTYPE_USAGE: u8 = 0b0000_1000;
// tag=0000, type=01 (global)
const HID_ITEM_TAGTYPE_USAGE_PAGE: u8 = 0b0000_0100;
// tag=1000, type=00 (main)
const HID_ITEM_TAGTYPE_INPUT: u8 = 0b1000_0000;
// tag=1001, type=00 (main)
const HID_ITEM_TAGTYPE_OUTPUT: u8 = 0b1001_0000;
// tag=1001, type=01 (global)
const HID_ITEM_TAGTYPE_REPORT_COUNT: u8 = 0b1001_0100;

pub struct ReportDescriptor {
pub value: Vec<u8>,
Expand All @@ -35,6 +42,9 @@ impl ReportDescriptor {
pub enum Data {
UsagePage { data: u32 },
Usage { data: u32 },
Input,
Output,
ReportCount { data: u32 },
}

pub struct ReportDescriptorIterator {
Expand Down Expand Up @@ -69,10 +79,12 @@ impl ReportDescriptorIterator {

// 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 }),
HID_ITEM_TAGTYPE_INPUT => Some(Data::Input),
HID_ITEM_TAGTYPE_OUTPUT => Some(Data::Output),
HID_ITEM_TAGTYPE_REPORT_COUNT => Some(Data::ReportCount { data }),
_ => None,
}
}
Expand Down Expand Up @@ -150,6 +162,7 @@ pub fn has_fido_usage(desc: ReportDescriptor) -> bool {
match data {
Data::UsagePage { data } => usage_page = Some(data),
Data::Usage { data } => usage = Some(data),
_ => {}
}

// Check the values we found.
Expand All @@ -161,3 +174,77 @@ pub fn has_fido_usage(desc: ReportDescriptor) -> bool {

false
}

pub fn read_hid_rpt_sizes(desc: ReportDescriptor) -> io::Result<(usize, usize)> {
let mut in_rpt_count = None;
let mut out_rpt_count = None;
let mut last_rpt_count = None;

for data in desc.iter() {
match data {
Data::ReportCount { data } => {
if last_rpt_count != None {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Duplicate HID_ReportCount",
));
}
last_rpt_count = Some(data as usize);
}
Data::Input => {
if last_rpt_count.is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"HID_Input should be preceded by HID_ReportCount",
));
}
if in_rpt_count.is_some() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Duplicate HID_ReportCount",
));
}
in_rpt_count = last_rpt_count;
last_rpt_count = None
}
Data::Output => {
if last_rpt_count.is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"HID_Output should be preceded by HID_ReportCount",
));
}
if out_rpt_count.is_some() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Duplicate HID_ReportCount",
));
}
out_rpt_count = last_rpt_count;
last_rpt_count = None;
}
_ => {}
}
}

match (in_rpt_count, out_rpt_count) {
(Some(in_count), Some(out_count)) => {
if in_count > INIT_HEADER_SIZE
&& in_count <= MAX_HID_RPT_SIZE
&& out_count > INIT_HEADER_SIZE
&& out_count <= MAX_HID_RPT_SIZE
{
Ok((in_count, out_count))
} else {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"Report size is too small or too large",
))
}
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"Failed to extract report sizes from report descriptor",
)),
}
}
13 changes: 13 additions & 0 deletions src/linux/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use util::from_unix_result;
pub struct Device {
path: OsString,
fd: libc::c_int,
in_rpt_size: usize,
out_rpt_size: usize,
cid: [u8; 4],
}

Expand All @@ -26,9 +28,12 @@ impl Device {
let cstr = CString::new(path.as_bytes())?;
let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) };
let fd = from_unix_result(fd)?;
let (in_rpt_size, out_rpt_size) = hidraw::read_hid_rpt_sizes_or_defaults(fd);
Ok(Self {
path,
fd,
in_rpt_size,
out_rpt_size,
cid: CID_BROADCAST,
})
}
Expand Down Expand Up @@ -80,4 +85,12 @@ impl U2FDevice for Device {
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}

fn in_rpt_size(&self) -> usize {
self.in_rpt_size
}

fn out_rpt_size(&self) -> usize {
self.out_rpt_size
}
}
15 changes: 15 additions & 0 deletions src/linux/hidraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::io;
use std::os::unix::io::RawFd;

use super::hidwrapper::{_HIDIOCGRDESC, _HIDIOCGRDESCSIZE};
use consts::MAX_HID_RPT_SIZE;
use hidproto::*;
use util::{from_unix_result, io_err};

Expand Down Expand Up @@ -47,6 +48,20 @@ pub fn is_u2f_device(fd: RawFd) -> bool {
}
}

pub fn read_hid_rpt_sizes_or_defaults(fd: RawFd) -> (usize, usize) {
let default_rpt_sizes = (MAX_HID_RPT_SIZE, MAX_HID_RPT_SIZE);
let desc = read_report_descriptor(fd);
if let Ok(desc) = desc {
if let Ok(rpt_sizes) = read_hid_rpt_sizes(desc) {
rpt_sizes
} else {
default_rpt_sizes
}
} else {
default_rpt_sizes
}
}

fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
let mut desc = LinuxReportDescriptor {
size: 0,
Expand Down
10 changes: 9 additions & 1 deletion src/macos/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

extern crate log;

use consts::{CID_BROADCAST, HID_RPT_SIZE};
use consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
use core_foundation::base::*;
use platform::iokit::*;
use std::convert::TryInto;
Expand Down Expand Up @@ -100,4 +100,12 @@ impl U2FDevice for Device {
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}

fn in_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}

fn out_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}
}
10 changes: 9 additions & 1 deletion src/openbsd/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::ffi::OsString;
use std::io::{Read, Result, Write};
use std::mem;

use consts::CID_BROADCAST;
use consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
use platform::monitor::FidoDev;
use u2ftypes::U2FDevice;
use util::{from_unix_result, io_err};
Expand Down Expand Up @@ -120,4 +120,12 @@ impl U2FDevice for Device {
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}

fn in_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}

fn out_rpt_size(&self) -> usize {
MAX_HID_RPT_SIZE
}
}
8 changes: 8 additions & 0 deletions src/stub/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,12 @@ impl U2FDevice for Device {
fn set_cid(&mut self, cid: [u8; 4]) {
panic!("not implemented");
}

fn in_rpt_size(&self) -> usize {
panic!("not implemented");
}

fn out_rpt_size(&self) -> usize {
panic!("not implemented");
}
}
21 changes: 16 additions & 5 deletions src/u2fprotocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,16 @@ mod tests {
use std::io;
use std::io::{Read, Write};

use consts::{CID_BROADCAST, HID_RPT_SIZE};
use consts::CID_BROADCAST;
use u2ftypes::U2FDevice;

const IN_HID_RPT_SIZE: usize = 64;
const OUT_HID_RPT_SIZE: usize = 64;

pub struct TestDevice {
cid: [u8; 4],
reads: Vec<[u8; HID_RPT_SIZE]>,
writes: Vec<[u8; HID_RPT_SIZE + 1]>,
reads: Vec<[u8; IN_HID_RPT_SIZE]>,
writes: Vec<[u8; OUT_HID_RPT_SIZE + 1]>,
}

impl TestDevice {
Expand All @@ -243,7 +246,7 @@ mod tests {

pub fn add_write(&mut self, packet: &[u8], fill_value: u8) {
// Add one to deal with record index check
let mut write = [fill_value; HID_RPT_SIZE + 1];
let mut write = [fill_value; OUT_HID_RPT_SIZE + 1];
// Make sure we start with a 0, for HID record index
write[0] = 0;
// Clone packet data in at 1, since front is padded with HID record index
Expand All @@ -252,7 +255,7 @@ mod tests {
}

pub fn add_read(&mut self, packet: &[u8], fill_value: u8) {
let mut read = [fill_value; HID_RPT_SIZE];
let mut read = [fill_value; IN_HID_RPT_SIZE];
read[..packet.len()].clone_from_slice(packet);
self.reads.push(read);
}
Expand Down Expand Up @@ -300,6 +303,14 @@ mod tests {
fn set_cid(&mut self, cid: [u8; 4]) {
self.cid = cid;
}

fn in_rpt_size(&self) -> usize {
IN_HID_RPT_SIZE
}

fn out_rpt_size(&self) -> usize {
OUT_HID_RPT_SIZE
}
}
}

Expand Down
Loading