···2233use std::path::PathBuf;
4455-use anyhow::{Context, Result};
55+use anyhow::Result;
66use git2::{Oid, Repository};
77-use libsignify::{Codeable, PublicKey, Signature};
77+use libsignify::PublicKey;
8899use crate::utils;
1010···2626 tree_rev: &str,
2727 recover: bool,
2828) -> Result<Option<Oid>> {
2929- let oid = repo
3030- .revparse_single(tree_rev)
3131- .context("Failed to look-up git tree oid")?
3232- .id();
3333- let tree = repo
3434- .find_tree(oid)
3535- .context("No tree object found for the given revision")?;
3636-3737- let object = tree
3838- .get_name("object")
3939- .context("Failed to look-up signed object in the tree")?
4040- .to_object(repo)
4141- .context("The signed object could not be retrieved")?;
4242- let object = object
4343- .as_blob()
4444- .context("The signed object is not a blob")?;
4545- let dereferenced_obj = object.content();
4646-4747- let signature = {
4848- let signature = tree
4949- .get_name("signature")
5050- .context("Failed to look-up signature in the tree")?
5151- .to_object(repo)
5252- .context("The signature object could not be retrieved")?;
5353- let signature = signature
5454- .as_blob()
5555- .context("The signature object is not a blob")?;
5656- Signature::from_bytes(signature.content())
5757- .map_err(utils::Error::new)
5858- .context("Failed to parse signature")?
5959- };
6060-6161- public_key
6262- .verify(dereferenced_obj, &signature)
6363- .map_err(utils::Error::new)
6464- .context("Failed to verify signature")?;
6565-6666- recover
6767- .then(|| Oid::from_bytes(dereferenced_obj).context("Failed to parse git object id"))
6868- .transpose()
2929+ let tree_sig = utils::TreeSignature::load(repo, tree_rev)?;
3030+ tree_sig.verify(public_key)?;
3131+ recover.then(|| tree_sig.dereference()).transpose()
6932}
+76-3
src/utils.rs
···44use std::fmt;
55use std::path::PathBuf;
6677-use anyhow::{Context, Result};
88-use git2::{ObjectType, Oid, Repository, RepositoryOpenFlags};
99-use libsignify::{Codeable, PrivateKey, PublicKey};
77+use anyhow::{anyhow, Context, Result};
88+use git2::{Blob, ObjectType, Oid, Repository, RepositoryOpenFlags};
99+use libsignify::{Codeable, PrivateKey, PublicKey, Signature};
1010use zeroize::Zeroizing;
1111+1212+/// A signature stored in a git tree object.
1313+pub struct TreeSignature<'repo> {
1414+ /// Pointer to the object that was signed.
1515+ pub object_pointer: Blob<'repo>,
1616+ /// The signature over the git object.
1717+ pub signature: Signature,
1818+}
1919+2020+impl<'repo> TreeSignature<'repo> {
2121+ /// Load a [`TreeSignature`] at the given `tree_rev` from the
2222+ /// provided git repository.
2323+ #[inline]
2424+ pub fn load(repo: &'repo Repository, tree_rev: &str) -> Result<Self> {
2525+ let oid = repo
2626+ .revparse_single(tree_rev)
2727+ .context("Failed to look-up git tree oid")?
2828+ .id();
2929+ Self::load_oid(repo, oid)
3030+ }
3131+3232+ /// Like [`TreeSignature::load`], but uses a concrete revision pointing
3333+ /// to the tree signature.
3434+ pub fn load_oid(repo: &'repo Repository, oid: Oid) -> Result<Self> {
3535+ let tree = repo
3636+ .find_tree(oid)
3737+ .context("No tree object found for the given revision")?;
3838+3939+ let object = tree
4040+ .get_name("object")
4141+ .context("Failed to look-up signed object in the tree")?
4242+ .to_object(repo)
4343+ .context("The signed object could not be retrieved")?;
4444+ let object_pointer = match object.into_blob() {
4545+ Ok(ptr) => ptr,
4646+ Err(_) => return Err(anyhow!("The signed object is not a blob")),
4747+ };
4848+4949+ let signature = {
5050+ let signature = tree
5151+ .get_name("signature")
5252+ .context("Failed to look-up signature in the tree")?
5353+ .to_object(repo)
5454+ .context("The signature object could not be retrieved")?;
5555+ let signature = signature
5656+ .as_blob()
5757+ .context("The signature object is not a blob")?;
5858+ Signature::from_bytes(signature.content())
5959+ .map_err(Error::new)
6060+ .context("Failed to parse signature")?
6161+ };
6262+6363+ Ok(Self {
6464+ signature,
6565+ object_pointer,
6666+ })
6767+ }
6868+6969+ /// Verify the authenticity of this [`TreeSignature`].
7070+ pub fn verify(&self, public_key: &PublicKey) -> Result<()> {
7171+ let dereferenced_obj = self.object_pointer.content();
7272+ public_key
7373+ .verify(dereferenced_obj, &self.signature)
7474+ .map_err(Error::new)
7575+ .context("Failed to verify signature")
7676+ }
7777+7878+ /// Dereference the inner object pointer.
7979+ #[inline]
8080+ pub fn dereference(&self) -> Result<Oid> {
8181+ Oid::from_bytes(self.object_pointer.content()).context("Failed to parse git object id")
8282+ }
8383+}
11841285/// An error type.
1386#[derive(Debug)]