Skip to main content

rings_core/ecc/signers/bls/
mod.rs

1//! signer of bls
2//! A module for signing messages using BLS (Boneh-Lynn-Shacham) and secp256k1-compatible secret key bytes.
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::PrimeGroup;
19use ark_ff::fields::field_hashers::DefaultFieldHasher;
20use ark_serialize::CanonicalDeserialize;
21use ark_serialize::CanonicalSerialize;
22use ark_std::UniformRand;
23use rand::SeedableRng;
24use rand_hc::Hc128Rng;
25
26use crate::ecc::PublicKey;
27use crate::ecc::SecretKey;
28use crate::error::Error;
29use crate::error::Result;
30
31/// this is from `<https://docs.rs/bls-signatures/latest/src/bls_signatures/signature.rs.html#24>`
32const CSUITE: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
33
34/// Represents a BLS signature, stored as a 96-byte array.
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct Signature(pub [u8; 96]);
37
38/// this function is used to generate a random secret key
39pub fn random_sk() -> Result<SecretKey> {
40    let mut rng = Hc128Rng::from_entropy();
41    Fr::rand(&mut rng).try_into()
42}
43
44fn from_compressed<T: CanonicalDeserialize, const S: usize>(a: &[u8; S]) -> Result<T> {
45    T::deserialize_compressed(&a[..]).map_err(|_| Error::EccDeserializeFailed)
46}
47
48fn to_compressed<T: CanonicalSerialize, const S: usize>(s: &T) -> Result<[u8; S]> {
49    let mut data: Vec<u8> = vec![];
50    s.serialize_compressed(&mut data)
51        .map_err(|_| Error::EccSerializeFailed)?;
52    assert_eq!(s.compressed_size(), S);
53    assert_eq!(data.len(), S);
54    let ret: [u8; S] = data.try_into().map_err(|_| Error::EccSerializeFailed)?;
55    Ok(ret)
56}
57
58impl TryFrom<SecretKey> for Fr {
59    type Error = Error;
60    fn try_from(sk: SecretKey) -> Result<Fr> {
61        let data: [u8; 32] = sk.ser();
62        let ret: Fr = from_compressed(&data)?;
63        Ok(ret)
64    }
65}
66
67impl TryFrom<Fr> for SecretKey {
68    type Error = Error;
69    fn try_from(sk: Fr) -> Result<SecretKey> {
70        let data: [u8; 32] = to_compressed(&sk)?;
71        SecretKey::from_bytes(data)
72    }
73}
74
75impl TryFrom<Signature> for G2Projective {
76    type Error = Error;
77    fn try_from(s: Signature) -> Result<Self> {
78        from_compressed(&s.0)
79    }
80}
81
82impl TryFrom<G2Projective> for Signature {
83    type Error = Error;
84    fn try_from(s: G2Projective) -> Result<Self> {
85        Ok(Signature(to_compressed(&s)?))
86    }
87}
88
89impl TryFrom<G1Projective> for PublicKey<48> {
90    type Error = Error;
91    fn try_from(p: G1Projective) -> Result<Self> {
92        Ok(PublicKey(to_compressed::<G1Projective, 48>(&p)?))
93    }
94}
95
96impl TryFrom<PublicKey<48>> for G1Projective {
97    type Error = Error;
98    fn try_from(pk: PublicKey<48>) -> Result<Self> {
99        let data: [u8; 48] = pk.0;
100        let ret: Self = from_compressed(&data)?;
101        Ok(ret)
102    }
103}
104
105/// Hashes a message to a 96-byte array using BLS
106/// `<https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/>`
107pub fn hash_to_curve(msg: &[u8]) -> Result<[u8; 96]> {
108    // let swu_map: WBMap<G1Config> = WBMap::new().unwrap();
109    let hasher = MapToCurveBasedHasher::<
110        G2Projective,
111        DefaultFieldHasher<sha2::Sha256, 128>,
112        WBMap<G2Config>,
113    >::new(CSUITE)
114    .map_err(|_| Error::CurveHasherInitFailed)?;
115    let hashed = hasher.hash(msg).map_err(|_| Error::CurveHasherFailed)?;
116    let ret: [u8; 96] = to_compressed(&hashed)?;
117    Ok(ret)
118}
119
120/// Sign hashed message with bls privatekey
121pub fn sign_hash(sk: SecretKey, hashed_msg: &[u8; 96]) -> Result<Signature> {
122    let sk: Fr = sk.try_into()?;
123    let msg: G2Projective = from_compressed(hashed_msg)?;
124    Ok(Signature(to_compressed(&(msg * sk))?))
125}
126
127/// Sign message with bls privatekey
128/// signature = hash_into_g2(message) * sk
129pub fn sign(sk: SecretKey, msg: &[u8]) -> Result<Signature> {
130    let sk: Fr = sk.try_into()?;
131    let hashed_msg = hash_to_curve(msg)?;
132    let msg: G2Projective = from_compressed(&hashed_msg)?;
133    Ok(Signature(to_compressed(&(msg * sk))?))
134}
135
136/// Verifies that the signature is the actual aggregated signature of hashes - pubkeys. Calculated by
137/// e(g1, signature) == \prod_{i = 0}^n e(pk_i, hash_i).
138pub fn verify_hash(hashes: &[[u8; 96]], sig: &Signature, pks: &[PublicKey<48>]) -> Result<bool> {
139    let sig: G2Projective = sig.clone().try_into()?;
140    let g1 = G1Projective::generator();
141    let e1 = Bls12_381::pairing(g1, sig);
142
143    let hashes: Vec<G2Projective> = hashes
144        .iter()
145        .map(from_compressed)
146        .collect::<Result<Vec<G2Projective>>>()?;
147
148    let pks: Vec<G1Projective> = pks
149        .iter()
150        .map(|pk| (*pk).try_into())
151        .collect::<Result<Vec<G1Projective>>>()?;
152
153    let mm_out = Bls12_381::multi_miller_loop(pks, hashes);
154    if let Some(e2) = Bls12_381::final_exponentiation(mm_out) {
155        Ok(e1 == e2)
156    } else {
157        Ok(false)
158    }
159}
160
161/// Verifies that the signature is the actual aggregated signature of messages - pubkeys. Calculated by
162/// e(g1, signature) == \prod_{i = 0}^n e(pk_i, hash_to_curve(message_i)).
163pub fn verify(msgs: &[&[u8]], sig: &Signature, pks: &[PublicKey<48>]) -> Result<bool> {
164    let hashes: Vec<[u8; 96]> = msgs
165        .iter()
166        .map(|msg| hash_to_curve(msg))
167        .collect::<Result<Vec<[u8; 96]>>>()?;
168    verify_hash(hashes.as_slice(), sig, pks)
169}
170
171/// Aggregate signatures by multiplying them together.
172///
173/// 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_bls;