Initial commit
This commit is contained in:
296
src/ctap/hid/send.rs
Normal file
296
src/ctap/hid/send.rs
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{CtapHid, HidPacket, Message};
|
||||
|
||||
pub struct HidPacketIterator(Option<MessageSplitter>);
|
||||
|
||||
impl HidPacketIterator {
|
||||
pub fn new(message: Message) -> Option<HidPacketIterator> {
|
||||
let splitter = MessageSplitter::new(message);
|
||||
if splitter.is_some() {
|
||||
Some(HidPacketIterator(splitter))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn none() -> HidPacketIterator {
|
||||
HidPacketIterator(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for HidPacketIterator {
|
||||
type Item = HidPacket;
|
||||
|
||||
fn next(&mut self) -> Option<HidPacket> {
|
||||
match &mut self.0 {
|
||||
Some(splitter) => splitter.next(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageSplitter {
|
||||
message: Message,
|
||||
packet: HidPacket,
|
||||
seq: Option<u8>,
|
||||
i: usize,
|
||||
}
|
||||
|
||||
impl MessageSplitter {
|
||||
// Try to split this message into an iterator of HID packets. This fails if the message is too
|
||||
// long to fit into a sequence of HID packets (which is limited to 7609 bytes).
|
||||
pub fn new(message: Message) -> Option<MessageSplitter> {
|
||||
if message.payload.len() > 7609 {
|
||||
None
|
||||
} else {
|
||||
// Cache the CID, as it is constant for all packets in this message.
|
||||
let mut packet = [0; 64];
|
||||
packet[..4].copy_from_slice(&message.cid);
|
||||
|
||||
Some(MessageSplitter {
|
||||
message,
|
||||
packet,
|
||||
seq: None,
|
||||
i: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Copy as many bytes as possible from data to dst, and return how many bytes are copied.
|
||||
// Contrary to copy_from_slice, this doesn't require slices of the same length.
|
||||
// All unused bytes in dst are set to zero, as if the data was padded with zeros to match.
|
||||
fn consume_data(dst: &mut [u8], data: &[u8]) -> usize {
|
||||
let dst_len = dst.len();
|
||||
let data_len = data.len();
|
||||
|
||||
if data_len <= dst_len {
|
||||
// data fits in dst, copy all the bytes.
|
||||
dst[..data_len].copy_from_slice(data);
|
||||
for byte in dst[data_len..].iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
data_len
|
||||
} else {
|
||||
// Fill all of dst.
|
||||
dst.copy_from_slice(&data[..dst_len]);
|
||||
dst_len
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MessageSplitter {
|
||||
type Item = HidPacket;
|
||||
|
||||
fn next(&mut self) -> Option<HidPacket> {
|
||||
let payload_len = self.message.payload.len();
|
||||
match self.seq {
|
||||
None => {
|
||||
// First, send an initialization packet.
|
||||
self.packet[4] = self.message.cmd | CtapHid::TYPE_INIT_BIT;
|
||||
self.packet[5] = (payload_len >> 8) as u8;
|
||||
self.packet[6] = payload_len as u8;
|
||||
|
||||
self.seq = Some(0);
|
||||
self.i =
|
||||
MessageSplitter::consume_data(&mut self.packet[7..], &self.message.payload);
|
||||
Some(self.packet)
|
||||
}
|
||||
Some(seq) => {
|
||||
// Send the next continuation packet, if any.
|
||||
if self.i < payload_len {
|
||||
self.packet[4] = seq;
|
||||
self.seq = Some(seq + 1);
|
||||
self.i += MessageSplitter::consume_data(
|
||||
&mut self.packet[5..],
|
||||
&self.message.payload[self.i..],
|
||||
);
|
||||
Some(self.packet)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn assert_packet_output_equality(message: Message, expected_packets: Vec<HidPacket>) {
|
||||
let packets: Vec<HidPacket> = HidPacketIterator::new(message).unwrap().collect();
|
||||
assert_eq!(packets.len(), expected_packets.len());
|
||||
for (packet, expected_packet) in packets.iter().zip(expected_packets.iter()) {
|
||||
assert_eq!(packet as &[u8], expected_packet as &[u8]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_single_packet() {
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0x4C,
|
||||
payload: vec![0xAA, 0xBB],
|
||||
};
|
||||
let expected_packets: Vec<HidPacket> = vec![[
|
||||
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x02, 0xAA, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
]];
|
||||
assert_packet_output_equality(message, expected_packets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_big_single_packet() {
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0x4C,
|
||||
payload: vec![0xAA; 64 - 7],
|
||||
};
|
||||
let expected_packets: Vec<HidPacket> = vec![[
|
||||
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x39, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
]];
|
||||
assert_packet_output_equality(message, expected_packets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_two_packets() {
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0x4C,
|
||||
payload: vec![0xAA; 64 - 7 + 1],
|
||||
};
|
||||
let expected_packets: Vec<HidPacket> = vec![
|
||||
[
|
||||
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x3A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
],
|
||||
[
|
||||
0x12, 0x34, 0x56, 0x78, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
],
|
||||
];
|
||||
assert_packet_output_equality(message, expected_packets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_two_full_packets() {
|
||||
let mut payload = vec![0xAA; 64 - 7];
|
||||
payload.extend(vec![0xBB; 64 - 5]);
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0x4C,
|
||||
payload,
|
||||
};
|
||||
let expected_packets: Vec<HidPacket> = vec![
|
||||
[
|
||||
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x74, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
],
|
||||
[
|
||||
0x12, 0x34, 0x56, 0x78, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
],
|
||||
];
|
||||
assert_packet_output_equality(message, expected_packets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_max_packets() {
|
||||
let mut payload = vec![0xFF; 64 - 7];
|
||||
for i in 0..128 {
|
||||
payload.extend(vec![i + 1; 64 - 5]);
|
||||
}
|
||||
|
||||
// Sanity check for the length of the payload.
|
||||
assert_eq!((64 - 7) + 128 * (64 - 5), 0x1db9);
|
||||
assert_eq!(7609, 0x1db9);
|
||||
assert_eq!(payload.len(), 0x1db9);
|
||||
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0xAB,
|
||||
payload,
|
||||
};
|
||||
|
||||
let mut expected_packets = Vec::new();
|
||||
expected_packets.push([
|
||||
0x12, 0x34, 0x56, 0x78, 0xAB, 0x1D, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
]);
|
||||
for i in 0..128 {
|
||||
let mut packet: HidPacket = [0; 64];
|
||||
packet[0] = 0x12;
|
||||
packet[1] = 0x34;
|
||||
packet[2] = 0x56;
|
||||
packet[3] = 0x78;
|
||||
packet[4] = i;
|
||||
for byte in packet.iter_mut().skip(5) {
|
||||
*byte = i + 1;
|
||||
}
|
||||
expected_packets.push(packet);
|
||||
}
|
||||
|
||||
assert_packet_output_equality(message, expected_packets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_payload_one_too_large() {
|
||||
let payload = vec![0xFF; (64 - 7) + 128 * (64 - 5) + 1];
|
||||
assert_eq!(payload.len(), 0x1dba);
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0xAB,
|
||||
payload,
|
||||
};
|
||||
assert!(HidPacketIterator::new(message).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hid_packet_iterator_payload_way_too_large() {
|
||||
// Check that overflow of u16 doesn't bypass the size limit.
|
||||
let payload = vec![0xFF; 0x10000];
|
||||
let message = Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: 0xAB,
|
||||
payload,
|
||||
};
|
||||
assert!(HidPacketIterator::new(message).is_none());
|
||||
}
|
||||
|
||||
// TODO(kaczmarczyck) implement and test limits (maximum bytes and packets)
|
||||
}
|
||||
Reference in New Issue
Block a user