Let hash functions take a digest::Digest trait
... and add adapters for current Sha512 implementations that does not inplement the Digest trait
This commit is contained in:
		| @@ -24,6 +24,7 @@ features = ["defmt"] | |||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
| defmt = { version = "0.3", optional = true } | defmt = { version = "0.3", optional = true } | ||||||
|  | digest = "0.10" | ||||||
| log = { version = "0.4", optional = true  } | log = { version = "0.4", optional = true  } | ||||||
| ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } | ||||||
| embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | ||||||
| @@ -37,6 +38,7 @@ log = "0.4" | |||||||
| env_logger = "0.9" | env_logger = "0.9" | ||||||
| rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version | ||||||
| futures = { version = "0.3", features = ["executor"] } | futures = { version = "0.3", features = ["executor"] } | ||||||
|  | sha1 = "0.10.5" | ||||||
|  |  | ||||||
| [dev-dependencies.ed25519-dalek] | [dev-dependencies.ed25519-dalek] | ||||||
| default_features = false | default_features = false | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | use digest::typenum::U64; | ||||||
|  | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||||||
|  | use ed25519_dalek::Digest as _; | ||||||
|  |  | ||||||
|  | pub struct Sha512(ed25519_dalek::Sha512); | ||||||
|  |  | ||||||
|  | impl Default for Sha512 { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self(ed25519_dalek::Sha512::new()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Update for Sha512 { | ||||||
|  |     fn update(&mut self, data: &[u8]) { | ||||||
|  |         self.0.update(data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FixedOutput for Sha512 { | ||||||
|  |     fn finalize_into(self, out: &mut digest::Output<Self>) { | ||||||
|  |         let result = self.0.finalize(); | ||||||
|  |         out.as_mut_slice().copy_from_slice(result.as_slice()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl OutputSizeUser for Sha512 { | ||||||
|  |     type OutputSize = U64; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl HashMarker for Sha512 {} | ||||||
							
								
								
									
										5
									
								
								embassy-boot/boot/src/digest_adapters/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								embassy-boot/boot/src/digest_adapters/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | #[cfg(feature = "ed25519-dalek")] | ||||||
|  | pub(crate) mod ed25519_dalek; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "ed25519-salty")] | ||||||
|  | pub(crate) mod salty; | ||||||
							
								
								
									
										29
									
								
								embassy-boot/boot/src/digest_adapters/salty.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								embassy-boot/boot/src/digest_adapters/salty.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | use digest::typenum::U64; | ||||||
|  | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||||||
|  |  | ||||||
|  | pub struct Sha512(salty::Sha512); | ||||||
|  |  | ||||||
|  | impl Default for Sha512 { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self(salty::Sha512::new()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Update for Sha512 { | ||||||
|  |     fn update(&mut self, data: &[u8]) { | ||||||
|  |         self.0.update(data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FixedOutput for Sha512 { | ||||||
|  |     fn finalize_into(self, out: &mut digest::Output<Self>) { | ||||||
|  |         let result = self.0.finalize(); | ||||||
|  |         out.as_mut_slice().copy_from_slice(result.as_slice()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl OutputSizeUser for Sha512 { | ||||||
|  |     type OutputSize = U64; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl HashMarker for Sha512 {} | ||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | use digest::Digest; | ||||||
| use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | ||||||
| use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | ||||||
|  |  | ||||||
| @@ -128,25 +129,27 @@ impl FirmwareUpdater { | |||||||
|  |  | ||||||
|         #[cfg(feature = "ed25519-dalek")] |         #[cfg(feature = "ed25519-dalek")] | ||||||
|         { |         { | ||||||
|             use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; |             use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | ||||||
|  |  | ||||||
|  |             use crate::digest_adapters::ed25519_dalek::Sha512; | ||||||
|  |  | ||||||
|             let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |             let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | ||||||
|  |  | ||||||
|             let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; |             let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | ||||||
|             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||||||
|  |  | ||||||
|             let mut digest = Sha512::new(); |             let mut message = [0; 64]; | ||||||
|             self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) |             self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) | ||||||
|                 .await?; |                 .await?; | ||||||
|  |  | ||||||
|             public_key |             public_key.verify(&message, &signature).map_err(into_signature_error)? | ||||||
|                 .verify(&digest.finalize(), &signature) |  | ||||||
|                 .map_err(into_signature_error)? |  | ||||||
|         } |         } | ||||||
|         #[cfg(feature = "ed25519-salty")] |         #[cfg(feature = "ed25519-salty")] | ||||||
|         { |         { | ||||||
|             use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; |             use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||||||
|             use salty::{PublicKey, Sha512, Signature}; |             use salty::{PublicKey, Signature}; | ||||||
|  |  | ||||||
|  |             use crate::digest_adapters::salty::Sha512; | ||||||
|  |  | ||||||
|             fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { |             fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | ||||||
|                 FirmwareUpdaterError::Signature(signature::Error::default()) |                 FirmwareUpdaterError::Signature(signature::Error::default()) | ||||||
| @@ -157,11 +160,10 @@ impl FirmwareUpdater { | |||||||
|             let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; |             let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||||||
|             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||||||
|  |  | ||||||
|             let mut digest = Sha512::new(); |             let mut message = [0; 64]; | ||||||
|             self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) |             self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) | ||||||
|                 .await?; |                 .await?; | ||||||
|  |  | ||||||
|             let message = digest.finalize(); |  | ||||||
|             let r = public_key.verify(&message, &signature); |             let r = public_key.verify(&message, &signature); | ||||||
|             trace!( |             trace!( | ||||||
|                 "Verifying with public key {}, signature {} and message {} yields ok: {}", |                 "Verifying with public key {}, signature {} and message {} yields ok: {}", | ||||||
| @@ -176,19 +178,21 @@ impl FirmwareUpdater { | |||||||
|         self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await |         self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Iterate through the DFU and process all bytes with the provided closure. |     /// Verify the update in DFU with any digest. | ||||||
|     pub async fn incremental_hash<F: AsyncNorFlash>( |     pub async fn hash<F: AsyncNorFlash, D: Digest>( | ||||||
|         &mut self, |         &mut self, | ||||||
|         dfu_flash: &mut F, |         dfu_flash: &mut F, | ||||||
|         update_len: u32, |         update_len: u32, | ||||||
|         aligned: &mut [u8], |         chunk_buf: &mut [u8], | ||||||
|         mut update: impl FnMut(&[u8]), |         output: &mut [u8], | ||||||
|     ) -> Result<(), FirmwareUpdaterError> { |     ) -> Result<(), FirmwareUpdaterError> { | ||||||
|         for offset in (0..update_len).step_by(aligned.len()) { |         let mut digest = D::new(); | ||||||
|             self.dfu.read(dfu_flash, offset, aligned).await?; |         for offset in (0..update_len).step_by(chunk_buf.len()) { | ||||||
|             let len = core::cmp::min((update_len - offset) as usize, aligned.len()); |             self.dfu.read(dfu_flash, offset, chunk_buf).await?; | ||||||
|             update(&aligned[..len]); |             let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | ||||||
|  |             digest.update(&chunk_buf[..len]); | ||||||
|         } |         } | ||||||
|  |         output.copy_from_slice(digest.finalize().as_slice()); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -334,24 +338,26 @@ impl FirmwareUpdater { | |||||||
|  |  | ||||||
|         #[cfg(feature = "ed25519-dalek")] |         #[cfg(feature = "ed25519-dalek")] | ||||||
|         { |         { | ||||||
|             use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; |             use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; | ||||||
|  |  | ||||||
|  |             use crate::digest_adapters::ed25519_dalek::Sha512; | ||||||
|  |  | ||||||
|             let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); |             let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | ||||||
|  |  | ||||||
|             let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; |             let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | ||||||
|             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; |             let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||||||
|  |  | ||||||
|             let mut digest = Sha512::new(); |             let mut message = [0; 64]; | ||||||
|             self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; |             self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; | ||||||
|  |  | ||||||
|             public_key |             public_key.verify(&message, &signature).map_err(into_signature_error)? | ||||||
|                 .verify(&digest.finalize(), &signature) |  | ||||||
|                 .map_err(into_signature_error)? |  | ||||||
|         } |         } | ||||||
|         #[cfg(feature = "ed25519-salty")] |         #[cfg(feature = "ed25519-salty")] | ||||||
|         { |         { | ||||||
|             use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; |             use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||||||
|             use salty::{PublicKey, Sha512, Signature}; |             use salty::{PublicKey, Signature}; | ||||||
|  |  | ||||||
|  |             use crate::digest_adapters::salty::Sha512; | ||||||
|  |  | ||||||
|             fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { |             fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | ||||||
|                 FirmwareUpdaterError::Signature(signature::Error::default()) |                 FirmwareUpdaterError::Signature(signature::Error::default()) | ||||||
| @@ -362,10 +368,9 @@ impl FirmwareUpdater { | |||||||
|             let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; |             let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||||||
|             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; |             let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||||||
|  |  | ||||||
|             let mut digest = Sha512::new(); |             let mut message = [0; 64]; | ||||||
|             self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; |             self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; | ||||||
|  |  | ||||||
|             let message = digest.finalize(); |  | ||||||
|             let r = public_key.verify(&message, &signature); |             let r = public_key.verify(&message, &signature); | ||||||
|             trace!( |             trace!( | ||||||
|                 "Verifying with public key {}, signature {} and message {} yields ok: {}", |                 "Verifying with public key {}, signature {} and message {} yields ok: {}", | ||||||
| @@ -380,19 +385,21 @@ impl FirmwareUpdater { | |||||||
|         self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) |         self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Iterate through the DFU and process all bytes with the provided closure. |     /// Verify the update in DFU with any digest. | ||||||
|     pub fn incremental_hash_blocking<F: NorFlash>( |     pub fn hash_blocking<F: NorFlash, D: Digest>( | ||||||
|         &mut self, |         &mut self, | ||||||
|         dfu_flash: &mut F, |         dfu_flash: &mut F, | ||||||
|         update_len: u32, |         update_len: u32, | ||||||
|         aligned: &mut [u8], |         chunk_buf: &mut [u8], | ||||||
|         mut update: impl FnMut(&[u8]), |         output: &mut [u8], | ||||||
|     ) -> Result<(), FirmwareUpdaterError> { |     ) -> Result<(), FirmwareUpdaterError> { | ||||||
|         for offset in (0..update_len).step_by(aligned.len()) { |         let mut digest = D::new(); | ||||||
|             self.dfu.read_blocking(dfu_flash, offset, aligned)?; |         for offset in (0..update_len).step_by(chunk_buf.len()) { | ||||||
|             let len = core::cmp::min((update_len - offset) as usize, aligned.len()); |             self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; | ||||||
|             update(&aligned[..len]); |             let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); | ||||||
|  |             digest.update(&chunk_buf[..len]); | ||||||
|         } |         } | ||||||
|  |         output.copy_from_slice(digest.finalize().as_slice()); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -479,3 +486,32 @@ impl FirmwareUpdater { | |||||||
|         Ok(self.dfu) |         Ok(self.dfu) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use futures::executor::block_on; | ||||||
|  |     use sha1::{Digest, Sha1}; | ||||||
|  |  | ||||||
|  |     use super::*; | ||||||
|  |     use crate::tests::MemFlash; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn can_verify() { | ||||||
|  |         const STATE: Partition = Partition::new(0, 4096); | ||||||
|  |         const DFU: Partition = Partition::new(65536, 131072); | ||||||
|  |  | ||||||
|  |         let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]); | ||||||
|  |  | ||||||
|  |         let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; | ||||||
|  |         let mut to_write = [0; 4096]; | ||||||
|  |         to_write[..7].copy_from_slice(update.as_slice()); | ||||||
|  |  | ||||||
|  |         let mut updater = FirmwareUpdater::new(DFU, STATE); | ||||||
|  |         block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); | ||||||
|  |         let mut chunk_buf = [0; 2]; | ||||||
|  |         let mut hash = [0; 20]; | ||||||
|  |         block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); | ||||||
|  |  | ||||||
|  |         assert_eq!(Sha1::digest(update).as_slice(), hash); | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| mod fmt; | mod fmt; | ||||||
|  |  | ||||||
| mod boot_loader; | mod boot_loader; | ||||||
|  | mod digest_adapters; | ||||||
| mod firmware_updater; | mod firmware_updater; | ||||||
| mod partition; | mod partition; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user