encryption example with risc0

This commit is contained in:
Moudy 2025-08-05 13:31:57 +02:00
parent 29b56e3a32
commit 579bf0a624
14 changed files with 258 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[workspace]
members = [
"encryption-demo",
"encryption-demo-methods",
]
workspace.resolver = "2"

View File

@ -0,0 +1,15 @@
[package]
name = "encryption-demo-methods"
version = "0.1.0"
edition = "2021"
publish = false
build = "build.rs"
[dependencies]
build = "0.0.2"
risc0-zkvm = { version = "2.2", default-features = false }
risc0-build = { version = "2.2", default-features = false }
[package.metadata.risc0]
methods = ["guest/chacha20", "guest/xchacha20"]

View File

@ -0,0 +1,4 @@
fn main() {
risc0_build::embed_methods();
}

View File

@ -0,0 +1,19 @@
[package]
name = "chacha20"
version = "0.1.0"
edition = "2021"
publish = false
# Ensure staticlib for RISC-V
crate-type = ["staticlib"]
[dependencies]
# no_std stream cipher
chacha20 = { version = "0.9", default-features = false }
cipher = { version = "0.5", default-features = false }
risc0-zkvm-guest = "2.2"
[features]
default = ["alloc"] # pull in alloc inside guest
alloc = []

View File

@ -0,0 +1,27 @@
#![no_std]
#![no_main]
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::ChaCha20;
use risc0_zkvm::guest::env;
risc0_zkvm_guest::entry!(main);
pub fn main() {
// Read 32-byte key
let key: [u8; 32] = env::read();
// Read 12-byte nonce
let nonce: [u8; 12] = env::read();
// Read plaintext length
let len: u32 = env::read();
// Read plaintext bytes
let mut plaintext = vec![0u8; len as usize];
env::read_slice(&mut plaintext).unwrap();
// Encrypt in-place
let mut cipher = ChaCha20::new(&key.into(), &nonce.into());
cipher.apply_keystream(&mut plaintext);
// Commit ciphertext
env::commit_slice(&plaintext);
}

View File

@ -0,0 +1,19 @@
[package]
name = "xchacha20"
version = "0.1.0"
edition = "2021"
publish = false
# Ensure staticlib for RISC-V
crate-type = ["staticlib"]
[dependencies]
# no_std stream cipher
xchacha20 = { version = "0.9", default-features = false }
cipher = { version = "0.5", default-features = false }
risc0-zkvm-guest = "2.2"
[features]
default = ["alloc"] # pull in alloc inside guest
alloc = []

View File

@ -0,0 +1,21 @@
#![no_std]
#![no_main]
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use risc0_zkvm::guest::env;
risc0_zkvm_guest::entry!(main);
pub fn main() {
let key: [u8; 32] = env::read();
let nonce: [u8; 24] = env::read();
let len: u32 = env::read();
let mut plaintext = vec![0u8; len as usize];
env::read_slice(&mut plaintext).unwrap();
let mut cipher = XChaCha20::new(&key.into(), &nonce.into());
cipher.apply_keystream(&mut plaintext);
env::commit_slice(&plaintext);
}

View File

@ -0,0 +1,2 @@
include!(concat!(env!("OUT_DIR"), "/methods.rs"));

View File

@ -0,0 +1,16 @@
[package]
name = "encryption-demo"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
risc0-zkvm = "2.2"
rand = { version = "0.8", features = ["std"]}
hex = "0.4"
encryption-demo-methods = { path = "../encryption-demo-methods" }
[[bin]]
name = "encrypt-demo"
path = "src/main.rs"

View File

@ -0,0 +1,14 @@
use encryption_demo::*;
use rand::Rng;
fn main() -> anyhow::Result<()> {
let msg = b"zk-encrypted hello world!";
let mut rng = rand::thread_rng();
let key: [u8; 32] = rng.gen();
let nonce: [u8; 12] = rng.gen();
let (ciphertext, _proof) = encrypt_chacha20(&key, &nonce, msg)?;
println!("ciphertext = {}", hex::encode(ciphertext));
println!("proof OK");
Ok(())
}

114
encryption-demo/src/lib.rs Normal file
View File

@ -0,0 +1,114 @@
//! Host-side helpers: prove encryption inside RISC Zero and verify receipts.
use risc0_zkvm::{
default_prover, ExecutorEnv, Receipt,
};
use encryption_demo_methods::{
CHACHA20_ELF, CHACHA20_ID, XCHACHA20_ELF, XCHACHA20_ID,
};
/// Encrypt `plaintext` with ChaCha20 inside the zkVM.
/// Returns `(ciphertext, receipt)`.
pub fn encrypt_chacha20(
key: &[u8; 32],
nonce: &[u8; 12],
plaintext: &[u8],
) -> anyhow::Result<(Vec<u8>, Receipt)> {
let env = ExecutorEnv::builder()
.write(key)?
.write(nonce)?
.write(&(plaintext.len() as u32))?
.write_slice(plaintext)?
.build()?;
let prover = default_prover();
let receipt = prover.prove(env, CHACHA20_ELF)?;
receipt.verify(CHACHA20_ID)?;
let ciphertext: Vec<u8> = receipt.journal.decode()?;
Ok((ciphertext, receipt))
}
/// Same API for XChaCha20 (24-byte nonce)
pub fn encrypt_xchacha20(
key: &[u8; 32],
nonce: &[u8; 24],
plaintext: &[u8],
) -> anyhow::Result<(Vec<u8>, Receipt)> {
let env = ExecutorEnv::builder()
.write(key)?
.write(nonce)?
.write(&(plaintext.len() as u32))?
.write_slice(plaintext)?
.build()?;
let prover = default_prover();
let receipt = prover.prove(env, XCHACHA20_ELF)?;
receipt.verify(XCHACHA20_ID)?;
let ciphertext: Vec<u8> = receipt.journal.decode()?;
Ok((ciphertext, receipt))
}
// Test for chacha20
#[cfg(test)]
mod tests {
use super::*;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::ChaCha20;
// RFC 8439 test vector
const KEY: [u8; 32] = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
];
const NONCE: [u8; 12] = [0; 12];
const PLAINTEXT: [u8; 16] = *b"example message!";
#[test]
fn chacha20_vector_matches() {
let (ciphertext, _) = encrypt_chacha20(&KEY, &NONCE, &PLAINTEXT).unwrap();
// host decryption
let mut buf = ciphertext.clone();
let mut cipher = ChaCha20::new(&KEY.into(), &NONCE.into());
cipher.apply_keystream(&mut buf);
assert_eq!(&buf, &PLAINTEXT);
}
}
// Tests for Xchacha20
#[cfg(test)]
mod tests {
use super::*;
use xchacha20::cipher::{KeyIvInit, StreamCipher};
use xchacha20::xChaCha20;
// RFC 8439 test vector
const KEY: [u8; 32] = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
];
const NONCE: [u8; 12] = [0; 12];
const PLAINTEXT: [u8; 16] = *b"example message!";
#[test]
fn xchacha20_vector_matches() {
let (ciphertext, _) = encrypt_xchacha20(&KEY, &NONCE, &PLAINTEXT).unwrap();
// host decryption
let mut buf = ciphertext.clone();
let mut cipher = xChaCha20::new(&KEY.into(), &NONCE.into());
cipher.apply_keystream(&mut buf);
assert_eq!(&buf, &PLAINTEXT);
}
}

View File

@ -0,0 +1 @@

BIN
risc0-selective-privacy-poc/.DS_Store vendored Normal file

Binary file not shown.