Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clock trait #596

Merged
merged 8 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions src/api/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,19 @@
// limitations under the License.

pub trait Clock: Sized {
kaczmarczyck marked this conversation as resolved.
Show resolved Hide resolved
type Timer;
/// Stores data for the clock to recognize if this timer is elapsed or not.
///
/// The Clock does not keep track of the timers it creates. Therefore, they should not wrap
/// unexpectedly. A timer that is elapsed may never return to a non-elapsed state.
///
/// A default Timer should return `true` when checked with `is_elapsed`.
type Timer: Default;

/// Creates a new timer that expires after the given time in ms.
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer;

/// Consumes a timer, and returns it if not expired.
fn check_timer(&mut self, timer: Self::Timer) -> Option<Self::Timer>;

/// Checks the timer and takes it, if expired.
fn update_timer(&mut self, timer: &mut Option<Self::Timer>) {
*timer = timer.take().and_then(|t| Self::check_timer(self, t));
}

/// Tickles the clock and makes it check its timers. Often a NOOP.
fn tickle(&mut self);
/// Checks whether a given timer is expired.
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool;
kaczmarczyck marked this conversation as resolved.
Show resolved Hide resolved

/// Timestamp in microseconds.
///
Expand Down
13 changes: 6 additions & 7 deletions src/ctap/hid/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct MessageAssembler<E: Env> {
// Current channel ID.
cid: ChannelID,
// Timestamp of the last packet received on the current channel.
timer: Option<<<E as Env>::Clock as Clock>::Timer>,
timer: <<E as Env>::Clock as Clock>::Timer,
// Current command.
cmd: u8,
// Sequence number expected for the next packet.
Expand All @@ -47,7 +47,7 @@ impl<E: Env> MessageAssembler<E> {
MessageAssembler {
idle: true,
cid: [0, 0, 0, 0],
timer: None,
timer: <<E as Env>::Clock as Clock>::Timer::default(),
cmd: 0,
seq: 0,
remaining_payload_len: 0,
Expand All @@ -60,7 +60,7 @@ impl<E: Env> MessageAssembler<E> {
fn reset(&mut self) {
self.idle = true;
self.cid = [0, 0, 0, 0];
self.timer = None;
self.timer = <<E as Env>::Clock as Clock>::Timer::default();
self.cmd = 0;
self.seq = 0;
self.remaining_payload_len = 0;
Expand All @@ -81,8 +81,7 @@ impl<E: Env> MessageAssembler<E> {
// section 8.8.1
let (cid, processed_packet) = CtapHid::<E>::process_single_packet(packet);

env.clock().update_timer(&mut self.timer);
if !self.idle && self.timer.is_none() {
if !self.idle && env.clock().is_elapsed(&self.timer) {
// The current channel timed out.
// Save the channel ID and reset the state.
let current_cid = self.cid;
Expand Down Expand Up @@ -133,7 +132,7 @@ impl<E: Env> MessageAssembler<E> {
Err((cid, CtapHidError::InvalidSeq))
} else {
// Update the last timestamp.
self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION));
self.timer = env.clock().make_timer(TIMEOUT_DURATION);
// Increment the sequence number for the next packet.
self.seq += 1;
Ok(self.append_payload(data))
Expand All @@ -157,7 +156,7 @@ impl<E: Env> MessageAssembler<E> {
return Err((cid, CtapHidError::InvalidLen));
}
self.cid = cid;
self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION));
self.timer = env.clock().make_timer(TIMEOUT_DURATION);
self.cmd = cmd;
self.seq = 0;
self.remaining_payload_len = len;
Expand Down
16 changes: 8 additions & 8 deletions src/ctap/main_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const WINK_TIMEOUT_DURATION: usize = 5000;
/// Implements the standard CTAP command processing for HID.
pub struct MainHid<E: Env> {
hid: CtapHid<E>,
wink_permission: Option<<<E as Env>::Clock as Clock>::Timer>,
wink_permission: <<E as Env>::Clock as Clock>::Timer,
}

impl<E: Env> MainHid<E> {
Expand All @@ -42,7 +42,7 @@ impl<E: Env> MainHid<E> {
| CtapHid::<E>::CAPABILITY_NMSG;

let hid = CtapHid::<E>::new(capabilities);
let wink_permission = None;
let wink_permission = <<E as Env>::Clock as Clock>::Timer::default();
kaczmarczyck marked this conversation as resolved.
Show resolved Hide resolved
MainHid {
hid,
wink_permission,
Expand Down Expand Up @@ -73,7 +73,7 @@ impl<E: Env> MainHid<E> {
ctap_state: &mut CtapState<E>,
) -> Message {
// If another command arrives, stop winking to prevent accidential button touches.
self.wink_permission = None;
self.wink_permission = <<E as Env>::Clock as Clock>::Timer::default();

let cid = message.cid;
match message.cmd {
Expand Down Expand Up @@ -105,7 +105,7 @@ impl<E: Env> MainHid<E> {
// CTAP 2.1 from 2021-06-15, section 11.2.9.2.1.
CtapHidCommand::Wink => {
if message.payload.is_empty() {
self.wink_permission = Some(env.clock().make_timer(WINK_TIMEOUT_DURATION));
self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION);
// The response is empty like the request.
message
} else {
Expand All @@ -118,9 +118,8 @@ impl<E: Env> MainHid<E> {
}

/// Returns whether a wink permission is currently granted.
pub fn should_wink(&mut self, env: &mut E) -> bool {
env.clock().update_timer(&mut self.wink_permission);
self.wink_permission.is_some()
pub fn should_wink(&self, env: &mut E) -> bool {
!env.clock().is_elapsed(&self.wink_permission)
}

#[cfg(feature = "with_ctap1")]
Expand Down Expand Up @@ -154,10 +153,11 @@ mod test {

fn new_initialized() -> (MainHid<TestEnv>, ChannelID) {
let (hid, cid) = CtapHid::new_initialized();
let wink_permission = <<TestEnv as Env>::Clock as Clock>::Timer::default();
(
MainHid::<TestEnv> {
hid,
wink_permission: None,
wink_permission,
},
cid,
)
Expand Down
11 changes: 5 additions & 6 deletions src/ctap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ pub enum StatefulCommand {
/// Additionally, state that is held over multiple commands is assigned to a channel. We discard
/// all state when we receive data on a different channel.
pub struct StatefulPermission<E: Env> {
permission: Option<<<E as Env>::Clock as Clock>::Timer>,
permission: <<E as Env>::Clock as Clock>::Timer,
command_type: Option<StatefulCommand>,
channel: Option<Channel>,
}
Expand All @@ -372,15 +372,15 @@ impl<E: Env> StatefulPermission<E> {
/// means allowing Reset, and Reset cannot be granted later.
pub fn new_reset(env: &mut E) -> StatefulPermission<E> {
StatefulPermission {
permission: Some(env.clock().make_timer(RESET_TIMEOUT_DURATION)),
permission: env.clock().make_timer(RESET_TIMEOUT_DURATION),
command_type: Some(StatefulCommand::Reset),
channel: None,
}
}

/// Clears all permissions and state.
pub fn clear(&mut self) {
self.permission = None;
self.permission = <<E as Env>::Clock as Clock>::Timer::default();
self.command_type = None;
self.channel = None;
}
Expand All @@ -406,8 +406,7 @@ impl<E: Env> StatefulPermission<E> {

/// Clears all state if the permission timed out.
pub fn clear_timer(&mut self, env: &mut E) {
env.clock().update_timer(&mut self.permission);
if self.permission.is_none() {
if env.clock().is_elapsed(&self.permission) {
self.clear();
}
}
Expand All @@ -430,7 +429,7 @@ impl<E: Env> StatefulPermission<E> {
// Reset is only allowed after a power cycle.
StatefulCommand::Reset => unreachable!(),
_ => {
self.permission = Some(env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION));
self.permission = env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION);
self.command_type = Some(new_command_type);
self.channel = Some(channel);
}
Expand Down
11 changes: 5 additions & 6 deletions src/ctap/token_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct PinUvAuthTokenState<E: Env> {
// Relies on the fact that all permissions are represented by powers of two.
permissions_set: u8,
permissions_rp_id: Option<String>,
usage_timer: Option<<<E as Env>::Clock as Clock>::Timer>,
usage_timer: <<E as Env>::Clock as Clock>::Timer,
user_verified: bool,
in_use: bool,
}
Expand All @@ -48,7 +48,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
PinUvAuthTokenState {
permissions_set: 0,
permissions_rp_id: None,
usage_timer: None,
usage_timer: <<E as Env>::Clock as Clock>::Timer::default(),
user_verified: false,
in_use: false,
}
Expand Down Expand Up @@ -113,7 +113,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
/// Starts the timer for pinUvAuthToken usage.
pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) {
self.user_verified = true;
self.usage_timer = Some(env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT));
self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT);
self.in_use = true;
}

Expand All @@ -122,8 +122,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
if !self.in_use {
return;
}
env.clock().update_timer(&mut self.usage_timer);
if self.usage_timer.is_none() {
if env.clock().is_elapsed(&self.usage_timer) {
self.stop_using_pin_uv_auth_token();
}
}
Expand All @@ -147,7 +146,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
pub fn stop_using_pin_uv_auth_token(&mut self) {
self.permissions_rp_id = None;
self.permissions_set = 0;
self.usage_timer = None;
self.usage_timer = <<E as Env>::Clock as Clock>::Timer::default();
self.user_verified = false;
self.in_use = false;
}
Expand Down
42 changes: 19 additions & 23 deletions src/ctap/u2f_up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,47 @@ const U2F_UP_PROMPT_TIMEOUT: usize = 10000;

pub struct U2fUserPresenceState<E: Env> {
/// If user presence was recently requested, its timeout is saved here.
needs_up: Option<<<E as Env>::Clock as Clock>::Timer>,
needs_up: <<E as Env>::Clock as Clock>::Timer,

/// Button touch timeouts, while user presence is requested, are saved here.
has_up: Option<<<E as Env>::Clock as Clock>::Timer>,
has_up: <<E as Env>::Clock as Clock>::Timer,
}

impl<E: Env> U2fUserPresenceState<E> {
pub fn new() -> U2fUserPresenceState<E> {
U2fUserPresenceState {
needs_up: None,
has_up: None,
needs_up: <<E as Env>::Clock as Clock>::Timer::default(),
has_up: <<E as Env>::Clock as Clock>::Timer::default(),
}
}

// Granting user presence is ignored if it needs activation, but waits. Also cleans up.
/// Allows consuming user presence until timeout, if it was needed.
///
/// If user presence was not requested, granting user presence does nothing.
pub fn grant_up(&mut self, env: &mut E) {
self.check_expiration(env);
if self.needs_up.is_some() {
self.needs_up = None;
self.has_up = Some(env.clock().make_timer(TOUCH_TIMEOUT));
if !env.clock().is_elapsed(&self.needs_up) {
self.needs_up = <<E as Env>::Clock as Clock>::Timer::default();
self.has_up = env.clock().make_timer(TOUCH_TIMEOUT);
}
}

// This marks user presence as needed or uses it up if already granted. Also cleans up.
/// Returns whether user presence was granted within the timeout and not yet consumed.
pub fn consume_up(&mut self, env: &mut E) -> bool {
self.check_expiration(env);
if self.has_up.is_some() {
self.has_up = None;
if !env.clock().is_elapsed(&self.has_up) {
self.has_up = <<E as Env>::Clock as Clock>::Timer::default();
true
} else {
self.needs_up = Some(env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT));
self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT);
false
}
}

// Returns if user presence was requested. Also cleans up.
/// Returns whether user presence was requested.
///
/// This function doesn't represent interaction with the environment, and does not change the
/// state, i.e. neither grants nor consumes user presence.
pub fn is_up_needed(&mut self, env: &mut E) -> bool {
self.check_expiration(env);
self.needs_up.is_some()
}

/// Checks and updates all timers.
pub fn check_expiration(&mut self, env: &mut E) {
env.clock().update_timer(&mut self.needs_up);
env.clock().update_timer(&mut self.has_up);
!env.clock().is_elapsed(&self.needs_up)
}
}

Expand Down
18 changes: 6 additions & 12 deletions src/env/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl Rng256 for TestRng256 {
}
}

#[derive(Debug, PartialEq)]
#[derive(Debug, Default, PartialEq)]
pub struct TestTimer {
end: usize,
kaczmarczyck marked this conversation as resolved.
Show resolved Hide resolved
}
Expand All @@ -82,16 +82,10 @@ impl Clock for TestClock {
}
}

fn check_timer(&mut self, timer: Self::Timer) -> Option<Self::Timer> {
if self.cur_time < timer.end {
Some(timer)
} else {
None
}
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool {
self.cur_time >= timer.end
}

fn tickle(&mut self) {}

#[cfg(feature = "debug_ctap")]
fn timestamp_us(&mut self) -> usize {
// Unused, but let's implement something because it's easy.
Expand Down Expand Up @@ -280,10 +274,10 @@ mod test {
fn test_clock() {
let mut clock = TestClock::default();
let timer = clock.make_timer(3);
let timer = clock.check_timer(timer).unwrap();
assert!(!clock.is_elapsed(&timer));
clock.advance(2);
let timer = clock.check_timer(timer).unwrap();
assert!(!clock.is_elapsed(&timer));
clock.advance(1);
assert_eq!(clock.check_timer(timer), None);
assert!(clock.is_elapsed(&timer));
}
}
Loading