kxfides 0.1.0

CLI for signing & verifying media with KX-Fides.
// cli/src/cmd_sign.rs

use anyhow::{bail, Context, Result};
use owo_colors::OwoColorize;
use std::fs;
use std::path::Path;

use kx_fides_core::hash::hash_file;
use kx_fides_core::keys::load_keypair;
use kx_fides_core::manifest::{build_manifest, write_manifest};
use kx_fides_core::sign::sign_hash;
use kx_fides_core::timestamp::now_utc;

use super::SignArgs;

pub fn run(args: SignArgs) -> Result<()> {
    let media = args.media;
    let key_path = args.key;
    let out_path = args.out;
    let mime = args.mime;

    if !Path::new(&media).exists() {
        bail!("media file not found: {}", media.display());
    }
    if !Path::new(&key_path).exists() {
        bail!("key file not found: {}", key_path.display());
    }
    if let Some(parent) = out_path.parent() {
        if !parent.as_os_str().is_empty() && !parent.exists() {
            fs::create_dir_all(parent)
                .with_context(|| format!("failed to create {}", parent.display()))?;
        }
    }

    let kp = load_keypair(key_path.to_str().unwrap())
        .with_context(|| format!("failed to load keypair from {}", key_path.display()))?;

    let h = hash_file(&media)
        .with_context(|| format!("failed to hash {}", media.display()))?;

    let sig = sign_hash(&h, &kp).context("failed to sign hash")?;
    let ts = now_utc();

    let manifest = build_manifest(
        &media,
        &h,
        sig.value,
        kp.public_key,
        ts,
        mime,
        None, // meta
    );

    write_manifest(&out_path, &manifest).with_context(|| {
        format!(
            "failed to write manifest to {}",
            out_path.display()
        )
    })?;

    println!(
        "{} {}",
        "✓ signed".green().bold(),
        media.display()
    );
    println!(
        "{} {}",
        "→ manifest:".cyan(),
        out_path.display()
    );
    println!("{} {}", "hash:".cyan(), h.value);
    println!("{} {}", "alg:".cyan(), h.algorithm);

    Ok(())
}