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::{Message, 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>) -> Result<Self, anyhow::Error> {
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_pub_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    fn create_delegated_key() -> (Address, Vec<u8>) {
348        let key = generate_key(SignatureType::Delegated).unwrap();
349        let addr = key.address;
350        let priv_key = key.key_info.private_key().clone();
351        (addr, priv_key)
352    }
353
354    // Create a base EIP-1559 transaction
355    fn create_eip1559_tx() -> EthTx {
356        EthTx::Eip1559(Box::new(
357            EthEip1559TxArgsBuilder::default()
358                .chain_id(TEST_CHAIN_ID)
359                .nonce(486_u64)
360                .to(Some(ethereum_types::H160::from_str("0xeb4a9cdb9f42d3a503d580a39b6e3736eb21fffd").unwrap().into()))
361                .value(BigInt::from(0))
362                .max_fee_per_gas(BigInt::from(1500000120))
363                .max_priority_fee_per_gas(BigInt::from(1500000000))
364                .gas_limit(37442471_u64)
365                .input(hex::decode("383487be000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000660d4d120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003b6261666b726569656f6f75326d36356276376561786e7767656d7562723675787269696867366474646e6c7a663469616f37686c6e6a6d647372750000000000").unwrap())
366                .build()
367                .unwrap()
368            )
369        )
370    }
371
372    fn create_signed_message(signature_type: SignatureType) -> (Address, SignedMessage) {
373        let eth_tx = create_eip1559_tx();
374        let key = generate_key(signature_type).unwrap();
375        let from = key.address;
376        let encoded_msg = eth_tx.rlp_unsigned_message(calibnet::ETH_CHAIN_ID).unwrap();
377        let signature = sign(signature_type, key.key_info.private_key(), &encoded_msg).unwrap();
378        let msg = SignedMessage::new_unchecked(
379            eth_tx.get_unsigned_message(from, TEST_CHAIN_ID).unwrap(),
380            signature.clone(),
381        );
382        (from, msg)
383    }
384
385    #[test]
386    fn test_verify_delegated_sig_valid() {
387        let (address, priv_key) = create_delegated_key();
388        let message = b"important protocol message";
389        let signature = sign(SignatureType::Delegated, &priv_key, message).unwrap();
390
391        let result = verify_delegated_sig(&signature.bytes, message, &address);
392        assert!(result.is_ok(), "Valid delegated signature should verify");
393    }
394
395    #[test]
396    fn test_verify_delegated_sig_invalid_signature() {
397        let (address, priv_key) = create_delegated_key();
398        let message = b"important protocol message";
399        let mut signature = sign(SignatureType::Delegated, &priv_key, message).unwrap();
400
401        // Tamper with signature
402        if let Some(last_byte) = signature.bytes.last_mut() {
403            *last_byte = last_byte.wrapping_add(1);
404        }
405
406        let result = verify_delegated_sig(&signature.bytes, message, &address);
407        assert!(result.is_err(), "Tampered signature should fail");
408    }
409
410    #[test]
411    fn test_verify_delegated_sig_wrong_address() {
412        let (_, priv_key) = create_delegated_key();
413        let (wrong_address, _) = create_delegated_key();
414        let signature = sign(SignatureType::Delegated, &priv_key, b"message").unwrap();
415
416        let result = verify_delegated_sig(&signature.bytes, b"message", &wrong_address);
417        assert!(
418            result.is_err(),
419            "Signature should not verify for wrong address"
420        );
421    }
422
423    #[test]
424    fn test_verify_delegated_sig_invalid_length() {
425        let (address, _) = create_delegated_key();
426        let invalid_sig = vec![0u8; 64]; // Too short
427
428        let result = verify_delegated_sig(&invalid_sig, b"message", &address);
429        assert!(result.is_err(), "Should error on invalid signature length");
430    }
431
432    #[test]
433    fn test_verify_delegated_sig_non_delegated_address() {
434        let secp_key = generate_key(SignatureType::Secp256k1).unwrap();
435        let secp_addr = secp_key.address;
436        let (_, priv_key) = create_delegated_key();
437        let signature = sign(SignatureType::Delegated, &priv_key, b"message").unwrap();
438
439        let result = verify_delegated_sig(&signature.bytes, b"message", &secp_addr);
440        assert!(result.is_err(), "Should reject non-delegated address");
441    }
442
443    #[test]
444    fn test_verify_delegated_sig_empty_message() {
445        let (address, priv_key) = create_delegated_key();
446        let signature = sign(SignatureType::Delegated, &priv_key, &[]).unwrap();
447
448        let result = verify_delegated_sig(&signature.bytes, &[], &address);
449        assert!(result.is_ok(), "Should handle empty messages");
450    }
451
452    #[test]
453    fn authenticate_valid_signed_message() {
454        let (from, signed_msg) = create_signed_message(SignatureType::Delegated);
455        let result = signed_msg
456            .signature()
457            .authenticate_msg(TEST_CHAIN_ID, &signed_msg, &from);
458        assert!(result.is_ok(), "Invalid Delegated signature");
459    }
460
461    #[test]
462    fn authenticate_invalid_signature_type() {
463        let (addr, signed_msg) = create_signed_message(SignatureType::Bls);
464        let mut bad_sign = signed_msg.signature().clone();
465        bad_sign.sig_type = SignatureType::Delegated; // Wrong type
466
467        let result = bad_sign.authenticate_msg(TEST_CHAIN_ID, &signed_msg, &addr);
468        assert!(result.is_err(), "Mismatched signature type should fail");
469    }
470
471    #[test]
472    fn authenticate_tampered_signature() {
473        let (addr, mut signed_msg) = create_signed_message(SignatureType::Delegated);
474        signed_msg.signature.bytes[32] = signed_msg.signature.bytes[32].wrapping_add(1);
475
476        let result = signed_msg
477            .signature()
478            .authenticate_msg(TEST_CHAIN_ID, &signed_msg, &addr);
479
480        assert!(result.is_err(), "Tampered signature should fail");
481    }
482
483    #[test]
484    fn authenticate_delegated_invalid_chain_id() {
485        let (addr, signed_msg) = create_signed_message(SignatureType::Delegated);
486
487        let result = signed_msg
488            .signature()
489            .authenticate_msg(0, &signed_msg, &addr); // Invalid Chain ID
490
491        assert!(result.is_err(), "Chain ID mismatch should fail");
492    }
493}