Start to support any report size

This commit is contained in:
Arda Xi 2018-12-28 16:58:12 +01:00
parent 8678eccb9a
commit 0fa8d420c9
4 changed files with 125 additions and 102 deletions

View File

@ -12,4 +12,5 @@ pub struct DeviceInfo {
pub path: PathBuf, pub path: PathBuf,
pub usage_page: u16, pub usage_page: u16,
pub usage: u16, pub usage: u16,
pub report_size: u16,
} }

View File

@ -83,5 +83,6 @@ fn path_to_device(path: &PathBuf) -> io::Result<DeviceInfo> {
path: device_path, path: device_path,
usage_page, usage_page,
usage, usage,
report_size,
}) })
} }

View File

@ -61,14 +61,13 @@ use std::cmp;
use std::u8; use std::u8;
use std::u16; use std::u16;
use std::fs; use std::fs;
use std::io::{Read, Write, Cursor}; use std::io::{Write, Cursor};
use failure::{Fail, ResultExt}; use failure::{Fail, ResultExt};
use rand::prelude::*; use rand::prelude::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use self::hid_linux as hid; use self::hid_linux as hid;
use self::packet::CtapCommand; use self::packet::CtapCommand;
use self::packet::Packet;
pub use self::error::*; pub use self::error::*;
static BROADCAST_CID: [u8; 4] = [0xff, 0xff, 0xff, 0xff]; static BROADCAST_CID: [u8; 4] = [0xff, 0xff, 0xff, 0xff];
@ -135,6 +134,10 @@ impl FidoDevice {
if response.len() < 17 || response[0..8] != nonce { if response.len() < 17 || response[0..8] != nonce {
Err(FidoErrorKind::ParseCtap)? Err(FidoErrorKind::ParseCtap)?
} }
let flags = response[16];
if flags & 0x04 == 0 {
Err(FidoErrorKind::DeviceUnsupported)?
}
self.channel_id.copy_from_slice(&response[8..12]); self.channel_id.copy_from_slice(&response[8..12]);
let response = match self.cbor(cbor::Request::GetInfo)? { let response = match self.cbor(cbor::Request::GetInfo)? {
cbor::Response::GetInfo(resp) => resp, cbor::Response::GetInfo(resp) => resp,
@ -352,21 +355,13 @@ impl FidoDevice {
let to_send = payload.len() as u16; let to_send = payload.len() as u16;
let max_payload = (self.packet_size - 7) as usize; let max_payload = (self.packet_size - 7) as usize;
let (frame, payload) = payload.split_at(cmp::min(payload.len(), max_payload)); let (frame, payload) = payload.split_at(cmp::min(payload.len(), max_payload));
{ packet::write_init_packet(&mut self.device, 64, &self.channel_id, cmd, to_send, frame)?;
let packet = packet::InitPacket::new(&self.channel_id, cmd, to_send, frame);
self.device.write(packet.to_wire_format()).context(
FidoErrorKind::WritePacket,
)?;
}
if payload.is_empty() { if payload.is_empty() {
return Ok(()); return Ok(());
} }
let max_payload = (self.packet_size - 5) as usize; let max_payload = (self.packet_size - 5) as usize;
for (seq, frame) in (0..u8::MAX).zip(payload.chunks(max_payload)) { for (seq, frame) in (0..u8::MAX).zip(payload.chunks(max_payload)) {
let packet = packet::ContPacket::new(&self.channel_id, seq, frame); packet::write_cont_packet(&mut self.device, 64, &self.channel_id, seq, frame)?;
self.device.write(packet.to_wire_format()).context(
FidoErrorKind::WritePacket,
)?;
} }
self.device.flush().context(FidoErrorKind::WritePacket)?; self.device.flush().context(FidoErrorKind::WritePacket)?;
Ok(()) Ok(())
@ -374,43 +369,33 @@ impl FidoDevice {
fn receive(&mut self, cmd: &CtapCommand) -> FidoResult<Vec<u8>> { fn receive(&mut self, cmd: &CtapCommand) -> FidoResult<Vec<u8>> {
let mut first_packet: Option<packet::InitPacket> = None; let mut first_packet: Option<packet::InitPacket> = None;
let mut packet_size = 0;
while first_packet.is_none() { while first_packet.is_none() {
let mut buf = [0; 64]; let packet = packet::InitPacket::from_reader(&mut self.device, 64)?;
packet_size = self.device.read(&mut buf).context( if packet.cmd == CtapCommand::Error {
FidoErrorKind::ReadPacket,
)?;
let packet = packet::InitPacket::from_wire_format(&buf[0..packet_size]);
if packet.cmd() == CtapCommand::Error {
Err( Err(
packet::CtapError::from_u8(packet.payload()[0]) packet::CtapError::from_u8(packet.payload[0])
.unwrap_or(packet::CtapError::Other) .unwrap_or(packet::CtapError::Other)
.context(FidoErrorKind::ParseCtap), .context(FidoErrorKind::ParseCtap),
)? )?
} }
if packet.cid() == self.channel_id && &packet.cmd() == cmd { if packet.cid == self.channel_id && &packet.cmd == cmd {
first_packet = Some(packet); first_packet = Some(packet);
} }
} }
let first_packet = first_packet.unwrap(); let first_packet = first_packet.unwrap();
let mut data = first_packet.payload()[0..(packet_size - 7)].to_vec(); let mut data = first_packet.payload;
let mut to_read = (first_packet.size() as isize) - data.len() as isize; let mut to_read = (first_packet.size as isize) - data.len() as isize;
let mut seq = 0; let mut seq = 0;
while to_read > 0 { while to_read > 0 {
let mut buf = [0; 64]; let packet = packet::ContPacket::from_reader(&mut self.device, 64, to_read as usize)?;
let packet_size = self.device.read(&mut buf).context( if packet.cid != self.channel_id {
FidoErrorKind::ReadPacket,
)?;
let packet = packet::ContPacket::from_wire_format(&buf[0..packet_size]);
if packet.cid() != self.channel_id {
continue; continue;
} }
if packet.seq() != seq { if packet.seq != seq {
Err(FidoErrorKind::InvalidSequence)? Err(FidoErrorKind::InvalidSequence)?
} }
let payload_size = packet_size - 5; to_read -= packet.payload.len() as isize;
to_read -= payload_size as isize; data.extend(&packet.payload);
data.extend(&packet.payload()[0..payload_size]);
seq += 1; seq += 1;
} }
Ok(data) Ok(data)

View File

@ -5,6 +5,10 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be // http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms. // copied, modified, or distributed except according to those terms.
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
use failure::ResultExt;
use super::error::*;
use std::io::{Read, Write};
static FRAME_INIT: u8 = 0x80; static FRAME_INIT: u8 = 0x80;
@ -55,89 +59,121 @@ pub enum CtapError {
Other = 0x7F, Other = 0x7F,
} }
pub trait Packet { pub fn write_init_packet<W: Write>(
fn from_wire_format(data: &[u8]) -> Self; mut writer: W,
report_size: usize,
fn to_wire_format(&self) -> &[u8]; cid: &[u8],
cmd: &CtapCommand,
size: u16,
payload: &[u8],
) -> FidoResult<()> {
if cid.len() != 4 {
Err(FidoErrorKind::WritePacket)?
}
let mut packet = Vec::with_capacity(report_size);
packet.push(0);
packet.extend_from_slice(cid);
packet.push(FRAME_INIT | cmd.to_wire_format());
packet.push(((size >> 8) & 0xff) as u8);
packet.push((size & 0xff) as u8);
packet.extend_from_slice(payload);
if packet.len() > report_size + 1 {
Err(FidoErrorKind::WritePacket)?
}
packet.resize(report_size + 1, 0);
writer.write_all(&packet).context(
FidoErrorKind::WritePacket,
)?;
Ok(())
} }
pub struct InitPacket(pub [u8; 65]); pub struct InitPacket {
pub cid: [u8; 4],
pub cmd: CtapCommand,
pub size: u16,
pub payload: Vec<u8>,
}
impl InitPacket { impl InitPacket {
pub fn new(cid: &[u8], cmd: &CtapCommand, size: u16, payload: &[u8]) -> InitPacket { pub fn from_reader<R: Read>(mut reader: R, report_size: usize) -> FidoResult<InitPacket> {
let mut packet = InitPacket([0; 65]); let mut buf = Vec::with_capacity(report_size);
packet.0[1..5].copy_from_slice(cid); buf.resize(report_size, 0);
packet.0[5] = FRAME_INIT | cmd.to_wire_format(); reader.read_exact(&mut buf[0..report_size]).context(
packet.0[6] = ((size >> 8) & 0xff) as u8; FidoErrorKind::ReadPacket,
packet.0[7] = (size & 0xff) as u8; )?;
packet.0[8..(payload.len() + 8)].copy_from_slice(payload); let mut cid = [0; 4];
packet cid.copy_from_slice(&buf[0..4]);
} let cmd = match CtapCommand::from_u8(buf[4] ^ FRAME_INIT) {
pub fn cid(&self) -> &[u8] {
&self.0[1..5]
}
pub fn cmd(&self) -> CtapCommand {
match CtapCommand::from_u8(self.0[5] ^ FRAME_INIT) {
Some(cmd) => cmd, Some(cmd) => cmd,
None => CtapCommand::Invalid, None => CtapCommand::Invalid,
} };
} let size = ((u16::from(buf[5])) << 8) | u16::from(buf[6]);
let payload_end = if (size as usize) >= (report_size - 7) {
pub fn size(&self) -> u16 { report_size
((u16::from(self.0[6])) << 8) | u16::from(self.0[7]) } else {
} size as usize + 7
};
pub fn payload(&self) -> &[u8] { let payload = buf.drain(7..payload_end).collect();
&self.0[8..65] Ok(InitPacket {
cid,
cmd,
size,
payload,
})
} }
} }
impl Packet for InitPacket { pub fn write_cont_packet<W: Write>(
fn from_wire_format(data: &[u8]) -> InitPacket { mut writer: W,
let mut packet = InitPacket([0; 65]); report_size: usize,
packet.0[1..65].copy_from_slice(data); cid: &[u8],
packet seq: u8,
payload: &[u8],
) -> FidoResult<()> {
if cid.len() != 4 {
Err(FidoErrorKind::WritePacket)?
} }
let mut packet = Vec::with_capacity(report_size);
fn to_wire_format(&self) -> &[u8] { packet.push(0);
&self.0 packet.extend_from_slice(cid);
packet.push(seq);
packet.extend_from_slice(payload);
if packet.len() > report_size + 1 {
Err(FidoErrorKind::WritePacket)?
} }
packet.resize(report_size + 1, 0);
writer.write_all(&packet).context(
FidoErrorKind::WritePacket,
)?;
Ok(())
} }
pub struct ContPacket(pub [u8; 65]); pub struct ContPacket {
pub cid: [u8; 4],
pub seq: u8,
pub payload: Vec<u8>,
}
impl ContPacket { impl ContPacket {
pub fn new(cid: &[u8], seq: u8, payload: &[u8]) -> ContPacket { pub fn from_reader<R: Read>(
let mut packet = ContPacket([0; 65]); mut reader: R,
packet.0[1..5].copy_from_slice(cid); report_size: usize,
packet.0[5] = seq; expected_data: usize,
packet.0[6..(payload.len() + 6)].copy_from_slice(payload); ) -> FidoResult<ContPacket> {
packet let mut buf = Vec::with_capacity(report_size);
} buf.resize(report_size, 0);
reader.read_exact(&mut buf[0..report_size]).context(
pub fn cid(&self) -> &[u8] { FidoErrorKind::ReadPacket,
&self.0[1..5] )?;
} let mut cid = [0; 4];
cid.copy_from_slice(&buf[0..4]);
pub fn seq(&self) -> u8 { let seq = buf[4];
self.0[5] let payload_end = if expected_data >= (report_size - 5) {
} report_size
} else {
pub fn payload(&self) -> &[u8] { expected_data + 5
&self.0[6..65] };
} let payload = buf.drain(5..payload_end).collect();
} Ok(ContPacket { cid, seq, payload })
impl Packet for ContPacket {
fn from_wire_format(data: &[u8]) -> ContPacket {
let mut packet = ContPacket([0; 65]);
packet.0[1..65].copy_from_slice(data);
packet
}
fn to_wire_format(&self) -> &[u8] {
&self.0
} }
} }