use crate::{peripherals::syscon::Syscon, raw, typestates::init_state};
pub struct Started;
pub struct Enrolled;
#[derive(Debug)]
pub enum Error {
InitializationFailed,
CommandFailedToStart,
CommandFailed,
NotAllowed,
}
pub type Result<T> = core::result::Result<T, Error>;
pub enum KeyDestination {
AES = 0,
PRINCE1 = 1,
PRINCE2 = 2,
PRINCE3 = 3,
OUTPUT,
}
crate::wrap_stateful_peripheral!(Puf, PUF);
impl<State> Puf<State> {
pub fn enabled(mut self, syscon: &mut Syscon) -> Result<Puf<init_state::Enabled>> {
syscon.enable_clock(&mut self.raw);
self.raw.pwrctrl.write(|w| w.ramon().set_bit());
while self.raw.stat.read().busy().bit_is_set() {}
let stat = self.raw.stat.read();
if stat.error().bit_is_set() || stat.success().bit_is_clear() {
Err(Error::InitializationFailed)
} else {
Ok(Puf {
raw: self.raw,
_state: init_state::Enabled(()),
})
}
}
pub fn disabled(mut self, syscon: &mut Syscon) -> Puf<init_state::Disabled> {
syscon.disable_clock(&mut self.raw);
self.raw.pwrctrl.write(|w| w.ramon().clear_bit());
Puf {
raw: self.raw,
_state: init_state::Disabled,
}
}
}
impl<T> Puf<init_state::Enabled<T>> {
fn wait_for_cmd(&self) -> Result<()> {
while self.raw.stat.read().busy().bit_is_clear()
&& self.raw.stat.read().error().bit_is_clear()
{}
if self.raw.stat.read().error().bit_is_set() {
return Err(Error::CommandFailedToStart);
}
Ok(())
}
fn check_success(&self) -> Result<()> {
if self.raw.stat.read().success().bit_is_clear() {
return Err(Error::CommandFailed);
}
Ok(())
}
fn read_data(&self, data: &mut [u8]) {
let mut count = 0;
while self.raw.stat.read().busy().bit_is_set() {
if self.raw.stat.read().codeoutavail().bit_is_set() {
let word = self.raw.codeoutput.read().bits();
data[count..count + 4].copy_from_slice(&word.to_ne_bytes());
count += 4;
}
}
}
pub fn generate_key(&self, key_size: u32, key_index: u8, key_code: &mut [u8]) -> Result<()> {
if self.raw.allow.read().allowsetkey().bit_is_clear() {
return Err(Error::NotAllowed);
}
assert!(key_size >= 64);
assert!(key_index < 16);
key_code.fill(0);
self.raw
.keysize
.write(|w| unsafe { w.bits((key_size >> 6) & 0x3f) });
self.raw
.keyindex
.write(|w| unsafe { w.bits(((key_index) & 0x0f) as u32) });
self.raw.ctrl.write(|w| w.generatekey().set_bit());
self.wait_for_cmd()?;
self.read_data(key_code);
self.check_success()?;
Ok(())
}
pub fn set_key(
&self,
_key_size: u32,
_key_index: u8,
_user_key: &[u8],
_key_code: &mut [u8],
) -> Result<()> {
unimplemented!();
}
pub fn version(&self) -> u32 {
self.raw.version.read().bits()
}
pub fn reset(&self) {
unimplemented!();
}
}
impl Puf<init_state::Enabled> {
pub fn enroll(self, ac_buffer: &mut [u8; 1192]) -> Result<Puf<init_state::Enabled<Enrolled>>> {
if self.raw.allow.read().allowenroll().bit_is_clear() {
return Err(Error::NotAllowed);
}
ac_buffer.fill(0);
self.raw.ctrl.write(|w| w.enroll().set_bit());
self.wait_for_cmd()?;
self.read_data(ac_buffer);
self.check_success()?;
Ok(Puf {
raw: self.raw,
_state: init_state::Enabled(Enrolled),
})
}
fn do_start(&mut self, ac_buffer: &[u8; 1192]) -> Result<()> {
self.raw.ctrl.write(|w| w.start().set_bit());
self.wait_for_cmd()?;
let mut word_buf = [0u8; 4];
let mut i = 0;
while self.raw.stat.read().busy().bit_is_set() {
if self.raw.stat.read().codeinreq().bit_is_set() {
word_buf.copy_from_slice(&ac_buffer[i..i + 4]);
let word = u32::from_ne_bytes(word_buf);
self.raw.codeinput.write(|w| unsafe { w.bits(word) });
i += 4;
}
}
self.check_success()
}
pub fn start(mut self, ac_buffer: &[u8; 1192]) -> Result<Puf<init_state::Enabled<Started>>> {
if self.raw.allow.read().allowstart().bit_is_clear() {
Err(Error::NotAllowed)
} else {
self.do_start(ac_buffer)?;
Ok(Puf {
raw: self.raw,
_state: init_state::Enabled(Started),
})
}
}
pub fn try_start(
mut self,
ac_buffer: &[u8; 1192],
) -> Result<Puf<init_state::Enabled<Started>>> {
if self.raw.allow.read().allowstart().bit_is_set() {
self.do_start(ac_buffer)?;
}
Ok(Puf {
raw: self.raw,
_state: init_state::Enabled(Started),
})
}
}
impl Puf<init_state::Enabled<Started>> {
pub fn get_key(
&self,
key_destination: raw::puf::keyenable::KEY_A,
key_code: &[u8],
key: &mut [u8],
) -> Result<usize> {
if self.raw.allow.read().allowgetkey().bit_is_clear() {
return Err(Error::NotAllowed);
}
self.raw
.keyenable
.write(|w| w.key().variant(key_destination));
self.raw.ctrl.write(|w| w.getkey().set_bit());
self.wait_for_cmd()?;
let mut word_buf = [0u8; 4];
let mut count_in = 0;
let mut count_out = 0;
while self.raw.stat.read().busy().bit_is_set() {
if self.raw.stat.read().codeinreq().bit_is_set() {
word_buf.copy_from_slice(&key_code[count_in..count_in + 4]);
let word = u32::from_ne_bytes(word_buf);
self.raw.codeinput.write(|w| unsafe { w.bits(word) });
count_in += 4;
}
if self.raw.stat.read().keyoutavail().bit_is_set() {
self.raw.keyindex.read().bits();
let word = self.raw.keyoutput.read().bits();
key[count_out..count_out + 4].copy_from_slice(&word.to_ne_bytes());
count_out += 4;
}
}
self.check_success()?;
Ok(count_out)
}
}
impl<State> core::fmt::Debug for Puf<State> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f).unwrap();
writeln!(f, " control = x{:X}", self.raw.ctrl.read().bits()).unwrap();
writeln!(f, " ramstatus= x{:X}", self.raw.pwrctrl.read().bits()).unwrap();
writeln!(f, " status = x{:X}", self.raw.stat.read().bits()).unwrap();
writeln!(f, " if-status= x{:X}", self.raw.ifstat.read().bits()).unwrap();
writeln!(f, " allow = x{:X}", self.raw.allow.read().bits()).unwrap();
writeln!(f, " keyindex = x{:X}", self.raw.keyindex.read().bits())
}
}