use core::marker::PhantomData;
use cc2538_pac::{aes, AES};
pub struct NotSpecified;
pub struct Sha256Engine;
pub trait CryptoExt {
type Parts;
fn constrain(self) -> Self::Parts;
}
pub struct Crypto<STATE> {
aes: AES,
_state: PhantomData<STATE>,
}
impl CryptoExt for AES {
type Parts = Crypto<NotSpecified>;
fn constrain(self) -> Self::Parts {
Crypto {
aes: self,
_state: PhantomData,
}
}
}
const BLOCK_SIZE: usize = 64;
const OUTPUT_LEN: usize = 32;
#[derive(Debug, Clone, Copy)]
pub struct Sha256State {
length: u64,
state: [u32; 8],
curlen: u32,
buf: [u8; BLOCK_SIZE],
new_digest: bool,
final_digest: bool,
}
impl<STATE> Crypto<STATE> {
pub fn reset(&mut self) {
}
pub fn free(self) -> AES {
self.aes
}
}
impl Crypto<NotSpecified> {
pub fn sha256_engine(self) -> Crypto<Sha256Engine> {
Crypto {
aes: self.aes,
_state: PhantomData,
}
}
}
impl Crypto<Sha256Engine> {
pub fn sha256(&mut self, data: &[u8], digest: &mut [u8]) {
let mut state = Sha256State {
length: 0,
state: [0; 8],
curlen: 0,
buf: [0; BLOCK_SIZE],
new_digest: true,
final_digest: false,
};
debug_assert!(!data.is_empty());
debug_assert!(digest.len() == 32);
let mut offset = 0;
let mut len = data.len();
if self.is_in_use() {
return;
}
if len > 0 && state.new_digest {
if state.curlen == 0 && len > BLOCK_SIZE {
state
.buf
.copy_from_slice(&data[offset..offset + BLOCK_SIZE]);
self.new_hash(&mut state);
state.new_digest = false;
state.length += (BLOCK_SIZE << 3) as u64;
offset += BLOCK_SIZE;
len -= BLOCK_SIZE;
} else {
let n = usize::min(len, BLOCK_SIZE - state.curlen as usize);
state.buf[state.curlen as usize..n].copy_from_slice(&data[offset..offset + n]);
state.curlen += n as u32;
offset += n;
len -= n;
if state.curlen == BLOCK_SIZE as u32 && len > 0 {
self.new_hash(&mut state);
state.new_digest = false;
state.length += (BLOCK_SIZE << 3) as u64;
state.curlen = 0;
}
}
}
while len > 0 && !state.new_digest {
if state.curlen == 0 && len > BLOCK_SIZE {
state
.buf
.copy_from_slice(&data[offset..offset + BLOCK_SIZE]);
self.resume_hash(&mut state);
state.length += (BLOCK_SIZE << 3) as u64;
offset += BLOCK_SIZE;
len -= BLOCK_SIZE;
} else {
let n = usize::min(len, BLOCK_SIZE - state.curlen as usize);
state.buf[state.curlen as usize..n].copy_from_slice(&data[offset..offset + n]);
state.curlen += n as u32;
offset += n;
len -= n;
if state.curlen == BLOCK_SIZE as u32 && len > 0 {
self.resume_hash(&mut state);
state.length += (BLOCK_SIZE << 3) as u64;
state.curlen = 0;
}
}
}
self.finalize(&mut state);
digest.copy_from_slice(unsafe { &core::mem::transmute::<[u32; 8], [u8; 32]>(state.state) });
}
fn new_hash(&mut self, state: &mut Sha256State) {
self.aes.ctrl_int_cfg.write(|w| w.level().set_bit());
self.aes
.ctrl_int_en
.write(|w| w.dma_in_done().set_bit().result_av().set_bit());
self.aes
.ctrl_alg_sel
.write(|w| w.tag().set_bit().hash().set_bit());
self.aes.ctrl_int_clr.write(|w| w.result_av().set_bit());
self.aes
.hash_mode_in
.write(|w| w.sha256_mode().set_bit().new_hash().set_bit());
if state.final_digest {
unsafe {
self.aes
.hash_length_in_l
.write(|w| w.length_in().bits((state.length & 0xffff_ffff) as u32));
self.aes
.hash_length_in_h
.write(|w| w.length_in().bits((state.length >> 32) as u32));
self.aes
.hash_io_buf_ctrl
.write(|w| w.pad_dma_message().set_bit());
}
}
self.aes.dmac_ch0_ctrl.write(|w| w.en().set_bit());
unsafe {
self.aes
.dmac_ch0_extaddr
.write(|w| w.addr().bits(state.buf.as_ptr() as u32))
};
if state.final_digest {
unsafe {
self.aes
.dmac_ch0_dmalength
.write(|w| w.dmalen().bits(state.curlen as u16))
};
} else {
unsafe {
self.aes
.dmac_ch0_dmalength
.write(|w| w.dmalen().bits(BLOCK_SIZE as u16))
};
}
self.aes.dmac_ch1_ctrl.write(|w| w.en().set_bit());
unsafe {
self.aes
.dmac_ch1_extaddr
.write(|w| w.addr().bits(state.state.as_ptr() as u32));
self.aes
.dmac_ch1_dmalength
.write(|w| w.dmalen().bits(OUTPUT_LEN as u16));
}
while !self.is_completed() {}
self.aes
.ctrl_int_clr
.write(|w| w.dma_in_done().set_bit().result_av().set_bit());
unsafe {
self.aes.ctrl_alg_sel.write(|w| w.bits(0));
self.aes.aes_ctrl.write(|w| w.bits(0));
}
}
fn resume_hash(&mut self, state: &mut Sha256State) {
self.aes.ctrl_int_cfg.write(|w| w.level().set_bit());
self.aes
.ctrl_int_en
.write(|w| w.dma_in_done().set_bit().result_av().set_bit());
self.aes.ctrl_alg_sel.write(|w| w.hash().set_bit());
self.aes.ctrl_int_clr.write(|w| w.result_av().set_bit());
self.aes.hash_mode_in.write(|w| w.sha256_mode().set_bit());
if state.final_digest {
unsafe {
self.aes
.hash_length_in_l
.write(|w| w.length_in().bits((state.length & 0xffff_ffff) as u32));
self.aes
.hash_length_in_h
.write(|w| w.length_in().bits((state.length >> 32) as u32));
}
}
unsafe {
self.aes
.hash_digest_a
.write(|w| w.hash_digest().bits(state.state[0]));
self.aes
.hash_digest_b
.write(|w| w.hash_digest().bits(state.state[1]));
self.aes
.hash_digest_c
.write(|w| w.hash_digest().bits(state.state[2]));
self.aes
.hash_digest_d
.write(|w| w.hash_digest().bits(state.state[3]));
self.aes
.hash_digest_e
.write(|w| w.hash_digest().bits(state.state[4]));
self.aes
.hash_digest_f
.write(|w| w.hash_digest().bits(state.state[5]));
self.aes
.hash_digest_g
.write(|w| w.hash_digest().bits(state.state[6]));
self.aes
.hash_digest_h
.write(|w| w.hash_digest().bits(state.state[7]));
}
if state.final_digest {
self.aes
.hash_io_buf_ctrl
.write(|w| w.pad_dma_message().set_bit());
}
self.aes.dmac_ch0_ctrl.write(|w| w.en().set_bit());
unsafe {
self.aes
.dmac_ch0_extaddr
.write(|w| w.addr().bits(state.buf.as_ptr() as u32))
};
if state.final_digest {
unsafe {
self.aes
.dmac_ch0_dmalength
.write(|w| w.dmalen().bits(state.curlen as u16))
};
} else {
unsafe {
self.aes
.dmac_ch0_dmalength
.write(|w| w.dmalen().bits(BLOCK_SIZE as u16))
};
}
while !self.is_completed() {}
state.state[0] = self.aes.hash_digest_a.read().bits();
state.state[1] = self.aes.hash_digest_b.read().bits();
state.state[2] = self.aes.hash_digest_c.read().bits();
state.state[3] = self.aes.hash_digest_d.read().bits();
state.state[4] = self.aes.hash_digest_e.read().bits();
state.state[5] = self.aes.hash_digest_f.read().bits();
state.state[6] = self.aes.hash_digest_g.read().bits();
state.state[7] = self.aes.hash_digest_h.read().bits();
self.aes
.hash_io_buf_ctrl
.write(|w| w.output_full().set_bit());
self.aes
.ctrl_int_clr
.write(|w| w.dma_in_done().set_bit().result_av().set_bit());
unsafe {
self.aes.ctrl_alg_sel.write(|w| w.bits(0));
self.aes.aes_ctrl.write(|w| w.bits(0));
}
}
fn finalize(&mut self, state: &mut Sha256State) {
state.length += (state.curlen << 3) as u64;
state.final_digest = true;
if state.new_digest {
self.new_hash(state);
} else {
self.resume_hash(state);
}
state.new_digest = false;
state.final_digest = false;
}
fn is_completed(&mut self) -> bool {
self.aes.ctrl_int_stat.read().result_av().bit_is_set()
}
fn is_in_use(&mut self) -> bool {
self.aes.ctrl_alg_sel.read().bits() != 0
}
}