Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
490 changes: 398 additions & 92 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ethers-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ arrayvec = { version = "0.7.2", default-features = false }
rlp-derive = { version = "0.1.0", default-features = false }

# crypto
elliptic-curve = { version = "0.12.3", default-features = false }
elliptic-curve = { version = "0.13.2", default-features = false }
generic-array = { version = "0.14.6", default-features = false }
k256 = { version = "0.11", default-features = false, features = ["keccak256", "ecdsa", "std"] }
k256 = { version = "0.13.0", default-features = false, features = [ "ecdsa", "std"] }
rand = { version = "0.8.5", default-features = false }
tiny-keccak = { version = "2.0.2", default-features = false }

Expand Down
21 changes: 12 additions & 9 deletions ethers-core/src/types/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use elliptic_curve::{consts::U32, sec1::ToEncodedPoint};
use generic_array::GenericArray;
use k256::{
ecdsa::{
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
Error as K256SignatureError, Signature as K256Signature,
Error as K256SignatureError, RecoveryId, Signature as RecoverableSignature,
Signature as K256Signature, VerifyingKey,
},
PublicKey as K256PublicKey,
};
Expand Down Expand Up @@ -79,7 +79,7 @@ impl Signature {
let address = address.into();
let recovered = self.recover(message)?;
if recovered != address {
return Err(SignatureError::VerificationError(address, recovered))
return Err(SignatureError::VerificationError(address, recovered));
}

Ok(())
Expand All @@ -99,9 +99,12 @@ impl Signature {
RecoveryMessage::Hash(hash) => hash,
};

let (recoverable_sig, _recovery_id) = self.as_signature()?;
let verify_key = recoverable_sig
.recover_verifying_key_from_digest_bytes(message_hash.as_ref().into())?;
let (recoverable_sig, recovery_id) = self.as_signature()?;
let verify_key = VerifyingKey::recover_from_prehash(
message_hash.as_ref(),
&recoverable_sig,
recovery_id,
)?;

let public_key = K256PublicKey::from(&verify_key);
let public_key = public_key.to_encoded_point(/* compress = */ false);
Expand All @@ -122,7 +125,7 @@ impl Signature {
let gar: &GenericArray<u8, U32> = GenericArray::from_slice(&r_bytes);
let gas: &GenericArray<u8, U32> = GenericArray::from_slice(&s_bytes);
let sig = K256Signature::from_scalars(*gar, *gas)?;
RecoverableSignature::new(&sig, recovery_id)?
K256Signature::from_scalars(*gar, *gas)?
};

Ok((signature, recovery_id))
Expand All @@ -131,7 +134,7 @@ impl Signature {
/// Retrieve the recovery ID.
pub fn recovery_id(&self) -> Result<RecoveryId, SignatureError> {
let standard_v = normalize_recovery_id(self.v);
Ok(RecoveryId::new(standard_v)?)
Ok(RecoveryId::from_byte(standard_v).expect("normalized recovery id always valid"))
}

/// Copies and serializes `self` into a new `Vec` with the recovery id included
Expand Down Expand Up @@ -183,7 +186,7 @@ impl<'a> TryFrom<&'a [u8]> for Signature {
/// and the final byte is the `v` value in 'Electrum' notation.
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
if bytes.len() != 65 {
return Err(SignatureError::InvalidLength(bytes.len()))
return Err(SignatureError::InvalidLength(bytes.len()));
}

let v = bytes[64];
Expand Down
10 changes: 6 additions & 4 deletions ethers-core/src/utils/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
types::{Address, Chain},
utils::{secret_key_to_address, unused_port},
};
use generic_array::GenericArray;
use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey};
use std::{
io::{BufRead, BufReader},
Expand Down Expand Up @@ -259,16 +260,16 @@ impl Anvil {
let mut addresses = Vec::new();
let mut is_private_key = false;
loop {
if start + Duration::from_millis(self.timeout.unwrap_or(ANVIL_STARTUP_TIMEOUT_MILLIS)) <=
Instant::now()
if start + Duration::from_millis(self.timeout.unwrap_or(ANVIL_STARTUP_TIMEOUT_MILLIS))
<= Instant::now()
{
panic!("Timed out waiting for anvil to start. Is anvil installed?")
}

let mut line = String::new();
reader.read_line(&mut line).expect("Failed to read line from anvil process");
if line.contains("Listening on") {
break
break;
}

if line.starts_with("Private Keys") {
Expand All @@ -278,7 +279,8 @@ impl Anvil {
if is_private_key && line.starts_with('(') {
let key_str = &line[6..line.len() - 1];
let key_hex = hex::decode(key_str).expect("could not parse as hex");
let key = K256SecretKey::from_be_bytes(&key_hex).expect("did not get private key");
let key = K256SecretKey::from_bytes(&GenericArray::clone_from_slice(&key_hex))
Comment thread
keefertaylor marked this conversation as resolved.
.expect("did not get private key");
addresses.push(secret_key_to_address(&SigningKey::from(&key)));
private_keys.push(key);
}
Expand Down
6 changes: 4 additions & 2 deletions ethers-core/src/utils/ganache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
types::Address,
utils::{secret_key_to_address, unused_port},
};
use generic_array::GenericArray;
use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey};
use std::{
io::{BufRead, BufReader},
Expand Down Expand Up @@ -195,7 +196,7 @@ impl Ganache {
let mut line = String::new();
reader.read_line(&mut line).expect("Failed to read line from ganache process");
if line.contains("Listening on") {
break
break;
}

if line.starts_with("Private Keys") {
Expand All @@ -205,7 +206,8 @@ impl Ganache {
if is_private_key && line.starts_with('(') {
let key_str = &line[6..line.len() - 1];
let key_hex = hex::decode(key_str).expect("could not parse as hex");
let key = K256SecretKey::from_be_bytes(&key_hex).expect("did not get private key");
let key = K256SecretKey::from_bytes(&GenericArray::clone_from_slice(&key_hex))
Comment thread
keefertaylor marked this conversation as resolved.
.expect("did not get private key");
addresses.push(secret_key_to_address(&SigningKey::from(&key)));
private_keys.push(key);
}
Expand Down
15 changes: 7 additions & 8 deletions ethers-core/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ pub use rlp;
pub use hex;

use crate::types::{Address, Bytes, ParseI256Error, I256, U256};
use elliptic_curve::sec1::ToEncodedPoint;
use ethabi::ethereum_types::FromDecStrErr;
use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
use k256::ecdsa::SigningKey;
use std::{
convert::{TryFrom, TryInto},
fmt,
Expand Down Expand Up @@ -377,7 +376,7 @@ pub fn get_create2_address_from_hash(

/// Converts a K256 SigningKey to an Ethereum Address
pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
let public_key = K256PublicKey::from(&secret_key.verifying_key());
let public_key = secret_key.verifying_key();
let public_key = public_key.to_encoded_point(/* compress = */ false);
let public_key = public_key.as_bytes();
debug_assert_eq!(public_key[0], 0x04);
Expand Down Expand Up @@ -413,7 +412,7 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
pub fn format_bytes32_string(text: &str) -> Result<[u8; 32], ConversionError> {
let str_bytes: &[u8] = text.as_bytes();
if str_bytes.len() > 32 {
return Err(ConversionError::TextTooLong)
return Err(ConversionError::TextTooLong);
}

let mut bytes32: [u8; 32] = [0u8; 32];
Expand Down Expand Up @@ -451,10 +450,10 @@ fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {
let mut rewards: Vec<U256> =
rewards.iter().map(|r| r[0]).filter(|r| *r > U256::zero()).collect();
if rewards.is_empty() {
return U256::zero()
return U256::zero();
}
if rewards.len() == 1 {
return rewards[0]
return rewards[0];
}
// Sort the rewards as we will eventually take the median.
rewards.sort();
Expand All @@ -481,8 +480,8 @@ fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {

// If we encountered a big change in fees at a certain position, then consider only
// the values >= it.
let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into() &&
(max_change_index >= (rewards.len() / 2))
let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into()
&& (max_change_index >= (rewards.len() / 2))
{
rewards[max_change_index..].to_vec()
} else {
Expand Down
3 changes: 2 additions & 1 deletion ethers-core/src/utils/moonbeam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::collections::BTreeMap;

use generic_array::GenericArray;
use k256::SecretKey;

/// Returns the private developer keys <https://docs.moonbeam.network/builders/get-started/networks/moonbeam-dev/#pre-funded-development-accounts>
Expand Down Expand Up @@ -47,7 +48,7 @@ impl MoonbeamDev {
}

fn to_secret_key(s: &str) -> SecretKey {
SecretKey::from_be_bytes(&hex::decode(s).unwrap()).unwrap()
SecretKey::from_bytes(&GenericArray::clone_from_slice(&hex::decode(s).unwrap())).unwrap()
}

impl Default for MoonbeamDev {
Expand Down
6 changes: 3 additions & 3 deletions ethers-signers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ coins-bip39 = "0.7.0"
coins-ledger = { version = "0.7.0", default-features = false, optional = true }
hex = { version = "0.4.3", default-features = false, features = ["std"] }
async-trait = { version = "0.1.50", default-features = false }
elliptic-curve = { version = "0.12.3", default-features = false }
elliptic-curve = { version = "0.13.2", default-features = false }
sha2 = { version = "0.10.6", default-features = false }
rand = { version = "0.8.5", default-features = false }
yubihsm = { version = "0.41.0", features = ["secp256k1", "http", "usb"], optional = true }
yubihsm = { version = "0.42.1", features = ["secp256k1", "http", "usb"], optional = true }
futures-util = { version = "^0.3", optional = true }
futures-executor = { version = "^0.3", optional = true }
semver = { version = "1.0.14", optional = true }
Expand All @@ -48,7 +48,7 @@ ethers-contract-derive = { version = "^1.0.0", path = "../ethers-contract/ethers
ethers-derive-eip712 = { version = "^1.0.0", path = "../ethers-core/ethers-derive-eip712" }
serde_json = { version = "1.0.64" }
tracing-subscriber = "0.3.16"
yubihsm = { version = "0.41.0", features = ["secp256k1", "usb", "mockhsm"] }
yubihsm = { version = "0.42.1", features = ["secp256k1", "usb", "mockhsm"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
yubihsm = { version = "0.41.0", features = ["secp256k1", "usb", "mockhsm"] }
Expand Down
13 changes: 6 additions & 7 deletions ethers-signers/src/aws/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rusoto_kms::{
use tracing::{debug, instrument, trace};

mod utils;
use utils::{apply_eip155, rsig_to_ethsig, verifying_key_to_address};
use utils::{apply_eip155, verifying_key_to_address};

/// An ethers Signer that uses keys held in Amazon AWS KMS.
///
Expand Down Expand Up @@ -57,7 +57,7 @@ impl std::fmt::Debug for AwsSigner {
f.debug_struct("AwsSigner")
.field("key_id", &self.key_id)
.field("chain_id", &self.chain_id)
.field("pubkey", &hex::encode(self.pubkey.to_bytes()))
.field("pubkey", &hex::encode(self.pubkey.to_sec1_bytes()))
.field("address", &self.address)
.finish()
}
Expand Down Expand Up @@ -165,7 +165,7 @@ impl AwsSigner {

debug!(
"Instantiated AWS signer with pubkey 0x{} and address 0x{}",
hex::encode(pubkey.to_bytes()),
hex::encode(pubkey.to_sec1_bytes()),
hex::encode(address)
);

Expand Down Expand Up @@ -212,9 +212,9 @@ impl AwsSigner {
) -> Result<EthSig, AwsSignerError> {
let sig = self.sign_digest(digest.into()).await?;

let sig = utils::rsig_from_digest_bytes_trial_recovery(&sig, digest.into(), &self.pubkey);
let mut sig =
utils::sig_from_digest_bytes_trial_recovery(&sig, digest.into(), &self.pubkey);

let mut sig = rsig_to_ethsig(&sig);
apply_eip155(&mut sig, chain_id);
Ok(sig)
}
Expand Down Expand Up @@ -255,8 +255,7 @@ impl super::Signer for AwsSigner {
payload.encode_eip712().map_err(|e| Self::Error::Eip712Error(e.to_string()))?;

let sig = self.sign_digest(digest).await?;
let sig = utils::rsig_from_digest_bytes_trial_recovery(&sig, digest, &self.pubkey);
let sig = rsig_to_ethsig(&sig);
let sig = utils::sig_from_digest_bytes_trial_recovery(&sig, digest.into(), &self.pubkey);

Ok(sig)
}
Expand Down
50 changes: 20 additions & 30 deletions ethers-signers/src/aws/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ use std::convert::TryFrom;

use ethers_core::{
k256::{
ecdsa::{
recoverable::{Id, Signature as RSig},
Signature as KSig, VerifyingKey,
},
elliptic_curve::sec1::ToEncodedPoint,
ecdsa::{RecoveryId, Signature as RSig, Signature as KSig, VerifyingKey},
FieldBytes,
},
types::{Address, Signature as EthSig, U256},
Expand All @@ -20,40 +16,34 @@ use rusoto_kms::{GetPublicKeyResponse, SignResponse};

use crate::aws::AwsSignerError;

/// Converts a recoverable signature to an ethers signature
pub(super) fn rsig_to_ethsig(sig: &RSig) -> EthSig {
let v: u8 = sig.recovery_id().into();
let v = (v + 27) as u64;
let r_bytes: FieldBytes = sig.r().into();
let s_bytes: FieldBytes = sig.s().into();
let r = U256::from_big_endian(r_bytes.as_slice());
let s = U256::from_big_endian(s_bytes.as_slice());
EthSig { r, s, v }
}

/// Makes a trial recovery to check whether an RSig corresponds to a known
/// `VerifyingKey`
fn check_candidate(sig: &RSig, digest: [u8; 32], vk: &VerifyingKey) -> bool {
if let Ok(key) = sig.recover_verifying_key_from_digest_bytes(digest.as_ref().into()) {
key == *vk
} else {
false
}
fn check_candidate(
sig: &RSig,
recovery_id: RecoveryId,
digest: [u8; 32],
vk: &VerifyingKey,
) -> bool {
VerifyingKey::recover_from_prehash(digest.as_slice(), sig, recovery_id)
.map(|key| key == *vk)
.unwrap_or(false)
}

/// Recover an rsig from a signature under a known key by trial/error
pub(super) fn rsig_from_digest_bytes_trial_recovery(
pub(super) fn sig_from_digest_bytes_trial_recovery(
sig: &KSig,
digest: [u8; 32],
vk: &VerifyingKey,
) -> RSig {
let sig_0 = RSig::new(sig, Id::new(0).unwrap()).unwrap();
let sig_1 = RSig::new(sig, Id::new(1).unwrap()).unwrap();
) -> EthSig {
let r_bytes: FieldBytes = sig.r().into();
let s_bytes: FieldBytes = sig.s().into();
let r = U256::from_big_endian(r_bytes.as_slice());
let s = U256::from_big_endian(s_bytes.as_slice());

if check_candidate(&sig_0, digest, vk) {
sig_0
} else if check_candidate(&sig_1, digest, vk) {
sig_1
if check_candidate(&sig, RecoveryId::from_byte(0).unwrap(), digest, vk) {
EthSig { r, s, v: 0 }
} else if check_candidate(&sig, RecoveryId::from_byte(1).unwrap(), digest, vk) {
EthSig { r, s, v: 1 }
} else {
panic!("bad sig");
}
Expand Down
Loading