diff options
| author | Rasmus Melchior Jacobsen <[email protected]> | 2023-04-11 07:36:23 +0200 |
|---|---|---|
| committer | Rasmus Melchior Jacobsen <[email protected]> | 2023-04-11 07:36:23 +0200 |
| commit | d8c92c53d647b170cb49ede1b608e58c2dd676d2 (patch) | |
| tree | d4fb6ee79087b598cad5d5fe5ce023626d4a21be /embassy-boot | |
| parent | 05b2b2fb5f0f8d52689057c22dd2fa026d6cc796 (diff) | |
| parent | 1f25d2ba8335300368b32f9ceedf163376dfdb6f (diff) | |
Merge remote-tracking branch 'upstream/master' into u32-partition
Diffstat (limited to 'embassy-boot')
| -rw-r--r-- | embassy-boot/boot/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs | 30 | ||||
| -rw-r--r-- | embassy-boot/boot/src/digest_adapters/mod.rs | 5 | ||||
| -rw-r--r-- | embassy-boot/boot/src/digest_adapters/salty.rs | 29 | ||||
| -rw-r--r-- | embassy-boot/boot/src/firmware_updater.rs | 128 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 1 |
6 files changed, 160 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] |
| 26 | defmt = { version = "0.3", optional = true } | 26 | defmt = { version = "0.3", optional = true } |
| 27 | digest = "0.10" | ||
| 27 | log = { version = "0.4", optional = true } | 28 | log = { version = "0.4", optional = true } |
| 28 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } | 29 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } |
| 29 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | 30 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } |
| @@ -37,6 +38,7 @@ log = "0.4" | |||
| 37 | env_logger = "0.9" | 38 | env_logger = "0.9" |
| 38 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version | 39 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version |
| 39 | futures = { version = "0.3", features = ["executor"] } | 40 | futures = { version = "0.3", features = ["executor"] } |
| 41 | sha1 = "0.10.5" | ||
| 40 | 42 | ||
| 41 | [dev-dependencies.ed25519-dalek] | 43 | [dev-dependencies.ed25519-dalek] |
| 42 | default_features = false | 44 | default_features = false |
| @@ -47,4 +49,4 @@ ed25519-dalek = ["dep:ed25519-dalek", "_verify"] | |||
| 47 | ed25519-salty = ["dep:salty", "_verify"] | 49 | ed25519-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 @@ | |||
| 1 | use digest::typenum::U64; | ||
| 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||
| 3 | use ed25519_dalek::Digest as _; | ||
| 4 | |||
| 5 | pub struct Sha512(ed25519_dalek::Sha512); | ||
| 6 | |||
| 7 | impl Default for Sha512 { | ||
| 8 | fn default() -> Self { | ||
| 9 | Self(ed25519_dalek::Sha512::new()) | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | impl Update for Sha512 { | ||
| 14 | fn update(&mut self, data: &[u8]) { | ||
| 15 | self.0.update(data) | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | impl 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 | |||
| 26 | impl OutputSizeUser for Sha512 { | ||
| 27 | type OutputSize = U64; | ||
| 28 | } | ||
| 29 | |||
| 30 | impl 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")] | ||
| 2 | pub(crate) mod ed25519_dalek; | ||
| 3 | |||
| 4 | #[cfg(feature = "ed25519-salty")] | ||
| 5 | pub(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 @@ | |||
| 1 | use digest::typenum::U64; | ||
| 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||
| 3 | |||
| 4 | pub struct Sha512(salty::Sha512); | ||
| 5 | |||
| 6 | impl Default for Sha512 { | ||
| 7 | fn default() -> Self { | ||
| 8 | Self(salty::Sha512::new()) | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | impl Update for Sha512 { | ||
| 13 | fn update(&mut self, data: &[u8]) { | ||
| 14 | self.0.update(data) | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | impl 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 | |||
| 25 | impl OutputSizeUser for Sha512 { | ||
| 26 | type OutputSize = U64; | ||
| 27 | } | ||
| 28 | |||
| 29 | impl HashMarker for Sha512 {} | ||
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 61c902ed0..6aedec003 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | use digest::Digest; | ||
| 1 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | 2 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; |
| 2 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | 3 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; |
| 3 | 4 | ||
| @@ -121,28 +122,27 @@ impl FirmwareUpdater { | |||
| 121 | 122 | ||
| 122 | #[cfg(feature = "ed25519-dalek")] | 123 | #[cfg(feature = "ed25519-dalek")] |
| 123 | { | 124 | { |
| 124 | use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; | 125 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; |
| 126 | |||
| 127 | use crate::digest_adapters::ed25519_dalek::Sha512; | ||
| 125 | 128 | ||
| 126 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | 129 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |
| 127 | 130 | ||
| 128 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | 131 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; |
| 129 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 132 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 130 | 133 | ||
| 131 | let mut digest = Sha512::new(); | 134 | let mut message = [0; 64]; |
| 132 | for offset in (0.._update_len).step_by(_aligned.len()) { | 135 | self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) |
| 133 | self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; | 136 | .await?; |
| 134 | let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||
| 135 | digest.update(&_aligned[..len]); | ||
| 136 | } | ||
| 137 | 137 | ||
| 138 | public_key | 138 | public_key.verify(&message, &signature).map_err(into_signature_error)? |
| 139 | .verify(&digest.finalize(), &signature) | ||
| 140 | .map_err(into_signature_error)? | ||
| 141 | } | 139 | } |
| 142 | #[cfg(feature = "ed25519-salty")] | 140 | #[cfg(feature = "ed25519-salty")] |
| 143 | { | 141 | { |
| 144 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | 142 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; |
| 145 | use salty::{PublicKey, Sha512, Signature}; | 143 | use salty::{PublicKey, Signature}; |
| 144 | |||
| 145 | use crate::digest_adapters::salty::Sha512; | ||
| 146 | 146 | ||
| 147 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | 147 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { |
| 148 | FirmwareUpdaterError::Signature(signature::Error::default()) | 148 | FirmwareUpdaterError::Signature(signature::Error::default()) |
| @@ -153,14 +153,10 @@ impl FirmwareUpdater { | |||
| 153 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | 153 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; |
| 154 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 154 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 155 | 155 | ||
| 156 | let mut digest = Sha512::new(); | 156 | let mut message = [0; 64]; |
| 157 | for offset in (0.._update_len).step_by(_aligned.len()) { | 157 | self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) |
| 158 | self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; | 158 | .await?; |
| 159 | let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||
| 160 | digest.update(&_aligned[..len]); | ||
| 161 | } | ||
| 162 | 159 | ||
| 163 | let message = digest.finalize(); | ||
| 164 | let r = public_key.verify(&message, &signature); | 160 | let r = public_key.verify(&message, &signature); |
| 165 | trace!( | 161 | trace!( |
| 166 | "Verifying with public key {}, signature {} and message {} yields ok: {}", | 162 | "Verifying with public key {}, signature {} and message {} yields ok: {}", |
| @@ -175,6 +171,25 @@ impl FirmwareUpdater { | |||
| 175 | self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await | 171 | self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await |
| 176 | } | 172 | } |
| 177 | 173 | ||
| 174 | /// Verify the update in DFU with any digest. | ||
| 175 | pub async fn hash<F: AsyncNorFlash, D: Digest>( | ||
| 176 | &mut self, | ||
| 177 | dfu_flash: &mut F, | ||
| 178 | update_len: usize, | ||
| 179 | chunk_buf: &mut [u8], | ||
| 180 | output: &mut [u8], | ||
| 181 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 182 | let update_len = update_len as u32; | ||
| 183 | let mut digest = D::new(); | ||
| 184 | for offset in (0..update_len).step_by(chunk_buf.len()) { | ||
| 185 | self.dfu.read(dfu_flash, offset, chunk_buf).await?; | ||
| 186 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | ||
| 187 | digest.update(&chunk_buf[..len]); | ||
| 188 | } | ||
| 189 | output.copy_from_slice(digest.finalize().as_slice()); | ||
| 190 | Ok(()) | ||
| 191 | } | ||
| 192 | |||
| 178 | /// Mark to trigger firmware swap on next boot. | 193 | /// Mark to trigger firmware swap on next boot. |
| 179 | /// | 194 | /// |
| 180 | /// # Safety | 195 | /// # Safety |
| @@ -328,28 +343,26 @@ impl FirmwareUpdater { | |||
| 328 | 343 | ||
| 329 | #[cfg(feature = "ed25519-dalek")] | 344 | #[cfg(feature = "ed25519-dalek")] |
| 330 | { | 345 | { |
| 331 | use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; | 346 | use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; |
| 347 | |||
| 348 | use crate::digest_adapters::ed25519_dalek::Sha512; | ||
| 332 | 349 | ||
| 333 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | 350 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |
| 334 | 351 | ||
| 335 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | 352 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; |
| 336 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | 353 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |
| 337 | 354 | ||
| 338 | let mut digest = Sha512::new(); | 355 | let mut message = [0; 64]; |
| 339 | for offset in (0.._update_len).step_by(_aligned.len()) { | 356 | self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; |
| 340 | self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; | ||
| 341 | let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||
| 342 | digest.update(&_aligned[..len]); | ||
| 343 | } | ||
| 344 | 357 | ||
| 345 | public_key | 358 | public_key.verify(&message, &signature).map_err(into_signature_error)? |
| 346 | .verify(&digest.finalize(), &signature) | ||
| 347 | .map_err(into_signature_error)? | ||
| 348 | } | 359 | } |
| 349 | #[cfg(feature = "ed25519-salty")] | 360 | #[cfg(feature = "ed25519-salty")] |
| 350 | { | 361 | { |
| 351 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | 362 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; |
| 352 | use salty::{PublicKey, Sha512, Signature}; | 363 | use salty::{PublicKey, Signature}; |
| 364 | |||
| 365 | use crate::digest_adapters::salty::Sha512; | ||
| 353 | 366 | ||
| 354 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | 367 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { |
| 355 | FirmwareUpdaterError::Signature(signature::Error::default()) | 368 | FirmwareUpdaterError::Signature(signature::Error::default()) |
| @@ -360,14 +373,9 @@ impl FirmwareUpdater { | |||
| 360 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | 373 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; |
| 361 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | 374 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |
| 362 | 375 | ||
| 363 | let mut digest = Sha512::new(); | 376 | let mut message = [0; 64]; |
| 364 | for offset in (0.._update_len).step_by(_aligned.len()) { | 377 | self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; |
| 365 | self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; | ||
| 366 | let len = core::cmp::min(_update_len - offset, _aligned.len()); | ||
| 367 | digest.update(&_aligned[..len]); | ||
| 368 | } | ||
| 369 | 378 | ||
| 370 | let message = digest.finalize(); | ||
| 371 | let r = public_key.verify(&message, &signature); | 379 | let r = public_key.verify(&message, &signature); |
| 372 | trace!( | 380 | trace!( |
| 373 | "Verifying with public key {}, signature {} and message {} yields ok: {}", | 381 | "Verifying with public key {}, signature {} and message {} yields ok: {}", |
| @@ -382,6 +390,25 @@ impl FirmwareUpdater { | |||
| 382 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) | 390 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) |
| 383 | } | 391 | } |
| 384 | 392 | ||
| 393 | /// Verify the update in DFU with any digest. | ||
| 394 | pub fn hash_blocking<F: NorFlash, D: Digest>( | ||
| 395 | &mut self, | ||
| 396 | dfu_flash: &mut F, | ||
| 397 | update_len: usize, | ||
| 398 | chunk_buf: &mut [u8], | ||
| 399 | output: &mut [u8], | ||
| 400 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 401 | let update_len = update_len as u32; | ||
| 402 | let mut digest = D::new(); | ||
| 403 | for offset in (0..update_len).step_by(chunk_buf.len()) { | ||
| 404 | self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; | ||
| 405 | let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | ||
| 406 | digest.update(&chunk_buf[..len]); | ||
| 407 | } | ||
| 408 | output.copy_from_slice(digest.finalize().as_slice()); | ||
| 409 | Ok(()) | ||
| 410 | } | ||
| 411 | |||
| 385 | /// Mark to trigger firmware swap on next boot. | 412 | /// Mark to trigger firmware swap on next boot. |
| 386 | /// | 413 | /// |
| 387 | /// # Safety | 414 | /// # Safety |
| @@ -478,3 +505,32 @@ impl FirmwareUpdater { | |||
| 478 | Ok(self.dfu) | 505 | Ok(self.dfu) |
| 479 | } | 506 | } |
| 480 | } | 507 | } |
| 508 | |||
| 509 | #[cfg(test)] | ||
| 510 | mod tests { | ||
| 511 | use futures::executor::block_on; | ||
| 512 | use sha1::{Digest, Sha1}; | ||
| 513 | |||
| 514 | use super::*; | ||
| 515 | use crate::mem_flash::MemFlash; | ||
| 516 | |||
| 517 | #[test] | ||
| 518 | fn can_verify_sha1() { | ||
| 519 | const STATE: Partition = Partition::new(0, 4096); | ||
| 520 | const DFU: Partition = Partition::new(65536, 131072); | ||
| 521 | |||
| 522 | let mut flash = MemFlash::<131072, 4096, 8>::default(); | ||
| 523 | |||
| 524 | let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||
| 525 | let mut to_write = [0; 4096]; | ||
| 526 | to_write[..7].copy_from_slice(update.as_slice()); | ||
| 527 | |||
| 528 | let mut updater = FirmwareUpdater::new(DFU, STATE); | ||
| 529 | block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); | ||
| 530 | let mut chunk_buf = [0; 2]; | ||
| 531 | let mut hash = [0; 20]; | ||
| 532 | block_on(updater.hash::<_, Sha1>(&mut flash, update.len(), &mut chunk_buf, &mut hash)).unwrap(); | ||
| 533 | |||
| 534 | assert_eq!(Sha1::digest(update).as_slice(), hash); | ||
| 535 | } | ||
| 536 | } | ||
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 8b94b6bdc..ef9333d36 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | mod fmt; | 6 | mod fmt; |
| 7 | 7 | ||
| 8 | mod boot_loader; | 8 | mod boot_loader; |
| 9 | mod digest_adapters; | ||
| 9 | mod firmware_updater; | 10 | mod firmware_updater; |
| 10 | mod mem_flash; | 11 | mod mem_flash; |
| 11 | mod partition; | 12 | mod partition; |
