diff --git a/rust/lanzaboote/Cargo.lock b/rust/lanzaboote/Cargo.lock index 5cd2e80..5d8413c 100644 --- a/rust/lanzaboote/Cargo.lock +++ b/rust/lanzaboote/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "bit_field" version = "0.10.1" @@ -14,12 +26,37 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake3" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "constant_time_eq" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" + [[package]] name = "ed25519-compact" version = "2.0.2" @@ -41,6 +78,7 @@ dependencies = [ name = "lanzaboote" version = "0.1.0" dependencies = [ + "blake3", "ed25519-compact", "goblin", "log", diff --git a/rust/lanzaboote/Cargo.toml b/rust/lanzaboote/Cargo.toml index 0acb79c..06f4aaf 100644 --- a/rust/lanzaboote/Cargo.toml +++ b/rust/lanzaboote/Cargo.toml @@ -11,6 +11,9 @@ log = "0.4.17" ed25519-compact = { version = "2.0.2", default-features = false, features = [] } goblin = { version = "0.6.0", default-features = false, features = [ "pe64", "alloc" ]} +# We don't want the assembly implementations for now. +blake3 = { version = "1.3.3", default-features = false, features = [ "pure" ]} + [profile.release] opt-level = "s" lto = true diff --git a/rust/lanzaboote/src/main.rs b/rust/lanzaboote/src/main.rs index 9fa8eff..15a1a06 100644 --- a/rust/lanzaboote/src/main.rs +++ b/rust/lanzaboote/src/main.rs @@ -9,7 +9,8 @@ mod linux_loader; mod pe_section; mod uefi_helpers; -use pe_section::pe_section_as_string; +use blake3::Hash; +use pe_section::{pe_section, pe_section_as_string}; use uefi::{ prelude::*, proto::{ @@ -52,9 +53,33 @@ struct EmbeddedConfiguration { /// lanzaboote binary. kernel_filename: CString16, + /// The cryptographic hash of the kernel. + kernel_hash: Hash, + /// The filename of the initrd to be passed to the kernel. See /// `kernel_filename` for how to interpret these filenames. initrd_filename: CString16, + + /// The cryptographic hash of the initrd. This hash is computed + /// over the whole PE binary, not only the embedded initrd. + initrd_hash: Hash, +} + +/// Extract a filename from a PE section. The filename is stored as UTF-8. +fn extract_filename(file_data: &[u8], section: &str) -> Result { + let filename = pe_section_as_string(file_data, section).ok_or(Status::INVALID_PARAMETER)?; + + Ok(CString16::try_from(filename.as_str()).map_err(|_| Status::INVALID_PARAMETER)?) +} + +/// Extract a Blake3 hash from a PE section. +fn extract_hash(file_data: &[u8], section: &str) -> Result { + let array: [u8; 32] = pe_section(file_data, section) + .ok_or(Status::INVALID_PARAMETER)? + .try_into() + .map_err(|_| Status::INVALID_PARAMETER)?; + + Ok(array.into()) } impl EmbeddedConfiguration { @@ -62,14 +87,12 @@ impl EmbeddedConfiguration { file.set_position(0)?; let file_data = read_all(file)?; - let kernel_filename = - pe_section_as_string(&file_data, ".kernelp").ok_or(Status::INVALID_PARAMETER)?; - let initrd_filename = - pe_section_as_string(&file_data, ".initrdp").ok_or(Status::INVALID_PARAMETER)?; - Ok(Self { - kernel_filename: CString16::try_from(kernel_filename.as_str()).unwrap(), - initrd_filename: CString16::try_from(initrd_filename.as_str()).unwrap(), + kernel_filename: extract_filename(&file_data, ".kernelp")?, + kernel_hash: extract_hash(&file_data, ".kernelh")?, + + initrd_filename: extract_filename(&file_data, ".initrdp")?, + initrd_hash: extract_hash(&file_data, ".initrdh")?, }) } } @@ -121,6 +144,22 @@ fn main(handle: Handle, mut system_table: SystemTable) -> Status { initrd_data = read_all(&mut initrd_file).expect("Failed to read kernel file into memory"); } + if blake3::hash(&kernel_data) != config.kernel_hash { + system_table + .stdout() + .output_string(cstr16!("Hash mismatch for kernel. Refusing to load!\r\n")) + .unwrap(); + return Status::SECURITY_VIOLATION; + } + + if blake3::hash(&initrd_data) != config.initrd_hash { + system_table + .stdout() + .output_string(cstr16!("Hash mismatch for initrd. Refusing to load!\r\n")) + .unwrap(); + return Status::SECURITY_VIOLATION; + } + let kernel_cmdline = booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");