From f594e1698e90424b923f551cb15d7c149a6851bf Mon Sep 17 00:00:00 2001 From: Brechy Date: Fri, 30 Aug 2024 12:12:33 -0300 Subject: [PATCH 1/4] begin impl --- src/kem.rs | 9 +++++-- src/kzg.rs | 4 +-- src/lib.rs | 5 ++-- src/{operations.rs => pol_op.rs} | 16 ++++++------ src/we.rs | 43 ++++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 15 deletions(-) rename src/{operations.rs => pol_op.rs} (98%) create mode 100644 src/we.rs diff --git a/src/kem.rs b/src/kem.rs index 7b58fd6..b9cfcd4 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -1,4 +1,4 @@ -//! KEM Module +//! # KEM Module //! //! This module contains the implementation of an Extractable Witness Key Encapsulation Mechanism (KEM). @@ -121,6 +121,11 @@ impl KEM { .map(|(&proof, &ciphertext)| self.decapsulate(proof, ciphertext)) .collect() } + + /// Returns KZG scheme + pub fn kzg(&self) -> &KZG { + &self.kzg + } } #[derive(Error, Debug)] @@ -136,7 +141,7 @@ pub enum KEMError { #[cfg(test)] mod tests { use super::*; - use crate::operations::evaluate_polynomial; + use crate::pol_op::evaluate_polynomial; use ark_bls12_381::{Bls12_381, Fr, G1Projective, G2Projective}; use ark_std::test_rng; use ark_std::UniformRand; diff --git a/src/kzg.rs b/src/kzg.rs index d1794a9..cdaa5b3 100644 --- a/src/kzg.rs +++ b/src/kzg.rs @@ -1,8 +1,8 @@ -//! KZG Module +//! # KZG Module //! //! This module contains the implementation of the KZG polynomial commitment scheme. -use crate::operations::*; +use crate::pol_op::*; use ark_ec::pairing::Pairing; use ark_ff::{Field, Zero}; use std::ops::Mul; diff --git a/src/lib.rs b/src/lib.rs index acd72d7..06f1cd5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ -//! WE-KZG +//! # WE-KZG pub mod kem; pub mod kzg; -pub mod operations; +pub mod pol_op; +pub mod we; diff --git a/src/operations.rs b/src/pol_op.rs similarity index 98% rename from src/operations.rs rename to src/pol_op.rs index bfbe31c..95a9de1 100644 --- a/src/operations.rs +++ b/src/pol_op.rs @@ -1,6 +1,6 @@ -//! Operations Module +//! # Polynomial Operations Module //! -//! This module contains polynomial operations. +//! This module contains functions to perform operations on polynomials. use ark_ec::pairing::Pairing; use ark_ff::{Field, One, Zero}; @@ -12,26 +12,24 @@ pub fn subtract_polynomials( p: &[E::ScalarField], q: &[E::ScalarField], ) -> Vec { - let max_len = p.len().max(q.len()); - let mut result = Vec::with_capacity(max_len); - let min_len = p.len().min(q.len()); + let mut res = Vec::with_capacity(p.len().max(q.len())); // Subtract the overlapping parts for i in 0..min_len { - result.push(p[i] - q[i]); + res.push(p[i] - q[i]); } // Handle remaining terms in the longer polynomial if p.len() > min_len { - result.extend_from_slice(&p[min_len..]); + res.extend_from_slice(&p[min_len..]); } else { for &coeff in &q[min_len..] { - result.push(-coeff); + res.push(-coeff); } } - result + res } /// Multiplies two polynomials. diff --git a/src/we.rs b/src/we.rs new file mode 100644 index 0000000..f14c217 --- /dev/null +++ b/src/we.rs @@ -0,0 +1,43 @@ +//! # Witness Encryption Module +//! +//! This module contains the implementation of an Extractable Witness Encryption from Extractable Witness KEMs. + +use crate::kem::KEM; +use ark_ec::pairing::Pairing; + +/// Witness Encryption struct. +pub struct WE { + kem: KEM, +} + +impl WE { + /// Create a new WE instance. + pub fn new(kem: KEM) -> Self { + Self { kem } + } + + /// Encrypts a message with a statement. + /// Returns two ciphertexts. + /// - ct_1: used to generate the decryption key. + /// - ct_2: encrypted message. + pub fn encrypt(&self, statement: &[E::ScalarField], message: &[u8]) -> (&[u8], &[u8]) { + // Commit + let com = self.kem.kzg().commit(statement).unwrap(); + + // (ct_1, k) <- Encap(x) + let x = self.kem.encapsulate_set(com, &[], &[]); + + // ct_2 <- Enc(k, m) + // return (ct_1, ct_2) + todo!() + } + + /// Decrypts a ciphertext with a witness. + /// Returns a message or an error if the decryption fails. + pub fn decrypt(&self, witness: &[u8], ciphertext: (&[u8], &[u8])) -> Result<&[u8], ()> { + // k = Decap(w, ct_1) + // m = Dec(k, ct_2) + // return m + todo!() + } +} From 5d7c69c14ffac94cd5ae9d83eb52238bbb16ba41 Mon Sep 17 00:00:00 2001 From: Brechy Date: Tue, 3 Sep 2024 18:51:42 -0300 Subject: [PATCH 2/4] single opening implementation --- Cargo.lock | 28 +++++----- Cargo.toml | 2 +- README.md | 11 ++-- src/kem.rs | 26 ++++++---- src/lib.rs | 4 +- src/we.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 166 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8896fba..5cbbed7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,6 +265,20 @@ dependencies = [ "either", ] +[[package]] +name = "keaki" +version = "0.1.0" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "blake3", + "rand", + "thiserror", +] + [[package]] name = "libc" version = "0.2.158" @@ -455,20 +469,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "we-kzg" -version = "0.1.0" -dependencies = [ - "ark-bls12-381", - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", - "blake3", - "rand", - "thiserror", -] - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 002083c..0bc7fc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "we-kzg" +name = "keaki" version = "0.1.0" edition = "2021" diff --git a/README.md b/README.md index 01becb6..73476cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# WE-KZG (wip) +# ケヤキ 🌳 [![License][mit-badge]][mit-url] [![Build][actions-badge]][actions-url] @@ -11,13 +11,12 @@ [codecov-badge]: https://codecov.io/github/brech1/we-kzg/graph/badge.svg [codecov-url]: https://app.codecov.io/github/brech1/we-kzg/ -This crate is a Rust implementation of an **Extractable Witness Encryption for KZG Commitments** scheme. +**Keaki** is a Rust implementation of an Extractable Witness Encryption for KZG Commitments scheme. Based on the following paper: -- [Extractable Witness Encryption for KZG Commitments - and Efficient Laconic OT](https://eprint.iacr.org/2024/264.pdf). +- [Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT](https://eprint.iacr.org/2024/264.pdf) -A great set of notes on this paper is available here: +A great post on it can be found here: -- [Notes on Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT](https://hackmd.io/@letargicus/Hk3rpPnK0) +- [Notes on Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT](https://www.leku.blog/kzg-we/) diff --git a/src/kem.rs b/src/kem.rs index b9cfcd4..9a4f195 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -1,6 +1,6 @@ -//! # KEM Module +//! # Key Encapsulation Module //! -//! This module contains the implementation of an Extractable Witness Key Encapsulation Mechanism (KEM). +//! This module contains the implementation of an Extractable Witness Key Encapsulation Mechanism. use crate::kzg::KZG; use ark_ec::pairing::Pairing; @@ -11,34 +11,36 @@ use rand::thread_rng; use std::ops::Mul; use thiserror::Error; -/// Key Encapsulation Mechanism struct. +/// Extractable Witness Key Encapsulation Mechanism struct. pub struct KEM { kzg: KZG, } impl KEM { - /// Creates a new KEM instance. + /// Creates a new instance. pub fn new(kzg: KZG) -> Self { Self { kzg } } /// Encapsulation method. - /// Returns the ciphertext and the key. + /// Generates a key for a commitment and a point-value pair. pub fn encapsulate( &self, commitment: E::G1, point: E::ScalarField, value: E::ScalarField, ) -> Result<(E::G2, OutputReader), KEMError> { - let mut rng = thread_rng(); - let r = E::ScalarField::rand(&mut rng); - // [beta]_1 let value_in_g1: E::G1 = self.kzg.g1_gen().mul(value); // (com - [beta]_1) let com_beta = commitment - value_in_g1; + // Generate a random value + // This allows the generated secret not to be tied to the inputs. + let mut rng = thread_rng(); + let r = E::ScalarField::rand(&mut rng); + // Calculate secret // s = e(r * (com - [beta]_1), g2) let secret = E::pairing(com_beta.mul(r), self.kzg.g2_gen()); @@ -47,12 +49,13 @@ impl KEM { .serialize_uncompressed(&mut secret_bytes) .map_err(KEMError::SerializationError)?; - // Calculate ciphertext + // Calculate a ciphertext to share the randomness used in the encapsulation. // ct = r([tau]_2 - [alpha]_2) let tau_alpha: E::G2 = self.kzg.tau_g2() - self.kzg.g2_gen().mul(point); let ciphertext: E::G2 = tau_alpha.mul(r); - // Get the key + // Generate the key + // Hash the secret to make the key indistinguishable from random. // k = H(s) let mut key_hasher = blake3::Hasher::new(); key_hasher.update(&secret_bytes); @@ -63,7 +66,8 @@ impl KEM { } /// Decapsulation method. - /// Returns the key. + /// Generates a key for an opening and a ciphertext. + /// The generated key will be the same as the one generated during encapsulation for a valid opening. pub fn decapsulate(&self, proof: E::G1, ciphertext: E::G2) -> Result { // Calculate secret // s = e(proof, ct) diff --git a/src/lib.rs b/src/lib.rs index 06f1cd5..f65a71a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ -//! # WE-KZG +//! # Keaki +//! +//! Implementation of an Extractable Witness Encryption for KZG Commitments scheme. pub mod kem; pub mod kzg; diff --git a/src/we.rs b/src/we.rs index f14c217..ae0eadc 100644 --- a/src/we.rs +++ b/src/we.rs @@ -1,11 +1,12 @@ -//! # Witness Encryption Module +//! # Extractable Witness Encryption Module //! -//! This module contains the implementation of an Extractable Witness Encryption from Extractable Witness KEMs. +//! This module contains the implementation of an Extractable Witness Encryption from an Extractable Witness KEM. -use crate::kem::KEM; +use crate::kem::{KEMError, KEM}; use ark_ec::pairing::Pairing; +use thiserror::Error; -/// Witness Encryption struct. +/// Extractable Witness Encryption struct. pub struct WE { kem: KEM, } @@ -16,28 +17,135 @@ impl WE { Self { kem } } - /// Encrypts a message with a statement. - /// Returns two ciphertexts. - /// - ct_1: used to generate the decryption key. - /// - ct_2: encrypted message. - pub fn encrypt(&self, statement: &[E::ScalarField], message: &[u8]) -> (&[u8], &[u8]) { - // Commit - let com = self.kem.kzg().commit(statement).unwrap(); - + /// Encrypts a message using a commitment, point, and value. + /// Returns two ciphertexts: + /// - `key_ct`: used to generate the decryption key. + /// - `msg_ct`: the encrypted message. + pub fn encrypt( + &self, + com: E::G1, + point: E::ScalarField, + value: E::ScalarField, + msg: &[u8], + ) -> Result<(E::G2, Vec), WEError> { + // Generate a key and the corresponding key ciphertext // (ct_1, k) <- Encap(x) - let x = self.kem.encapsulate_set(com, &[], &[]); + let (key_ct, mut key_stream) = self.kem.encapsulate(com, point, value)?; // ct_2 <- Enc(k, m) - // return (ct_1, ct_2) - todo!() + let mut msg_ct = vec![0u8; msg.len()]; + key_stream.fill(&mut msg_ct); + for i in 0..msg.len() { + msg_ct[i] ^= msg[i]; + } + + // (ct_1, ct_2) + Ok((key_ct, msg_ct)) } - /// Decrypts a ciphertext with a witness. - /// Returns a message or an error if the decryption fails. - pub fn decrypt(&self, witness: &[u8], ciphertext: (&[u8], &[u8])) -> Result<&[u8], ()> { + /// Decrypts a ciphertext with a proof. + /// Returns the decrypted message. + pub fn decrypt(&self, proof: E::G1, key_ct: E::G2, msg_ct: &[u8]) -> Result, WEError> { // k = Decap(w, ct_1) + let mut key_stream = self.kem.decapsulate(proof, key_ct)?; + // m = Dec(k, ct_2) - // return m - todo!() + let mut msg = vec![0u8; msg_ct.len()]; + key_stream.fill(&mut msg); + for i in 0..msg_ct.len() { + msg[i] ^= msg_ct[i]; + } + + Ok(msg) + } +} + +#[derive(Error, Debug)] +pub enum WEError { + #[error("Key Encapsulation Error {0}")] + KEMError(KEMError), +} + +impl From for WEError { + fn from(error: KEMError) -> Self { + WEError::KEMError(error) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::kzg::KZG; + use crate::pol_op::evaluate_polynomial; + use ark_bls12_381::{Bls12_381, Fr, G1Projective, G2Projective}; + use ark_std::test_rng; + use ark_std::UniformRand; + + #[test] + fn test_encrypt_decrypt() { + let rng = &mut test_rng(); + let g1_gen = G1Projective::rand(rng); + let g2_gen = G2Projective::rand(rng); + let secret = Fr::rand(rng); + let max_degree = 10; + let point: Fr = Fr::rand(rng); + let kzg: KZG = KZG::setup(g1_gen, g2_gen, max_degree, secret); + let kem: KEM = KEM::new(kzg); + let we: WE = WE::new(kem); + + // p(x) = 7 x^4 + 9 x^3 - 5 x^2 - 25 x - 24 + let p = vec![ + Fr::from(-24), + Fr::from(-25), + Fr::from(-5), + Fr::from(9), + Fr::from(7), + ]; + let val = evaluate_polynomial::(&p, &point); + let commitment = we.kem.kzg().commit(&p).unwrap(); + + let msg = b"helloworld"; + + let (key_ct, msg_ct) = we.encrypt(commitment, point, val, msg).unwrap(); + + let proof = we.kem.kzg().open(&p, &point).unwrap(); + + let decrypted_msg = we.decrypt(proof, key_ct, &msg_ct).unwrap(); + + assert_eq!(msg.to_vec(), decrypted_msg); + } + + #[test] + fn test_decrypt_invalid_proof() { + let rng = &mut test_rng(); + let g1_gen = G1Projective::rand(rng); + let g2_gen = G2Projective::rand(rng); + let secret = Fr::rand(rng); + let max_degree = 10; + let point: Fr = Fr::rand(rng); + let kzg: KZG = KZG::setup(g1_gen, g2_gen, max_degree, secret); + let kem: KEM = KEM::new(kzg); + let we: WE = WE::new(kem); + + // p(x) = 7 x^4 + 9 x^3 - 5 x^2 - 25 x - 24 + let p = vec![ + Fr::from(-24), + Fr::from(-25), + Fr::from(-5), + Fr::from(9), + Fr::from(7), + ]; + let val = evaluate_polynomial::(&p, &point); + let commitment = we.kem.kzg().commit(&p).unwrap(); + + let msg = b"helloworld"; + let (key_ct, msg_ct) = we.encrypt(commitment, point, val, msg).unwrap(); + + let wrong_point: Fr = Fr::rand(rng); + let invalid_proof = we.kem.kzg().open(&p, &wrong_point).unwrap(); + + let decrypted_msg = we.decrypt(invalid_proof, key_ct, &msg_ct).unwrap(); + + assert_ne!(msg.to_vec(), decrypted_msg); } } From 36b4a05d09379bd518fc24864b8b89337465dc04 Mon Sep 17 00:00:00 2001 From: Brechy Date: Wed, 4 Sep 2024 14:40:30 -0300 Subject: [PATCH 3/4] add set enc --- src/we.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/src/we.rs b/src/we.rs index ae0eadc..2d29cf9 100644 --- a/src/we.rs +++ b/src/we.rs @@ -12,16 +12,35 @@ pub struct WE { } impl WE { - /// Create a new WE instance. + /// Create a new instance. pub fn new(kem: KEM) -> Self { Self { kem } } + /// Encrypts a message for a commitment and a set of points and values. + /// Returns a vector of ciphertext tuples, in the order of the input points and values. + pub fn encrypt( + &self, + com: E::G1, + points: Vec, + values: Vec, + msg: &[u8], + ) -> Result)>, WEError> { + let mut cts = Vec::new(); + + for i in 0..points.len() { + let (key_ct, msg_ct) = self.encrypt_single(com, points[i], values[i], msg)?; + cts.push((key_ct, msg_ct)); + } + + Ok(cts) + } + /// Encrypts a message using a commitment, point, and value. /// Returns two ciphertexts: /// - `key_ct`: used to generate the decryption key. /// - `msg_ct`: the encrypted message. - pub fn encrypt( + pub fn encrypt_single( &self, com: E::G1, point: E::ScalarField, @@ -45,7 +64,12 @@ impl WE { /// Decrypts a ciphertext with a proof. /// Returns the decrypted message. - pub fn decrypt(&self, proof: E::G1, key_ct: E::G2, msg_ct: &[u8]) -> Result, WEError> { + pub fn decrypt_single( + &self, + proof: E::G1, + key_ct: E::G2, + msg_ct: &[u8], + ) -> Result, WEError> { // k = Decap(w, ct_1) let mut key_stream = self.kem.decapsulate(proof, key_ct)?; @@ -82,7 +106,7 @@ mod tests { use ark_std::UniformRand; #[test] - fn test_encrypt_decrypt() { + fn test_encrypt_single() { let rng = &mut test_rng(); let g1_gen = G1Projective::rand(rng); let g2_gen = G2Projective::rand(rng); @@ -106,17 +130,17 @@ mod tests { let msg = b"helloworld"; - let (key_ct, msg_ct) = we.encrypt(commitment, point, val, msg).unwrap(); + let (key_ct, msg_ct) = we.encrypt_single(commitment, point, val, msg).unwrap(); let proof = we.kem.kzg().open(&p, &point).unwrap(); - let decrypted_msg = we.decrypt(proof, key_ct, &msg_ct).unwrap(); + let decrypted_msg = we.decrypt_single(proof, key_ct, &msg_ct).unwrap(); assert_eq!(msg.to_vec(), decrypted_msg); } #[test] - fn test_decrypt_invalid_proof() { + fn test_decrypt_single_invalid_proof() { let rng = &mut test_rng(); let g1_gen = G1Projective::rand(rng); let g2_gen = G2Projective::rand(rng); @@ -139,13 +163,62 @@ mod tests { let commitment = we.kem.kzg().commit(&p).unwrap(); let msg = b"helloworld"; - let (key_ct, msg_ct) = we.encrypt(commitment, point, val, msg).unwrap(); + let (key_ct, msg_ct) = we.encrypt_single(commitment, point, val, msg).unwrap(); let wrong_point: Fr = Fr::rand(rng); let invalid_proof = we.kem.kzg().open(&p, &wrong_point).unwrap(); - let decrypted_msg = we.decrypt(invalid_proof, key_ct, &msg_ct).unwrap(); + let decrypted_msg = we.decrypt_single(invalid_proof, key_ct, &msg_ct).unwrap(); assert_ne!(msg.to_vec(), decrypted_msg); } + + #[test] + fn test_encrypt_decrypt_single() { + let rng = &mut test_rng(); + let g1_gen = G1Projective::rand(rng); + let g2_gen = G2Projective::rand(rng); + let secret = Fr::rand(rng); + let max_degree = 10; + let kzg: KZG = KZG::setup(g1_gen, g2_gen, max_degree, secret); + let kem: KEM = KEM::new(kzg); + let we: WE = WE::new(kem); + + let p = vec![ + Fr::from(-24), + Fr::from(-25), + Fr::from(-5), + Fr::from(9), + Fr::from(7), + ]; + let points = vec![Fr::rand(rng), Fr::rand(rng), Fr::rand(rng)]; + let values: Vec = points + .iter() + .map(|&point| evaluate_polynomial::(&p, &point)) + .collect(); + let commitment = we.kem.kzg().commit(&p).unwrap(); + + let msg = b"helloworld"; + + let cts = we + .encrypt(commitment, points.clone(), values.clone(), msg) + .unwrap(); + + let target_index = 1; + let proof = we.kem.kzg().open(&p, &points[target_index]).unwrap(); + + let (correct_key_ct, correct_msg_ct) = &cts[target_index]; + let decrypted_msg = we + .decrypt_single(proof, *correct_key_ct, correct_msg_ct) + .unwrap(); + assert_eq!(msg.to_vec(), decrypted_msg); + + // Attempt to decrypt a different message with the same proof + let (wrong_key_ct, wrong_msg_ct) = &cts[0]; + let wrong_decrypted_msg = we + .decrypt_single(proof, *wrong_key_ct, wrong_msg_ct) + .unwrap(); + + assert_ne!(msg.to_vec(), wrong_decrypted_msg); + } } From 285791b5a61787a86db77ef7594d39349105eed8 Mon Sep 17 00:00:00 2001 From: Brechy Date: Wed, 4 Sep 2024 14:46:27 -0300 Subject: [PATCH 4/4] bypass type --- src/we.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/we.rs b/src/we.rs index 2d29cf9..46894d6 100644 --- a/src/we.rs +++ b/src/we.rs @@ -1,7 +1,8 @@ //! # Extractable Witness Encryption Module //! -//! This module contains the implementation of an Extractable Witness Encryption from an Extractable Witness KEM. +//! This module contains the implementation of an Extractable Witness Encryption from an Extractable Witness KEM.' +#![allow(clippy::type_complexity)] use crate::kem::{KEMError, KEM}; use ark_ec::pairing::Pairing; use thiserror::Error;