diff --git a/src/fingerprint.rs b/src/fingerprint.rs index 974a203..60cdfff 100644 --- a/src/fingerprint.rs +++ b/src/fingerprint.rs @@ -8,8 +8,10 @@ use super::utils; /// Execute the `fingerprint` command. pub fn command(key_path: PathBuf) -> Result<()> { - let public_key = utils::get_public_key(key_path)?; - let hash = public_key.fingerprint()?; - println!("{hash}"); + for (path, public_key) in utils::get_public_keys(key_path)? { + let hash = public_key.fingerprint()?; + println!("{}:", path.display()); + println!(" - {hash}"); + } Ok(()) } diff --git a/src/raw/sign.rs b/src/raw/sign.rs index 77adc2f..985bbd0 100644 --- a/src/raw/sign.rs +++ b/src/raw/sign.rs @@ -11,9 +11,10 @@ use crate::utils; /// Execute the `sign` command. pub fn command(key_path: PathBuf, rev: String) -> Result<()> { let repo = utils::open_repository()?; - let secret_key = utils::get_secret_key(key_path)?; - let tree_oid = sign(&repo, &secret_key, &rev)?; - println!("{tree_oid}"); + for secret_key in utils::get_secret_keys(key_path)?.into_values() { + let tree_oid = sign(&repo, &secret_key, &rev)?; + println!("{tree_oid}"); + } Ok(()) } diff --git a/src/raw/verify.rs b/src/raw/verify.rs index 6e1a434..7c5e734 100644 --- a/src/raw/verify.rs +++ b/src/raw/verify.rs @@ -10,10 +10,11 @@ use crate::utils; /// Execute the `verify` command. pub fn command(key_path: PathBuf, recover: bool, tree_rev: String) -> Result<()> { let repo = utils::open_repository()?; - let public_key = utils::get_public_key(key_path)?; - let recovered_oid = verify(&repo, &public_key, &tree_rev, recover)?; - if let Some(recovered_oid) = recovered_oid { - println!("{recovered_oid}"); + for public_key in utils::get_public_keys(key_path)?.into_values() { + let recovered_oid = verify(&repo, &public_key, &tree_rev, recover)?; + if let Some(recovered_oid) = recovered_oid { + println!("{recovered_oid}"); + } } Ok(()) } diff --git a/src/rev_lookup.rs b/src/rev_lookup.rs index 7923929..6213bc7 100644 --- a/src/rev_lookup.rs +++ b/src/rev_lookup.rs @@ -10,17 +10,18 @@ use crate::utils; /// Execute the `rev-lookup` command. pub fn command(key_path: PathBuf, rev: String) -> Result<()> { let repo = utils::open_repository()?; - let public_key = utils::get_public_key(key_path)?; - let tree_rev = { - let object_oid = repo - .revparse_single(&rev) - .context("Failed to look-up git object")? - .id(); - let key_fingerprint = public_key.fingerprint()?; - utils::craft_signature_reference(key_fingerprint, object_oid) - }; - repo.revparse_single(&tree_rev) - .with_context(|| format!("No signature found for {rev}"))?; - println!("{tree_rev}"); + for public_key in utils::get_public_keys(key_path)?.into_values() { + let tree_rev = { + let object_oid = repo + .revparse_single(&rev) + .context("Failed to look-up git object")? + .id(); + let key_fingerprint = public_key.fingerprint()?; + utils::craft_signature_reference(key_fingerprint, object_oid) + }; + repo.revparse_single(&tree_rev) + .with_context(|| format!("No signature found for {rev}"))?; + println!("{tree_rev}"); + } Ok(()) } diff --git a/src/sign.rs b/src/sign.rs index 66ffc13..d1c6175 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -11,21 +11,25 @@ use crate::utils; /// Execute the `sign` command. pub fn command(key_path: PathBuf, rev: String) -> Result<()> { let repo = utils::open_repository()?; - let secret_key = utils::get_secret_key(key_path)?; - let tree_oid = sign(&repo, &secret_key, &rev)?; - let signed_object = { - let tree_sig = utils::TreeSignature::load_oid(&repo, tree_oid)?; - tree_sig.dereference()? - }; - let key_fingerprint = secret_key.public_key()?.fingerprint()?; - let reference = utils::craft_signature_reference(key_fingerprint, signed_object); - repo.reference( - &reference, tree_oid, - // references to signatures will never change, so it is - // safe to force overwriting faulty references - true, "", - ) - .context("Failed to store reference to signature")?; - println!("Signature stored under: {reference}"); + for (path, secret_key) in utils::get_secret_keys(key_path)? { + let tree_oid = sign(&repo, &secret_key, &rev)?; + let signed_object = { + let tree_sig = utils::TreeSignature::load_oid(&repo, tree_oid)?; + tree_sig.dereference()? + }; + let key_fingerprint = secret_key.public_key()?.fingerprint()?; + let reference = utils::craft_signature_reference(key_fingerprint, signed_object); + repo.reference( + &reference, tree_oid, + // references to signatures will never change, so it is + // safe to force overwriting faulty references + true, "", + ) + .context("Failed to store reference to signature")?; + println!("Signed with key:"); + println!(" - {}", path.display()); + println!("Signature stored under:"); + println!(" - {reference}"); + } Ok(()) } diff --git a/src/utils.rs b/src/utils.rs index 23d972f..09b3f63 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,11 @@ //! Catch-all utilities module. +use std::collections::BTreeMap; use std::error; use std::fmt; +use std::fs; use std::io::Cursor; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context, Result}; use git2::{Blob, Object, ObjectType, Oid, Repository, RepositoryOpenFlags}; @@ -426,8 +428,47 @@ fn determine_key_format(key_data: &str) -> Result { } } +/// Read all keys under the given `path` with `read`. +fn read_key_entries(ext: &str, path: PathBuf, mut read: F) -> Result> +where + F: FnMut(&Path) -> Result, +{ + let mut keys = BTreeMap::new(); + + for maybe_ent in fs::read_dir(path) + .with_context(|| format!("Failed to query entries in {ext} key directory"))? + { + let ent = + maybe_ent.with_context(|| format!("Failed to read entry in {ext} key directory"))?; + let path = ent.path(); + + if matches!(path.extension().and_then(|p| p.to_str()), Some(e) if e == ext) { + let key = read(&path)?; + keys.insert(path, key); + } + } + + Ok(keys) +} + +/// Read public keys from the given path. If a directory is provided, +/// keys are read from files whose extension is `.pub`. +pub fn get_public_keys(path: PathBuf) -> Result> { + let meta = fs::metadata(&path).context("Failed to query public key path metadata")?; + + if meta.is_dir() { + read_key_entries("pub", path, get_public_key) + } else { + get_public_key(&path).map(|key| { + let mut map = BTreeMap::new(); + map.insert(path, key); + map + }) + } +} + /// Read a public key from the given path. -pub fn get_public_key(path: PathBuf) -> Result { +fn get_public_key(path: &Path) -> Result { let key_data = std::fs::read_to_string(path).context("Failed to read public key")?; Ok(match determine_key_format(&key_data)? { @@ -451,8 +492,24 @@ pub fn get_public_key(path: PathBuf) -> Result { }) } +/// Read secret keys from the given path. If a directory is provided, +/// keys are read from files whose extension is `.sec`. +pub fn get_secret_keys(path: PathBuf) -> Result> { + let meta = fs::metadata(&path).context("Failed to query secret key path metadata")?; + + if meta.is_dir() { + read_key_entries("sec", path, get_secret_key) + } else { + get_secret_key(&path).map(|key| { + let mut map = BTreeMap::new(); + map.insert(path, key); + map + }) + } +} + /// Read a secret key from the given path. -pub fn get_secret_key(path: PathBuf) -> Result { +fn get_secret_key(path: &Path) -> Result { let key_data = std::fs::read_to_string(path) .map(Zeroizing::new) .context("Failed to read secret key")?; diff --git a/src/verify.rs b/src/verify.rs index e8b8d70..022bfd4 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -11,16 +11,17 @@ use crate::utils; /// Execute the `verify` command. pub fn command(key_path: PathBuf, rev: String) -> Result<()> { let repo = utils::open_repository()?; - let public_key = utils::get_public_key(key_path)?; - let tree_rev = { - let object_oid = repo - .revparse_single(&rev) - .context("Failed to look-up git object")? - .id(); - let key_fingerprint = public_key.fingerprint()?; - utils::craft_signature_reference(key_fingerprint, object_oid) - }; - verify(&repo, &public_key, &tree_rev, false)?; - println!("Signature verified successfully"); + for (path, public_key) in utils::get_public_keys(key_path)? { + let tree_rev = { + let object_oid = repo + .revparse_single(&rev) + .context("Failed to look-up git object")? + .id(); + let key_fingerprint = public_key.fingerprint()?; + utils::craft_signature_reference(key_fingerprint, object_oid) + }; + verify(&repo, &public_key, &tree_rev, false)?; + println!("Signature verified successfully with {}", path.display()); + } Ok(()) }