Skip to main content

forest/shim/
crypto.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3pub use super::fvm_shared_latest::{
4    self, IPLD_RAW, commcid::Commitment, crypto::signature::SECP_SIG_LEN,
5};
6use super::version::NetworkVersion;
7use crate::eth::{EthChainId, EthTx};
8use crate::message::{MessageRead as _, SignedMessage};
9use anyhow::{Context, ensure};
10use bls_signatures::{PublicKey as BlsPublicKey, Signature as BlsSignature};
11use cid::Cid;
12use fvm_ipld_encoding::{
13    de,
14    repr::{Deserialize_repr, Serialize_repr},
15    ser, strict_bytes,
16};
17pub use fvm_shared_latest::crypto::signature::BLS_SIG_LEN;
18pub use fvm_shared3::TICKET_RANDOMNESS_LOOKBACK;
19use get_size2::GetSize;
20use num::FromPrimitive;
21use num_derive::FromPrimitive;
22use schemars::JsonSchema;
23use std::borrow::Cow;
24
25/// A cryptographic signature, represented in bytes, of any key protocol.
26#[derive(Clone, Debug, PartialEq, Eq, Hash, GetSize, derive_more::Constructor)]
27#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
28pub struct Signature {
29    pub sig_type: SignatureType,
30    pub bytes: Vec<u8>,
31}
32
33impl ser::Serialize for Signature {
34    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35    where
36        S: ser::Serializer,
37    {
38        let mut bytes = Vec::with_capacity(self.bytes.len() + 1);
39        // Insert signature type byte
40        bytes.push(self.sig_type as u8);
41        bytes.extend_from_slice(&self.bytes);
42
43        strict_bytes::Serialize::serialize(&bytes, serializer)
44    }
45}
46
47impl<'de> de::Deserialize<'de> for Signature {
48    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
49    where
50        D: de::Deserializer<'de>,
51    {
52        let bytes: Cow<'de, [u8]> = strict_bytes::Deserialize::deserialize(deserializer)?;
53        match bytes.split_first() {
54            None => Err(de::Error::custom("Cannot deserialize empty bytes")),
55            Some((&sig_byte, rest)) => {
56                // Remove signature type byte
57                let sig_type = SignatureType::from_u8(sig_byte).ok_or_else(|| {
58                    de::Error::custom(format!(
59                        "Invalid signature type byte (must be 1, 2 or 3), was {sig_byte}"
60                    ))
61                })?;
62
63                Ok(Signature {
64                    bytes: rest.to_vec(),
65                    sig_type,
66                })
67            }
68        }
69    }
70}
71
72impl Signature {
73    /// Creates a BLS Signature given the raw bytes.
74    pub fn new_bls(bytes: Vec<u8>) -> Self {
75        Self {
76            sig_type: SignatureType::Bls,
77            bytes,
78        }
79    }
80
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 Delegated Signature given the raw bytes.
90    pub fn new_delegated(bytes: Vec<u8>) -> Self {
91        Self {
92            sig_type: SignatureType::Delegated,
93            bytes,
94        }
95    }
96
97    /// Creates a signature from bytes.
98    pub fn from_bytes(bytes: Vec<u8>) -> anyhow::Result<Self> {
99        if bytes.is_empty() {
100            anyhow::bail!("Empty signature bytes");
101        }
102
103        let first_byte = bytes
104            .first()
105            .ok_or_else(|| anyhow::anyhow!("Invalid signature bytes"))?;
106
107        let signature_data = bytes
108            .get(1..)
109            .ok_or_else(|| anyhow::anyhow!("Invalid signature bytes"))?
110            .to_vec();
111
112        // the first byte in signature represents the signature type
113        let sig_type = SignatureType::try_from(*first_byte)?;
114        match sig_type {
115            SignatureType::Secp256k1 => Ok(Self::new_secp256k1(signature_data)),
116            SignatureType::Bls => Ok(Self::new_bls(signature_data)),
117            SignatureType::Delegated => Ok(Self::new_delegated(signature_data)),
118        }
119    }
120
121    /// Returns the signature bytes including the signature type byte.
122    pub fn to_bytes(&self) -> Vec<u8> {
123        let mut bytes = Vec::with_capacity(self.bytes.len() + 1);
124        bytes.push(self.sig_type as u8);
125        bytes.extend_from_slice(&self.bytes);
126        bytes
127    }
128
129    pub fn signature_type(&self) -> SignatureType {
130        self.sig_type
131    }
132
133    /// Authenticates the message signature using protocol-specific validation:
134    /// - Delegated: Uses the Ethereum message with RLP encoding for signature verification, Verifies message roundtrip integrity
135    /// - BLS/SECP: Standard signature verification
136    pub fn authenticate_msg(
137        &self,
138        eth_chain_id: EthChainId,
139        msg: &SignedMessage,
140        addr: &crate::shim::address::Address,
141    ) -> anyhow::Result<()> {
142        match self.sig_type {
143            SignatureType::Delegated => {
144                let eth_tx = EthTx::from_signed_message(eth_chain_id, msg)?;
145                let filecoin_msg = eth_tx.get_unsigned_message(msg.from(), eth_chain_id)?;
146                ensure!(
147                    msg.message().cid() == filecoin_msg.cid(),
148                    "Ethereum transaction roundtrip mismatch"
149                );
150                // update the exiting signature bytes with the verifiable signature for delegated signature
151                let sig = Signature {
152                    bytes: eth_tx.to_verifiable_signature(Vec::from(self.bytes()), eth_chain_id)?,
153                    ..*self
154                };
155                // delegated uses rlp encoding for the message
156                let digest = eth_tx.rlp_unsigned_message(eth_chain_id)?;
157                sig.verify(&digest, addr)
158            }
159            _ => {
160                let digest = msg.message().cid().to_bytes();
161                self.verify(&digest, addr)
162            }
163        }
164    }
165
166    /// Checks if a signature is valid given data and address.
167    pub fn verify(&self, data: &[u8], addr: &crate::shim::address::Address) -> anyhow::Result<()> {
168        use super::fvm_shared_latest::crypto::signature::ops::{
169            verify_bls_sig, verify_secp256k1_sig,
170        };
171        match self.sig_type {
172            SignatureType::Bls => {
173                verify_bls_sig(&self.bytes, data, addr).map_err(anyhow::Error::msg)
174            }
175            SignatureType::Secp256k1 => {
176                verify_secp256k1_sig(&self.bytes, data, addr).map_err(anyhow::Error::msg)
177            }
178            SignatureType::Delegated => verify_delegated_sig(&self.bytes, data, addr),
179        }
180    }
181
182    /// Returns reference to signature bytes.
183    pub fn bytes(&self) -> &[u8] {
184        &self.bytes
185    }
186
187    /// Checks if the signature is a valid `secp256k1` signature type given the network version.
188    pub fn is_valid_secpk_sig_type(&self, network_version: NetworkVersion) -> bool {
189        if network_version < NetworkVersion::V18 {
190            matches!(self.sig_type, SignatureType::Secp256k1)
191        } else {
192            matches!(
193                self.sig_type,
194                SignatureType::Secp256k1 | SignatureType::Delegated
195            )
196        }
197    }
198}
199
200impl TryFrom<&Signature> for BlsSignature {
201    type Error = anyhow::Error;
202    fn try_from(value: &Signature) -> Result<Self, Self::Error> {
203        use bls_signatures::Serialize as _;
204
205        match value.sig_type {
206            SignatureType::Secp256k1 => {
207                anyhow::bail!("cannot convert Secp256k1 signature to bls signature")
208            }
209            SignatureType::Bls => Ok(BlsSignature::from_bytes(&value.bytes)?),
210            SignatureType::Delegated => {
211                anyhow::bail!("cannot convert delegated signature to bls signature")
212            }
213        }
214    }
215}
216
217// Forest's version of the `verify_bls_aggregate` function is semantically different
218// from the version in FVM.
219/// Aggregates and verifies BLS signatures collectively.
220pub fn verify_bls_aggregate(data: &[&[u8]], pub_keys: &[BlsPublicKey], sig: &Signature) -> bool {
221    // If the number of public keys and data does not match, then return false
222    if data.len() != pub_keys.len() {
223        return false;
224    }
225    if data.is_empty() {
226        return true;
227    }
228
229    let bls_sig = match sig.try_into() {
230        Ok(bls_sig) => bls_sig,
231        _ => return false,
232    };
233
234    // Does the aggregate verification
235    bls_signatures::verify_messages(&bls_sig, data, pub_keys)
236}
237
238/// Returns `String` error if a BLS signature is invalid.
239pub fn verify_bls_sig(
240    signature: &[u8],
241    data: &[u8],
242    addr: &crate::shim::address::Address,
243) -> Result<(), String> {
244    fvm_shared_latest::crypto::signature::ops::verify_bls_sig(signature, data, &addr.into())
245}
246
247/// Returns `String` error if a delegated signature is invalid.
248pub fn verify_delegated_sig(
249    signature: &[u8],
250    data: &[u8],
251    addr: &crate::shim::address::Address,
252) -> anyhow::Result<()> {
253    use super::fvm_shared_latest::{
254        address::Protocol::Delegated,
255        crypto::signature::{SECP_SIG_LEN, ops::recover_secp_public_key},
256    };
257    use crate::rpc::eth::types::EthAddress;
258    use crate::utils::encoding::keccak_256;
259
260    anyhow::ensure!(
261        addr.protocol() == Delegated,
262        "cannot validate a delegated signature against a {} address expected",
263        addr.protocol()
264    );
265
266    let sig: [u8; SECP_SIG_LEN] = signature.try_into().with_context(|| {
267        format!(
268            "invalid delegated signature length. Was {}, must be {}",
269            signature.len(),
270            SECP_SIG_LEN,
271        )
272    })?;
273
274    let hash = keccak_256(data);
275    let pub_key = recover_secp_public_key(&hash, &sig)?;
276
277    let eth_addr = EthAddress::eth_address_from_uncompressed_public_key(&pub_key)?;
278
279    let rec_addr = eth_addr.to_filecoin_address()?;
280
281    // check address against recovered address
282    anyhow::ensure!(rec_addr == *addr, "Delegated signature verification failed");
283
284    Ok(())
285}
286
287/// Extracts the raw replica commitment from a CID
288/// assuming that it has the correct hashing function and
289/// serialization types
290pub fn cid_to_replica_commitment_v1(c: &Cid) -> Result<Commitment, &'static str> {
291    fvm_shared_latest::commcid::cid_to_replica_commitment_v1(c)
292}
293
294/// Signature variants for Filecoin signatures.
295#[derive(
296    Clone,
297    Debug,
298    PartialEq,
299    FromPrimitive,
300    Copy,
301    Eq,
302    Serialize_repr,
303    Deserialize_repr,
304    Hash,
305    strum::Display,
306    strum::EnumString,
307    JsonSchema,
308    GetSize,
309)]
310#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
311#[repr(u8)]
312#[strum(serialize_all = "lowercase")]
313pub enum SignatureType {
314    Secp256k1 = 1,
315    Bls = 2,
316    Delegated = 3,
317}
318
319impl TryFrom<u8> for SignatureType {
320    type Error = anyhow::Error;
321
322    fn try_from(value: u8) -> Result<Self, Self::Error> {
323        match value {
324            1 => Ok(SignatureType::Secp256k1),
325            2 => Ok(SignatureType::Bls),
326            3 => Ok(SignatureType::Delegated),
327            invalid => anyhow::bail!("Invalid signature type byte: {}", invalid),
328        }
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    use super::*;
335    use crate::eth::EthEip1559TxArgsBuilder;
336    use crate::networks::calibnet;
337    use crate::{
338        key_management::{generate_key, sign},
339        message::SignedMessage,
340        shim::{address::Address, crypto::SignatureType},
341    };
342    use num_bigint::BigInt;
343    use std::str::FromStr;
344
345    const TEST_CHAIN_ID: EthChainId = calibnet::ETH_CHAIN_ID;
346
347    #[quickcheck_macros::quickcheck]
348    fn signature_from_bytes_no_panic(bytes: Vec<u8>) {
349        let _ = Signature::from_bytes(bytes);
350    }
351
352    #[quickcheck_macros::quickcheck]
353    fn signature_to_from_bytes_roundtrip(sig: Signature) {
354        assert_eq!(Signature::from_bytes(sig.to_bytes()).unwrap(), sig);
355    }
356
357    fn create_delegated_key() -> (Address, Vec<u8>) {
358        let key = generate_key(SignatureType::Delegated).unwrap();
359        let addr = key.address;
360        let priv_key = key.key_info.private_key().clone();
361        (addr, priv_key)
362    }
363
364    // Create a base EIP-1559 transaction
365    fn create_eip1559_tx() -> EthTx {
366        EthEip1559TxArgsBuilder::default()
367                .chain_id(TEST_CHAIN_ID)
368                .nonce(486_u64)
369                .to(Some(ethereum_types::H160::from_str("0xeb4a9cdb9f42d3a503d580a39b6e3736eb21fffd").unwrap().into()))
370                .value(BigInt::from(0))
371                .max_fee_per_gas(BigInt::from(1500000120))
372                .max_priority_fee_per_gas(BigInt::from(1500000000))
373                .gas_limit(37442471_u64)
374                .input(hex::decode("383487be000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000660d4d120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003b6261666b726569656f6f75326d36356276376561786e7767656d7562723675787269696867366474646e6c7a663469616f37686c6e6a6d647372750000000000").unwrap())
375                .build()
376                .unwrap().into()
377    }
378
379    fn create_signed_message(signature_type: SignatureType) -> (Address, SignedMessage) {
380        let eth_tx = create_eip1559_tx();
381        let key = generate_key(signature_type).unwrap();
382        let from = key.address;
383        let encoded_msg = eth_tx.rlp_unsigned_message(calibnet::ETH_CHAIN_ID).unwrap();
384        let signature = sign(signature_type, key.key_info.private_key(), &encoded_msg).unwrap();
385        let msg = SignedMessage::new_unchecked(
386            eth_tx.get_unsigned_message(from, TEST_CHAIN_ID).unwrap(),
387            signature.clone(),
388        );
389        (from, msg)
390    }
391
392    #[test]
393    fn test_verify_delegated_sig_valid() {
394        let (address, priv_key) = create_delegated_key();
395        let message = b"important protocol message";
396        let signature = sign(SignatureType::Delegated, &priv_key, message).unwrap();
397
398        let result = verify_delegated_sig(&signature.bytes, message, &address);
399        assert!(result.is_ok(), "Valid delegated signature should verify");
400    }
401
402    #[test]
403    fn test_verify_delegated_sig_invalid_signature() {
404        let (address, priv_key) = create_delegated_key();
405        let message = b"important protocol message";
406        let mut signature = sign(SignatureType::Delegated, &priv_key, message).unwrap();
407
408        // Tamper with signature
409        if let Some(last_byte) = signature.bytes.last_mut() {
410            *last_byte = last_byte.wrapping_add(1);
411        }
412
413        let result = verify_delegated_sig(&signature.bytes, message, &address);
414        assert!(result.is_err(), "Tampered signature should fail");
415    }
416
417    #[test]
418    fn test_verify_delegated_sig_wrong_address() {
419        let (_, priv_key) = create_delegated_key();
420        let (wrong_address, _) = create_delegated_key();
421        let signature = sign(SignatureType::Delegated, &priv_key, b"message").unwrap();
422
423        let result = verify_delegated_sig(&signature.bytes, b"message", &wrong_address);
424        assert!(
425            result.is_err(),
426            "Signature should not verify for wrong address"
427        );
428    }
429
430    #[test]
431    fn test_verify_delegated_sig_invalid_length() {
432        let (address, _) = create_delegated_key();
433        let invalid_sig = vec![0u8; 64]; // Too short
434
435        let result = verify_delegated_sig(&invalid_sig, b"message", &address);
436        assert!(result.is_err(), "Should error on invalid signature length");
437    }
438
439    #[test]
440    fn test_verify_delegated_sig_non_delegated_address() {
441        let secp_key = generate_key(SignatureType::Secp256k1).unwrap();
442        let secp_addr = secp_key.address;
443        let (_, priv_key) = create_delegated_key();
444        let signature = sign(SignatureType::Delegated, &priv_key, b"message").unwrap();
445
446        let result = verify_delegated_sig(&signature.bytes, b"message", &secp_addr);
447        assert!(result.is_err(), "Should reject non-delegated address");
448    }
449
450    #[test]
451    fn test_verify_delegated_sig_empty_message() {
452        let (address, priv_key) = create_delegated_key();
453        let signature = sign(SignatureType::Delegated, &priv_key, &[]).unwrap();
454
455        let result = verify_delegated_sig(&signature.bytes, &[], &address);
456        assert!(result.is_ok(), "Should handle empty messages");
457    }
458
459    #[test]
460    fn authenticate_valid_signed_message() {
461        let (from, signed_msg) = create_signed_message(SignatureType::Delegated);
462        let result = signed_msg
463            .signature()
464            .authenticate_msg(TEST_CHAIN_ID, &signed_msg, &from);
465        assert!(result.is_ok(), "Invalid Delegated signature");
466    }
467
468    #[test]
469    fn authenticate_invalid_signature_type() {
470        let (addr, signed_msg) = create_signed_message(SignatureType::Bls);
471        let mut bad_sign = signed_msg.signature().clone();
472        bad_sign.sig_type = SignatureType::Delegated; // Wrong type
473
474        let result = bad_sign.authenticate_msg(TEST_CHAIN_ID, &signed_msg, &addr);
475        assert!(result.is_err(), "Mismatched signature type should fail");
476    }
477
478    #[test]
479    fn authenticate_tampered_signature() {
480        let (addr, mut signed_msg) = create_signed_message(SignatureType::Delegated);
481        signed_msg.signature.bytes[32] = signed_msg.signature.bytes[32].wrapping_add(1);
482
483        let result = signed_msg
484            .signature()
485            .authenticate_msg(TEST_CHAIN_ID, &signed_msg, &addr);
486
487        assert!(result.is_err(), "Tampered signature should fail");
488    }
489
490    #[test]
491    fn authenticate_delegated_invalid_chain_id() {
492        let (addr, signed_msg) = create_signed_message(SignatureType::Delegated);
493
494        let result = signed_msg
495            .signature()
496            .authenticate_msg(0, &signed_msg, &addr); // Invalid Chain ID
497
498        assert!(result.is_err(), "Chain ID mismatch should fail");
499    }
500}