Skip to main content

commonware_cryptography/bls12381/
scheme.rs

1//! BLS12-381 implementation of the [crate::Verifier] and [crate::Signer] traits.
2//!
3//! This implementation uses the `blst` crate for BLS12-381 operations. This
4//! crate implements serialization according to the "ZCash BLS12-381" specification
5//! (<https://github.com/supranational/blst/tree/master?tab=readme-ov-file#serialization-format>)
6//! and hashes messages according to RFC 9380.
7//!
8//! # Example
9//! ```rust
10//! use commonware_cryptography::{bls12381, PrivateKey, PublicKey, Signature, Verifier as _, Signer as _};
11//! use commonware_math::algebra::Random;
12//! use commonware_utils::test_rng;
13//!
14//! let mut rng = test_rng();
15//!
16//! // Generate a new private key
17//! let mut signer = bls12381::PrivateKey::random(&mut rng);
18//!
19//! // Create a message to sign
20//! let namespace = b"demo";
21//! let msg = b"hello, world!";
22//!
23//! // Sign the message
24//! let signature = signer.sign(namespace, msg);
25//!
26//! // Verify the signature
27//! assert!(signer.public_key().verify(namespace, msg, &signature));
28//! ```
29
30use super::primitives::{
31    group::{self, Private},
32    ops,
33    variant::{MinPk, Variant},
34};
35use crate::{BatchEntry, BatchVerifier, Secret, Signer as _};
36#[cfg(not(feature = "std"))]
37use alloc::vec::Vec;
38use bytes::BufMut;
39use commonware_codec::{
40    Buf, Copying, DecodeExt, EncodeFixed, Error as CodecError, FixedArray, FixedSize, Read,
41    ReadExt, Write,
42};
43use commonware_formatting::Hex;
44use commonware_math::algebra::Random;
45use commonware_parallel::Strategy;
46use commonware_utils::{Array, Span};
47use core::{
48    fmt::{Debug, Display, Formatter},
49    hash::{Hash, Hasher},
50    ops::Deref,
51};
52use rand_core::CryptoRng;
53use zeroize::Zeroizing;
54
55const CURVE_NAME: &str = "bls12381";
56
57/// BLS12-381 private key.
58#[derive(Clone, Debug)]
59pub struct PrivateKey {
60    raw: Secret<[u8; group::PRIVATE_KEY_LENGTH]>,
61    key: Private,
62}
63
64impl PartialEq for PrivateKey {
65    fn eq(&self, other: &Self) -> bool {
66        self.raw == other.raw
67    }
68}
69
70impl Eq for PrivateKey {}
71
72impl Write for PrivateKey {
73    fn write(&self, buf: &mut impl BufMut) {
74        self.raw.expose(|raw| raw.write(buf));
75    }
76}
77
78impl Read for PrivateKey {
79    type Cfg = ();
80
81    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
82        let raw = Zeroizing::new(<[u8; Self::SIZE]>::read(buf)?);
83        let key = Private::decode(Copying(raw.as_ref()))
84            .map_err(|e| CodecError::Wrapped(CURVE_NAME, e.into()))?;
85        Ok(Self {
86            raw: Secret::new(*raw),
87            key,
88        })
89    }
90}
91
92impl FixedSize for PrivateKey {
93    const SIZE: usize = group::PRIVATE_KEY_LENGTH;
94}
95
96impl From<Private> for PrivateKey {
97    fn from(key: Private) -> Self {
98        let raw = Zeroizing::new(key.expose(|s| s.encode_fixed()));
99        Self {
100            raw: Secret::new(*raw),
101            key,
102        }
103    }
104}
105
106impl Display for PrivateKey {
107    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
108        write!(f, "{:?}", self)
109    }
110}
111
112impl crate::PrivateKey for PrivateKey {}
113
114impl crate::Signer for PrivateKey {
115    type Signature = Signature;
116    type PublicKey = PublicKey;
117
118    fn public_key(&self) -> Self::PublicKey {
119        PublicKey::from(ops::compute_public::<MinPk>(&self.key))
120    }
121
122    fn sign(&self, namespace: &[u8], msg: &[u8]) -> Self::Signature {
123        ops::sign_message::<MinPk>(&self.key, namespace, msg).into()
124    }
125}
126
127impl Random for PrivateKey {
128    fn random(mut rng: impl CryptoRng) -> Self {
129        let (private, _) = ops::keypair::<_, MinPk>(&mut rng);
130        private.into()
131    }
132}
133
134#[cfg(feature = "arbitrary")]
135impl arbitrary::Arbitrary<'_> for PrivateKey {
136    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
137        use rand::{SeedableRng, rngs::StdRng};
138
139        let mut rand = StdRng::from_seed(u.arbitrary::<[u8; 32]>()?);
140        Ok(Self::random(&mut rand))
141    }
142}
143
144impl crate::PublicKey for PublicKey {}
145
146impl crate::Verifier for PublicKey {
147    type Signature = Signature;
148
149    fn verify(&self, namespace: &[u8], msg: &[u8], sig: &Self::Signature) -> bool {
150        ops::verify_message::<MinPk>(&self.key, namespace, msg, &sig.signature).is_ok()
151    }
152}
153
154impl BatchVerifier for PublicKey {
155    fn verify_batch<'a, R, T, F>(
156        rng: &mut R,
157        items: &'a [T],
158        project: F,
159        strategy: &impl Strategy,
160    ) -> bool
161    where
162        R: CryptoRng,
163        T: Sync,
164        F: Fn(usize, &'a T) -> BatchEntry<'a, Self> + Sync,
165    {
166        let hms = strategy.map_collect_vec(items.iter().enumerate(), |(index, item)| {
167            let entry = project(index, item);
168            ops::hash_with_namespace::<MinPk>(MinPk::MESSAGE, entry.namespace, entry.message)
169        });
170        let (publics, signatures): (Vec<_>, Vec<_>) = items
171            .iter()
172            .enumerate()
173            .map(|(index, item)| {
174                let entry = project(index, item);
175                (entry.public_key.key, entry.signature.signature)
176            })
177            .unzip();
178
179        MinPk::batch_verify(rng, &publics, &hms, &signatures, strategy).is_ok()
180    }
181}
182
183/// BLS12-381 public key.
184#[derive(Clone, Eq, FixedArray)]
185pub struct PublicKey {
186    raw: [u8; <MinPk as Variant>::Public::SIZE],
187    key: <MinPk as Variant>::Public,
188}
189
190impl PartialEq for PublicKey {
191    fn eq(&self, other: &Self) -> bool {
192        self.raw == other.raw
193    }
194}
195
196impl From<PrivateKey> for PublicKey {
197    fn from(private_key: PrivateKey) -> Self {
198        private_key.public_key()
199    }
200}
201
202impl AsRef<<MinPk as Variant>::Public> for PublicKey {
203    fn as_ref(&self) -> &<MinPk as Variant>::Public {
204        &self.key
205    }
206}
207
208impl Write for PublicKey {
209    fn write(&self, buf: &mut impl BufMut) {
210        self.raw.write(buf);
211    }
212}
213
214impl Read for PublicKey {
215    type Cfg = ();
216
217    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
218        let raw = <[u8; Self::SIZE]>::read(buf)?;
219        let key = <MinPk as Variant>::Public::decode(Copying(&raw))
220            .map_err(|e| CodecError::Wrapped(CURVE_NAME, e.into()))?;
221        Ok(Self { raw, key })
222    }
223}
224
225impl FixedSize for PublicKey {
226    const SIZE: usize = <MinPk as Variant>::Public::SIZE;
227}
228
229impl Span for PublicKey {}
230
231impl Array for PublicKey {}
232
233impl Hash for PublicKey {
234    fn hash<H: Hasher>(&self, state: &mut H) {
235        self.raw.hash(state);
236    }
237}
238
239impl Ord for PublicKey {
240    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
241        self.raw.cmp(&other.raw)
242    }
243}
244
245impl PartialOrd for PublicKey {
246    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
247        Some(self.cmp(other))
248    }
249}
250
251impl AsRef<[u8]> for PublicKey {
252    fn as_ref(&self) -> &[u8] {
253        &self.raw
254    }
255}
256
257impl Deref for PublicKey {
258    type Target = [u8];
259    fn deref(&self) -> &[u8] {
260        &self.raw
261    }
262}
263
264impl From<<MinPk as Variant>::Public> for PublicKey {
265    fn from(key: <MinPk as Variant>::Public) -> Self {
266        let raw = key.encode_fixed();
267        Self { raw, key }
268    }
269}
270
271impl Debug for PublicKey {
272    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
273        write!(f, "{}", Hex(&self.raw))
274    }
275}
276
277impl Display for PublicKey {
278    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
279        write!(f, "{}", Hex(&self.raw))
280    }
281}
282
283#[cfg(feature = "arbitrary")]
284impl arbitrary::Arbitrary<'_> for PublicKey {
285    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
286        use crate::Signer;
287        use rand::{SeedableRng, rngs::StdRng};
288
289        let mut rand = StdRng::from_seed(u.arbitrary::<[u8; 32]>()?);
290        let private_key = PrivateKey::random(&mut rand);
291        Ok(private_key.public_key())
292    }
293}
294
295/// BLS12-381 signature.
296#[derive(Clone, Eq, PartialEq, FixedArray)]
297pub struct Signature {
298    raw: [u8; <MinPk as Variant>::Signature::SIZE],
299    signature: <MinPk as Variant>::Signature,
300}
301
302impl crate::Signature for Signature {}
303
304impl AsRef<<MinPk as Variant>::Signature> for Signature {
305    fn as_ref(&self) -> &<MinPk as Variant>::Signature {
306        &self.signature
307    }
308}
309
310impl Write for Signature {
311    fn write(&self, buf: &mut impl BufMut) {
312        self.raw.write(buf);
313    }
314}
315
316impl Read for Signature {
317    type Cfg = ();
318
319    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
320        let raw = <[u8; Self::SIZE]>::read(buf)?;
321        let signature = <MinPk as Variant>::Signature::decode(Copying(&raw))
322            .map_err(|e| CodecError::Wrapped(CURVE_NAME, e.into()))?;
323        Ok(Self { raw, signature })
324    }
325}
326
327impl FixedSize for Signature {
328    const SIZE: usize = <MinPk as Variant>::Signature::SIZE;
329}
330
331impl Span for Signature {}
332
333impl Array for Signature {}
334
335impl Hash for Signature {
336    fn hash<H: Hasher>(&self, state: &mut H) {
337        self.raw.hash(state);
338    }
339}
340
341impl Ord for Signature {
342    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
343        self.raw.cmp(&other.raw)
344    }
345}
346
347impl PartialOrd for Signature {
348    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
349        Some(self.cmp(other))
350    }
351}
352
353impl AsRef<[u8]> for Signature {
354    fn as_ref(&self) -> &[u8] {
355        &self.raw
356    }
357}
358
359impl Deref for Signature {
360    type Target = [u8];
361    fn deref(&self) -> &[u8] {
362        &self.raw
363    }
364}
365
366impl From<<MinPk as Variant>::Signature> for Signature {
367    fn from(signature: <MinPk as Variant>::Signature) -> Self {
368        let raw = signature.encode_fixed();
369        Self { raw, signature }
370    }
371}
372
373impl Debug for Signature {
374    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
375        write!(f, "{}", Hex(&self.raw))
376    }
377}
378
379impl Display for Signature {
380    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
381        write!(f, "{}", Hex(&self.raw))
382    }
383}
384
385#[cfg(feature = "arbitrary")]
386impl arbitrary::Arbitrary<'_> for Signature {
387    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
388        use crate::Signer;
389        use rand::{SeedableRng, rngs::StdRng};
390
391        let mut rand = StdRng::from_seed(u.arbitrary::<[u8; 32]>()?);
392        let private_key = PrivateKey::random(&mut rand);
393        let len = u.arbitrary::<usize>()? % 256;
394        let message = u
395            .arbitrary_iter()?
396            .take(len)
397            .collect::<Result<Vec<_>, _>>()?;
398
399        Ok(private_key.sign(b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_TEST", &message))
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406    use crate::{Verifier as _, bls12381};
407    use commonware_codec::{DecodeExt, Encode};
408    use commonware_math::algebra::Random;
409    use commonware_parallel::{Sequential, mocks::explore};
410    use commonware_utils::test_rng;
411
412    #[test]
413    fn test_codec_private_key() {
414        let original =
415            parse_private_key("0x263dbd792f5b1be47ed85f8938c0f29586af0d3ac7b977f21c278fe1462040e3")
416                .unwrap();
417        let encoded = original.encode();
418        assert_eq!(encoded.len(), bls12381::PrivateKey::SIZE);
419        let decoded = bls12381::PrivateKey::decode(encoded).unwrap();
420        assert_eq!(original, decoded);
421    }
422
423    #[test]
424    fn test_codec_public_key() {
425        let original =
426            parse_public_key("0xa491d1b0ecd9bb917989f0e74f0dea0422eac4a873e5e2644f368dffb9a6e20fd6e10c1b77654d067c0618f6e5a7f79a")
427            .unwrap();
428        let encoded = original.encode();
429        assert_eq!(encoded.len(), PublicKey::SIZE);
430        let decoded = PublicKey::decode(encoded).unwrap();
431        assert_eq!(original, decoded);
432    }
433
434    #[test]
435    fn test_codec_signature() {
436        let original =
437            parse_signature("0x882730e5d03f6b42c3abc26d3372625034e1d871b65a8a6b900a56dae22da98abbe1b68f85e49fe7652a55ec3d0591c20767677e33e5cbb1207315c41a9ac03be39c2e7668edc043d6cb1d9fd93033caa8a1c5b0e84bedaeb6c64972503a43eb")
438            .unwrap();
439        let encoded = original.encode();
440        assert_eq!(encoded.len(), Signature::SIZE);
441        let decoded = Signature::decode(encoded).unwrap();
442        assert_eq!(original, decoded);
443    }
444
445    fn parse_private_key(private_key: &str) -> Result<PrivateKey, CodecError> {
446        PrivateKey::decode(commonware_formatting::from_hex(private_key).unwrap())
447    }
448
449    fn parse_public_key(public_key: &str) -> Result<PublicKey, CodecError> {
450        PublicKey::decode(commonware_formatting::from_hex(public_key).unwrap())
451    }
452
453    fn parse_signature(signature: &str) -> Result<Signature, CodecError> {
454        Signature::decode(commonware_formatting::from_hex(signature).unwrap())
455    }
456
457    #[test]
458    fn test_from_private() {
459        let mut rng = test_rng();
460        let private = Private::random(&mut rng);
461        let private_key = PrivateKey::from(private);
462        // Verify the key works by signing and verifying
463        let msg = b"test message";
464        let sig = private_key.sign(b"ns", msg);
465        assert!(private_key.public_key().verify(b"ns", msg, &sig));
466    }
467
468    #[test]
469    fn test_private_key_redacted() {
470        let mut rng = test_rng();
471        let private_key = PrivateKey::random(&mut rng);
472        let debug = format!("{:?}", private_key);
473        let display = format!("{}", private_key);
474        assert!(debug.contains("REDACTED"));
475        assert!(display.contains("REDACTED"));
476    }
477
478    #[test]
479    fn batch_verify_empty() {
480        let entries: [BatchEntry<'_, PublicKey>; 0] = [];
481        assert!(!PublicKey::verify_batch(
482            &mut test_rng(),
483            &entries,
484            |_, entry| *entry,
485            &Sequential,
486        ));
487    }
488
489    #[test]
490    fn batch_verify_invalid_message() {
491        // Start with a valid namespaced signature.
492        let mut rng = test_rng();
493        let private_key = PrivateKey::random(&mut rng);
494        let public_key = private_key.public_key();
495        let signature = private_key.sign(b"ns", b"message");
496        let mut entries = [BatchEntry {
497            namespace: b"ns",
498            message: b"message",
499            public_key: &public_key,
500            signature: &signature,
501        }];
502        assert!(PublicKey::verify_batch(
503            &mut rng,
504            &entries,
505            |_, entry| *entry,
506            &Sequential,
507        ));
508
509        // Changing the message must invalidate the signature.
510        entries[0].message = b"invalid";
511        assert!(!PublicKey::verify_batch(
512            &mut rng,
513            &entries,
514            |_, entry| *entry,
515            &Sequential,
516        ));
517    }
518
519    #[test]
520    fn projected_batch_indexes_zero_sized_items() {
521        struct Batch {
522            namespaces: Vec<&'static [u8]>,
523            messages: Vec<Vec<u8>>,
524            signers: Vec<usize>,
525            public_keys: Vec<PublicKey>,
526            signatures: Vec<Signature>,
527        }
528
529        impl Batch {
530            fn verify(&self, strategy: &impl Strategy) -> bool {
531                let items = vec![(); self.messages.len()];
532                PublicKey::verify_batch(
533                    &mut test_rng(),
534                    &items,
535                    |index, ()| BatchEntry {
536                        namespace: self.namespaces[index],
537                        message: &self.messages[index],
538                        public_key: &self.public_keys[self.signers[index]],
539                        signature: &self.signatures[index],
540                    },
541                    strategy,
542                )
543            }
544        }
545
546        // Build an uneven batch whose unit items identify their namespace, message, signer, and
547        // signature only by original index.
548        let mut rng = test_rng();
549        let keys: Vec<_> = (0..3).map(|_| PrivateKey::random(&mut rng)).collect();
550        let namespaces: Vec<&'static [u8]> = (0..9)
551            .map(|i| [b"".as_slice(), b"alpha", b"beta"][(i / 3) % 3])
552            .collect();
553        let messages: Vec<_> = (0..9).map(|i| vec![i as u8; i * 7]).collect();
554        let signers: Vec<_> = (0..9).map(|i| i % keys.len()).collect();
555        let signatures = (0..9)
556            .map(|i| keys[signers[i]].sign(namespaces[i], &messages[i]))
557            .collect();
558        let mut batch = Batch {
559            namespaces,
560            messages,
561            signers,
562            public_keys: keys.iter().map(|key| key.public_key()).collect(),
563            signatures,
564        };
565
566        // Check each expected verdict across execution shapes.
567        let check = |batch: &Batch, expected: bool| {
568            assert_eq!(explore(|strategy| batch.verify(strategy)), expected);
569        };
570        check(&batch, true);
571
572        // Changing the message, namespace, or signer at any index must fail the whole batch.
573        for index in 0..batch.messages.len() {
574            batch.messages[index].push(0);
575            check(&batch, false);
576            batch.messages[index].pop();
577
578            let namespace = core::mem::replace(&mut batch.namespaces[index], b"other");
579            check(&batch, false);
580            batch.namespaces[index] = namespace;
581
582            let signer = batch.signers[index];
583            batch.signers[index] = (signer + 1) % keys.len();
584            check(&batch, false);
585            batch.signers[index] = signer;
586        }
587
588        // Restoring every entry must verify again.
589        check(&batch, true);
590    }
591
592    #[cfg(feature = "arbitrary")]
593    mod conformance {
594        use super::*;
595        use commonware_codec::conformance::CodecConformance;
596
597        commonware_conformance::conformance_tests! {
598            CodecConformance<PublicKey>,
599            CodecConformance<PrivateKey>,
600            CodecConformance<Signature>,
601        }
602    }
603}