linera_base/crypto/secp256k1/
evm.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Defines EIP-191 compatible signature primitives used by the Linera protocol.
5
6use std::{
7    borrow::Cow,
8    fmt,
9    hash::{Hash, Hasher},
10    str::FromStr,
11};
12
13use alloy_primitives::{eip191_hash_message, Signature};
14use k256::{
15    ecdsa::{SigningKey, VerifyingKey},
16    elliptic_curve::sec1::FromEncodedPoint,
17    EncodedPoint,
18};
19use linera_witty::{
20    GuestPointer, HList, InstanceWithMemory, Layout, Memory, Runtime, RuntimeError, RuntimeMemory,
21    WitLoad, WitStore, WitType,
22};
23use serde::{Deserialize, Serialize};
24
25use super::{BcsHashable, BcsSignable, CryptoError, CryptoHash, HasTypeName};
26use crate::doc_scalar;
27
28/// Name of the secp256k1 scheme.
29const EVM_SECP256K1_SCHEME_LABEL: &str = "evm_secp256k1";
30
31/// Length of secp256k1 compressed public key.
32const EVM_SECP256K1_PUBLIC_KEY_SIZE: usize = 33;
33
34/// Length of secp256k1 signature.
35const EVM_SECP256K1_SIGNATURE_SIZE: usize = 65;
36
37/// A secp256k1 secret key.
38pub struct EvmSecretKey(pub SigningKey);
39
40impl Eq for EvmSecretKey {}
41impl PartialEq for EvmSecretKey {
42    fn eq(&self, other: &Self) -> bool {
43        self.0.to_bytes() == other.0.to_bytes()
44    }
45}
46
47/// A secp256k1 public key.
48#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
49pub struct EvmPublicKey(pub VerifyingKey);
50
51impl Hash for EvmPublicKey {
52    fn hash<H: Hasher>(&self, state: &mut H) {
53        self.0.to_encoded_point(true).as_bytes().hash(state);
54    }
55}
56
57/// Secp256k1 public/secret key pair.
58#[derive(Debug, PartialEq, Eq)]
59pub struct EvmKeyPair {
60    /// Secret key.
61    pub secret_key: EvmSecretKey,
62    /// Public key.
63    pub public_key: EvmPublicKey,
64}
65
66/// A secp256k1 signature.
67#[derive(Eq, PartialEq, Copy, Clone)]
68pub struct EvmSignature(pub Signature);
69
70impl FromStr for EvmSignature {
71    type Err = CryptoError;
72
73    fn from_str(s: &str) -> Result<Self, Self::Err> {
74        // If the string starts with "0x", we remove it before decoding.
75        let bytes = hex::decode(s.strip_prefix("0x").unwrap_or(s))?;
76        Self::from_slice(&bytes)
77    }
78}
79
80impl EvmPublicKey {
81    /// A fake public key used for testing.
82    #[cfg(with_testing)]
83    pub fn test_key(seed: u8) -> Self {
84        use rand::SeedableRng;
85        let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
86        let sk = k256::SecretKey::random(&mut rng);
87        Self(sk.public_key().into())
88    }
89
90    /// Returns the bytes of the public key in compressed representation.
91    pub fn as_bytes(&self) -> [u8; EVM_SECP256K1_PUBLIC_KEY_SIZE] {
92        // UNWRAP: We already have valid key so conversion should not fail.
93        self.0.to_encoded_point(true).as_bytes().try_into().unwrap()
94    }
95
96    /// Decodes the bytes into the public key.
97    /// Expects the bytes to be of compressed representation.
98    ///
99    /// Panics if the encoding can't be done in a constant time.
100    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
101        let encoded_point =
102            EncodedPoint::from_bytes(bytes).map_err(|_| CryptoError::IncorrectPublicKeySize {
103                scheme: EVM_SECP256K1_SCHEME_LABEL,
104                len: bytes.len(),
105                expected: EVM_SECP256K1_PUBLIC_KEY_SIZE,
106            })?;
107
108        match k256::PublicKey::from_encoded_point(&encoded_point).into_option() {
109            Some(public_key) => Ok(Self(public_key.into())),
110            None => {
111                let error = CryptoError::Secp256k1PointAtInfinity(hex::encode(bytes));
112                Err(error)
113            }
114        }
115    }
116
117    /// Returns an EVM address for the public key.
118    pub fn address(&self) -> alloy_primitives::Address {
119        alloy_primitives::Address::from_public_key(&self.0)
120    }
121
122    /// Recovers the public key from the signature and the value.
123    ///
124    /// This function turns the `value` into a `CryptoHash`, then hashes it using EIP-191
125    /// and finally recovers the public key from the signature.
126    pub fn recover_from_msg<'de, T>(
127        signature: &EvmSignature,
128        value: &T,
129    ) -> Result<Self, CryptoError>
130    where
131        T: BcsSignable<'de>,
132    {
133        let message = CryptoHash::new(value).as_bytes().0;
134        let public_key =
135            signature
136                .0
137                .recover_from_msg(message)
138                .map_err(|_| CryptoError::InvalidSignature {
139                    error: "Failed to recover public key from signature".to_string(),
140                    type_name: Self::type_name().to_string(),
141                })?;
142        Ok(EvmPublicKey(public_key))
143    }
144}
145
146impl fmt::Debug for EvmSecretKey {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "<redacted for secp256k1 secret key>")
149    }
150}
151
152impl Serialize for EvmSecretKey {
153    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154    where
155        S: serde::ser::Serializer,
156    {
157        // This is only used for JSON configuration.
158        assert!(serializer.is_human_readable());
159        serializer.serialize_str(&hex::encode(self.0.to_bytes()))
160    }
161}
162
163impl<'de> Deserialize<'de> for EvmSecretKey {
164    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
165    where
166        D: serde::de::Deserializer<'de>,
167    {
168        // This is only used for JSON configuration.
169        assert!(deserializer.is_human_readable());
170        let str = String::deserialize(deserializer)?;
171        let bytes = hex::decode(&str).map_err(serde::de::Error::custom)?;
172        let sk = SigningKey::from_slice(&bytes).map_err(serde::de::Error::custom)?;
173        Ok(EvmSecretKey(sk))
174    }
175}
176
177#[cfg(with_testing)]
178impl FromStr for EvmSecretKey {
179    type Err = CryptoError;
180
181    fn from_str(s: &str) -> Result<Self, Self::Err> {
182        let bytes = hex::decode(s)?;
183        let sk = SigningKey::from_slice(&bytes).expect("Failed to create secret key");
184        Ok(EvmSecretKey(sk))
185    }
186}
187
188impl Serialize for EvmPublicKey {
189    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190    where
191        S: serde::ser::Serializer,
192    {
193        if serializer.is_human_readable() {
194            serializer.serialize_str(&hex::encode(self.as_bytes()))
195        } else {
196            let compact_pk = serde_utils::CompressedPublicKey(self.as_bytes());
197            serializer.serialize_newtype_struct("EvmPublicKey", &compact_pk)
198        }
199    }
200}
201
202impl<'de> Deserialize<'de> for EvmPublicKey {
203    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
204    where
205        D: serde::de::Deserializer<'de>,
206    {
207        if deserializer.is_human_readable() {
208            let s = String::deserialize(deserializer)?;
209            let value = hex::decode(s).map_err(serde::de::Error::custom)?;
210            Ok(EvmPublicKey::from_bytes(&value).map_err(serde::de::Error::custom)?)
211        } else {
212            #[derive(Deserialize)]
213            #[serde(rename = "EvmPublicKey")]
214            struct PublicKey(serde_utils::CompressedPublicKey);
215            let compact = PublicKey::deserialize(deserializer)?;
216            Ok(EvmPublicKey::from_bytes(&compact.0 .0).map_err(serde::de::Error::custom)?)
217        }
218    }
219}
220
221impl FromStr for EvmPublicKey {
222    type Err = CryptoError;
223
224    fn from_str(s: &str) -> Result<Self, Self::Err> {
225        hex::decode(s.strip_prefix("0x").unwrap_or(s))?
226            .as_slice()
227            .try_into()
228    }
229}
230
231impl TryFrom<&[u8]> for EvmPublicKey {
232    type Error = CryptoError;
233
234    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
235        Self::from_bytes(value)
236    }
237}
238
239impl fmt::Display for EvmPublicKey {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        let str = hex::encode(self.as_bytes());
242        write!(f, "{}", str)
243    }
244}
245
246impl fmt::Debug for EvmPublicKey {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        write!(f, "{}..", hex::encode(&self.as_bytes()[0..9]))
249    }
250}
251
252impl BcsHashable<'_> for EvmPublicKey {}
253
254impl WitType for EvmPublicKey {
255    const SIZE: u32 = <(u64, u64, u64, u64, u8) as WitType>::SIZE;
256    type Layout = <(u64, u64, u64, u64, u8) as WitType>::Layout;
257    type Dependencies = HList![];
258
259    fn wit_type_name() -> Cow<'static, str> {
260        "evm-secp256k1-public-key".into()
261    }
262
263    fn wit_type_declaration() -> Cow<'static, str> {
264        concat!(
265            "    record evm-secp256k1-public-key {\n",
266            "        part1: u64,\n",
267            "        part2: u64,\n",
268            "        part3: u64,\n",
269            "        part4: u64,\n",
270            "        part5: u8\n",
271            "    }\n",
272        )
273        .into()
274    }
275}
276
277impl WitLoad for EvmPublicKey {
278    fn load<Instance>(
279        memory: &Memory<'_, Instance>,
280        location: GuestPointer,
281    ) -> Result<Self, RuntimeError>
282    where
283        Instance: InstanceWithMemory,
284        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
285    {
286        let (part1, part2, part3, part4, part5) = WitLoad::load(memory, location)?;
287        Ok(Self::from((part1, part2, part3, part4, part5)))
288    }
289
290    fn lift_from<Instance>(
291        flat_layout: <Self::Layout as Layout>::Flat,
292        memory: &Memory<'_, Instance>,
293    ) -> Result<Self, RuntimeError>
294    where
295        Instance: InstanceWithMemory,
296        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
297    {
298        let (part1, part2, part3, part4, part5) = WitLoad::lift_from(flat_layout, memory)?;
299        Ok(Self::from((part1, part2, part3, part4, part5)))
300    }
301}
302
303impl WitStore for EvmPublicKey {
304    fn store<Instance>(
305        &self,
306        memory: &mut Memory<'_, Instance>,
307        location: GuestPointer,
308    ) -> Result<(), RuntimeError>
309    where
310        Instance: InstanceWithMemory,
311        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
312    {
313        let (part1, part2, part3, part4, part5) = (*self).into();
314        (part1, part2, part3, part4, part5).store(memory, location)
315    }
316
317    fn lower<Instance>(
318        &self,
319        memory: &mut Memory<'_, Instance>,
320    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
321    where
322        Instance: InstanceWithMemory,
323        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
324    {
325        let (part1, part2, part3, part4, part5) = (*self).into();
326        (part1, part2, part3, part4, part5).lower(memory)
327    }
328}
329
330impl From<(u64, u64, u64, u64, u8)> for EvmPublicKey {
331    fn from((part1, part2, part3, part4, part5): (u64, u64, u64, u64, u8)) -> Self {
332        let mut bytes = [0u8; EVM_SECP256K1_PUBLIC_KEY_SIZE];
333        bytes[0..8].copy_from_slice(&part1.to_be_bytes());
334        bytes[8..16].copy_from_slice(&part2.to_be_bytes());
335        bytes[16..24].copy_from_slice(&part3.to_be_bytes());
336        bytes[24..32].copy_from_slice(&part4.to_be_bytes());
337        bytes[32] = part5;
338        Self::from_bytes(&bytes).unwrap()
339    }
340}
341
342impl From<EvmPublicKey> for (u64, u64, u64, u64, u8) {
343    fn from(key: EvmPublicKey) -> Self {
344        let bytes = key.as_bytes();
345        let part1 = u64::from_be_bytes(bytes[0..8].try_into().unwrap());
346        let part2 = u64::from_be_bytes(bytes[8..16].try_into().unwrap());
347        let part3 = u64::from_be_bytes(bytes[16..24].try_into().unwrap());
348        let part4 = u64::from_be_bytes(bytes[24..32].try_into().unwrap());
349        let part5 = bytes[32];
350        (part1, part2, part3, part4, part5)
351    }
352}
353
354impl EvmKeyPair {
355    /// Generates a new key pair.
356    #[cfg(all(with_getrandom, with_testing))]
357    pub fn generate() -> Self {
358        let mut rng = rand::rngs::OsRng;
359        Self::generate_from(&mut rng)
360    }
361
362    /// Generates a new key pair from the given RNG. Use with care.
363    #[cfg(with_getrandom)]
364    pub fn generate_from<R: crate::crypto::CryptoRng>(rng: &mut R) -> Self {
365        let secret_key = EvmSecretKey(SigningKey::random(rng));
366        let public_key = secret_key.public();
367        EvmKeyPair {
368            secret_key,
369            public_key,
370        }
371    }
372}
373
374impl EvmSecretKey {
375    /// Returns a public key for the given secret key.
376    pub fn public(&self) -> EvmPublicKey {
377        EvmPublicKey(*self.0.verifying_key())
378    }
379
380    /// Copies the key pair, **including the secret key**.
381    ///
382    /// The `Clone` and `Copy` traits are deliberately not implemented for `EvmSecretKey` to prevent
383    /// accidental copies of secret keys.
384    pub fn copy(&self) -> Self {
385        Self(self.0.clone())
386    }
387
388    /// Generates a new key pair.
389    #[cfg(all(with_getrandom, with_testing))]
390    pub fn generate() -> Self {
391        let mut rng = rand::rngs::OsRng;
392        Self::generate_from(&mut rng)
393    }
394
395    /// Generates a new key pair from the given RNG. Use with care.
396    #[cfg(with_getrandom)]
397    pub fn generate_from<R: crate::crypto::CryptoRng>(rng: &mut R) -> Self {
398        EvmSecretKey(SigningKey::random(rng))
399    }
400
401    /// Returns an EVM address for the public key.
402    pub fn address(&self) -> alloy_primitives::Address {
403        alloy_primitives::Address::from_private_key(&self.0)
404    }
405}
406
407impl EvmSignature {
408    /// Computes a secp256k1 signature for `prehash` using the given `secret`.
409    pub fn new(prehash: CryptoHash, secret: &EvmSecretKey) -> Self {
410        Self::sign_prehash(secret, prehash)
411    }
412
413    /// Computes a signature from a prehash.
414    pub fn sign_prehash(secret: &EvmSecretKey, prehash: CryptoHash) -> Self {
415        let message = eip191_hash_message(prehash.as_bytes().0).0;
416        let (signature, rid) = secret
417            .0
418            .sign_prehash_recoverable(&message)
419            .expect("Failed to sign prehashed data"); // NOTE: This is a critical error we don't control.
420        EvmSignature((signature, rid).into())
421    }
422
423    /// Checks a signature.
424    pub fn check<'de, T>(&self, value: &T, author: EvmPublicKey) -> Result<(), CryptoError>
425    where
426        T: BcsSignable<'de> + fmt::Debug,
427    {
428        let prehash = CryptoHash::new(value).as_bytes().0;
429        self.verify_inner::<T>(prehash, author)
430    }
431
432    /// Checks a signature against a recovered public key.
433    pub fn check_with_recover<'de, T>(
434        &self,
435        value: &T,
436        sender_address: [u8; 20],
437    ) -> Result<EvmPublicKey, CryptoError>
438    where
439        T: BcsSignable<'de> + fmt::Debug,
440    {
441        let msg = CryptoHash::new(value).as_bytes().0;
442        let recovered_public_key = match self.0.recover_from_msg(msg) {
443            Ok(public_key) => EvmPublicKey(public_key),
444            Err(_) => {
445                return Err(CryptoError::InvalidSignature {
446                    error: "Failed to recover public key from signature".to_string(),
447                    type_name: T::type_name().to_string(),
448                });
449            }
450        };
451        if recovered_public_key.address() != alloy_primitives::Address::new(sender_address) {
452            return Err(CryptoError::InvalidSignature {
453                error: "Recovered public key does not match sender address".to_string(),
454                type_name: T::type_name().to_string(),
455            });
456        }
457        Ok(recovered_public_key)
458    }
459
460    /// Verifies a batch of signatures.
461    ///
462    /// Returns an error on first failed signature.
463    pub fn verify_batch<'a, 'de, T, I>(value: &'a T, votes: I) -> Result<(), CryptoError>
464    where
465        T: BcsSignable<'de> + fmt::Debug,
466        I: IntoIterator<Item = &'a (EvmPublicKey, EvmSignature)>,
467    {
468        let prehash = CryptoHash::new(value).as_bytes().0;
469        for (author, signature) in votes {
470            signature.verify_inner::<T>(prehash, *author)?;
471        }
472        Ok(())
473    }
474
475    /// Returns the byte representation of the signature.
476    pub fn as_bytes(&self) -> [u8; EVM_SECP256K1_SIGNATURE_SIZE] {
477        self.0.as_bytes()
478    }
479
480    fn verify_inner<'de, T>(
481        &self,
482        prehash: [u8; 32],
483        author: EvmPublicKey,
484    ) -> Result<(), CryptoError>
485    where
486        T: BcsSignable<'de> + fmt::Debug,
487    {
488        use k256::ecdsa::signature::hazmat::PrehashVerifier;
489
490        let message_hash = eip191_hash_message(prehash).0;
491
492        author
493            .0
494            .verify_prehash(
495                &message_hash,
496                &self.0.to_k256().map_err(CryptoError::Secp256k1Error)?,
497            )
498            .map_err(|error| CryptoError::InvalidSignature {
499                error: error.to_string(),
500                type_name: T::type_name().to_string(),
501            })
502    }
503
504    /// Creates a signature from the bytes.
505    /// Expects the signature to be serialized in raw-bytes form.
506    pub fn from_slice<A: AsRef<[u8]>>(bytes: A) -> Result<Self, CryptoError> {
507        let bytes = bytes.as_ref();
508        let sig = alloy_primitives::Signature::from_raw(bytes).map_err(|_| {
509            CryptoError::IncorrectSignatureBytes {
510                scheme: EVM_SECP256K1_SCHEME_LABEL,
511                len: bytes.len(),
512                expected: EVM_SECP256K1_SIGNATURE_SIZE,
513            }
514        })?;
515        Ok(EvmSignature(sig))
516    }
517}
518
519impl Serialize for EvmSignature {
520    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
521    where
522        S: serde::ser::Serializer,
523    {
524        if serializer.is_human_readable() {
525            serializer.serialize_str(&hex::encode(self.as_bytes()))
526        } else {
527            let compact = serde_utils::CompactSignature(self.as_bytes());
528            serializer.serialize_newtype_struct("EvmSignature", &compact)
529        }
530    }
531}
532
533impl<'de> Deserialize<'de> for EvmSignature {
534    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
535    where
536        D: serde::de::Deserializer<'de>,
537    {
538        if deserializer.is_human_readable() {
539            let s = String::deserialize(deserializer)?;
540            let value = hex::decode(s).map_err(serde::de::Error::custom)?;
541            Self::from_slice(&value).map_err(serde::de::Error::custom)
542        } else {
543            #[derive(Deserialize)]
544            #[serde(rename = "EvmSignature")]
545            struct Signature(serde_utils::CompactSignature);
546
547            let value = Signature::deserialize(deserializer)?;
548            Self::from_slice(value.0 .0.as_ref()).map_err(serde::de::Error::custom)
549        }
550    }
551}
552
553impl fmt::Display for EvmSignature {
554    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555        let s = hex::encode(self.as_bytes());
556        write!(f, "{}", s)
557    }
558}
559
560impl fmt::Debug for EvmSignature {
561    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562        write!(f, "{}..", hex::encode(&self.as_bytes()[0..9]))
563    }
564}
565
566doc_scalar!(EvmSignature, "A secp256k1 signature value");
567doc_scalar!(EvmPublicKey, "A secp256k1 public key value");
568
569mod serde_utils {
570    use serde::{Deserialize, Serialize};
571    use serde_with::serde_as;
572
573    use super::{EVM_SECP256K1_PUBLIC_KEY_SIZE, EVM_SECP256K1_SIGNATURE_SIZE};
574
575    /// Wrapper around compact signature serialization
576    /// so that we can implement custom serializer for it that uses fixed length.
577    // Serde treats arrays larger than 32 as variable length arrays, and adds the length as a prefix.
578    // Since we want a fixed size representation, we wrap it in this helper struct and use serde_as.
579    #[serde_as]
580    #[derive(Serialize, Deserialize)]
581    #[serde(transparent)]
582    pub struct CompactSignature(#[serde_as(as = "[_; 65]")] pub [u8; EVM_SECP256K1_SIGNATURE_SIZE]);
583
584    #[serde_as]
585    #[derive(Serialize, Deserialize)]
586    #[serde(transparent)]
587    pub struct CompressedPublicKey(
588        #[serde_as(as = "[_; 33]")] pub [u8; EVM_SECP256K1_PUBLIC_KEY_SIZE],
589    );
590}
591
592#[cfg(with_testing)]
593mod tests {
594    #[test]
595    fn eip191_compatibility() {
596        use std::str::FromStr;
597
598        use crate::crypto::{CryptoHash, EvmSecretKey, EvmSignature};
599
600        // Generated in MetaMask.
601        let secret_key = "f77a21701522a03b01c111ad2d2cdaf2b8403b47507ee0aec3c2e52b765d7a66";
602        let signer = EvmSecretKey::from_str(secret_key).unwrap();
603
604        let crypto_hash = CryptoHash::from_str(
605            "c520e2b24b05e70c39c36d4aa98e9129ac0079ea002d4c382e6996ea11946d1e",
606        )
607        .unwrap();
608
609        let signature = EvmSignature::new(crypto_hash, &signer);
610        let js_signature = EvmSignature::from_str("0xe257048813b851f812ba6e508e972d8bb09504824692b027ca95d31301dbe8c7103a2f35ce9950d031d260f412dcba09c24027288872a67abe261c0a3e55c9121b").unwrap();
611        assert_eq!(signature, js_signature);
612    }
613
614    #[test]
615    fn test_signatures() {
616        use serde::{Deserialize, Serialize};
617
618        use crate::crypto::{
619            secp256k1::evm::{EvmKeyPair, EvmSignature},
620            BcsSignable, CryptoHash, TestString,
621        };
622
623        #[derive(Debug, Serialize, Deserialize)]
624        struct Foo(String);
625
626        impl BcsSignable<'_> for Foo {}
627
628        let keypair1 = EvmKeyPair::generate();
629        let keypair2 = EvmKeyPair::generate();
630
631        let ts = TestString("hello".into());
632        let ts_cryptohash = CryptoHash::new(&ts);
633        let tsx = TestString("hellox".into());
634        let foo = Foo("hello".into());
635
636        let s = EvmSignature::new(ts_cryptohash, &keypair1.secret_key);
637        assert!(s.check(&ts, keypair1.public_key).is_ok());
638        assert!(s.check(&ts, keypair2.public_key).is_err());
639        assert!(s.check(&tsx, keypair1.public_key).is_err());
640        assert!(s.check(&foo, keypair1.public_key).is_err());
641    }
642
643    #[test]
644    fn test_public_key_serialization() {
645        use crate::crypto::secp256k1::evm::EvmPublicKey;
646        let key_in = EvmPublicKey::test_key(0);
647        let s = serde_json::to_string(&key_in).unwrap();
648        let key_out: EvmPublicKey = serde_json::from_str(&s).unwrap();
649        assert_eq!(key_out, key_in);
650
651        let s = bcs::to_bytes(&key_in).unwrap();
652        let key_out: EvmPublicKey = bcs::from_bytes(&s).unwrap();
653        assert_eq!(key_out, key_in);
654    }
655
656    #[test]
657    fn test_secret_key_serialization() {
658        use crate::crypto::secp256k1::evm::{EvmKeyPair, EvmSecretKey};
659        let key_in = EvmKeyPair::generate().secret_key;
660        let s = serde_json::to_string(&key_in).unwrap();
661        let key_out: EvmSecretKey = serde_json::from_str(&s).unwrap();
662        assert_eq!(key_out, key_in);
663    }
664
665    #[test]
666    fn test_signature_serialization() {
667        use crate::crypto::{
668            secp256k1::evm::{EvmKeyPair, EvmSignature},
669            CryptoHash, TestString,
670        };
671        let keypair = EvmKeyPair::generate();
672        let prehash = CryptoHash::new(&TestString("hello".into()));
673        let sig = EvmSignature::new(prehash, &keypair.secret_key);
674        let s = serde_json::to_string(&sig).unwrap();
675        let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
676        assert_eq!(sig, sig2);
677
678        let s = bcs::to_bytes(&sig).unwrap();
679        let sig2: EvmSignature = bcs::from_bytes(&s).unwrap();
680        assert_eq!(sig, sig2);
681    }
682
683    #[test]
684    fn public_key_from_str() {
685        use std::str::FromStr;
686
687        use crate::crypto::secp256k1::evm::EvmPublicKey;
688        let key = EvmPublicKey::test_key(0);
689        let s = key.to_string();
690        let key2 = EvmPublicKey::from_str(s.as_str()).unwrap();
691        assert_eq!(key, key2);
692    }
693
694    #[test]
695    fn bytes_repr_compact_public_key() {
696        use crate::crypto::secp256k1::evm::{EvmPublicKey, EVM_SECP256K1_PUBLIC_KEY_SIZE};
697        let key_in: EvmPublicKey = EvmPublicKey::test_key(0);
698        let bytes = key_in.as_bytes();
699        assert!(
700            bytes.len() == EVM_SECP256K1_PUBLIC_KEY_SIZE,
701            "::to_bytes() should return compressed representation"
702        );
703        let key_out = EvmPublicKey::from_bytes(&bytes).unwrap();
704        assert_eq!(key_in, key_out);
705    }
706
707    #[test]
708    fn human_readable_ser() {
709        use crate::crypto::{
710            secp256k1::evm::{EvmKeyPair, EvmSignature},
711            CryptoHash, TestString,
712        };
713        let key_pair = EvmKeyPair::generate();
714        let prehash = CryptoHash::new(&TestString("hello".into()));
715        let sig = EvmSignature::new(prehash, &key_pair.secret_key);
716        let s = serde_json::to_string(&sig).unwrap();
717        let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
718        assert_eq!(sig, sig2);
719    }
720
721    #[test]
722    fn public_key_recovery() {
723        use crate::crypto::{
724            secp256k1::evm::{EvmKeyPair, EvmPublicKey, EvmSignature},
725            CryptoHash, TestString,
726        };
727        let key_pair = EvmKeyPair::generate();
728        let address = key_pair.public_key.address();
729        let msg = TestString("hello".into());
730        let prehash = CryptoHash::new(&msg);
731        let sig = EvmSignature::new(prehash, &key_pair.secret_key);
732
733        sig.check_with_recover(&msg, address.0 .0).unwrap();
734
735        let public_key = EvmPublicKey::recover_from_msg(&sig, &msg).unwrap();
736        assert_eq!(public_key, key_pair.public_key);
737    }
738}