use anyhow::Context;
use image4::{
manifest::{CertChain, UnsignedManifest},
property::Value,
Manifest, Tag,
};
use image4_pki::{PrivateKey, SigningKey};
use std::{collections::BTreeMap, path::Path};
use tokio::{fs::File, io::AsyncReadExt};
use x509_cert::Certificate;
use crate::config::{SignerConfig, SignersConfig};
#[derive(Clone, Debug)]
pub struct Signer {
chain: CertChain,
key: SigningKey,
}
async fn load_pem_cert_chain(path: impl AsRef<Path>) -> anyhow::Result<CertChain> {
let mut v = Vec::new();
File::open(path).await?.read_to_end(&mut v).await?;
let certs = Certificate::load_pem_chain(&v)?;
Ok(CertChain::from_certs(&certs)?)
}
async fn load_pem_private_key(path: impl AsRef<Path>) -> anyhow::Result<PrivateKey> {
let mut s = String::new();
File::open(path).await?.read_to_string(&mut s).await?;
Ok(PrivateKey::from_pem(&s)?)
}
impl Signer {
pub async fn load(config: &SignerConfig) -> anyhow::Result<Self> {
let chain = load_pem_cert_chain(&config.certificate_chain_path)
.await
.context("Failed to load certificate chain.")?;
let private_key = load_pem_private_key(&config.private_key_path)
.await
.context("Failed to load private key.")?;
let key = SigningKey::from_key_and_algo(private_key, config.digest_algorithm)?;
Ok(Self { chain, key })
}
pub fn encode_and_sign(&self, body: &BTreeMap<Tag, Value>) -> anyhow::Result<Manifest> {
let encoded = UnsignedManifest::encode_from(body)?;
Ok(encoded.sign(&self.key, self.chain.clone())?)
}
}
#[derive(Clone)]
pub struct Signers {
pub ticket: Signer,
pub local_policy: Signer,
}
impl Signers {
pub async fn load(config: &SignersConfig) -> anyhow::Result<Self> {
let ticket = Signer::load(&config.ap_ticket_signer)
.await
.context("Failed to load AP ticket signer.")?;
let local_policy = Signer::load(&config.local_policy_signer)
.await
.context("Failed to load local policy signer at {}.")?;
Ok(Self {
ticket,
local_policy,
})
}
}