Shake256 with 33 bytes #14
Conversation
There was a problem hiding this comment.
LGTM. I thought of a few improvements to the code. I leave them as comments below.
Also, the project structure I think can be improved as follows to make the host just import a compiled version of the guest programs. Here is the diff in case you want to consider it. You'll need also to add a methods/src/lib.rs with just the content include!(concat!(env!("OUT_DIR"), "/methods.rs")); (and you can delete methods/src/main.rs probably)
serde-big-array = "0.5"
tiny-keccak = { version = "2", default-features = false, features = ["shake"] } # ADD
+methods = { path = "methods" }
[dev-dependencies]
rand = "0.8"
cipher = { version = "0.4", features = ["std"] }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
-
-
-[build-dependencies]
-risc0-build = "2.3.1"
-
-[package.metadata.risc0]
-methods = ["methods/guest"]
-
-[lints.rust]
-unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }
\ No newline at end of file
diff --git a/shake256-33bytes-demo/build.rs b/shake256-33bytes-demo/build.rs
deleted file mode 100644
index 08a8a4e..0000000
--- a/shake256-33bytes-demo/build.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
- risc0_build::embed_methods();
-}
diff --git a/shake256-33bytes-demo/methods/Cargo.toml b/shake256-33bytes-demo/methods/Cargo.toml
index 9ab59da..f5da0df 100644
--- a/shake256-33bytes-demo/methods/Cargo.toml
+++ b/shake256-33bytes-demo/methods/Cargo.toml
@@ -7,4 +7,4 @@ edition = "2021"
risc0-build = { version = "2.3.1" }
[package.metadata.risc0]
-methods = ["methods/guest"]
+methods = ["guest"]
diff --git a/shake256-33bytes-demo/src/lib.rs b/shake256-33bytes-demo/src/lib.rs
index 2bffbc1..61a26a3 100644
--- a/shake256-33bytes-demo/src/lib.rs
+++ b/shake256-33bytes-demo/src/lib.rs
@@ -1,11 +1,5 @@
use serde::{Deserialize, Serialize};
-// ---------- expose generated guest constants ----------
-pub mod methods {
- include!(concat!(env!("OUT_DIR"), "/methods.rs"));
-}
-// -------------------------------------------------------
-
// ---------- 33-byte wrapper (public) ----------
pub mod ser_bytes33 { // (public so main.rs can use it)
use core::fmt;
diff --git a/shake256-33bytes-demo/src/main.rs b/shake256-33bytes-demo/src/main.rs
index 50532da..0e47728 100644
--- a/shake256-33bytes-demo/src/main.rs
+++ b/shake256-33bytes-demo/src/main.rs
@@ -3,7 +3,7 @@ use risc0_zkvm::{default_prover, ExecutorEnv};
use shake256_33bytes_demo::{EncInput, enc_xor_shake256, nssa_kdf}; // now works via re-exports
use shake256_33bytes_demo::ser_bytes33::Bytes33; // for constructing wrapper
-use shake256_33bytes_demo::methods::GUEST_ELF; // generated guest image
+use methods::GUEST_ELF; // generated guest image
fn main() -> anyhow::Result<()> {| #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| pub struct EncInput { | ||
| pub ss_bytes: [u8; 32], | ||
| pub epk_bytes: Bytes33, | ||
| pub ipk_bytes: Bytes33, | ||
| pub commitment: [u8; 32], | ||
| pub out_index: u32, | ||
| } |
There was a problem hiding this comment.
There's a workaround to get rid of the ser_bytes33 module and the Bytes33 struct.
The issues you had with 33 bytes come from trying to derive Serialize and Deserialize for EncInput. Since you only need a way to convert an instance of EncInput to bytes, you can make it work by implementing those manually.
| #[derive(Debug, Serialize, Deserialize, Clone)] | |
| pub struct EncInput { | |
| pub ss_bytes: [u8; 32], | |
| pub epk_bytes: Bytes33, | |
| pub ipk_bytes: Bytes33, | |
| pub commitment: [u8; 32], | |
| pub out_index: u32, | |
| } | |
| #[derive(Debug, Clone)] | |
| pub struct EncInput { | |
| pub ss_bytes: [u8; 32], | |
| pub epk_bytes: [u8; 33], | |
| pub ipk_bytes: [u8; 33], | |
| pub commitment: [u8; 32], | |
| pub out_index: u32, | |
| } | |
| impl EncInput { | |
| pub fn to_bytes(&self) -> Vec<u8> { | |
| let mut bytes = Vec::with_capacity(32 + 33 + 33 + 32 + 4); | |
| bytes.extend_from_slice(&self.ss_bytes); | |
| bytes.extend_from_slice(&self.epk_bytes); | |
| bytes.extend_from_slice(&self.ipk_bytes); | |
| bytes.extend_from_slice(&self.commitment); | |
| bytes.extend_from_slice(&self.out_index.to_le_bytes()); | |
| bytes | |
| } | |
| fn from_bytes(bytes: Vec<u8>) -> EncInput { | |
| assert_eq!(bytes.len(), 32 + 33 + 33 + 32 + 4); | |
| let ss_bytes = bytes[0..32].try_into().unwrap(); | |
| let epk_bytes = bytes[32..65].try_into().unwrap(); | |
| let ipk_bytes = bytes[65..98].try_into().unwrap(); | |
| let commitment = bytes[98..130].try_into().unwrap(); | |
| let out_index = u32::from_le_bytes(bytes[130..134].try_into().unwrap()); | |
| EncInput { | |
| ss_bytes, | |
| epk_bytes, | |
| ipk_bytes, | |
| commitment, | |
| out_index, | |
| } | |
| } | |
| } |
Then in src/main.rs when writing inputs to the guets program you can do the following
let input_bytes = input.to_bytes();
let env = ExecutorEnv::builder().write(&input_bytes.as_slice())?.build()?;Inside the guest program, we read it as follows:
let input_bytes: Vec<u8> = env::read();
let EncInput { ss_bytes, epk_bytes, ipk_bytes, commitment, out_index } = EncInput::from_bytes(input_bytes);Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
|
🎯 Purpose
This PR adds the same end-to-end
SHAKE256 XOR-encryption demo, but with stricter typing:epkandipkare wrapped as fixed-size 33-byte values for safer (de)serialization across the host/guest boundary.⚙️ Approach
Identical crypto as the baseline demo:
SHAKE256and XOR with plaintext.The difference is in how we pass
epkandipk:struct Bytes33(pub [u8; 33]);AsRef<[u8;33]>andAsRef<[u8]>so crypto code can work with slices/arrays.Bytes33inEncInputon both host and guest.Flow
Guest —
methods/guest/src/main.rsEncInput { ss_bytes: [u8;32], epk_bytes: Bytes33, ipk_bytes: Bytes33, commitment: [u8;32], out_index: u32 }.info = epk || ipk || commitment.k_encvia KDF; runs SHAKE256 keystream; XORs; commits ciphertext to journal.Host / Prover —
src/main.rsBytes33(epk_raw)/Bytes33(ipk_raw)and feeds them to the guest; proves and verifies as usual.Verifier —
src/main.rsreceipt.verify(GUEST_ID).Files
methods/guest/src/main.rs— includes theBytes33wrapper (Serde impls +AsRef), KDF + SHAKE256 XOR, entrypoint.src/lib.rs— re-exportsBytes33,EncInput, and crypto helpers for the host.src/main.rs— constructsBytes33from[u8; 33]and runs prove/verify.build.rs/methods.rs— guest image binding.Cargo.toml(guest) —tiny-keccakwithfeatures = ["shake"].Notes
Do not derive
Serialize/DeserializeonBytes33and also provide manual impls, pick manual impls only to avoid conflicting implementations.The pattern used here:
In the host, wrap raw arrays:
Bytes33(epk_raw),Bytes33(ipk_raw).Journal access:
receipt.receipt.journal.bytes(field).🧪 How to Run
From repo root:
# Build and run the 33-byte variant cargo clean -p shake256-33bytes-demo cargo build -p shake256-33bytes-demo cargo run -p shake256-33bytes-demo --release🔗 Dependencies
Same as baseline:
🔜 Future Work
cargo bench.cargo test+ example run.📋 PR Completion Checklist
Bytes33wrapper and wire it through guest + host.