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:
parent
c94f1e1450
commit
7c6936a2e3
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user