examples-features: Add a full CTAP example Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/Cargo.toml b/Cargo.toml index 5109187..002ccaa 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -25,6 +25,7 @@ # We pin the serde version because newer serde versions may not be compatible # with the nightly toolchain used by libtock-rs. serde = { version = "=1.0.114", default-features = false, features = ["derive"] } +ctap2-authenticator = { git = "https://gitlab.com/ctap2-authenticator/ctap2-authenticator.git" } [[example]] name = "alloc_error" @@ -32,6 +33,11 @@ required-features = ["alloc", "custom_alloc_error_handler"] [[example]] +name = "ctap_features" +path = "examples-features/ctap.rs" +required-features = ["alloc", "custom_alloc_error_handler"] + +[[example]] name = "ble_scanning" path = "examples-features/ble_scanning.rs" required-features = ["alloc"]
diff --git a/examples-features/ctap.rs b/examples-features/ctap.rs new file mode 100644 index 0000000..a6d2d57 --- /dev/null +++ b/examples-features/ctap.rs
@@ -0,0 +1,269 @@ +#![no_std] +#![feature(alloc_error_handler)] + +extern crate alloc; + +use core::alloc::Layout; +use core::cell::Cell; +use core::fmt::{Debug, Error, Formatter}; +use core::time::Duration; +use ctap2_authenticator::credentials::{RpCredential, UserCredential}; +use ctap2_authenticator::usbhid::{ + CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor, +}; +use ctap2_authenticator::Authenticator; +use ctap2_authenticator::{ + AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature, +}; +use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer}; +use libtock::println; +use libtock::result::TockResult; +use libtock::syscalls; + +/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext` +pub(crate) struct UsbKeyHidPlatform {} + +impl UsbKeyHidPlatform { + pub fn new() -> Self { + Self {} + } +} + +impl CtapHidPlatform for UsbKeyHidPlatform { + // Should be kept in sync with the Crates version if possible + const MAJOR_VERSION: u8 = 0; + const MINOR_VERSION: u8 = 0; + const BUILD_VERSION: u8 = 0; + const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities { + wink: true, + cbor: true, + msg: false, + }; + + fn wink(&mut self) {} + + fn cancel(&mut self) { + unimplemented!() + } + + fn keepalive_needed(&mut self) -> KeepaliveResponse { + unimplemented!() + } + + fn start_timer(&mut self) { + unimplemented!() + } + + fn has_timed_out(&mut self) -> bool { + unimplemented!() + } +} + +#[derive(Clone, Copy)] +pub struct HmacKeyCredential(pub(crate) [u8; 40]); + +impl AsRef<[u8]> for HmacKeyCredential { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Debug for HmacKeyCredential { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!( + f, + "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}", + &self.0[..32], + &self.0[32..] + ) + } +} + +impl PartialEq for HmacKeyCredential { + fn eq(&self, other: &Self) -> bool { + self.0[..] == other.0[..] + } +} + +impl Eq for HmacKeyCredential {} + +const ITERATOR_MAX_LENGTH: usize = 8; + +pub struct HmacCredentialQueue { + index: usize, + length: usize, + list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH], +} + +impl HmacCredentialQueue { + pub(crate) fn new() -> Self { + Self { + index: 0, + length: 0, + list: [None; ITERATOR_MAX_LENGTH], + } + } + + pub(crate) fn len(&self) -> usize { + self.length + } +} + +impl Iterator for HmacCredentialQueue { + type Item = (HmacKeyCredential, u32); + + fn next(&mut self) -> Option<Self::Item> { + if self.list[self.index].is_some() { + self.index += 1; + self.list[self.index - 1] + } else { + None + } + } +} + +#[derive(Debug)] +pub(crate) struct CtapPlatform; +impl AuthenticatorPlatform for CtapPlatform { + const AAGUID: [u8; 16] = [ + 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, + 0x7d, + ]; + const CERTIFICATE: Option<&'static [u8]> = None; + const MAX_MSG_LENGTH: u16 = 7609; + const SUPPORTED_OPTIONS: CtapOptions = CtapOptions { + plat: None, + rk: Some(true), + client_pin: None, + up: Some(true), + uv: Some(false), + }; + type CredentialId = HmacKeyCredential; + type PublicKeyBuffer = [u8; 32]; + type SignatureBuffer = [u8; 32]; + type CredentialIterator = HmacCredentialQueue; + + fn reset(&mut self) -> Result<(), ()> { + unimplemented!() + } + + fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool { + unimplemented!() + } + + fn locate_credentials( + &mut self, + _rp_id: &str, + list: Option<CredentialDescriptorList>, + ) -> (u16, Self::CredentialIterator) { + match list { + None => (0, HmacCredentialQueue::new()), + Some(_list) => { + let result = HmacCredentialQueue::new(); + + // TODO Add location + + (result.len() as u16, result) + } + } + } + + fn create_credential( + &mut self, + _rp: RpCredential<&[u8], &str>, + _user: UserCredential<&[u8], &str>, + ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) { + unimplemented!() + } + + fn attest( + &mut self, + _id: &Self::CredentialId, + _data: &[u8], + ) -> Option<Signature<Self::SignatureBuffer>> { + unimplemented!(); + } + + fn sign( + &mut self, + _id: &Self::CredentialId, + _data: &[u8], + ) -> Option<Signature<Self::SignatureBuffer>> { + unimplemented!(); + } + + fn start_timeout(&mut self) { + unimplemented!() + } + + fn has_timed_out(&mut self, _time: Duration) -> bool { + unimplemented!() + } +} + +#[alloc_error_handler] +unsafe fn alloc_error_handler(_: Layout) -> ! { + println!("alloc_error_handler called"); + loop { + syscalls::raw::yieldk(); + } +} + +#[libtock::main] +async fn main() -> TockResult<()> { + let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>; + let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE]; + let mut drivers = libtock::retrieve_drivers()?; + drivers.console.create_console(); + + println!("Starting CTAP feature example"); + + let ctap_driver = drivers.ctap.init_driver()?; + + let mut recv_buffer = CtapRecvBuffer::default(); + let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?; + + let mut send_buffer = CtapSendBuffer::default(); + let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?; + + static mut BUFFER: &'static mut [u8] = &mut [0; 2048]; + unsafe { + tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new())); + } + + let mut authenticator = Authenticator::create(CtapPlatform); + + let mut callback = |sent, _| { + let mut looping = true; + recv_buffer.read_bytes(&mut temp_buffer[..]); + + let temp_tp = tp.get_mut(); + let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None }; + + while looping { + looping = false; + let (maybe_request, maybe_data) = temp_tp.poll(poll_msg); + + if let Some(request) = maybe_request { + let response = authenticator.process(request); + temp_tp.response(response); + looping = true; + } + + if let Some(mut data) = maybe_data { + send_buffer.write_bytes(&mut data[0..]); + let _ = ctap_driver.send_data(); + } + + poll_msg = None; + } + let _ = ctap_driver.allow_receive(); + }; + + let _subscription = ctap_driver.subscribe(&mut callback)?; + ctap_driver.allow_receive()?; + + loop { + unsafe { syscalls::raw::yieldk() }; + } +}