diem_crypto/
bls.rs

1// Copyright 2021 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use crate::{
6    hash::{CryptoHash, CryptoHasher},
7    traits::*,
8    CryptoMaterialError, PrivateKey, PublicKey, Signature, SigningKey, Uniform,
9    ValidCryptoMaterial, ValidCryptoMaterialStringExt, VerifyingKey,
10};
11use anyhow::{anyhow, Result};
12use bls_signatures::{
13    DeserializeUnchecked, PrivateKey as RawPrivateKey,
14    PublicKey as RawPublicKey, Serialize as BLSSerialize,
15    Signature as RawSignature,
16};
17use diem_crypto_derive::{
18    DeserializeKey, SerializeKey, SilentDebug, SilentDisplay,
19};
20use diem_logger::prelude::*;
21use mirai_annotations::*;
22use serde::{Deserialize, Deserializer, Serialize};
23use std::convert::TryFrom;
24
25#[cfg(mirai)]
26use crate::tags::ValidatedPublicKeyTag;
27use std::fmt::{self, Formatter};
28
29/// Private key length in bytes. The actual key length should be 255 bits.
30pub const BLS_PRIVATE_KEY_LENGTH: usize = 32;
31/// Public key length in bytes.
32pub const BLS_PUBLIC_KEY_LENGTH: usize = 48;
33/// Signature length in bytes.
34pub const BLS_SIGNATURE_LENGTH: usize = 96;
35
36#[cfg(not(mirai))]
37struct ValidatedPublicKeyTag {}
38
39/// BLS signature private key
40#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)]
41pub struct BLSPrivateKey(RawPrivateKey);
42
43#[cfg(feature = "assert-private-keys-not-cloneable")]
44static_assertions::assert_not_impl_any!(BLSPrivateKey: Clone);
45
46#[cfg(any(test, feature = "cloneable-private-keys"))]
47impl Clone for BLSPrivateKey {
48    fn clone(&self) -> Self {
49        let serialized: &[u8] = &(self.to_bytes());
50        BLSPrivateKey::try_from(serialized).unwrap()
51    }
52}
53
54/// BLS signature public key
55#[derive(DeserializeKey, Clone, SerializeKey)]
56pub struct BLSPublicKey(RawPublicKey);
57
58// TODO(lpl): Signature aggregation.
59/// BLS signature wrapper
60#[derive(DeserializeKey, Clone, SerializeKey)]
61pub struct BLSSignature(RawSignature);
62
63impl BLSPrivateKey {
64    ///
65    pub fn raw_key(self) -> RawPrivateKey { self.0 }
66}
67
68impl PartialEq<Self> for BLSPrivateKey {
69    fn eq(&self, other: &Self) -> bool { self.to_bytes() == other.to_bytes() }
70}
71
72impl Eq for BLSPrivateKey {}
73
74impl SigningKey for BLSPrivateKey {
75    type SignatureMaterial = BLSSignature;
76    type VerifyingKeyMaterial = BLSPublicKey;
77
78    fn sign<T: CryptoHash + Serialize>(
79        &self, message: &T,
80    ) -> Self::SignatureMaterial {
81        let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
82        bcs::serialize_into(&mut bytes, &message)
83            .map_err(|_| CryptoMaterialError::SerializationError)
84            .expect("Serialization of signable material should not fail.");
85        BLSSignature(self.0.sign(bytes))
86    }
87
88    #[cfg(any(test, feature = "fuzzing"))]
89    fn sign_arbitrary_message(
90        &self, message: &[u8],
91    ) -> Self::SignatureMaterial {
92        BLSSignature(self.0.sign(message))
93    }
94}
95
96impl From<RawPublicKey> for BLSPublicKey {
97    fn from(raw: RawPublicKey) -> Self { BLSPublicKey(raw) }
98}
99
100impl VerifyingKey for BLSPublicKey {
101    type SignatureMaterial = BLSSignature;
102    type SigningKeyMaterial = BLSPrivateKey;
103}
104
105impl PrivateKey for BLSPrivateKey {
106    type PublicKeyMaterial = BLSPublicKey;
107}
108
109impl BLSPublicKey {
110    /// return raw public key
111    pub fn raw(self) -> RawPublicKey { self.0 }
112}
113
114impl BLSSignature {
115    /// return an all-zero signature (for test only)
116    #[cfg(any(test, feature = "fuzzing"))]
117    pub fn dummy_signature() -> Self {
118        let bytes = [0u8; BLS_SIGNATURE_LENGTH];
119        Self::try_from(&bytes[..]).unwrap()
120    }
121
122    /// return raw signature
123    pub fn raw(self) -> RawSignature { self.0 }
124}
125
126impl Signature for BLSSignature {
127    type SigningKeyMaterial = BLSPrivateKey;
128    type VerifyingKeyMaterial = BLSPublicKey;
129
130    fn verify<T: CryptoHash + Serialize>(
131        &self, message: &T, public_key: &Self::VerifyingKeyMaterial,
132    ) -> Result<()> {
133        let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
134        bcs::serialize_into(&mut bytes, &message)
135            .map_err(|_| CryptoMaterialError::SerializationError)?;
136        self.verify_arbitrary_msg(&bytes, public_key)
137    }
138
139    fn verify_arbitrary_msg(
140        &self, message: &[u8], public_key: &Self::VerifyingKeyMaterial,
141    ) -> Result<()> {
142        precondition!(has_tag!(public_key, ValidatedPublicKeyTag));
143        match bls_signatures::verify_messages(
144            &self.0,
145            std::slice::from_ref(&message),
146            std::slice::from_ref(&public_key.0),
147        ) {
148            true => Ok(()),
149            false => Err(anyhow!("Invalid BLS signature!")),
150        }
151    }
152}
153
154impl PublicKey for BLSPublicKey {
155    type PrivateKeyMaterial = BLSPrivateKey;
156}
157
158impl From<&BLSPrivateKey> for BLSPublicKey {
159    fn from(private_key: &BLSPrivateKey) -> Self {
160        BLSPublicKey(private_key.0.public_key())
161    }
162}
163
164impl From<&RawPrivateKey> for BLSPrivateKey {
165    fn from(raw_private_key: &RawPrivateKey) -> Self {
166        BLSPrivateKey(*raw_private_key)
167    }
168}
169
170impl From<RawPrivateKey> for BLSPrivateKey {
171    fn from(raw_private_key: RawPrivateKey) -> Self {
172        BLSPrivateKey(raw_private_key)
173    }
174}
175
176impl From<&RawSignature> for BLSSignature {
177    fn from(raw_signature: &RawSignature) -> Self {
178        BLSSignature(*raw_signature)
179    }
180}
181
182impl From<RawSignature> for BLSSignature {
183    fn from(raw_signature: RawSignature) -> Self { BLSSignature(raw_signature) }
184}
185
186impl TryFrom<&[u8]> for BLSPrivateKey {
187    type Error = CryptoMaterialError;
188
189    /// Deserialize an BLSPrivateKey. This method will also check for key
190    /// validity.
191    fn try_from(
192        bytes: &[u8],
193    ) -> std::result::Result<BLSPrivateKey, CryptoMaterialError> {
194        match RawPrivateKey::from_bytes(bytes) {
195            Ok(sig) => Ok(Self(sig)),
196            Err(_) => Err(CryptoMaterialError::DeserializationError),
197        }
198    }
199}
200
201impl TryFrom<&[u8]> for BLSPublicKey {
202    type Error = CryptoMaterialError;
203
204    /// Deserialize an BLSPrivateKey. This method will also check for key
205    /// validity.
206    fn try_from(
207        bytes: &[u8],
208    ) -> std::result::Result<BLSPublicKey, CryptoMaterialError> {
209        match RawPublicKey::from_bytes(bytes) {
210            Ok(sig) => Ok(Self(sig)),
211            Err(e) => {
212                diem_debug!(
213                    "BLSPublicKey debug error: bytes={:?}, err={:?}",
214                    bytes,
215                    e
216                );
217                Err(CryptoMaterialError::DeserializationError)
218            }
219        }
220    }
221}
222
223impl TryFrom<&[u8]> for BLSSignature {
224    type Error = CryptoMaterialError;
225
226    /// Deserialize an BLSPrivateKey. This method will also check for key
227    /// validity.
228    fn try_from(
229        bytes: &[u8],
230    ) -> std::result::Result<BLSSignature, CryptoMaterialError> {
231        // TODO(lpl): Check malleability?
232        match RawSignature::from_bytes(bytes) {
233            Ok(sig) => Ok(Self(sig)),
234            Err(_) => Err(CryptoMaterialError::DeserializationError),
235        }
236    }
237}
238
239impl std::hash::Hash for BLSPublicKey {
240    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
241        let encoded_pubkey = self.to_bytes();
242        state.write(&encoded_pubkey);
243    }
244}
245
246impl PartialEq for BLSPublicKey {
247    fn eq(&self, other: &BLSPublicKey) -> bool {
248        self.to_bytes() == other.to_bytes()
249    }
250}
251
252impl std::hash::Hash for BLSSignature {
253    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
254        let encoded_pubkey = ValidCryptoMaterial::to_bytes(self);
255        state.write(&encoded_pubkey);
256    }
257}
258
259impl PartialEq for BLSSignature {
260    fn eq(&self, other: &BLSSignature) -> bool {
261        self.to_bytes() == other.to_bytes()
262    }
263}
264
265impl Eq for BLSPublicKey {}
266
267impl Eq for BLSSignature {}
268
269impl ValidCryptoMaterial for BLSPrivateKey {
270    fn to_bytes(&self) -> Vec<u8> { self.0.as_bytes() }
271}
272
273impl Genesis for BLSPrivateKey {
274    fn genesis() -> Self {
275        let mut buf = [0u8; BLS_PRIVATE_KEY_LENGTH];
276        buf[BLS_PRIVATE_KEY_LENGTH - 1] = 1;
277        Self::try_from(buf.as_ref()).unwrap()
278    }
279}
280
281impl ValidCryptoMaterial for BLSPublicKey {
282    fn to_bytes(&self) -> Vec<u8> { self.0.as_bytes() }
283}
284
285impl ValidCryptoMaterial for BLSSignature {
286    fn to_bytes(&self) -> Vec<u8> { self.0.as_bytes() }
287}
288
289impl fmt::Display for BLSPublicKey {
290    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
291        write!(f, "{}", self.to_encoded_string().map_err(|_| fmt::Error)?)
292    }
293}
294
295impl Uniform for BLSPrivateKey {
296    fn generate<R>(rng: &mut R) -> Self
297    where R: ::rand::RngCore + ::rand::CryptoRng {
298        BLSPrivateKey(RawPrivateKey::generate(rng))
299    }
300}
301
302impl fmt::Debug for BLSPublicKey {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        write!(f, "BLSPublicKey({})", self)
305    }
306}
307
308impl fmt::Display for BLSSignature {
309    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
310        write!(f, "{}", self.to_encoded_string().map_err(|_| fmt::Error)?)
311    }
312}
313
314impl fmt::Debug for BLSSignature {
315    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316        write!(f, "BLSSignature({})", self)
317    }
318}
319
320/// Used to deserialize keys in local storage whose validity has been checked
321/// before.
322#[derive(SerializeKey, DeserializeKey)]
323pub struct BLSPublicKeyUnchecked(RawPublicKey);
324/// Used to deserialize keys in local storage whose validity has been checked
325/// before.
326#[derive(SerializeKey, DeserializeKey)]
327pub struct BLSSignatureUnchecked(RawSignature);
328
329impl TryFrom<&[u8]> for BLSPublicKeyUnchecked {
330    type Error = CryptoMaterialError;
331
332    /// Deserialize an BLSPrivateKey. This method will also check for key
333    /// validity.
334    fn try_from(
335        bytes: &[u8],
336    ) -> std::result::Result<BLSPublicKeyUnchecked, CryptoMaterialError> {
337        match RawPublicKey::from_bytes_unchecked(bytes) {
338            Ok(sig) => Ok(Self(sig)),
339            Err(e) => {
340                diem_debug!(
341                    "BLSPublicKey debug error: bytes={:?}, err={:?}",
342                    bytes,
343                    e
344                );
345                Err(CryptoMaterialError::DeserializationError)
346            }
347        }
348    }
349}
350
351impl TryFrom<&[u8]> for BLSSignatureUnchecked {
352    type Error = CryptoMaterialError;
353
354    /// Deserialize an BLSPrivateKey. This method will also check for key
355    /// validity.
356    fn try_from(
357        bytes: &[u8],
358    ) -> std::result::Result<BLSSignatureUnchecked, CryptoMaterialError> {
359        // TODO(lpl): Check malleability?
360        match RawSignature::from_bytes_unchecked(bytes) {
361            Ok(sig) => Ok(Self(sig)),
362            Err(_) => Err(CryptoMaterialError::DeserializationError),
363        }
364    }
365}
366
367impl ValidCryptoMaterial for BLSPublicKeyUnchecked {
368    fn to_bytes(&self) -> Vec<u8> { self.0.as_bytes() }
369}
370
371impl ValidCryptoMaterial for BLSSignatureUnchecked {
372    fn to_bytes(&self) -> Vec<u8> { self.0.as_bytes() }
373}
374
375impl From<BLSPublicKeyUnchecked> for BLSPublicKey {
376    fn from(unchecked: BLSPublicKeyUnchecked) -> Self { Self(unchecked.0) }
377}
378
379impl From<BLSSignatureUnchecked> for BLSSignature {
380    fn from(unchecked: BLSSignatureUnchecked) -> Self { Self(unchecked.0) }
381}
382
383/// Deserialize public key from local storage.
384pub fn deserialize_bls_public_key_unchecked<'de, D>(
385    deserializer: D,
386) -> Result<BLSPublicKey, D::Error>
387where D: Deserializer<'de> {
388    BLSPublicKeyUnchecked::deserialize(deserializer).map(Into::into)
389}
390
391#[cfg(any(test, feature = "fuzzing"))]
392use crate::test_utils::{self, KeyPair};
393
394/// Produces a uniformly random bls keypair from a seed
395#[cfg(any(test, feature = "fuzzing"))]
396pub fn keypair_strategy(
397) -> impl Strategy<Value = KeyPair<BLSPrivateKey, BLSPublicKey>> {
398    test_utils::uniform_keypair_strategy::<BLSPrivateKey, BLSPublicKey>()
399}
400
401#[cfg(any(test, feature = "fuzzing"))]
402use proptest::prelude::*;
403
404#[cfg(any(test, feature = "fuzzing"))]
405impl proptest::arbitrary::Arbitrary for BLSPublicKey {
406    type Parameters = ();
407    type Strategy = BoxedStrategy<Self>;
408
409    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
410        crate::test_utils::uniform_keypair_strategy::<
411            BLSPrivateKey,
412            BLSPublicKey,
413        >()
414        .prop_map(|v| v.public_key)
415        .boxed()
416    }
417}
418
419#[cfg(test)]
420mod test {
421    use crate as diem_crypto;
422    use crate::{
423        bls::{BLSPrivateKey, BLSSignature},
424        SigningKey, Uniform, ValidCryptoMaterial,
425    };
426    use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
427    use serde::{Deserialize, Serialize};
428    use std::{convert::TryFrom, time::Instant};
429
430    #[derive(Debug, CryptoHasher, BCSCryptoHash, Serialize, Deserialize)]
431    pub struct TestDiemCrypto(pub String);
432    #[test]
433    fn test_bls_sig_decode() {
434        let sk = BLSPrivateKey::generate(&mut rand::thread_rng());
435        let sig = sk.sign(&TestDiemCrypto("".to_string()));
436        let sig_bytes = sig.to_bytes();
437        let start = Instant::now();
438        let _decoded = BLSSignature::try_from(sig_bytes.as_slice()).unwrap();
439        println!("Time elapsed: {} us", start.elapsed().as_micros());
440    }
441}