Merge #244

244: Make binaries specify their stack size using a new stack_size!{} macro. r=alistair23 a=jrvanwhy

Prior to this PR, it was not possible for binaries to specify their own stack size. libtock-rs had a hardcoded stack size of 2KiB.

This is an alternative to #180, which had some pushback for requiring `libtock-rs`'s users to write low-level code.

Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2333df3..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# Disable bors on the bors temp branch
-branches:
-  except:
-    - staging.tmp
-    - trying.tmp
-
-language: rust
-rust:
-  - nightly-2020-04-06
-
-os:
-  - linux
-
-cache: cargo
-
-install:
-  - CI=y make -j8 setup
-  - rm -rf ~/opentitan* # Make sure this isn't left over, see https://github.com/tock/tock/pull/1978
-
-script:
-  - make test
diff --git a/Cargo.toml b/Cargo.toml
index 5109187..caeae02 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,16 @@
 # 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" }
+p256 = { version = "0.5.0" , default-features = false, features = ["arithmetic", "ecdsa", "ecdsa-core"] }
+subtle = { version = "2.3.0", default-features = false, features = ["i128"] }
+generic-array = { version = "0.14.3" }
+
+# We need to override this to allow builds for targets that don't support atomics.
+# Once a version newer then 0.4.11 is released we can update to use that.
+# See: https://github.com/rust-lang/log/releases
+[patch.crates-io]
+log = { git = "https://github.com/rust-lang/log.git", branch = "master" }
 
 [[example]]
 name = "alloc_error"
@@ -32,6 +42,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..3a66eff
--- /dev/null
+++ b/examples-features/ctap.rs
@@ -0,0 +1,507 @@
+//! This is a featured CTAP example
+//! WARNING! This currently uses unsound crypto operations
+//! This is only a demo and should not be used in real enviroments
+#![no_std]
+#![feature(alloc_error_handler)]
+
+extern crate alloc;
+
+use core::alloc::Layout;
+use core::cell::Cell;
+use core::convert::TryInto;
+use core::fmt::{self, 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 generic_array::GenericArray;
+use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
+use libtock::hmac::{HmacDataBuffer, HmacDestBuffer, HmacDriverFactory, HmacKeyBuffer};
+use libtock::println;
+use libtock::result::TockResult;
+use libtock::syscalls;
+use p256::ecdsa::{signature::Signer, SigningKey};
+use p256::elliptic_curve::ff::PrimeField;
+use p256::{Scalar, SecretKey};
+use subtle::{Choice, ConditionallySelectable};
+
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+pub struct PrivateKey(Scalar);
+
+impl ConditionallySelectable for PrivateKey {
+    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
+        Self(ConditionallySelectable::conditional_select(
+            &a.0, &b.0, choice,
+        ))
+    }
+}
+
+impl Default for PrivateKey {
+    fn default() -> Self {
+        Self(Scalar::default())
+    }
+}
+
+impl PrivateKey {
+    pub fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
+        Scalar::from_repr(GenericArray::clone_from_slice(bytes)).map(|s| Self(s))
+    }
+}
+
+/// 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) {
+        println!("cancel");
+        unimplemented!()
+    }
+
+    fn keepalive_needed(&mut self) -> KeepaliveResponse {
+        println!("keepalive_needed");
+        unimplemented!()
+    }
+
+    fn start_timer(&mut self) {
+        println!("start_timer");
+    }
+
+    fn has_timed_out(&mut self) -> bool {
+        println!("has_timed_out");
+        false
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct HmacKeyCredential(pub(crate) [u8; 40]);
+
+impl HmacKeyCredential {
+    fn new(mac: &[u8; 32], nonce: [u8; 8]) -> Self {
+        let mut new = [0; 40];
+        new[..32].copy_from_slice(&mac[..]);
+        new[32..].copy_from_slice(&nonce[..]);
+        Self(new)
+    }
+
+    fn get_mac(&self) -> [u8; 32] {
+        let mut mac = [0; 32];
+        mac.copy_from_slice(&self.0[..32]);
+        mac
+    }
+}
+
+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 push(&mut self, credential: HmacKeyCredential, counter: u32) -> Result<(), ()> {
+        if self.length < ITERATOR_MAX_LENGTH {
+            self.list[self.length] = Some((credential, counter));
+            self.length += 1;
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+
+    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
+        }
+    }
+}
+
+pub(crate) struct CtapPlatform {
+    hmac: HmacDriverFactory,
+}
+
+impl CtapPlatform {
+    fn new(hmac: HmacDriverFactory) -> Self {
+        Self { hmac }
+    }
+}
+
+impl fmt::Debug for CtapPlatform {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        Ok(())
+    }
+}
+
+impl CtapPlatform {
+    fn generate_key_seed(
+        &mut self,
+        rp_id: &str,
+        nonce: &[u8; 8],
+    ) -> [u8; libtock::hmac::DEST_BUFFER_SIZE] {
+        let hmac_driver;
+        match self.hmac.init_driver() {
+            Err(_) => {
+                panic!("Hmac init error");
+            }
+            Ok(driver) => {
+                hmac_driver = driver;
+            }
+        }
+
+        let mut key_buffer = HmacKeyBuffer::default();
+        for (i, d) in rp_id.as_bytes().iter().enumerate() {
+            key_buffer[i] = *d;
+        }
+        // We need to make sure this isn't dropped straight away
+        let key_buffer_ret = hmac_driver.init_key_buffer(&mut key_buffer);
+        if key_buffer_ret.is_err() {
+            panic!("Hmac key buffer init error");
+        }
+
+        let mut data_buffer = HmacDataBuffer::default();
+        for (i, d) in nonce.iter().enumerate() {
+            data_buffer[i] = *d;
+        }
+        // We need to make sure this isn't dropped straight away
+        let data_buffer_ret = hmac_driver.init_data_buffer(&mut data_buffer);
+        if data_buffer_ret.is_err() {
+            panic!("Hmac data buffer init error");
+        }
+
+        let mut dest_buffer = HmacDestBuffer::default();
+        let seed_buffer;
+        match hmac_driver.init_dest_buffer(&mut dest_buffer) {
+            Err(_) => {
+                panic!("Hmac subscribe error");
+            }
+            Ok(buffer) => {
+                seed_buffer = buffer;
+            }
+        }
+
+        let mut callback = |_result, _digest| {};
+
+        // We need to make sure this isn't dropped straight away
+        let subscribe = hmac_driver.subscribe(&mut callback);
+        if subscribe.is_err() {
+            panic!("Hmac subscribe error");
+        }
+
+        if hmac_driver.run().is_err() {
+            panic!("Hmac run error");
+        }
+
+        // Yield waiting for the HMAC callback
+        unsafe {
+            syscalls::raw::yieldk();
+        }
+
+        let mut temp_buffer = [0; libtock::hmac::DEST_BUFFER_SIZE];
+        seed_buffer.read_bytes(&mut temp_buffer[..]);
+        temp_buffer
+    }
+
+    /// Generates a Credential from a CredentialId and checks that the Id is valid
+    fn checked_generate_hmac_cred(
+        &mut self,
+        rp_id: &str,
+        credential_id: &[u8],
+    ) -> Option<HmacKeyCredential> {
+        if credential_id.len() != 40 {
+            return None;
+        }
+
+        let received_mac = &credential_id[..32];
+
+        let seed_buffer = self.generate_key_seed(rp_id, &credential_id[32..40].try_into().unwrap());
+
+        let credential =
+            HmacKeyCredential::new(&seed_buffer, credential_id[32..].try_into().unwrap());
+
+        let generated_mac = &credential.0[..32];
+
+        if generated_mac == received_mac {
+            Some(credential)
+        } else {
+            None
+        }
+    }
+}
+
+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<(), ()> {
+        println!("reset");
+        unimplemented!()
+    }
+
+    fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
+        println!("check_exclude_list");
+        unimplemented!()
+    }
+
+    fn locate_credentials(
+        &mut self,
+        rp_id: &str,
+        list: Option<CredentialDescriptorList>,
+    ) -> (u16, Self::CredentialIterator) {
+        match list {
+            None => (0, HmacCredentialQueue::new()),
+            Some(list) => {
+                let mut result = HmacCredentialQueue::new();
+
+                for descriptor in list {
+                    match self.checked_generate_hmac_cred(rp_id, descriptor.get_id()) {
+                        None => (),
+                        Some(credential) => {
+                            match result.push(credential, 0) {
+                                Ok(()) => (),
+                                // NOTE: We just truncate if we don't have enough space to store all
+                                // fitting credentials
+                                Err(()) => break,
+                            }
+                        }
+                    }
+                }
+
+                (result.len() as u16, result)
+            }
+        }
+    }
+
+    fn create_credential(
+        &mut self,
+        rp: RpCredential<&[u8], &str>,
+        _user: UserCredential<&[u8], &str>,
+    ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
+        println!("create_credential");
+        let rp_id = core::str::from_utf8(rp.rp_id()).unwrap();
+        // This nonce is static!!!!
+        // This is really bad cryptowise, let's print a warning
+        // TODO: Convert this to generate a nonce from Tock's TRNG
+        println!("WARNING!!! The nonce is static, this key is insecure");
+        println!("WARNING!!! Do not use this anywhere important");
+        let nonce = [0; 8];
+
+        let seed_buffer = self.generate_key_seed(rp_id, &nonce);
+
+        let credential = HmacKeyCredential::new(&seed_buffer, nonce.try_into().unwrap());
+
+        let secret_key = SecretKey::from_bytes(&seed_buffer[0..32]).unwrap();
+
+        let pub_key = p256::EncodedPoint::from_secret_key(&secret_key, false);
+
+        let x: [u8; 32] = pub_key.as_bytes()[1..33].try_into().unwrap();
+        let y: [u8; 32] = pub_key.as_bytes()[33..].try_into().unwrap();
+
+        let ret_key = PublicKey::nistp256(x, y);
+
+        (credential, ret_key, 0)
+    }
+
+    fn attest(
+        &mut self,
+        id: &Self::CredentialId,
+        data: &[u8],
+    ) -> Option<Signature<Self::SignatureBuffer>> {
+        // TODO: Should attest be different then sign?
+        let attest = {
+            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
+
+            let signer = SigningKey::from(&secret_key);
+
+            let sig = signer.sign(data);
+
+            let mut r: [u8; 32] = [0; 32];
+            r.clone_from_slice(&sig.r().as_ref().to_bytes());
+            let mut s: [u8; 32] = [0; 32];
+            s.clone_from_slice(&sig.s().as_ref().to_bytes());
+
+            Signature::nistp256(r, s)
+        };
+
+        Some(attest)
+    }
+
+    fn sign(
+        &mut self,
+        id: &Self::CredentialId,
+        data: &[u8],
+    ) -> Option<Signature<Self::SignatureBuffer>> {
+        let attest = {
+            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
+            let signer = SigningKey::from(&secret_key);
+            let sig = signer.sign(data);
+
+            let mut r: [u8; 32] = [0; 32];
+            r.clone_from_slice(&sig.r().as_ref().to_bytes());
+            let mut s: [u8; 32] = [0; 32];
+            s.clone_from_slice(&sig.s().as_ref().to_bytes());
+
+            Signature::nistp256(r, s)
+        };
+
+        Some(attest)
+    }
+
+    fn start_timeout(&mut self) {
+        println!("start_timeout");
+        unimplemented!()
+    }
+
+    fn has_timed_out(&mut self, _time: Duration) -> bool {
+        println!("has_timed_out");
+        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::new(drivers.hmac));
+
+    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() };
+    }
+}