1pub mod eip2333;
7pub mod keystore;
8pub mod threshold;
9
10use crate::error::SignerError;
11use crate::traits;
12use blst::min_pk::{AggregateSignature, PublicKey, SecretKey, Signature as BlstSignature};
13use blst::BLST_ERROR;
14use zeroize::Zeroizing;
15
16pub const ETH2_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[must_use]
23pub struct BlsSignature {
24 #[cfg_attr(feature = "serde", serde(with = "crate::hex_bytes"))]
26 pub bytes: [u8; 96],
27}
28
29impl core::fmt::Display for BlsSignature {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 write!(f, "0x")?;
32 for byte in &self.bytes {
33 write!(f, "{byte:02x}")?;
34 }
35 Ok(())
36 }
37}
38
39impl BlsSignature {
40 pub fn to_bytes(&self) -> [u8; 96] {
42 self.bytes
43 }
44
45 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignerError> {
47 if bytes.len() != 96 {
48 return Err(SignerError::InvalidSignature(format!(
49 "expected 96 bytes, got {}",
50 bytes.len()
51 )));
52 }
53 let mut out = [0u8; 96];
54 out.copy_from_slice(bytes);
55 Ok(Self { bytes: out })
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub struct BlsPublicKey {
63 #[cfg_attr(feature = "serde", serde(with = "crate::hex_bytes"))]
65 pub bytes: [u8; 48],
66}
67
68impl BlsPublicKey {
69 pub fn to_bytes(&self) -> [u8; 48] {
71 self.bytes
72 }
73
74 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignerError> {
76 if bytes.len() != 48 {
77 return Err(SignerError::InvalidPublicKey(format!(
78 "expected 48 bytes, got {}",
79 bytes.len()
80 )));
81 }
82 let mut out = [0u8; 48];
83 out.copy_from_slice(bytes);
84 Ok(Self { bytes: out })
85 }
86}
87
88pub struct BlsSigner {
90 secret_key: SecretKey,
91}
92
93impl Drop for BlsSigner {
95 fn drop(&mut self) {
96 }
99}
100
101impl BlsSigner {
102 pub fn public_key(&self) -> BlsPublicKey {
104 let pk = self.secret_key.sk_to_pk();
105 let bytes = pk.compress();
106 let mut out = [0u8; 48];
107 out.copy_from_slice(&bytes);
108 BlsPublicKey { bytes: out }
109 }
110}
111
112impl traits::Signer for BlsSigner {
113 type Signature = BlsSignature;
114 type Error = SignerError;
115
116 fn sign(&self, message: &[u8]) -> Result<BlsSignature, SignerError> {
117 let sig = self.secret_key.sign(message, ETH2_DST, &[]);
118 let compressed = sig.compress();
119 let mut bytes = [0u8; 96];
120 bytes.copy_from_slice(&compressed);
121 Ok(BlsSignature { bytes })
122 }
123
124 fn sign_prehashed(&self, digest: &[u8]) -> Result<BlsSignature, SignerError> {
128 self.sign(digest)
130 }
131
132 fn public_key_bytes(&self) -> Vec<u8> {
133 self.public_key().bytes.to_vec()
134 }
135
136 fn public_key_bytes_uncompressed(&self) -> Vec<u8> {
137 self.public_key_bytes()
139 }
140}
141
142impl traits::KeyPair for BlsSigner {
143 fn generate() -> Result<Self, SignerError> {
144 use zeroize::Zeroize;
145 let mut ikm = [0u8; 32];
146 crate::security::secure_random(&mut ikm)?;
147 let secret_key = SecretKey::key_gen(&ikm, &[]).map_err(|_| SignerError::EntropyError)?;
148 ikm.zeroize(); Ok(Self { secret_key })
150 }
151
152 fn from_bytes(private_key: &[u8]) -> Result<Self, SignerError> {
153 if private_key.len() != 32 {
154 return Err(SignerError::InvalidPrivateKey(format!(
155 "expected 32 bytes, got {}",
156 private_key.len()
157 )));
158 }
159 let secret_key = SecretKey::from_bytes(private_key)
160 .map_err(|_| SignerError::InvalidPrivateKey("invalid BLS secret key".into()))?;
161 Ok(Self { secret_key })
162 }
163
164 fn private_key_bytes(&self) -> Zeroizing<Vec<u8>> {
165 Zeroizing::new(self.secret_key.to_bytes().to_vec())
166 }
167}
168
169pub struct BlsVerifier {
171 public_key: PublicKey,
172}
173
174impl BlsVerifier {
175 pub fn from_public_key_bytes(bytes: &[u8]) -> Result<Self, SignerError> {
177 if bytes.len() != 48 {
178 return Err(SignerError::InvalidPublicKey(format!(
179 "expected 48 bytes, got {}",
180 bytes.len()
181 )));
182 }
183 let public_key = PublicKey::from_bytes(bytes)
184 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))?;
185 Ok(Self { public_key })
186 }
187}
188
189impl traits::Verifier for BlsVerifier {
190 type Signature = BlsSignature;
191 type Error = SignerError;
192
193 fn verify(&self, message: &[u8], signature: &BlsSignature) -> Result<bool, SignerError> {
194 let sig = BlstSignature::from_bytes(&signature.bytes)
195 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
196 let result = sig.verify(true, message, ETH2_DST, &[], &self.public_key, true);
197 Ok(result == BLST_ERROR::BLST_SUCCESS)
198 }
199
200 fn verify_prehashed(
201 &self,
202 digest: &[u8],
203 signature: &BlsSignature,
204 ) -> Result<bool, SignerError> {
205 self.verify(digest, signature)
206 }
207}
208
209pub fn aggregate_signatures(signatures: &[BlsSignature]) -> Result<BlsSignature, SignerError> {
211 if signatures.is_empty() {
212 return Err(SignerError::AggregationError(
213 "no signatures to aggregate".into(),
214 ));
215 }
216
217 let blst_sigs: Vec<BlstSignature> = signatures
218 .iter()
219 .map(|s| {
220 BlstSignature::from_bytes(&s.bytes)
221 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))
222 })
223 .collect::<Result<Vec<_>, _>>()?;
224
225 let sig_refs: Vec<&BlstSignature> = blst_sigs.iter().collect();
226 let agg = AggregateSignature::aggregate(&sig_refs, true)
227 .map_err(|_| SignerError::AggregationError("aggregation failed".into()))?;
228
229 let compressed = agg.to_signature().compress();
230 let mut bytes = [0u8; 96];
231 bytes.copy_from_slice(&compressed);
232 Ok(BlsSignature { bytes })
233}
234
235pub fn verify_aggregated(
237 public_keys: &[BlsPublicKey],
238 message: &[u8],
239 agg_signature: &BlsSignature,
240) -> Result<bool, SignerError> {
241 if public_keys.is_empty() {
242 return Err(SignerError::AggregationError("no public keys".into()));
243 }
244
245 let pks: Vec<PublicKey> = public_keys
246 .iter()
247 .map(|pk| {
248 PublicKey::from_bytes(&pk.bytes)
249 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))
250 })
251 .collect::<Result<Vec<_>, _>>()?;
252
253 let pk_refs: Vec<&PublicKey> = pks.iter().collect();
254 let sig = BlstSignature::from_bytes(&agg_signature.bytes)
255 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
256
257 let msgs: Vec<&[u8]> = vec![message; pk_refs.len()];
258
259 let result = sig.aggregate_verify(true, &msgs, ETH2_DST, &pk_refs, true);
260 Ok(result == BLST_ERROR::BLST_SUCCESS)
261}
262
263pub fn verify_aggregated_multi(
271 pairs: &[(BlsPublicKey, &[u8])],
272 agg_signature: &BlsSignature,
273) -> Result<bool, SignerError> {
274 if pairs.is_empty() {
275 return Err(SignerError::AggregationError("no pairs to verify".into()));
276 }
277
278 let pks: Vec<PublicKey> = pairs
279 .iter()
280 .map(|(pk, _)| {
281 PublicKey::from_bytes(&pk.bytes)
282 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))
283 })
284 .collect::<Result<Vec<_>, _>>()?;
285
286 let pk_refs: Vec<&PublicKey> = pks.iter().collect();
287 let msgs: Vec<&[u8]> = pairs.iter().map(|(_, m)| *m).collect();
288
289 let sig = BlstSignature::from_bytes(&agg_signature.bytes)
290 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
291
292 let result = sig.aggregate_verify(true, &msgs, ETH2_DST, &pk_refs, true);
293 Ok(result == BLST_ERROR::BLST_SUCCESS)
294}
295
296#[cfg(test)]
297#[allow(clippy::unwrap_used, clippy::expect_used)]
298mod tests {
299 use super::*;
300 use crate::traits::{KeyPair, Signer, Verifier};
301
302 #[test]
303 fn test_generate_keypair() {
304 let signer = BlsSigner::generate().unwrap();
305 assert_eq!(signer.public_key_bytes().len(), 48);
306 }
307
308 #[test]
309 fn test_from_bytes_roundtrip() {
310 let signer = BlsSigner::generate().unwrap();
311 let key_bytes = signer.private_key_bytes();
312 let restored = BlsSigner::from_bytes(&key_bytes).unwrap();
313 assert_eq!(signer.public_key_bytes(), restored.public_key_bytes());
314 }
315
316 #[test]
317 fn test_sign_verify_roundtrip() {
318 let signer = BlsSigner::generate().unwrap();
319 let sig = signer.sign(b"hello bls").unwrap();
320 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
321 assert!(verifier.verify(b"hello bls", &sig).unwrap());
322 }
323
324 #[test]
325 fn test_signature_96_bytes() {
326 let signer = BlsSigner::generate().unwrap();
327 let sig = signer.sign(b"test").unwrap();
328 assert_eq!(sig.bytes.len(), 96);
329 }
330
331 #[test]
332 fn test_aggregate_2_sigs() {
333 let msg = b"aggregate test";
334 let s1 = BlsSigner::generate().unwrap();
335 let s2 = BlsSigner::generate().unwrap();
336 let sig1 = s1.sign(msg).unwrap();
337 let sig2 = s2.sign(msg).unwrap();
338
339 let agg_sig = aggregate_signatures(&[sig1, sig2]).unwrap();
340 let result = verify_aggregated(&[s1.public_key(), s2.public_key()], msg, &agg_sig).unwrap();
341 assert!(result);
342 }
343
344 #[test]
345 fn test_aggregate_10_sigs() {
346 let msg = b"ten signers";
347 let signers: Vec<BlsSigner> = (0..10).map(|_| BlsSigner::generate().unwrap()).collect();
348 let sigs: Vec<BlsSignature> = signers.iter().map(|s| s.sign(msg).unwrap()).collect();
349 let pks: Vec<BlsPublicKey> = signers.iter().map(|s| s.public_key()).collect();
350
351 let agg_sig = aggregate_signatures(&sigs).unwrap();
352 assert!(verify_aggregated(&pks, msg, &agg_sig).unwrap());
353 }
354
355 #[test]
356 fn test_invalid_agg_fails() {
357 let msg = b"bad aggregate";
358 let s1 = BlsSigner::generate().unwrap();
359 let s2 = BlsSigner::generate().unwrap();
360 let sig1 = s1.sign(msg).unwrap();
361 let sig2 = s2.sign(b"different message").unwrap(); let agg_sig = aggregate_signatures(&[sig1, sig2]).unwrap();
364 let result = verify_aggregated(&[s1.public_key(), s2.public_key()], msg, &agg_sig).unwrap();
365 assert!(!result);
366 }
367
368 #[test]
369 fn test_dst_correctness() {
370 assert_eq!(ETH2_DST, b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_");
371 }
372
373 #[test]
374 fn test_known_vector_eth2() {
375 let sk_bytes =
377 hex::decode("263dbd792f5b1be47ed85f8938c0f29586af0d3ac7b977f21c278fe1462040e3")
378 .unwrap();
379 let signer = BlsSigner::from_bytes(&sk_bytes).unwrap();
380 let msg = hex::decode("5656565656565656565656565656565656565656565656565656565656565656")
381 .unwrap();
382 let sig = signer.sign(&msg).unwrap();
383 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
384 assert!(verifier.verify(&msg, &sig).unwrap());
385 }
386
387 #[test]
388 fn test_invalid_key_rejected() {
389 assert!(BlsSigner::from_bytes(&[0u8; 31]).is_err());
390 assert!(BlsSigner::from_bytes(&[0u8; 33]).is_err());
391 }
392
393 #[test]
394 fn test_tampered_sig_fails() {
395 let signer = BlsSigner::generate().unwrap();
396 let sig = signer.sign(b"tamper").unwrap();
397 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
398 let mut tampered = sig.clone();
399 tampered.bytes[0] ^= 0xff;
400 let result = verifier.verify(b"tamper", &tampered);
401 assert!(result.is_err() || !result.unwrap());
402 }
403
404 #[test]
405 fn test_sign_prehashed_roundtrip() {
406 let signer = BlsSigner::generate().unwrap();
407 let msg = b"prehash bls";
408 let sig = signer.sign_prehashed(msg).unwrap();
409 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
410 assert!(verifier.verify(msg, &sig).unwrap());
411 }
412
413 #[test]
414 fn test_zeroize_on_drop() {
415 let signer = BlsSigner::generate().unwrap();
416 let _: Zeroizing<Vec<u8>> = signer.private_key_bytes();
417 drop(signer);
418 }
419
420 #[test]
421 fn test_multi_message_aggregation() {
422 let s1 = BlsSigner::generate().unwrap();
423 let s2 = BlsSigner::generate().unwrap();
424 let s3 = BlsSigner::generate().unwrap();
425
426 let msg1 = b"attestation slot 100";
427 let msg2 = b"attestation slot 101";
428 let msg3 = b"attestation slot 102";
429
430 let sig1 = s1.sign(msg1).unwrap();
431 let sig2 = s2.sign(msg2).unwrap();
432 let sig3 = s3.sign(msg3).unwrap();
433
434 let agg = aggregate_signatures(&[sig1, sig2, sig3]).unwrap();
435
436 let pairs: Vec<(BlsPublicKey, &[u8])> = vec![
437 (s1.public_key(), msg1.as_slice()),
438 (s2.public_key(), msg2.as_slice()),
439 (s3.public_key(), msg3.as_slice()),
440 ];
441 assert!(verify_aggregated_multi(&pairs, &agg).unwrap());
442 }
443
444 #[test]
445 fn test_multi_message_wrong_message_fails() {
446 let s1 = BlsSigner::generate().unwrap();
447 let s2 = BlsSigner::generate().unwrap();
448
449 let sig1 = s1.sign(b"correct 1").unwrap();
450 let sig2 = s2.sign(b"correct 2").unwrap();
451
452 let agg = aggregate_signatures(&[sig1, sig2]).unwrap();
453
454 let pairs: Vec<(BlsPublicKey, &[u8])> = vec![
455 (s1.public_key(), b"correct 1".as_slice()),
456 (s2.public_key(), b"WRONG MESSAGE".as_slice()), ];
458 assert!(!verify_aggregated_multi(&pairs, &agg).unwrap());
459 }
460}