Skip to main content

fvm_shared/crypto/
signature.rs

1// Copyright 2021-2023 Protocol Labs
2// Copyright 2019-2022 ChainSafe Systems
3// SPDX-License-Identifier: Apache-2.0, MIT
4
5use std::borrow::Cow;
6use std::error;
7
8use fvm_ipld_encoding::repr::*;
9use fvm_ipld_encoding::{Error as EncodingError, de, ser, strict_bytes};
10use num_derive::FromPrimitive;
11use num_traits::FromPrimitive;
12use thiserror::Error;
13
14use crate::address::Error as AddressError;
15
16/// BLS signature length in bytes.
17pub const BLS_SIG_LEN: usize = 96;
18/// BLS Public key length in bytes.
19pub const BLS_PUB_LEN: usize = 48;
20
21/// Secp256k1 signature length in bytes.
22pub const SECP_SIG_LEN: usize = 65;
23/// Secp256k1 Public key length in bytes.
24pub const SECP_PUB_LEN: usize = 65;
25/// Length of the signature input message hash in bytes (32).
26pub const SECP_SIG_MESSAGE_HASH_SIZE: usize = 32;
27
28/// Signature variants for Filecoin signatures.
29#[derive(
30    Clone, Debug, PartialEq, FromPrimitive, Copy, Eq, Serialize_repr, Deserialize_repr, Hash,
31)]
32#[repr(u8)]
33pub enum SignatureType {
34    Secp256k1 = 1,
35    BLS = 2,
36}
37
38/// A cryptographic signature, represented in bytes, of any key protocol.
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub struct Signature {
41    pub sig_type: SignatureType,
42    pub bytes: Vec<u8>,
43}
44
45impl ser::Serialize for Signature {
46    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47    where
48        S: ser::Serializer,
49    {
50        let mut bytes = Vec::with_capacity(self.bytes.len() + 1);
51        // Insert signature type byte
52        bytes.push(self.sig_type as u8);
53        bytes.extend_from_slice(&self.bytes);
54
55        strict_bytes::Serialize::serialize(&bytes, serializer)
56    }
57}
58
59impl<'de> de::Deserialize<'de> for Signature {
60    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61    where
62        D: de::Deserializer<'de>,
63    {
64        let bytes: Cow<'de, [u8]> = strict_bytes::Deserialize::deserialize(deserializer)?;
65        if bytes.is_empty() {
66            return Err(de::Error::custom("Cannot deserialize empty bytes"));
67        }
68
69        // Remove signature type byte
70        let sig_type = SignatureType::from_u8(bytes[0])
71            .ok_or_else(|| de::Error::custom("Invalid signature type byte (must be 1 or 2)"))?;
72
73        Ok(Signature {
74            bytes: bytes[1..].to_vec(),
75            sig_type,
76        })
77    }
78}
79
80impl Signature {
81    /// Creates a SECP Signature given the raw bytes.
82    pub fn new_secp256k1(bytes: Vec<u8>) -> Self {
83        Self {
84            sig_type: SignatureType::Secp256k1,
85            bytes,
86        }
87    }
88
89    /// Creates a BLS Signature given the raw bytes.
90    pub fn new_bls(bytes: Vec<u8>) -> Self {
91        Self {
92            sig_type: SignatureType::BLS,
93            bytes,
94        }
95    }
96
97    /// Returns reference to signature bytes.
98    pub fn bytes(&self) -> &[u8] {
99        &self.bytes
100    }
101
102    /// Returns [SignatureType] for the signature.
103    pub fn signature_type(&self) -> SignatureType {
104        self.sig_type
105    }
106}
107
108#[cfg(feature = "arb")]
109impl quickcheck::Arbitrary for SignatureType {
110    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
111        if bool::arbitrary(g) {
112            SignatureType::Secp256k1
113        } else {
114            SignatureType::BLS
115        }
116    }
117}
118
119#[cfg(feature = "arb")]
120impl quickcheck::Arbitrary for Signature {
121    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
122        Self {
123            bytes: Vec::arbitrary(g),
124            sig_type: SignatureType::arbitrary(g),
125        }
126    }
127}
128
129#[cfg(feature = "crypto")]
130impl Signature {
131    /// Checks if a signature is valid given data and address.
132    pub fn verify(&self, data: &[u8], addr: &crate::address::Address) -> Result<(), String> {
133        verify(self.sig_type, &self.bytes, data, addr)
134    }
135}
136
137#[cfg(feature = "crypto")]
138pub fn verify(
139    sig_type: SignatureType,
140    sig_data: &[u8],
141    data: &[u8],
142    addr: &crate::address::Address,
143) -> Result<(), String> {
144    match sig_type {
145        SignatureType::BLS => self::ops::verify_bls_sig(sig_data, data, addr),
146        SignatureType::Secp256k1 => self::ops::verify_secp256k1_sig(sig_data, data, addr),
147    }
148}
149
150#[cfg(feature = "crypto")]
151pub mod ops {
152    use bls_signatures::{
153        PublicKey as BlsPubKey, Serialize, Signature as BlsSignature, verify_messages,
154    };
155    use k256::ecdsa::{RecoveryId, Signature as EcdsaSignature, VerifyingKey};
156
157    use super::{Error, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE};
158    use crate::address::{Address, Protocol};
159
160    /// Returns `String` error if a bls signature is invalid.
161    pub fn verify_bls_sig(signature: &[u8], data: &[u8], addr: &Address) -> Result<(), String> {
162        if addr.protocol() != Protocol::BLS {
163            return Err(format!(
164                "cannot validate a BLS signature against a {} address",
165                addr.protocol()
166            ));
167        }
168
169        let pub_k = addr.payload_bytes();
170
171        // generate public key object from bytes
172        let pk = BlsPubKey::from_bytes(&pub_k).map_err(|e| e.to_string())?;
173
174        // generate signature struct from bytes
175        let sig = BlsSignature::from_bytes(signature).map_err(|e| e.to_string())?;
176
177        // BLS verify hash against key
178        if verify_messages(&sig, &[data], &[pk]) {
179            Ok(())
180        } else {
181            Err(format!(
182                "bls signature verification failed for addr: {}",
183                addr
184            ))
185        }
186    }
187
188    /// Verifies an aggregated BLS signature. Returns `Ok(false)` if signature verification fails
189    /// and `String` error if arguments are invalid.
190    pub fn verify_bls_aggregate(
191        aggregate_sig: &[u8; super::BLS_SIG_LEN],
192        pub_keys: &[[u8; super::BLS_PUB_LEN]],
193        plaintexts: &[&[u8]],
194    ) -> Result<bool, String> {
195        // If the number of public keys and data does not match, return false;
196        let (num_pub_keys, num_plaintexts) = (pub_keys.len(), plaintexts.len());
197        if num_pub_keys != num_plaintexts {
198            return Err(format!(
199                "unequal numbers of public keys ({num_pub_keys}) and plaintexts ({num_plaintexts})",
200            ));
201        }
202        if num_pub_keys == 0 {
203            return Ok(true);
204        }
205
206        // Deserialize signature bytes into a curve point.
207        let sig = BlsSignature::from_bytes(aggregate_sig)
208            .map_err(|_| "bls aggregate signature bytes are invalid G2 curve point".to_string())?;
209
210        // Deserialize each public key's bytes into a curve point.
211        let pub_keys = pub_keys
212            .iter()
213            .map(|pub_key| BlsPubKey::from_bytes(pub_key.as_slice()))
214            .collect::<Result<Vec<_>, _>>()
215            .map_err(|_| "bls public key bytes are invalid G2 curve point".to_string())?;
216
217        Ok(bls_signatures::verify_messages(&sig, plaintexts, &pub_keys))
218    }
219
220    /// Returns `String` error if a secp256k1 signature is invalid.
221    pub fn verify_secp256k1_sig(
222        signature: &[u8],
223        data: &[u8],
224        addr: &Address,
225    ) -> Result<(), String> {
226        if addr.protocol() != Protocol::Secp256k1 {
227            return Err(format!(
228                "cannot validate a secp256k1 signature against a {} address",
229                addr.protocol()
230            ));
231        }
232
233        if signature.len() != SECP_SIG_LEN {
234            return Err(format!(
235                "Invalid Secp256k1 signature length. Was {}, must be 65",
236                signature.len()
237            ));
238        }
239
240        // blake2b 256 hash
241        let hash = blake2b_simd::Params::new()
242            .hash_length(32)
243            .to_state()
244            .update(data)
245            .finalize();
246
247        // Ecrecover with hash and signature
248        let mut sig = [0u8; SECP_SIG_LEN];
249        sig[..].copy_from_slice(signature);
250        let rec_addr = ecrecover(hash.as_bytes().try_into().expect("fixed array size"), &sig)
251            .map_err(|e| e.to_string())?;
252
253        // check address against recovered address
254        if &rec_addr == addr {
255            Ok(())
256        } else {
257            Err("Secp signature verification failed".to_owned())
258        }
259    }
260
261    /// Return the public key used for signing a message given it's signing bytes hash and signature.
262    pub fn recover_secp_public_key(
263        hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE],
264        signature: &[u8; SECP_SIG_LEN],
265    ) -> Result<[u8; SECP_PUB_LEN], Error> {
266        // Extract recovery ID from the last byte
267        let mut rec_byte = signature[64];
268
269        // Create signature from the first 64 bytes
270        let mut signature = EcdsaSignature::from_slice(&signature[..64])
271            .map_err(|e| Error::SigningError(format!("Invalid signature: {}", e)))?;
272
273        // Normalize the signature & recovery byte (required for Ethereum compatibility).
274        if let Some(normalized) = signature.normalize_s() {
275            signature = normalized;
276            rec_byte ^= 1;
277        }
278
279        // Extract recovery ID from the last byte
280        let recovery_id = RecoveryId::try_from(rec_byte)
281            .map_err(|e| Error::InvalidRecovery(format!("Invalid recovery ID: {}", e)))?;
282
283        // Recover the verifying key
284        let pk = VerifyingKey::recover_from_prehash(&hash[..], &signature, recovery_id)
285            .map_err(|e| Error::InvalidRecovery(format!("Failed to recover key: {}", e)))?;
286        Ok(pk
287            .to_encoded_point(false)
288            .as_bytes()
289            .try_into()
290            .expect("expected the key to be 65 bytes"))
291    }
292
293    /// Return Address for a message given it's signing bytes hash and signature.
294    pub fn ecrecover(hash: &[u8; 32], signature: &[u8; SECP_SIG_LEN]) -> Result<Address, Error> {
295        // recover public key from a message hash and secp signature.
296        let key = recover_secp_public_key(hash, signature)?;
297        let addr = Address::new_secp256k1(&key)?;
298        Ok(addr)
299    }
300}
301
302#[cfg(all(test, feature = "crypto"))]
303mod tests {
304    use bls_signatures::{PrivateKey, Serialize, Signature as BlsSignature};
305    use k256::ecdsa::SigningKey;
306    use multihash_codetable::Code;
307    use multihash_codetable::MultihashDigest;
308    use rand::{Rng, SeedableRng};
309    use rand_chacha::ChaCha8Rng;
310
311    use super::ops::recover_secp_public_key;
312    use super::*;
313    use crate::Address;
314    use crate::crypto::signature::ops::{ecrecover, verify_bls_aggregate};
315
316    #[test]
317    fn bls_agg_verify() {
318        // The number of signatures in aggregate
319        let num_sigs = 10;
320        let message_length = num_sigs * 64;
321
322        let rng = &mut ChaCha8Rng::seed_from_u64(11);
323
324        let msg = (0..message_length)
325            .map(|_| rng.r#gen())
326            .collect::<Vec<u8>>();
327        let data: Vec<&[u8]> = (0..num_sigs).map(|x| &msg[x * 64..(x + 1) * 64]).collect();
328
329        let private_keys: Vec<PrivateKey> =
330            (0..num_sigs).map(|_| PrivateKey::generate(rng)).collect();
331        let public_keys: Vec<[u8; BLS_PUB_LEN]> = private_keys
332            .iter()
333            .map(|x| {
334                x.public_key()
335                    .as_bytes()
336                    .try_into()
337                    .expect("public key bytes to array conversion should not fail")
338            })
339            .collect();
340
341        let signatures: Vec<BlsSignature> = (0..num_sigs)
342            .map(|x| private_keys[x].sign(data[x]))
343            .collect();
344
345        let agg_sig: [u8; BLS_SIG_LEN] = bls_signatures::aggregate(&signatures)
346            .expect("bls signature aggregation should not fail")
347            .as_bytes()
348            .try_into()
349            .expect("bls aggregate signature to bytes array should not fail");
350
351        assert!(verify_bls_aggregate(&agg_sig, &public_keys, &data).unwrap());
352    }
353
354    #[test]
355    fn recover_pubkey() {
356        let rng = &mut ChaCha8Rng::seed_from_u64(8);
357
358        // Create a random signing key
359        let signing_key = SigningKey::random(rng);
360        let verifying_key = signing_key.verifying_key();
361
362        let hash: [u8; 32] = blake2b_simd::Params::new()
363            .hash_length(32)
364            .to_state()
365            .update(&[42, 43])
366            .finalize()
367            .as_bytes()
368            .try_into()
369            .expect("fixed array size");
370
371        // Sign the digest
372        let (signature, recovery_id) = signing_key
373            .sign_prehash_recoverable(&hash)
374            .expect("signing should not fail");
375
376        // Create a 65-byte signature with recovery ID
377        let mut sig_bytes = [0u8; 65];
378        sig_bytes[..64].copy_from_slice(&signature.to_bytes());
379        sig_bytes[64] = recovery_id.to_byte();
380
381        // Recover the key and verify it matches
382        let recovered_key = recover_secp_public_key(&hash, &sig_bytes).unwrap();
383        let encoded_point = verifying_key.to_encoded_point(false);
384        let target_key = encoded_point.as_bytes();
385        assert_eq!(target_key, &recovered_key[..]);
386    }
387
388    // Tests malleability.
389    #[test]
390    fn secp_ecrecover_testvector() {
391        let hash: [u8; 32] =
392            hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c")
393                .unwrap()
394                .try_into()
395                .unwrap();
396        let recbyte = 0x1c - 27;
397        let r = hex::decode("73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f")
398            .unwrap();
399        let s = hex::decode("eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549")
400            .unwrap();
401        let expected = hex::decode("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap();
402
403        let mut sig = [0u8; SECP_SIG_LEN];
404        sig[..32].copy_from_slice(&r);
405        sig[32..64].copy_from_slice(&s);
406        sig[64] = recbyte;
407
408        let recovered = recover_secp_public_key(&hash, &sig).unwrap();
409        let hashed = Code::Keccak256.digest(&recovered[1..]);
410        assert_eq!(expected, &hashed.digest()[12..]);
411    }
412
413    #[test]
414    fn secp_ecrecover() {
415        let rng = &mut ChaCha8Rng::seed_from_u64(8);
416
417        // Create a random signing key
418        let signing_key = SigningKey::random(rng);
419        let verifying_key = signing_key.verifying_key();
420
421        // Get the encoded public key and create an address
422        let encoded_point = verifying_key.to_encoded_point(false);
423        let secp_addr = Address::new_secp256k1(encoded_point.as_bytes()).unwrap();
424
425        let hash: [u8; 32] = blake2b_simd::Params::new()
426            .hash_length(32)
427            .to_state()
428            .update(&[8, 8])
429            .finalize()
430            .as_bytes()
431            .try_into()
432            .expect("fixed array size");
433
434        // Sign the digest
435        let (signature, recovery_id) = signing_key
436            .sign_prehash_recoverable(&hash)
437            .expect("signing should not fail");
438
439        // Create a 65-byte signature with recovery ID
440        let mut sig_bytes = [0u8; 65];
441        sig_bytes[..64].copy_from_slice(&signature.to_bytes());
442        sig_bytes[64] = recovery_id.to_byte();
443
444        assert_eq!(ecrecover(&hash, &sig_bytes).unwrap(), secp_addr);
445    }
446}
447
448/// Crypto error
449#[derive(Debug, PartialEq, Eq, Error)]
450pub enum Error {
451    /// Failed to produce a signature
452    #[error("Failed to sign data {0}")]
453    SigningError(String),
454    /// Unable to perform ecrecover with the given params
455    #[error("Could not recover public key from signature: {0}")]
456    InvalidRecovery(String),
457    /// Provided public key is not understood
458    #[error("Invalid generated pub key to create address: {0}")]
459    InvalidPubKey(#[from] AddressError),
460}
461
462impl From<Box<dyn error::Error>> for Error {
463    fn from(err: Box<dyn error::Error>) -> Error {
464        // Pass error encountered in signer trait as module error type
465        Error::SigningError(err.to_string())
466    }
467}
468
469impl From<EncodingError> for Error {
470    fn from(err: EncodingError) -> Error {
471        // Pass error encountered in signer trait as module error type
472        Error::SigningError(err.to_string())
473    }
474}