From fec26e896052cc0eac6bfa6415a4ebad5352d1d9 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 18 Feb 2024 21:40:18 -0500 Subject: [PATCH] Refactored ciphers into traits. --- embassy-stm32/src/cryp/mod.rs | 651 ++++++++++++++++++++++------------ 1 file changed, 431 insertions(+), 220 deletions(-) diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 447bcf2f8..29c1db12e 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -1,4 +1,6 @@ //! Crypto Accelerator (CRYP) +use core::marker::PhantomData; + use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac; @@ -9,51 +11,375 @@ use crate::{interrupt, peripherals, Peripheral}; const DES_BLOCK_SIZE: usize = 8; // 64 bits const AES_BLOCK_SIZE: usize = 16; // 128 bits +/// This trait encapsulates all cipher-specific behavior/ +pub trait Cipher<'c> { + /// Processing block size. Determined by the processor and the algorithm. + const BLOCK_SIZE: usize; + + /// Indicates whether the cipher requires the application to provide padding. + /// If `true`, no partial blocks will be accepted (a panic will occur). + const REQUIRES_PADDING: bool = false; + + /// Returns the symmetric key. + fn key(&self) -> &'c [u8]; + + /// Returns the initialization vector. + fn iv(&self) -> &[u8]; + + /// Sets the processor algorithm mode according to the associated cipher. + fn set_algomode(&self, p: &pac::cryp::Cryp); + + /// Performs any key preparation within the processor, if necessary. + fn prepare_key(&self, _p: &pac::cryp::Cryp) {} + + /// Performs any cipher-specific initialization. + fn init_phase(&self, _p: &pac::cryp::Cryp) {} + + /// Called prior to processing the last data block for cipher-specific operations. + fn pre_final_block(&self, _p: &pac::cryp::Cryp) {} + + /// Called after processing the last data block for cipher-specific operations. + fn post_final_block(&self, _p: &pac::cryp::Cryp, _dir: Direction, _int_data: &[u8; AES_BLOCK_SIZE]) {} +} + +/// This trait enables restriction of ciphers to specific key sizes. +pub trait CipherSized {} + +/// This trait enables restriction of a header phase to authenticated ciphers only. +pub trait CipherAuthenticated {} + +/// AES-ECB Cipher Mode +pub struct AesEcb<'c, const KEY_SIZE: usize> { + iv: &'c [u8; 0], + key: &'c [u8; KEY_SIZE], +} + +impl<'c, const KEY_SIZE: usize> AesEcb<'c, KEY_SIZE> { + /// Constructs a new AES-ECB cipher for a cryptographic operation. + pub fn new(key: &'c [u8; KEY_SIZE]) -> Self { + return Self { key: key, iv: &[0; 0] }; + } +} + +impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesEcb<'c, KEY_SIZE> { + const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + const REQUIRES_PADDING: bool = true; + + fn key(&self) -> &'c [u8] { + self.key + } + + fn iv(&self) -> &'c [u8] { + self.iv + } + + fn prepare_key(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(7)); + p.cr().modify(|w| w.set_algomode3(false)); + p.cr().modify(|w| w.set_crypen(true)); + while p.sr().read().busy() {} + } + + fn set_algomode(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(4)); + p.cr().modify(|w| w.set_algomode3(false)); + } +} + +impl<'c> CipherSized for AesEcb<'c, { 128 / 8 }> {} +impl<'c> CipherSized for AesEcb<'c, { 192 / 8 }> {} +impl<'c> CipherSized for AesEcb<'c, { 256 / 8 }> {} + +/// AES-CBC Cipher Mode +pub struct AesCbc<'c, const KEY_SIZE: usize> { + iv: &'c [u8; 16], + key: &'c [u8; KEY_SIZE], +} + +impl<'c, const KEY_SIZE: usize> AesCbc<'c, KEY_SIZE> { + /// Constructs a new AES-CBC cipher for a cryptographic operation. + pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self { + return Self { key: key, iv: iv }; + } +} + +impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCbc<'c, KEY_SIZE> { + const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + const REQUIRES_PADDING: bool = true; + + fn key(&self) -> &'c [u8] { + self.key + } + + fn iv(&self) -> &'c [u8] { + self.iv + } + + fn prepare_key(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(7)); + p.cr().modify(|w| w.set_algomode3(false)); + p.cr().modify(|w| w.set_crypen(true)); + while p.sr().read().busy() {} + } + + fn set_algomode(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(5)); + p.cr().modify(|w| w.set_algomode3(false)); + } +} + +impl<'c> CipherSized for AesCbc<'c, { 128 / 8 }> {} +impl<'c> CipherSized for AesCbc<'c, { 192 / 8 }> {} +impl<'c> CipherSized for AesCbc<'c, { 256 / 8 }> {} + +/// AES-CTR Cipher Mode +pub struct AesCtr<'c, const KEY_SIZE: usize> { + iv: &'c [u8; 16], + key: &'c [u8; KEY_SIZE], +} + +impl<'c, const KEY_SIZE: usize> AesCtr<'c, KEY_SIZE> { + /// Constructs a new AES-CTR cipher for a cryptographic operation. + pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self { + return Self { key: key, iv: iv }; + } +} + +impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCtr<'c, KEY_SIZE> { + const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + + fn key(&self) -> &'c [u8] { + self.key + } + + fn iv(&self) -> &'c [u8] { + self.iv + } + + fn set_algomode(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(6)); + p.cr().modify(|w| w.set_algomode3(false)); + } +} + +impl<'c> CipherSized for AesCtr<'c, { 128 / 8 }> {} +impl<'c> CipherSized for AesCtr<'c, { 192 / 8 }> {} +impl<'c> CipherSized for AesCtr<'c, { 256 / 8 }> {} + +///AES-GCM Cipher Mode +pub struct AesGcm<'c, const KEY_SIZE: usize> { + iv: [u8; 16], + key: &'c [u8; KEY_SIZE], +} + +impl<'c, const KEY_SIZE: usize> AesGcm<'c, KEY_SIZE> { + /// Constucts a new AES-GCM cipher for a cryptographic operation. + pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self { + let mut new_gcm = Self { key: key, iv: [0; 16] }; + new_gcm.iv[..12].copy_from_slice(iv); + new_gcm.iv[15] = 2; + new_gcm + } +} + +impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { + const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + + fn key(&self) -> &'c [u8] { + self.key + } + + fn iv(&self) -> &[u8] { + self.iv.as_slice() + } + + fn set_algomode(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(0)); + p.cr().modify(|w| w.set_algomode3(true)); + } + + fn init_phase(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_gcm_ccmph(0)); + p.cr().modify(|w| w.set_crypen(true)); + while p.cr().read().crypen() {} + } + + fn pre_final_block(&self, p: &pac::cryp::Cryp) { + //Handle special GCM partial block process. + p.cr().modify(|w| w.set_crypen(false)); + p.cr().modify(|w| w.set_algomode3(false)); + p.cr().modify(|w| w.set_algomode0(6)); + let iv1r = p.csgcmccmr(7).read() - 1; + p.init(1).ivrr().write_value(iv1r); + p.cr().modify(|w| w.set_crypen(true)); + } + + fn post_final_block(&self, p: &pac::cryp::Cryp, dir: Direction, int_data: &[u8; AES_BLOCK_SIZE]) { + if dir == Direction::Encrypt { + //Handle special GCM partial block process. + p.cr().modify(|w| w.set_crypen(false)); + p.cr().write(|w| w.set_algomode3(true)); + p.cr().write(|w| w.set_algomode0(0)); + p.init(1).ivrr().write_value(2); + p.cr().modify(|w| w.set_crypen(true)); + p.cr().modify(|w| w.set_gcm_ccmph(3)); + let mut index = 0; + let end_index = Self::BLOCK_SIZE; + while index < end_index { + let mut in_word: [u8; 4] = [0; 4]; + in_word.copy_from_slice(&int_data[index..index + 4]); + p.din().write_value(u32::from_ne_bytes(in_word)); + index += 4; + } + for _ in 0..4 { + p.dout().read(); + } + } + } +} + +impl<'c> CipherSized for AesGcm<'c, { 128 / 8 }> {} +impl<'c> CipherSized for AesGcm<'c, { 192 / 8 }> {} +impl<'c> CipherSized for AesGcm<'c, { 256 / 8 }> {} +impl<'c, const KEY_SIZE: usize> CipherAuthenticated for AesGcm<'c, KEY_SIZE> {} + +/// AES-GMAC Cipher Mode +pub struct AesGmac<'c, const KEY_SIZE: usize> { + iv: [u8; 16], + key: &'c [u8; KEY_SIZE], +} + +impl<'c, const KEY_SIZE: usize> AesGmac<'c, KEY_SIZE> { + /// Constructs a new AES-GMAC cipher for a cryptographic operation. + pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self { + let mut new_gmac = Self { key: key, iv: [0; 16] }; + new_gmac.iv[..12].copy_from_slice(iv); + new_gmac.iv[15] = 2; + new_gmac + } +} + +impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { + const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + + fn key(&self) -> &'c [u8] { + self.key + } + + fn iv(&self) -> &[u8] { + self.iv.as_slice() + } + + fn set_algomode(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_algomode0(0)); + p.cr().modify(|w| w.set_algomode3(true)); + } + + fn init_phase(&self, p: &pac::cryp::Cryp) { + p.cr().modify(|w| w.set_gcm_ccmph(0)); + p.cr().modify(|w| w.set_crypen(true)); + while p.cr().read().crypen() {} + } + + fn pre_final_block(&self, p: &pac::cryp::Cryp) { + //Handle special GCM partial block process. + p.cr().modify(|w| w.set_crypen(false)); + p.cr().modify(|w| w.set_algomode3(false)); + p.cr().modify(|w| w.set_algomode0(6)); + let iv1r = p.csgcmccmr(7).read() - 1; + p.init(1).ivrr().write_value(iv1r); + p.cr().modify(|w| w.set_crypen(true)); + } + + fn post_final_block(&self, p: &pac::cryp::Cryp, dir: Direction, int_data: &[u8; AES_BLOCK_SIZE]) { + if dir == Direction::Encrypt { + //Handle special GCM partial block process. + p.cr().modify(|w| w.set_crypen(false)); + p.cr().write(|w| w.set_algomode3(true)); + p.cr().write(|w| w.set_algomode0(0)); + p.init(1).ivrr().write_value(2); + p.cr().modify(|w| w.set_crypen(true)); + p.cr().modify(|w| w.set_gcm_ccmph(3)); + let mut index = 0; + let end_index = Self::BLOCK_SIZE; + while index < end_index { + let mut in_word: [u8; 4] = [0; 4]; + in_word.copy_from_slice(&int_data[index..index + 4]); + p.din().write_value(u32::from_ne_bytes(in_word)); + index += 4; + } + for _ in 0..4 { + p.dout().read(); + } + } + } +} + +impl<'c> CipherSized for AesGmac<'c, { 128 / 8 }> {} +impl<'c> CipherSized for AesGmac<'c, { 192 / 8 }> {} +impl<'c> CipherSized for AesGmac<'c, { 256 / 8 }> {} +impl<'c, const KEY_SIZE: usize> CipherAuthenticated for AesGmac<'c, KEY_SIZE> {} + +// struct AesCcm<'c, const KEY_SIZE: usize> { +// iv: &'c [u8], +// key: &'c [u8; KEY_SIZE], +// aad_len: usize, +// payload_len: usize, +// } + +// impl<'c, const KEY_SIZE: usize> AesCcm<'c, KEY_SIZE> { +// pub fn new(&self, key: &[u8; KEY_SIZE], iv: &[u8], aad_len: usize, payload_len: usize) { +// if iv.len() > 13 { +// panic!("CCM IV length must be 13 bytes or less."); +// } +// self.key = key; +// self.iv = iv; +// self.aad_len = aad_len; +// self.payload_len = payload_len; +// } +// } + +// impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCcm<'c, KEY_SIZE> { +// const BLOCK_SIZE: usize = AES_BLOCK_SIZE; + +// fn key(&self) -> &'c [u8] { +// self.key +// } + +// fn iv(&self) -> &'c [u8] { +// self.iv +// } + +// fn set_algomode(&self, p: &pac::cryp::Cryp) { +// p.cr().modify(|w| w.set_algomode0(1)); +// p.cr().modify(|w| w.set_algomode3(true)); +// } + +// fn init_phase(&self, p: &pac::cryp::Cryp) { +// todo!(); +// } +// } + +// impl<'c> CipherSized for AesCcm<'c, { 128 / 8 }> {} +// impl<'c> CipherSized for AesCcm<'c, { 192 / 8 }> {} +// impl<'c> CipherSized for AesCcm<'c, { 256 / 8 }> {} + /// Holds the state information for a cipher operation. /// Allows suspending/resuming of cipher operations. -pub struct Context<'c> { - algo: Algorithm, - mode: Mode, +pub struct Context<'c, C: Cipher<'c> + CipherSized> { + phantom_data: PhantomData<&'c C>, + cipher: &'c C, dir: Direction, last_block_processed: bool, aad_complete: bool, cr: u32, iv: [u32; 4], - key: &'c [u8], csgcmccm: [u32; 8], csgcm: [u32; 8], header_len: u64, payload_len: u64, } -/// Selects the encryption algorithm. -#[derive(PartialEq, Clone, Copy)] -pub enum Algorithm { - /// Advanced Encryption Standard - AES, - /// Data Encryption Standard - DES, - /// Triple-DES - TDES, -} - -/// Selects the cipher mode. -#[derive(PartialEq, Clone, Copy)] -pub enum Mode { - /// Electronic Codebook - ECB, - /// Cipher Block Chaining - CBC, - /// Counter Mode - CTR, - /// Galois Counter Mode - GCM, - /// Galois Message Authentication Code - GMAC, - /// Counter with CBC-MAC - CCM, -} - /// Selects whether the crypto processor operates in encryption or decryption mode. #[derive(PartialEq, Clone, Copy)] pub enum Direction { @@ -68,10 +394,6 @@ pub struct Cryp<'d, T: Instance> { _peripheral: PeripheralRef<'d, T>, } -/// Initialization vector of arbitrary length. -/// When an initialization vector is not needed, `None` may be supplied. -pub type InitVector<'v> = Option<&'v [u8]>; - impl<'d, T: Instance> Cryp<'d, T> { /// Create a new CRYP driver. pub fn new(peri: impl Peripheral<P = T> + 'd) -> Self { @@ -85,51 +407,31 @@ impl<'d, T: Instance> Cryp<'d, T> { /// Key size must be 128, 192, or 256 bits. /// Initialization vector must only be supplied if necessary. /// Panics if there is any mismatch in parameters, such as an incorrect IV length or invalid mode. - pub fn start<'c>(&self, key: &'c [u8], iv: InitVector, algo: Algorithm, mode: Mode, dir: Direction) -> Context<'c> { - let mut ctx = Context { - algo, - mode, + pub fn start<'c, C: Cipher<'c> + CipherSized>(&self, cipher: &'c C, dir: Direction) -> Context<'c, C> { + let mut ctx: Context<'c, C> = Context { dir, last_block_processed: false, cr: 0, iv: [0; 4], - key, csgcmccm: [0; 8], csgcm: [0; 8], aad_complete: false, header_len: 0, payload_len: 0, + cipher: cipher, + phantom_data: PhantomData, }; T::regs().cr().modify(|w| w.set_crypen(false)); - // Checks for correctness - if algo == Algorithm::AES { - let keylen = key.len() * 8; - let ivlen; - if let Some(iv) = iv { - ivlen = iv.len() * 8; - } else { - ivlen = 0; - } - match keylen { - 128 => T::regs().cr().modify(|w| w.set_keysize(0)), - 192 => T::regs().cr().modify(|w| w.set_keysize(1)), - 256 => T::regs().cr().modify(|w| w.set_keysize(2)), - _ => panic!("Key length must be 128, 192, or 256 bits."), - } + let key = ctx.cipher.key(); - if (mode == Mode::GCM) && (ivlen != 96) { - panic!("IV length must be 96 bits for GCM."); - } else if (mode == Mode::CBC) && (ivlen != 128) { - panic!("IV length must be 128 bits for CBC."); - } else if (mode == Mode::CCM) && (ivlen != 128) { - panic!("IV length must be 128 bits for CCM."); - } else if (mode == Mode::CTR) && (ivlen != 128) { - panic!("IV length must be 128 bits for CTR."); - } else if (mode == Mode::GMAC) && (ivlen != 96) { - panic!("IV length must be 96 bits for GMAC."); - } + if key.len() == (128 / 8) { + T::regs().cr().modify(|w| w.set_keysize(0)); + } else if key.len() == (192 / 8) { + T::regs().cr().modify(|w| w.set_keysize(1)); + } else if key.len() == (256 / 8) { + T::regs().cr().modify(|w| w.set_keysize(2)); } self.load_key(key); @@ -137,40 +439,9 @@ impl<'d, T: Instance> Cryp<'d, T> { // Set data type to 8-bit. This will match software implementations. T::regs().cr().modify(|w| w.set_datatype(2)); - self.prepare_key(&ctx); + ctx.cipher.prepare_key(&T::regs()); - if algo == Algorithm::AES { - match mode { - Mode::ECB => T::regs().cr().modify(|w| w.set_algomode0(4)), - Mode::CBC => T::regs().cr().modify(|w| w.set_algomode0(5)), - Mode::CTR => T::regs().cr().modify(|w| w.set_algomode0(6)), - Mode::GCM => T::regs().cr().modify(|w| w.set_algomode0(0)), - Mode::GMAC => T::regs().cr().modify(|w| w.set_algomode0(0)), - Mode::CCM => T::regs().cr().modify(|w| w.set_algomode0(1)), - } - match mode { - Mode::ECB => T::regs().cr().modify(|w| w.set_algomode3(false)), - Mode::CBC => T::regs().cr().modify(|w| w.set_algomode3(false)), - Mode::CTR => T::regs().cr().modify(|w| w.set_algomode3(false)), - Mode::GCM => T::regs().cr().modify(|w| w.set_algomode3(true)), - Mode::GMAC => T::regs().cr().modify(|w| w.set_algomode3(true)), - Mode::CCM => T::regs().cr().modify(|w| w.set_algomode3(true)), - } - } else if algo == Algorithm::DES { - T::regs().cr().modify(|w| w.set_algomode3(false)); - match mode { - Mode::ECB => T::regs().cr().modify(|w| w.set_algomode0(2)), - Mode::CBC => T::regs().cr().modify(|w| w.set_algomode0(3)), - _ => panic!("Only ECB and CBC modes are valid for DES."), - } - } else if algo == Algorithm::TDES { - T::regs().cr().modify(|w| w.set_algomode3(false)); - match mode { - Mode::ECB => T::regs().cr().modify(|w| w.set_algomode0(0)), - Mode::CBC => T::regs().cr().modify(|w| w.set_algomode0(1)), - _ => panic!("Only ECB and CBC modes are valid for TDES."), - } - } + ctx.cipher.set_algomode(&T::regs()); // Set encrypt/decrypt if dir == Direction::Encrypt { @@ -180,38 +451,27 @@ impl<'d, T: Instance> Cryp<'d, T> { } // Load the IV into the registers. - if let Some(iv) = iv { - let mut full_iv: [u8; 16] = [0; 16]; - full_iv[0..iv.len()].copy_from_slice(iv); - - if (mode == Mode::GCM) || (mode == Mode::GMAC) { - full_iv[15] = 2; - } - - let mut iv_idx = 0; - let mut iv_word: [u8; 4] = [0; 4]; - iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); - iv_idx += 4; - T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word)); - iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); - iv_idx += 4; - T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word)); - iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); - iv_idx += 4; - T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word)); - iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); - T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word)); - } + let iv = ctx.cipher.iv(); + let mut full_iv: [u8; 16] = [0; 16]; + full_iv[0..iv.len()].copy_from_slice(iv); + let mut iv_idx = 0; + let mut iv_word: [u8; 4] = [0; 4]; + iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); + iv_idx += 4; + T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word)); + iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); + iv_idx += 4; + T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word)); + iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); + iv_idx += 4; + T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word)); + iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); + T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word)); // Flush in/out FIFOs T::regs().cr().modify(|w| w.fflush()); - if mode == Mode::GCM { - // GCM init phase - T::regs().cr().modify(|w| w.set_gcm_ccmph(0)); - T::regs().cr().modify(|w| w.set_crypen(true)); - while T::regs().cr().read().crypen() {} - } + ctx.cipher.init_phase(&T::regs()); self.store_context(&mut ctx); @@ -224,42 +484,38 @@ impl<'d, T: Instance> Cryp<'d, T> { /// All AAD must be supplied to this function prior to starting the payload phase with `payload_blocking`. /// The AAD must be supplied in multiples of the block size (128 bits), except when supplying the last block. /// When supplying the last block of AAD, `last_aad_block` must be `true`. - pub fn aad_blocking(&self, ctx: &mut Context, aad: &[u8], last_aad_block: bool) { + pub fn aad_blocking<'c, C: Cipher<'c> + CipherSized + CipherAuthenticated>( + &self, + ctx: &mut Context<'c, C>, + aad: &[u8], + last_aad_block: bool, + ) { self.load_context(ctx); - let block_size; - if ctx.algo == Algorithm::DES { - block_size = DES_BLOCK_SIZE; - } else { - block_size = AES_BLOCK_SIZE; - } - let last_block_remainder = aad.len() % block_size; + let last_block_remainder = aad.len() % C::BLOCK_SIZE; // Perform checks for correctness. if ctx.aad_complete { panic!("Cannot update AAD after calling 'update'!") } - if (ctx.mode != Mode::GCM) && (ctx.mode != Mode::GMAC) && (ctx.mode != Mode::CCM) { - panic!("Associated data only valid for GCM, GMAC, and CCM modes.") - } if !last_aad_block { if last_block_remainder != 0 { - panic!("Input length must be a multiple of {} bytes.", block_size); + panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE); } } ctx.header_len += aad.len() as u64; - // GCM header phase + // Header phase T::regs().cr().modify(|w| w.set_crypen(false)); T::regs().cr().modify(|w| w.set_gcm_ccmph(1)); T::regs().cr().modify(|w| w.set_crypen(true)); // Load data into core, block by block. - let num_full_blocks = aad.len() / block_size; + let num_full_blocks = aad.len() / C::BLOCK_SIZE; for block in 0..num_full_blocks { - let mut index = block * block_size; - let end_index = index + block_size; + let mut index = block * C::BLOCK_SIZE; + let end_index = index + C::BLOCK_SIZE; // Write block in while index < end_index { let mut in_word: [u8; 4] = [0; 4]; @@ -276,7 +532,7 @@ impl<'d, T: Instance> Cryp<'d, T> { let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; last_block[..last_block_remainder].copy_from_slice(&aad[aad.len() - last_block_remainder..aad.len()]); let mut index = 0; - let end_index = block_size; + let end_index = C::BLOCK_SIZE; // Write block in while index < end_index { let mut in_word: [u8; 4] = [0; 4]; @@ -307,16 +563,16 @@ impl<'d, T: Instance> Cryp<'d, T> { /// Data must be a multiple of block size (128-bits for AES, 64-bits for DES) for CBC and ECB modes. /// Padding or ciphertext stealing must be managed by the application for these modes. /// Data must also be a multiple of block size unless `last_block` is `true`. - pub fn payload_blocking(&self, ctx: &mut Context, input: &[u8], output: &mut [u8], last_block: bool) { + pub fn payload_blocking<'c, C: Cipher<'c> + CipherSized>( + &self, + ctx: &mut Context<'c, C>, + input: &[u8], + output: &mut [u8], + last_block: bool, + ) { self.load_context(ctx); - let block_size; - if ctx.algo == Algorithm::DES { - block_size = DES_BLOCK_SIZE; - } else { - block_size = AES_BLOCK_SIZE; - } - let last_block_remainder = input.len() % block_size; + let last_block_remainder = input.len() % C::BLOCK_SIZE; // Perform checks for correctness. if !ctx.aad_complete && ctx.header_len > 0 { @@ -328,9 +584,6 @@ impl<'d, T: Instance> Cryp<'d, T> { T::regs().cr().modify(|w| w.fflush()); T::regs().cr().modify(|w| w.set_crypen(true)); } - if ctx.mode == Mode::GMAC { - panic!("GMAC works on header data only. Do not call this function for GMAC."); - } if ctx.last_block_processed { panic!("The last block has already been processed!"); } @@ -339,24 +592,23 @@ impl<'d, T: Instance> Cryp<'d, T> { } if !last_block { if last_block_remainder != 0 { - panic!("Input length must be a multiple of {} bytes.", block_size); + panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE); } } - if (ctx.mode == Mode::ECB) || (ctx.mode == Mode::CBC) { + if C::REQUIRES_PADDING { if last_block_remainder != 0 { - panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", block_size); + panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); } } - if last_block { ctx.last_block_processed = true; } // Load data into core, block by block. - let num_full_blocks = input.len() / block_size; + let num_full_blocks = input.len() / C::BLOCK_SIZE; for block in 0..num_full_blocks { - let mut index = block * block_size; - let end_index = index + block_size; + let mut index = block * C::BLOCK_SIZE; + let end_index = index + C::BLOCK_SIZE; // Write block in while index < end_index { let mut in_word: [u8; 4] = [0; 4]; @@ -364,8 +616,8 @@ impl<'d, T: Instance> Cryp<'d, T> { T::regs().din().write_value(u32::from_ne_bytes(in_word)); index += 4; } - let mut index = block * block_size; - let end_index = index + block_size; + let mut index = block * C::BLOCK_SIZE; + let end_index = index + C::BLOCK_SIZE; // Block until there is output to read. while !T::regs().sr().read().ofne() {} // Read block out @@ -378,21 +630,13 @@ impl<'d, T: Instance> Cryp<'d, T> { // Handle the final block, which is incomplete. if last_block_remainder > 0 { - if ctx.mode == Mode::GCM && ctx.dir == Direction::Encrypt { - //Handle special GCM partial block process. - T::regs().cr().modify(|w| w.set_crypen(false)); - T::regs().cr().modify(|w| w.set_algomode3(false)); - T::regs().cr().modify(|w| w.set_algomode0(6)); - let iv1r = T::regs().csgcmccmr(7).read() - 1; - T::regs().init(1).ivrr().write_value(iv1r); - T::regs().cr().modify(|w| w.set_crypen(true)); - } + ctx.cipher.pre_final_block(&T::regs()); let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; last_block[..last_block_remainder].copy_from_slice(&input[input.len() - last_block_remainder..input.len()]); let mut index = 0; - let end_index = block_size; + let end_index = C::BLOCK_SIZE; // Write block in while index < end_index { let mut in_word: [u8; 4] = [0; 4]; @@ -401,7 +645,7 @@ impl<'d, T: Instance> Cryp<'d, T> { index += 4; } let mut index = 0; - let end_index = block_size; + let end_index = C::BLOCK_SIZE; // Block until there is output to read. while !T::regs().sr().read().ofne() {} // Read block out @@ -416,41 +660,19 @@ impl<'d, T: Instance> Cryp<'d, T> { output[output_len - last_block_remainder..output_len] .copy_from_slice(&intermediate_data[0..last_block_remainder]); - if ctx.mode == Mode::GCM && ctx.dir == Direction::Encrypt { - //Handle special GCM partial block process. - T::regs().cr().modify(|w| w.set_crypen(false)); - T::regs().cr().write(|w| w.set_algomode3(true)); - T::regs().cr().write(|w| w.set_algomode0(0)); - T::regs().init(1).ivrr().write_value(2); - T::regs().cr().modify(|w| w.set_crypen(true)); - T::regs().cr().modify(|w| w.set_gcm_ccmph(3)); - let mut index = 0; - let end_index = block_size; - while index < end_index { - let mut in_word: [u8; 4] = [0; 4]; - in_word.copy_from_slice(&intermediate_data[index..index + 4]); - T::regs().din().write_value(u32::from_ne_bytes(in_word)); - index += 4; - } - for _ in 0..4 { - T::regs().dout().read(); - } - } + ctx.cipher.post_final_block(&T::regs(), ctx.dir, &intermediate_data); } ctx.payload_len += input.len() as u64; } /// This function only needs to be called for GCM, CCM, and GMAC modes to - /// generate an authentication tag. Calling this function on any other mode - /// does nothing except consumes the context. A buffer for the authentication - /// tag must be supplied. - pub fn finish_blocking(&self, mut ctx: Context, tag: &mut [u8; 16]) { - // Just consume the context if called for any other mode. - if (ctx.mode != Mode::GCM) || (ctx.mode != Mode::CCM) || (ctx.mode != Mode::GMAC) { - return; - } - + /// generate an authentication tag. + pub fn finish_blocking<'c, C: Cipher<'c> + CipherSized + CipherAuthenticated>( + &self, + mut ctx: Context<'c, C>, + tag: &mut [u8; 16], + ) { self.load_context(&mut ctx); T::regs().cr().modify(|w| w.set_crypen(false)); @@ -477,17 +699,6 @@ impl<'d, T: Instance> Cryp<'d, T> { T::regs().cr().modify(|w| w.set_crypen(false)); } - fn prepare_key(&self, ctx: &Context) { - if ctx.algo == Algorithm::AES && ctx.dir == Direction::Decrypt { - if (ctx.mode == Mode::ECB) || (ctx.mode == Mode::CBC) { - T::regs().cr().modify(|w| w.set_algomode0(7)); - T::regs().cr().modify(|w| w.set_algomode3(false)); - T::regs().cr().modify(|w| w.set_crypen(true)); - while T::regs().sr().read().busy() {} - } - } - } - fn load_key(&self, key: &[u8]) { // Load the key into the registers. let mut keyidx = 0; @@ -524,7 +735,7 @@ impl<'d, T: Instance> Cryp<'d, T> { T::regs().key(3).krr().write_value(u32::from_be_bytes(keyword)); } - fn store_context(&self, ctx: &mut Context) { + fn store_context<'c, C: Cipher<'c> + CipherSized>(&self, ctx: &mut Context<'c, C>) { // Wait for data block processing to finish. while !T::regs().sr().read().ifem() {} while T::regs().sr().read().ofne() {} @@ -545,7 +756,7 @@ impl<'d, T: Instance> Cryp<'d, T> { } } - fn load_context(&self, ctx: &Context) { + fn load_context<'c, C: Cipher<'c> + CipherSized>(&self, ctx: &Context<'c, C>) { // Reload state registers. T::regs().cr().write(|w| w.0 = ctx.cr); T::regs().init(0).ivlr().write_value(ctx.iv[0]); @@ -556,10 +767,10 @@ impl<'d, T: Instance> Cryp<'d, T> { T::regs().csgcmccmr(i).write_value(ctx.csgcmccm[i]); T::regs().csgcmr(i).write_value(ctx.csgcm[i]); } - self.load_key(ctx.key); + self.load_key(ctx.cipher.key()); // Prepare key if applicable. - self.prepare_key(ctx); + ctx.cipher.prepare_key(&T::regs()); T::regs().cr().write(|w| w.0 = ctx.cr); // Enable crypto processor.