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 with ml-signify

+96 -6
+96 -6
src/utils.rs
··· 10 10 use anyhow::{anyhow, Context, Result}; 11 11 use git2::{Blob, ErrorCode, Object, ObjectType, Oid, Repository, RepositoryOpenFlags}; 12 12 use libsignify::Codeable; 13 + use ml_signify::codec::{codecs, Decode as _, Encode as _}; 13 14 use zeroize::Zeroizing; 14 15 15 16 /// Private key used to sign git objects. ··· 18 19 Signify(libsignify::PrivateKey), 19 20 /// Private key originating from [`minisign`]. 20 21 Minisign(minisign::SecretKey), 22 + /// Private key originating from [`ml_signify`]. 23 + MlSignify(Box<ml_signify::SigningKey>), 21 24 } 22 25 23 26 impl PrivateKey { ··· 29 32 minisign::PublicKey::from_secret_key(private_key) 30 33 .context("Failed to convert minisign private key to public key")?, 31 34 )), 35 + Self::MlSignify(private_key) => { 36 + Ok(PublicKey::MlSignify(Box::new(private_key.verifying_key()))) 37 + } 32 38 } 33 39 } 34 40 ··· 44 50 .context("Failed to sign git object with minisign private key")?; 45 51 Ok(String::from(signature_box).into_bytes()) 46 52 } 53 + Self::MlSignify(private_key) => { 54 + let signature = ml_signify::sign(private_key, &ml_signify::hash(msg.as_ref())) 55 + .context("Failed to sign git object with ml-signify private key")?; 56 + 57 + let key_id = ml_signify::id_from_verifying_key(&private_key.verifying_key()); 58 + 59 + Ok((key_id, &signature) 60 + .ml_signify_encode::<codecs::Signature>(Some( 61 + "signed with git-signify via ml-signify", 62 + )) 63 + .into_bytes()) 64 + } 47 65 } 48 66 } 49 67 ··· 52 70 match self { 53 71 Self::Signify(_) => TreeSignatureAlgo::Signify, 54 72 Self::Minisign(_) => TreeSignatureAlgo::Minisign, 73 + Self::MlSignify(_) => TreeSignatureAlgo::MlSignify, 55 74 } 56 75 } 57 76 } ··· 62 81 Signify(libsignify::PublicKey), 63 82 /// Public key originating from [`minisign`]. 64 83 Minisign(minisign::PublicKey), 84 + /// Public key originating from [`ml_signify`]. 85 + MlSignify(Box<ml_signify::VerifyingKey>), 65 86 } 66 87 67 88 impl PublicKey { ··· 72 93 .context("Failed to compute signify public key fingerprint"), 73 94 Self::Minisign(public_key) => hash_bytes(public_key.to_bytes()) 74 95 .context("Failed to compute minisign public key fingerprint"), 96 + Self::MlSignify(public_key) => hash_bytes(public_key.encode()) 97 + .context("Failed to compute ml-signify public key fingerprint"), 75 98 } 76 99 } 77 100 } ··· 84 107 V1, 85 108 /// Version 2 tree signatures. 86 109 V2, 110 + /// Version 3 tree signatures. 111 + V3, 87 112 } 88 113 89 114 impl TreeSignatureVersion { ··· 92 117 match blob.content() { 93 118 b"v1" => Ok(Self::V1), 94 119 b"v2" => Ok(Self::V2), 120 + b"v3" => Ok(Self::V3), 95 121 blob => Err(anyhow!( 96 122 "Invalid tree signature version {:?}", 97 123 String::from_utf8_lossy(blob) ··· 101 127 102 128 /// Return the current version. 103 129 pub const fn current() -> Self { 104 - TreeSignatureVersion::V2 130 + TreeSignatureVersion::V3 105 131 } 106 132 107 133 /// Encode the version as a string. ··· 110 136 Self::V0 => "v0", 111 137 Self::V1 => "v1", 112 138 Self::V2 => "v2", 139 + Self::V3 => "v3", 113 140 } 114 141 } 115 142 } ··· 120 147 Signify, 121 148 /// Minisign key. 122 149 Minisign, 150 + /// ML-Signify key. 151 + MlSignify, 123 152 } 124 153 125 154 impl TreeSignatureAlgo { ··· 128 157 match blob.content() { 129 158 b"signify" => Ok(Self::Signify), 130 159 b"minisign" => Ok(Self::Minisign), 160 + b"ml-signify" => Ok(Self::MlSignify), 131 161 blob => Err(anyhow!( 132 162 "Invalid tree signature algorithm {:?}", 133 163 String::from_utf8_lossy(blob) ··· 140 170 match self { 141 171 Self::Signify => "signify", 142 172 Self::Minisign => "minisign", 173 + Self::MlSignify => "ml-signify", 143 174 } 144 175 } 145 176 } ··· 287 318 to be signed could be found", 288 319 )? 289 320 .into_object()), 290 - TreeSignatureVersion::V2 => { 321 + TreeSignatureVersion::V2 | TreeSignatureVersion::V3 => { 291 322 anyhow::bail!("No signed `object` could be found in the tree signature"); 292 323 } 293 324 }, ··· 333 364 .map_err(Error::new) 334 365 .context("Failed to parse signify signature from git blob")? 335 366 } 336 - TreeSignatureVersion::V1 | TreeSignatureVersion::V2 => { 367 + TreeSignatureVersion::V1 368 + | TreeSignatureVersion::V2 369 + | TreeSignatureVersion::V3 => { 337 370 let signature_content = std::str::from_utf8(self.signature.content()) 338 371 .context("Found non-utf8 data in signify signature content")?; 339 372 ··· 357 390 TreeSignatureVersion::V0 => { 358 391 anyhow::bail!("minisign public keys not supported in v0"); 359 392 } 360 - TreeSignatureVersion::V1 | TreeSignatureVersion::V2 => { 393 + TreeSignatureVersion::V1 394 + | TreeSignatureVersion::V2 395 + | TreeSignatureVersion::V3 => { 361 396 let signature_content = std::str::from_utf8(self.signature.content()) 362 397 .context("Found non-utf8 data in minisign signature content")?; 363 398 ··· 378 413 ) 379 414 .context("Invalid minisign signature") 380 415 } 416 + PublicKey::MlSignify(public_key) => { 417 + let signature = match &self.version { 418 + TreeSignatureVersion::V0 419 + | TreeSignatureVersion::V1 420 + | TreeSignatureVersion::V2 => { 421 + anyhow::bail!("ml-signify public keys are only supported from v3 onwards"); 422 + } 423 + TreeSignatureVersion::V3 => { 424 + let signature_content = std::str::from_utf8(self.signature.content()) 425 + .context("Found non-utf8 data in ml-signify signature content")?; 426 + 427 + let (_, signature) = signature_content 428 + .ml_signify_decode::<codecs::Signature>() 429 + .context("Failed to parse ml-signify signature from git blob")?; 430 + 431 + signature 432 + } 433 + }; 434 + 435 + let dereferenced_obj = self.dereference()?; 436 + let message = ml_signify::hash(dereferenced_obj.as_bytes()); 437 + 438 + anyhow::ensure!( 439 + ml_signify::verify(public_key, &message, &signature), 440 + "Invalid ml-signify signature" 441 + ); 442 + 443 + Ok(()) 444 + } 381 445 } 382 446 } 383 447 ··· 389 453 | (TreeSignatureVersion::V1, TreeSignatureAlgo::Signify, PublicKey::Signify(_)) 390 454 | (TreeSignatureVersion::V1, TreeSignatureAlgo::Minisign, PublicKey::Minisign(_)) 391 455 | (TreeSignatureVersion::V2, TreeSignatureAlgo::Signify, PublicKey::Signify(_)) 392 - | (TreeSignatureVersion::V2, TreeSignatureAlgo::Minisign, PublicKey::Minisign(_)) => { 456 + | (TreeSignatureVersion::V2, TreeSignatureAlgo::Minisign, PublicKey::Minisign(_)) 457 + | (TreeSignatureVersion::V3, TreeSignatureAlgo::Signify, PublicKey::Signify(_)) 458 + | (TreeSignatureVersion::V3, TreeSignatureAlgo::Minisign, PublicKey::Minisign(_)) 459 + | (TreeSignatureVersion::V3, TreeSignatureAlgo::MlSignify, PublicKey::MlSignify(_)) => { 393 460 Ok(()) 394 461 } 395 462 _ => { ··· 413 480 let oid_bytes = blob.content(); 414 481 Oid::from_bytes(oid_bytes).context("Failed to parse git object id from raw bytes") 415 482 } 416 - TreeSignatureVersion::V1 | TreeSignatureVersion::V2 => Ok(self.object_pointer.id()), 483 + TreeSignatureVersion::V1 | TreeSignatureVersion::V2 | TreeSignatureVersion::V3 => { 484 + Ok(self.object_pointer.id()) 485 + } 417 486 } 418 487 } 419 488 } ··· 457 526 match rest { 458 527 s if s.starts_with("signify") => Ok(TreeSignatureAlgo::Signify), 459 528 s if s.starts_with("minisign") => Ok(TreeSignatureAlgo::Minisign), 529 + s if s.starts_with("ml-signify") => Ok(TreeSignatureAlgo::MlSignify), 460 530 _ => Err(anyhow!("Unknown key format")), 461 531 } 462 532 } ··· 522 592 .context("Failed to decode minisign public key")?, 523 593 ) 524 594 } 595 + TreeSignatureAlgo::MlSignify => { 596 + let (_, public_key) = key_data 597 + .ml_signify_decode::<codecs::VerifyingKey>() 598 + .context("Failed to decode ml-signify verifying key")?; 599 + 600 + PublicKey::MlSignify(Box::new(public_key)) 601 + } 525 602 }) 526 603 } 527 604 ··· 575 652 .into_secret_key(Some(passphrase)) 576 653 .context("Failed to decode minisign private key")?, 577 654 ) 655 + } 656 + TreeSignatureAlgo::MlSignify => { 657 + let sealed_key = key_data 658 + .ml_signify_decode::<codecs::SigningKey>() 659 + .context("Failed to decode ml-signify sealed secret key")?; 660 + 661 + let passphrase = prompt_key_passphrase(path)?; 662 + 663 + let private_key = 664 + ml_signify::seal::unseal_signing_key(&sealed_key, passphrase.as_bytes()) 665 + .context("failed to unseal signing key")?; 666 + 667 + PrivateKey::MlSignify(Box::new(private_key.signing_key().clone())) 578 668 } 579 669 }) 580 670 }