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() };
+    }
+}