aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot/boot
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-boot/boot')
-rw-r--r--embassy-boot/boot/Cargo.toml4
-rw-r--r--embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs30
-rw-r--r--embassy-boot/boot/src/digest_adapters/mod.rs5
-rw-r--r--embassy-boot/boot/src/digest_adapters/salty.rs29
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs108
-rw-r--r--embassy-boot/boot/src/lib.rs1
6 files changed, 140 insertions, 37 deletions
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml
index 3312c2f9f..c4ebdaff2 100644
--- a/embassy-boot/boot/Cargo.toml
+++ b/embassy-boot/boot/Cargo.toml
@@ -24,6 +24,7 @@ features = ["defmt"]
24 24
25[dependencies] 25[dependencies]
26defmt = { version = "0.3", optional = true } 26defmt = { version = "0.3", optional = true }
27digest = "0.10"
27log = { version = "0.4", optional = true } 28log = { version = "0.4", optional = true }
28ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } 29ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
29embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } 30embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
@@ -37,6 +38,7 @@ log = "0.4"
37env_logger = "0.9" 38env_logger = "0.9"
38rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version 39rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
39futures = { version = "0.3", features = ["executor"] } 40futures = { version = "0.3", features = ["executor"] }
41sha1 = "0.10.5"
40 42
41[dev-dependencies.ed25519-dalek] 43[dev-dependencies.ed25519-dalek]
42default_features = false 44default_features = false
@@ -47,4 +49,4 @@ ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
47ed25519-salty = ["dep:salty", "_verify"] 49ed25519-salty = ["dep:salty", "_verify"]
48 50
49#Internal features 51#Internal features
50_verify = [] \ No newline at end of file 52_verify = []
diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
new file mode 100644
index 000000000..a184d1c51
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
@@ -0,0 +1,30 @@
1use digest::typenum::U64;
2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
3use ed25519_dalek::Digest as _;
4
5pub struct Sha512(ed25519_dalek::Sha512);
6
7impl Default for Sha512 {
8 fn default() -> Self {
9 Self(ed25519_dalek::Sha512::new())
10 }
11}
12
13impl Update for Sha512 {
14 fn update(&mut self, data: &[u8]) {
15 self.0.update(data)
16 }
17}
18
19impl FixedOutput for Sha512 {
20 fn finalize_into(self, out: &mut digest::Output<Self>) {
21 let result = self.0.finalize();
22 out.as_mut_slice().copy_from_slice(result.as_slice())
23 }
24}
25
26impl OutputSizeUser for Sha512 {
27 type OutputSize = U64;
28}
29
30impl HashMarker for Sha512 {}
diff --git a/embassy-boot/boot/src/digest_adapters/mod.rs b/embassy-boot/boot/src/digest_adapters/mod.rs
new file mode 100644
index 000000000..9b4b4b60c
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/mod.rs
@@ -0,0 +1,5 @@
1#[cfg(feature = "ed25519-dalek")]
2pub(crate) mod ed25519_dalek;
3
4#[cfg(feature = "ed25519-salty")]
5pub(crate) mod salty;
diff --git a/embassy-boot/boot/src/digest_adapters/salty.rs b/embassy-boot/boot/src/digest_adapters/salty.rs
new file mode 100644
index 000000000..2b5dcf3af
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/salty.rs
@@ -0,0 +1,29 @@
1use digest::typenum::U64;
2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
3
4pub struct Sha512(salty::Sha512);
5
6impl Default for Sha512 {
7 fn default() -> Self {
8 Self(salty::Sha512::new())
9 }
10}
11
12impl Update for Sha512 {
13 fn update(&mut self, data: &[u8]) {
14 self.0.update(data)
15 }
16}
17
18impl FixedOutput for Sha512 {
19 fn finalize_into(self, out: &mut digest::Output<Self>) {
20 let result = self.0.finalize();
21 out.as_mut_slice().copy_from_slice(result.as_slice())
22 }
23}
24
25impl OutputSizeUser for Sha512 {
26 type OutputSize = U64;
27}
28
29impl HashMarker for Sha512 {}
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
index 22e3e6b00..2d1b26980 100644
--- a/embassy-boot/boot/src/firmware_updater.rs
+++ b/embassy-boot/boot/src/firmware_updater.rs
@@ -1,3 +1,4 @@
1use digest::Digest;
1use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; 2use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 3use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
3 4
@@ -128,25 +129,27 @@ impl FirmwareUpdater {
128 129
129 #[cfg(feature = "ed25519-dalek")] 130 #[cfg(feature = "ed25519-dalek")]
130 { 131 {
131 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; 132 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
133
134 use crate::digest_adapters::ed25519_dalek::Sha512;
132 135
133 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); 136 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
134 137
135 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; 138 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
136 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 139 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
137 140
138 let mut digest = Sha512::new(); 141 let mut message = [0; 64];
139 self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) 142 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
140 .await?; 143 .await?;
141 144
142 public_key 145 public_key.verify(&message, &signature).map_err(into_signature_error)?
143 .verify(&digest.finalize(), &signature)
144 .map_err(into_signature_error)?
145 } 146 }
146 #[cfg(feature = "ed25519-salty")] 147 #[cfg(feature = "ed25519-salty")]
147 { 148 {
148 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; 149 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
149 use salty::{PublicKey, Sha512, Signature}; 150 use salty::{PublicKey, Signature};
151
152 use crate::digest_adapters::salty::Sha512;
150 153
151 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { 154 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
152 FirmwareUpdaterError::Signature(signature::Error::default()) 155 FirmwareUpdaterError::Signature(signature::Error::default())
@@ -157,11 +160,10 @@ impl FirmwareUpdater {
157 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; 160 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
158 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 161 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
159 162
160 let mut digest = Sha512::new(); 163 let mut message = [0; 64];
161 self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) 164 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
162 .await?; 165 .await?;
163 166
164 let message = digest.finalize();
165 let r = public_key.verify(&message, &signature); 167 let r = public_key.verify(&message, &signature);
166 trace!( 168 trace!(
167 "Verifying with public key {}, signature {} and message {} yields ok: {}", 169 "Verifying with public key {}, signature {} and message {} yields ok: {}",
@@ -176,19 +178,21 @@ impl FirmwareUpdater {
176 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await 178 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
177 } 179 }
178 180
179 /// Iterate through the DFU and process all bytes with the provided closure. 181 /// Verify the update in DFU with any digest.
180 pub async fn incremental_hash<F: AsyncNorFlash>( 182 pub async fn hash<F: AsyncNorFlash, D: Digest>(
181 &mut self, 183 &mut self,
182 dfu_flash: &mut F, 184 dfu_flash: &mut F,
183 update_len: u32, 185 update_len: u32,
184 aligned: &mut [u8], 186 chunk_buf: &mut [u8],
185 mut update: impl FnMut(&[u8]), 187 output: &mut [u8],
186 ) -> Result<(), FirmwareUpdaterError> { 188 ) -> Result<(), FirmwareUpdaterError> {
187 for offset in (0..update_len).step_by(aligned.len()) { 189 let mut digest = D::new();
188 self.dfu.read(dfu_flash, offset, aligned).await?; 190 for offset in (0..update_len).step_by(chunk_buf.len()) {
189 let len = core::cmp::min((update_len - offset) as usize, aligned.len()); 191 self.dfu.read(dfu_flash, offset, chunk_buf).await?;
190 update(&aligned[..len]); 192 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
193 digest.update(&chunk_buf[..len]);
191 } 194 }
195 output.copy_from_slice(digest.finalize().as_slice());
192 Ok(()) 196 Ok(())
193 } 197 }
194 198
@@ -334,24 +338,26 @@ impl FirmwareUpdater {
334 338
335 #[cfg(feature = "ed25519-dalek")] 339 #[cfg(feature = "ed25519-dalek")]
336 { 340 {
337 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; 341 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
342
343 use crate::digest_adapters::ed25519_dalek::Sha512;
338 344
339 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); 345 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
340 346
341 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; 347 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
342 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; 348 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
343 349
344 let mut digest = Sha512::new(); 350 let mut message = [0; 64];
345 self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; 351 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
346 352
347 public_key 353 public_key.verify(&message, &signature).map_err(into_signature_error)?
348 .verify(&digest.finalize(), &signature)
349 .map_err(into_signature_error)?
350 } 354 }
351 #[cfg(feature = "ed25519-salty")] 355 #[cfg(feature = "ed25519-salty")]
352 { 356 {
353 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; 357 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
354 use salty::{PublicKey, Sha512, Signature}; 358 use salty::{PublicKey, Signature};
359
360 use crate::digest_adapters::salty::Sha512;
355 361
356 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { 362 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
357 FirmwareUpdaterError::Signature(signature::Error::default()) 363 FirmwareUpdaterError::Signature(signature::Error::default())
@@ -362,10 +368,9 @@ impl FirmwareUpdater {
362 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; 368 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
363 let signature = Signature::try_from(&signature).map_err(into_signature_error)?; 369 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
364 370
365 let mut digest = Sha512::new(); 371 let mut message = [0; 64];
366 self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; 372 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
367 373
368 let message = digest.finalize();
369 let r = public_key.verify(&message, &signature); 374 let r = public_key.verify(&message, &signature);
370 trace!( 375 trace!(
371 "Verifying with public key {}, signature {} and message {} yields ok: {}", 376 "Verifying with public key {}, signature {} and message {} yields ok: {}",
@@ -380,19 +385,21 @@ impl FirmwareUpdater {
380 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) 385 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
381 } 386 }
382 387
383 /// Iterate through the DFU and process all bytes with the provided closure. 388 /// Verify the update in DFU with any digest.
384 pub fn incremental_hash_blocking<F: NorFlash>( 389 pub fn hash_blocking<F: NorFlash, D: Digest>(
385 &mut self, 390 &mut self,
386 dfu_flash: &mut F, 391 dfu_flash: &mut F,
387 update_len: u32, 392 update_len: u32,
388 aligned: &mut [u8], 393 chunk_buf: &mut [u8],
389 mut update: impl FnMut(&[u8]), 394 output: &mut [u8],
390 ) -> Result<(), FirmwareUpdaterError> { 395 ) -> Result<(), FirmwareUpdaterError> {
391 for offset in (0..update_len).step_by(aligned.len()) { 396 let mut digest = D::new();
392 self.dfu.read_blocking(dfu_flash, offset, aligned)?; 397 for offset in (0..update_len).step_by(chunk_buf.len()) {
393 let len = core::cmp::min((update_len - offset) as usize, aligned.len()); 398 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?;
394 update(&aligned[..len]); 399 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
400 digest.update(&chunk_buf[..len]);
395 } 401 }
402 output.copy_from_slice(digest.finalize().as_slice());
396 Ok(()) 403 Ok(())
397 } 404 }
398 405
@@ -479,3 +486,32 @@ impl FirmwareUpdater {
479 Ok(self.dfu) 486 Ok(self.dfu)
480 } 487 }
481} 488}
489
490#[cfg(test)]
491mod tests {
492 use futures::executor::block_on;
493 use sha1::{Digest, Sha1};
494
495 use super::*;
496 use crate::tests::MemFlash;
497
498 #[test]
499 fn can_verify() {
500 const STATE: Partition = Partition::new(0, 4096);
501 const DFU: Partition = Partition::new(65536, 131072);
502
503 let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]);
504
505 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
506 let mut to_write = [0; 4096];
507 to_write[..7].copy_from_slice(update.as_slice());
508
509 let mut updater = FirmwareUpdater::new(DFU, STATE);
510 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap();
511 let mut chunk_buf = [0; 2];
512 let mut hash = [0; 20];
513 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
514
515 assert_eq!(Sha1::digest(update).as_slice(), hash);
516 }
517}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 6888a8055..da9055476 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -6,6 +6,7 @@
6mod fmt; 6mod fmt;
7 7
8mod boot_loader; 8mod boot_loader;
9mod digest_adapters;
9mod firmware_updater; 10mod firmware_updater;
10mod partition; 11mod partition;
11 12