signet 0.0.1

code signing tool
Documentation
use anyhow::Result;
use rand_core::OsRng;
use ssh_key::{Algorithm, LineEnding, PrivateKey};
use crate::{Input, Signet, System};
use crate::args::Keys;
use crate::persist::read;

pub fn keys<S: System>(signet: &Signet<S>, cmd: Keys) -> Result<()> {
    match cmd {
        Keys::Create      => create(signet),
        Keys::Delete(key) => delete(signet, &key),
        Keys::Export(key) => export(signet, &key),
        Keys::Import(key) => import(signet, &key),
        Keys::Public(key) => public(signet, &key),
        Keys::List        => list(signet),
    }
}

pub fn create<S: System>(signet: &Signet<S>) -> Result<()> {
    let mut keychain = signet.current()?;

    let password = keychain.password();
    let password = password.lookup::<S>()?;

    let alg = Algorithm::Ed25519;
    let key = PrivateKey::random(OsRng, alg)?;

    let key = key.encrypt(OsRng, password)?;
    let id  = keychain.add(key);
    signet.sync(&keychain)?;

    Ok(println!("created key {id}"))
}

pub fn delete<S: System>(signet: &Signet<S>, key: &str) -> Result<()> {
    let mut keychain = signet.current()?;

    let key = keychain.find(key)?;
    let id  = key.id();

    keychain.delete(&id);
    signet.sync(&keychain)?;

    Ok(println!("deleted key {id}"))
}

pub fn export<S: System>(signet: &Signet<S>, key: &str) -> Result<()> {
    let keychain = signet.current()?;
    let password = keychain.password();
    let password = password.lookup::<S>()?;

    let key = keychain.find(key)?;
    let key = key.decrypt(password)?;

    let password = S::prompt("export password: ")?;
    let exported = key.encrypt(OsRng, password)?;
    let exported = &*exported.to_openssh(LineEnding::default())?;

    Ok(print!("{exported}"))
}

pub fn import<S: System>(signet: &Signet<S>, key: &Input) -> Result<()> {
    let mut keychain = signet.current()?;

    let key = read::<S>(key)?;
    let key = PrivateKey::from_openssh(key)?;

    let password = keychain.password();
    let password = password.lookup::<S>()?;

    let key = match key.is_encrypted() {
        true  => decrypt::<S>(key)?,
        false => key,
    }.encrypt(OsRng, password)?;

    let id = keychain.add(key);
    signet.sync(&keychain)?;

    Ok(println!("imported key {id}"))
}

pub fn public<S: System>(signet: &Signet<S>, key: &str) -> Result<()> {
    let keychain = signet.current()?;

    let key = keychain.find(key)?;
    let key = key.public_key().to_string();

    Ok(println!("{key}"))
}

pub fn list<S: System>(signet: &Signet<S>) -> Result<()> {
    let keystore = signet.keystore()?;
    let current  = keystore.current()?;
    let keychain = signet.keychain(current)?;

    let list = keychain.keys().map(|key| {
        format!("{:>66}", key.id())
    }).collect::<Vec<_>>().join("\n");

    Ok(println!("keychain '{current}':\n{list}"))
}

fn decrypt<S: System>(key: PrivateKey) -> Result<PrivateKey> {
    let password = S::prompt("key password: ")?;
    Ok(key.decrypt(password)?)
}