···55use anyhow::{Context, Result};
66use clap::{Parser, Subcommand};
77use git2::{Oid, Repository};
88-use libsignify::{Codeable, PrivateKey};
88+use libsignify::{Codeable, PrivateKey, PublicKey, Signature};
99use zeroize::Zeroizing;
10101111#[derive(Debug)]
···24242525#[derive(Subcommand)]
2626enum Action {
2727- /// Sign an arbitrary git object
2727+ /// Sign an arbitrary object and return a tree with the signature
2828 Sign {
2929 /// The path to the base64 encoded secret key to sign with
3030 #[arg(short = 'k', long)]
···3333 /// The object id to sign
3434 git_object_id: String,
3535 },
3636+ /// Verify the signature contained in a tree object
3737+ Verify {
3838+ /// The path to the base64 encoded public key to verify with
3939+ #[arg(short = 'k', long)]
4040+ public_key: PathBuf,
4141+4242+ /// Print the id of the signed object to stdout
4343+ #[arg(short = 'p', long)]
4444+ print_signed_oid: bool,
4545+4646+ /// The git tree containing a signed object
4747+ git_tree_oid: String,
4848+ },
3649}
37503851fn main() -> Result<()> {
···4356 secret_key,
4457 git_object_id: oid,
4558 } => sign(secret_key, oid),
5959+ Action::Verify {
6060+ public_key,
6161+ print_signed_oid: recover,
6262+ git_tree_oid: oid,
6363+ } => verify(public_key, recover, oid),
4664 }
4765}
48666767+fn verify(key_path: PathBuf, recover: bool, oid: String) -> Result<()> {
6868+ let repo = Repository::open(".").context("Failed to open git repository")?;
6969+7070+ let oid = Oid::from_str(&oid).context("Failed to parse git tree oid")?;
7171+ let tree = repo
7272+ .find_tree(oid)
7373+ .context("Failed to look-up tree oid in the repository")?;
7474+7575+ let object = tree
7676+ .get_name("object")
7777+ .context("Failed to look-up signed object in the tree")?
7878+ .to_object(&repo)
7979+ .context("The signed object could not be retrieved")?;
8080+ let object = object
8181+ .as_blob()
8282+ .context("The signed object is not a blob")?;
8383+ let dereferenced_obj = object.content();
8484+8585+ let signature = {
8686+ let signature = tree
8787+ .get_name("signature")
8888+ .context("Failed to look-up signature in the tree")?
8989+ .to_object(&repo)
9090+ .context("The signature object could not be retrieved")?;
9191+ let signature = signature
9292+ .as_blob()
9393+ .context("The signature object is not a blob")?;
9494+ Signature::from_bytes(signature.content())
9595+ .map_err(Error::new)
9696+ .context("Failed to parse signature")?
9797+ };
9898+9999+ let public_key = get_public_key(key_path)?;
100100+101101+ public_key
102102+ .verify(dereferenced_obj, &signature)
103103+ .map_err(Error::new)
104104+ .context("Failed to verify signature")?;
105105+106106+ if recover {
107107+ let oid = Oid::from_bytes(dereferenced_obj).context("Failed to parse git object id")?;
108108+ println!("{oid}");
109109+ }
110110+111111+ Ok(())
112112+}
113113+49114fn sign(key_path: PathBuf, oid: String) -> Result<()> {
50115 let repo = Repository::open(".").context("Failed to open git repository")?;
51116···66131 let mut tree_builder = repo
67132 .treebuilder(None)
68133 .context("Failed to get a git tree object builder")?;
134134+135135+ // TODO: insert a tree entry containing the version of this program
6913670137 tree_builder
71138 .insert("object", object_blob, 0o100644)
···105172 }
106173107174 Ok(secret_key)
175175+}
176176+177177+fn get_public_key(path: PathBuf) -> Result<PublicKey> {
178178+ let key_data = std::fs::read_to_string(path).context("Failed to read public key")?;
179179+180180+ let (public_key, _) = PublicKey::from_base64(&key_data[..])
181181+ .map_err(Error::new)
182182+ .context("Failed to decode secret key")?;
183183+184184+ Ok(public_key)
108185}
109186110187impl<E: fmt::Display> fmt::Display for Error<E> {