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, PrimitiveSignature};
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 = 64;
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(crate) PrimitiveSignature);
69
70#[cfg(with_testing)]
71impl FromStr for EvmSignature {
72    type Err = CryptoError;
73
74    fn from_str(s: &str) -> Result<Self, Self::Err> {
75        let bytes = hex::decode(s)?;
76        let sig = PrimitiveSignature::from_erc2098(&bytes);
77        Ok(EvmSignature(sig))
78    }
79}
80
81impl EvmPublicKey {
82    /// A fake public key used for testing.
83    #[cfg(with_testing)]
84    pub fn test_key(seed: u8) -> Self {
85        use rand::SeedableRng;
86        let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
87        let sk = k256::SecretKey::random(&mut rng);
88        Self(sk.public_key().into())
89    }
90
91    /// Returns the bytes of the public key in compressed representation.
92    pub fn as_bytes(&self) -> [u8; EVM_SECP256K1_PUBLIC_KEY_SIZE] {
93        // UNWRAP: We already have valid key so conversion should not fail.
94        self.0.to_encoded_point(true).as_bytes().try_into().unwrap()
95    }
96
97    /// Decodes the bytes into the public key.
98    /// Expects the bytes to be of compressed representation.
99    ///
100    /// Panics if the encoding can't be done in a constant time.
101    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
102        let encoded_point =
103            EncodedPoint::from_bytes(bytes).map_err(|_| CryptoError::IncorrectPublicKeySize {
104                scheme: EVM_SECP256K1_SCHEME_LABEL,
105                len: bytes.len(),
106                expected: EVM_SECP256K1_PUBLIC_KEY_SIZE,
107            })?;
108
109        match k256::PublicKey::from_encoded_point(&encoded_point).into_option() {
110            Some(public_key) => Ok(Self(public_key.into())),
111            None => {
112                let error = CryptoError::Secp256k1PointAtInfinity(hex::encode(bytes));
113                Err(error)
114            }
115        }
116    }
117}
118
119impl fmt::Debug for EvmSecretKey {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "<redacted for secp256k1 secret key>")
122    }
123}
124
125impl Serialize for EvmSecretKey {
126    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127    where
128        S: serde::ser::Serializer,
129    {
130        // This is only used for JSON configuration.
131        assert!(serializer.is_human_readable());
132        serializer.serialize_str(&hex::encode(self.0.to_bytes()))
133    }
134}
135
136impl<'de> Deserialize<'de> for EvmSecretKey {
137    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138    where
139        D: serde::de::Deserializer<'de>,
140    {
141        // This is only used for JSON configuration.
142        assert!(deserializer.is_human_readable());
143        let str = String::deserialize(deserializer)?;
144        let bytes = hex::decode(&str).map_err(serde::de::Error::custom)?;
145        let sk = SigningKey::from_slice(&bytes).map_err(serde::de::Error::custom)?;
146        Ok(EvmSecretKey(sk))
147    }
148}
149
150#[cfg(with_testing)]
151impl FromStr for EvmSecretKey {
152    type Err = CryptoError;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        let bytes = hex::decode(s)?;
156        let sk = SigningKey::from_slice(&bytes).expect("Failed to create secret key");
157        Ok(EvmSecretKey(sk))
158    }
159}
160
161impl Serialize for EvmPublicKey {
162    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
163    where
164        S: serde::ser::Serializer,
165    {
166        if serializer.is_human_readable() {
167            serializer.serialize_str(&hex::encode(self.as_bytes()))
168        } else {
169            let compact_pk = serde_utils::CompressedPublicKey(self.as_bytes());
170            serializer.serialize_newtype_struct("EvmPublicKey", &compact_pk)
171        }
172    }
173}
174
175impl<'de> Deserialize<'de> for EvmPublicKey {
176    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177    where
178        D: serde::de::Deserializer<'de>,
179    {
180        if deserializer.is_human_readable() {
181            let s = String::deserialize(deserializer)?;
182            let value = hex::decode(s).map_err(serde::de::Error::custom)?;
183            Ok(EvmPublicKey::from_bytes(&value).map_err(serde::de::Error::custom)?)
184        } else {
185            #[derive(Deserialize)]
186            #[serde(rename = "EvmPublicKey")]
187            struct PublicKey(serde_utils::CompressedPublicKey);
188            let compact = PublicKey::deserialize(deserializer)?;
189            Ok(EvmPublicKey::from_bytes(&compact.0 .0).map_err(serde::de::Error::custom)?)
190        }
191    }
192}
193
194impl FromStr for EvmPublicKey {
195    type Err = CryptoError;
196
197    fn from_str(s: &str) -> Result<Self, Self::Err> {
198        hex::decode(s)?.as_slice().try_into()
199    }
200}
201
202impl TryFrom<&[u8]> for EvmPublicKey {
203    type Error = CryptoError;
204
205    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
206        Self::from_bytes(value)
207    }
208}
209
210impl fmt::Display for EvmPublicKey {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        let str = hex::encode(self.as_bytes());
213        write!(f, "{}", str)
214    }
215}
216
217impl fmt::Debug for EvmPublicKey {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        write!(f, "{}..", hex::encode(&self.as_bytes()[0..9]))
220    }
221}
222
223impl BcsHashable<'_> for EvmPublicKey {}
224
225impl WitType for EvmPublicKey {
226    const SIZE: u32 = <(u64, u64, u64, u64, u8) as WitType>::SIZE;
227    type Layout = <(u64, u64, u64, u64, u8) as WitType>::Layout;
228    type Dependencies = HList![];
229
230    fn wit_type_name() -> Cow<'static, str> {
231        "evm-secp256k1-public-key".into()
232    }
233
234    fn wit_type_declaration() -> Cow<'static, str> {
235        concat!(
236            "    record evm-secp256k1-public-key {\n",
237            "        part1: u64,\n",
238            "        part2: u64,\n",
239            "        part3: u64,\n",
240            "        part4: u64,\n",
241            "        part5: u8\n",
242            "    }\n",
243        )
244        .into()
245    }
246}
247
248impl WitLoad for EvmPublicKey {
249    fn load<Instance>(
250        memory: &Memory<'_, Instance>,
251        location: GuestPointer,
252    ) -> Result<Self, RuntimeError>
253    where
254        Instance: InstanceWithMemory,
255        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
256    {
257        let (part1, part2, part3, part4, part5) = WitLoad::load(memory, location)?;
258        Ok(Self::from((part1, part2, part3, part4, part5)))
259    }
260
261    fn lift_from<Instance>(
262        flat_layout: <Self::Layout as Layout>::Flat,
263        memory: &Memory<'_, Instance>,
264    ) -> Result<Self, RuntimeError>
265    where
266        Instance: InstanceWithMemory,
267        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
268    {
269        let (part1, part2, part3, part4, part5) = WitLoad::lift_from(flat_layout, memory)?;
270        Ok(Self::from((part1, part2, part3, part4, part5)))
271    }
272}
273
274impl WitStore for EvmPublicKey {
275    fn store<Instance>(
276        &self,
277        memory: &mut Memory<'_, Instance>,
278        location: GuestPointer,
279    ) -> Result<(), RuntimeError>
280    where
281        Instance: InstanceWithMemory,
282        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
283    {
284        let (part1, part2, part3, part4, part5) = (*self).into();
285        (part1, part2, part3, part4, part5).store(memory, location)
286    }
287
288    fn lower<Instance>(
289        &self,
290        memory: &mut Memory<'_, Instance>,
291    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
292    where
293        Instance: InstanceWithMemory,
294        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
295    {
296        let (part1, part2, part3, part4, part5) = (*self).into();
297        (part1, part2, part3, part4, part5).lower(memory)
298    }
299}
300
301impl From<(u64, u64, u64, u64, u8)> for EvmPublicKey {
302    fn from((part1, part2, part3, part4, part5): (u64, u64, u64, u64, u8)) -> Self {
303        let mut bytes = [0u8; EVM_SECP256K1_PUBLIC_KEY_SIZE];
304        bytes[0..8].copy_from_slice(&part1.to_be_bytes());
305        bytes[8..16].copy_from_slice(&part2.to_be_bytes());
306        bytes[16..24].copy_from_slice(&part3.to_be_bytes());
307        bytes[24..32].copy_from_slice(&part4.to_be_bytes());
308        bytes[32] = part5;
309        Self::from_bytes(&bytes).unwrap()
310    }
311}
312
313impl From<EvmPublicKey> for (u64, u64, u64, u64, u8) {
314    fn from(key: EvmPublicKey) -> Self {
315        let bytes = key.as_bytes();
316        let part1 = u64::from_be_bytes(bytes[0..8].try_into().unwrap());
317        let part2 = u64::from_be_bytes(bytes[8..16].try_into().unwrap());
318        let part3 = u64::from_be_bytes(bytes[16..24].try_into().unwrap());
319        let part4 = u64::from_be_bytes(bytes[24..32].try_into().unwrap());
320        let part5 = bytes[32];
321        (part1, part2, part3, part4, part5)
322    }
323}
324
325impl EvmKeyPair {
326    /// Generates a new key pair.
327    #[cfg(all(with_getrandom, with_testing))]
328    pub fn generate() -> Self {
329        let mut rng = rand::rngs::OsRng;
330        Self::generate_from(&mut rng)
331    }
332
333    /// Generates a new key pair from the given RNG. Use with care.
334    #[cfg(with_getrandom)]
335    pub fn generate_from<R: crate::crypto::CryptoRng>(rng: &mut R) -> Self {
336        let secret_key = EvmSecretKey(SigningKey::random(rng));
337        let public_key = secret_key.public();
338        EvmKeyPair {
339            secret_key,
340            public_key,
341        }
342    }
343}
344
345impl EvmSecretKey {
346    /// Returns a public key for the given secret key.
347    pub fn public(&self) -> EvmPublicKey {
348        EvmPublicKey(*self.0.verifying_key())
349    }
350
351    /// Copies the key pair, **including the secret key**.
352    ///
353    /// The `Clone` and `Copy` traits are deliberately not implemented for `EvmSecretKey` to prevent
354    /// accidental copies of secret keys.
355    pub fn copy(&self) -> Self {
356        Self(self.0.clone())
357    }
358
359    /// Generates a new key pair.
360    #[cfg(all(with_getrandom, with_testing))]
361    pub fn generate() -> Self {
362        let mut rng = rand::rngs::OsRng;
363        Self::generate_from(&mut rng)
364    }
365
366    /// Generates a new key pair from the given RNG. Use with care.
367    #[cfg(with_getrandom)]
368    pub fn generate_from<R: crate::crypto::CryptoRng>(rng: &mut R) -> Self {
369        EvmSecretKey(SigningKey::random(rng))
370    }
371}
372
373impl EvmSignature {
374    /// Computes a secp256k1 signature for `value` using the given `secret`.
375    /// It first serializes the `T` type and then creates the `CryptoHash` from the serialized bytes.
376    pub fn new<'de, T>(value: &T, secret: &EvmSecretKey) -> Self
377    where
378        T: BcsSignable<'de>,
379    {
380        use k256::ecdsa::signature::hazmat::PrehashSigner;
381
382        let message = eip191_hash_message(CryptoHash::new(value).as_bytes().0).0;
383        let (signature, rid) = secret
384            .0
385            .sign_prehash(&message)
386            .expect("Failed to sign prehashed data"); // NOTE: This is a critical error we don't control.
387        EvmSignature((signature, rid).into())
388    }
389
390    /// Checks a signature.
391    pub fn check<'de, T>(&self, value: &T, author: &EvmPublicKey) -> Result<(), CryptoError>
392    where
393        T: BcsSignable<'de> + fmt::Debug,
394    {
395        let prehash = CryptoHash::new(value).as_bytes().0;
396        self.verify_inner::<T>(prehash, author)
397    }
398
399    /// Verifies a batch of signatures.
400    ///
401    /// Returns an error on first failed signature.
402    pub fn verify_batch<'a, 'de, T, I>(value: &'a T, votes: I) -> Result<(), CryptoError>
403    where
404        T: BcsSignable<'de> + fmt::Debug,
405        I: IntoIterator<Item = &'a (EvmPublicKey, EvmSignature)>,
406    {
407        let prehash = CryptoHash::new(value).as_bytes().0;
408        for (author, signature) in votes {
409            signature.verify_inner::<T>(prehash, author)?;
410        }
411        Ok(())
412    }
413
414    /// Returns the byte representation of the signature.
415    pub fn as_bytes(&self) -> [u8; EVM_SECP256K1_SIGNATURE_SIZE] {
416        self.0.as_erc2098()
417    }
418
419    fn verify_inner<'de, T>(
420        &self,
421        prehash: [u8; 32],
422        author: &EvmPublicKey,
423    ) -> Result<(), CryptoError>
424    where
425        T: BcsSignable<'de> + fmt::Debug,
426    {
427        use k256::ecdsa::signature::hazmat::PrehashVerifier;
428
429        let message_hash = eip191_hash_message(prehash).0;
430
431        author
432            .0
433            .verify_prehash(&message_hash, &self.0.to_k256().unwrap())
434            .map_err(|error| CryptoError::InvalidSignature {
435                error: error.to_string(),
436                type_name: T::type_name().to_string(),
437            })
438    }
439
440    /// Creates a signature from the bytes.
441    /// Expects the signature to be serialized in raw-bytes form.
442    pub fn from_slice<A: AsRef<[u8]>>(bytes: A) -> Result<Self, CryptoError> {
443        let bytes = bytes.as_ref();
444        if bytes.len() < 64 {
445            return Err(CryptoError::IncorrectSignatureBytes {
446                scheme: EVM_SECP256K1_SCHEME_LABEL,
447                len: bytes.len(),
448                expected: EVM_SECP256K1_SIGNATURE_SIZE,
449            });
450        }
451        let sig = alloy_primitives::PrimitiveSignature::from_erc2098(bytes);
452        Ok(EvmSignature(sig))
453    }
454}
455
456impl Serialize for EvmSignature {
457    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
458    where
459        S: serde::ser::Serializer,
460    {
461        if serializer.is_human_readable() {
462            serializer.serialize_str(&hex::encode(self.as_bytes()))
463        } else {
464            let compact = serde_utils::CompactSignature(self.as_bytes());
465            serializer.serialize_newtype_struct("EvmSignature", &compact)
466        }
467    }
468}
469
470impl<'de> Deserialize<'de> for EvmSignature {
471    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
472    where
473        D: serde::de::Deserializer<'de>,
474    {
475        if deserializer.is_human_readable() {
476            let s = String::deserialize(deserializer)?;
477            let value = hex::decode(s).map_err(serde::de::Error::custom)?;
478            Self::from_slice(&value).map_err(serde::de::Error::custom)
479        } else {
480            #[derive(Deserialize)]
481            #[serde(rename = "EvmSignature")]
482            struct Signature(serde_utils::CompactSignature);
483
484            let value = Signature::deserialize(deserializer)?;
485            Self::from_slice(value.0 .0.as_ref()).map_err(serde::de::Error::custom)
486        }
487    }
488}
489
490impl fmt::Display for EvmSignature {
491    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492        let s = hex::encode(self.as_bytes());
493        write!(f, "{}", s)
494    }
495}
496
497impl fmt::Debug for EvmSignature {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        write!(f, "{}..", hex::encode(&self.as_bytes()[0..9]))
500    }
501}
502
503doc_scalar!(EvmSignature, "A secp256k1 signature value");
504doc_scalar!(EvmPublicKey, "A secp256k1 public key value");
505
506mod serde_utils {
507    use serde::{Deserialize, Serialize};
508    use serde_with::serde_as;
509
510    use super::{EVM_SECP256K1_PUBLIC_KEY_SIZE, EVM_SECP256K1_SIGNATURE_SIZE};
511
512    /// Wrapper around compact signature serialization
513    /// so that we can implement custom serializer for it that uses fixed length.
514    // Serde treats arrays larger than 32 as variable length arrays, and adds the length as a prefix.
515    // Since we want a fixed size representation, we wrap it in this helper struct and use serde_as.
516    #[serde_as]
517    #[derive(Serialize, Deserialize)]
518    #[serde(transparent)]
519    pub struct CompactSignature(#[serde_as(as = "[_; 64]")] pub [u8; EVM_SECP256K1_SIGNATURE_SIZE]);
520
521    #[serde_as]
522    #[derive(Serialize, Deserialize)]
523    #[serde(transparent)]
524    pub struct CompressedPublicKey(
525        #[serde_as(as = "[_; 33]")] pub [u8; EVM_SECP256K1_PUBLIC_KEY_SIZE],
526    );
527}
528
529#[cfg(with_testing)]
530mod tests {
531    #[test]
532    fn test_signatures() {
533        use serde::{Deserialize, Serialize};
534
535        use crate::crypto::{
536            secp256k1::evm::{EvmKeyPair, EvmSignature},
537            BcsSignable, TestString,
538        };
539
540        #[derive(Debug, Serialize, Deserialize)]
541        struct Foo(String);
542
543        impl BcsSignable<'_> for Foo {}
544
545        let keypair1 = EvmKeyPair::generate();
546        let keypair2 = EvmKeyPair::generate();
547
548        let ts = TestString("hello".into());
549        let tsx = TestString("hellox".into());
550        let foo = Foo("hello".into());
551
552        let s = EvmSignature::new(&ts, &keypair1.secret_key);
553        assert!(s.check(&ts, &keypair1.public_key).is_ok());
554        assert!(s.check(&ts, &keypair2.public_key).is_err());
555        assert!(s.check(&tsx, &keypair1.public_key).is_err());
556        assert!(s.check(&foo, &keypair1.public_key).is_err());
557    }
558
559    #[test]
560    fn test_public_key_serialization() {
561        use crate::crypto::secp256k1::evm::EvmPublicKey;
562        let key_in = EvmPublicKey::test_key(0);
563        let s = serde_json::to_string(&key_in).unwrap();
564        let key_out: EvmPublicKey = serde_json::from_str(&s).unwrap();
565        assert_eq!(key_out, key_in);
566
567        let s = bcs::to_bytes(&key_in).unwrap();
568        let key_out: EvmPublicKey = bcs::from_bytes(&s).unwrap();
569        assert_eq!(key_out, key_in);
570    }
571
572    #[test]
573    fn test_secret_key_serialization() {
574        use crate::crypto::secp256k1::evm::{EvmKeyPair, EvmSecretKey};
575        let key_in = EvmKeyPair::generate().secret_key;
576        let s = serde_json::to_string(&key_in).unwrap();
577        let key_out: EvmSecretKey = serde_json::from_str(&s).unwrap();
578        assert_eq!(key_out, key_in);
579    }
580
581    #[test]
582    fn test_signature_serialization() {
583        use crate::crypto::{
584            secp256k1::evm::{EvmKeyPair, EvmSignature},
585            TestString,
586        };
587        let keypair = EvmKeyPair::generate();
588        let sig = EvmSignature::new(&TestString("hello".into()), &keypair.secret_key);
589        let s = serde_json::to_string(&sig).unwrap();
590        let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
591        assert_eq!(sig, sig2);
592
593        let s = bcs::to_bytes(&sig).unwrap();
594        let sig2: EvmSignature = bcs::from_bytes(&s).unwrap();
595        assert_eq!(sig, sig2);
596    }
597
598    #[test]
599    fn public_key_from_str() {
600        use std::str::FromStr;
601
602        use crate::crypto::secp256k1::evm::EvmPublicKey;
603        let key = EvmPublicKey::test_key(0);
604        let s = key.to_string();
605        let key2 = EvmPublicKey::from_str(s.as_str()).unwrap();
606        assert_eq!(key, key2);
607    }
608
609    #[test]
610    fn bytes_repr_compact_public_key() {
611        use crate::crypto::secp256k1::evm::{EvmPublicKey, EVM_SECP256K1_PUBLIC_KEY_SIZE};
612        let key_in: EvmPublicKey = EvmPublicKey::test_key(0);
613        let bytes = key_in.as_bytes();
614        assert!(
615            bytes.len() == EVM_SECP256K1_PUBLIC_KEY_SIZE,
616            "::to_bytes() should return compressed representation"
617        );
618        let key_out = EvmPublicKey::from_bytes(&bytes).unwrap();
619        assert_eq!(key_in, key_out);
620    }
621
622    #[test]
623    fn human_readable_ser() {
624        use crate::crypto::{
625            secp256k1::evm::{EvmKeyPair, EvmSignature},
626            TestString,
627        };
628        let key_pair = EvmKeyPair::generate();
629        let sig = EvmSignature::new(&TestString("hello".into()), &key_pair.secret_key);
630        let s = serde_json::to_string(&sig).unwrap();
631        let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
632        assert_eq!(sig, sig2);
633    }
634}