Skip to content

Commit

Permalink
Merge branch 'master' into ao-prefix-iter-optimized
Browse files Browse the repository at this point in the history
* master:
  keccak-hash: fix bench and add one for range (#372)
  [parity-crypto] Release 0.6.1 (#373)
  keccak-hash: bump version to 0.5.1 (#371)
  keccak-hash: add keccak256_range and keccak512_range functions (#370)
  Allow pubkey recovery for all-zero messages (#369)
  • Loading branch information
ordian committed Apr 14, 2020
2 parents 1acaefc + 0bba566 commit ddb330e
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ script:
cargo fmt -- --check;
fi
- cargo check --workspace --tests --benches
- cargo test --all --exclude uint --exclude fixed-hash --exclude parity-crypto
- cargo test --workspace --exclude uint --exclude fixed-hash --exclude parity-crypto
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then
cd contract-address/ && cargo test --features=external_doc && cd ..;
fi
Expand Down
6 changes: 5 additions & 1 deletion keccak-hash/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ The format is based on [Keep a Changelog].

## [Unreleased]

## [0.4.2] - 2020-03-16
## [0.5.1] - 2020-04-10
- Added `keccak256_range` and `keccak512_range` functions. [#370](https://github.com/paritytech/parity-common/pull/370)

## [0.5.0] - 2020-03-16
- License changed from GPL3 to dual MIT/Apache2. [#342](https://github.com/paritytech/parity-common/pull/342)
- Updated dependencies. [#361](https://github.com/paritytech/parity-common/pull/361)
- Updated tiny-keccak. [#260](https://github.com/paritytech/parity-common/pull/260)

## [0.4.1] - 2019-10-24
### Dependencies
Expand Down
6 changes: 5 additions & 1 deletion keccak-hash/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "keccak-hash"
version = "0.5.0"
version = "0.5.1"
description = "`keccak-hash` is a set of utility functions to facilitate working with Keccak hashes (256/512 bits long)."
authors = ["Parity Technologies <[email protected]>"]
repository = "https://github.com/paritytech/parity-common"
Expand All @@ -19,3 +19,7 @@ criterion = "0.3.0"
[features]
default = ["std"]
std = []

[[bench]]
name = "keccak_256"
harness = false
18 changes: 16 additions & 2 deletions keccak-hash/benches/keccak_256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,26 @@ pub fn keccak_256_with_empty_input(c: &mut Criterion) {
}

pub fn keccak_256_with_typical_input(c: &mut Criterion) {
let data: Vec<u8> = From::from("some medium length string with important information");
c.bench_function("keccak_256_with_typical_input", |b| {
let mut data: Vec<u8> = From::from("some medium length string with important information");
let len = data.len();
let mut group = c.benchmark_group("keccak_256_with_typical_input");
group.bench_function("regular", |b| {
b.iter(|| {
let _out = keccak(black_box(&data));
})
});
group.bench_function("inplace", |b| {
b.iter(|| {
keccak_hash::keccak256(black_box(&mut data[..]));
})
});
group.bench_function("inplace_range", |b| {
b.iter(|| {
keccak_hash::keccak256_range(black_box(&mut data[..]), 0..len);
})
});

group.finish();
}

pub fn keccak_256_with_large_input(c: &mut Criterion) {
Expand Down
51 changes: 51 additions & 0 deletions keccak-hash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,64 @@ pub fn keccak256(data: &mut [u8]) {
keccak256.finalize(data);
}

/// Computes in-place keccak256 hash of `data[range]`.
///
/// The `range` argument specifies a subslice of `data` in bytes to be hashed.
/// The resulting hash will be written back to `data`.
/// # Panics
///
/// If `range` is out of bounds.
///
/// # Example
///
/// ```
/// let mut data = [1u8; 32];
/// // Hash the first 8 bytes of `data` and write the result, 32 bytes, to `data`.
/// keccak_hash::keccak256_range(&mut data, 0..8);
/// let expected = [
/// 0x54, 0x84, 0x4f, 0x69, 0xb4, 0xda, 0x4b, 0xb4, 0xa9, 0x9f, 0x24, 0x59, 0xb5, 0x11, 0xd4, 0x42,
/// 0xcc, 0x5b, 0xd2, 0xfd, 0xf4, 0xc3, 0x54, 0xd2, 0x07, 0xbb, 0x13, 0x08, 0x94, 0x43, 0xaf, 0x68,
/// ];
/// assert_eq!(&data, &expected);
/// ```
pub fn keccak256_range(data: &mut [u8], range: core::ops::Range<usize>) {
let mut keccak256 = Keccak::v256();
keccak256.update(&data[range]);
keccak256.finalize(data);
}

/// Computes in-place keccak512 hash of `data`.
pub fn keccak512(data: &mut [u8]) {
let mut keccak512 = Keccak::v512();
keccak512.update(data.as_ref());
keccak512.finalize(data);
}

/// Computes in-place keccak512 hash of `data[range]`.
///
/// The `range` argument specifies a subslice of `data` in bytes to be hashed.
/// The resulting hash will be written back to `data`.
/// # Panics
///
/// If `range` is out of bounds.
///
/// # Example
///
/// ```
/// let mut data = [1u8; 64];
/// keccak_hash::keccak512_range(&mut data, 0..8);
/// let expected = [
/// 0x90, 0x45, 0xc5, 0x9e, 0xd3, 0x0e, 0x1f, 0x42, 0xac, 0x35, 0xcc, 0xc9, 0x55, 0x7c, 0x77, 0x17,
/// 0xc8, 0x89, 0x3a, 0x77, 0x6c, 0xea, 0x2e, 0xf3, 0x88, 0xea, 0xe5, 0xc0, 0xea, 0x40, 0x26, 0x64,
/// ];
/// assert_eq!(&data[..32], &expected);
/// ```
pub fn keccak512_range(data: &mut [u8], range: core::ops::Range<usize>) {
let mut keccak512 = Keccak::v512();
keccak512.update(&data[range]);
keccak512.finalize(data);
}

pub fn keccak_256(input: &[u8], output: &mut [u8]) {
write_keccak(input, output);
}
Expand Down
3 changes: 3 additions & 0 deletions parity-crypto/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog].

## [Unreleased]

## [0.6.1] - 2020-04-11
- Add `recover_allowing_all_zero_message()` and `ZeroesAllowedMessage` to accomodate ethereum's `ecrecover` builtin. [#369](https://github.com/paritytech/parity-common/pull/369)

## [0.6.0] - 2020-03-16
- License changed from GPL3 to dual MIT/Apache2. [#342](https://github.com/paritytech/parity-common/pull/342)
- Updated dependencies. [#361](https://github.com/paritytech/parity-common/pull/361)
Expand Down
2 changes: 1 addition & 1 deletion parity-crypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "parity-crypto"
version = "0.6.0"
version = "0.6.1"
authors = ["Parity Technologies <[email protected]>"]
repository = "https://github.com/paritytech/parity-common"
description = "Crypto utils used by ethstore and network."
Expand Down
62 changes: 56 additions & 6 deletions parity-crypto/src/publickey/ecdsa_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

//! Signature based on ECDSA, algorithm's description: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm

use super::{public_to_address, Address, Error, Message, Public, Secret, SECP256K1};
use super::{public_to_address, Address, Error, Message, Public, Secret, ZeroesAllowedMessage, SECP256K1};
use ethereum_types::{H256, H520};
use rustc_hex::{FromHex, ToHex};
use secp256k1::key::{PublicKey, SecretKey};
Expand Down Expand Up @@ -246,22 +246,56 @@ pub fn verify_address(address: &Address, signature: &Signature, message: &Messag

/// Recovers the public key from the signature for the message
pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> {
let context = &SECP256K1;
let rsig = RecoverableSignature::from_compact(&signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?;
let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?;
let pubkey = &SECP256K1.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?;
let serialized = pubkey.serialize_uncompressed();

let mut public = Public::default();
public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
Ok(public)
}

/// Recovers the public key from the signature for the given message.
/// This version of `recover()` allows for all-zero messages, which is necessary
/// for ethereum but is otherwise highly discouraged. Use with caution.
pub fn recover_allowing_all_zero_message(
signature: &Signature,
message: ZeroesAllowedMessage,
) -> Result<Public, Error> {
let rsig = RecoverableSignature::from_compact(&signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?;
let pubkey = &SECP256K1.recover(&message.into(), &rsig)?;
let serialized = pubkey.serialize_uncompressed();
let mut public = Public::zero();
public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
Ok(public)
}

#[cfg(test)]
mod tests {
use super::super::{Generator, Message, Random};
use super::{recover, sign, verify_address, verify_public, Signature};
use super::super::{Generator, Message, Random, SECP256K1};
use super::{
recover, recover_allowing_all_zero_message, sign, verify_address, verify_public, Secret, Signature,
ZeroesAllowedMessage,
};
use secp256k1::SecretKey;
use std::str::FromStr;

// Copy of `sign()` that allows signing all-zero Messages.
// Note: this is for *tests* only. DO NOT USE UNLESS YOU NEED IT.
fn sign_zero_message(secret: &Secret) -> Signature {
let context = &SECP256K1;
let sec = SecretKey::from_slice(secret.as_ref()).unwrap();
// force an all-zero message into a secp `Message` bypassing the validity check.
let zero_msg = ZeroesAllowedMessage(Message::zero());
let s = context.sign_recoverable(&zero_msg.into(), &sec);
let (rec_id, data) = s.serialize_compact();
let mut data_arr = [0; 65];

// no need to check if s is low, it always is
data_arr[0..64].copy_from_slice(&data[0..64]);
data_arr[64] = rec_id.to_i32() as u8;
Signature(data_arr)
}

#[test]
fn vrs_conversion() {
// given
Expand Down Expand Up @@ -295,6 +329,22 @@ mod tests {
assert_eq!(keypair.public(), &recover(&signature, &message).unwrap());
}

#[test]
fn sign_and_recover_public_fails_with_zeroed_messages() {
let keypair = Random.generate();
let signature = sign_zero_message(keypair.secret());
let zero_message = Message::zero();
assert!(&recover(&signature, &zero_message).is_err());
}

#[test]
fn recover_allowing_all_zero_message_can_recover_from_all_zero_messages() {
let keypair = Random.generate();
let signature = sign_zero_message(keypair.secret());
let zero_message = ZeroesAllowedMessage(Message::zero());
assert_eq!(keypair.public(), &recover_allowing_all_zero_message(&signature, zero_message).unwrap())
}

#[test]
fn sign_and_verify_public() {
let keypair = Random.generate();
Expand Down
20 changes: 19 additions & 1 deletion parity-crypto/src/publickey/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ pub mod ecdh;
pub mod ecies;
pub mod error;

pub use self::ecdsa_signature::{recover, sign, verify_address, verify_public, Signature};
pub use self::ecdsa_signature::{
recover, recover_allowing_all_zero_message, sign, verify_address, verify_public, Signature,
};
pub use self::error::Error;
pub use self::extended_keys::{Derivation, DerivationError, ExtendedKeyPair, ExtendedPublic, ExtendedSecret};
pub use self::keypair::{public_to_address, KeyPair};
Expand All @@ -33,6 +35,22 @@ use lazy_static::lazy_static;
pub use ethereum_types::{Address, Public};
pub type Message = H256;

use secp256k1::ThirtyTwoByteHash;

/// In ethereum we allow public key recovery from a signature + message pair
/// where the message is all-zeroes. This conflicts with the best practise of
/// not allowing such values and so in order to avoid breaking consensus we need
/// this to work around it. The `ZeroesAllowedType` wraps an `H256` that can be
/// converted to a `[u8; 32]` which in turn can be cast to a
/// `secp256k1::Message` by the `ThirtyTwoByteHash` and satisfy the API for
/// `recover()`.
pub struct ZeroesAllowedMessage(pub H256);
impl ThirtyTwoByteHash for ZeroesAllowedMessage {
fn into_32(self) -> [u8; 32] {
self.0.to_fixed_bytes()
}
}

/// The number -1 encoded as a secret key
const MINUS_ONE_KEY: &'static [u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc,
Expand Down

0 comments on commit ddb330e

Please sign in to comment.