1use std::io::{Read as _, Write as _};23use age::{Identity, Recipient};4use anyhow::{Context as _, Result, anyhow, bail};5use camino::Utf8PathBuf;6use serde::{Deserialize, Serialize};78use crate::MANIFEST_VERSION;910#[derive(Serialize, Deserialize, Debug, Clone)]11#[serde(rename_all = "camelCase")]12pub struct Manifest {13 pub version: u32,14 pub host: String,15 pub toplevel: Utf8PathBuf,16 pub paths: Vec<PathEntry>,17}1819#[derive(Serialize, Deserialize, Debug, Clone)]20#[serde(rename_all = "camelCase")]21pub struct PathEntry {22 pub store_path: Utf8PathBuf,23 pub nar_hash: String,24 pub nar_size: u64,25 pub references: Vec<Utf8PathBuf>,26 pub sigs: Vec<String>,27 pub compression: String,28 pub key: String,29 pub chunks: Vec<String>,30}3132pub fn encrypt_manifest<'r>(33 manifest: &Manifest,34 recipients: impl Iterator<Item = &'r dyn Recipient>,35) -> Result<Vec<u8>> {36 let json = serde_json::to_vec(manifest)?;37 let mut out = Vec::new();38 let mut encryptor = age::Encryptor::with_recipients(recipients)39 .map_err(|e| anyhow!("no recipients: {e}"))?40 .wrap_output(&mut out)41 .context("age encrypt")?;42 encryptor.write_all(&json)?;43 encryptor.finish()?;44 Ok(out)45}464748pub fn decrypt_manifest(data: &[u8], identity: &dyn Identity) -> Result<Option<Manifest>> {49 let decryptor = age::Decryptor::new(data).context("age decrypt")?;50 let mut reader = match decryptor.decrypt(std::iter::once(identity)) {51 Ok(reader) => reader,52 Err(age::DecryptError::NoMatchingKeys) => return Ok(None),53 Err(e) => return Err(e).context("decrypting manifest"),54 };55 let mut json = Vec::new();56 reader.read_to_end(&mut json)?;57 let manifest: Manifest = serde_json::from_slice(&json).context("manifest json")?;58 if manifest.version != MANIFEST_VERSION {59 bail!(60 "unsupported manifest version {}, this fleet-usbd supports {MANIFEST_VERSION}",61 manifest.version62 );63 }64 Ok(Some(manifest))65}