rustytoken/src/storage.rs

132 lines
3.9 KiB
Rust
Raw Normal View History

2023-04-27 03:50:18 +00:00
use arduino_hal::eeprom::Eeprom;
use randomize::PCG32;
pub const SECRET_KEY_MAX_LEN: u16 = 32;
pub const SECRET_KEY_NAME_LEN: u16 = 16;
pub const SECRET_KEY_FULL_LEN: u16 = SECRET_KEY_MAX_LEN + SECRET_KEY_NAME_LEN;
pub struct Tokens<'a> {
mem: &'a mut Eeprom,
pub current: Option<u16>,
capacity: u16,
rand: PCG32,
}
impl<'a> Tokens<'a> {
pub fn new(mem: &'a mut Eeprom, rand_seed: u64) -> Self {
let capacity = mem.capacity() / SECRET_KEY_FULL_LEN;
let mut tokens = Tokens {
mem,
capacity,
rand: PCG32::seed(rand_seed, 1),
current: None,
};
tokens.current = tokens.first();
tokens
}
pub fn search_free(&self) -> Option<u16> {
for n in 0..self.capacity {
let index = SECRET_KEY_FULL_LEN * n;
if self.mem.read_byte(index) == 255 {
return Some(n);
}
}
None
}
fn first(&self) -> Option<u16> {
for n in 0..self.capacity {
let index = SECRET_KEY_FULL_LEN * n;
if self.mem.read_byte(index) != 255 {
return Some(n);
}
}
return None;
}
pub fn next(&mut self) -> Option<u16> {
let mut index = self.current.unwrap();
for _ in 0..self.capacity {
index = (index + 1) % self.capacity;
if self.mem.read_byte(index * SECRET_KEY_FULL_LEN) != 255 {
self.current = Some(index);
return Some(index);
}
}
None
}
pub fn prev(&mut self) -> Option<u16> {
todo!();
}
pub fn read(&self, index: u16, name: &mut [u8], key: &mut [u8]) -> Option<(usize, usize)> {
name.fill(0);
key.fill(0);
let index_name = index * SECRET_KEY_FULL_LEN;
let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN;
match self.mem.read(index_name, name) {
Ok(n) => {}
Err(_) => return None,
}
match self.mem.read(index_key, key) {
Ok(n) => {}
Err(_) => return None,
}
let mut len_name = name
.iter()
.position(|&n| n == 0)
.unwrap_or(SECRET_KEY_NAME_LEN as usize);
let mut len_key = key
.iter()
.position(|&n| n == 0)
.unwrap_or(SECRET_KEY_MAX_LEN as usize);
Some((len_name, len_key))
}
pub fn write(&mut self, index: u16, name: &[u8], key: &[u8]) -> Option<u16> {
let index_name = index * SECRET_KEY_FULL_LEN;
let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN;
match self.mem.write(index_name, name) {
Ok(_) => {}
Err(_) => return None,
}
match self.mem.write(index_key, key) {
Ok(_) => {}
Err(_) => return None,
}
Some(index)
}
pub fn delete(&mut self, index: u16) -> Option<u16> {
// The Arduino's EEPROM memory has a maximum number of write cycles.
// To keep writes to a minimum, only the first byte of the token name is set to 0
// and a byte of the key is randomly chosen to be overwritten with
// another random value, so that it's unrecoverable.
let index_name = index * SECRET_KEY_FULL_LEN;
let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN;
let index_key = index_key + (self.rand.next_u32() % SECRET_KEY_MAX_LEN as u32) as u16;
let rand_byte = (self.rand.next_u32() % 255) as u8;
self.mem.write_byte(index_name, 255);
self.mem.write_byte(index_key, rand_byte);
Some(index)
}
pub fn wipe_all_tokens(&mut self) -> u8 {
let mut inc = 0;
for n in 0..self.capacity {
let index = SECRET_KEY_FULL_LEN * n;
if self.mem.read_byte(index) != 255 {
self.delete(index);
inc += 1;
}
}
inc
}
}