Harness the power of signify(1) to sign arbitrary git objects
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

impl verify

+79 -2
+79 -2
src/main.rs
··· 5 5 use anyhow::{Context, Result}; 6 6 use clap::{Parser, Subcommand}; 7 7 use git2::{Oid, Repository}; 8 - use libsignify::{Codeable, PrivateKey}; 8 + use libsignify::{Codeable, PrivateKey, PublicKey, Signature}; 9 9 use zeroize::Zeroizing; 10 10 11 11 #[derive(Debug)] ··· 24 24 25 25 #[derive(Subcommand)] 26 26 enum Action { 27 - /// Sign an arbitrary git object 27 + /// Sign an arbitrary object and return a tree with the signature 28 28 Sign { 29 29 /// The path to the base64 encoded secret key to sign with 30 30 #[arg(short = 'k', long)] ··· 33 33 /// The object id to sign 34 34 git_object_id: String, 35 35 }, 36 + /// Verify the signature contained in a tree object 37 + Verify { 38 + /// The path to the base64 encoded public key to verify with 39 + #[arg(short = 'k', long)] 40 + public_key: PathBuf, 41 + 42 + /// Print the id of the signed object to stdout 43 + #[arg(short = 'p', long)] 44 + print_signed_oid: bool, 45 + 46 + /// The git tree containing a signed object 47 + git_tree_oid: String, 48 + }, 36 49 } 37 50 38 51 fn main() -> Result<()> { ··· 43 56 secret_key, 44 57 git_object_id: oid, 45 58 } => sign(secret_key, oid), 59 + Action::Verify { 60 + public_key, 61 + print_signed_oid: recover, 62 + git_tree_oid: oid, 63 + } => verify(public_key, recover, oid), 46 64 } 47 65 } 48 66 67 + fn verify(key_path: PathBuf, recover: bool, oid: String) -> Result<()> { 68 + let repo = Repository::open(".").context("Failed to open git repository")?; 69 + 70 + let oid = Oid::from_str(&oid).context("Failed to parse git tree oid")?; 71 + let tree = repo 72 + .find_tree(oid) 73 + .context("Failed to look-up tree oid in the repository")?; 74 + 75 + let object = tree 76 + .get_name("object") 77 + .context("Failed to look-up signed object in the tree")? 78 + .to_object(&repo) 79 + .context("The signed object could not be retrieved")?; 80 + let object = object 81 + .as_blob() 82 + .context("The signed object is not a blob")?; 83 + let dereferenced_obj = object.content(); 84 + 85 + let signature = { 86 + let signature = tree 87 + .get_name("signature") 88 + .context("Failed to look-up signature in the tree")? 89 + .to_object(&repo) 90 + .context("The signature object could not be retrieved")?; 91 + let signature = signature 92 + .as_blob() 93 + .context("The signature object is not a blob")?; 94 + Signature::from_bytes(signature.content()) 95 + .map_err(Error::new) 96 + .context("Failed to parse signature")? 97 + }; 98 + 99 + let public_key = get_public_key(key_path)?; 100 + 101 + public_key 102 + .verify(dereferenced_obj, &signature) 103 + .map_err(Error::new) 104 + .context("Failed to verify signature")?; 105 + 106 + if recover { 107 + let oid = Oid::from_bytes(dereferenced_obj).context("Failed to parse git object id")?; 108 + println!("{oid}"); 109 + } 110 + 111 + Ok(()) 112 + } 113 + 49 114 fn sign(key_path: PathBuf, oid: String) -> Result<()> { 50 115 let repo = Repository::open(".").context("Failed to open git repository")?; 51 116 ··· 66 131 let mut tree_builder = repo 67 132 .treebuilder(None) 68 133 .context("Failed to get a git tree object builder")?; 134 + 135 + // TODO: insert a tree entry containing the version of this program 69 136 70 137 tree_builder 71 138 .insert("object", object_blob, 0o100644) ··· 105 172 } 106 173 107 174 Ok(secret_key) 175 + } 176 + 177 + fn get_public_key(path: PathBuf) -> Result<PublicKey> { 178 + let key_data = std::fs::read_to_string(path).context("Failed to read public key")?; 179 + 180 + let (public_key, _) = PublicKey::from_base64(&key_data[..]) 181 + .map_err(Error::new) 182 + .context("Failed to decode secret key")?; 183 + 184 + Ok(public_key) 108 185 } 109 186 110 187 impl<E: fmt::Display> fmt::Display for Error<E> {