Skip to main content

oasis_runtime_sdk/crypto/signature/
secp256k1.rs

1//! Secp256k1 signatures.
2use base64::prelude::*;
3use digest::{consts::U32, Digest, FixedOutput};
4use k256::{
5    self,
6    ecdsa::{
7        self,
8        signature::{
9            hazmat::{PrehashSigner, PrehashVerifier},
10            Signer as _, Verifier as _,
11        },
12    },
13    elliptic_curve::sec1::{FromSec1Point, ToSec1Point},
14    sha2::Sha512_256,
15};
16use rand::TryCryptoRng;
17
18use crate::crypto::signature::{Error, Signature};
19
20/// A Secp256k1 public key (in compressed form).
21#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct PublicKey(k256::Sec1Point);
23
24impl PublicKey {
25    /// Return a byte representation of this public key.
26    pub fn as_bytes(&self) -> &[u8] {
27        self.0.as_bytes()
28    }
29
30    /// Return an alternative byte representation used in deriving Ethereum-compatible addresses.
31    pub fn to_uncompressed_untagged_bytes(&self) -> Vec<u8> {
32        // Our wrapper type only accepts compressed points, so we shouldn't get None.
33        let pk = k256::PublicKey::from_sec1_point(&self.0).unwrap();
34        pk.to_sec1_point(false).as_bytes()[1..].to_vec()
35    }
36
37    /// Derive an Ethereum-compatible address.
38    pub fn to_eth_address(&self) -> Vec<u8> {
39        sha3::Keccak256::digest(self.to_uncompressed_untagged_bytes())[32 - 20..].to_vec()
40    }
41
42    /// Construct a public key from a slice of bytes.
43    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
44        k256::Sec1Point::from_bytes(bytes)
45            .map_err(|_| Error::MalformedPublicKey)
46            .map(PublicKey)
47    }
48
49    /// Verify a signature.
50    pub fn verify(
51        &self,
52        context: &[u8],
53        message: &[u8],
54        signature: &Signature,
55    ) -> Result<(), Error> {
56        let digest = Sha512_256::new()
57            .chain_update(context)
58            .chain_update(message);
59        self.verify_digest(digest, signature)
60            .map_err(|_| Error::VerificationFailed)
61    }
62
63    /// Verify signature without using any domain separation scheme.
64    pub fn verify_raw(&self, message: &[u8], signature: &Signature) -> Result<(), Error> {
65        let sig = ecdsa::Signature::from_der(signature.0.as_ref())
66            .map_err(|_| Error::MalformedSignature)?;
67        let verify_key =
68            ecdsa::VerifyingKey::from_sec1_point(&self.0).map_err(|_| Error::MalformedPublicKey)?;
69        verify_key
70            .verify(message, &sig)
71            .map_err(|_| Error::VerificationFailed)
72    }
73
74    /// Verify signature of a pre-hashed message.
75    pub fn verify_digest<D>(&self, digest: D, signature: &Signature) -> Result<(), Error>
76    where
77        D: Digest + FixedOutput<OutputSize = U32>,
78    {
79        let sig = ecdsa::Signature::from_der(signature.as_ref())
80            .map_err(|_| Error::MalformedSignature)?;
81        let verify_key =
82            ecdsa::VerifyingKey::from_sec1_point(&self.0).map_err(|_| Error::MalformedPublicKey)?;
83        let prehash = digest.finalize_fixed();
84        verify_key
85            .verify_prehash(&prehash, &sig)
86            .map_err(|_| Error::VerificationFailed)
87    }
88}
89
90impl From<&'static str> for PublicKey {
91    fn from(s: &'static str) -> PublicKey {
92        PublicKey::from_bytes(&BASE64_STANDARD.decode(s).unwrap()).unwrap()
93    }
94}
95
96impl cbor::Encode for PublicKey {
97    fn into_cbor_value(self) -> cbor::Value {
98        cbor::Value::ByteString(self.as_bytes().to_vec())
99    }
100}
101
102impl cbor::Decode for PublicKey {
103    fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
104        match value {
105            cbor::Value::ByteString(data) => {
106                Self::from_bytes(&data).map_err(|_| cbor::DecodeError::UnexpectedType)
107            }
108            _ => Err(cbor::DecodeError::UnexpectedType),
109        }
110    }
111}
112
113/// A memory-backed signer for Secp256k1.
114pub struct MemorySigner {
115    sk: ecdsa::SigningKey,
116}
117
118impl MemorySigner {
119    pub fn sign_digest<D>(&self, digest: D) -> Result<Signature, Error>
120    where
121        D: Digest + FixedOutput<OutputSize = U32>,
122    {
123        let prehash = digest.finalize_fixed();
124        let signature: ecdsa::Signature = self
125            .sk
126            .sign_prehash(&prehash)
127            .map_err(|_| Error::SigningError)?;
128        Ok(signature.to_der().as_bytes().to_vec().into())
129    }
130}
131
132impl super::Signer for MemorySigner {
133    fn random(rng: &mut impl TryCryptoRng) -> Result<Self, Error> {
134        let mut seed = [0u8; 32];
135        rng.try_fill_bytes(&mut seed).map_err(|_| Error::RngError)?;
136        Self::new_from_seed(&seed)
137    }
138
139    fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
140        let sk = ecdsa::SigningKey::from_slice(seed).map_err(|_| Error::InvalidArgument)?;
141        Ok(Self { sk })
142    }
143
144    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
145        Ok(Self {
146            sk: ecdsa::SigningKey::from_slice(bytes).map_err(|_| Error::MalformedPrivateKey)?,
147        })
148    }
149
150    fn to_bytes(&self) -> Vec<u8> {
151        self.sk.to_bytes().to_vec()
152    }
153
154    fn public_key(&self) -> super::PublicKey {
155        super::PublicKey::Secp256k1(PublicKey(self.sk.verifying_key().to_sec1_point(true)))
156    }
157
158    fn sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
159        let digest = Sha512_256::new()
160            .chain_update(context)
161            .chain_update(message);
162        let prehash = digest.finalize_fixed();
163        let signature: ecdsa::Signature = self
164            .sk
165            .sign_prehash(&prehash)
166            .map_err(|_| Error::SigningError)?;
167        Ok(signature.to_der().as_bytes().to_vec().into())
168    }
169
170    fn sign_raw(&self, message: &[u8]) -> Result<Signature, Error> {
171        let signature: ecdsa::Signature = self.sk.sign(message);
172        Ok(signature.to_der().as_bytes().to_vec().into())
173    }
174}