Now let's take a silly one
0

Configure Feed

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

at main 4.0 kB View raw
1use k256::ecdsa::signature::{Signer as _, Verifier as _}; 2use k256::ecdsa::{Signature as K256Signature, SigningKey, VerifyingKey}; 3 4use crate::Entropy; 5 6pub const MAX_SCALAR_ATTEMPTS: usize = 64; 7 8#[derive(Debug, Clone, PartialEq, Eq)] 9pub struct Signature(Vec<u8>); 10 11impl Signature { 12 pub fn from_bytes(bytes: Vec<u8>) -> Self { 13 Self(bytes) 14 } 15 16 pub fn as_bytes(&self) -> &[u8] { 17 &self.0 18 } 19} 20 21#[derive(Debug, Clone, PartialEq, Eq)] 22pub struct PublicKeyBytes(Vec<u8>); 23 24impl PublicKeyBytes { 25 pub fn as_bytes(&self) -> &[u8] { 26 &self.0 27 } 28} 29 30#[derive(Debug, thiserror::Error)] 31pub enum SignerError { 32 #[error("invalid signing key bytes")] 33 InvalidKey, 34} 35 36#[derive(Debug, Clone, Copy, PartialEq, Eq)] 37pub enum SignatureScheme { 38 Secp256k1, 39 P256, 40} 41 42pub trait Signer: Send + Sync + 'static { 43 fn sign(&self, message: &[u8]) -> Signature; 44 fn public_key(&self) -> PublicKeyBytes; 45 fn scheme(&self) -> SignatureScheme; 46} 47 48pub struct K256Signer { 49 key: SigningKey, 50} 51 52impl K256Signer { 53 pub fn from_slice(bytes: &[u8]) -> Result<Self, SignerError> { 54 SigningKey::from_slice(bytes) 55 .map(|key| Self { key }) 56 .map_err(|_| SignerError::InvalidKey) 57 } 58 59 pub fn generate(entropy: &dyn Entropy) -> Self { 60 std::iter::repeat_with(|| { 61 let mut bytes = [0u8; 32]; 62 entropy.fill(&mut bytes); 63 SigningKey::from_slice(&bytes).ok() 64 }) 65 .take(MAX_SCALAR_ATTEMPTS) 66 .flatten() 67 .next() 68 .map(|key| Self { key }) 69 .unwrap_or_else(|| { 70 panic!( 71 "entropy failed to yield valid secp256k1 scalar in {MAX_SCALAR_ATTEMPTS} attempts" 72 ) 73 }) 74 } 75} 76 77impl Signer for K256Signer { 78 fn sign(&self, message: &[u8]) -> Signature { 79 let signature: K256Signature = self.key.sign(message); 80 Signature(signature.to_bytes().to_vec()) 81 } 82 83 fn public_key(&self) -> PublicKeyBytes { 84 let point = self.key.verifying_key().to_encoded_point(true); 85 PublicKeyBytes(point.as_bytes().to_vec()) 86 } 87 88 fn scheme(&self) -> SignatureScheme { 89 SignatureScheme::Secp256k1 90 } 91} 92 93pub fn verify(public_key: &PublicKeyBytes, message: &[u8], signature: &Signature) -> bool { 94 let Ok(verifying_key) = VerifyingKey::from_sec1_bytes(public_key.as_bytes()) else { 95 return false; 96 }; 97 let Ok(parsed) = K256Signature::from_slice(signature.as_bytes()) else { 98 return false; 99 }; 100 verifying_key.verify(message, &parsed).is_ok() 101} 102 103#[cfg(test)] 104mod tests { 105 use super::*; 106 use crate::SeededEntropy; 107 108 #[test] 109 fn sign_then_verify_roundtrips() { 110 let signer = K256Signer::generate(&SeededEntropy::new(1)); 111 let message = b"refs/heads/main update"; 112 let signature = signer.sign(message); 113 assert!(verify(&signer.public_key(), message, &signature)); 114 } 115 116 #[test] 117 fn verify_rejects_tampered_message() { 118 let signer = K256Signer::generate(&SeededEntropy::new(2)); 119 let signature = signer.sign(b"original"); 120 assert!(!verify(&signer.public_key(), b"tampered", &signature)); 121 } 122 123 #[test] 124 fn generate_is_deterministic_from_seed() { 125 let one = K256Signer::generate(&SeededEntropy::new(99)); 126 let two = K256Signer::generate(&SeededEntropy::new(99)); 127 assert_eq!(one.public_key(), two.public_key()); 128 } 129 130 struct BrokenEntropy; 131 132 impl crate::Entropy for BrokenEntropy { 133 fn next_u64(&self) -> u64 { 134 0 135 } 136 137 fn fill(&self, buffer: &mut [u8]) { 138 buffer.fill(0); 139 } 140 141 fn derive(&self, _label: u64) -> Box<dyn crate::Entropy> { 142 Box::new(BrokenEntropy) 143 } 144 } 145 146 #[test] 147 #[should_panic(expected = "entropy failed to yield valid secp256k1 scalar")] 148 fn broken_entropy_fails_stop_instead_of_spinning() { 149 let _ = K256Signer::generate(&BrokenEntropy); 150 } 151}