This repository has no description
0

Configure Feed

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

core / knot2 / crates / knot-runtime / src / signer.rs
3.6 kB 141 lines
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 from_bytes(bytes: Vec<u8>) -> Self { 26 Self(bytes) 27 } 28 29 pub fn as_bytes(&self) -> &[u8] { 30 &self.0 31 } 32} 33 34#[derive(Debug, thiserror::Error)] 35pub enum SignerError { 36 #[error("invalid signing key bytes")] 37 InvalidKey, 38} 39 40#[derive(Debug, Clone, Copy, PartialEq, Eq)] 41pub enum SignatureScheme { 42 Secp256k1, 43 P256, 44} 45 46pub trait Signer: Send + Sync + 'static { 47 fn sign(&self, message: &[u8]) -> Signature; 48 fn public_key(&self) -> PublicKeyBytes; 49 fn scheme(&self) -> SignatureScheme; 50} 51 52pub struct K256Signer { 53 key: SigningKey, 54} 55 56impl K256Signer { 57 pub fn from_slice(bytes: &[u8]) -> Result<Self, SignerError> { 58 SigningKey::from_slice(bytes) 59 .map(|key| Self { key }) 60 .map_err(|_| SignerError::InvalidKey) 61 } 62 63 // peak dice rolling 64 pub fn generate(entropy: &dyn Entropy) -> Self { 65 std::iter::repeat_with(|| { 66 let mut bytes = [0u8; 32]; 67 entropy.fill(&mut bytes); 68 SigningKey::from_slice(&bytes).ok() 69 }) 70 .take(MAX_SCALAR_ATTEMPTS) 71 .flatten() 72 .next() 73 .map(|key| Self { key }) 74 .unwrap_or_else(|| { 75 panic!( 76 "entropy failed to yield valid secp256k1 scalar in {MAX_SCALAR_ATTEMPTS} attempts" 77 ) 78 }) 79 } 80} 81 82impl Signer for K256Signer { 83 fn sign(&self, message: &[u8]) -> Signature { 84 let signature: K256Signature = self.key.sign(message); 85 Signature(signature.to_bytes().to_vec()) 86 } 87 88 fn public_key(&self) -> PublicKeyBytes { 89 let point = self.key.verifying_key().to_encoded_point(true); 90 PublicKeyBytes(point.as_bytes().to_vec()) 91 } 92 93 fn scheme(&self) -> SignatureScheme { 94 SignatureScheme::Secp256k1 95 } 96} 97 98pub fn verify(public_key: &PublicKeyBytes, message: &[u8], signature: &Signature) -> bool { 99 let Ok(verifying_key) = VerifyingKey::from_sec1_bytes(public_key.as_bytes()) else { 100 return false; 101 }; 102 let Ok(parsed) = K256Signature::from_slice(signature.as_bytes()) else { 103 return false; 104 }; 105 verifying_key.verify(message, &parsed).is_ok() 106} 107 108#[cfg(test)] 109mod tests { 110 use super::*; 111 use crate::SeededEntropy; 112 113 #[test] 114 fn generate_is_deterministic_from_seed() { 115 let one = K256Signer::generate(&SeededEntropy::new(99)); 116 let two = K256Signer::generate(&SeededEntropy::new(99)); 117 assert_eq!(one.public_key(), two.public_key()); 118 } 119 120 struct BrokenEntropy; 121 122 impl crate::Entropy for BrokenEntropy { 123 fn next_u64(&self) -> u64 { 124 0 125 } 126 127 fn fill(&self, buffer: &mut [u8]) { 128 buffer.fill(0); 129 } 130 131 fn derive(&self, _label: u64) -> Box<dyn crate::Entropy> { 132 Box::new(BrokenEntropy) 133 } 134 } 135 136 #[test] 137 #[should_panic(expected = "entropy failed to yield valid secp256k1 scalar")] 138 fn broken_entropy_fails_stop_instead_of_spinning() { 139 let _ = K256Signer::generate(&BrokenEntropy); 140 } 141}