chore(core/rust): WIP trezor.cache

rust_cache
Jan Pochyla 2 years ago
parent 47e2193e3b
commit e8f5eb97d3

@ -274,6 +274,8 @@ dependencies = [
"cty",
"glob",
"heapless",
"spin",
"zeroize",
]
[[package]]
@ -315,3 +317,9 @@ name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "zeroize"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608"

@ -32,6 +32,10 @@ split-debuginfo = "unpacked"
# Runtime dependencies
[dependencies.cstr_core]
version = "0.2.4"
default_features = false
[dependencies.cty]
version = "0.2.1"
@ -39,8 +43,13 @@ version = "0.2.1"
version = "0.7.3"
default_features = false
[dependencies.cstr_core]
version = "0.2.4"
[dependencies.spin]
version = "0.9.2"
default_features = false
features = ["mutex", "spin_mutex"]
[dependencies.zeroize]
version = "1.5.3"
default_features = false
# Build dependencies

@ -12,10 +12,11 @@ mod time;
#[cfg(feature = "ui_debug")]
mod trace;
mod trezorhal;
#[cfg(feature = "ui")]
#[macro_use]
mod ui;
mod lru;
mod storage;
mod util;
#[cfg(not(feature = "test"))]

@ -0,0 +1,132 @@
//! Tiny last-recently-used (LRU) cache.
//!
//! Heavily inspired by [uluru](https://github.com/servo/uluru) crate, with
//! minor changes (using the `heapless` crate for the backing vector, and
//! `u8` index type).
use core::mem;
use heapless::Vec;
pub struct LruCache<T, const N: usize> {
/// Values are stored together with indices of a doubly-linked list.
entries: Vec<Entry<T>, N>,
/// Points to the most-recently-used entry in `entries`.
head: u8,
/// Points to the least-recently-used entry in `entries`.
tail: u8,
}
struct Entry<T> {
val: T,
/// Index of previous (more recently used) entry in the linked-list.
prev: u8,
/// Index of next (less recently used) entry in the linked-list.
next: u8,
}
impl<T, const N: usize> Default for LruCache<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> LruCache<T, N> {
/// Create a new, empty, LRU cache.
pub const fn new() -> Self {
debug_assert!(N < u8::MAX as usize, "Capacity overflow");
Self {
entries: Vec::new(),
head: 0,
tail: 0,
}
}
pub fn insert(&mut self, val: T) -> Option<T> {
let entry = Entry {
val,
prev: 0,
next: 0,
};
if self.is_full() {
let tail = self.pop_tail();
let prev = mem::replace(&mut self.entries[tail as usize], entry);
self.push_head(tail);
Some(prev.val)
} else {
let _ = self.entries.push(entry);
self.push_head((self.len() - 1) as u8);
None
}
}
pub fn find(&mut self, mut pred: impl FnMut(&T) -> bool) -> Option<&mut T> {
for (i, entry) in self.entries.iter().enumerate() {
if pred(&entry.val) {
self.touch_index(i as u8);
return self.front_mut();
}
}
None
}
pub fn front(&self) -> Option<&T> {
self.entries.get(self.head as usize).map(|e| &e.val)
}
pub fn front_mut(&mut self) -> Option<&mut T> {
self.entries.get_mut(self.head as usize).map(|e| &mut e.val)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn is_full(&self) -> bool {
self.entries.is_full()
}
fn touch_index(&mut self, i: u8) {
if i != self.head {
self.remove(i);
self.push_head(i);
}
}
fn remove(&mut self, i: u8) {
let prev = self.entries[i as usize].prev;
let next = self.entries[i as usize].next;
if i == self.head {
self.head = next;
} else {
self.entries[prev as usize].next = next;
}
if i == self.tail {
self.tail = prev;
} else {
self.entries[next as usize].prev = prev;
}
}
fn push_head(&mut self, i: u8) {
if self.entries.len() == 1 {
self.tail = i;
} else {
self.entries[i as usize].next = self.head;
self.entries[self.head as usize].prev = i;
}
self.head = i;
}
fn pop_tail(&mut self) -> u8 {
let old_tail = self.tail;
let new_tail = self.entries[old_tail as usize].prev;
self.tail = new_tail;
old_tail
}
}

@ -0,0 +1,149 @@
use core::ops::Deref;
use spin::{Mutex, MutexGuard};
use zeroize::Zeroizing;
use crate::{lru::LruCache, trezorhal::random};
pub enum CacheError {
InvalidSessionId,
InvalidValue,
}
pub struct Cache {
sessions: LruCache<SessionCache, 10>,
active_id: Option<SessionId>,
}
impl Cache {
pub const fn new() -> Self {
Self {
sessions: LruCache::new(),
active_id: None,
}
}
pub fn global() -> MutexGuard<'static, Self> {
static GLOBAL_CACHE: Mutex<Cache> = Mutex::new(Cache::new());
GLOBAL_CACHE.lock()
}
pub fn start_session(&mut self, received_session_id_bytes: Option<&[u8]>) -> SessionId {
// If we have received a session ID, take a look to the cache and return an ID
// of existing entry.
let received_session_id =
received_session_id_bytes.and_then(|bytes| SessionId::try_from(bytes).ok());
if let Some(received_id) = received_session_id {
if self.sessions.find(|s| s.id == Some(received_id)).is_some() {
return received_id;
}
}
// Either we haven't received an ID, or the entry doesn't exist (because of
// previous eviction). Create a new session.
let id = SessionId::random();
self.sessions.insert(SessionCache::new(id));
self.active_id = Some(id);
id
}
pub fn end_session(&mut self) {
if let Some(active_id) = self.active_id {
if let Some(active_session) = self.sessions.find(|s| s.id == Some(active_id)) {
active_session.clear();
}
self.active_id = None;
}
}
pub fn has_active_session(&self) -> bool {
self.active_id.is_some()
}
}
pub struct SessionCache {
id: Option<SessionId>,
fields: Option<SessionFields>,
}
impl SessionCache {
fn new(id: SessionId) -> Self {
Self {
id: Some(id),
fields: Some(SessionFields::default()),
}
}
fn clear(&mut self) {
self.id = None;
self.fields = None;
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct SessionId([u8; Self::LENGTH]);
impl SessionId {
const LENGTH: usize = 32;
fn random() -> Self {
let mut id = [0; Self::LENGTH];
random::bytes(&mut id);
Self(id)
}
}
impl Deref for SessionId {
type Target = [u8; Self::LENGTH];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&[u8]> for SessionId {
type Error = CacheError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(
value
.try_into()
.map_err(|_| Self::Error::InvalidSessionId)?,
))
}
}
#[derive(Default)]
struct SessionFields {
seed: Field<64>,
auth_type: Field<2>,
auth_data: Field<128>,
nonce: Field<32>,
#[cfg(not(feature = "bitcoin_only"))]
derive_cardano: Field<1>,
#[cfg(not(feature = "bitcoin_only"))]
cardano_icarus_secret: Field<96>,
#[cfg(not(feature = "bitcoin_only"))]
cardano_icarus_trezor_secret: Field<96>,
#[cfg(not(feature = "bitcoin_only"))]
monero_live_refresh: Field<1>,
}
#[derive(Default)]
struct Field<const N: usize> {
data: Option<Zeroizing<[u8; N]>>,
}
impl<const N: usize> Field<N> {
fn set(&mut self, value: &[u8]) -> Result<(), CacheError> {
self.data = Some(Zeroizing::new(
value.try_into().map_err(|_| CacheError::InvalidValue)?,
));
Ok(())
}
fn unset(&mut self) {
self.data = None;
}
}

@ -1,6 +1,13 @@
extern "C" {
// trezor-crypto/rand.h
fn random_uniform(n: u32) -> u32;
fn random_uniform(n: cty::uint32_t) -> cty::uint32_t;
// trezor-crypto/rand.h
fn random_buffer(buf: *mut cty::uint8_t, len: cty::size_t);
}
pub fn bytes(buf: &mut [u8]) {
unsafe { random_buffer(buf.as_mut_ptr(), buf.len()) };
}
pub fn uniform(n: u32) -> u32 {

Loading…
Cancel
Save