Harness the power of signify(1) to sign arbitrary git objects
1//! Create signatures with [`libsignify`].
2
3use std::path::PathBuf;
4
5use anyhow::{Context, Result};
6use git2::{ObjectType, Oid, Repository};
7
8use crate::utils;
9
10/// Execute the `raw sign` command.
11pub fn command(key_path: PathBuf, rev: String) -> Result<()> {
12 let repo = utils::open_repository()?;
13 for secret_key in utils::get_secret_keys(key_path)?.into_values() {
14 let tree_oid = sign(&repo, &secret_key, &rev)?;
15 println!("{tree_oid}");
16 }
17 Ok(())
18}
19
20/// Sign the revision `rev` with the given secret key, write the results
21/// to `repo` and return the object id of the resulting signature tree.
22pub fn sign(repo: &Repository, secret_key: &utils::PrivateKey, rev: &str) -> Result<Oid> {
23 let object = repo
24 .revparse_single(rev)
25 .context("Failed to look-up git object id")?;
26
27 let object_ptr = object.id();
28 let object_mode = match object
29 .kind()
30 .context("Failed to determine object kind to sign")?
31 {
32 ObjectType::Blob => 0o100644,
33 ObjectType::Tree => 0o040000,
34 ObjectType::Commit | ObjectType::Tag => 0o160000,
35 ty @ ObjectType::Any => {
36 anyhow::bail!("Unsupported object type {ty}");
37 }
38 };
39
40 let commit_author = repo
41 .signature()
42 .context("Failed to retrieve commit author")?;
43
44 let signature = secret_key.sign(object_ptr.as_bytes())?;
45 let signature_blob = repo
46 .blob(&signature)
47 .context("Failed to write signature to the object store")?;
48
49 let version_blob = repo
50 .blob(utils::TreeSignatureVersion::current().as_str().as_bytes())
51 .context("Failed to write tree signature version to the object store")?;
52
53 let algo_blob = repo
54 .blob(secret_key.algorithm().as_str().as_bytes())
55 .context("Failed to write tree signature algorithm to the object store")?;
56
57 let mut tree_builder = repo
58 .treebuilder(None)
59 .context("Failed to get a git tree object builder")?;
60
61 tree_builder
62 .insert("version", version_blob, 0o100644)
63 .context("Failed to write version to the tree")?;
64 tree_builder
65 .insert("algorithm", algo_blob, 0o100644)
66 .context("Failed to write algorithm to the tree")?;
67 tree_builder
68 .insert("signature", signature_blob, 0o100644)
69 .context("Failed to write signature to the tree")?;
70 tree_builder
71 .insert("object", object_ptr, object_mode)
72 .context("Failed to write object to the tree")?;
73
74 let tree_oid = tree_builder
75 .write()
76 .context("Failed to write tree to the object store")?;
77 let tree = repo
78 .find_tree(tree_oid)
79 .context("Failed to look-up newly created git tree signature")?;
80
81 let commit_oid = repo
82 .commit(
83 None,
84 &commit_author,
85 &commit_author,
86 &format!("git-signify signature over {rev}"),
87 &tree,
88 &[],
89 )
90 .context("Failed to create git signature commit")?;
91
92 Ok(commit_oid)
93}