commonware_cryptography/bls12381/
mod.rs

1//! Distributed Key Generation (DKG), Resharing, Signatures, and Threshold Signatures over the BLS12-381 curve.
2//!
3//! # Features
4//!
5//! This crate has the following features:
6//!
7//! - `portable`: Enables `portable` feature on `blst` (<https://github.com/supranational/blst?tab=readme-ov-file#platform-and-language-compatibility>).
8
9pub mod dkg;
10pub mod primitives;
11mod scheme;
12pub use scheme::{Bls12381, PrivateKey, PublicKey, Signature};
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17    use dkg::ops::generate_shares;
18    use primitives::group::Private;
19    use primitives::ops::{
20        partial_sign_message, partial_verify_message, threshold_signature_recover, verify_message,
21    };
22    use primitives::poly::public;
23    use primitives::Error;
24    use rand::{rngs::StdRng, SeedableRng};
25
26    #[test]
27    fn test_partial_aggregate_signature() {
28        let (n, t) = (5, 4);
29        let mut rng = StdRng::seed_from_u64(0);
30
31        // Create the private key polynomial and evaluate it at `n`
32        // points to generate the shares.
33        //
34        // If receiving a share from an untrusted party, the recipient
35        // should verify the share is on the public polynomial.
36        let (group, shares) = generate_shares(&mut rng, None, n, t);
37
38        // Generate the partial signatures
39        let namespace = Some(&b"test"[..]);
40        let msg = b"hello";
41        let partials = shares
42            .iter()
43            .map(|s| partial_sign_message(s, namespace, msg))
44            .collect::<Vec<_>>();
45
46        // Each partial sig can be partially verified against the public polynomial
47        partials.iter().for_each(|partial| {
48            partial_verify_message(&group, namespace, msg, partial).unwrap();
49        });
50
51        // Generate and verify the threshold sig
52        let threshold_sig = threshold_signature_recover(t, &partials).unwrap();
53        let threshold_pub = public(&group);
54        verify_message(threshold_pub, namespace, msg, &threshold_sig).unwrap();
55    }
56
57    #[test]
58    fn test_partial_aggregate_signature_bad_namespace() {
59        let (n, t) = (5, 4);
60        let mut rng = StdRng::seed_from_u64(0);
61
62        // Create the private key polynomial and evaluate it at `n`
63        // points to generate the shares.
64        //
65        // If receiving a share from an untrusted party, the recipient
66        // should verify the share is on the public polynomial.
67        let (group, shares) = generate_shares(&mut rng, None, n, t);
68
69        // Generate the partial signatures
70        let namespace = Some(&b"test"[..]);
71        let msg = b"hello";
72        let partials = shares
73            .iter()
74            .map(|s| partial_sign_message(s, namespace, msg))
75            .collect::<Vec<_>>();
76
77        // Each partial sig can be partially verified against the public polynomial
78        let namespace = Some(&b"bad"[..]);
79        partials.iter().for_each(|partial| {
80            assert!(matches!(
81                partial_verify_message(&group, namespace, msg, partial).unwrap_err(),
82                Error::InvalidSignature
83            ));
84        });
85
86        // Generate and verify the threshold sig
87        let threshold_sig = threshold_signature_recover(t, &partials).unwrap();
88        let threshold_pub = public(&group);
89        assert!(matches!(
90            verify_message(threshold_pub, namespace, msg, &threshold_sig).unwrap_err(),
91            Error::InvalidSignature
92        ));
93    }
94
95    #[test]
96    fn test_partial_aggregate_signature_insufficient() {
97        let (n, t) = (5, 4);
98        let mut rng = StdRng::seed_from_u64(0);
99
100        // Create the private key polynomial and evaluate it at `n`
101        // points to generate the shares
102        let (group, shares) = generate_shares(&mut rng, None, n, t);
103
104        // Only take t-1 shares
105        let shares = shares.into_iter().take(t as usize - 1).collect::<Vec<_>>();
106
107        // Generate the partial signatures
108        let namespace = Some(&b"test"[..]);
109        let msg = b"hello";
110        let partials = shares
111            .iter()
112            .map(|s| partial_sign_message(s, namespace, msg))
113            .collect::<Vec<_>>();
114
115        // Each partial sig can be partially verified against the public polynomial
116        partials.iter().for_each(|partial| {
117            partial_verify_message(&group, namespace, msg, partial).unwrap();
118        });
119
120        // Generate the threshold sig
121        assert!(matches!(
122            threshold_signature_recover(t, &partials).unwrap_err(),
123            Error::NotEnoughPartialSignatures(4, 3)
124        ));
125    }
126
127    #[test]
128    #[should_panic(expected = "InvalidSignature")]
129    fn test_partial_aggregate_signature_bad_share() {
130        let (n, t) = (5, 4);
131        let mut rng = StdRng::seed_from_u64(0);
132
133        // Create the private key polynomial and evaluate it at `n`
134        // points to generate the shares
135        let (group, mut shares) = generate_shares(&mut rng, None, n, t);
136
137        // Corrupt a share
138        let share = shares.get_mut(3).unwrap();
139        share.private = Private::rand(&mut rand::thread_rng());
140
141        // Generate the partial signatures
142        let namespace = Some(&b"test"[..]);
143        let msg = b"hello";
144        let partials = shares
145            .iter()
146            .map(|s| partial_sign_message(s, namespace, msg))
147            .collect::<Vec<_>>();
148
149        // Each partial sig can be partially verified against the public polynomial
150        partials.iter().for_each(|partial| {
151            partial_verify_message(&group, namespace, msg, partial).unwrap();
152        });
153
154        // Generate and verify the threshold sig
155        let threshold_sig = threshold_signature_recover(t, &partials).unwrap();
156        let threshold_pub = public(&group);
157        verify_message(threshold_pub, namespace, msg, &threshold_sig).unwrap();
158    }
159}