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, 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 { 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 { 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 { 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 { todo!(); } pub fn read(&self, index: u16, name: &mut [u8], key: &mut [u8]) -> Result<(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; self.mem.read(index_name, name).map_err(|_| ())?; self.mem.read(index_key, key).map_err(|_| ())?; let len_name = name .iter() .position(|&n| n == 0) .unwrap_or(SECRET_KEY_NAME_LEN as usize); let len_key = key .iter() .position(|&n| n == 0) .unwrap_or(SECRET_KEY_MAX_LEN as usize); Ok((len_name, len_key)) } pub fn write(&mut self, index: u16, name: &[u8], key: &[u8]) -> Result { let index_name = index * SECRET_KEY_FULL_LEN; let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN; self.mem.write(index_name, name).map_err(|_| ())?; self.mem.write(index_key, key).map_err(|_| ())?; Ok(index) } pub fn delete(&mut self, index: u16) -> Option { // 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 soft_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 } pub fn hard_wipe_all_tokens(&mut self) { self.mem.erase(0, self.mem.capacity()).unwrap(); } }