reth_primitives_traits/
crypto.rs

1//! Crypto utilities.
2
3use crate::transaction::signature::Signature;
4use alloy_primitives::U256;
5
6/// The order of the secp256k1 curve, divided by two. Signatures that should be checked according
7/// to EIP-2 should have an S value less than or equal to this.
8///
9/// `57896044618658097711785492504343953926418782139537452191302581570759080747168`
10pub const SECP256K1N_HALF: U256 = U256::from_be_bytes([
11    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
12    0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
13]);
14
15/// Secp256k1 utility functions.
16#[cfg(feature = "secp256k1")]
17pub mod secp256k1 {
18    pub use super::impl_secp256k1::*;
19}
20
21/// Secp256k1 utility functions.
22#[cfg(not(feature = "secp256k1"))]
23pub mod secp256k1 {
24    pub use super::impl_k256::*;
25}
26
27#[cfg(feature = "secp256k1")]
28#[allow(unused)]
29mod impl_secp256k1 {
30    use super::*;
31    pub(crate) use ::secp256k1::Error;
32    use ::secp256k1::{
33        ecdsa::{RecoverableSignature, RecoveryId},
34        Message, PublicKey, SecretKey, SECP256K1,
35    };
36    use alloy_primitives::{keccak256, Address, B256, U256};
37
38    /// Recovers the address of the sender using secp256k1 pubkey recovery.
39    ///
40    /// Converts the public key into an ethereum address by hashing the public key with keccak256.
41    ///
42    /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the
43    /// underlying secp256k1 library.
44    pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result<Address, Error> {
45        let sig =
46            RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?;
47
48        let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?;
49        Ok(public_key_to_address(public))
50    }
51
52    /// Signs message with the given secret key.
53    /// Returns the corresponding signature.
54    pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
55        let sec = SecretKey::from_slice(secret.as_ref())?;
56        let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec);
57        let (rec_id, data) = s.serialize_compact();
58
59        let signature = Signature::new(
60            U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"),
61            U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"),
62            rec_id.to_i32() != 0,
63        );
64        Ok(signature)
65    }
66
67    /// Converts a public key into an ethereum address by hashing the encoded public key with
68    /// keccak256.
69    pub fn public_key_to_address(public: PublicKey) -> Address {
70        // strip out the first byte because that should be the SECP256K1_TAG_PUBKEY_UNCOMPRESSED
71        // tag returned by libsecp's uncompressed pubkey serialization
72        let hash = keccak256(&public.serialize_uncompressed()[1..]);
73        Address::from_slice(&hash[12..])
74    }
75}
76
77#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))]
78mod impl_k256 {
79    use super::*;
80    use alloy_primitives::{keccak256, Address, B256};
81    pub(crate) use k256::ecdsa::Error;
82    use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey};
83
84    /// Recovers the address of the sender using secp256k1 pubkey recovery.
85    ///
86    /// Converts the public key into an ethereum address by hashing the public key with keccak256.
87    ///
88    /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the
89    /// underlying secp256k1 library.
90    pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result<Address, Error> {
91        let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?;
92        let mut recid = sig[64];
93
94        // normalize signature and flip recovery id if needed.
95        if let Some(sig_normalized) = signature.normalize_s() {
96            signature = sig_normalized;
97            recid ^= 1;
98        }
99        let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");
100
101        // recover key
102        let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?;
103        Ok(public_key_to_address(recovered_key))
104    }
105
106    /// Signs message with the given secret key.
107    /// Returns the corresponding signature.
108    pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
109        let sec = SigningKey::from_slice(secret.as_ref())?;
110        sec.sign_prehash_recoverable(&message.0).map(Into::into)
111    }
112
113    /// Converts a public key into an ethereum address by hashing the encoded public key with
114    /// keccak256.
115    pub fn public_key_to_address(public: VerifyingKey) -> Address {
116        let hash = keccak256(&public.to_encoded_point(/* compress = */ false).as_bytes()[1..]);
117        Address::from_slice(&hash[12..])
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use alloy_primitives::{keccak256, B256};
124
125    #[cfg(feature = "secp256k1")]
126    #[test]
127    fn sanity_ecrecover_call_secp256k1() {
128        use super::impl_secp256k1::*;
129
130        let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng());
131        let signer = public_key_to_address(public);
132
133        let message = b"hello world";
134        let hash = keccak256(message);
135        let signature =
136            sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message");
137
138        let mut sig: [u8; 65] = [0; 65];
139        sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
140        sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
141        sig[64] = signature.v() as u8;
142
143        assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer));
144    }
145
146    #[cfg(not(feature = "secp256k1"))]
147    #[test]
148    fn sanity_ecrecover_call_k256() {
149        use super::impl_k256::*;
150
151        let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
152        let public = *secret.verifying_key();
153        let signer = public_key_to_address(public);
154
155        let message = b"hello world";
156        let hash = keccak256(message);
157        let signature =
158            sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message");
159
160        let mut sig: [u8; 65] = [0; 65];
161        sig[0..32].copy_from_slice(&signature.r.to_be_bytes::<32>());
162        sig[32..64].copy_from_slice(&signature.s.to_be_bytes::<32>());
163        sig[64] = signature.odd_y_parity as u8;
164
165        assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer));
166    }
167
168    #[test]
169    fn sanity_secp256k1_k256_compat() {
170        use super::{impl_k256, impl_secp256k1};
171
172        let (secp256k1_secret, secp256k1_public) =
173            secp256k1::generate_keypair(&mut rand::thread_rng());
174        let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes())
175            .expect("k256 secret");
176        let k256_public = *k256_secret.verifying_key();
177
178        let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public);
179        let k256_signer = impl_k256::public_key_to_address(k256_public);
180        assert_eq!(secp256k1_signer, k256_signer);
181
182        let message = b"hello world";
183        let hash = keccak256(message);
184
185        let secp256k1_signature = impl_secp256k1::sign_message(
186            B256::from_slice(&secp256k1_secret.secret_bytes()[..]),
187            hash,
188        )
189        .expect("secp256k1 sign");
190        let k256_signature =
191            impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash)
192                .expect("k256 sign");
193        assert_eq!(secp256k1_signature, k256_signature);
194
195        let mut sig: [u8; 65] = [0; 65];
196
197        sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>());
198        sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>());
199        sig[64] = secp256k1_signature.v() as u8;
200        let secp256k1_recovered =
201            impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover");
202        assert_eq!(secp256k1_recovered, secp256k1_signer);
203
204        sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>());
205        sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>());
206        sig[64] = k256_signature.v() as u8;
207        let k256_recovered =
208            impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover");
209        assert_eq!(k256_recovered, k256_signer);
210
211        assert_eq!(secp256k1_recovered, k256_recovered);
212    }
213}