use std::fs;
use anyhow::{Context as _, Result};
use crate::pki::{Certificate, CertificateSlot, Certificates, SigningKey};
use crate::secure_binary::Config;
use crate::util::word_padded;
pub struct SignedImage(pub Vec<u8>);
pub struct ImageSigningRequest {
pub plain_image: Vec<u8>,
certificates: Certificates,
signing_key: SigningKey,
pub slot: CertificateSlot,
}
impl ImageSigningRequest {
pub fn try_from(config: &Config) -> Result<Self> {
let plain_image = fs::read(&config.firmware.image).with_context(|| {
format!(
"Failed to read firmware image from {}",
config.firmware.image
)
})?;
let certificates = Certificates::try_from_pki(&config.pki)?;
let signing_key = SigningKey::try_from_uri(config.pki.signing_key.as_ref())?;
let slot = certificates.index_of(signing_key.public_key())?;
Ok(Self {
plain_image,
certificates,
signing_key,
slot,
})
}
pub fn selected_certificate(&self) -> &Certificate {
self.certificates.certificate(self.slot)
}
pub fn certificates(&self) -> &Certificates {
&self.certificates
}
pub fn sign(&self) -> SignedImage {
let mut image = self.assemble_unsigned_image(self.slot);
let signature = self.signing_key.sign(&image);
image.extend_from_slice(signature.as_ref());
SignedImage(image)
}
fn assemble_unsigned_image(&self, i: CertificateSlot) -> Vec<u8> {
let mut image = word_padded(&self.plain_image);
let certificate = word_padded(self.certificates.certificate_der(i));
let total_image_size = modify_header(&mut image, certificate.len());
let build_number = 1;
let certificate_block_header = certificate_block_header_bytes(
total_image_size - 256,
certificate.len(),
build_number,
);
image.extend_from_slice(&certificate_block_header);
extendu(&mut image, certificate.len());
image.extend_from_slice(&certificate);
for h in self.certificates.fingerprints() {
image.extend_from_slice(&h.0);
}
image
}
}
fn modify_header(padded_image: &mut Vec<u8>, padded_certificate_length: usize) -> usize {
let image_size = padded_image.len();
let non_image_size =
32 +
(4 + padded_certificate_length) +
4*32 +
256;
let total_image_size = image_size + non_image_size;
padded_image[0x20..][..4].copy_from_slice((total_image_size as u32).to_le_bytes().as_ref());
padded_image[0x24..][..4].copy_from_slice(&[0x04, 0x00, 0x00, 0x00]);
padded_image[0x28..][..4].copy_from_slice((image_size as u32).to_le_bytes().as_ref());
total_image_size
}
fn certificate_block_header_bytes(
total_image_length: usize,
aligned_cert_length: usize,
build_number: u32,
) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"cert");
extend16(&mut bytes, 1);
extend16(&mut bytes, 0);
extend32(&mut bytes, 0x20);
extend32(&mut bytes, 0);
extend32(&mut bytes, build_number);
extendu(&mut bytes, total_image_length);
let certificates: u32 = 1;
extend32(&mut bytes, certificates);
extendu(&mut bytes, aligned_cert_length + 4);
assert_eq!(32, bytes.len());
bytes
}
fn extend16(bytes: &mut Vec<u8>, value: u16) {
bytes.extend_from_slice(value.to_le_bytes().as_ref());
}
fn extend32(bytes: &mut Vec<u8>, value: u32) {
bytes.extend_from_slice(value.to_le_bytes().as_ref());
}
#[allow(dead_code)]
fn extend64(bytes: &mut Vec<u8>, value: u64) {
bytes.extend_from_slice(value.to_le_bytes().as_ref());
}
fn extendu(bytes: &mut Vec<u8>, value: usize) {
bytes.extend_from_slice((value as u32).to_le_bytes().as_ref());
}