Skip to content

Commit

Permalink
feat: support Keccak with sha3 (#737)
Browse files Browse the repository at this point in the history
* feat: support Keccak with sha3

* chore: simplify cfgs

* chore: simplify

---------

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
  • Loading branch information
xJonathanLEI and DaniPopes authored Sep 18, 2024
1 parent 4233f69 commit 9867b55
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 34 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ paste = "1.0"
num_enum = "0.7"
thiserror = "1.0"

digest = "0.10"
k256 = { version = "0.13", default-features = false }
keccak-asm = { version = "0.1.0", default-features = false }
tiny-keccak = "2.0"
tiny-keccak = { version = "2.0", default-features = false }
sha3 = { version = "0.10.8", default-features = false }

# misc
allocative = { version = "0.3.2", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ sol-types = ["dep:alloy-sol-types"]
tiny-keccak = ["alloy-primitives/tiny-keccak"]
native-keccak = ["alloy-primitives/native-keccak"]
asm-keccak = ["alloy-primitives/asm-keccak"]
sha3-keccak = ["alloy-primitives/sha3-keccak"]

postgres = ["std", "alloy-primitives/postgres"]
getrandom = ["alloy-primitives/getrandom"]
Expand Down
9 changes: 7 additions & 2 deletions crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ hex-literal.workspace = true
hex.workspace = true
itoa.workspace = true
ruint.workspace = true
tiny-keccak = { workspace = true, features = ["keccak"] }
keccak-asm = { workspace = true, optional = true }

# macros
derive_more = { workspace = true, features = [
Expand All @@ -47,6 +45,11 @@ derive_more = { workspace = true, features = [
] }
cfg-if.workspace = true

# keccak256
keccak-asm = { workspace = true, optional = true }
sha3 = { workspace = true, optional = true }
tiny-keccak = { workspace = true, features = ["keccak"] }

# rlp
alloy-rlp = { workspace = true, optional = true }

Expand Down Expand Up @@ -92,11 +95,13 @@ std = [
"rand?/std",
"serde?/std",
"k256?/std",
"sha3?/std",
]

tiny-keccak = []
native-keccak = []
asm-keccak = ["dep:keccak-asm"]
sha3-keccak = ["dep:sha3"]

postgres = ["std", "dep:postgres-types", "ruint/postgres"]
getrandom = ["dep:getrandom"]
Expand Down
3 changes: 3 additions & 0 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#[macro_use]
extern crate alloc;

#[cfg(feature = "sha3-keccak")]
use sha3 as _;

use tiny_keccak as _;

#[cfg(feature = "postgres")]
Expand Down
79 changes: 48 additions & 31 deletions crates/primitives/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ pub use units::{
format_ether, format_units, parse_ether, parse_units, ParseUnits, Unit, UnitsError,
};

cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
use keccak_asm::Digest as _;
} else {
use tiny_keccak::Hasher as _;
}
}

#[doc(hidden)]
#[deprecated(since = "0.5.0", note = "use `Unit::ETHER.wei()` instead")]
pub const WEI_IN_ETHER: crate::U256 = Unit::ETHER.wei_const();
Expand Down Expand Up @@ -153,13 +145,13 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
let mut output = MaybeUninit::<B256>::uninit();

cfg_if! {
if #[cfg(all(feature = "native-keccak", not(feature = "tiny-keccak"), not(miri)))] {
if #[cfg(all(feature = "native-keccak", not(any(feature = "sha3-keccak", feature = "tiny-keccak", miri))))] {
#[link(wasm_import_module = "vm_hooks")]
extern "C" {
/// When targeting VMs with native keccak hooks, the `native-keccak` feature
/// can be enabled to import and use the host environment's implementation
/// of [`keccak256`] in place of [`tiny_keccak`]. This is overridden when
/// the `tiny-keccak` feature is enabled.
/// of [`keccak256`] in place of [`sha3`] or [`tiny_keccak`]. This is overridden
/// when the `sha3-keccak` or `tiny-keccak` feature is enabled.
///
/// # Safety
///
Expand All @@ -169,6 +161,7 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
/// - `output` must point to a buffer that is at least 32-bytes long.
///
/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3
/// [`sha3`]: https://docs.rs/sha3/latest/sha3/
/// [`tiny_keccak`]: https://docs.rs/tiny-keccak/latest/tiny_keccak/
fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8);
}
Expand All @@ -190,6 +183,45 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
keccak256(bytes.as_ref())
}

mod keccak256_state {
cfg_if::cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
pub(super) use keccak_asm::Digest;

pub(super) type State = keccak_asm::Keccak256;
} else if #[cfg(feature = "sha3-keccak")] {
pub(super) use sha3::Digest;

pub(super) type State = sha3::Keccak256;
} else {
pub(super) use tiny_keccak::Hasher as Digest;

/// Wraps `tiny_keccak::Keccak` to implement `Digest`-like API.
#[derive(Clone)]
pub(super) struct State(tiny_keccak::Keccak);

impl State {
#[inline]
pub(super) fn new() -> Self {
Self(tiny_keccak::Keccak::v256())
}

#[inline]
pub(super) fn finalize_into(self, output: &mut [u8; 32]) {
self.0.finalize(output);
}

#[inline]
pub(super) fn update(&mut self, bytes: &[u8]) {
self.0.update(bytes);
}
}
}
}
}
#[allow(unused_imports)]
use keccak256_state::Digest;

/// Simple [`Keccak-256`] hasher.
///
/// Note that the "native-keccak" feature is not supported for this struct, and will default to the
Expand All @@ -198,10 +230,7 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
/// [`Keccak-256`]: https://en.wikipedia.org/wiki/SHA-3
#[derive(Clone)]
pub struct Keccak256 {
#[cfg(all(feature = "asm-keccak", not(miri)))]
hasher: keccak_asm::Keccak256,
#[cfg(not(all(feature = "asm-keccak", not(miri))))]
hasher: tiny_keccak::Keccak,
state: keccak256_state::State,
}

impl Default for Keccak256 {
Expand All @@ -222,20 +251,13 @@ impl Keccak256 {
/// Creates a new [`Keccak256`] hasher.
#[inline]
pub fn new() -> Self {
cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
let hasher = keccak_asm::Keccak256::new();
} else {
let hasher = tiny_keccak::Keccak::v256();
}
}
Self { hasher }
Self { state: keccak256_state::State::new() }
}

/// Absorbs additional input. Can be called multiple times.
#[inline]
pub fn update(&mut self, bytes: impl AsRef<[u8]>) {
self.hasher.update(bytes.as_ref());
self.state.update(bytes.as_ref());
}

/// Pad and squeeze the state.
Expand All @@ -261,14 +283,9 @@ impl Keccak256 {

/// Pad and squeeze the state into `output`.
#[inline]
#[allow(clippy::useless_conversion)]
pub fn finalize_into_array(self, output: &mut [u8; 32]) {
cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
self.hasher.finalize_into(output.into());
} else {
self.hasher.finalize(output);
}
}
self.state.finalize_into(output.into());
}

/// Pad and squeeze the state into `output`.
Expand Down

0 comments on commit 9867b55

Please sign in to comment.