Skip to content

Commit db3c674

Browse files
authored
feat: add keccak guest lib (#1070)
1 parent e3062a0 commit db3c674

File tree

12 files changed

+1365
-49
lines changed

12 files changed

+1365
-49
lines changed

Cargo.lock

Lines changed: 952 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"ceno_zkvm",
88
"examples-builder",
99
"examples",
10+
"guest_libs/*",
1011
]
1112
resolver = "2"
1213

@@ -30,6 +31,7 @@ transcript = { git = "https://github.com/scroll-tech/gkr-backend.git", package =
3031
whir = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "whir", rev = "v1.0.0-alpha.6" }
3132
witness = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "witness", rev = "v1.0.0-alpha.6" }
3233

34+
alloy-primitives = "1.3"
3335
anyhow = { version = "1.0", default-features = false }
3436
bincode = "1"
3537
clap = { version = "4.5", features = ["derive"] }

ceno_host/tests/test_elf.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,24 @@ fn test_keccak_no_syscall() -> Result<()> {
622622
Ok(())
623623
}
624624

625+
#[test]
626+
fn test_keccak_guest() -> Result<()> {
627+
let _ = ceno_host::run(
628+
CENO_PLATFORM,
629+
ceno_examples::keccak_lib,
630+
&CenoStdin::default(),
631+
None,
632+
);
633+
634+
let _ = ceno_host::run(
635+
CENO_PLATFORM,
636+
ceno_examples::keccak_native,
637+
&CenoStdin::default(),
638+
None,
639+
);
640+
Ok(())
641+
}
642+
625643
fn unsafe_platform() -> Platform {
626644
let mut platform = CENO_PLATFORM;
627645
platform.unsafe_ecall_nop = true;

ceno_rt/src/syscalls.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub const BN254_FP_MUL: u32 = 0x00_01_01_28;
1313
pub const BN254_FP2_ADD: u32 = 0x00_01_01_29;
1414
pub const BN254_FP2_MUL: u32 = 0x00_01_01_2B;
1515

16+
pub const KECCAK_STATE_WORDS: usize = 25;
17+
1618
/// Based on https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/zkvm/entrypoint/src/syscalls/keccak_permute.rs
1719
/// Executes the Keccak256 permutation on the given state.
1820
///
@@ -21,7 +23,7 @@ pub const BN254_FP2_MUL: u32 = 0x00_01_01_2B;
2123
/// - The caller must ensure that `state` is valid pointer to data that is aligned along a four
2224
/// byte boundary.
2325
#[allow(unused_variables)]
24-
pub fn syscall_keccak_permute(state: &mut [u64; 25]) {
26+
pub fn syscall_keccak_permute(state: &mut [u64; KECCAK_STATE_WORDS]) {
2527
#[cfg(target_os = "zkvm")]
2628
unsafe {
2729
asm!(

examples/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ repository = "https://github.com/scroll-tech/ceno"
1010
version = "0.1.0"
1111

1212
[dependencies]
13+
alloy-primitives = { version = "1.3", features = ["native-keccak"] }
14+
ceno_keccak = { path = "../guest_libs/keccak" }
1315
ceno_rt = { path = "../ceno_rt" }
1416
rand.workspace = true
1517
tiny-keccak.workspace = true

examples/examples/keccak_lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Compute the Keccak-256.
2+
3+
extern crate ceno_rt;
4+
5+
use ceno_keccak::{Hasher, Keccak};
6+
7+
fn main() {
8+
let keccak = Keccak::v256();
9+
let mut output = [0; 32];
10+
let expected = b"\
11+
\xc5\xd2\x46\x01\x86\xf7\x23\x3c\x92\x7e\x7d\xb2\xdc\xc7\x03\xc0\
12+
\xe5\x00\xb6\x53\xca\x82\x27\x3b\x7b\xfa\xd8\x04\x5d\x85\xa4\x70\
13+
";
14+
15+
keccak.finalize(&mut output);
16+
assert_eq!(expected, &output);
17+
18+
let mut keccak = Keccak::v256();
19+
let mut in_and_out: [u8; 32] = [0; 32];
20+
for i in 1..6 {
21+
in_and_out[i as usize - 1] = i
22+
}
23+
let expected = b"\
24+
\x7d\x87\xc5\xea\x75\xf7\x37\x8b\xb7\x01\xe4\x04\xc5\x06\x39\x16\
25+
\x1a\xf3\xef\xf6\x62\x93\xe9\xf3\x75\xb5\xf1\x7e\xb5\x04\x76\xf4\
26+
";
27+
keccak.update(&in_and_out[0..5]);
28+
keccak.finalize(&mut in_and_out);
29+
assert_eq!(expected, &in_and_out);
30+
}

examples/examples/keccak_native.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//! Compute the Keccak-256 using alloy-primitives with native-keccak hook.
2+
3+
extern crate ceno_keccak;
4+
extern crate ceno_rt; // Make sure the native keccak hook is linked in.
5+
6+
use alloy_primitives::keccak256;
7+
8+
fn main() {
9+
let output = keccak256(b"");
10+
let expected = b"\
11+
\xc5\xd2\x46\x01\x86\xf7\x23\x3c\x92\x7e\x7d\xb2\xdc\xc7\x03\xc0\
12+
\xe5\x00\xb6\x53\xca\x82\x27\x3b\x7b\xfa\xd8\x04\x5d\x85\xa4\x70\
13+
";
14+
assert_eq!(expected, &output);
15+
16+
let mut input: [u8; 5] = [0; 5];
17+
for i in 1..6 {
18+
input[i as usize - 1] = i;
19+
}
20+
let output = keccak256(input);
21+
let expected = b"\
22+
\x7d\x87\xc5\xea\x75\xf7\x37\x8b\xb7\x01\xe4\x04\xc5\x06\x39\x16\
23+
\x1a\xf3\xef\xf6\x62\x93\xe9\xf3\x75\xb5\xf1\x7e\xb5\x04\x76\xf4\
24+
";
25+
assert_eq!(expected, &output);
26+
}

guest_libs/keccak/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
categories = ["cryptography", "zk", "blockchain", "ceno"]
3+
description = "Ceno zkVM Keccak Guest Library"
4+
edition = "2021"
5+
keywords = ["cryptography", "zk", "blockchain", "ceno"]
6+
license = "MIT OR Apache-2.0"
7+
name = "ceno_keccak"
8+
readme = "README.md"
9+
repository = "https://github.com/scroll-tech/ceno"
10+
version = "0.1.0"
11+
12+
[dependencies]
13+
ceno_rt = { path = "../../ceno_rt" }
14+
tiny-keccak = { workspace = true }

guest_libs/keccak/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Ceno Keccak zkVM Guest Library
2+
3+
Copied and modified from [tiny-keccak](https://github.com/debris/tiny-keccak)
4+
under CC0 license.

guest_libs/keccak/src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! Ceno Keccak zkVM Guest Library
2+
3+
#![no_std]
4+
#![deny(missing_docs)]
5+
extern crate alloc;
6+
7+
/// Re-export the `tiny_keccak` crate's `Hasher` trait.
8+
pub use tiny_keccak::{self, Hasher};
9+
10+
mod vendor;
11+
pub use vendor::keccak::Keccak;
12+
13+
pub use ceno_rt::syscalls::syscall_keccak_permute as keccakf;
14+
15+
mod keccakf {
16+
use crate::{
17+
keccakf,
18+
vendor::{Buffer, Permutation},
19+
};
20+
21+
pub struct KeccakF;
22+
23+
impl Permutation for KeccakF {
24+
fn execute(buffer: &mut Buffer) {
25+
keccakf(buffer.words());
26+
}
27+
}
28+
}
29+
30+
/// Native hook for keccak256 for use with `alloy-primitives` "native-keccak" feature.
31+
///
32+
/// # Safety
33+
///
34+
/// The VM accepts the preimage by pointer and length, and writes the
35+
/// 32-byte hash.
36+
/// - `bytes` must point to an input buffer at least `len` long.
37+
/// - `output` must point to a buffer that is at least 32-bytes long.
38+
///
39+
/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3
40+
/// [`sha3`]: https://docs.rs/sha3/latest/sha3/
41+
/// [`tiny_keccak`]: https://docs.rs/tiny-keccak/latest/tiny_keccak/
42+
#[inline(always)]
43+
#[no_mangle]
44+
pub unsafe extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) {
45+
use crate::{Hasher, Keccak};
46+
47+
unsafe {
48+
let input = core::slice::from_raw_parts(bytes, len);
49+
let out = core::slice::from_raw_parts_mut(output, 32);
50+
let mut hasher = Keccak::v256();
51+
hasher.update(input);
52+
hasher.finalize(out);
53+
}
54+
}

0 commit comments

Comments
 (0)