Start to support any report size
This commit is contained in:
parent
8678eccb9a
commit
0fa8d420c9
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
51
src/lib.rs
51
src/lib.rs
@ -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)
|
||||||
|
174
src/packet.rs
174
src/packet.rs
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user