132 lines
3.9 KiB
Rust
132 lines
3.9 KiB
Rust
|
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
|
||
|
}
|
||
|
}
|