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

Allows initialization without Reset permission #673

Merged
merged 2 commits into from
Jan 9, 2024
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
4 changes: 2 additions & 2 deletions libraries/opensk/fuzz/fuzz_helper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub fn process_ctap_any_type(data: &[u8]) -> arbitrary::Result<()> {

let data = unstructured.take_rest();
// Initialize ctap state and hid and get the allocated cid.
let mut ctap = Ctap::new(env);
let mut ctap = Ctap::new(env, false);
let cid = initialize(&mut ctap);
// Wrap input as message with the allocated cid.
let mut command = cid.to_vec();
Expand Down Expand Up @@ -191,7 +191,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) -> arbitra
return Ok(());
}
// Initialize ctap state and hid and get the allocated cid.
let mut ctap = Ctap::new(env);
let mut ctap = Ctap::new(env, false);
let cid = initialize(&mut ctap);
// Wrap input as message with allocated cid and command type.
let mut command = cid.to_vec();
Expand Down
11 changes: 11 additions & 0 deletions libraries/opensk/src/api/attestation_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,14 @@ impl From<StoreError> for Error {
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_store_error() {
assert_eq!(Error::from(StoreError::StorageError), Error::Storage);
assert_eq!(Error::from(StoreError::InvalidStorage), Error::Internal);
}
}
16 changes: 15 additions & 1 deletion libraries/opensk/src/api/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use core::convert::TryFrom;

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UsbEndpoint {
MainHid = 1,
#[cfg(feature = "vendor_hid")]
Expand All @@ -40,10 +40,24 @@ pub enum SendOrRecvStatus {
Received(UsbEndpoint),
}

#[derive(Debug, PartialEq, Eq)]
pub struct SendOrRecvError;

pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;

pub trait HidConnection {
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult;
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_endpoint_num() {
assert_eq!(UsbEndpoint::try_from(1), Ok(UsbEndpoint::MainHid));
#[cfg(feature = "vendor_hid")]
assert_eq!(UsbEndpoint::try_from(2), Ok(UsbEndpoint::VendorHid));
assert_eq!(UsbEndpoint::try_from(3), Err(SendOrRecvError));
}
}
43 changes: 43 additions & 0 deletions libraries/opensk/src/api/customization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,47 @@ mod test {
fn test_invariants() {
assert!(is_valid(&DEFAULT_CUSTOMIZATION));
}

#[test]
fn test_accessors() {
let customization = CustomizationImpl {
aaguid: &[0; AAGUID_LENGTH],
allows_pin_protocol_v1: true,
default_cred_protect: None,
default_min_pin_length: 4,
default_min_pin_length_rp_ids: &["example.com"],
enforce_always_uv: false,
enterprise_attestation_mode: None,
enterprise_rp_id_list: &[],
max_msg_size: 7609,
max_pin_retries: 8,
use_batch_attestation: true,
use_signature_counter: true,
max_cred_blob_length: 32,
max_credential_count_in_list: Some(3),
max_large_blob_array_size: 2048,
max_rp_ids_length: 8,
max_supported_resident_keys: 150,
};
assert_eq!(customization.aaguid(), &[0; AAGUID_LENGTH]);
assert!(customization.allows_pin_protocol_v1());
assert!(customization.default_cred_protect().is_none());
assert_eq!(customization.default_min_pin_length(), 4);
assert_eq!(
customization.default_min_pin_length_rp_ids(),
vec![String::from("example.com")]
);
assert!(!customization.enforce_always_uv());
assert!(customization.enterprise_attestation_mode().is_none());
assert!(customization.enterprise_rp_id_list().is_empty());
assert_eq!(customization.max_msg_size(), 7609);
assert_eq!(customization.max_pin_retries(), 8);
assert!(customization.use_batch_attestation());
assert!(customization.use_signature_counter());
assert_eq!(customization.max_cred_blob_length(), 32);
assert_eq!(customization.max_credential_count_in_list(), Some(3));
assert_eq!(customization.max_large_blob_array_size(), 2048);
assert_eq!(customization.max_rp_ids_length(), 8);
assert_eq!(customization.max_supported_resident_keys(), 150);
}
}
11 changes: 10 additions & 1 deletion libraries/opensk/src/ctap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ pub struct StatefulPermission<E: Env> {
impl<E: Env> StatefulPermission<E> {
/// Creates the command state at device startup.
///
/// Resets are only possible after a power cycle. Therefore, initialization
/// Resets are only possible after a power cycle. Therefore, there is no way to grant the Reset
/// permission outside of this function. If you initialize the app without a power cycle
/// (potentially after waking up from sleep), `clear` the permissions.
/// means allowing Reset, and Reset cannot be granted later.
pub fn new_reset(env: &mut E) -> StatefulPermission<E> {
StatefulPermission {
Expand Down Expand Up @@ -552,6 +554,13 @@ impl<E: Env> CtapState<E> {
}
}

/// Creates new CTAP state that doesn't assume a user intended power cycle.
pub fn new_soft_reset(env: &mut E) -> Self {
let mut ctap_state = CtapState::new(env);
ctap_state.stateful_command_permission.clear();
ctap_state
}

pub fn increment_global_signature_counter(
&mut self,
env: &mut E,
Expand Down
78 changes: 69 additions & 9 deletions libraries/opensk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ impl<E: Env> Ctap<E> {
/// Instantiates a CTAP implementation given its environment.
// This should only take the environment, but it temporarily takes the boot time until the
// clock is part of the environment.
pub fn new(mut env: E) -> Self {
let state = CtapState::<E>::new(&mut env);
pub fn new(mut env: E, is_soft_reset: bool) -> Self {
let state = if is_soft_reset {
CtapState::<E>::new_soft_reset(&mut env)
} else {
CtapState::<E>::new(&mut env)
};
let hid = MainHid::default();
#[cfg(feature = "vendor_hid")]
let vendor_hid = VendorHid::default();
Expand All @@ -81,10 +85,6 @@ impl<E: Env> Ctap<E> {
&mut self.state
}

pub fn hid(&mut self) -> &mut MainHid<E> {
&mut self.hid
}

pub fn env(&mut self) -> &mut E {
&mut self.env
}
Expand Down Expand Up @@ -134,6 +134,7 @@ impl<E: Env> Ctap<E> {
#[cfg(test)]
mod test {
use super::*;
use crate::ctap::status_code::Ctap2StatusCode;
use crate::env::test::TestEnv;

/// Assembles a packet for a payload that fits into one packet.
Expand Down Expand Up @@ -162,7 +163,7 @@ mod test {
#[test]
fn test_wink() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env);
let mut ctap = Ctap::<TestEnv>::new(env, false);

// Send Init, receive Init response and check wink if disabled.
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
Expand All @@ -181,7 +182,7 @@ mod test {
#[test]
fn test_locked_channel_id() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env);
let mut ctap = Ctap::<TestEnv>::new(env, false);

// Send Init, receive Init response.
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
Expand All @@ -200,11 +201,60 @@ mod test {
assert_eq!(response_packet[4], 0xBF);
}

#[test]
fn test_hard_reset() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env, false);

// Send Init, receive Init response.
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
let response_packet = init_response.next().unwrap();
assert_eq!(response_packet[4], 0x86);
let cid = *array_ref!(response_packet, 15, 4);

// Send Reset, get Ok.
let reset_packet = assemble_packet(&cid, 0x10, &[0x07]);
let mut reset_response = ctap.process_hid_packet(&reset_packet, Transport::MainHid);
let response_packet = reset_response.next().unwrap();
let status_byte = Ctap2StatusCode::CTAP2_OK as u8;
let expected_data = [0x90, 0x00, 0x01, status_byte];
assert_eq!(response_packet[..4], cid);
assert_eq!(response_packet[4..8], expected_data);
}

#[test]
fn test_soft_reset() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env, true);

// Send Init, receive Init response.
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
let response_packet = init_response.next().unwrap();
assert_eq!(response_packet[4], 0x86);
let cid = *array_ref!(response_packet, 15, 4);

// Send Reset, get error.
let reset_packet = assemble_packet(&cid, 0x10, &[0x07]);
let mut reset_response = ctap.process_hid_packet(&reset_packet, Transport::MainHid);
let response_packet = reset_response.next().unwrap();
let status_byte = Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED as u8;
let expected_data = [0x90, 0x00, 0x01, status_byte];
assert_eq!(response_packet[..4], cid);
assert_eq!(response_packet[4..8], expected_data);
}

#[test]
fn test_env_api() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env, true);
assert_eq!(ctap.env().firmware_version(), Some(0));
}

#[test]
#[cfg(feature = "vendor_hid")]
fn test_locked_transport() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env);
let mut ctap = Ctap::<TestEnv>::new(env, false);

// Send Init, receive Init response.
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
Expand All @@ -222,4 +272,14 @@ mod test {
let response_packet = init_response.next().unwrap();
assert_eq!(response_packet[4], 0xBF);
}

#[test]
#[cfg(feature = "with_ctap1")]
fn test_ctap1_initial_state() {
let env = TestEnv::default();
let mut ctap = Ctap::<TestEnv>::new(env, false);
// Granting doesn't work until a CTAP1 request was processed.
ctap.u2f_grant_user_presence();
assert!(!ctap.u2f_needs_user_presence());
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn main() {
}

let env = TockEnv::<SyscallImplementation>::default();
let mut ctap = opensk::Ctap::new(env);
let mut ctap = opensk::Ctap::new(env, false);

let mut led_counter = 0;
let mut led_blink_timer =
Expand Down
Loading