1#![forbid(unsafe_code)]
36
37use alloc::vec::Vec;
38use core::fmt;
39
40use dcrypt_algorithms::ec::bls12_381::{
41 pairing, Bls12_381Scalar, G1Affine, G1Projective, G2Affine, G2Projective, Gt,
42};
43use dcrypt_algorithms::hash::{sha2::Sha256, HashFunction};
44use dcrypt_algorithms::kdf::Hkdf;
45use dcrypt_api::{Error as ApiError, Result as ApiResult};
46use dcrypt_internal::{
47 random::try_fill_bytes_zeroing_on_error, CryptoRng, RngCore, Zeroize, ZeroizeOnDrop, Zeroizing,
48};
49
50pub const BLS_SECRET_KEY_SIZE: usize = 32;
52pub const BLS_PUBLIC_KEY_SIZE: usize = 48;
54pub const BLS_SIGNATURE_SIZE: usize = 96;
56
57pub const BLS_BASIC_G2_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
59pub const BLS_AUG_G2_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_";
61pub const BLS_POP_G2_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
63pub const BLS_POP_PROOF_G2_DST: &[u8] = b"BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
65
66const KEYGEN_V4_SALT_TAG: &[u8] = b"BLS-SIG-KEYGEN-SALT-";
67const KEYGEN_L: usize = 48;
68
69fn invalid_key(context: &'static str, _message: &'static str) -> ApiError {
70 ApiError::InvalidKey {
71 context,
72 #[cfg(feature = "std")]
73 message: _message.into(),
74 }
75}
76
77fn invalid_signature(context: &'static str, _message: &'static str) -> ApiError {
78 ApiError::InvalidSignature {
79 context,
80 #[cfg(feature = "std")]
81 message: _message.into(),
82 }
83}
84
85fn invalid_parameter(context: &'static str, _message: &'static str) -> ApiError {
86 ApiError::InvalidParameter {
87 context,
88 #[cfg(feature = "std")]
89 message: _message.into(),
90 }
91}
92
93pub struct Bls12381SecretKey {
99 bytes: Zeroizing<[u8; BLS_SECRET_KEY_SIZE]>,
100}
101
102impl fmt::Debug for Bls12381SecretKey {
103 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104 formatter.write_str("Bls12381SecretKey([REDACTED])")
105 }
106}
107
108impl Zeroize for Bls12381SecretKey {
109 fn zeroize(&mut self) {
110 self.bytes.zeroize();
111 }
112}
113
114impl Drop for Bls12381SecretKey {
115 fn drop(&mut self) {
116 self.zeroize();
117 }
118}
119
120impl ZeroizeOnDrop for Bls12381SecretKey {}
121
122impl Bls12381SecretKey {
123 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
125 if bytes.len() != BLS_SECRET_KEY_SIZE {
126 return Err(ApiError::InvalidLength {
127 context: "Bls12381SecretKey::from_bytes",
128 expected: BLS_SECRET_KEY_SIZE,
129 actual: bytes.len(),
130 });
131 }
132
133 let mut protected = Zeroizing::new([0u8; BLS_SECRET_KEY_SIZE]);
134 protected.copy_from_slice(bytes);
135 if !bool::from(Bls12_381Scalar::secret_key_bytes_are_valid(&protected)) {
136 return Err(invalid_key(
137 "Bls12381SecretKey::from_bytes",
138 "secret scalar must be canonical and nonzero",
139 ));
140 }
141 Ok(Self { bytes: protected })
142 }
143
144 pub fn key_gen(ikm: &[u8], salt: &[u8]) -> ApiResult<Self> {
150 Self::key_gen_with_info(ikm, salt, &[])
151 }
152
153 pub fn key_gen_with_info(ikm: &[u8], salt: &[u8], key_info: &[u8]) -> ApiResult<Self> {
155 Self::key_gen_inner(ikm, salt, key_info, |current_salt, protected_ikm, info| {
156 Hkdf::<Sha256>::derive(Some(current_salt), protected_ikm, Some(info), KEYGEN_L)
157 .map_err(ApiError::from)
158 })
159 }
160
161 pub fn generate<R: CryptoRng + RngCore>(rng: &mut R, salt: &[u8]) -> ApiResult<Self> {
164 Self::generate_with_info(rng, salt, &[])
165 }
166
167 pub fn generate_with_info<R: CryptoRng + RngCore>(
169 rng: &mut R,
170 salt: &[u8],
171 key_info: &[u8],
172 ) -> ApiResult<Self> {
173 let mut ikm = Zeroizing::new([0u8; 32]);
174 try_fill_bytes_zeroing_on_error(rng, &mut *ikm).map_err(|_| {
175 ApiError::RandomGenerationError {
176 context: "Bls12381SecretKey::generate",
177 #[cfg(feature = "std")]
178 message: "caller-provided randomness source failed".into(),
179 }
180 })?;
181 Self::key_gen_with_info(&*ikm, salt, key_info)
182 }
183
184 fn key_gen_inner<F>(ikm: &[u8], salt: &[u8], key_info: &[u8], mut derive: F) -> ApiResult<Self>
185 where
186 F: FnMut(&[u8], &[u8], &[u8]) -> ApiResult<dcrypt_internal::zeroing::ZeroizingBytes>,
187 {
188 if ikm.len() < 32 {
189 return Err(ApiError::InvalidLength {
190 context: "Bls12381SecretKey::key_gen IKM",
191 expected: 32,
192 actual: ikm.len(),
193 });
194 }
195
196 let ikm_len = ikm.len().checked_add(1).ok_or_else(|| {
197 invalid_parameter("Bls12381SecretKey::key_gen", "IKM length overflow")
198 })?;
199 let mut protected_ikm =
200 Zeroizing::new(dcrypt_internal::zeroing::boxed_bytes_zeroed(ikm_len));
201 protected_ikm[..ikm.len()].copy_from_slice(ikm);
202
203 let info_len = key_info.len().checked_add(2).ok_or_else(|| {
204 invalid_parameter("Bls12381SecretKey::key_gen", "key_info length overflow")
205 })?;
206 let mut info = Vec::with_capacity(info_len);
207 info.extend_from_slice(key_info);
208 info.extend_from_slice(&(KEYGEN_L as u16).to_be_bytes());
209
210 let mut current_salt = salt.to_vec();
211 loop {
212 let okm = derive(¤t_salt, &protected_ikm, &info)?;
213 if okm.len() != KEYGEN_L {
214 return Err(invalid_parameter(
215 "Bls12381SecretKey::key_gen",
216 "HKDF returned an unexpected output length",
217 ));
218 }
219
220 let mut scalar =
221 Bls12_381Scalar::from_be_bytes_mod_order(&okm).map_err(ApiError::from)?;
222 if !bool::from(scalar.is_zero()) {
223 let bytes = scalar.to_be_bytes_zeroizing();
224 scalar.zeroize();
225 return Ok(Self { bytes });
226 }
227 scalar.zeroize();
228
229 let mut digest = Sha256::digest(¤t_salt).map_err(ApiError::from)?;
230 current_salt.clear();
231 current_salt.extend_from_slice(digest.as_ref());
232 digest.zeroize();
233 }
234 }
235
236 pub fn public_key(&self) -> ApiResult<Bls12381PublicKey> {
238 let point = G1Projective::generator()
239 .multiply_secret_be_bytes(&self.bytes)
240 .map_err(ApiError::from)?;
241 Bls12381PublicKey::from_point(point)
242 }
243
244 pub fn to_bytes_zeroizing(&self) -> Zeroizing<[u8; BLS_SECRET_KEY_SIZE]> {
246 self.bytes.clone()
247 }
248
249 fn bytes(&self) -> &[u8; BLS_SECRET_KEY_SIZE] {
250 &self.bytes
251 }
252}
253
254#[derive(Clone, Eq, PartialEq)]
256pub struct Bls12381PublicKey {
257 bytes: [u8; BLS_PUBLIC_KEY_SIZE],
258}
259
260impl fmt::Debug for Bls12381PublicKey {
261 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
262 formatter
263 .debug_struct("Bls12381PublicKey")
264 .field("length", &BLS_PUBLIC_KEY_SIZE)
265 .finish()
266 }
267}
268
269impl Bls12381PublicKey {
270 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
272 if bytes.len() != BLS_PUBLIC_KEY_SIZE {
273 return Err(ApiError::InvalidLength {
274 context: "Bls12381PublicKey::from_bytes",
275 expected: BLS_PUBLIC_KEY_SIZE,
276 actual: bytes.len(),
277 });
278 }
279 G1Projective::from_bytes_validated(bytes).map_err(|_| {
280 invalid_key(
281 "Bls12381PublicKey::from_bytes",
282 "invalid encoding, identity, or point outside the subgroup",
283 )
284 })?;
285 let encoded: [u8; BLS_PUBLIC_KEY_SIZE] = bytes.try_into().map_err(|_| {
286 invalid_key(
287 "Bls12381PublicKey::from_bytes",
288 "validated public-key length changed unexpectedly",
289 )
290 })?;
291 Ok(Self { bytes: encoded })
292 }
293
294 pub fn to_bytes(&self) -> [u8; BLS_PUBLIC_KEY_SIZE] {
296 self.bytes
297 }
298
299 pub fn key_validate(bytes: &[u8]) -> bool {
301 Self::from_bytes(bytes).is_ok()
302 }
303
304 pub fn aggregate(public_keys: &[Self]) -> ApiResult<Self> {
306 let first = public_keys.first().ok_or_else(|| {
307 invalid_parameter(
308 "Bls12381PublicKey::aggregate",
309 "at least one public key is required",
310 )
311 })?;
312 let mut aggregate = first.point()?;
313 for public_key in &public_keys[1..] {
314 aggregate += public_key.point()?;
315 }
316 Self::from_point(aggregate)
317 }
318
319 fn point(&self) -> ApiResult<G1Projective> {
320 G1Projective::from_bytes_validated(&self.bytes).map_err(|_| {
321 invalid_key(
322 "Bls12381PublicKey",
323 "stored public-key invariant was violated",
324 )
325 })
326 }
327
328 fn from_point(point: G1Projective) -> ApiResult<Self> {
329 if bool::from(point.is_identity()) {
330 return Err(invalid_key(
331 "Bls12381PublicKey",
332 "aggregate public key is the identity",
333 ));
334 }
335 Ok(Self {
336 bytes: point.to_bytes(),
337 })
338 }
339}
340
341#[derive(Clone, Eq, PartialEq)]
348pub struct Bls12381Signature {
349 bytes: [u8; BLS_SIGNATURE_SIZE],
350}
351
352impl fmt::Debug for Bls12381Signature {
353 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
354 formatter
355 .debug_struct("Bls12381Signature")
356 .field("length", &BLS_SIGNATURE_SIZE)
357 .finish()
358 }
359}
360
361impl Bls12381Signature {
362 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
364 let encoded: [u8; BLS_SIGNATURE_SIZE] =
365 bytes.try_into().map_err(|_| ApiError::InvalidLength {
366 context: "Bls12381Signature::from_bytes",
367 expected: BLS_SIGNATURE_SIZE,
368 actual: bytes.len(),
369 })?;
370 G2Projective::from_bytes(&encoded)
371 .into_option()
372 .ok_or_else(|| {
373 invalid_signature(
374 "Bls12381Signature::from_bytes",
375 "invalid encoding or point outside the subgroup",
376 )
377 })?;
378 Ok(Self { bytes: encoded })
379 }
380
381 pub fn to_bytes(&self) -> [u8; BLS_SIGNATURE_SIZE] {
383 self.bytes
384 }
385
386 pub fn is_identity(&self) -> bool {
388 self.point()
389 .map(|point| bool::from(point.is_identity()))
390 .unwrap_or(false)
391 }
392
393 pub fn aggregate(signatures: &[Self]) -> ApiResult<Self> {
395 let first = signatures.first().ok_or_else(|| {
396 invalid_parameter(
397 "Bls12381Signature::aggregate",
398 "at least one signature is required",
399 )
400 })?;
401 let mut aggregate = first.point()?;
402 for signature in &signatures[1..] {
403 aggregate += signature.point()?;
404 }
405 Ok(Self::from_point(aggregate))
406 }
407
408 fn point(&self) -> ApiResult<G2Projective> {
409 G2Projective::from_bytes(&self.bytes)
410 .into_option()
411 .ok_or_else(|| {
412 invalid_signature(
413 "Bls12381Signature",
414 "stored signature invariant was violated",
415 )
416 })
417 }
418
419 fn from_point(point: G2Projective) -> Self {
420 Self {
421 bytes: point.to_bytes(),
422 }
423 }
424}
425
426#[derive(Clone, Eq, PartialEq)]
428pub struct Bls12381ProofOfPossession {
429 bytes: [u8; BLS_SIGNATURE_SIZE],
430}
431
432impl fmt::Debug for Bls12381ProofOfPossession {
433 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
434 formatter
435 .debug_struct("Bls12381ProofOfPossession")
436 .field("length", &BLS_SIGNATURE_SIZE)
437 .finish()
438 }
439}
440
441impl Bls12381ProofOfPossession {
442 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
444 let signature = Bls12381Signature::from_bytes(bytes)?;
445 Ok(Self {
446 bytes: signature.bytes,
447 })
448 }
449
450 pub fn to_bytes(&self) -> [u8; BLS_SIGNATURE_SIZE] {
452 self.bytes
453 }
454
455 fn signature(&self) -> Bls12381Signature {
456 Bls12381Signature { bytes: self.bytes }
457 }
458}
459
460#[derive(Clone, Copy)]
461enum MessageMode {
462 Raw,
463 Augmented,
464}
465
466fn message_equal(
467 mode: MessageMode,
468 public_keys: &[Bls12381PublicKey],
469 messages: &[&[u8]],
470 left: usize,
471 right: usize,
472) -> bool {
473 match mode {
474 MessageMode::Raw => messages[left] == messages[right],
475 MessageMode::Augmented => {
476 public_keys[left] == public_keys[right] && messages[left] == messages[right]
477 }
478 }
479}
480
481fn hash_message(
482 mode: MessageMode,
483 public_key: &Bls12381PublicKey,
484 message: &[u8],
485 dst: &[u8],
486) -> ApiResult<G2Projective> {
487 match mode {
488 MessageMode::Raw => G2Projective::hash_to_curve(message, dst).map_err(ApiError::from),
489 MessageMode::Augmented => {
490 let length = BLS_PUBLIC_KEY_SIZE
491 .checked_add(message.len())
492 .ok_or_else(|| {
493 invalid_parameter("BLS message augmentation", "message length overflow")
494 })?;
495 let mut augmented = Vec::with_capacity(length);
496 augmented.extend_from_slice(&public_key.bytes);
497 augmented.extend_from_slice(message);
498 G2Projective::hash_to_curve(&augmented, dst).map_err(ApiError::from)
499 }
500 }
501}
502
503fn core_sign(
504 secret_key: &Bls12381SecretKey,
505 public_key: &Bls12381PublicKey,
506 message: &[u8],
507 dst: &[u8],
508 mode: MessageMode,
509) -> ApiResult<Bls12381Signature> {
510 let message_point = hash_message(mode, public_key, message, dst)?;
511 let signature = message_point
512 .multiply_secret_be_bytes(secret_key.bytes())
513 .map_err(ApiError::from)?;
514 Ok(Bls12381Signature::from_point(signature))
515}
516
517fn core_verify(
518 public_key: &Bls12381PublicKey,
519 message: &[u8],
520 signature: &Bls12381Signature,
521 dst: &[u8],
522 mode: MessageMode,
523) -> ApiResult<()> {
524 let public_key_point = G1Affine::from(public_key.point()?);
525 let signature_point = G2Affine::from(signature.point()?);
526 let message_point = G2Affine::from(hash_message(mode, public_key, message, dst)?);
527
528 if pairing(&public_key_point, &message_point)
529 == pairing(&G1Affine::generator(), &signature_point)
530 {
531 Ok(())
532 } else {
533 Err(invalid_signature(
534 "BLS verification",
535 "pairing equation failed",
536 ))
537 }
538}
539
540fn validate_parallel_inputs(
541 context: &'static str,
542 public_keys: &[Bls12381PublicKey],
543 messages: &[&[u8]],
544) -> ApiResult<()> {
545 if public_keys.is_empty() {
546 return Err(invalid_parameter(
547 context,
548 "at least one public key and message is required",
549 ));
550 }
551 if public_keys.len() != messages.len() {
552 return Err(invalid_parameter(
553 context,
554 "public-key and message counts differ",
555 ));
556 }
557 Ok(())
558}
559
560fn core_aggregate_verify(
561 public_keys: &[Bls12381PublicKey],
562 messages: &[&[u8]],
563 signature: &Bls12381Signature,
564 dst: &[u8],
565 mode: MessageMode,
566) -> ApiResult<()> {
567 validate_parallel_inputs("BLS aggregate verification", public_keys, messages)?;
568
569 let mut product = Gt::identity();
570 for index in 0..public_keys.len() {
571 if (0..index).any(|previous| message_equal(mode, public_keys, messages, previous, index)) {
572 continue;
573 }
574
575 let mut aggregate_public_key = public_keys[index].point()?;
576 let mut group_len = 1usize;
577 for next in (index + 1)..public_keys.len() {
578 if message_equal(mode, public_keys, messages, index, next) {
579 aggregate_public_key += public_keys[next].point()?;
580 group_len += 1;
581 }
582 }
583
584 if group_len > 1 && bool::from(aggregate_public_key.is_identity()) {
587 return Err(invalid_key(
588 "BLS aggregate verification",
589 "same-message aggregate public key is the identity",
590 ));
591 }
592
593 let message_point = G2Affine::from(hash_message(
594 mode,
595 &public_keys[index],
596 messages[index],
597 dst,
598 )?);
599 product += pairing(&G1Affine::from(aggregate_public_key), &message_point);
600 }
601
602 let signature_point = G2Affine::from(signature.point()?);
603 let expected = pairing(&G1Affine::generator(), &signature_point);
604 if product == expected {
605 Ok(())
606 } else {
607 Err(invalid_signature(
608 "BLS aggregate verification",
609 "aggregate pairing equation failed",
610 ))
611 }
612}
613
614pub struct Bls12381G2Basic;
616
617impl Bls12381G2Basic {
618 pub fn sign(secret_key: &Bls12381SecretKey, message: &[u8]) -> ApiResult<Bls12381Signature> {
620 let public_key = secret_key.public_key()?;
621 core_sign(
622 secret_key,
623 &public_key,
624 message,
625 BLS_BASIC_G2_DST,
626 MessageMode::Raw,
627 )
628 }
629
630 pub fn verify(
632 public_key: &Bls12381PublicKey,
633 message: &[u8],
634 signature: &Bls12381Signature,
635 ) -> ApiResult<()> {
636 core_verify(
637 public_key,
638 message,
639 signature,
640 BLS_BASIC_G2_DST,
641 MessageMode::Raw,
642 )
643 }
644
645 pub fn aggregate(signatures: &[Bls12381Signature]) -> ApiResult<Bls12381Signature> {
647 Bls12381Signature::aggregate(signatures)
648 }
649
650 pub fn aggregate_verify(
652 public_keys: &[Bls12381PublicKey],
653 messages: &[&[u8]],
654 signature: &Bls12381Signature,
655 ) -> ApiResult<()> {
656 validate_parallel_inputs("BLS Basic aggregate verification", public_keys, messages)?;
657 for left in 0..messages.len() {
658 for right in (left + 1)..messages.len() {
659 if messages[left] == messages[right] {
660 return Err(invalid_parameter(
661 "BLS Basic aggregate verification",
662 "Basic scheme messages must be distinct",
663 ));
664 }
665 }
666 }
667 core_aggregate_verify(
668 public_keys,
669 messages,
670 signature,
671 BLS_BASIC_G2_DST,
672 MessageMode::Raw,
673 )
674 }
675}
676
677pub struct Bls12381G2MessageAugmentation;
679
680impl Bls12381G2MessageAugmentation {
681 pub fn sign(secret_key: &Bls12381SecretKey, message: &[u8]) -> ApiResult<Bls12381Signature> {
683 let public_key = secret_key.public_key()?;
684 core_sign(
685 secret_key,
686 &public_key,
687 message,
688 BLS_AUG_G2_DST,
689 MessageMode::Augmented,
690 )
691 }
692
693 pub fn verify(
695 public_key: &Bls12381PublicKey,
696 message: &[u8],
697 signature: &Bls12381Signature,
698 ) -> ApiResult<()> {
699 core_verify(
700 public_key,
701 message,
702 signature,
703 BLS_AUG_G2_DST,
704 MessageMode::Augmented,
705 )
706 }
707
708 pub fn aggregate(signatures: &[Bls12381Signature]) -> ApiResult<Bls12381Signature> {
710 Bls12381Signature::aggregate(signatures)
711 }
712
713 pub fn aggregate_verify(
716 public_keys: &[Bls12381PublicKey],
717 messages: &[&[u8]],
718 signature: &Bls12381Signature,
719 ) -> ApiResult<()> {
720 core_aggregate_verify(
721 public_keys,
722 messages,
723 signature,
724 BLS_AUG_G2_DST,
725 MessageMode::Augmented,
726 )
727 }
728}
729
730pub struct Bls12381G2ProofOfPossession;
732
733impl Bls12381G2ProofOfPossession {
734 pub fn sign(secret_key: &Bls12381SecretKey, message: &[u8]) -> ApiResult<Bls12381Signature> {
736 let public_key = secret_key.public_key()?;
737 core_sign(
738 secret_key,
739 &public_key,
740 message,
741 BLS_POP_G2_DST,
742 MessageMode::Raw,
743 )
744 }
745
746 pub fn pop_prove(secret_key: &Bls12381SecretKey) -> ApiResult<Bls12381ProofOfPossession> {
749 let public_key = secret_key.public_key()?;
750 let message_point = G2Projective::hash_to_curve(&public_key.bytes, BLS_POP_PROOF_G2_DST)
751 .map_err(ApiError::from)?;
752 let proof = message_point
753 .multiply_secret_be_bytes(secret_key.bytes())
754 .map_err(ApiError::from)?;
755 Ok(Bls12381ProofOfPossession {
756 bytes: proof.to_bytes(),
757 })
758 }
759
760 pub fn pop_verify(
762 public_key: &Bls12381PublicKey,
763 proof: &Bls12381ProofOfPossession,
764 ) -> ApiResult<()> {
765 core_verify(
766 public_key,
767 &public_key.bytes,
768 &proof.signature(),
769 BLS_POP_PROOF_G2_DST,
770 MessageMode::Raw,
771 )
772 }
773
774 pub fn verify(
776 public_key: &Bls12381PublicKey,
777 proof: &Bls12381ProofOfPossession,
778 message: &[u8],
779 signature: &Bls12381Signature,
780 ) -> ApiResult<()> {
781 Self::pop_verify(public_key, proof)?;
782 core_verify(
783 public_key,
784 message,
785 signature,
786 BLS_POP_G2_DST,
787 MessageMode::Raw,
788 )
789 }
790
791 pub fn aggregate(signatures: &[Bls12381Signature]) -> ApiResult<Bls12381Signature> {
793 Bls12381Signature::aggregate(signatures)
794 }
795
796 pub fn aggregate_verify(
798 public_keys: &[Bls12381PublicKey],
799 proofs: &[Bls12381ProofOfPossession],
800 messages: &[&[u8]],
801 signature: &Bls12381Signature,
802 ) -> ApiResult<()> {
803 validate_parallel_inputs("BLS PoP aggregate verification", public_keys, messages)?;
804 if public_keys.len() != proofs.len() {
805 return Err(invalid_parameter(
806 "BLS PoP aggregate verification",
807 "public-key and proof counts differ",
808 ));
809 }
810 for (public_key, proof) in public_keys.iter().zip(proofs) {
811 Self::pop_verify(public_key, proof)?;
812 }
813 core_aggregate_verify(
814 public_keys,
815 messages,
816 signature,
817 BLS_POP_G2_DST,
818 MessageMode::Raw,
819 )
820 }
821
822 pub fn fast_aggregate_verify(
825 public_keys: &[Bls12381PublicKey],
826 proofs: &[Bls12381ProofOfPossession],
827 message: &[u8],
828 signature: &Bls12381Signature,
829 ) -> ApiResult<()> {
830 if public_keys.is_empty() {
831 return Err(invalid_parameter(
832 "BLS PoP fast aggregate verification",
833 "at least one public key is required",
834 ));
835 }
836 if public_keys.len() != proofs.len() {
837 return Err(invalid_parameter(
838 "BLS PoP fast aggregate verification",
839 "public-key and proof counts differ",
840 ));
841 }
842 for (public_key, proof) in public_keys.iter().zip(proofs) {
843 Self::pop_verify(public_key, proof)?;
844 }
845 let aggregate_public_key = Bls12381PublicKey::aggregate(public_keys)?;
846 core_verify(
847 &aggregate_public_key,
848 message,
849 signature,
850 BLS_POP_G2_DST,
851 MessageMode::Raw,
852 )
853 }
854}
855
856pub struct Eth2Bls12381G2PopV4;
863
864impl Eth2Bls12381G2PopV4 {
865 pub fn key_gen(ikm: &[u8]) -> ApiResult<Bls12381SecretKey> {
868 Self::key_gen_with_info(ikm, &[])
869 }
870
871 pub fn key_gen_with_info(ikm: &[u8], key_info: &[u8]) -> ApiResult<Bls12381SecretKey> {
873 let mut salt = Sha256::digest(KEYGEN_V4_SALT_TAG).map_err(ApiError::from)?;
874 let result = Bls12381SecretKey::key_gen_with_info(ikm, salt.as_ref(), key_info);
875 salt.zeroize();
876 result
877 }
878
879 pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Bls12381SecretKey> {
881 let mut ikm = Zeroizing::new([0u8; 32]);
882 try_fill_bytes_zeroing_on_error(rng, &mut *ikm).map_err(|_| {
883 ApiError::RandomGenerationError {
884 context: "Eth2Bls12381G2PopV4::generate",
885 #[cfg(feature = "std")]
886 message: "caller-provided randomness source failed".into(),
887 }
888 })?;
889 Self::key_gen(&*ikm)
890 }
891
892 pub fn sign(secret_key: &Bls12381SecretKey, message: &[u8]) -> ApiResult<Bls12381Signature> {
894 Bls12381G2ProofOfPossession::sign(secret_key, message)
895 }
896
897 pub fn verify(
899 public_key: &Bls12381PublicKey,
900 message: &[u8],
901 signature: &Bls12381Signature,
902 ) -> ApiResult<()> {
903 core_verify(
904 public_key,
905 message,
906 signature,
907 BLS_POP_G2_DST,
908 MessageMode::Raw,
909 )
910 }
911
912 pub fn aggregate(signatures: &[Bls12381Signature]) -> ApiResult<Bls12381Signature> {
914 Bls12381Signature::aggregate(signatures)
915 }
916
917 pub fn aggregate_verify(
920 public_keys: &[Bls12381PublicKey],
921 messages: &[&[u8]],
922 signature: &Bls12381Signature,
923 ) -> ApiResult<()> {
924 core_aggregate_verify(
925 public_keys,
926 messages,
927 signature,
928 BLS_POP_G2_DST,
929 MessageMode::Raw,
930 )
931 }
932
933 pub fn aggregate_public_keys(
936 public_keys: &[Bls12381PublicKey],
937 ) -> ApiResult<Bls12381PublicKey> {
938 Bls12381PublicKey::aggregate(public_keys)
939 }
940
941 pub fn fast_aggregate_verify(
947 public_keys: &[Bls12381PublicKey],
948 message: &[u8; 32],
949 signature: &Bls12381Signature,
950 ) -> ApiResult<()> {
951 if public_keys.is_empty() {
952 return if signature.is_identity() {
953 Ok(())
954 } else {
955 Err(invalid_signature(
956 "Eth2 fast aggregate verification",
957 "empty public-key input requires the G2 identity signature",
958 ))
959 };
960 }
961
962 let aggregate_public_key = Bls12381PublicKey::aggregate(public_keys)?;
963 core_verify(
964 &aggregate_public_key,
965 message,
966 signature,
967 BLS_POP_G2_DST,
968 MessageMode::Raw,
969 )
970 }
971}
972
973#[cfg(test)]
974mod tests;