diff --git a/Cargo.toml b/Cargo.toml index a11d0f7..8faf8dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ ram_storage = [] verbose = ["debug_ctap", "libtock_drivers/verbose_usb"] with_ctap1 = ["crypto/with_ctap1"] with_ctap2_1 = [] +with_nfc = ["libtock_drivers/with_nfc"] [dev-dependencies] elf2tab = "0.6.0" diff --git a/deploy.py b/deploy.py index 5598797..358355d 100755 --- a/deploy.py +++ b/deploy.py @@ -846,6 +846,13 @@ if __name__ == "__main__": help=("Compiles the OpenSK application with backward compatible " "support for CTAP2.1 protocol."), ) + main_parser.add_argument( + "--nfc", + action="append_const", + const="with_nfc", + dest="features", + help=("Compiles the OpenSK application with support for nfc."), + ) main_parser.add_argument( "--regen-keys", action="store_true", diff --git a/third_party/libtock-drivers/Cargo.toml b/third_party/libtock-drivers/Cargo.toml index 74fe7ac..41e5c32 100644 --- a/third_party/libtock-drivers/Cargo.toml +++ b/third_party/libtock-drivers/Cargo.toml @@ -14,3 +14,4 @@ libtock_core = { path = "../../third_party/libtock-rs/core" } [features] debug_ctap = [] verbose_usb = ["debug_ctap"] +with_nfc=[] diff --git a/third_party/libtock-drivers/src/lib.rs b/third_party/libtock-drivers/src/lib.rs index bf62d05..8b8983c 100644 --- a/third_party/libtock-drivers/src/lib.rs +++ b/third_party/libtock-drivers/src/lib.rs @@ -3,6 +3,8 @@ pub mod buttons; pub mod console; pub mod led; +#[cfg(feature = "with_nfc")] +pub mod nfc; pub mod result; pub mod rng; pub mod timer; diff --git a/third_party/libtock-drivers/src/nfc.rs b/third_party/libtock-drivers/src/nfc.rs new file mode 100644 index 0000000..a67e944 --- /dev/null +++ b/third_party/libtock-drivers/src/nfc.rs @@ -0,0 +1,103 @@ +use crate::result::TockResult; +use crate::util; +use core::cell::Cell; +use core::mem; +use libtock_core::{callback, syscalls}; + +const DRIVER_NUMBER: usize = 0x30003; + +mod command_nr { + pub const CHECK: usize = 0; + pub const TRANSMIT: usize = 1; + pub const RECEIVE: usize = 2; + pub const EMULATE: usize = 3; + pub const CONFIGURE: usize = 4; +} + +mod subscribe_nr { + pub const TRANSMIT: usize = 1; + pub const RECEIVE: usize = 2; +} + +mod allow_nr { + pub const TRANSMIT: usize = 1; + pub const RECEIVE: usize = 2; +} + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub struct RecvOp { + pub result_code: usize, + pub recv_amount: usize, +} + +pub struct NfcTag {} + +impl NfcTag { + /// Check the existence of an NFC driver. + pub fn setup() -> bool { + syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_ok() + } + + pub fn enable_emulation() -> bool { + NfcTag::emulate(true) + } + + pub fn disable_emulation() -> bool { + NfcTag::emulate(false) + } + + fn emulate(enabled: bool) -> bool { + syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0).is_ok() + } + + /// Configure the tag type command. + pub fn configure(tag_type: u8) -> bool { + syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0).is_ok() + } + + /// 1. Share with the driver a buffer. + /// 2. Subscribe to having a successful receive callback. + /// 3. Issue the request for reception. + pub fn receive(buf: &mut [u8; 256]) -> TockResult { + let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?; + // set callback with 2 arguments, to receive ReturnCode and RX Amount + let recv_data = Cell::new(None); + let mut callback = |result, amount| { + recv_data.set(Some(RecvOp { + result_code: result, + recv_amount: amount, + })) + }; + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::RECEIVE, + &mut callback, + )?; + syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?; + util::yieldk_for(|| recv_data.get().is_some()); + mem::drop(subscription); + mem::drop(result); + Ok(recv_data.get().unwrap()) + } + + /// 1. Share with the driver a buffer containing the app's reply. + /// 2. Subscribe to having a successful transmission callback. + /// 3. Issue the request for transmitting. + pub fn transmit(buf: &mut [u8], amount: usize) -> TockResult { + let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf)?; + // set callback with 1 argument, to receive ReturnCode + let result_code = Cell::new(None); + let mut callback = |result| result_code.set(Some(result)); + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::TRANSMIT, + &mut callback, + )?; + syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)?; + util::yieldk_for(|| result_code.get().is_some()); + mem::drop(subscription); + mem::drop(result); + Ok(result_code.get().unwrap()) + } +}