From 2437712b7bb5dd08e8a57bd3ee088b8046bc2d6e Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:20:40 +0800 Subject: [PATCH] add some basic image integrity checking code this doesn't protect against malicious tampering (because the public key is the developer key) but it will at least catch correctness errors in USB upload and filesystem behavior. --- loader/src/platform/cramium/update.rs | 77 ++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 33b96c123..75b55c604 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,12 +1,18 @@ +use core::convert::TryInto; + use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::Mono; use cramium_hal::udma; use cramium_hal::usb::driver::UsbDeviceState; +use ed25519_dalek::{Digest, Signature, VerifyingKey}; +use sha2::Sha512; use simple_fatfs::PathBuf; use utralib::generated::*; +use crate::SIGBLOCK_SIZE; use crate::platform::cramium::gfx; +use crate::platform::cramium::sha512_digest::Sha512Prehash; use crate::platform::cramium::usb; use crate::platform::cramium::usb::SliceCursor; @@ -18,6 +24,20 @@ use crate::platform::cramium::usb::SliceCursor; // to detect if the device is unplugged. const DISCONNECT_STATE: u32 = 0x40b; +// loader is not updateable here because we're XIP. But we can update these other images: +const SWAP_NAME: &'static str = "SWAP.IMG"; +const KERNEL_NAME: &'static str = "XOUS.IMG"; +const DEV_PUBKEY: [u8; 32] = [ + 0x1c, 0x9b, 0xea, 0xe3, 0x2a, 0xea, 0xc8, 0x75, 0x07, 0xc1, 0x80, 0x94, 0x38, 0x7e, 0xff, 0x1c, 0x74, + 0x61, 0x42, 0x82, 0xaf, 0xfd, 0x81, 0x52, 0xd8, 0x71, 0x35, 0x2e, 0xdf, 0x3f, 0x58, 0xbb, +]; +#[repr(C)] +struct SignatureInFlash { + pub version: u32, + pub signed_len: u32, + pub signature: [u8; 64], +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyPress { Up, @@ -193,7 +213,62 @@ pub fn process_update(perclk: u32) { match fs.read_dir(PathBuf::from("/")) { Ok(dir) => { for entry in dir { - crate::println!("file: {:?}", entry); + crate::println!("{:?}", entry); + if let Some(file_name) = entry.path().file_name() { + if file_name.to_ascii_uppercase() == KERNEL_NAME { + match fs.get_file(entry.path().clone()) { + Ok(f) => { + let sector_offset = f.sector_offset(); + crate::println!("sector offset: {}", sector_offset); + let disk_access = usb::conjure_disk(); + crate::println!( + "{:x?}", + &disk_access[sector_offset as usize * 512 + ..sector_offset as usize * 512 + 32] + ); + let pubkey = VerifyingKey::from_bytes(&DEV_PUBKEY) + .expect("public key was not valid"); + crate::println!("pubkey as reconstituted: {:x?}", pubkey); + + let k_start = sector_offset as usize * 512; + let sig_region = &disk_access + [k_start..k_start + core::mem::size_of::()]; + let sig_rec: &SignatureInFlash = (sig_region.as_ptr() + as *const SignatureInFlash) + .as_ref() + .unwrap(); // this pointer better not be null, we just created it! + let sig = Signature::from_bytes(&sig_rec.signature); + + let kern_len = sig_rec.signed_len as usize; + crate::println!("recorded kernel len: {} bytes", kern_len); + crate::println!( + "verifying with signature {:x?}", + sig_rec.signature + ); + crate::println!("verifying with pubkey {:x?}", pubkey.to_bytes()); + + let mut h: Sha512 = Sha512::new(); + let image = &disk_access[k_start + SIGBLOCK_SIZE + ..k_start + SIGBLOCK_SIZE + sig_rec.signed_len as usize]; + h.update(&image); + let hash = h.finalize(); + let mut ph = Sha512Prehash::new(); + ph.set_prehash(hash.as_slice().try_into().unwrap()); + match pubkey.verify_prehashed(ph, None, &sig) { + Ok(()) => crate::println!("Kernel image is good!"), + Err(e) => { + crate::println!("error verifying signature: {:?}", e); + } + } + } + Err(e) => { + crate::println!("Couldn't access image: {:?}", e); + } + } + } else if file_name.to_ascii_lowercase() == SWAP_NAME { + crate::println!("Found swap image"); + } + } } } Err(e) => {