Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 171 additions & 10 deletions crates/bfv-helpers/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,50 @@ pub fn bfv_encrypt_u64(
Ok(encrypted_data)
}

/// Encrypt a Vec<u64> using BFV homomorphic encryption
///
/// # Arguments
/// * `data` - The value to encrypt (Vec<u64>)
/// * `public_key` - Serialized BFV public key bytes
/// # `degree` - Polynomial degree for BFV parameters
/// # `plaintext_modulus` - Plaintext modulus for BFV parameters
/// * `moduli` - Vector of moduli for BFV parameters
///
/// # Returns
/// * `Result<Vec<u8>>` - Serialized BFV ciphertext bytes
///
/// # Errors
/// Returns error string if:
/// - Public key deserialization fails
/// - Plaintext encoding fails
/// - Encryption fails
/// - Input validation vector computation fails
pub fn bfv_encrypt_v64(
data: Vec<u64>,
Comment thread
ryardley marked this conversation as resolved.
public_key: Vec<u8>,
degree: usize,
plaintext_modulus: u64,
moduli: [u64; 1],
) -> Result<Vec<u8>> {
let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli);

let pk = PublicKey::from_bytes(&public_key, &params)
.map_err(|e| anyhow!("Error deserializing public key:{e}"))?;

let pt = Plaintext::try_encode(&data, Encoding::poly(), &params)
.map_err(|e| anyhow!("Error encoding plaintext: {e}"))?;

let ct = pk
.try_encrypt(&pt, &mut thread_rng())
.map_err(|e| anyhow!("Error encrypting data: {e}"))?;

let encrypted_data = ct.to_bytes();
Ok(encrypted_data)
}

#[derive(Debug, Clone)]
pub struct VerifiableEncryptionResult {
pub encrypted_vote: Vec<u8>,
pub encrypted_data: Vec<u8>,
pub circuit_inputs: String,
}

Expand All @@ -81,7 +122,7 @@ pub struct VerifiableEncryptionResult {
/// - Encryption fails
/// - Input validation vector computation fails
pub fn bfv_verifiable_encrypt_u64(
vote: u64,
data: u64,
public_key: Vec<u8>,
degree: usize,
plaintext_modulus: u64,
Expand All @@ -92,14 +133,78 @@ pub fn bfv_verifiable_encrypt_u64(
let pk = PublicKey::from_bytes(&public_key, &params)
.map_err(|e| anyhow!("Error deserializing public key: {}", e))?;

let vote_vector = vec![vote];
let data_vector = vec![data];

let plaintext = Plaintext::try_encode(&data_vector, Encoding::poly(), &params)
.map_err(|e| anyhow!("Error encoding plaintext: {}", e))?;

let (cipher_text, u_rns, e0_rns, e1_rns) = pk
.try_encrypt_extended(&plaintext, &mut thread_rng())
.map_err(|e| anyhow!("Error encrypting data: {}", e))?;

// Create Greco input validation ZK proof
let input_val_vectors = InputValidationVectors::compute(
&plaintext,
&u_rns,
&e0_rns,
&e1_rns,
&cipher_text,
&pk,
&params,
)
.map_err(|e| anyhow!("Error computing input validation vectors: {}", e))?;

let zkp_modulus = BigInt::from_str_radix(
"21888242871839275222246405745257275088548364400416034343698204186575808495617",
10,
)
.unwrap();

let standard_input_val = input_val_vectors.standard_form(&zkp_modulus);

Ok(VerifiableEncryptionResult {
encrypted_data: cipher_text.to_bytes(),
circuit_inputs: standard_input_val.to_json().to_string(),
})
}

/// Verifiably encrypt a u64 using BFV homomorphic encryption and generate circuit inputs
/// to pass into Greco to prove the validity of the ciphertext
///
/// # Arguments
/// * `data` - The value to encrypt (u64)
/// * `public_key` - Serialized BFV public key bytes
/// # `degree` - Polynomial degree for BFV parameters
/// # `plaintext_modulus` - Plaintext modulus for BFV parameters
/// * `moduli` - Vector of moduli for BFV parameters
///
/// # Returns
/// * `Result<VerifiableEncryptionResult, String>` - Contains encrypted u64 and circuit inputs for ZKP
///
/// # Errors
/// Returns error string if:
/// - Public key deserialization fails
/// - Plaintext encoding fails
/// - Encryption fails
/// - Input validation vector computation fails
pub fn bfv_verifiable_encrypt_v64(
data: Vec<u64>,
public_key: Vec<u8>,
degree: usize,
plaintext_modulus: u64,
moduli: [u64; 1],
) -> Result<VerifiableEncryptionResult> {
let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli);

let plaintext = Plaintext::try_encode(&vote_vector, Encoding::poly(), &params)
let pk = PublicKey::from_bytes(&public_key, &params)
.map_err(|e| anyhow!("Error deserializing public key: {}", e))?;

let plaintext = Plaintext::try_encode(&data, Encoding::poly(), &params)
.map_err(|e| anyhow!("Error encoding plaintext: {}", e))?;

let (cipher_text, u_rns, e0_rns, e1_rns) = pk
.try_encrypt_extended(&plaintext, &mut thread_rng())
.map_err(|e| anyhow!("Error encrypting vote: {}", e))?;
.map_err(|e| anyhow!("Error encrypting data: {}", e))?;

// Create Greco input validation ZK proof
let input_val_vectors = InputValidationVectors::compute(
Expand All @@ -122,7 +227,7 @@ pub fn bfv_verifiable_encrypt_u64(
let standard_input_val = input_val_vectors.standard_form(&zkp_modulus);

Ok(VerifiableEncryptionResult {
encrypted_vote: cipher_text.to_bytes(),
encrypted_data: cipher_text.to_bytes(),
circuit_inputs: standard_input_val.to_json().to_string(),
})
}
Expand All @@ -145,15 +250,43 @@ mod tests {
let pk = PublicKey::new(&sk, &mut rng);

let num = 1;
let encrypted_vote =
let encrypted_data =
bfv_encrypt_u64(num, pk.to_bytes(), degree, plaintext_modulus, moduli).unwrap();

let ct = Ciphertext::from_bytes(&encrypted_vote, &params).unwrap();
let ct = Ciphertext::from_bytes(&encrypted_data, &params).unwrap();
let pt = sk.try_decrypt(&ct).unwrap();

assert_eq!(pt.value[0], num);
}

#[test]
fn test_bfv_encrypt_v64() {
use fhe::bfv::{Ciphertext, PublicKey, SecretKey};
use fhe_traits::{DeserializeParametrized, FheDecrypter, Serialize};

let (degree, plaintext_modulus, moduli) = SET_2048_1032193_1;
let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli);
let mut rng = thread_rng();
let sk = SecretKey::random(&params, &mut rng);
let pk = PublicKey::new(&sk, &mut rng);

let num = vec![1, 2];
let encrypted_data = bfv_encrypt_v64(
num.clone(),
pk.to_bytes(),
degree,
plaintext_modulus,
moduli,
)
.unwrap();

let ct = Ciphertext::from_bytes(&encrypted_data, &params).unwrap();
let pt = sk.try_decrypt(&ct).unwrap();

assert_eq!(pt.value[0], num[0]);
assert_eq!(pt.value[1], num[1]);
}

#[test]
fn test_bfv_verifiable_encrypt_u64() {
use fhe::bfv::{Ciphertext, PublicKey, SecretKey};
Expand All @@ -166,13 +299,41 @@ mod tests {
let pk = PublicKey::new(&sk, &mut rng);

let num = 1;
let encrypted_vote =
let encrypted_data =
bfv_verifiable_encrypt_u64(num, pk.to_bytes(), degree, plaintext_modulus, moduli)
.unwrap();

let ct = Ciphertext::from_bytes(&encrypted_vote.encrypted_vote, &params).unwrap();
let ct = Ciphertext::from_bytes(&encrypted_data.encrypted_data, &params).unwrap();
let pt = sk.try_decrypt(&ct).unwrap();

assert_eq!(pt.value[0], num);
}

#[test]
fn test_bfv_verifiable_encrypt_v64() {
use fhe::bfv::{Ciphertext, PublicKey, SecretKey};
use fhe_traits::{DeserializeParametrized, FheDecrypter, Serialize};

let (degree, plaintext_modulus, moduli) = SET_2048_1032193_1;
let params = build_bfv_params_arc(degree, plaintext_modulus, &moduli);
let mut rng = thread_rng();
let sk = SecretKey::random(&params, &mut rng);
let pk = PublicKey::new(&sk, &mut rng);

let num = vec![1, 2];
let encrypted_data = bfv_verifiable_encrypt_v64(
num.clone(),
pk.to_bytes(),
degree,
plaintext_modulus,
moduli,
)
.unwrap();

let ct = Ciphertext::from_bytes(&encrypted_data.encrypted_data, &params).unwrap();
let pt = sk.try_decrypt(&ct).unwrap();

assert_eq!(pt.value[0], num[0]);
assert_eq!(pt.value[1], num[1]);
}
}
72 changes: 70 additions & 2 deletions crates/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use e3_bfv_helpers::client::{bfv_encrypt_u64, bfv_verifiable_encrypt_u64};
use e3_bfv_helpers::client::{
bfv_encrypt_u64, bfv_encrypt_v64, bfv_verifiable_encrypt_u64, bfv_verifiable_encrypt_v64,
};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
Expand Down Expand Up @@ -37,6 +39,36 @@ pub fn bfv_encrypt_number(
Ok(encrypted_data)
}

#[wasm_bindgen]
/// A function to encrypt a Vec<u64> value using BFV and default params.
///
/// # Arguments
///
/// * `data` - The data to encrypt - must be a Vec<u64>
/// * `public_key` - The public key to be used for encryption
/// * `degree` - Polynomial degree for BFV parameters
/// * `plaintext_modulus` - Plaintext modulus for BFV parameters
/// * `moduli` - Modulus for BFV parameters
///
/// # Returns
///
/// Returns a `Result<Vec<u8>, JsValue>` containing the encrypted data and any errors.
///
/// # Panics
///
/// Panics if the data cannot be encrypted
pub fn bfv_encrypt_vector(
data: Vec<u64>,
public_key: Vec<u8>,
degree: usize,
plaintext_modulus: u64,
moduli: u64,
) -> Result<Vec<u8>, JsValue> {
let encrypted_data = bfv_encrypt_v64(data, public_key, degree, plaintext_modulus, [moduli])
.map_err(|e| JsValue::from_str(&format!("{}", e)))?;
Ok(encrypted_data)
}

#[wasm_bindgen]
/// A function to encrypt a u64 value using BFV and default params and
/// generate circuit inputs for Greco
Expand Down Expand Up @@ -68,7 +100,43 @@ pub fn bfv_verifiable_encrypt_number(

// Return as a vector of JsValues
Ok(vec![
JsValue::from(result.encrypted_vote),
JsValue::from(result.encrypted_data),
JsValue::from(result.circuit_inputs),
])
}

#[wasm_bindgen]
/// A function to encrypt a Vec<u64> value using BFV and default params and
/// generate circuit inputs for Greco
///
/// # Arguments
///
/// * `data` - The data to encrypt - must be a Vec<u64>
/// * `public_key` - The public key to be used for encryption
/// * `degree` - Polynomial degree for BFV parameters
/// * `plaintext_modulus` - Plaintext modulus for BFV parameters
/// * `moduli` - Modulus for BFV parameters
///
/// # Returns
///
/// Returns a `Result<Vec<JsValue>, JsValue>` containing the encrypted data, circuit inputs and any errors.
///
/// # Panics
///
/// Panics if the data cannot be encrypted
pub fn bfv_verifiable_encrypt_vector(
data: Vec<u64>,
public_key: Vec<u8>,
degree: usize,
plaintext_modulus: u64,
moduli: u64,
) -> Result<Vec<JsValue>, JsValue> {
let result = bfv_verifiable_encrypt_v64(data, public_key, degree, plaintext_modulus, [moduli])
.map_err(|e| JsValue::from_str(&format!("{}", e)))?;

// Return as a vector of JsValues
Ok(vec![
JsValue::from(result.encrypted_data),
JsValue::from(result.circuit_inputs),
])
}
2 changes: 1 addition & 1 deletion examples/CRISP/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
},
"packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808",
"dependencies": {
"@enclave-e3/sdk": "^0.1.5",
"@enclave-e3/sdk": "workspace:*",
"@zk-kit/lean-imt": "^2.2.4",
"poseidon-lite": "^0.3.0",
"viem": "2.30.6"
Expand Down
Loading
Loading