From c18dfc9c0a06f52819dee597b55b7f58e7aaf112 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Sat, 11 Jul 2026 14:16:41 -0300 Subject: [PATCH] perf(crypto): wire keccak_permute ecall into merkle/transcript/grinding Route the STARK verifier's Keccak-256 usage (Merkle tree backends, Fiat-Shamir transcript, FRI grinding) through the keccak_permute precompile on the riscv64 guest via a new PlatformKeccak256 digest wrapper, falling back to software sha3 on host. The guest previously used sha3's software permutation exclusively, making it the dominant verifier cost. Recursion guest cycles (blowup8 preset): - single query: 5,234,002,718 -> 1,722,846,582 (3.04x) - multi query: 22,494,934,472 -> 7,687,016,693 (2.93x) Also excludes the riscv-only `syscalls` crate from the root workspace: making it reachable from crypto/crypto (a workspace member) via a target-gated path dependency caused Cargo to auto-adopt it as an implicit member, so `cargo test` tried to run its bare-metal unit tests on host and aborted. Adds an end-to-end guest test (keccak_precompile) exercising the ecall-backed sponge against known Keccak-256 vectors, covering the padding edge cases (empty, rate-1, exactly-rate, multi-block input). --- Cargo.lock | 185 ++++++++-- Cargo.toml | 5 + bench_vs/lambda/recursion/Cargo.lock | 3 +- crypto/crypto/Cargo.toml | 3 + .../src/fiat_shamir/default_transcript.rs | 3 +- crypto/crypto/src/hash/mod.rs | 1 + crypto/crypto/src/hash/platform_keccak.rs | 58 +++ .../crypto/src/merkle_tree/backends/types.rs | 2 +- crypto/stark/Cargo.toml | 2 +- crypto/stark/src/grinding.rs | 3 +- .../rust/keccak_precompile/.cargo/config.toml | 5 + .../rust/keccak_precompile/Cargo.lock | 331 ++++++++++++++++++ .../rust/keccak_precompile/Cargo.toml | 9 + .../rust/keccak_precompile/src/main.rs | 26 ++ executor/tests/rust.rs | 32 ++ syscalls/src/keccak.rs | 1 + syscalls/src/random.rs | 5 +- 17 files changed, 634 insertions(+), 40 deletions(-) create mode 100644 crypto/crypto/src/hash/platform_keccak.rs create mode 100644 executor/programs/rust/keccak_precompile/.cargo/config.toml create mode 100644 executor/programs/rust/keccak_precompile/Cargo.lock create mode 100644 executor/programs/rust/keccak_precompile/Cargo.toml create mode 100644 executor/programs/rust/keccak_precompile/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index d2f699b80..c024d877c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,7 +159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -172,7 +172,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -211,7 +211,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -262,6 +262,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -392,7 +398,7 @@ checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -520,7 +526,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -568,6 +574,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + [[package]] name = "const-oid" version = "0.9.6" @@ -761,6 +773,7 @@ version = "0.1.0" dependencies = [ "bincode", "digest", + "lambda-vm-syscalls", "libc", "math", "memmap2", @@ -825,7 +838,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.111", ] [[package]] @@ -836,7 +849,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -877,7 +890,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "unicode-xid", ] @@ -931,7 +944,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -959,6 +972,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +dependencies = [ + "const-default", + "critical-section", + "linked_list_allocator", + "rlsf", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + [[package]] name = "embedded-io" version = "0.4.0" @@ -988,7 +1019,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1533,7 +1564,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1639,7 +1670,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1698,6 +1729,19 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "lambda-vm-syscalls" +version = "0.1.0" +dependencies = [ + "embedded-alloc", + "getrandom 0.2.16", + "getrandom 0.3.4", + "lazy_static", + "rand 0.9.2", + "riscv", + "thiserror 1.0.69", +] + [[package]] name = "lambdaworks-crypto" version = "0.13.0" @@ -1754,6 +1798,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "linked_list_allocator" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -1901,7 +1951,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2032,7 +2082,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2204,7 +2254,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2348,7 +2398,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2408,6 +2458,36 @@ dependencies = [ "digest", ] +[[package]] +name = "riscv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + [[package]] name = "rkyv" version = "0.8.16" @@ -2435,7 +2515,7 @@ checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2448,6 +2528,19 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlsf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "rustversion", + "svgbobdoc", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -2648,7 +2741,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2670,7 +2763,7 @@ version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -2692,7 +2785,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2779,6 +2872,7 @@ dependencies = [ "bincode", "criterion 0.4.0", "crypto", + "digest", "env_logger", "itertools 0.11.0", "libc", @@ -2792,7 +2886,6 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_cbor", - "sha3", "tempfile", "test-log", "thiserror 1.0.69", @@ -2830,7 +2923,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2839,6 +2932,30 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64 0.13.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.111" @@ -2901,7 +3018,7 @@ checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2936,7 +3053,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2947,7 +3064,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3105,7 +3222,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3182,6 +3299,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3288,7 +3411,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -3382,7 +3505,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3393,7 +3516,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3525,7 +3648,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3545,5 +3668,5 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] diff --git a/Cargo.toml b/Cargo.toml index 270825fe1..ec0942ce4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,11 @@ members = [ "crypto/math-cuda", "bin/cli", ] +# Riscv-only bare-metal crate, path-dependent from crypto/crypto (target-gated), +# ethrex-crypto, and guest programs. Without this exclude, Cargo auto-adopts it as +# a member (nothing else claims it), so `cargo test` runs its unit tests on host, +# which link against riscv-only symbols and abort. +exclude = ["syscalls"] resolver = "2" diff --git a/bench_vs/lambda/recursion/Cargo.lock b/bench_vs/lambda/recursion/Cargo.lock index 66048ba81..cd2f493e6 100644 --- a/bench_vs/lambda/recursion/Cargo.lock +++ b/bench_vs/lambda/recursion/Cargo.lock @@ -128,6 +128,7 @@ name = "crypto" version = "0.1.0" dependencies = [ "digest", + "lambda-vm-syscalls", "math", "rand 0.8.6", "rand_chacha 0.3.1", @@ -845,12 +846,12 @@ name = "stark" version = "0.1.0" dependencies = [ "crypto", + "digest", "itertools", "log", "math", "serde", "serde_cbor", - "sha3", "thiserror 1.0.69", ] diff --git a/crypto/crypto/Cargo.toml b/crypto/crypto/Cargo.toml index 6e3731beb..fc8754e1d 100644 --- a/crypto/crypto/Cargo.toml +++ b/crypto/crypto/Cargo.toml @@ -23,6 +23,9 @@ memmap2 = { version = "0.9", optional = true } tempfile = { version = "3", optional = true } libc = { version = "0.2", optional = true } +[target.'cfg(target_arch = "riscv64")'.dependencies] +lambda-vm-syscalls = { path = "../../syscalls" } + [dev-dependencies] math = { path = "../math", features = ["test-utils"] } rand = "0.8.5" diff --git a/crypto/crypto/src/fiat_shamir/default_transcript.rs b/crypto/crypto/src/fiat_shamir/default_transcript.rs index 7c3c0bf99..8ab3eafc3 100644 --- a/crypto/crypto/src/fiat_shamir/default_transcript.rs +++ b/crypto/crypto/src/fiat_shamir/default_transcript.rs @@ -1,6 +1,8 @@ use crate::fiat_shamir::is_transcript::{IsStarkTranscript, IsTranscript}; +use crate::hash::platform_keccak::PlatformKeccak256 as Keccak256; use core::marker::PhantomData; +use digest::Digest; use math::{ field::{ element::FieldElement, @@ -9,7 +11,6 @@ use math::{ traits::ByteConversion, }; use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng}; -use sha3::{Digest, Keccak256}; pub struct DefaultTranscript { hasher: Keccak256, diff --git a/crypto/crypto/src/hash/mod.rs b/crypto/crypto/src/hash/mod.rs index 358ee298c..78f89fca3 100644 --- a/crypto/crypto/src/hash/mod.rs +++ b/crypto/crypto/src/hash/mod.rs @@ -1,2 +1,3 @@ +pub mod platform_keccak; pub mod poseidon; pub mod sha3; diff --git a/crypto/crypto/src/hash/platform_keccak.rs b/crypto/crypto/src/hash/platform_keccak.rs new file mode 100644 index 000000000..96dfbcc6d --- /dev/null +++ b/crypto/crypto/src/hash/platform_keccak.rs @@ -0,0 +1,58 @@ +//! Keccak-256 implementation selected per target: the `keccak_permute` +//! precompile on the riscv64 guest, plain software `sha3::Keccak256` on host. +//! Wraps `lambda_vm_syscalls::keccak::Keccak256` with the `digest` crate +//! traits so it's a drop-in replacement anywhere a `D: Digest` is expected +//! (Merkle tree backends, Fiat-Shamir transcript). + +#[cfg(target_arch = "riscv64")] +mod imp { + use digest::{ + FixedOutput, FixedOutputReset, HashMarker, Output, OutputSizeUser, Reset, Update, + }; + use lambda_vm_syscalls::keccak::Keccak256 as SyscallKeccak256; + + #[derive(Clone, Default)] + pub struct PlatformKeccak256(SyscallKeccak256); + + impl HashMarker for PlatformKeccak256 {} + + impl OutputSizeUser for PlatformKeccak256 { + type OutputSize = digest::typenum::U32; + } + + impl Update for PlatformKeccak256 { + fn update(&mut self, data: &[u8]) { + self.0.update(data); + } + } + + impl FixedOutput for PlatformKeccak256 { + fn finalize_into(self, out: &mut Output) { + let mut bytes = [0u8; 32]; + self.0.finalize(&mut bytes); + out.copy_from_slice(&bytes); + } + } + + impl Reset for PlatformKeccak256 { + fn reset(&mut self) { + *self = Self::default(); + } + } + + impl FixedOutputReset for PlatformKeccak256 { + fn finalize_into_reset(&mut self, out: &mut Output) { + let mut bytes = [0u8; 32]; + self.0.clone().finalize(&mut bytes); + out.copy_from_slice(&bytes); + Reset::reset(self); + } + } +} + +#[cfg(not(target_arch = "riscv64"))] +mod imp { + pub type PlatformKeccak256 = sha3::Keccak256; +} + +pub use imp::PlatformKeccak256; diff --git a/crypto/crypto/src/merkle_tree/backends/types.rs b/crypto/crypto/src/merkle_tree/backends/types.rs index 0c2a30422..2384fda3a 100644 --- a/crypto/crypto/src/merkle_tree/backends/types.rs +++ b/crypto/crypto/src/merkle_tree/backends/types.rs @@ -1,4 +1,4 @@ -use sha3::Keccak256; +use crate::hash::platform_keccak::PlatformKeccak256 as Keccak256; use super::{ field_element::FieldElementBackend, diff --git a/crypto/stark/Cargo.toml b/crypto/stark/Cargo.toml index 9e90e789e..89483bbdf 100644 --- a/crypto/stark/Cargo.toml +++ b/crypto/stark/Cargo.toml @@ -16,7 +16,7 @@ math = { path = "../math", features = [ crypto = { path = "../crypto", features = ["std", "serde"] } thiserror = "1.0.38" log = "0.4.17" -sha3 = "0.10.8" +digest = "0.10.7" serde = { version = "1.0", features = ["derive"] } itertools = "0.11.0" diff --git a/crypto/stark/src/grinding.rs b/crypto/stark/src/grinding.rs index 196b235ea..4666b7946 100644 --- a/crypto/stark/src/grinding.rs +++ b/crypto/stark/src/grinding.rs @@ -1,6 +1,7 @@ +use crypto::hash::platform_keccak::PlatformKeccak256 as Keccak256; +use digest::Digest; #[cfg(feature = "parallel")] use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -use sha3::{Digest, Keccak256}; const PREFIX: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xed]; diff --git a/executor/programs/rust/keccak_precompile/.cargo/config.toml b/executor/programs/rust/keccak_precompile/.cargo/config.toml new file mode 100644 index 000000000..ca99a3f45 --- /dev/null +++ b/executor/programs/rust/keccak_precompile/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.riscv64im-lambda-vm-elf] +rustflags = [ + "--cfg", "getrandom_backend=\"custom\"", + "-C", "passes=lower-atomic" +] diff --git a/executor/programs/rust/keccak_precompile/Cargo.lock b/executor/programs/rust/keccak_precompile/Cargo.lock new file mode 100644 index 000000000..3aa2810f5 --- /dev/null +++ b/executor/programs/rust/keccak_precompile/Cargo.lock @@ -0,0 +1,331 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "embedded-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +dependencies = [ + "const-default", + "critical-section", + "linked_list_allocator", + "rlsf", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "keccak_precompile" +version = "0.1.0" +dependencies = [ + "lambda-vm-syscalls", +] + +[[package]] +name = "lambda-vm-syscalls" +version = "0.1.0" +dependencies = [ + "embedded-alloc", + "getrandom 0.2.17", + "getrandom 0.3.4", + "lazy_static", + "rand", + "riscv", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linked_list_allocator" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "riscv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + +[[package]] +name = "rlsf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "rustversion", + "svgbobdoc", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] diff --git a/executor/programs/rust/keccak_precompile/Cargo.toml b/executor/programs/rust/keccak_precompile/Cargo.toml new file mode 100644 index 000000000..addf6e2da --- /dev/null +++ b/executor/programs/rust/keccak_precompile/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "keccak_precompile" +version = "0.1.0" +edition = "2024" + +[dependencies] +lambda-vm-syscalls = { path = "../../../../syscalls" } diff --git a/executor/programs/rust/keccak_precompile/src/main.rs b/executor/programs/rust/keccak_precompile/src/main.rs new file mode 100644 index 000000000..27d46456e --- /dev/null +++ b/executor/programs/rust/keccak_precompile/src/main.rs @@ -0,0 +1,26 @@ +use lambda_vm_syscalls::keccak::keccak256; +use lambda_vm_syscalls::syscalls; + +// Exercises the `keccak_permute`-ecall-backed sponge (`lambda_vm_syscalls::keccak`) +// against known Keccak-256 vectors: empty input, one rate block minus one byte, +// exactly one rate block, and multi-block input — the padding edge cases a +// single small input can't cover. +pub fn main() { + const RATE_BYTES: usize = 136; + + let empty = keccak256(b""); + let abc = keccak256(b"abc"); + let rate_minus_one = keccak256(&[0x5a; RATE_BYTES - 1]); + let exactly_rate = keccak256(&[0x3c; RATE_BYTES]); + let multi_block_input: Vec = (0..2 * RATE_BYTES + 17).map(|i| i as u8).collect(); + let multi_block = keccak256(&multi_block_input); + + let mut output = Vec::with_capacity(5 * 32); + output.extend_from_slice(&empty); + output.extend_from_slice(&abc); + output.extend_from_slice(&rate_minus_one); + output.extend_from_slice(&exactly_rate); + output.extend_from_slice(&multi_block); + + syscalls::commit(&output); +} diff --git a/executor/tests/rust.rs b/executor/tests/rust.rs index 458a0bd6c..ca5152de1 100644 --- a/executor/tests/rust.rs +++ b/executor/tests/rust.rs @@ -231,6 +231,38 @@ fn test_keccak() { ); } +#[test] +fn test_keccak_precompile() { + fn hex_bytes(hex: &str) -> Vec { + (0..hex.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap()) + .collect() + } + + // Known-answer vectors for the `keccak_permute`-ecall-backed sponge + // (`lambda_vm_syscalls::keccak::keccak256`), computed with the trusted + // `sha3::Keccak256` crate. Covers empty input, one rate block (136 + // bytes) minus one byte, exactly one rate block, and a multi-block + // input — the sponge's padding edge cases. + let expected: Vec = [ + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", + "03c527855334eb2e62b3b9b4d02ab76721707d3dde5fb218369640ee2edc7f3a", + "0c0a7040fb3ca12143e59ee4a8d8dceab069cc4c65e725a146563b6304ed6e72", + "e4a2f4955400b1a0dd4d52f30bdd542bda1f01f36883b8df6271da7a96cc02ef", + ] + .iter() + .flat_map(|hex| hex_bytes(hex)) + .collect(); + + run_program_and_check_public_output( + "./program_artifacts/rust/keccak_precompile.elf", + expected, + vec![], + ); +} + #[test] fn test_stdin_read_panics() { let result = run_program_without_expect("./program_artifacts/rust/stdin_read.elf", vec![]); diff --git a/syscalls/src/keccak.rs b/syscalls/src/keccak.rs index c9e2c6510..56408e339 100644 --- a/syscalls/src/keccak.rs +++ b/syscalls/src/keccak.rs @@ -25,6 +25,7 @@ const DELIMITER: u8 = 0x01; const FINAL_PAD_BIT: u8 = 0x80; /// Incremental Keccak-256 hasher. +#[derive(Clone)] pub struct Keccak256 { state: [u64; 25], buf: [u8; RATE_BYTES], diff --git a/syscalls/src/random.rs b/syscalls/src/random.rs index 18a00e866..430caf0d2 100644 --- a/syscalls/src/random.rs +++ b/syscalls/src/random.rs @@ -41,10 +41,7 @@ pub unsafe extern "C" fn sys_rand(buf: *mut u8, len: usize) { /// /// `dest_ptr` must be valid for writes of `len` bytes. #[unsafe(no_mangle)] -unsafe extern "Rust" fn __getrandom_v03_custom( - dest_ptr: *mut u8, - len: usize, -) -> Result<(), Error> { +unsafe extern "Rust" fn __getrandom_v03_custom(dest_ptr: *mut u8, len: usize) -> Result<(), Error> { print_string("getrandom called\n"); print_string("WARNING: Using getrandom is insecure\n");