inapt 0.3.2

A minimal Debian/Ubuntu APT repository proxy written in Rust. Exposes a valid APT repo structure over HTTP, sourcing .deb packages from GitHub Releases.
Documentation
use std::{io::Write, path::PathBuf};

use anyhow::Context;
use sequoia_openpgp::{
    Cert,
    crypto::{KeyPair, Password},
    parse::Parse,
    policy::StandardPolicy,
};

mod cipher;

#[derive(serde::Deserialize)]
pub struct Config {
    private_key_path: PathBuf,
    #[serde(default)]
    passphrase: Option<String>,
}

impl Config {
    fn generate_private_key(&self) -> anyhow::Result<()> {
        use sequoia_openpgp::{cert::CertBuilder, serialize::Serialize, types::KeyFlags};

        let (cert, _) = CertBuilder::new()
            .set_validity_period(None)
            .add_subkey(
                KeyFlags::empty().set_transport_encryption().set_group_key(),
                None,
                None,
            )
            .add_signing_subkey()
            .add_certification_subkey()
            .add_storage_encryption_subkey()
            .generate()?;

        let mut output = std::fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&self.private_key_path)
            .context("unable to create file")?;
        cert.as_tsk().armored().serialize(&mut output)?;
        output
            .flush()
            .context("unable to flush private key content")?;

        Ok(())
    }

    pub fn build(self) -> anyhow::Result<PGPClient> {
        if !self.private_key_path.exists() {
            self.generate_private_key()?;
        }

        let private_key = Cert::from_file(self.private_key_path)?;

        let policy = StandardPolicy::new();
        let keypair = private_key
            .keys()
            .secret()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .context("signing key not found")?;

        let mut private_key = keypair.key().clone();
        if private_key.secret().is_encrypted()
            && let Some(password) = self.passphrase
        {
            let password = Password::from(password);
            private_key
                .secret_mut()
                .decrypt_in_place(keypair.key(), &password)
                .context("unable to decrypt private key")?;
        };
        let keypair = private_key
            .into_keypair()
            .context("unable to convert to key pair")?;

        Ok(PGPClient { keypair })
    }
}

#[derive(Clone)]
pub struct PGPClient {
    keypair: KeyPair,
}