diff options
| author | Caleb Garrett <[email protected]> | 2024-01-31 21:21:36 -0500 |
|---|---|---|
| committer | Caleb Garrett <[email protected]> | 2024-01-31 21:21:36 -0500 |
| commit | 6e9ddd46267fd0fce2333af4f15bfd86f6f17f4d (patch) | |
| tree | b5614fec7c6e3e32e13c01dac6e050e32831fd77 /embassy-stm32/src | |
| parent | dcce40c8a2eefb956ffadbfcc3db6c27cde55dab (diff) | |
Added hash module with blocking implementation. Included SHA256 example.
Diffstat (limited to 'embassy-stm32/src')
| -rw-r--r-- | embassy-stm32/src/hash/mod.rs | 260 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 |
2 files changed, 262 insertions, 0 deletions
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs new file mode 100644 index 000000000..e3d2d7b16 --- /dev/null +++ b/embassy-stm32/src/hash/mod.rs | |||
| @@ -0,0 +1,260 @@ | |||
| 1 | //! Hash generator (HASH) | ||
| 2 | use core::cmp::min; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 5 | use stm32_metapac::hash::regs::*; | ||
| 6 | |||
| 7 | use crate::pac::HASH as PAC_HASH; | ||
| 8 | use crate::peripherals::HASH; | ||
| 9 | use crate::rcc::sealed::RccPeripheral; | ||
| 10 | use crate::Peripheral; | ||
| 11 | |||
| 12 | const NUM_CONTEXT_REGS: usize = 54; | ||
| 13 | const HASH_BUFFER_LEN: usize = 68; | ||
| 14 | const DIGEST_BLOCK_SIZE: usize = 64; | ||
| 15 | |||
| 16 | ///Hash algorithm selection | ||
| 17 | #[derive(PartialEq)] | ||
| 18 | pub enum Algorithm { | ||
| 19 | /// SHA-1 Algorithm | ||
| 20 | SHA1 = 0, | ||
| 21 | /// MD5 Algorithm | ||
| 22 | MD5 = 1, | ||
| 23 | /// SHA-224 Algorithm | ||
| 24 | SHA224 = 2, | ||
| 25 | /// SHA-256 Algorithm | ||
| 26 | SHA256 = 3, | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Input data width selection | ||
| 30 | #[repr(u8)] | ||
| 31 | #[derive(Clone, Copy)] | ||
| 32 | pub enum DataType { | ||
| 33 | ///32-bit data, no data is swapped. | ||
| 34 | Width32 = 0, | ||
| 35 | ///16-bit data, each half-word is swapped. | ||
| 36 | Width16 = 1, | ||
| 37 | ///8-bit data, all bytes are swapped. | ||
| 38 | Width8 = 2, | ||
| 39 | ///1-bit data, all bits are swapped. | ||
| 40 | Width1 = 3, | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Stores the state of the HASH peripheral for suspending/resuming | ||
| 44 | /// digest calculation. | ||
| 45 | pub struct Context { | ||
| 46 | first_word_sent: bool, | ||
| 47 | buffer: [u8; HASH_BUFFER_LEN], | ||
| 48 | buflen: usize, | ||
| 49 | algo: Algorithm, | ||
| 50 | format: DataType, | ||
| 51 | imr: u32, | ||
| 52 | str: u32, | ||
| 53 | cr: u32, | ||
| 54 | csr: [u32; NUM_CONTEXT_REGS], | ||
| 55 | } | ||
| 56 | |||
| 57 | /// HASH driver. | ||
| 58 | pub struct Hash<'d> { | ||
| 59 | _peripheral: PeripheralRef<'d, HASH>, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl<'d> Hash<'d> { | ||
| 63 | /// Instantiates, resets, and enables the HASH peripheral. | ||
| 64 | pub fn new(peripheral: impl Peripheral<P = HASH> + 'd) -> Self { | ||
| 65 | HASH::enable_and_reset(); | ||
| 66 | into_ref!(peripheral); | ||
| 67 | let instance = Self { | ||
| 68 | _peripheral: peripheral, | ||
| 69 | }; | ||
| 70 | instance | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Starts computation of a new hash and returns the saved peripheral state. | ||
| 74 | pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { | ||
| 75 | // Define a context for this new computation. | ||
| 76 | let mut ctx = Context { | ||
| 77 | first_word_sent: false, | ||
| 78 | buffer: [0; 68], | ||
| 79 | buflen: 0, | ||
| 80 | algo: algorithm, | ||
| 81 | format: format, | ||
| 82 | imr: 0, | ||
| 83 | str: 0, | ||
| 84 | cr: 0, | ||
| 85 | csr: [0; NUM_CONTEXT_REGS], | ||
| 86 | }; | ||
| 87 | |||
| 88 | // Set the data type in the peripheral. | ||
| 89 | PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8)); | ||
| 90 | |||
| 91 | // Select the algorithm. | ||
| 92 | let mut algo0 = false; | ||
| 93 | let mut algo1 = false; | ||
| 94 | if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { | ||
| 95 | algo0 = true; | ||
| 96 | } | ||
| 97 | if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { | ||
| 98 | algo1 = true; | ||
| 99 | } | ||
| 100 | PAC_HASH.cr().modify(|w| w.set_algo0(algo0)); | ||
| 101 | PAC_HASH.cr().modify(|w| w.set_algo1(algo1)); | ||
| 102 | PAC_HASH.cr().modify(|w| w.set_init(true)); | ||
| 103 | |||
| 104 | // Store and return the state of the peripheral. | ||
| 105 | self.store_context(&mut ctx); | ||
| 106 | ctx | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Restores the peripheral state using the given context, | ||
| 110 | /// then updates the state with the provided data. | ||
| 111 | pub fn update(&mut self, ctx: &mut Context, input: &[u8]) { | ||
| 112 | let mut data_waiting = input.len() + ctx.buflen; | ||
| 113 | if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { | ||
| 114 | // There isn't enough data to digest a block, so append it to the buffer. | ||
| 115 | ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | ||
| 116 | ctx.buflen += input.len(); | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | |||
| 120 | //Restore the peripheral state. | ||
| 121 | self.load_context(&ctx); | ||
| 122 | |||
| 123 | let mut ilen_remaining = input.len(); | ||
| 124 | let mut input_start = 0; | ||
| 125 | |||
| 126 | // Handle first block. | ||
| 127 | if !ctx.first_word_sent { | ||
| 128 | let empty_len = ctx.buffer.len() - ctx.buflen; | ||
| 129 | let copy_len = min(empty_len, ilen_remaining); | ||
| 130 | // Fill the buffer. | ||
| 131 | if copy_len > 0 { | ||
| 132 | ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); | ||
| 133 | ctx.buflen += copy_len; | ||
| 134 | ilen_remaining -= copy_len; | ||
| 135 | input_start += copy_len; | ||
| 136 | } | ||
| 137 | assert_eq!(ctx.buflen, HASH_BUFFER_LEN); | ||
| 138 | self.accumulate(ctx.buffer.as_slice()); | ||
| 139 | data_waiting -= ctx.buflen; | ||
| 140 | ctx.buflen = 0; | ||
| 141 | ctx.first_word_sent = true; | ||
| 142 | } | ||
| 143 | |||
| 144 | if data_waiting < 64 { | ||
| 145 | // There isn't enough data remaining to process another block, so store it. | ||
| 146 | assert_eq!(ctx.buflen, 0); | ||
| 147 | ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); | ||
| 148 | ctx.buflen += ilen_remaining; | ||
| 149 | } else { | ||
| 150 | let mut total_data_sent = 0; | ||
| 151 | // First ingest the data in the buffer. | ||
| 152 | let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | ||
| 153 | if empty_len > 0 { | ||
| 154 | let copy_len = min(empty_len, ilen_remaining); | ||
| 155 | ctx.buffer[ctx.buflen..ctx.buflen + copy_len] | ||
| 156 | .copy_from_slice(&input[input_start..input_start + copy_len]); | ||
| 157 | ctx.buflen += copy_len; | ||
| 158 | ilen_remaining -= copy_len; | ||
| 159 | input_start += copy_len; | ||
| 160 | } | ||
| 161 | assert_eq!(ctx.buflen % 64, 0); | ||
| 162 | self.accumulate(&ctx.buffer[0..64]); | ||
| 163 | total_data_sent += ctx.buflen; | ||
| 164 | ctx.buflen = 0; | ||
| 165 | |||
| 166 | // Move any extra data to the now-empty buffer. | ||
| 167 | let leftovers = ilen_remaining % 64; | ||
| 168 | if leftovers > 0 { | ||
| 169 | assert!(ilen_remaining >= leftovers); | ||
| 170 | ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||
| 171 | ctx.buflen += leftovers; | ||
| 172 | ilen_remaining -= leftovers; | ||
| 173 | } | ||
| 174 | assert_eq!(ilen_remaining % 64, 0); | ||
| 175 | |||
| 176 | // Hash the remaining data. | ||
| 177 | self.accumulate(&input[input_start..input_start + ilen_remaining]); | ||
| 178 | |||
| 179 | total_data_sent += ilen_remaining; | ||
| 180 | assert_eq!(total_data_sent % 64, 0); | ||
| 181 | assert!(total_data_sent >= 64); | ||
| 182 | } | ||
| 183 | |||
| 184 | // Save the peripheral context. | ||
| 185 | self.store_context(ctx); | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Computes a digest for the given context. A slice of the provided digest buffer is returned. | ||
| 189 | /// The length of the returned slice is dependent on the digest length of the selected algorithm. | ||
| 190 | pub fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { | ||
| 191 | // Restore the peripheral state. | ||
| 192 | self.load_context(&ctx); | ||
| 193 | // Hash the leftover bytes, if any. | ||
| 194 | self.accumulate(&ctx.buffer[0..ctx.buflen]); | ||
| 195 | ctx.buflen = 0; | ||
| 196 | |||
| 197 | //Start the digest calculation. | ||
| 198 | PAC_HASH.str().write(|w| w.set_dcal(true)); | ||
| 199 | |||
| 200 | //Wait for completion. | ||
| 201 | while !PAC_HASH.sr().read().dcis() {} | ||
| 202 | |||
| 203 | //Return the digest. | ||
| 204 | let digest_words = match ctx.algo { | ||
| 205 | Algorithm::SHA1 => 5, | ||
| 206 | Algorithm::MD5 => 4, | ||
| 207 | Algorithm::SHA224 => 7, | ||
| 208 | Algorithm::SHA256 => 8, | ||
| 209 | }; | ||
| 210 | let mut i = 0; | ||
| 211 | while i < digest_words { | ||
| 212 | let word = PAC_HASH.hr(i).read(); | ||
| 213 | digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||
| 214 | i += 1; | ||
| 215 | } | ||
| 216 | &digest[0..digest_words * 4] | ||
| 217 | } | ||
| 218 | |||
| 219 | fn accumulate(&mut self, input: &[u8]) { | ||
| 220 | //Set the number of valid bits. | ||
| 221 | let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||
| 222 | PAC_HASH.str().modify(|w| w.set_nblw(num_valid_bits)); | ||
| 223 | |||
| 224 | let mut i = 0; | ||
| 225 | while i < input.len() { | ||
| 226 | let mut word: [u8; 4] = [0; 4]; | ||
| 227 | let copy_idx = min(i + 4, input.len()); | ||
| 228 | word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | ||
| 229 | PAC_HASH.din().write_value(u32::from_ne_bytes(word)); | ||
| 230 | i += 4; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Save the peripheral state to a context. | ||
| 235 | fn store_context(&mut self, ctx: &mut Context) { | ||
| 236 | while !PAC_HASH.sr().read().dinis() {} | ||
| 237 | ctx.imr = PAC_HASH.imr().read().0; | ||
| 238 | ctx.str = PAC_HASH.str().read().0; | ||
| 239 | ctx.cr = PAC_HASH.cr().read().0; | ||
| 240 | let mut i = 0; | ||
| 241 | while i < NUM_CONTEXT_REGS { | ||
| 242 | ctx.csr[i] = PAC_HASH.csr(i).read(); | ||
| 243 | i += 1; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Restore the peripheral state from a context. | ||
| 248 | fn load_context(&mut self, ctx: &Context) { | ||
| 249 | // Restore the peripheral state from the context. | ||
| 250 | PAC_HASH.imr().write_value(Imr { 0: ctx.imr }); | ||
| 251 | PAC_HASH.str().write_value(Str { 0: ctx.str }); | ||
| 252 | PAC_HASH.cr().write_value(Cr { 0: ctx.cr }); | ||
| 253 | PAC_HASH.cr().modify(|w| w.set_init(true)); | ||
| 254 | let mut i = 0; | ||
| 255 | while i < NUM_CONTEXT_REGS { | ||
| 256 | PAC_HASH.csr(i).write_value(ctx.csr[i]); | ||
| 257 | i += 1; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a465fccd8..cd1ede0fa 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -45,6 +45,8 @@ pub mod exti; | |||
| 45 | pub mod flash; | 45 | pub mod flash; |
| 46 | #[cfg(fmc)] | 46 | #[cfg(fmc)] |
| 47 | pub mod fmc; | 47 | pub mod fmc; |
| 48 | #[cfg(hash)] | ||
| 49 | pub mod hash; | ||
| 48 | #[cfg(hrtim)] | 50 | #[cfg(hrtim)] |
| 49 | pub mod hrtim; | 51 | pub mod hrtim; |
| 50 | #[cfg(i2c)] | 52 | #[cfg(i2c)] |
