Move check_user_presence to env::tock
This commit is contained in:
234
src/env/tock.rs
vendored
234
src/env/tock.rs
vendored
@@ -1,35 +1,36 @@
|
|||||||
use crypto::rng256::TockRng256;
|
use crate::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
|
||||||
|
|
||||||
use crate::ctap::hid::ChannelID;
|
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crate::env::{Env, UserPresence};
|
use crate::env::{Env, UserPresence};
|
||||||
|
use core::cell::Cell;
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
use core::fmt::Write;
|
||||||
|
use crypto::rng256::TockRng256;
|
||||||
|
use libtock_core::result::{CommandError, EALREADY};
|
||||||
|
use libtock_drivers::buttons::{self, ButtonState};
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
use libtock_drivers::console::Console;
|
||||||
|
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||||
|
use libtock_drivers::timer::Duration;
|
||||||
|
use libtock_drivers::{led, timer, usb_ctap_hid};
|
||||||
|
|
||||||
pub struct TockEnv<CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> {
|
pub struct TockEnv {
|
||||||
rng: TockRng256,
|
rng: TockRng256,
|
||||||
check_user_presence: CheckUserPresence,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> TockEnv<CheckUserPresence> {
|
impl TockEnv {
|
||||||
pub fn new(check_user_presence: CheckUserPresence) -> Self {
|
pub fn new() -> Self {
|
||||||
let rng = TockRng256 {};
|
let rng = TockRng256 {};
|
||||||
TockEnv {
|
TockEnv { rng }
|
||||||
rng,
|
|
||||||
check_user_presence,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> UserPresence
|
impl UserPresence for TockEnv {
|
||||||
for TockEnv<CheckUserPresence>
|
|
||||||
{
|
|
||||||
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
||||||
(self.check_user_presence)(cid)
|
check_user_presence(cid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> Env
|
impl Env for TockEnv {
|
||||||
for TockEnv<CheckUserPresence>
|
|
||||||
{
|
|
||||||
type Rng = TockRng256;
|
type Rng = TockRng256;
|
||||||
type UserPresence = Self;
|
type UserPresence = Self;
|
||||||
|
|
||||||
@@ -41,3 +42,200 @@ impl<CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> Env
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether the keepalive was sent, or false if cancelled.
|
||||||
|
fn send_keepalive_up_needed(
|
||||||
|
cid: ChannelID,
|
||||||
|
timeout: Duration<isize>,
|
||||||
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
|
let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
|
||||||
|
for mut pkt in keepalive_msg {
|
||||||
|
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout);
|
||||||
|
match status {
|
||||||
|
None => {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(Console::new(), "Sending a KEEPALIVE packet timed out").unwrap();
|
||||||
|
// TODO: abort user presence test?
|
||||||
|
}
|
||||||
|
Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending KEEPALIVE packet"),
|
||||||
|
Some(usb_ctap_hid::SendOrRecvStatus::Sent) => {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(Console::new(), "Sent KEEPALIVE packet").unwrap();
|
||||||
|
}
|
||||||
|
Some(usb_ctap_hid::SendOrRecvStatus::Received) => {
|
||||||
|
// We only parse one packet, because we only care about CANCEL.
|
||||||
|
let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt);
|
||||||
|
if received_cid != &cid {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(
|
||||||
|
Console::new(),
|
||||||
|
"Received a packet on channel ID {:?} while sending a KEEPALIVE packet",
|
||||||
|
received_cid,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
match processed_packet {
|
||||||
|
ProcessedPacket::InitPacket { cmd, .. } => {
|
||||||
|
if cmd == CtapHid::COMMAND_CANCEL {
|
||||||
|
// We ignore the payload, we can't answer with an error code anyway.
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(Console::new(), "User presence check cancelled").unwrap();
|
||||||
|
return Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(
|
||||||
|
Console::new(),
|
||||||
|
"Discarded packet with command {} received while sending a KEEPALIVE packet",
|
||||||
|
cmd,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProcessedPacket::ContinuationPacket { .. } => {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
writeln!(
|
||||||
|
Console::new(),
|
||||||
|
"Discarded continuation packet received while sending a KEEPALIVE packet",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blink_leds(pattern_seed: usize) {
|
||||||
|
for l in 0..led::count().flex_unwrap() {
|
||||||
|
if (pattern_seed ^ l).count_ones() & 1 != 0 {
|
||||||
|
led::get(l).flex_unwrap().on().flex_unwrap();
|
||||||
|
} else {
|
||||||
|
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wink_leds(pattern_seed: usize) {
|
||||||
|
// This generates a "snake" pattern circling through the LEDs.
|
||||||
|
// Fox example with 4 LEDs the sequence of lit LEDs will be the following.
|
||||||
|
// 0 1 2 3
|
||||||
|
// * *
|
||||||
|
// * * *
|
||||||
|
// * *
|
||||||
|
// * * *
|
||||||
|
// * *
|
||||||
|
// * * *
|
||||||
|
// * *
|
||||||
|
// * * *
|
||||||
|
// * *
|
||||||
|
let count = led::count().flex_unwrap();
|
||||||
|
let a = (pattern_seed / 2) % count;
|
||||||
|
let b = ((pattern_seed + 1) / 2) % count;
|
||||||
|
let c = ((pattern_seed + 3) / 2) % count;
|
||||||
|
|
||||||
|
for l in 0..count {
|
||||||
|
// On nRF52840-DK, logically swap LEDs 3 and 4 so that the order of LEDs form a circle.
|
||||||
|
let k = match l {
|
||||||
|
2 => 3,
|
||||||
|
3 => 2,
|
||||||
|
_ => l,
|
||||||
|
};
|
||||||
|
if k == a || k == b || k == c {
|
||||||
|
led::get(l).flex_unwrap().on().flex_unwrap();
|
||||||
|
} else {
|
||||||
|
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_off_leds() {
|
||||||
|
for l in 0..led::count().flex_unwrap() {
|
||||||
|
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KEEPALIVE_DELAY_MS: isize = 100;
|
||||||
|
pub const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS);
|
||||||
|
|
||||||
|
fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
||||||
|
// The timeout is N times the keepalive delay.
|
||||||
|
const TIMEOUT_ITERATIONS: usize =
|
||||||
|
crate::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize;
|
||||||
|
|
||||||
|
// First, send a keep-alive packet to notify that the keep-alive status has changed.
|
||||||
|
send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;
|
||||||
|
|
||||||
|
// Listen to the button presses.
|
||||||
|
let button_touched = Cell::new(false);
|
||||||
|
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
|
||||||
|
match state {
|
||||||
|
ButtonState::Pressed => button_touched.set(true),
|
||||||
|
ButtonState::Released => (),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
let mut buttons = buttons_callback.init().flex_unwrap();
|
||||||
|
// At the moment, all buttons are accepted. You can customize your setup here.
|
||||||
|
for mut button in &mut buttons {
|
||||||
|
button.enable().flex_unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut keepalive_response = Ok(());
|
||||||
|
for i in 0..TIMEOUT_ITERATIONS {
|
||||||
|
blink_leds(i);
|
||||||
|
|
||||||
|
// Setup a keep-alive callback.
|
||||||
|
let keepalive_expired = Cell::new(false);
|
||||||
|
let mut keepalive_callback = timer::with_callback(|_, _| {
|
||||||
|
keepalive_expired.set(true);
|
||||||
|
});
|
||||||
|
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
||||||
|
let keepalive_alarm = keepalive.set_alarm(KEEPALIVE_DELAY).flex_unwrap();
|
||||||
|
|
||||||
|
// Wait for a button touch or an alarm.
|
||||||
|
libtock_drivers::util::yieldk_for(|| button_touched.get() || keepalive_expired.get());
|
||||||
|
|
||||||
|
// Cleanup alarm callback.
|
||||||
|
match keepalive.stop_alarm(keepalive_alarm) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(TockError::Command(CommandError {
|
||||||
|
return_code: EALREADY,
|
||||||
|
..
|
||||||
|
})) => assert!(keepalive_expired.get()),
|
||||||
|
Err(_e) => {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||||
|
#[cfg(not(feature = "debug_ctap"))]
|
||||||
|
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this may take arbitrary time. The keepalive_delay should be adjusted accordingly,
|
||||||
|
// so that LEDs blink with a consistent pattern.
|
||||||
|
if keepalive_expired.get() {
|
||||||
|
// Do not return immediately, because we must clean up still.
|
||||||
|
keepalive_response = send_keepalive_up_needed(cid, KEEPALIVE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if button_touched.get() || keepalive_response.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_off_leds();
|
||||||
|
|
||||||
|
// Cleanup button callbacks.
|
||||||
|
for mut button in &mut buttons {
|
||||||
|
button.disable().flex_unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the user was present.
|
||||||
|
if keepalive_response.is_err() {
|
||||||
|
keepalive_response
|
||||||
|
} else if button_touched.get() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_USER_ACTION_TIMEOUT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ use crate::ctap::CtapState;
|
|||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use libtock_drivers::timer::ClockValue;
|
use libtock_drivers::timer::ClockValue;
|
||||||
|
|
||||||
|
// Implementation details must be public for testing (in particular fuzzing).
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub mod ctap;
|
pub mod ctap;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
mod ctap;
|
||||||
|
// Store example binaries use the flash directly. Eventually, they should access it from env::tock.
|
||||||
pub mod embedded_flash;
|
pub mod embedded_flash;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
|
|
||||||
|
|||||||
213
src/main.rs
213
src/main.rs
@@ -21,19 +21,18 @@ extern crate byteorder;
|
|||||||
extern crate core;
|
extern crate core;
|
||||||
extern crate lang_items;
|
extern crate lang_items;
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap1")]
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use ctap2::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
|
#[cfg(feature = "with_ctap1")]
|
||||||
use ctap2::ctap::status_code::Ctap2StatusCode;
|
use ctap2::env::tock::blink_leds;
|
||||||
use ctap2::env::tock::TockEnv;
|
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv, KEEPALIVE_DELAY};
|
||||||
use libtock_core::result::{CommandError, EALREADY};
|
#[cfg(feature = "with_ctap1")]
|
||||||
use libtock_drivers::buttons;
|
use libtock_drivers::buttons::{self, ButtonState};
|
||||||
use libtock_drivers::buttons::ButtonState;
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
use libtock_drivers::led;
|
use libtock_drivers::result::FlexUnwrap;
|
||||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
|
||||||
use libtock_drivers::timer;
|
use libtock_drivers::timer;
|
||||||
use libtock_drivers::timer::Duration;
|
use libtock_drivers::timer::Duration;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
@@ -44,8 +43,6 @@ use libtock_drivers::usb_ctap_hid;
|
|||||||
|
|
||||||
libtock_core::stack_size! {0x4000}
|
libtock_core::stack_size! {0x4000}
|
||||||
|
|
||||||
const KEEPALIVE_DELAY_MS: isize = 100;
|
|
||||||
const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS);
|
|
||||||
const SEND_TIMEOUT: Duration<isize> = Duration::from_ms(1000);
|
const SEND_TIMEOUT: Duration<isize> = Duration::from_ms(1000);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -60,7 +57,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let boot_time = timer.get_current_clock().flex_unwrap();
|
let boot_time = timer.get_current_clock().flex_unwrap();
|
||||||
let env = TockEnv::new(check_user_presence);
|
let env = TockEnv::new();
|
||||||
let mut ctap = ctap2::Ctap::new(env, boot_time);
|
let mut ctap = ctap2::Ctap::new(env, boot_time);
|
||||||
|
|
||||||
let mut led_counter = 0;
|
let mut led_counter = 0;
|
||||||
@@ -192,197 +189,3 @@ fn print_packet_notice(notice_text: &str, timer: &Timer) {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the keepalive was sent, or false if cancelled.
|
|
||||||
fn send_keepalive_up_needed(
|
|
||||||
cid: ChannelID,
|
|
||||||
timeout: Duration<isize>,
|
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
|
||||||
let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
|
|
||||||
for mut pkt in keepalive_msg {
|
|
||||||
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout);
|
|
||||||
match status {
|
|
||||||
None => {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(Console::new(), "Sending a KEEPALIVE packet timed out").unwrap();
|
|
||||||
// TODO: abort user presence test?
|
|
||||||
}
|
|
||||||
Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending KEEPALIVE packet"),
|
|
||||||
Some(usb_ctap_hid::SendOrRecvStatus::Sent) => {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(Console::new(), "Sent KEEPALIVE packet").unwrap();
|
|
||||||
}
|
|
||||||
Some(usb_ctap_hid::SendOrRecvStatus::Received) => {
|
|
||||||
// We only parse one packet, because we only care about CANCEL.
|
|
||||||
let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt);
|
|
||||||
if received_cid != &cid {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(
|
|
||||||
Console::new(),
|
|
||||||
"Received a packet on channel ID {:?} while sending a KEEPALIVE packet",
|
|
||||||
received_cid,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
match processed_packet {
|
|
||||||
ProcessedPacket::InitPacket { cmd, .. } => {
|
|
||||||
if cmd == CtapHid::COMMAND_CANCEL {
|
|
||||||
// We ignore the payload, we can't answer with an error code anyway.
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(Console::new(), "User presence check cancelled").unwrap();
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
|
||||||
} else {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(
|
|
||||||
Console::new(),
|
|
||||||
"Discarded packet with command {} received while sending a KEEPALIVE packet",
|
|
||||||
cmd,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProcessedPacket::ContinuationPacket { .. } => {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
writeln!(
|
|
||||||
Console::new(),
|
|
||||||
"Discarded continuation packet received while sending a KEEPALIVE packet",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blink_leds(pattern_seed: usize) {
|
|
||||||
for l in 0..led::count().flex_unwrap() {
|
|
||||||
if (pattern_seed ^ l).count_ones() & 1 != 0 {
|
|
||||||
led::get(l).flex_unwrap().on().flex_unwrap();
|
|
||||||
} else {
|
|
||||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wink_leds(pattern_seed: usize) {
|
|
||||||
// This generates a "snake" pattern circling through the LEDs.
|
|
||||||
// Fox example with 4 LEDs the sequence of lit LEDs will be the following.
|
|
||||||
// 0 1 2 3
|
|
||||||
// * *
|
|
||||||
// * * *
|
|
||||||
// * *
|
|
||||||
// * * *
|
|
||||||
// * *
|
|
||||||
// * * *
|
|
||||||
// * *
|
|
||||||
// * * *
|
|
||||||
// * *
|
|
||||||
let count = led::count().flex_unwrap();
|
|
||||||
let a = (pattern_seed / 2) % count;
|
|
||||||
let b = ((pattern_seed + 1) / 2) % count;
|
|
||||||
let c = ((pattern_seed + 3) / 2) % count;
|
|
||||||
|
|
||||||
for l in 0..count {
|
|
||||||
// On nRF52840-DK, logically swap LEDs 3 and 4 so that the order of LEDs form a circle.
|
|
||||||
let k = match l {
|
|
||||||
2 => 3,
|
|
||||||
3 => 2,
|
|
||||||
_ => l,
|
|
||||||
};
|
|
||||||
if k == a || k == b || k == c {
|
|
||||||
led::get(l).flex_unwrap().on().flex_unwrap();
|
|
||||||
} else {
|
|
||||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn switch_off_leds() {
|
|
||||||
for l in 0..led::count().flex_unwrap() {
|
|
||||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
|
||||||
// The timeout is N times the keepalive delay.
|
|
||||||
const TIMEOUT_ITERATIONS: usize =
|
|
||||||
ctap2::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize;
|
|
||||||
|
|
||||||
// First, send a keep-alive packet to notify that the keep-alive status has changed.
|
|
||||||
send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;
|
|
||||||
|
|
||||||
// Listen to the button presses.
|
|
||||||
let button_touched = Cell::new(false);
|
|
||||||
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
|
|
||||||
match state {
|
|
||||||
ButtonState::Pressed => button_touched.set(true),
|
|
||||||
ButtonState::Released => (),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
let mut buttons = buttons_callback.init().flex_unwrap();
|
|
||||||
// At the moment, all buttons are accepted. You can customize your setup here.
|
|
||||||
for mut button in &mut buttons {
|
|
||||||
button.enable().flex_unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut keepalive_response = Ok(());
|
|
||||||
for i in 0..TIMEOUT_ITERATIONS {
|
|
||||||
blink_leds(i);
|
|
||||||
|
|
||||||
// Setup a keep-alive callback.
|
|
||||||
let keepalive_expired = Cell::new(false);
|
|
||||||
let mut keepalive_callback = timer::with_callback(|_, _| {
|
|
||||||
keepalive_expired.set(true);
|
|
||||||
});
|
|
||||||
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
|
||||||
let keepalive_alarm = keepalive.set_alarm(KEEPALIVE_DELAY).flex_unwrap();
|
|
||||||
|
|
||||||
// Wait for a button touch or an alarm.
|
|
||||||
libtock_drivers::util::yieldk_for(|| button_touched.get() || keepalive_expired.get());
|
|
||||||
|
|
||||||
// Cleanup alarm callback.
|
|
||||||
match keepalive.stop_alarm(keepalive_alarm) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(TockError::Command(CommandError {
|
|
||||||
return_code: EALREADY,
|
|
||||||
..
|
|
||||||
})) => assert!(keepalive_expired.get()),
|
|
||||||
Err(_e) => {
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
|
||||||
#[cfg(not(feature = "debug_ctap"))]
|
|
||||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this may take arbitrary time. The keepalive_delay should be adjusted accordingly,
|
|
||||||
// so that LEDs blink with a consistent pattern.
|
|
||||||
if keepalive_expired.get() {
|
|
||||||
// Do not return immediately, because we must clean up still.
|
|
||||||
keepalive_response = send_keepalive_up_needed(cid, KEEPALIVE_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if button_touched.get() || keepalive_response.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch_off_leds();
|
|
||||||
|
|
||||||
// Cleanup button callbacks.
|
|
||||||
for mut button in &mut buttons {
|
|
||||||
button.disable().flex_unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns whether the user was present.
|
|
||||||
if keepalive_response.is_err() {
|
|
||||||
keepalive_response
|
|
||||||
} else if button_touched.get() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_USER_ACTION_TIMEOUT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user