Merge branch 'develop' into firmware_protection
This commit is contained in:
15
src/env/mod.rs
vendored
15
src/env/mod.rs
vendored
@@ -3,7 +3,7 @@ use crate::api::upgrade_storage::UpgradeStorage;
|
||||
use crate::ctap::hid::ChannelID;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crypto::rng256::Rng256;
|
||||
use persistent_store::{Storage, StorageResult};
|
||||
use persistent_store::{Storage, Store};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod test;
|
||||
@@ -26,16 +26,13 @@ pub trait Env {
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng;
|
||||
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
||||
fn store(&mut self) -> &mut Store<Self::Storage>;
|
||||
|
||||
/// Returns the unique storage instance.
|
||||
/// Returns the upgrade storage instance.
|
||||
///
|
||||
/// This function is called at most once. Implementation may panic if called more than once.
|
||||
fn storage(&mut self) -> StorageResult<Self::Storage>;
|
||||
|
||||
/// Returns the unique upgrade storage instance.
|
||||
///
|
||||
/// This function is called at most once. Implementation may panic if called more than once.
|
||||
fn upgrade_storage(&mut self) -> StorageResult<Self::UpgradeStorage>;
|
||||
/// Upgrade storage is optional, so implementations may return `None`. However, implementations
|
||||
/// should either always return `None` or always return `Some`.
|
||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage>;
|
||||
|
||||
fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection;
|
||||
}
|
||||
|
||||
52
src/env/test/mod.rs
vendored
52
src/env/test/mod.rs
vendored
@@ -4,26 +4,55 @@ use crate::ctap::hid::ChannelID;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::env::{Env, UserPresence};
|
||||
use crypto::rng256::ThreadRng256;
|
||||
use persistent_store::{BufferOptions, BufferStorage, StorageResult};
|
||||
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||
|
||||
mod upgrade_storage;
|
||||
|
||||
pub struct TestEnv {
|
||||
rng: ThreadRng256,
|
||||
user_presence: TestUserPresence,
|
||||
store: Store<BufferStorage>,
|
||||
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||
}
|
||||
|
||||
pub struct TestUserPresence {
|
||||
check: Box<dyn Fn(ChannelID) -> Result<(), Ctap2StatusCode>>,
|
||||
}
|
||||
|
||||
fn new_storage() -> BufferStorage {
|
||||
// Use the Nordic configuration.
|
||||
const PAGE_SIZE: usize = 0x1000;
|
||||
const NUM_PAGES: usize = 20;
|
||||
let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice();
|
||||
let options = BufferOptions {
|
||||
word_size: 4,
|
||||
page_size: PAGE_SIZE,
|
||||
max_word_writes: 2,
|
||||
max_page_erases: 10000,
|
||||
strict_mode: true,
|
||||
};
|
||||
BufferStorage::new(store, options)
|
||||
}
|
||||
|
||||
impl TestEnv {
|
||||
pub fn new() -> Self {
|
||||
let rng = ThreadRng256 {};
|
||||
let user_presence = TestUserPresence {
|
||||
check: Box::new(|_| Ok(())),
|
||||
};
|
||||
TestEnv { rng, user_presence }
|
||||
let storage = new_storage();
|
||||
let store = Store::new(storage).ok().unwrap();
|
||||
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||
TestEnv {
|
||||
rng,
|
||||
user_presence,
|
||||
store,
|
||||
upgrade_storage,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_upgrade_storage(&mut self) {
|
||||
self.upgrade_storage = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,23 +89,12 @@ impl Env for TestEnv {
|
||||
&mut self.user_presence
|
||||
}
|
||||
|
||||
fn storage(&mut self) -> StorageResult<Self::Storage> {
|
||||
// Use the Nordic configuration.
|
||||
const PAGE_SIZE: usize = 0x1000;
|
||||
const NUM_PAGES: usize = 20;
|
||||
let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice();
|
||||
let options = BufferOptions {
|
||||
word_size: 4,
|
||||
page_size: PAGE_SIZE,
|
||||
max_word_writes: 2,
|
||||
max_page_erases: 10000,
|
||||
strict_mode: true,
|
||||
};
|
||||
Ok(BufferStorage::new(store, options))
|
||||
fn store(&mut self) -> &mut Store<Self::Storage> {
|
||||
&mut self.store
|
||||
}
|
||||
|
||||
fn upgrade_storage(&mut self) -> StorageResult<Self::UpgradeStorage> {
|
||||
BufferUpgradeStorage::new()
|
||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||
self.upgrade_storage.as_mut()
|
||||
}
|
||||
|
||||
fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection {
|
||||
|
||||
73
src/env/tock/mod.rs
vendored
73
src/env/tock/mod.rs
vendored
@@ -1,6 +1,6 @@
|
||||
use self::storage::{SyscallStorage, SyscallUpgradeStorage};
|
||||
pub use self::storage::{TockStorage, TockUpgradeStorage};
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
use crate::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
|
||||
use crate::ctap::hid::{ChannelID, CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::env::{Env, UserPresence};
|
||||
use core::cell::Cell;
|
||||
@@ -15,46 +15,45 @@ use libtock_drivers::console::Console;
|
||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||
use libtock_drivers::timer::Duration;
|
||||
use libtock_drivers::{crp, led, timer, usb_ctap_hid};
|
||||
use persistent_store::StorageResult;
|
||||
use persistent_store::{StorageResult, Store};
|
||||
|
||||
mod storage;
|
||||
|
||||
pub struct TockEnv {
|
||||
rng: TockRng256,
|
||||
storage: bool,
|
||||
upgrade_storage: bool,
|
||||
store: Store<TockStorage>,
|
||||
upgrade_storage: Option<TockUpgradeStorage>,
|
||||
}
|
||||
|
||||
impl TockEnv {
|
||||
/// Returns the unique instance of the Tock environment.
|
||||
///
|
||||
/// This function returns `Some` the first time it is called. Afterwards, it repeatedly returns
|
||||
/// `None`.
|
||||
pub fn new() -> Option<Self> {
|
||||
// Make sure the environment was not already taken.
|
||||
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
if TAKEN.fetch_or(true, Ordering::SeqCst) {
|
||||
return None;
|
||||
}
|
||||
Some(TockEnv {
|
||||
/// # Panics
|
||||
///
|
||||
/// - If called a second time.
|
||||
pub fn new() -> 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 {},
|
||||
storage: false,
|
||||
upgrade_storage: false,
|
||||
})
|
||||
store,
|
||||
upgrade_storage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new storage instance.
|
||||
/// Returns the unique storage instance.
|
||||
///
|
||||
/// # Safety
|
||||
/// # Panics
|
||||
///
|
||||
/// It is probably technically memory-safe to hame multiple storage instances at the same time, but
|
||||
/// for extra precaution we mark the function as unsafe. To ensure correct usage, this function
|
||||
/// should only be called if the previous storage instance was dropped.
|
||||
// This function is exposed for example binaries testing the hardware. This could probably be
|
||||
// cleaned up by having the persistent store return its storage.
|
||||
pub unsafe fn steal_storage() -> StorageResult<SyscallStorage> {
|
||||
SyscallStorage::new()
|
||||
/// - 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 {
|
||||
@@ -79,8 +78,8 @@ impl FirmwareProtection for TockEnv {
|
||||
impl Env for TockEnv {
|
||||
type Rng = TockRng256;
|
||||
type UserPresence = Self;
|
||||
type Storage = SyscallStorage;
|
||||
type UpgradeStorage = SyscallUpgradeStorage;
|
||||
type Storage = TockStorage;
|
||||
type UpgradeStorage = TockUpgradeStorage;
|
||||
type FirmwareProtection = Self;
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
@@ -91,14 +90,12 @@ impl Env for TockEnv {
|
||||
self
|
||||
}
|
||||
|
||||
fn storage(&mut self) -> StorageResult<Self::Storage> {
|
||||
assert_once(&mut self.storage);
|
||||
unsafe { steal_storage() }
|
||||
fn store(&mut self) -> &mut Store<Self::Storage> {
|
||||
&mut self.store
|
||||
}
|
||||
|
||||
fn upgrade_storage(&mut self) -> StorageResult<Self::UpgradeStorage> {
|
||||
assert_once(&mut self.upgrade_storage);
|
||||
SyscallUpgradeStorage::new()
|
||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||
self.upgrade_storage.as_mut()
|
||||
}
|
||||
|
||||
fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection {
|
||||
@@ -106,12 +103,6 @@ impl Env for TockEnv {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts a boolean is false and sets it to true.
|
||||
fn assert_once(b: &mut bool) {
|
||||
assert!(!*b);
|
||||
*b = true;
|
||||
}
|
||||
|
||||
// Returns whether the keepalive was sent, or false if cancelled.
|
||||
fn send_keepalive_up_needed(
|
||||
cid: ChannelID,
|
||||
@@ -146,7 +137,7 @@ fn send_keepalive_up_needed(
|
||||
}
|
||||
match processed_packet {
|
||||
ProcessedPacket::InitPacket { cmd, .. } => {
|
||||
if cmd == CtapHid::COMMAND_CANCEL {
|
||||
if cmd == CtapHidCommand::Cancel as u8 {
|
||||
// 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();
|
||||
|
||||
20
src/env/tock/storage.rs
vendored
20
src/env/tock/storage.rs
vendored
@@ -115,7 +115,7 @@ fn erase_page(ptr: usize, page_length: usize) -> StorageResult<()> {
|
||||
block_command(DRIVER_NUMBER, command_nr::ERASE_PAGE, ptr, page_length)
|
||||
}
|
||||
|
||||
pub struct SyscallStorage {
|
||||
pub struct TockStorage {
|
||||
word_size: usize,
|
||||
page_size: usize,
|
||||
num_pages: usize,
|
||||
@@ -124,7 +124,7 @@ pub struct SyscallStorage {
|
||||
storage_locations: Vec<&'static [u8]>,
|
||||
}
|
||||
|
||||
impl SyscallStorage {
|
||||
impl TockStorage {
|
||||
/// Provides access to the embedded flash if available.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -134,8 +134,8 @@ impl SyscallStorage {
|
||||
/// - The page size is a power of two.
|
||||
/// - The page size is a multiple of the word size.
|
||||
/// - The storage is page-aligned.
|
||||
pub fn new() -> StorageResult<SyscallStorage> {
|
||||
let mut syscall = SyscallStorage {
|
||||
pub fn new() -> StorageResult<TockStorage> {
|
||||
let mut syscall = TockStorage {
|
||||
word_size: get_info(command_nr::get_info_nr::WORD_SIZE, 0)?,
|
||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||
num_pages: 0,
|
||||
@@ -175,7 +175,7 @@ impl SyscallStorage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for SyscallStorage {
|
||||
impl Storage for TockStorage {
|
||||
fn word_size(&self) -> usize {
|
||||
self.word_size
|
||||
}
|
||||
@@ -217,13 +217,13 @@ impl Storage for SyscallStorage {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SyscallUpgradeStorage {
|
||||
pub struct TockUpgradeStorage {
|
||||
page_size: usize,
|
||||
partition: ModRange,
|
||||
metadata: ModRange,
|
||||
}
|
||||
|
||||
impl SyscallUpgradeStorage {
|
||||
impl TockUpgradeStorage {
|
||||
/// Provides access to the other upgrade partition and metadata if available.
|
||||
///
|
||||
/// The implementation assumes that storage locations returned by the kernel through
|
||||
@@ -238,8 +238,8 @@ impl SyscallUpgradeStorage {
|
||||
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
||||
/// - not exclusive or,
|
||||
/// - not consecutive.
|
||||
pub fn new() -> StorageResult<SyscallUpgradeStorage> {
|
||||
let mut locations = SyscallUpgradeStorage {
|
||||
pub fn new() -> StorageResult<TockUpgradeStorage> {
|
||||
let mut locations = TockUpgradeStorage {
|
||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||
partition: ModRange::new_empty(),
|
||||
metadata: ModRange::new_empty(),
|
||||
@@ -287,7 +287,7 @@ impl SyscallUpgradeStorage {
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeStorage for SyscallUpgradeStorage {
|
||||
impl UpgradeStorage for TockUpgradeStorage {
|
||||
fn read_partition(&self, offset: usize, length: usize) -> StorageResult<&[u8]> {
|
||||
if length == 0 {
|
||||
return Err(StorageError::OutOfBounds);
|
||||
|
||||
Reference in New Issue
Block a user