1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Signing capabilities and utilities.

use crate::types::H256;

/// Error during signing.
#[derive(Debug, derive_more::Display, PartialEq, Clone)]
pub enum SigningError {
    /// A message to sign is invalid. Has to be a non-zero 32-bytes slice.
    #[display(fmt = "Message has to be a non-zero 32-bytes slice.")]
    InvalidMessage,
}
impl std::error::Error for SigningError {}

/// Error during sender recovery.
#[derive(Debug, derive_more::Display, PartialEq, Clone)]
pub enum RecoveryError {
    /// A message to recover is invalid. Has to be a non-zero 32-bytes slice.
    #[display(fmt = "Message has to be a non-zero 32-bytes slice.")]
    InvalidMessage,
    /// A signature is invalid and the sender could not be recovered.
    #[display(fmt = "Signature is invalid (check recovery id).")]
    InvalidSignature,
}
impl std::error::Error for RecoveryError {}

#[cfg(feature = "signing")]
pub use feature_gated::*;

#[cfg(feature = "signing")]
mod feature_gated {
    use super::*;

    use secp256k1::recovery::{RecoverableSignature, RecoveryId};
    pub(crate) use secp256k1::SecretKey;
    use secp256k1::{Message, PublicKey, Secp256k1};
    use std::ops::Deref;

    use crate::types::Address;

    /// A trait representing ethereum-compatible key with signing capabilities.
    ///
    /// The purpose of this trait is to prevent leaking `secp256k1::SecretKey` struct
    /// in stack or memory.
    /// To use secret keys securely, they should be wrapped in a struct that prevents
    /// leaving copies in memory (both when it's moved or dropped). Please take a look
    /// at:
    /// - https://github.com/graphprotocol/solidity-bindgen/blob/master/solidity-bindgen/src/secrets.rs
    /// - or https://crates.io/crates/zeroize
    /// if you care enough about your secrets to be used securely.
    ///
    /// If it's enough to pass a reference to `SecretKey` (lifetimes) than you can use `SecretKeyRef`
    /// wrapper.
    pub trait Key {
        /// Sign given message and include chain-id replay protection.
        ///
        /// When a chain ID is provided, the `Signature`'s V-value will have chain relay
        /// protection added (as per EIP-155). Otherwise, the V-value will be in
        /// 'Electrum' notation.
        fn sign(&self, message: &[u8], chain_id: Option<u64>) -> Result<Signature, SigningError>;

        /// Get public address that this key represents.
        fn address(&self) -> Address;
    }

    /// A `SecretKey` reference wrapper.
    ///
    /// A wrapper around `secp256k1::SecretKey` reference, which enables it to be used in methods expecting
    /// `Key` capabilities.
    pub struct SecretKeyRef<'a> {
        pub(super) key: &'a SecretKey,
    }

    impl<'a> SecretKeyRef<'a> {
        /// A simple wrapper around a reference to `SecretKey` which allows it to be usable for signing.
        pub fn new(key: &'a SecretKey) -> Self {
            Self { key }
        }
    }

    impl<'a> From<&'a SecretKey> for SecretKeyRef<'a> {
        fn from(key: &'a SecretKey) -> Self {
            Self::new(key)
        }
    }

    impl<'a> Deref for SecretKeyRef<'a> {
        type Target = SecretKey;

        fn deref(&self) -> &Self::Target {
            &self.key
        }
    }

    impl<T: Deref<Target = SecretKey>> Key for T {
        fn sign(&self, message: &[u8], chain_id: Option<u64>) -> Result<Signature, SigningError> {
            let message = Message::from_slice(&message).map_err(|_| SigningError::InvalidMessage)?;
            let (recovery_id, signature) = Secp256k1::signing_only()
                .sign_recoverable(&message, self)
                .serialize_compact();

            let standard_v = recovery_id.to_i32() as u64;
            let v = if let Some(chain_id) = chain_id {
                // When signing with a chain ID, add chain replay protection.
                standard_v + 35 + chain_id * 2
            } else {
                // Otherwise, convert to 'Electrum' notation.
                standard_v + 27
            };
            let r = H256::from_slice(&signature[..32]);
            let s = H256::from_slice(&signature[32..]);

            Ok(Signature { v, r, s })
        }

        fn address(&self) -> Address {
            secret_key_address(self)
        }
    }

    /// Recover a sender, given message and the signature.
    ///
    /// Signature and `recovery_id` can be obtained from `types::Recovery` type.
    pub fn recover(message: &[u8], signature: &[u8], recovery_id: i32) -> Result<Address, RecoveryError> {
        let message = Message::from_slice(message).map_err(|_| RecoveryError::InvalidMessage)?;
        let recovery_id = RecoveryId::from_i32(recovery_id).map_err(|_| RecoveryError::InvalidSignature)?;
        let signature =
            RecoverableSignature::from_compact(&signature, recovery_id).map_err(|_| RecoveryError::InvalidSignature)?;
        let public_key = Secp256k1::verification_only()
            .recover(&message, &signature)
            .map_err(|_| RecoveryError::InvalidSignature)?;

        Ok(public_key_address(&public_key))
    }

    /// Gets the address of a public key.
    ///
    /// The public address is defined as the low 20 bytes of the keccak hash of
    /// the public key. Note that the public key returned from the `secp256k1`
    /// crate is 65 bytes long, that is because it is prefixed by `0x04` to
    /// indicate an uncompressed public key; this first byte is ignored when
    /// computing the hash.
    pub(crate) fn public_key_address(public_key: &PublicKey) -> Address {
        let public_key = public_key.serialize_uncompressed();

        debug_assert_eq!(public_key[0], 0x04);
        let hash = keccak256(&public_key[1..]);

        Address::from_slice(&hash[12..])
    }

    /// Gets the public address of a private key.
    pub(crate) fn secret_key_address(key: &SecretKey) -> Address {
        let secp = Secp256k1::signing_only();
        let public_key = PublicKey::from_secret_key(&secp, key);
        public_key_address(&public_key)
    }
}

/// A struct that represents the components of a secp256k1 signature.
pub struct Signature {
    /// V component in electrum format with chain-id replay protection.
    pub v: u64,
    /// R component of the signature.
    pub r: H256,
    /// S component of the signature.
    pub s: H256,
}

/// Compute the Keccak-256 hash of input bytes.
pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
    use tiny_keccak::{Hasher, Keccak};
    let mut output = [0u8; 32];
    let mut hasher = Keccak::v256();
    hasher.update(bytes);
    hasher.finalize(&mut output);
    output
}