Files
OpenSK/src/env/tock/mod.rs
kaczmarczyck 03031e6970 Maintenance PR for clippy, license and authors (#601)
* Maintenance PR for clippy, license and authors

* remove author from libraries
2023-03-06 12:45:01 +01:00

351 lines
10 KiB
Rust

// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub use self::storage::{TockStorage, TockUpgradeStorage};
use crate::api::attestation_store::AttestationStore;
use crate::api::connection::{
HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus, UsbEndpoint,
};
use crate::api::customization::{CustomizationImpl, AAGUID_LENGTH, DEFAULT_CUSTOMIZATION};
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult};
use crate::api::{attestation_store, key_store};
use crate::env::Env;
use clock::TockClock;
use core::cell::Cell;
use core::convert::TryFrom;
use core::sync::atomic::{AtomicBool, Ordering};
use libtock_core::result::{CommandError, EALREADY};
use libtock_drivers::buttons::{self, ButtonState};
use libtock_drivers::console::Console;
use libtock_drivers::result::{FlexUnwrap, TockError};
use libtock_drivers::timer::Duration;
use libtock_drivers::{crp, led, rng, timer, usb_ctap_hid};
use persistent_store::{StorageResult, Store};
use rng256::Rng256;
mod clock;
mod storage;
pub const AAGUID: &[u8; AAGUID_LENGTH] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
const TOCK_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
aaguid: AAGUID,
..DEFAULT_CUSTOMIZATION
};
/// RNG backed by the TockOS rng driver.
pub struct TockRng256 {}
impl Rng256 for TockRng256 {
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
let mut buf: [u8; 32] = [Default::default(); 32];
rng::fill_buffer(&mut buf);
buf
}
}
pub struct TockHidConnection {
endpoint: UsbEndpoint,
}
impl HidConnection for TockHidConnection {
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult {
match usb_ctap_hid::send_or_recv_with_timeout(
buf,
Duration::from_ms(timeout_ms as isize),
self.endpoint as usize,
) {
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
Ok(usb_ctap_hid::SendOrRecvStatus::Sent) => Ok(SendOrRecvStatus::Sent),
Ok(usb_ctap_hid::SendOrRecvStatus::Received(recv_endpoint)) => {
UsbEndpoint::try_from(recv_endpoint).map(SendOrRecvStatus::Received)
}
_ => Err(SendOrRecvError),
}
}
}
pub struct TockEnv {
rng: TockRng256,
store: Store<TockStorage>,
upgrade_storage: Option<TockUpgradeStorage>,
main_connection: TockHidConnection,
#[cfg(feature = "vendor_hid")]
vendor_connection: TockHidConnection,
blink_pattern: usize,
clock: TockClock,
}
impl Default for TockEnv {
/// Returns the unique instance of the Tock environment.
///
/// # Panics
///
/// - If called a second time.
fn default() -> Self {
// We rely on `take_storage` to ensure that this function is called only once.
let storage = take_storage().unwrap();
let store = Store::new(storage).ok().unwrap();
let upgrade_storage = TockUpgradeStorage::new().ok();
TockEnv {
rng: TockRng256 {},
store,
upgrade_storage,
main_connection: TockHidConnection {
endpoint: UsbEndpoint::MainHid,
},
#[cfg(feature = "vendor_hid")]
vendor_connection: TockHidConnection {
endpoint: UsbEndpoint::VendorHid,
},
blink_pattern: 0,
clock: TockClock::default(),
}
}
}
/// Returns the unique storage instance.
///
/// # Panics
///
/// - If called a second time.
pub fn take_storage() -> StorageResult<TockStorage> {
// Make sure the storage was not already taken.
static TAKEN: AtomicBool = AtomicBool::new(false);
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
TockStorage::new()
}
impl UserPresence for TockEnv {
fn check_init(&mut self) {
self.blink_pattern = 0;
}
fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult {
if timeout_ms == 0 {
return Err(UserPresenceError::Timeout);
}
blink_leds(self.blink_pattern);
self.blink_pattern += 1;
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();
for mut button in &mut buttons {
button.enable().flex_unwrap();
}
// 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(Duration::from_ms(timeout_ms as isize))
.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>");
}
}
for mut button in &mut buttons {
button.disable().flex_unwrap();
}
if button_touched.get() {
Ok(())
} else if keepalive_expired.get() {
Err(UserPresenceError::Timeout)
} else {
panic!("Unexpected exit condition");
}
}
fn check_complete(&mut self) {
switch_off_leds();
}
}
impl FirmwareProtection for TockEnv {
fn lock(&mut self) -> bool {
matches!(
crp::set_protection(crp::ProtectionLevel::FullyLocked),
Ok(())
| Err(TockError::Command(CommandError {
return_code: EALREADY,
..
}))
)
}
}
impl key_store::Helper for TockEnv {}
impl AttestationStore for TockEnv {
fn get(
&mut self,
id: &attestation_store::Id,
) -> Result<Option<attestation_store::Attestation>, attestation_store::Error> {
if !matches!(id, attestation_store::Id::Batch) {
return Err(attestation_store::Error::NoSupport);
}
attestation_store::helper_get(self)
}
fn set(
&mut self,
id: &attestation_store::Id,
attestation: Option<&attestation_store::Attestation>,
) -> Result<(), attestation_store::Error> {
if !matches!(id, attestation_store::Id::Batch) {
return Err(attestation_store::Error::NoSupport);
}
attestation_store::helper_set(self, attestation)
}
}
impl Env for TockEnv {
type Rng = TockRng256;
type UserPresence = Self;
type Storage = TockStorage;
type KeyStore = Self;
type AttestationStore = Self;
type Clock = TockClock;
type UpgradeStorage = TockUpgradeStorage;
type FirmwareProtection = Self;
type Write = Console;
type Customization = CustomizationImpl;
type HidConnection = TockHidConnection;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn user_presence(&mut self) -> &mut Self::UserPresence {
self
}
fn store(&mut self) -> &mut Store<Self::Storage> {
&mut self.store
}
fn key_store(&mut self) -> &mut Self {
self
}
fn attestation_store(&mut self) -> &mut Self {
self
}
fn clock(&mut self) -> &mut Self::Clock {
&mut self.clock
}
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
self.upgrade_storage.as_mut()
}
fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection {
self
}
fn write(&mut self) -> Self::Write {
Console::new()
}
fn customization(&self) -> &Self::Customization {
&TOCK_CUSTOMIZATION
}
fn main_hid_connection(&mut self) -> &mut Self::HidConnection {
&mut self.main_connection
}
#[cfg(feature = "vendor_hid")]
fn vendor_hid_connection(&mut self) -> &mut Self::HidConnection {
&mut self.vendor_connection
}
}
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();
}
}