aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/hash/mod.rs260
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--examples/stm32f7/Cargo.toml5
-rw-r--r--examples/stm32f7/src/bin/hash.rs49
5 files changed, 316 insertions, 4 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 70d4daf09..d8a4c65fa 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -68,7 +68,7 @@ rand_core = "0.6.3"
68sdio-host = "0.5.0" 68sdio-host = "0.5.0"
69critical-section = "1.1" 69critical-section = "1.1"
70#stm32-metapac = { version = "15" } 70#stm32-metapac = { version = "15" }
71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d" } 71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" }
72vcell = "0.1.3" 72vcell = "0.1.3"
73bxcan = "0.7.0" 73bxcan = "0.7.0"
74nb = "1.0.0" 74nb = "1.0.0"
@@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] }
87proc-macro2 = "1.0.36" 87proc-macro2 = "1.0.36"
88quote = "1.0.15" 88quote = "1.0.15"
89#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 89#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
90stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d", default-features = false, features = ["metadata"]} 90stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]}
91 91
92 92
93[features] 93[features]
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)]
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 941ba38cd..a612c2554 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f767zi to your chip name, if necessary. 8# Change stm32f777zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
@@ -28,6 +28,7 @@ rand_core = "0.6.3"
28critical-section = "1.1" 28critical-section = "1.1"
29embedded-storage = "0.3.1" 29embedded-storage = "0.3.1"
30static_cell = "2" 30static_cell = "2"
31sha2 = { version = "0.10.8", default-features = false }
31 32
32[profile.release] 33[profile.release]
33debug = 2 34debug = 2
diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs
new file mode 100644
index 000000000..1fd0e87eb
--- /dev/null
+++ b/examples/stm32f7/src/bin/hash.rs
@@ -0,0 +1,49 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_time::{Duration, Instant};
8use {defmt_rtt as _, panic_probe as _};
9
10use embassy_stm32::hash::*;
11use sha2::{Digest, Sha256};
12
13const TEST_STRING_1: &[u8] = b"hello world";
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! {
17 let config = Config::default();
18 let p = embassy_stm32::init(config);
19
20 let hw_start_time = Instant::now();
21
22 // Compute a digest in hardware.
23 let mut hw_hasher = Hash::new(p.HASH);
24 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8);
25 hw_hasher.update(&mut context, TEST_STRING_1);
26 let mut buffer: [u8; 32] = [0; 32];
27 let hw_digest = hw_hasher.finish(context, &mut buffer);
28
29 let hw_end_time = Instant::now();
30 let hw_execution_time = hw_end_time - hw_start_time;
31
32 let sw_start_time = Instant::now();
33
34 // Compute a digest in software.
35 let mut sw_hasher = Sha256::new();
36 sw_hasher.update(TEST_STRING_1);
37 let sw_digest = sw_hasher.finalize();
38
39 let sw_end_time = Instant::now();
40 let sw_execution_time = sw_end_time - sw_start_time;
41
42 info!("Hardware Digest: {:?}", hw_digest);
43 info!("Software Digest: {:?}", sw_digest[..]);
44 info!("Hardware Execution Time: {:?}", hw_execution_time);
45 info!("Software Execution Time: {:?}", sw_execution_time);
46 assert_eq!(*hw_digest, sw_digest[..]);
47
48 loop {}
49}