Skip to main content

rings_core/ecc/signers/
bls.rs

1//! signer of bls
2//! A module for signing messages using BLS (Boneh-Lynn-Shacham) and interfacing with secp256k1 cryptographic libraries.
3//!
4//! This module provides functionality for generating private and public keys, signing messages,
5//! and verifying signatures using both BLS and secp256k1 cryptographic standards.
6//! It integrates the use of random number generation for key creation and provides conversions
7//! between different key types.
8
9use ark_bls12_381::fr::Fr;
10use ark_bls12_381::g2::Config as G2Config;
11use ark_bls12_381::Bls12_381;
12use ark_bls12_381::G1Projective;
13use ark_bls12_381::G2Projective;
14use ark_ec::hashing::curve_maps::wb::WBMap;
15use ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher;
16use ark_ec::hashing::HashToCurve;
17use ark_ec::pairing::Pairing;
18use ark_ec::Group;
19use ark_ff::fields::field_hashers::DefaultFieldHasher;
20use ark_serialize::CanonicalDeserialize;
21use ark_serialize::CanonicalSerialize;
22use ark_std::UniformRand;
23use libsecp256k1;
24use rand::SeedableRng;
25use rand_hc::Hc128Rng;
26
27use crate::ecc::PublicKey;
28use crate::ecc::SecretKey;
29use crate::error::Error;
30use crate::error::Result;
31
32/// this is from `<https://docs.rs/bls-signatures/latest/src/bls_signatures/signature.rs.html#24>`
33const CSUITE: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
34
35/// Represents a BLS signature, stored as a 96-byte array.
36#[derive(Clone, Debug, Eq, PartialEq)]
37pub struct Signature(pub [u8; 96]);
38
39/// this function is used to generate a random secret key
40pub fn random_sk() -> Result<SecretKey> {
41    let mut rng = Hc128Rng::from_entropy();
42    Fr::rand(&mut rng).try_into()
43}
44
45fn from_compressed<T: CanonicalDeserialize, const S: usize>(a: &[u8; S]) -> Result<T> {
46    T::deserialize_compressed(&a[..]).map_err(|_| Error::EccDeserializeFailed)
47}
48
49fn to_compressed<T: CanonicalSerialize, const S: usize>(s: &T) -> Result<[u8; S]> {
50    let mut data: Vec<u8> = vec![];
51    s.serialize_compressed(&mut data)
52        .map_err(|_| Error::EccSerializeFailed)?;
53    assert_eq!(s.compressed_size(), S);
54    assert_eq!(data.len(), S);
55    let ret: [u8; S] = data.try_into().map_err(|_| Error::EccSerializeFailed)?;
56    Ok(ret)
57}
58
59impl TryFrom<SecretKey> for Fr {
60    type Error = Error;
61    fn try_from(sk: SecretKey) -> Result<Fr> {
62        let data: [u8; 32] = sk.0.serialize();
63        let ret: Fr = from_compressed(&data)?;
64        Ok(ret)
65    }
66}
67
68impl TryFrom<Fr> for SecretKey {
69    type Error = Error;
70    fn try_from(sk: Fr) -> Result<SecretKey> {
71        let data: [u8; 32] = to_compressed(&sk)?;
72        let sk = libsecp256k1::SecretKey::parse(&data)?;
73        Ok(SecretKey(sk))
74    }
75}
76
77impl TryFrom<Signature> for G2Projective {
78    type Error = Error;
79    fn try_from(s: Signature) -> Result<Self> {
80        from_compressed(&s.0)
81    }
82}
83
84impl TryFrom<G2Projective> for Signature {
85    type Error = Error;
86    fn try_from(s: G2Projective) -> Result<Self> {
87        Ok(Signature(to_compressed(&s)?))
88    }
89}
90
91impl TryFrom<G1Projective> for PublicKey<48> {
92    type Error = Error;
93    fn try_from(p: G1Projective) -> Result<Self> {
94        Ok(PublicKey(to_compressed::<G1Projective, 48>(&p)?))
95    }
96}
97
98impl TryFrom<PublicKey<48>> for G1Projective {
99    type Error = Error;
100    fn try_from(pk: PublicKey<48>) -> Result<Self> {
101        let data: [u8; 48] = pk.0;
102        let ret: Self = from_compressed(&data)?;
103        Ok(ret)
104    }
105}
106
107/// Hashes a message to a 96-byte array using BLS
108/// `<https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/>`
109pub fn hash_to_curve(msg: &[u8]) -> Result<[u8; 96]> {
110    // let swu_map: WBMap<G1Config> = WBMap::new().unwrap();
111    let hasher = MapToCurveBasedHasher::<
112        G2Projective,
113        DefaultFieldHasher<sha2::Sha256, 128>,
114        WBMap<G2Config>,
115    >::new(CSUITE)
116    .map_err(|_| Error::CurveHasherInitFailed)?;
117    let hashed = hasher.hash(msg).map_err(|_| Error::CurveHasherFailed)?;
118    let ret: [u8; 96] = to_compressed(&hashed)?;
119    Ok(ret)
120}
121
122/// Sign hashed message with bls privatekey
123pub fn sign_hash(sk: SecretKey, hashed_msg: &[u8; 96]) -> Result<Signature> {
124    let sk: Fr = sk.try_into()?;
125    let msg: G2Projective = from_compressed(hashed_msg)?;
126    Ok(Signature(to_compressed(&(msg * sk))?))
127}
128
129/// Sign message with bls privatekey
130/// signature = hash_into_g2(message) * sk
131pub fn sign(sk: SecretKey, msg: &[u8]) -> Result<Signature> {
132    let sk: Fr = sk.try_into()?;
133    let hashed_msg = hash_to_curve(msg)?;
134    let msg: G2Projective = from_compressed(&hashed_msg)?;
135    Ok(Signature(to_compressed(&(msg * sk))?))
136}
137
138/// Verifies that the signature is the actual aggregated signature of hashes - pubkeys. Calculated by
139/// e(g1, signature) == \prod_{i = 0}^n e(pk_i, hash_i).
140pub fn verify_hash(hashes: &[[u8; 96]], sig: &Signature, pks: &[PublicKey<48>]) -> Result<bool> {
141    let sig: G2Projective = sig.clone().try_into()?;
142    let g1 = G1Projective::generator();
143    let e1 = Bls12_381::pairing(g1, sig);
144
145    let hashes: Vec<G2Projective> = hashes
146        .iter()
147        .map(from_compressed)
148        .collect::<Result<Vec<G2Projective>>>()?;
149
150    let pks: Vec<G1Projective> = pks
151        .iter()
152        .map(|pk| (*pk).try_into())
153        .collect::<Result<Vec<G1Projective>>>()?;
154
155    let mm_out = Bls12_381::multi_miller_loop(pks, hashes);
156    if let Some(e2) = Bls12_381::final_exponentiation(mm_out) {
157        Ok(e1 == e2)
158    } else {
159        Ok(false)
160    }
161}
162
163/// Verifies that the signature is the actual aggregated signature of messages - pubkeys. Calculated by
164/// e(g1, signature) == \prod_{i = 0}^n e(pk_i, hash_to_curve(message_i)).
165pub fn verify(msgs: &[&[u8]], sig: &Signature, pks: &[PublicKey<48>]) -> Result<bool> {
166    let hashes: Vec<[u8; 96]> = msgs
167        .iter()
168        .map(|msg| hash_to_curve(msg))
169        .collect::<Result<Vec<[u8; 96]>>>()?;
170    verify_hash(hashes.as_slice(), sig, pks)
171}
172
173// Aggregate signatures by multiplying them together. Calculated by signature = \sum_{i = 0}^n signature_i.
174pub fn aggregate(signatures: &[Signature]) -> Result<Signature> {
175    signatures
176        .iter()
177        .map(|sig| sig.clone().try_into())
178        .collect::<Result<Vec<G2Projective>>>()?
179        .iter()
180        .sum::<G2Projective>()
181        .try_into()
182}
183
184/// Converts a BLS private key to a BLS public key.
185/// Get the public key for this private key. Calculated by pk = g1 * sk.
186pub fn public_key(key: &SecretKey) -> Result<PublicKey<48>> {
187    let sk: Fr = (*key).try_into()?;
188    let g1 = G1Projective::generator();
189    (g1 * sk).try_into()
190}
191
192#[cfg(test)]
193mod test {
194    use super::*;
195
196    #[test]
197    fn test_sign_and_verify() {
198        let key = random_sk().unwrap();
199        let msg = "hello world";
200        let pk = public_key(&key).unwrap();
201        let h = hash_to_curve(msg.as_bytes()).unwrap();
202        let sig = sign_hash(key, &h).unwrap();
203        assert!(super::verify_hash(vec![h].as_slice(), &sig, vec![pk].as_slice()).unwrap());
204        assert!(super::verify(vec![msg.as_bytes()].as_slice(), &sig, vec![pk].as_slice()).unwrap());
205    }
206
207    #[test]
208    fn test_hash_result() {
209        // this is from hash("hello world") via bls_signature
210        // `<https://docs.rs/bls-signatures/latest/bls_signatures/fn.hash.html`>
211        let hashed_data: [u8; 96] = [
212            138, 203, 106, 10, 25, 0, 11, 120, 167, 254, 109, 207, 27, 42, 63, 46, 108, 179, 30,
213            196, 146, 10, 94, 148, 237, 209, 198, 48, 23, 211, 67, 188, 147, 170, 94, 52, 176, 113,
214            111, 214, 28, 35, 235, 16, 215, 69, 185, 65, 15, 66, 199, 2, 245, 101, 145, 144, 209,
215            52, 71, 179, 27, 209, 127, 155, 231, 9, 235, 11, 82, 89, 83, 171, 47, 179, 253, 128,
216            26, 104, 238, 91, 182, 207, 152, 70, 243, 206, 65, 226, 81, 113, 69, 125, 85, 142, 27,
217            254,
218        ];
219        let msg = "hello world";
220        let h = hash_to_curve(msg.as_bytes()).unwrap();
221        assert_eq!(h, hashed_data);
222    }
223
224    #[test]
225    fn test_aggregate() {
226        let key1 = random_sk().unwrap();
227        let key2 = random_sk().unwrap();
228
229        let msg1 = "hello alice";
230        let msg2 = "hello bob";
231
232        let pk1 = public_key(&key1).unwrap();
233        let pk2 = public_key(&key2).unwrap();
234
235        let h1 = hash_to_curve(msg1.as_bytes()).unwrap();
236        let h2 = hash_to_curve(msg2.as_bytes()).unwrap();
237
238        let sig1 = sign_hash(key1, &h1).unwrap();
239        let sig2 = sign_hash(key2, &h2).unwrap();
240
241        let sig_agg = aggregate(&[sig1, sig2]).unwrap();
242
243        assert!(
244            super::verify_hash(vec![h1, h2].as_slice(), &sig_agg, vec![pk1, pk2].as_slice())
245                .unwrap()
246        );
247    }
248}