diff --git a/build.rs b/build.rs index 66c85292..d955dd59 100644 --- a/build.rs +++ b/build.rs @@ -29,7 +29,7 @@ fn main() { } // place side by side with binaries - let outdir = path::PathBuf::from(path::PathBuf::from(env_outdir).ancestors().skip(3).next().unwrap()); + let outdir = path::PathBuf::from(path::PathBuf::from(env_outdir).ancestors().nth(3).unwrap()); fs::create_dir_all(&outdir).unwrap(); println!("{:?}", &outdir); diff --git a/src/bin/lpc55/cli.rs b/src/bin/lpc55/cli.rs index 8576acf6..3bdf76ac 100644 --- a/src/bin/lpc55/cli.rs +++ b/src/bin/lpc55/cli.rs @@ -4,7 +4,7 @@ use clap::{self, crate_authors, crate_version, App, Arg, SubCommand}; // duplicated here, because when using this `cli` module in build.rs // to generate shell completions, there is no `lpc55` crate yet -pub const KEYSTORE_KEY_NAMES: [&'static str; 6] = [ +pub const KEYSTORE_KEY_NAMES: [&str; 6] = [ "secure-boot-kek", "user-key", "unique-device-secret", diff --git a/src/bin/lpc55/logger.rs b/src/bin/lpc55/logger.rs index 72605284..45cdaf61 100644 --- a/src/bin/lpc55/logger.rs +++ b/src/bin/lpc55/logger.rs @@ -14,7 +14,7 @@ use log::{self, Log}; #[derive(Debug)] pub struct Logger(()); -const LOGGER: &'static Logger = &Logger(()); +const LOGGER: &Logger = &Logger(()); impl Logger { /// Create a new logger that logs to stderr and initialize it as the diff --git a/src/bin/lpc55/main.rs b/src/bin/lpc55/main.rs index d36469dd..71910a53 100644 --- a/src/bin/lpc55/main.rs +++ b/src/bin/lpc55/main.rs @@ -59,7 +59,7 @@ fn try_main(args: clap::ArgMatches<'_>) -> anyhow::Result<()> { if let Some(command) = args.subcommand_matches("http") { let bootloader = bootloader()?; let addr = command.value_of("ADDR").unwrap().to_string(); - let port = u16::from_str_radix(command.value_of("PORT").unwrap(), 10).unwrap(); + let port = command.value_of("PORT").unwrap().parse::().unwrap(); let http_config = lpc55::http::HttpConfig { addr, port, timeout_ms: 5000 }; let server = lpc55::http::Server::new(&http_config, bootloader)?; server.run()?; @@ -169,13 +169,11 @@ fn try_main(args: clap::ArgMatches<'_>) -> anyhow::Result<()> { info!("preserving firmware, prince-iv, and reserved fields."); // Do not overwrite firmware versions - for i in 8 .. 16 { - settings[i] = current_pfr_raw[i]; - } + let protect = 8..16; + settings[protect.clone()].clone_from_slice(¤t_pfr_raw[protect]); // Do not overwrite any of the PRINCE IV's or reserved areas. - for i in 48 .. 256 { - settings[i] = current_pfr_raw[i]; - } + let protect = 48..256; + settings[protect.clone()].clone_from_slice(¤t_pfr_raw[protect]); } trace!("writing pfr: {}", hex_str!(&settings)); @@ -445,8 +443,8 @@ fn try_main(args: clap::ArgMatches<'_>) -> anyhow::Result<()> { if let Some(product_date) = command.value_of("product-date") { use chrono::naive::NaiveDate; let date = NaiveDate::parse_from_str(product_date, "%Y-%m-%d") - .or(NaiveDate::parse_from_str(product_date, "%Y%m%d")) - .or(NaiveDate::parse_from_str(product_date, "%y%m%d"))?; + .or_else(|_| NaiveDate::parse_from_str(product_date, "%Y%m%d")) + .or_else(|_| NaiveDate::parse_from_str(product_date, "%y%m%d"))?; let days_since_twenties = (date - NaiveDate::from_ymd(2020, 1, 1)).num_days(); assert!(days_since_twenties > 0); info!("overriding product.major with date {}, i.e. {}", &date, days_since_twenties); diff --git a/src/bootloader.rs b/src/bootloader.rs index 947b6afd..04ddff61 100644 --- a/src/bootloader.rs +++ b/src/bootloader.rs @@ -106,9 +106,8 @@ impl Bootloader { }) .filter_map(|(device, vid, pid)| { let protocol = Protocol::new(device); - let bootloader = GetProperties { protocol: &protocol }.device_uuid().ok() - .map(|uuid| Self { protocol, vid, pid, uuid }); - bootloader + GetProperties { protocol: &protocol }.device_uuid().ok() + .map(|uuid| Self { protocol, vid, pid, uuid }) }) .collect() } diff --git a/src/bootloader/command.rs b/src/bootloader/command.rs index 58e5c448..7eaa69f3 100644 --- a/src/bootloader/command.rs +++ b/src/bootloader/command.rs @@ -69,10 +69,7 @@ pub enum DataPhase { impl DataPhase { pub fn has_command_data(&self) -> bool { - match self { - DataPhase::CommandData(_) => true, - _ => false, - } + matches!(self, DataPhase::CommandData(_)) } } @@ -93,8 +90,8 @@ impl Command { let mut bytes = Vec::with_capacity(words.len() * 4); let cursor = &mut bytes; - for i in 0 .. words.len() { - cursor.write_all(&words[i].to_le_bytes()).unwrap(); + for word in words.iter() { + cursor.write_all(&word.to_le_bytes()).unwrap(); } DataPhase::CommandData(bytes) @@ -483,7 +480,7 @@ pub enum Key { } // unfortunately duplicated in cli.rs, for why see there -pub const KEYSTORE_KEY_NAMES: [&'static str; 6] = [ +pub const KEYSTORE_KEY_NAMES: [&str; 6] = [ "secure-boot-kek", "user-key", "unique-device-secret", diff --git a/src/bootloader/error.rs b/src/bootloader/error.rs index 3989c16e..8d8dd962 100644 --- a/src/bootloader/error.rs +++ b/src/bootloader/error.rs @@ -1,4 +1,4 @@ -//! https://github.com/NXPmicro/spsdk/blob/020a983e53769fe16cb9b49395d56f0201eccca6/spsdk/mboot/error_codes.py +//! use core::convert::TryFrom; @@ -125,10 +125,10 @@ generate! { CrcCheckerError: OutOfRange = 4, } -impl Into<(ErrorGroup, u8)> for BootloaderError { - fn into(self) -> (ErrorGroup, u8) { +impl From for (ErrorGroup, u8) { + fn from(error: BootloaderError) -> Self { use BootloaderError::*; - match self { + match error { Generic(error) => (ErrorGroup::Generic, error as u8), FlashDriver(error) => (ErrorGroup::FlashDriver, error as u8), PropertyStore(error) => (ErrorGroup::PropertyStore, error as u8), @@ -142,8 +142,7 @@ impl Into<(ErrorGroup, u8)> for BootloaderError { impl From for u32 { fn from(error: BootloaderError) -> u32 { let (group, code) = error.into(); - let status = (group as u32 * 100) + code as u32; - status + (group as u32 * 100) + code as u32 } } @@ -158,7 +157,7 @@ impl From for BootloaderError { (ErrorGroup::PropertyStore, code) => PropertyStoreError::try_from(code).map_or(Unknown(status), PropertyStore), (ErrorGroup::CrcChecker, code) => CrcCheckerError::try_from(code).map_or(Unknown(status), CrcChecker), (ErrorGroup::SbLoader, code) => SbLoaderError::try_from(code).map_or(Unknown(status), SbLoader), - _ => return Unknown(status), + _ => Unknown(status), } } else { Unknown(status) diff --git a/src/bootloader/property.rs b/src/bootloader/property.rs index 2d9cdda3..c88d3cdd 100644 --- a/src/bootloader/property.rs +++ b/src/bootloader/property.rs @@ -226,7 +226,7 @@ impl GetProperties<'_> { ((values[3] as u128) << 96) + ((values[2] as u128) << 64) + ((values[1] as u128) << 32) + - ((values[0] as u128)) + (values[0] as u128) // ((u32::from_le_bytes(values[0].to_be_bytes()) as u128) << 96) + // ((u32::from_le_bytes(values[1].to_be_bytes()) as u128) << 64) + // ((u32::from_le_bytes(values[2].to_be_bytes()) as u128) << 32) + diff --git a/src/bootloader/protocol.rs b/src/bootloader/protocol.rs index 9fa3dcde..9b641a1f 100644 --- a/src/bootloader/protocol.rs +++ b/src/bootloader/protocol.rs @@ -136,7 +136,7 @@ impl Protocol { // successful status, mirroring our command header let packet = ResponsePacket::try_from(initial_response)?; - assert_eq!(packet.has_data, false); + assert!(!packet.has_data); if let Some(status) = packet.status { panic!("{:?}", status); } @@ -196,7 +196,7 @@ impl Protocol { } let packet = ResponsePacket::try_from(self.read_packet()?)?; - assert_eq!(packet.has_data, false); + assert!(!packet.has_data); if let Some(status) = packet.status { panic!("unexpected status {:?}", &status); } @@ -226,7 +226,7 @@ impl Protocol { } let packet = ResponsePacket::try_from(self.read_packet()?)?; - assert_eq!(packet.has_data, false); + assert!(!packet.has_data); if let Some(status) = packet.status { panic!("unexpected status {:?}", &status); } @@ -255,7 +255,7 @@ impl Protocol { x => x?, })?; // let packet = ResponsePacket::try_from(self.read_packet()?)?; - assert_eq!(packet.has_data, false); + assert!(!packet.has_data); if let Some(status) = packet.status { panic!("unexpected status {:?}", &status); } @@ -292,7 +292,7 @@ impl Protocol { let packet = ResponsePacket::try_from(initial_response)?; // assert_eq!([0x03, 0x00, 0x0C, 0x00], &initial_generic_response[..4]); - assert_eq!(packet.has_data, true); + assert!(!packet.has_data); assert!(packet.status.is_none()); assert_eq!(packet.tag, command::ResponseTag::ReadMemory); @@ -309,7 +309,7 @@ impl Protocol { } let packet = ResponsePacket::try_from(self.read_packet()?)?; - assert_eq!(packet.has_data, false); + assert!(!packet.has_data); assert!(packet.status.is_none()); assert_eq!(packet.tag, command::ResponseTag::Generic); @@ -395,7 +395,7 @@ impl Protocol { if sent >= all { Ok(()) } else { - Err(hidapi::HidError::IncompleteSendError { sent, all })? + Err(hidapi::HidError::IncompleteSendError { sent, all }.into()) } } diff --git a/src/crypto.rs b/src/crypto.rs index d14a6af0..0c8b19f2 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -40,9 +40,9 @@ pub fn nxp_aes_ctr_cipher(ciphertext: &[u8], dek: [u8; 32], nonce: [u32; 4], off let nonce3_offset = offset_blocks + (i as u32); let mut nonce2 = [0u8; 16]; nonce2[..4].copy_from_slice(nonce[0].to_le_bytes().as_ref()); - nonce2[4..8].copy_from_slice(&nonce[1].to_le_bytes().as_ref()); - nonce2[8..12].copy_from_slice(&nonce[2].to_le_bytes().as_ref()); - nonce2[12..16].copy_from_slice(&(nonce[3] + nonce3_offset).to_le_bytes().as_ref()); + nonce2[4..8].copy_from_slice(nonce[1].to_le_bytes().as_ref()); + nonce2[8..12].copy_from_slice(nonce[2].to_le_bytes().as_ref()); + nonce2[12..16].copy_from_slice((nonce[3] + nonce3_offset).to_le_bytes().as_ref()); let mut cipher = Aes256Ctr::new(dek.as_ref().into(), nonce2.as_ref().into()); cipher.apply_keystream(chunk); } diff --git a/src/lib.rs b/src/lib.rs index 87b142c9..5527bcdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ //! //! There is also the somehow underadvertised . +// for readability +#![allow(clippy::identity_op)] #[macro_use] extern crate log; diff --git a/src/pki.rs b/src/pki.rs index e04b16d0..df1f87d3 100644 --- a/src/pki.rs +++ b/src/pki.rs @@ -154,8 +154,7 @@ impl SigningKey { let mut hasher = sha2::Sha256::new(); hasher.update(data); let hashed_data = hasher.finalize(); - let signature = key.sign(padding_scheme, &hashed_data).expect("signatures work"); - signature + key.sign(padding_scheme, &hashed_data).expect("signatures work") } Pkcs11Uri(uri) => { let (context, session, object) = uri.identify_object().unwrap(); @@ -169,8 +168,7 @@ impl SigningKey { // now do a signature, assuming this is an RSA key context.sign_init(session, &mechanism, object).unwrap(); - let signature = context.sign(session, data).unwrap(); - signature + context.sign(session, data).unwrap() } }; assert_eq!(256, signature.len()); @@ -209,8 +207,7 @@ impl SigningKey { // https://github.com/mheese/rust-pkcs11/issues/44 let n = rsa::BigUint::from_bytes_be(&n.to_bytes_le()); let e = rsa::BigUint::from_bytes_be(&e.to_bytes_le()); - let public_key = rsa::RsaPublicKey::new(n, e).unwrap(); - public_key + rsa::RsaPublicKey::new(n, e).unwrap() } }) } @@ -347,7 +344,7 @@ impl Certificate { // let OID_RSA_ENCRYPTION = oid!(1.2.840.113549.1.1.1); assert_eq!(oid_registry::OID_PKCS1_RSAENCRYPTION, spki.algorithm.algorithm); - let public_key = PublicKey(rsa::RsaPublicKey::from_pkcs1_der(&spki.subject_public_key.data)?); + let public_key = PublicKey(rsa::RsaPublicKey::from_pkcs1_der(spki.subject_public_key.data)?); Ok(public_key.fingerprint()) } @@ -363,7 +360,7 @@ impl Certificate { pub fn public_key(&self) -> PublicKey { let spki = self.certificate().tbs_certificate.subject_pki; assert_eq!(oid_registry::OID_PKCS1_RSAENCRYPTION, spki.algorithm.algorithm); - PublicKey(rsa::RsaPublicKey::from_pkcs1_der(&spki.subject_public_key.data).unwrap()) + PublicKey(rsa::RsaPublicKey::from_pkcs1_der(spki.subject_public_key.data).unwrap()) } pub fn fingerprint(&self) -> Sha256Hash { diff --git a/src/protected_flash.rs b/src/protected_flash.rs index c0758cc5..1ecfacd6 100644 --- a/src/protected_flash.rs +++ b/src/protected_flash.rs @@ -533,7 +533,7 @@ impl From for BootSpeed { 0b00 => Nxp, 0b01 => Fro96, 0b10 => Fro48, - 0b11 | _ => Reserved, + /*0b11 |*/ _ => Reserved, } } } @@ -713,8 +713,8 @@ pub struct SecureBootConfiguration { pub trustzone_mode: TrustzoneMode, #[serde(default)] #[serde(skip_serializing_if = "is_default")] - /// For this DICE stuff, see also https://www.microsoft.com/en-us/research/project/dice-device-identifier-composition-engine/ - /// and the actual standard https://trustedcomputinggroup.org/resource/hardware-requirements-for-a-device-identifier-composition-engine/ + /// For this DICE stuff, see also + /// and the actual standard pub dice_computation_disabled: bool, #[serde(default)] #[serde(skip_serializing_if = "is_default")] diff --git a/src/secure_binary.rs b/src/secure_binary.rs index bcb6ba06..564ef9b4 100644 --- a/src/secure_binary.rs +++ b/src/secure_binary.rs @@ -187,8 +187,8 @@ pub fn sniff(file: &[u8]) -> Result { // firmware starts with SP (4b) then PC (4B) // maybe: fallback to viewing as "bin" if not ELF or SB? &[0x00, 0x00, 0x04, 0x20] => { - match &file[0x20..0x24] { - &[0x00, 0x00, 0x00, 0x00] => Filetype::UnsignedBin, + match file[0x20..0x24] { + [0x00, 0x00, 0x00, 0x00] => Filetype::UnsignedBin, _ => Filetype::SignedBin, } } @@ -522,8 +522,7 @@ impl UnsignedSb21File { pub fn total_serialized_length(&self) -> usize { // this needs to be everything-everything (i.e., len(file.sb2)) - let blocks = 0 - + self.boot_tag_offset_blocks() + let blocks = self.boot_tag_offset_blocks() // boot tag(1) + hmac table(2*2) + 5 // the actual payload @@ -652,7 +651,7 @@ impl UnsignedSb21File { /// - on-disk/file keys (cf. RFC 8089: The "file" URI Scheme) /// - PKCS#11 keys, so any kind of HSM can be used (cf. RFC 7512: The PKCS #11 URI Scheme) /// - possibly other hardware interfaces, such as Tony's yubihsm crate (perhaps: yubihsm:id=) - /// cf: https://docs.rs/yubihsm/0.37.0/yubihsm/client/struct.Client.html#method.sign_rsa_pkcs1v15_sha256 + /// cf: pub fn sign(&self, signing_key: &SigningKey) -> SignedSb21File { let header_part = self.header_part(); let header_bytes = header_part.to_bytes(); @@ -693,143 +692,140 @@ pub fn show(filename: &str) -> Result> { let filetype = sniff(&data)?; trace!("filetype: {:?}", filetype); - match filetype { - Filetype::Sb21 => { - let (i, header) = Sb2Header::inner_from_bytes(&data)?;//.unwrap();//.1;//.map_err(|_| anyhow::anyhow!("could not parse SB2 file"))?.1; - let (i, digest_hmac) = take::<_, _, ()>(32u8)(i)?; - let (i, keyblob) = Keyblob::from_bytes(i)?; - let (i, certificate_block_header) = FullCertificateBlockHeader::from_bytes(i)?; - let (i, certificate_length) = le_u32::<_, ()>(i).unwrap(); - let (i, certificate_data) = take::<_, _, ()>(certificate_length)(i)?; - - let unpadded_signed_data_length = 16*(6 + 2 + 5 + 2) + 4 + certificate_length + 128; - - let (i, rot_key_hashes) = take::<_, _, ()>(128usize)(i)?; - let i = if (unpadded_signed_data_length % 16) != 0 { - take::<_, _, ()>(16u8 - (unpadded_signed_data_length % 16) as u8)(i)?.0 - } else { - i - }; - let (i, signature) = take::<_, _, ()>(256usize)(i)?; - - info!("rotkh: {}", hex_str!(&crate::pki::Certificates::fingerprint_from_bytes(&rot_key_hashes).0)); - - // the weird sectionAllignment (sic!) - info!("SB2 header: \n{:#?}", &header); - info!(" nonce: {:?}", &header.nonce); - info!(" tstmp: {}", header.timestamp_microseconds_since_millenium); - info!(" junk: {}", hexstr!(&header.sb_header_padding)); - info!("HMAC: \n{:?}", &digest_hmac); - info!("keyblob: \n{:?}", &keyblob); - info!(" DEK: {}", hexstr!(&keyblob.dek)); - info!(" MAC: {}", hexstr!(&keyblob.mac)); - info!("CTH: \n{:?}", &certificate_block_header); - - let certificate = match X509Certificate::from_der(certificate_data) { - Ok((rem, cert)) => { - println!("remainder: {}", hex_str!(rem)); - // assert!(rem.is_empty()); - assert_eq!(cert.tbs_certificate.version, x509_parser::x509::X509Version::V3); - cert - } - _ => { panic!("invalid certificate"); } - }; - // info!("cert: \n{:?}", &certificate); - println!("certificate length: {}", certificate_length); - - // now let's verify the signature - // pad 16 - println!("unpadded signed data length: 0x{:x}", unpadded_signed_data_length); - // let signed_data_length = signed_data_length + (16 - (signed_data_length % 16)); - let signed_data_length = 16 * ((unpadded_signed_data_length + 15) / 16); - - // let signed_data_length = 0x5f0; - println!("end of cert data: {:>16x}", hex_str!(&certificate_data)); - println!("signed data length: 0x{:x}", signed_data_length); - - let signed_data_hash = sha256(&data[..signed_data_length as usize]); - println!("data hash: {}", hex_str!(&signed_data_hash, 4)); - - let spki = certificate.tbs_certificate.subject_pki; - trace!("alg: {:?}", spki.algorithm.algorithm); - assert_eq!(oid_registry::OID_PKCS1_RSAENCRYPTION, spki.algorithm.algorithm); - - println!("rsa pub key: {:?}", &spki.subject_public_key.data); - let public_key = rsa::RsaPublicKey::from_pkcs1_der(&spki.subject_public_key.data).expect("can parse public key"); - println!("signature: {}", hexstr!(&signature)); - let padding_scheme = rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)); - use rsa::PublicKey; - public_key.verify(padding_scheme, &signed_data_hash, signature).expect("signature valid"); - // let signature = secret_key.sign(padding_scheme, &hashed_image).expect("signatures work"); - - let calculated_boot_tag_offset_bytes = signed_data_length + 256; - assert_eq!(calculated_boot_tag_offset_bytes, header.boot_tag_offset_blocks * 16); - - // alright, BootTag, Hmac, Section - // - // here's what happens: - // - encryption is weird... (big-endian AES-CTR, but with nonce modified by adding - // block number to little-endian encoding of last nonce-value) - // - // - first: encrypted boot tag - // - then: unencrypted HMAC of encrypted boot tag - // - then: unencrypted HMAC of encrypted section data (commands and their data) - // - then: encrypted section data - // - // the digest HMAC at the top after image header is HMAC(first HMAC || second HMAC) - // - // - let _boot_tag_offset_blocks = header.boot_tag_offset_blocks; - - let (i, enciphered_boot_tag) = take::<_, _, ()>(16u8)(i)?; - let calculated_boot_tag_hmac = hmac(keyblob.mac, &enciphered_boot_tag); - - let deciphered_boot_tag = nxp_aes_ctr_cipher( - enciphered_boot_tag, - keyblob.dek, header.nonce, - header.boot_tag_offset_blocks, - ); - - let (_, boot_tag) = BootCommand::from_bytes(&deciphered_boot_tag)?; - println!("boot tag: {:?}", &boot_tag); - // TODO? check cipher blocks - - let (i, hmac_table) = take::<_, _, ()>(64u8)(i)?; - - let (_, (boot_tag_hmac, section_hmac)) = tuple(( - take::<_, _, ()>(32u8), - take::<_, _, ()>(32u8), - ))(hmac_table)?; - - assert_eq!(boot_tag_hmac, calculated_boot_tag_hmac); - - // let (i, section_hmac) = take::<_, _, ()>(32u8)(i)?; - - let enciphered_section = i; - - let calculated_section_hmac = hmac(keyblob.mac, enciphered_section); - assert_eq!(section_hmac, calculated_section_hmac); - - let deciphered_section = nxp_aes_ctr_cipher( - enciphered_section, - keyblob.dek, header.nonce, - header.boot_tag_offset_blocks + 5, - ); - - let calculated_digest_hmac = hmac(keyblob.mac, hmac_table); - assert_eq!(digest_hmac, calculated_digest_hmac); - - let mut i = deciphered_section.as_ref(); - loop { - let (j, command) = BootCommand::from_bytes(i)?; - i = j; - trace!("command: {:?}", &command); - if i.is_empty() { - break; - } + if let Filetype::Sb21 = filetype { + let (i, header) = Sb2Header::inner_from_bytes(&data)?;//.unwrap();//.1;//.map_err(|_| anyhow::anyhow!("could not parse SB2 file"))?.1; + let (i, digest_hmac) = take::<_, _, ()>(32u8)(i)?; + let (i, keyblob) = Keyblob::from_bytes(i)?; + let (i, certificate_block_header) = FullCertificateBlockHeader::from_bytes(i)?; + let (i, certificate_length) = le_u32::<_, ()>(i).unwrap(); + let (i, certificate_data) = take::<_, _, ()>(certificate_length)(i)?; + + let unpadded_signed_data_length = 16*(6 + 2 + 5 + 2) + 4 + certificate_length + 128; + + let (i, rot_key_hashes) = take::<_, _, ()>(128usize)(i)?; + let i = if (unpadded_signed_data_length % 16) != 0 { + take::<_, _, ()>(16u8 - (unpadded_signed_data_length % 16) as u8)(i)?.0 + } else { + i + }; + let (i, signature) = take::<_, _, ()>(256usize)(i)?; + + info!("rotkh: {}", hex_str!(&crate::pki::Certificates::fingerprint_from_bytes(rot_key_hashes).0)); + + // the weird sectionAllignment (sic!) + info!("SB2 header: \n{:#?}", &header); + info!(" nonce: {:?}", &header.nonce); + info!(" tstmp: {}", header.timestamp_microseconds_since_millenium); + info!(" junk: {}", hexstr!(&header.sb_header_padding)); + info!("HMAC: \n{:?}", &digest_hmac); + info!("keyblob: \n{:?}", &keyblob); + info!(" DEK: {}", hexstr!(&keyblob.dek)); + info!(" MAC: {}", hexstr!(&keyblob.mac)); + info!("CTH: \n{:?}", &certificate_block_header); + + let certificate = match X509Certificate::from_der(certificate_data) { + Ok((rem, cert)) => { + println!("remainder: {}", hex_str!(rem)); + // assert!(rem.is_empty()); + assert_eq!(cert.tbs_certificate.version, x509_parser::x509::X509Version::V3); + cert + } + _ => { panic!("invalid certificate"); } + }; + // info!("cert: \n{:?}", &certificate); + println!("certificate length: {}", certificate_length); + + // now let's verify the signature + // pad 16 + println!("unpadded signed data length: 0x{:x}", unpadded_signed_data_length); + // let signed_data_length = signed_data_length + (16 - (signed_data_length % 16)); + let signed_data_length = 16 * ((unpadded_signed_data_length + 15) / 16); + + // let signed_data_length = 0x5f0; + println!("end of cert data: {:>16x}", hex_str!(&certificate_data)); + println!("signed data length: 0x{:x}", signed_data_length); + + let signed_data_hash = sha256(&data[..signed_data_length as usize]); + println!("data hash: {}", hex_str!(&signed_data_hash, 4)); + + let spki = certificate.tbs_certificate.subject_pki; + trace!("alg: {:?}", spki.algorithm.algorithm); + assert_eq!(oid_registry::OID_PKCS1_RSAENCRYPTION, spki.algorithm.algorithm); + + println!("rsa pub key: {:?}", &spki.subject_public_key.data); + let public_key = rsa::RsaPublicKey::from_pkcs1_der(spki.subject_public_key.data).expect("can parse public key"); + println!("signature: {}", hexstr!(&signature)); + let padding_scheme = rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)); + use rsa::PublicKey; + public_key.verify(padding_scheme, &signed_data_hash, signature).expect("signature valid"); + // let signature = secret_key.sign(padding_scheme, &hashed_image).expect("signatures work"); + + let calculated_boot_tag_offset_bytes = signed_data_length + 256; + assert_eq!(calculated_boot_tag_offset_bytes, header.boot_tag_offset_blocks * 16); + + // alright, BootTag, Hmac, Section + // + // here's what happens: + // - encryption is weird... (big-endian AES-CTR, but with nonce modified by adding + // block number to little-endian encoding of last nonce-value) + // + // - first: encrypted boot tag + // - then: unencrypted HMAC of encrypted boot tag + // - then: unencrypted HMAC of encrypted section data (commands and their data) + // - then: encrypted section data + // + // the digest HMAC at the top after image header is HMAC(first HMAC || second HMAC) + // + // + let _boot_tag_offset_blocks = header.boot_tag_offset_blocks; + + let (i, enciphered_boot_tag) = take::<_, _, ()>(16u8)(i)?; + let calculated_boot_tag_hmac = hmac(keyblob.mac, enciphered_boot_tag); + + let deciphered_boot_tag = nxp_aes_ctr_cipher( + enciphered_boot_tag, + keyblob.dek, header.nonce, + header.boot_tag_offset_blocks, + ); + + let (_, boot_tag) = BootCommand::from_bytes(&deciphered_boot_tag)?; + println!("boot tag: {:?}", &boot_tag); + // TODO? check cipher blocks + + let (i, hmac_table) = take::<_, _, ()>(64u8)(i)?; + + let (_, (boot_tag_hmac, section_hmac)) = tuple(( + take::<_, _, ()>(32u8), + take::<_, _, ()>(32u8), + ))(hmac_table)?; + + assert_eq!(boot_tag_hmac, calculated_boot_tag_hmac); + + // let (i, section_hmac) = take::<_, _, ()>(32u8)(i)?; + + let enciphered_section = i; + + let calculated_section_hmac = hmac(keyblob.mac, enciphered_section); + assert_eq!(section_hmac, calculated_section_hmac); + + let deciphered_section = nxp_aes_ctr_cipher( + enciphered_section, + keyblob.dek, header.nonce, + header.boot_tag_offset_blocks + 5, + ); + + let calculated_digest_hmac = hmac(keyblob.mac, hmac_table); + assert_eq!(digest_hmac, calculated_digest_hmac); + + let mut i = deciphered_section.as_ref(); + loop { + let (j, command) = BootCommand::from_bytes(i)?; + i = j; + trace!("command: {:?}", &command); + if i.is_empty() { + break; } } - _ => {} } println!("crc32(123456789) = 0x{:x}", crc32(b"123456789")); @@ -1015,9 +1011,8 @@ fn aes_wrap(key: [u8; 32], data: &[u8]) -> Vec { let n = (data.len() as u64) / 8; let mut A = u64::from_be_bytes([0xA6u8; 8]); - let mut R = Vec::new(); // to keep NIST indices, never used - R.push(0); + let mut R = vec![0]; for (_, P) in (1..=n).zip(data.chunks(8)) { R.push(u64::from_be_bytes(P.try_into().unwrap())); } @@ -1033,7 +1028,7 @@ fn aes_wrap(key: [u8; 32], data: &[u8]) -> Vec { let t = (n*j + i) as u64; A = u64::from_be_bytes(B[..8].try_into().unwrap()); // i.e., MSB(64, B) ^ t - A = A ^ t; + A ^= t; R[i as usize] = u64::from_be_bytes(B[8..].try_into().unwrap()); } } @@ -1058,9 +1053,8 @@ fn aes_unwrap(key: [u8; 32], wrapped: &[u8]) -> Vec { let aes = aes::Aes256::new(&key.into()); let n = (wrapped.len() as u64) / 8 - 1; let mut A = u64::from_be_bytes(wrapped[..8].try_into().unwrap()); - let mut R = Vec::new(); // to keep NIST indices, never used - R.push(0); + let mut R = vec![0]; for (_, C) in (1..=n).zip(wrapped.chunks(8).skip(1)) { R.push(u64::from_be_bytes(C.try_into().unwrap())); } @@ -1308,6 +1302,10 @@ impl Sb2Header { Self::LEN } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn product_version(&self) -> Version { self.product_version } diff --git a/src/secure_binary/command.rs b/src/secure_binary/command.rs index 6ff4a74b..e6ba2db4 100644 --- a/src/secure_binary/command.rs +++ b/src/secure_binary/command.rs @@ -57,9 +57,7 @@ pub struct RawBootCommand { impl RawBootCommand { pub fn to_bytes(&self) -> [u8; 16] { - let mut buffer = Vec::new(); - buffer.push(0); - buffer.push(self.tag); + let mut buffer = vec![0, self.tag]; buffer.extend_from_slice(self.flags.to_le_bytes().as_ref()); buffer.extend_from_slice(self.address.to_le_bytes().as_ref()); buffer.extend_from_slice(self.count.to_le_bytes().as_ref()); diff --git a/src/signed_binary.rs b/src/signed_binary.rs index 78171299..cf0289f5 100644 --- a/src/signed_binary.rs +++ b/src/signed_binary.rs @@ -45,7 +45,7 @@ impl ImageSigningRequest { plain_image, certificates, signing_key, - slot: slot, + slot, }) } @@ -74,7 +74,7 @@ impl ImageSigningRequest { let mut image = word_padded(&self.plain_image); - let certificate = word_padded(self.certificates.certificate_der(i.into())); + let certificate = word_padded(self.certificates.certificate_der(i)); let total_image_size = modify_header(&mut image, certificate.len()); // println!("{:x}", total_image_size);