diff options
| author | huntc <[email protected]> | 2023-01-06 22:21:39 +1100 |
|---|---|---|
| committer | huntc <[email protected]> | 2023-01-12 13:30:58 +1100 |
| commit | b0529bc943c9da0eb5f43335d06779d6064b765a (patch) | |
| tree | 56cb03dbffa74ba471123c44552f36f3125d4a63 /embassy-boot/boot/src/lib.rs | |
| parent | dbf749370897290e16ef6ed2917d075c40358991 (diff) | |
Support codesigning in the firmware updater
This commit provides a method to verify that firmware has been signed with a private key given its public key. The implementation uses ed25519-dalek as the signature verifier. An "ed25519" feature is required to enable the functionality. When disabled (the default), calling the firmware updater's verify method will return a failure.
Diffstat (limited to 'embassy-boot/boot/src/lib.rs')
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 367 |
1 files changed, 353 insertions, 14 deletions
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 76b14bc8c..be254e9d7 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -52,6 +52,16 @@ pub enum BootError { | |||
| 52 | BadMagic, | 52 | BadMagic, |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | #[cfg(feature = "defmt")] | ||
| 56 | impl defmt::Format for BootError { | ||
| 57 | fn format(&self, fmt: defmt::Formatter) { | ||
| 58 | match self { | ||
| 59 | BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), | ||
| 60 | BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 55 | impl<E> From<E> for BootError | 65 | impl<E> From<E> for BootError |
| 56 | where | 66 | where |
| 57 | E: NorFlashError, | 67 | E: NorFlashError, |
| @@ -557,6 +567,33 @@ where | |||
| 557 | self.state | 567 | self.state |
| 558 | } | 568 | } |
| 559 | } | 569 | } |
| 570 | /// Errors returned by FirmwareUpdater | ||
| 571 | #[derive(Debug)] | ||
| 572 | pub enum FirmwareUpdaterError { | ||
| 573 | /// Error from flash. | ||
| 574 | Flash(NorFlashErrorKind), | ||
| 575 | /// Signature errors. | ||
| 576 | Signature(signature::Error), | ||
| 577 | } | ||
| 578 | |||
| 579 | #[cfg(feature = "defmt")] | ||
| 580 | impl defmt::Format for FirmwareUpdaterError { | ||
| 581 | fn format(&self, fmt: defmt::Formatter) { | ||
| 582 | match self { | ||
| 583 | FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), | ||
| 584 | FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), | ||
| 585 | } | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | impl<E> From<E> for FirmwareUpdaterError | ||
| 590 | where | ||
| 591 | E: NorFlashError, | ||
| 592 | { | ||
| 593 | fn from(error: E) -> Self { | ||
| 594 | FirmwareUpdaterError::Flash(error.kind()) | ||
| 595 | } | ||
| 596 | } | ||
| 560 | 597 | ||
| 561 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to | 598 | /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to |
| 562 | /// 'mess up' the internal bootloader state | 599 | /// 'mess up' the internal bootloader state |
| @@ -609,7 +646,11 @@ impl FirmwareUpdater { | |||
| 609 | /// This is useful to check if the bootloader has just done a swap, in order | 646 | /// This is useful to check if the bootloader has just done a swap, in order |
| 610 | /// to do verifications and self-tests of the new image before calling | 647 | /// to do verifications and self-tests of the new image before calling |
| 611 | /// `mark_booted`. | 648 | /// `mark_booted`. |
| 612 | pub async fn get_state<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> { | 649 | pub async fn get_state<F: AsyncNorFlash>( |
| 650 | &mut self, | ||
| 651 | flash: &mut F, | ||
| 652 | aligned: &mut [u8], | ||
| 653 | ) -> Result<State, FirmwareUpdaterError> { | ||
| 613 | flash.read(self.state.from as u32, aligned).await?; | 654 | flash.read(self.state.from as u32, aligned).await?; |
| 614 | 655 | ||
| 615 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 656 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| @@ -619,12 +660,126 @@ impl FirmwareUpdater { | |||
| 619 | } | 660 | } |
| 620 | } | 661 | } |
| 621 | 662 | ||
| 663 | /// Verify the DFU given a public key. If there is an error then DO NOT | ||
| 664 | /// proceed with updating the firmware as it must be signed with a | ||
| 665 | /// corresponding private key (otherwise it could be malicious firmware). | ||
| 666 | /// | ||
| 667 | /// Mark to trigger firmware swap on next boot if verify suceeds. | ||
| 668 | /// | ||
| 669 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have | ||
| 670 | /// been generated from a SHA-512 digest of the firmware bytes. | ||
| 671 | /// | ||
| 672 | /// If no signature feature is set then this method will always return a | ||
| 673 | /// signature error. | ||
| 674 | /// | ||
| 675 | /// # Safety | ||
| 676 | /// | ||
| 677 | /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 678 | /// and written to. | ||
| 679 | #[cfg(feature = "_verify")] | ||
| 680 | pub async fn verify_and_mark_updated<F: AsyncNorFlash>( | ||
| 681 | &mut self, | ||
| 682 | _flash: &mut F, | ||
| 683 | _public_key: &[u8], | ||
| 684 | _signature: &[u8], | ||
| 685 | _update_len: usize, | ||
| 686 | _aligned: &mut [u8], | ||
| 687 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 688 | let _end = self.dfu.from + _update_len; | ||
| 689 | let _read_size = _aligned.len(); | ||
| 690 | |||
| 691 | assert_eq!(_aligned.len(), F::WRITE_SIZE); | ||
| 692 | assert!(_end <= self.dfu.to); | ||
| 693 | |||
| 694 | #[cfg(feature = "ed25519-dalek")] | ||
| 695 | { | ||
| 696 | use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; | ||
| 697 | |||
| 698 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | ||
| 699 | |||
| 700 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | ||
| 701 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||
| 702 | |||
| 703 | let mut digest = Sha512::new(); | ||
| 704 | |||
| 705 | let mut offset = self.dfu.from; | ||
| 706 | let last_offset = _end / _read_size * _read_size; | ||
| 707 | |||
| 708 | while offset < last_offset { | ||
| 709 | _flash.read(offset as u32, _aligned).await?; | ||
| 710 | digest.update(&_aligned); | ||
| 711 | offset += _read_size; | ||
| 712 | } | ||
| 713 | |||
| 714 | let remaining = _end % _read_size; | ||
| 715 | |||
| 716 | if remaining > 0 { | ||
| 717 | _flash.read(last_offset as u32, _aligned).await?; | ||
| 718 | digest.update(&_aligned[0..remaining]); | ||
| 719 | } | ||
| 720 | |||
| 721 | public_key | ||
| 722 | .verify(&digest.finalize(), &signature) | ||
| 723 | .map_err(into_signature_error)? | ||
| 724 | } | ||
| 725 | #[cfg(feature = "ed25519-salty")] | ||
| 726 | { | ||
| 727 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||
| 728 | use salty::{PublicKey, Sha512, Signature}; | ||
| 729 | |||
| 730 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | ||
| 731 | FirmwareUpdaterError::Signature(signature::Error::default()) | ||
| 732 | } | ||
| 733 | |||
| 734 | let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; | ||
| 735 | let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; | ||
| 736 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||
| 737 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||
| 738 | |||
| 739 | let mut digest = Sha512::new(); | ||
| 740 | |||
| 741 | let mut offset = self.dfu.from; | ||
| 742 | let last_offset = _end / _read_size * _read_size; | ||
| 743 | |||
| 744 | while offset < last_offset { | ||
| 745 | _flash.read(offset as u32, _aligned).await?; | ||
| 746 | digest.update(&_aligned); | ||
| 747 | offset += _read_size; | ||
| 748 | } | ||
| 749 | |||
| 750 | let remaining = _end % _read_size; | ||
| 751 | |||
| 752 | if remaining > 0 { | ||
| 753 | _flash.read(last_offset as u32, _aligned).await?; | ||
| 754 | digest.update(&_aligned[0..remaining]); | ||
| 755 | } | ||
| 756 | |||
| 757 | let message = digest.finalize(); | ||
| 758 | let r = public_key.verify(&message, &signature); | ||
| 759 | trace!( | ||
| 760 | "Verifying with public key {}, signature {} and message {} yields ok: {}", | ||
| 761 | public_key.to_bytes(), | ||
| 762 | signature.to_bytes(), | ||
| 763 | message, | ||
| 764 | r.is_ok() | ||
| 765 | ); | ||
| 766 | r.map_err(into_signature_error)? | ||
| 767 | } | ||
| 768 | |||
| 769 | self.set_magic(_aligned, SWAP_MAGIC, _flash).await | ||
| 770 | } | ||
| 771 | |||
| 622 | /// Mark to trigger firmware swap on next boot. | 772 | /// Mark to trigger firmware swap on next boot. |
| 623 | /// | 773 | /// |
| 624 | /// # Safety | 774 | /// # Safety |
| 625 | /// | 775 | /// |
| 626 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 776 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 627 | pub async fn mark_updated<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { | 777 | #[cfg(not(feature = "_verify"))] |
| 778 | pub async fn mark_updated<F: AsyncNorFlash>( | ||
| 779 | &mut self, | ||
| 780 | flash: &mut F, | ||
| 781 | aligned: &mut [u8], | ||
| 782 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 628 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 783 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 629 | self.set_magic(aligned, SWAP_MAGIC, flash).await | 784 | self.set_magic(aligned, SWAP_MAGIC, flash).await |
| 630 | } | 785 | } |
| @@ -634,7 +789,11 @@ impl FirmwareUpdater { | |||
| 634 | /// # Safety | 789 | /// # Safety |
| 635 | /// | 790 | /// |
| 636 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 791 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 637 | pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { | 792 | pub async fn mark_booted<F: AsyncNorFlash>( |
| 793 | &mut self, | ||
| 794 | flash: &mut F, | ||
| 795 | aligned: &mut [u8], | ||
| 796 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 638 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 797 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 639 | self.set_magic(aligned, BOOT_MAGIC, flash).await | 798 | self.set_magic(aligned, BOOT_MAGIC, flash).await |
| 640 | } | 799 | } |
| @@ -644,7 +803,7 @@ impl FirmwareUpdater { | |||
| 644 | aligned: &mut [u8], | 803 | aligned: &mut [u8], |
| 645 | magic: u8, | 804 | magic: u8, |
| 646 | flash: &mut F, | 805 | flash: &mut F, |
| 647 | ) -> Result<(), F::Error> { | 806 | ) -> Result<(), FirmwareUpdaterError> { |
| 648 | flash.read(self.state.from as u32, aligned).await?; | 807 | flash.read(self.state.from as u32, aligned).await?; |
| 649 | 808 | ||
| 650 | if aligned.iter().any(|&b| b != magic) { | 809 | if aligned.iter().any(|&b| b != magic) { |
| @@ -672,7 +831,7 @@ impl FirmwareUpdater { | |||
| 672 | data: &[u8], | 831 | data: &[u8], |
| 673 | flash: &mut F, | 832 | flash: &mut F, |
| 674 | block_size: usize, | 833 | block_size: usize, |
| 675 | ) -> Result<(), F::Error> { | 834 | ) -> Result<(), FirmwareUpdaterError> { |
| 676 | assert!(data.len() >= F::ERASE_SIZE); | 835 | assert!(data.len() >= F::ERASE_SIZE); |
| 677 | 836 | ||
| 678 | flash | 837 | flash |
| @@ -700,7 +859,10 @@ impl FirmwareUpdater { | |||
| 700 | /// | 859 | /// |
| 701 | /// Using this instead of `write_firmware` allows for an optimized API in | 860 | /// Using this instead of `write_firmware` allows for an optimized API in |
| 702 | /// exchange for added complexity. | 861 | /// exchange for added complexity. |
| 703 | pub async fn prepare_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> { | 862 | pub async fn prepare_update<F: AsyncNorFlash>( |
| 863 | &mut self, | ||
| 864 | flash: &mut F, | ||
| 865 | ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | ||
| 704 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; | 866 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; |
| 705 | 867 | ||
| 706 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | 868 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); |
| @@ -717,7 +879,11 @@ impl FirmwareUpdater { | |||
| 717 | /// This is useful to check if the bootloader has just done a swap, in order | 879 | /// This is useful to check if the bootloader has just done a swap, in order |
| 718 | /// to do verifications and self-tests of the new image before calling | 880 | /// to do verifications and self-tests of the new image before calling |
| 719 | /// `mark_booted`. | 881 | /// `mark_booted`. |
| 720 | pub fn get_state_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> { | 882 | pub fn get_state_blocking<F: NorFlash>( |
| 883 | &mut self, | ||
| 884 | flash: &mut F, | ||
| 885 | aligned: &mut [u8], | ||
| 886 | ) -> Result<State, FirmwareUpdaterError> { | ||
| 721 | flash.read(self.state.from as u32, aligned)?; | 887 | flash.read(self.state.from as u32, aligned)?; |
| 722 | 888 | ||
| 723 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { | 889 | if !aligned.iter().any(|&b| b != SWAP_MAGIC) { |
| @@ -727,12 +893,126 @@ impl FirmwareUpdater { | |||
| 727 | } | 893 | } |
| 728 | } | 894 | } |
| 729 | 895 | ||
| 896 | /// Verify the DFU given a public key. If there is an error then DO NOT | ||
| 897 | /// proceed with updating the firmware as it must be signed with a | ||
| 898 | /// corresponding private key (otherwise it could be malicious firmware). | ||
| 899 | /// | ||
| 900 | /// Mark to trigger firmware swap on next boot if verify suceeds. | ||
| 901 | /// | ||
| 902 | /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have | ||
| 903 | /// been generated from a SHA-512 digest of the firmware bytes. | ||
| 904 | /// | ||
| 905 | /// If no signature feature is set then this method will always return a | ||
| 906 | /// signature error. | ||
| 907 | /// | ||
| 908 | /// # Safety | ||
| 909 | /// | ||
| 910 | /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from | ||
| 911 | /// and written to. | ||
| 912 | #[cfg(feature = "_verify")] | ||
| 913 | pub fn verify_and_mark_updated_blocking<F: NorFlash>( | ||
| 914 | &mut self, | ||
| 915 | _flash: &mut F, | ||
| 916 | _public_key: &[u8], | ||
| 917 | _signature: &[u8], | ||
| 918 | _update_len: usize, | ||
| 919 | _aligned: &mut [u8], | ||
| 920 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 921 | let _end = self.dfu.from + _update_len; | ||
| 922 | let _read_size = _aligned.len(); | ||
| 923 | |||
| 924 | assert_eq!(_aligned.len(), F::WRITE_SIZE); | ||
| 925 | assert!(_end <= self.dfu.to); | ||
| 926 | |||
| 927 | #[cfg(feature = "ed25519-dalek")] | ||
| 928 | { | ||
| 929 | use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; | ||
| 930 | |||
| 931 | let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); | ||
| 932 | |||
| 933 | let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; | ||
| 934 | let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; | ||
| 935 | |||
| 936 | let mut digest = Sha512::new(); | ||
| 937 | |||
| 938 | let mut offset = self.dfu.from; | ||
| 939 | let last_offset = _end / _read_size * _read_size; | ||
| 940 | |||
| 941 | while offset < last_offset { | ||
| 942 | _flash.read(offset as u32, _aligned)?; | ||
| 943 | digest.update(&_aligned); | ||
| 944 | offset += _read_size; | ||
| 945 | } | ||
| 946 | |||
| 947 | let remaining = _end % _read_size; | ||
| 948 | |||
| 949 | if remaining > 0 { | ||
| 950 | _flash.read(last_offset as u32, _aligned)?; | ||
| 951 | digest.update(&_aligned[0..remaining]); | ||
| 952 | } | ||
| 953 | |||
| 954 | public_key | ||
| 955 | .verify(&digest.finalize(), &signature) | ||
| 956 | .map_err(into_signature_error)? | ||
| 957 | } | ||
| 958 | #[cfg(feature = "ed25519-salty")] | ||
| 959 | { | ||
| 960 | use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; | ||
| 961 | use salty::{PublicKey, Sha512, Signature}; | ||
| 962 | |||
| 963 | fn into_signature_error<E>(_: E) -> FirmwareUpdaterError { | ||
| 964 | FirmwareUpdaterError::Signature(signature::Error::default()) | ||
| 965 | } | ||
| 966 | |||
| 967 | let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; | ||
| 968 | let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; | ||
| 969 | let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; | ||
| 970 | let signature = Signature::try_from(&signature).map_err(into_signature_error)?; | ||
| 971 | |||
| 972 | let mut digest = Sha512::new(); | ||
| 973 | |||
| 974 | let mut offset = self.dfu.from; | ||
| 975 | let last_offset = _end / _read_size * _read_size; | ||
| 976 | |||
| 977 | while offset < last_offset { | ||
| 978 | _flash.read(offset as u32, _aligned)?; | ||
| 979 | digest.update(&_aligned); | ||
| 980 | offset += _read_size; | ||
| 981 | } | ||
| 982 | |||
| 983 | let remaining = _end % _read_size; | ||
| 984 | |||
| 985 | if remaining > 0 { | ||
| 986 | _flash.read(last_offset as u32, _aligned)?; | ||
| 987 | digest.update(&_aligned[0..remaining]); | ||
| 988 | } | ||
| 989 | |||
| 990 | let message = digest.finalize(); | ||
| 991 | let r = public_key.verify(&message, &signature); | ||
| 992 | trace!( | ||
| 993 | "Verifying with public key {}, signature {} and message {} yields ok: {}", | ||
| 994 | public_key.to_bytes(), | ||
| 995 | signature.to_bytes(), | ||
| 996 | message, | ||
| 997 | r.is_ok() | ||
| 998 | ); | ||
| 999 | r.map_err(into_signature_error)? | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) | ||
| 1003 | } | ||
| 1004 | |||
| 730 | /// Mark to trigger firmware swap on next boot. | 1005 | /// Mark to trigger firmware swap on next boot. |
| 731 | /// | 1006 | /// |
| 732 | /// # Safety | 1007 | /// # Safety |
| 733 | /// | 1008 | /// |
| 734 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 1009 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 735 | pub fn mark_updated_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { | 1010 | #[cfg(not(feature = "_verify"))] |
| 1011 | pub fn mark_updated_blocking<F: NorFlash>( | ||
| 1012 | &mut self, | ||
| 1013 | flash: &mut F, | ||
| 1014 | aligned: &mut [u8], | ||
| 1015 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 736 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 1016 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 737 | self.set_magic_blocking(aligned, SWAP_MAGIC, flash) | 1017 | self.set_magic_blocking(aligned, SWAP_MAGIC, flash) |
| 738 | } | 1018 | } |
| @@ -742,7 +1022,11 @@ impl FirmwareUpdater { | |||
| 742 | /// # Safety | 1022 | /// # Safety |
| 743 | /// | 1023 | /// |
| 744 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. | 1024 | /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. |
| 745 | pub fn mark_booted_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { | 1025 | pub fn mark_booted_blocking<F: NorFlash>( |
| 1026 | &mut self, | ||
| 1027 | flash: &mut F, | ||
| 1028 | aligned: &mut [u8], | ||
| 1029 | ) -> Result<(), FirmwareUpdaterError> { | ||
| 746 | assert_eq!(aligned.len(), F::WRITE_SIZE); | 1030 | assert_eq!(aligned.len(), F::WRITE_SIZE); |
| 747 | self.set_magic_blocking(aligned, BOOT_MAGIC, flash) | 1031 | self.set_magic_blocking(aligned, BOOT_MAGIC, flash) |
| 748 | } | 1032 | } |
| @@ -752,7 +1036,7 @@ impl FirmwareUpdater { | |||
| 752 | aligned: &mut [u8], | 1036 | aligned: &mut [u8], |
| 753 | magic: u8, | 1037 | magic: u8, |
| 754 | flash: &mut F, | 1038 | flash: &mut F, |
| 755 | ) -> Result<(), F::Error> { | 1039 | ) -> Result<(), FirmwareUpdaterError> { |
| 756 | flash.read(self.state.from as u32, aligned)?; | 1040 | flash.read(self.state.from as u32, aligned)?; |
| 757 | 1041 | ||
| 758 | if aligned.iter().any(|&b| b != magic) { | 1042 | if aligned.iter().any(|&b| b != magic) { |
| @@ -780,7 +1064,7 @@ impl FirmwareUpdater { | |||
| 780 | data: &[u8], | 1064 | data: &[u8], |
| 781 | flash: &mut F, | 1065 | flash: &mut F, |
| 782 | block_size: usize, | 1066 | block_size: usize, |
| 783 | ) -> Result<(), F::Error> { | 1067 | ) -> Result<(), FirmwareUpdaterError> { |
| 784 | assert!(data.len() >= F::ERASE_SIZE); | 1068 | assert!(data.len() >= F::ERASE_SIZE); |
| 785 | 1069 | ||
| 786 | flash.erase( | 1070 | flash.erase( |
| @@ -804,7 +1088,10 @@ impl FirmwareUpdater { | |||
| 804 | /// | 1088 | /// |
| 805 | /// Using this instead of `write_firmware_blocking` allows for an optimized | 1089 | /// Using this instead of `write_firmware_blocking` allows for an optimized |
| 806 | /// API in exchange for added complexity. | 1090 | /// API in exchange for added complexity. |
| 807 | pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> { | 1091 | pub fn prepare_update_blocking<F: NorFlash>( |
| 1092 | &mut self, | ||
| 1093 | flash: &mut F, | ||
| 1094 | ) -> Result<FirmwareWriter, FirmwareUpdaterError> { | ||
| 808 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; | 1095 | flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; |
| 809 | 1096 | ||
| 810 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); | 1097 | trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); |
| @@ -953,6 +1240,7 @@ mod tests { | |||
| 953 | } | 1240 | } |
| 954 | 1241 | ||
| 955 | #[test] | 1242 | #[test] |
| 1243 | #[cfg(not(feature = "_verify"))] | ||
| 956 | fn test_swap_state() { | 1244 | fn test_swap_state() { |
| 957 | const STATE: Partition = Partition::new(0, 4096); | 1245 | const STATE: Partition = Partition::new(0, 4096); |
| 958 | const ACTIVE: Partition = Partition::new(4096, 61440); | 1246 | const ACTIVE: Partition = Partition::new(4096, 61440); |
| @@ -1022,6 +1310,7 @@ mod tests { | |||
| 1022 | } | 1310 | } |
| 1023 | 1311 | ||
| 1024 | #[test] | 1312 | #[test] |
| 1313 | #[cfg(not(feature = "_verify"))] | ||
| 1025 | fn test_separate_flash_active_page_biggest() { | 1314 | fn test_separate_flash_active_page_biggest() { |
| 1026 | const STATE: Partition = Partition::new(2048, 4096); | 1315 | const STATE: Partition = Partition::new(2048, 4096); |
| 1027 | const ACTIVE: Partition = Partition::new(4096, 16384); | 1316 | const ACTIVE: Partition = Partition::new(4096, 16384); |
| @@ -1074,6 +1363,7 @@ mod tests { | |||
| 1074 | } | 1363 | } |
| 1075 | 1364 | ||
| 1076 | #[test] | 1365 | #[test] |
| 1366 | #[cfg(not(feature = "_verify"))] | ||
| 1077 | fn test_separate_flash_dfu_page_biggest() { | 1367 | fn test_separate_flash_dfu_page_biggest() { |
| 1078 | const STATE: Partition = Partition::new(2048, 4096); | 1368 | const STATE: Partition = Partition::new(2048, 4096); |
| 1079 | const ACTIVE: Partition = Partition::new(4096, 16384); | 1369 | const ACTIVE: Partition = Partition::new(4096, 16384); |
| @@ -1133,6 +1423,55 @@ mod tests { | |||
| 1133 | assert_partitions(ACTIVE, DFU, STATE, 4096, 4); | 1423 | assert_partitions(ACTIVE, DFU, STATE, 4096, 4); |
| 1134 | } | 1424 | } |
| 1135 | 1425 | ||
| 1426 | #[test] | ||
| 1427 | #[cfg(feature = "_verify")] | ||
| 1428 | fn test_verify() { | ||
| 1429 | // The following key setup is based on: | ||
| 1430 | // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example | ||
| 1431 | |||
| 1432 | use ed25519_dalek::Keypair; | ||
| 1433 | use rand::rngs::OsRng; | ||
| 1434 | |||
| 1435 | let mut csprng = OsRng {}; | ||
| 1436 | let keypair: Keypair = Keypair::generate(&mut csprng); | ||
| 1437 | |||
| 1438 | use ed25519_dalek::{Digest, Sha512, Signature, Signer}; | ||
| 1439 | let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU."; | ||
| 1440 | let mut digest = Sha512::new(); | ||
| 1441 | digest.update(&firmware); | ||
| 1442 | let message = digest.finalize(); | ||
| 1443 | let signature: Signature = keypair.sign(&message); | ||
| 1444 | |||
| 1445 | use ed25519_dalek::PublicKey; | ||
| 1446 | let public_key: PublicKey = keypair.public; | ||
| 1447 | |||
| 1448 | // Setup flash | ||
| 1449 | |||
| 1450 | const STATE: Partition = Partition::new(0, 4096); | ||
| 1451 | const DFU: Partition = Partition::new(4096, 8192); | ||
| 1452 | let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); | ||
| 1453 | |||
| 1454 | let firmware_len = firmware.len(); | ||
| 1455 | |||
| 1456 | let mut write_buf = [0; 4096]; | ||
| 1457 | write_buf[0..firmware_len].copy_from_slice(firmware); | ||
| 1458 | NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); | ||
| 1459 | |||
| 1460 | // On with the test | ||
| 1461 | |||
| 1462 | let mut updater = FirmwareUpdater::new(DFU, STATE); | ||
| 1463 | |||
| 1464 | let mut aligned = [0; 4]; | ||
| 1465 | |||
| 1466 | assert!(block_on(updater.verify_and_mark_updated( | ||
| 1467 | &mut flash, | ||
| 1468 | &public_key.to_bytes(), | ||
| 1469 | &signature.to_bytes(), | ||
| 1470 | firmware_len, | ||
| 1471 | &mut aligned, | ||
| 1472 | )) | ||
| 1473 | .is_ok()); | ||
| 1474 | } | ||
| 1136 | struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]); | 1475 | struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]); |
| 1137 | 1476 | ||
| 1138 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash | 1477 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash |
| @@ -1171,7 +1510,7 @@ mod tests { | |||
| 1171 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash | 1510 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash |
| 1172 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | 1511 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> |
| 1173 | { | 1512 | { |
| 1174 | const READ_SIZE: usize = 4; | 1513 | const READ_SIZE: usize = 1; |
| 1175 | 1514 | ||
| 1176 | fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { | 1515 | fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { |
| 1177 | let len = buf.len(); | 1516 | let len = buf.len(); |
| @@ -1194,7 +1533,7 @@ mod tests { | |||
| 1194 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash | 1533 | impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash |
| 1195 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> | 1534 | for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> |
| 1196 | { | 1535 | { |
| 1197 | const READ_SIZE: usize = 4; | 1536 | const READ_SIZE: usize = 1; |
| 1198 | 1537 | ||
| 1199 | type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; | 1538 | type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; |
| 1200 | fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | 1539 | fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { |
