use clap::Parser; use miniquorum::{Payload, PayloadVerification}; use sequoia_openpgp::Fingerprint; use std::{fs::File, path::PathBuf}; #[derive(clap::Parser)] /// An Icepick-specific subset of the Quorum decision-making system. enum MiniQuorum { /// Verify signatures on an Icepick Payload file. VerifySignatures { /// The file containing OpenPGP Certificates used for verifying signatures. keyring_file: PathBuf, /// The file provided as input. /// /// If no file is passed, standard input is used. input_file: Option, /// An OpenPGP Fingerprint to use in place of on-smartcard certificate detection. /// /// This functionality is only recommended if verifying a payload without the physical /// presence of any signer, and builds a web of trust from the signer fingerprint provided. #[arg(long)] fingerprint: Option, /// The file to write the resulting payload to, if verification is successful. #[arg(long)] output_file: Option, }, /// Add a signature to an Icepick Payload file. AddSignature { /// The file to use as input. /// /// If no file is provided, standard input is used. If a file is provided and no output /// file is provided, it will be used in-place as the output file with the additional /// signature added. input_file: Option, /// The file to use as output. /// /// If no file is provided, but an input file is provided, the input file is used. If no /// input file is provided, standard output is used. #[arg(long)] output_file: Option, }, } fn main() -> Result<(), Box> { match MiniQuorum::parse() { MiniQuorum::VerifySignatures { keyring_file, input_file, fingerprint, output_file, } => { assert_ne!( input_file, output_file, "output is verified data; not overwriting signed input data" ); let (payload, certs) = match input_file { Some(input_file) => Payload::load(&input_file, &keyring_file)?, None => { let stdin = std::io::stdin(); let keyring_file = File::open(&keyring_file)?; Payload::from_readers(stdin, keyring_file)? } }; let policy = PayloadVerification::new().with_threshold(certs.len().try_into()?); payload.verify_signatures(&certs, &policy, fingerprint)?; if let Some(output_file) = output_file { let file = File::create(output_file)?; serde_json::to_writer_pretty(file, &payload)?; } else { let stdout = std::io::stdout(); serde_json::to_writer_pretty(stdout, &payload)?; } } MiniQuorum::AddSignature { input_file, output_file, } => { let mut payload: Payload = match &input_file { Some(input_file) => { let input_file = File::open(input_file)?; serde_json::from_reader(input_file)? } None => { let stdin = std::io::stdin(); serde_json::from_reader(stdin)? } }; payload.add_signature()?; if let Some(output_file) = output_file { // write to output let file = File::create(output_file)?; serde_json::to_writer_pretty(file, &payload)?; } else if let Some(input_file) = input_file { // write to tempfile, move to input_file let output_file = input_file.with_extension("tmp"); let mut file = File::create_new(&output_file)?; serde_json::to_writer_pretty(&mut file, &payload)?; drop(file); std::fs::copy(&output_file, input_file)?; std::fs::remove_file(output_file)?; } else { // write to standard output? println!("{}", serde_json::to_string_pretty(&payload)?); } } } Ok(()) }