Vendor HID (#446)

* introduces vendor HID

* updates workflows with new feature

* feature renaming and variant covering
This commit is contained in:
kaczmarczyck
2022-03-15 14:41:48 +01:00
committed by GitHub
parent 7e7d5e38a1
commit 0b564d4a8a
11 changed files with 222 additions and 12 deletions

View File

@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::CtapState;
use crate::clock::{ClockInt, CtapInstant};
#[cfg(feature = "with_ctap1")]
use crate::ctap::ctap1;
@@ -21,7 +20,7 @@ use crate::ctap::hid::ChannelID;
use crate::ctap::hid::{
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
};
use crate::ctap::{Channel, TimedPermission};
use crate::ctap::{Channel, CtapState, TimedPermission};
use crate::env::Env;
use embedded_time::duration::Milliseconds;

View File

@@ -32,6 +32,8 @@ pub mod status_code;
mod storage;
mod timed_permission;
mod token_state;
#[cfg(feature = "vendor_hid")]
pub mod vendor_hid;
use self::client_pin::{ClientPin, PinPermission};
use self::command::{
@@ -136,6 +138,7 @@ pub enum Transport {
/// Corresponds to CTAP's USB transport.
MainHid,
/// No equivalent in CTAP, used for communication outside the specification.
#[cfg(feature = "vendor_hid")]
VendorHid,
}
@@ -151,6 +154,7 @@ pub enum Channel {
/// Corresponds to CTAP's USB transport.
MainHid(ChannelID),
/// No equivalent in CTAP, used for communication outside the specification.
#[cfg(feature = "vendor_hid")]
VendorHid(ChannelID),
}

174
src/ctap/vendor_hid.rs Normal file
View File

@@ -0,0 +1,174 @@
// Copyright 2022 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 crate::clock::CtapInstant;
use crate::ctap::hid::{
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
};
use crate::ctap::{Channel, CtapState};
use crate::env::Env;
/// Implements the non-standard command processing for HID.
///
/// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed.
pub struct VendorHid {
hid: CtapHid,
}
impl VendorHid {
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
pub fn new() -> Self {
let hid = CtapHid::new();
VendorHid { hid }
}
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
pub fn process_hid_packet(
&mut self,
env: &mut impl Env,
packet: &HidPacket,
now: CtapInstant,
ctap_state: &mut CtapState,
) -> HidPacketIterator {
if let Some(message) = self.hid.parse_packet(env, packet, now) {
let processed_message = self.process_message(env, message, now, ctap_state);
debug_ctap!(
env,
"Sending message through the second usage page: {:02x?}",
processed_message
);
CtapHid::split_message(processed_message)
} else {
HidPacketIterator::none()
}
}
/// Processes a message's commands that affect the protocol outside HID.
pub fn process_message(
&mut self,
env: &mut impl Env,
message: Message,
now: CtapInstant,
ctap_state: &mut CtapState,
) -> Message {
let cid = message.cid;
match message.cmd {
// There are no custom CTAP1 commands.
CtapHidCommand::Msg => CtapHid::error_message(cid, CtapHidError::InvalidCmd),
// The CTAP2 processing function multiplexes internally.
CtapHidCommand::Cbor => {
let response =
ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid), now);
Message {
cid,
cmd: CtapHidCommand::Cbor,
payload: response,
}
}
// Call Wink over the main HID.
CtapHidCommand::Wink => CtapHid::error_message(cid, CtapHidError::InvalidCmd),
// All other commands have already been processed, keep them as is.
_ => message,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::ctap::hid::ChannelID;
use crate::env::test::TestEnv;
fn new_initialized() -> (VendorHid, ChannelID) {
let (hid, cid) = CtapHid::new_initialized();
(VendorHid { hid }, cid)
}
#[test]
fn test_process_hid_packet() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let (mut vendor_hid, cid) = new_initialized();
let mut ping_packet = [0x00; 64];
ping_packet[..4].copy_from_slice(&cid);
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
let mut response = vendor_hid.process_hid_packet(
&mut env,
&ping_packet,
CtapInstant::new(0),
&mut ctap_state,
);
assert_eq!(response.next(), Some(ping_packet));
assert_eq!(response.next(), None);
}
#[test]
fn test_process_hid_packet_empty() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let (mut vendor_hid, cid) = new_initialized();
let mut cancel_packet = [0x00; 64];
cancel_packet[..4].copy_from_slice(&cid);
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
let mut response = vendor_hid.process_hid_packet(
&mut env,
&cancel_packet,
CtapInstant::new(0),
&mut ctap_state,
);
assert_eq!(response.next(), None);
}
#[test]
fn test_blocked_commands() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let (mut vendor_hid, cid) = new_initialized();
// Usually longer, but we don't parse them anyway.
let mut msg_packet = [0x00; 64];
msg_packet[..4].copy_from_slice(&cid);
msg_packet[4..7].copy_from_slice(&[0x83, 0x00, 0x00]);
let mut wink_packet = [0x00; 64];
wink_packet[..4].copy_from_slice(&cid);
wink_packet[4..7].copy_from_slice(&[0x88, 0x00, 0x00]);
let mut error_packet = [0x00; 64];
error_packet[..4].copy_from_slice(&cid);
error_packet[4..8].copy_from_slice(&[0xBF, 0x00, 0x01, 0x01]);
let mut response = vendor_hid.process_hid_packet(
&mut env,
&msg_packet,
CtapInstant::new(0),
&mut ctap_state,
);
assert_eq!(response.next(), Some(error_packet));
assert_eq!(response.next(), None);
let mut response = vendor_hid.process_hid_packet(
&mut env,
&wink_packet,
CtapInstant::new(0),
&mut ctap_state,
);
assert_eq!(response.next(), Some(error_packet));
assert_eq!(response.next(), None);
}
}

1
src/env/tock/mod.rs vendored
View File

@@ -58,6 +58,7 @@ impl UserPresence for TockEnv {
fn check(&mut self, channel: Channel) -> Result<(), Ctap2StatusCode> {
match channel {
Channel::MainHid(cid) => check_user_presence(self, cid),
#[cfg(feature = "vendor_hid")]
Channel::VendorHid(cid) => check_user_presence(self, cid),
}
}

View File

@@ -20,6 +20,8 @@ extern crate arrayref;
use crate::ctap::hid::{HidPacket, HidPacketIterator};
use crate::ctap::main_hid::MainHid;
#[cfg(feature = "vendor_hid")]
use crate::ctap::vendor_hid::VendorHid;
use crate::ctap::CtapState;
pub use crate::ctap::Transport;
use crate::env::Env;
@@ -56,6 +58,8 @@ pub struct Ctap<E: Env> {
env: E,
state: CtapState,
hid: MainHid,
#[cfg(feature = "vendor_hid")]
vendor_hid: VendorHid,
}
impl<E: Env> Ctap<E> {
@@ -65,7 +69,15 @@ impl<E: Env> Ctap<E> {
pub fn new(mut env: E, now: CtapInstant) -> Self {
let state = CtapState::new(&mut env, now);
let hid = MainHid::new();
Ctap { env, state, hid }
#[cfg(feature = "vendor_hid")]
let vendor_hid = VendorHid::new();
Ctap {
env,
state,
hid,
#[cfg(feature = "vendor_hid")]
vendor_hid,
}
}
pub fn state(&mut self) -> &mut CtapState {
@@ -87,7 +99,11 @@ impl<E: Env> Ctap<E> {
self.hid
.process_hid_packet(&mut self.env, packet, now, &mut self.state)
}
Transport::VendorHid => HidPacketIterator::none(),
#[cfg(feature = "vendor_hid")]
Transport::VendorHid => {
self.vendor_hid
.process_hid_packet(&mut self.env, packet, now, &mut self.state)
}
}
}