aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src
diff options
context:
space:
mode:
authorCaleb Garrett <[email protected]>2024-01-31 21:21:36 -0500
committerCaleb Garrett <[email protected]>2024-01-31 21:21:36 -0500
commit6e9ddd46267fd0fce2333af4f15bfd86f6f17f4d (patch)
treeb5614fec7c6e3e32e13c01dac6e050e32831fd77 /embassy-stm32/src
parentdcce40c8a2eefb956ffadbfcc3db6c27cde55dab (diff)
Added hash module with blocking implementation. Included SHA256 example.
Diffstat (limited to 'embassy-stm32/src')
-rw-r--r--embassy-stm32/src/hash/mod.rs260
-rw-r--r--embassy-stm32/src/lib.rs2
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)
2use core::cmp::min;
3
4use embassy_hal_internal::{into_ref, PeripheralRef};
5use stm32_metapac::hash::regs::*;
6
7use crate::pac::HASH as PAC_HASH;
8use crate::peripherals::HASH;
9use crate::rcc::sealed::RccPeripheral;
10use crate::Peripheral;
11
12const NUM_CONTEXT_REGS: usize = 54;
13const HASH_BUFFER_LEN: usize = 68;
14const DIGEST_BLOCK_SIZE: usize = 64;
15
16///Hash algorithm selection
17#[derive(PartialEq)]
18pub 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)]
32pub 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.
45pub 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.
58pub struct Hash<'d> {
59 _peripheral: PeripheralRef<'d, HASH>,
60}
61
62impl<'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;
45pub mod flash; 45pub mod flash;
46#[cfg(fmc)] 46#[cfg(fmc)]
47pub mod fmc; 47pub mod fmc;
48#[cfg(hash)]
49pub mod hash;
48#[cfg(hrtim)] 50#[cfg(hrtim)]
49pub mod hrtim; 51pub mod hrtim;
50#[cfg(i2c)] 52#[cfg(i2c)]