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.

sign commits

+138 -42
+7
Cargo.lock
··· 296 296 ] 297 297 298 298 [[package]] 299 + name = "either" 300 + version = "1.13.0" 301 + source = "registry+https://github.com/rust-lang/crates.io-index" 302 + checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 303 + 304 + [[package]] 299 305 name = "form_urlencoded" 300 306 version = "1.2.1" 301 307 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 333 339 dependencies = [ 334 340 "anyhow", 335 341 "clap", 342 + "either", 336 343 "git2", 337 344 "libsignify", 338 345 "minisign",
+1
Cargo.toml
··· 6 6 [dependencies] 7 7 anyhow = "1.0.95" 8 8 clap = { version = "4.5.23", features = ["derive", "env"] } 9 + either = "1.13.0" 9 10 git2 = "0.19.0" 10 11 minisign = "0.7.9" 11 12 rpassword = "7.3.1"
+36 -9
src/raw/sign.rs
··· 3 3 use std::path::PathBuf; 4 4 5 5 use anyhow::{Context, Result}; 6 + use either::*; 6 7 use git2::{ObjectType, Oid, Repository}; 7 8 8 9 use crate::utils; ··· 24 25 .context("Failed to look-up git object id")?; 25 26 26 27 let object_ptr = object.id(); 27 - let object_mode = match object 28 + let object_mode_or_commit = match object 28 29 .kind() 29 30 .context("Failed to determine object kind to sign")? 30 31 { 31 - ObjectType::Blob => 0o100644, 32 - ObjectType::Tree => 0o040000, 33 - ty @ (ObjectType::Any | ObjectType::Commit | ObjectType::Tag) => { 34 - anyhow::bail!("Unsupported object type {ty}"); 32 + ObjectType::Blob => Left(0o100644), 33 + ObjectType::Tree => Left(0o040000), 34 + ObjectType::Commit => Right(object.as_commit().expect("The object is a commit")), 35 + ty @ (ObjectType::Any | ObjectType::Tag) => { 36 + anyhow::bail!("Unsupported or recursive object type {ty}"); 35 37 } 36 38 }; 37 39 40 + let commit_author = repo 41 + .signature() 42 + .context("Failed to retrieve commit author")?; 43 + 38 44 let signature = secret_key.sign(object_ptr.as_bytes())?; 39 45 let signature_blob = repo 40 46 .blob(&signature) ··· 59 65 .insert("algorithm", algo_blob, 0o100644) 60 66 .context("Failed to write algorithm to the tree")?; 61 67 tree_builder 62 - .insert("object", object_ptr, object_mode) 63 - .context("Failed to write object to the tree")?; 64 - tree_builder 65 68 .insert("signature", signature_blob, 0o100644) 66 69 .context("Failed to write signature to the tree")?; 67 70 71 + let parents = object_mode_or_commit.either( 72 + |object_mode| { 73 + tree_builder 74 + .insert("object", object_ptr, object_mode) 75 + .context("Failed to write object to the tree")?; 76 + anyhow::Ok(vec![]) 77 + }, 78 + |commit| anyhow::Ok(vec![commit]), 79 + )?; 80 + 68 81 let tree_oid = tree_builder 69 82 .write() 70 83 .context("Failed to write tree to the object store")?; 84 + let tree = repo 85 + .find_tree(tree_oid) 86 + .context("Failed to look-up newly created git tree signature")?; 71 87 72 - Ok(tree_oid) 88 + let commit_oid = repo 89 + .commit( 90 + None, 91 + &commit_author, 92 + &commit_author, 93 + "git-signify signature", 94 + &tree, 95 + &parents, 96 + ) 97 + .context("Failed to create git signature commit")?; 98 + 99 + Ok(commit_oid) 73 100 }
+94 -33
src/utils.rs
··· 166 166 /// Like [`TreeSignature::load`], but uses a concrete revision pointing 167 167 /// to the tree signature. 168 168 pub fn load_oid(repo: &'repo Repository, oid: Oid) -> Result<Self> { 169 - let tree = repo 170 - .find_tree(oid) 171 - .context("No tree object found for the given revision")?; 172 - 173 - let (version, algorithm) = tree.get_name("version").map_or( 174 - Ok((TreeSignatureVersion::V0, TreeSignatureAlgo::Signify)), 175 - |version_tree_entry| { 176 - let version_obj = version_tree_entry 177 - .to_object(repo) 178 - .context("The tree signature version could not be retrieved")?; 179 - let version_blob = match version_obj.into_blob() { 180 - Ok(blob) => blob, 181 - Err(_) => { 182 - return Err(anyhow!("The tree signature version object is not a blob")) 183 - } 184 - }; 185 - let version = TreeSignatureVersion::from_blob(version_blob)?; 169 + let object = repo 170 + .find_object(oid, None) 171 + .context("No git object found for the given revision")?; 186 172 187 - let algorithm_obj = tree 188 - .get_name("algorithm") 189 - .context("Failed to look-up tree signature algorithm")? 190 - .to_object(repo) 191 - .context("The tree signature algorithm could not be retrieved")?; 192 - let algorithm_blob = match algorithm_obj.into_blob() { 193 - Ok(blob) => blob, 194 - Err(_) => { 195 - return Err(anyhow!("The tree signature algorithm object is not a blob")) 196 - } 197 - }; 198 - let algorithm = TreeSignatureAlgo::from_blob(algorithm_blob)?; 173 + match object.kind().context( 174 + "Failed to determine kind of git object, while determining version of the signature", 175 + )? { 176 + ObjectType::Tree => Self::load_oid_v0(repo, object), 177 + ObjectType::Commit => Self::load_oid_v1(repo, object), 178 + _ => anyhow::bail!( 179 + "Invalid object kind provided, while loading tree signature with oid={oid}" 180 + ), 181 + } 182 + } 199 183 200 - anyhow::Ok((version, algorithm)) 201 - }, 202 - )?; 184 + /// Load a v0 [`TreeSignature`]. 185 + pub fn load_oid_v0(repo: &'repo Repository, object: Object<'repo>) -> Result<Self> { 186 + let tree = object.as_tree().with_context(|| { 187 + format!( 188 + "No tree signature found for object with oid={}", 189 + object.id() 190 + ) 191 + })?; 203 192 204 193 let object_pointer = tree 205 194 .get_name("object") 206 195 .context("Failed to look-up signed object in the tree")? 207 196 .to_object(repo) 208 197 .context("The signed object could not be retrieved")?; 198 + let signature = { 199 + let signature = tree 200 + .get_name("signature") 201 + .context("Failed to look-up signature in the tree")? 202 + .to_object(repo) 203 + .context("The signature object could not be retrieved")?; 204 + signature 205 + .into_blob() 206 + .map_err(|_| anyhow!("The signature object in oid={} is not a blob", object.id()))? 207 + }; 208 + 209 + Ok(Self { 210 + signature, 211 + object_pointer, 212 + version: TreeSignatureVersion::V0, 213 + algorithm: TreeSignatureAlgo::Signify, 214 + }) 215 + } 216 + 217 + /// Load a v1 [`TreeSignature`]. 218 + pub fn load_oid_v1(repo: &'repo Repository, object: Object<'repo>) -> Result<Self> { 219 + let commit = object 220 + .as_commit() 221 + .context("Failed to retrieve v1 git commit with signature")?; 222 + let tree = commit 223 + .tree() 224 + .context("Failed to retrieve v1 git tree with signature")?; 225 + 226 + let version = { 227 + let version_obj = tree 228 + .get_name("version") 229 + .context("Failed to look-up tree signature version")? 230 + .to_object(repo) 231 + .context("The tree signature version could not be retrieved")?; 232 + let version_blob = match version_obj.into_blob() { 233 + Ok(blob) => blob, 234 + Err(_) => return Err(anyhow!("The tree signature version object is not a blob")), 235 + }; 236 + TreeSignatureVersion::from_blob(version_blob)? 237 + }; 238 + let algorithm = { 239 + let algorithm_obj = tree 240 + .get_name("algorithm") 241 + .context("Failed to look-up tree signature algorithm")? 242 + .to_object(repo) 243 + .context("The tree signature algorithm could not be retrieved")?; 244 + let algorithm_blob = match algorithm_obj.into_blob() { 245 + Ok(blob) => blob, 246 + Err(_) => return Err(anyhow!("The tree signature algorithm object is not a blob")), 247 + }; 248 + TreeSignatureAlgo::from_blob(algorithm_blob)? 249 + }; 250 + 251 + let object_pointer = tree.get_name("object").map_or_else( 252 + || { 253 + Ok(commit 254 + .parent(0) 255 + .context( 256 + "No signed `object` in the tree signature nor a parent commit \ 257 + to be signed could be found", 258 + )? 259 + .into_object()) 260 + }, 261 + |entry| { 262 + entry.to_object(repo).with_context(|| { 263 + format!( 264 + "The signed object with oid={} could not be cast to a git object", 265 + entry.id() 266 + ) 267 + }) 268 + }, 269 + )?; 209 270 210 271 let signature = { 211 272 let signature = tree ··· 215 276 .context("The signature object could not be retrieved")?; 216 277 signature 217 278 .into_blob() 218 - .map_err(|_| anyhow!("The signature object in {oid} is not a blob"))? 279 + .map_err(|_| anyhow!("The signature object in oid={} is not a blob", object.id()))? 219 280 }; 220 281 221 282 Ok(Self {