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> {
133 self.sign(digest)
134 }
135
136 fn public_key_bytes(&self) -> Vec<u8> {
137 self.public_key().bytes.to_vec()
138 }
139
140 fn public_key_bytes_uncompressed(&self) -> Vec<u8> {
141 self.public_key_bytes()
143 }
144}
145
146impl traits::KeyPair for BlsSigner {
147 fn generate() -> Result<Self, SignerError> {
148 use zeroize::Zeroize;
149 let mut ikm = [0u8; 32];
150 crate::security::secure_random(&mut ikm)?;
151 let secret_key = SecretKey::key_gen(&ikm, &[]).map_err(|_| SignerError::EntropyError)?;
152 ikm.zeroize(); Ok(Self { secret_key })
154 }
155
156 fn from_bytes(private_key: &[u8]) -> Result<Self, SignerError> {
157 if private_key.len() != 32 {
158 return Err(SignerError::InvalidPrivateKey(format!(
159 "expected 32 bytes, got {}",
160 private_key.len()
161 )));
162 }
163 let secret_key = SecretKey::from_bytes(private_key)
164 .map_err(|_| SignerError::InvalidPrivateKey("invalid BLS secret key".into()))?;
165 Ok(Self { secret_key })
166 }
167
168 fn private_key_bytes(&self) -> Zeroizing<Vec<u8>> {
169 Zeroizing::new(self.secret_key.to_bytes().to_vec())
170 }
171}
172
173pub struct BlsVerifier {
175 public_key: PublicKey,
176}
177
178impl BlsVerifier {
179 pub fn from_public_key_bytes(bytes: &[u8]) -> Result<Self, SignerError> {
181 if bytes.len() != 48 {
182 return Err(SignerError::InvalidPublicKey(format!(
183 "expected 48 bytes, got {}",
184 bytes.len()
185 )));
186 }
187 let public_key = PublicKey::from_bytes(bytes)
188 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))?;
189 Ok(Self { public_key })
190 }
191}
192
193impl traits::Verifier for BlsVerifier {
194 type Signature = BlsSignature;
195 type Error = SignerError;
196
197 fn verify(&self, message: &[u8], signature: &BlsSignature) -> Result<bool, SignerError> {
198 let sig = BlstSignature::from_bytes(&signature.bytes)
199 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
200 let result = sig.verify(true, message, ETH2_DST, &[], &self.public_key, true);
201 Ok(result == BLST_ERROR::BLST_SUCCESS)
202 }
203
204 fn verify_prehashed(
205 &self,
206 digest: &[u8],
207 signature: &BlsSignature,
208 ) -> Result<bool, SignerError> {
209 self.verify(digest, signature)
210 }
211}
212
213pub fn aggregate_signatures(signatures: &[BlsSignature]) -> Result<BlsSignature, SignerError> {
215 if signatures.is_empty() {
216 return Err(SignerError::AggregationError(
217 "no signatures to aggregate".into(),
218 ));
219 }
220
221 let blst_sigs: Vec<BlstSignature> = signatures
222 .iter()
223 .map(|s| {
224 BlstSignature::from_bytes(&s.bytes)
225 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))
226 })
227 .collect::<Result<Vec<_>, _>>()?;
228
229 let sig_refs: Vec<&BlstSignature> = blst_sigs.iter().collect();
230 let agg = AggregateSignature::aggregate(&sig_refs, true)
231 .map_err(|_| SignerError::AggregationError("aggregation failed".into()))?;
232
233 let compressed = agg.to_signature().compress();
234 let mut bytes = [0u8; 96];
235 bytes.copy_from_slice(&compressed);
236 Ok(BlsSignature { bytes })
237}
238
239pub fn verify_aggregated(
241 public_keys: &[BlsPublicKey],
242 message: &[u8],
243 agg_signature: &BlsSignature,
244) -> Result<bool, SignerError> {
245 if public_keys.is_empty() {
246 return Err(SignerError::AggregationError("no public keys".into()));
247 }
248
249 let pks: Vec<PublicKey> = public_keys
250 .iter()
251 .map(|pk| {
252 PublicKey::from_bytes(&pk.bytes)
253 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))
254 })
255 .collect::<Result<Vec<_>, _>>()?;
256
257 let pk_refs: Vec<&PublicKey> = pks.iter().collect();
258 let sig = BlstSignature::from_bytes(&agg_signature.bytes)
259 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
260
261 let msgs: Vec<&[u8]> = vec![message; pk_refs.len()];
262
263 let result = sig.aggregate_verify(true, &msgs, ETH2_DST, &pk_refs, true);
264 Ok(result == BLST_ERROR::BLST_SUCCESS)
265}
266
267pub fn verify_aggregated_multi(
275 pairs: &[(BlsPublicKey, &[u8])],
276 agg_signature: &BlsSignature,
277) -> Result<bool, SignerError> {
278 if pairs.is_empty() {
279 return Err(SignerError::AggregationError("no pairs to verify".into()));
280 }
281
282 let pks: Vec<PublicKey> = pairs
283 .iter()
284 .map(|(pk, _)| {
285 PublicKey::from_bytes(&pk.bytes)
286 .map_err(|_| SignerError::InvalidPublicKey("invalid BLS public key".into()))
287 })
288 .collect::<Result<Vec<_>, _>>()?;
289
290 let pk_refs: Vec<&PublicKey> = pks.iter().collect();
291 let msgs: Vec<&[u8]> = pairs.iter().map(|(_, m)| *m).collect();
292
293 let sig = BlstSignature::from_bytes(&agg_signature.bytes)
294 .map_err(|_| SignerError::InvalidSignature("invalid BLS signature".into()))?;
295
296 let result = sig.aggregate_verify(true, &msgs, ETH2_DST, &pk_refs, true);
297 Ok(result == BLST_ERROR::BLST_SUCCESS)
298}
299
300#[cfg(test)]
301#[allow(clippy::unwrap_used, clippy::expect_used)]
302mod tests {
303 use super::*;
304 use crate::traits::{KeyPair, Signer, Verifier};
305
306 #[test]
307 fn test_generate_keypair() {
308 let signer = BlsSigner::generate().unwrap();
309 assert_eq!(signer.public_key_bytes().len(), 48);
310 }
311
312 #[test]
313 fn test_from_bytes_roundtrip() {
314 let signer = BlsSigner::generate().unwrap();
315 let key_bytes = signer.private_key_bytes();
316 let restored = BlsSigner::from_bytes(&key_bytes).unwrap();
317 assert_eq!(signer.public_key_bytes(), restored.public_key_bytes());
318 }
319
320 #[test]
321 fn test_sign_verify_roundtrip() {
322 let signer = BlsSigner::generate().unwrap();
323 let sig = signer.sign(b"hello bls").unwrap();
324 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
325 assert!(verifier.verify(b"hello bls", &sig).unwrap());
326 }
327
328 #[test]
329 fn test_signature_96_bytes() {
330 let signer = BlsSigner::generate().unwrap();
331 let sig = signer.sign(b"test").unwrap();
332 assert_eq!(sig.bytes.len(), 96);
333 }
334
335 #[test]
336 fn test_aggregate_2_sigs() {
337 let msg = b"aggregate test";
338 let s1 = BlsSigner::generate().unwrap();
339 let s2 = BlsSigner::generate().unwrap();
340 let sig1 = s1.sign(msg).unwrap();
341 let sig2 = s2.sign(msg).unwrap();
342
343 let agg_sig = aggregate_signatures(&[sig1, sig2]).unwrap();
344 let result = verify_aggregated(&[s1.public_key(), s2.public_key()], msg, &agg_sig).unwrap();
345 assert!(result);
346 }
347
348 #[test]
349 fn test_aggregate_10_sigs() {
350 let msg = b"ten signers";
351 let signers: Vec<BlsSigner> = (0..10).map(|_| BlsSigner::generate().unwrap()).collect();
352 let sigs: Vec<BlsSignature> = signers.iter().map(|s| s.sign(msg).unwrap()).collect();
353 let pks: Vec<BlsPublicKey> = signers.iter().map(|s| s.public_key()).collect();
354
355 let agg_sig = aggregate_signatures(&sigs).unwrap();
356 assert!(verify_aggregated(&pks, msg, &agg_sig).unwrap());
357 }
358
359 #[test]
360 fn test_invalid_agg_fails() {
361 let msg = b"bad aggregate";
362 let s1 = BlsSigner::generate().unwrap();
363 let s2 = BlsSigner::generate().unwrap();
364 let sig1 = s1.sign(msg).unwrap();
365 let sig2 = s2.sign(b"different message").unwrap(); let agg_sig = aggregate_signatures(&[sig1, sig2]).unwrap();
368 let result = verify_aggregated(&[s1.public_key(), s2.public_key()], msg, &agg_sig).unwrap();
369 assert!(!result);
370 }
371
372 #[test]
373 fn test_dst_correctness() {
374 assert_eq!(ETH2_DST, b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_");
375 }
376
377 #[test]
378 fn test_known_vector_eth2() {
379 let sk_bytes =
381 hex::decode("263dbd792f5b1be47ed85f8938c0f29586af0d3ac7b977f21c278fe1462040e3")
382 .unwrap();
383 let signer = BlsSigner::from_bytes(&sk_bytes).unwrap();
384 let msg = hex::decode("5656565656565656565656565656565656565656565656565656565656565656")
385 .unwrap();
386 let sig = signer.sign(&msg).unwrap();
387 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
388 assert!(verifier.verify(&msg, &sig).unwrap());
389 }
390
391 #[test]
392 fn test_invalid_key_rejected() {
393 assert!(BlsSigner::from_bytes(&[0u8; 31]).is_err());
394 assert!(BlsSigner::from_bytes(&[0u8; 33]).is_err());
395 }
396
397 #[test]
398 fn test_tampered_sig_fails() {
399 let signer = BlsSigner::generate().unwrap();
400 let sig = signer.sign(b"tamper").unwrap();
401 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
402 let mut tampered = sig.clone();
403 tampered.bytes[0] ^= 0xff;
404 let result = verifier.verify(b"tamper", &tampered);
405 assert!(result.is_err() || !result.unwrap());
406 }
407
408 #[test]
409 fn test_sign_prehashed_roundtrip() {
410 let signer = BlsSigner::generate().unwrap();
411 let msg = b"prehash bls";
412 let sig = signer.sign_prehashed(msg).unwrap();
413 let verifier = BlsVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
414 assert!(verifier.verify(msg, &sig).unwrap());
415 }
416
417 #[test]
418 fn test_zeroize_on_drop() {
419 let signer = BlsSigner::generate().unwrap();
420 let _: Zeroizing<Vec<u8>> = signer.private_key_bytes();
421 drop(signer);
422 }
423
424 #[test]
425 fn test_multi_message_aggregation() {
426 let s1 = BlsSigner::generate().unwrap();
427 let s2 = BlsSigner::generate().unwrap();
428 let s3 = BlsSigner::generate().unwrap();
429
430 let msg1 = b"attestation slot 100";
431 let msg2 = b"attestation slot 101";
432 let msg3 = b"attestation slot 102";
433
434 let sig1 = s1.sign(msg1).unwrap();
435 let sig2 = s2.sign(msg2).unwrap();
436 let sig3 = s3.sign(msg3).unwrap();
437
438 let agg = aggregate_signatures(&[sig1, sig2, sig3]).unwrap();
439
440 let pairs: Vec<(BlsPublicKey, &[u8])> = vec![
441 (s1.public_key(), msg1.as_slice()),
442 (s2.public_key(), msg2.as_slice()),
443 (s3.public_key(), msg3.as_slice()),
444 ];
445 assert!(verify_aggregated_multi(&pairs, &agg).unwrap());
446 }
447
448 #[test]
449 fn test_multi_message_wrong_message_fails() {
450 let s1 = BlsSigner::generate().unwrap();
451 let s2 = BlsSigner::generate().unwrap();
452
453 let sig1 = s1.sign(b"correct 1").unwrap();
454 let sig2 = s2.sign(b"correct 2").unwrap();
455
456 let agg = aggregate_signatures(&[sig1, sig2]).unwrap();
457
458 let pairs: Vec<(BlsPublicKey, &[u8])> = vec![
459 (s1.public_key(), b"correct 1".as_slice()),
460 (s2.public_key(), b"WRONG MESSAGE".as_slice()), ];
462 assert!(!verify_aggregated_multi(&pairs, &agg).unwrap());
463 }
464}