mirror of
https://github.com/Sunscreen-tech/Sunscreen.git
synced 2026-01-09 13:48:06 -05:00
Misc updates from linked-docs (#364)
This commit is contained in:
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -232,6 +232,12 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
@@ -745,6 +751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"serdect",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -2237,7 +2244,6 @@ name = "private_tx_linkedproof"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"once_cell",
|
||||
"sunscreen",
|
||||
]
|
||||
|
||||
@@ -2714,6 +2720,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serdect"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
@@ -3014,6 +3030,7 @@ dependencies = [
|
||||
"ocl",
|
||||
"rand",
|
||||
"rayon",
|
||||
"serde",
|
||||
"statrs",
|
||||
"subtle",
|
||||
"sunscreen_curve25519",
|
||||
|
||||
@@ -49,7 +49,7 @@ tokio = { version = "1.25.0", features = ["rt"] }
|
||||
wgpu = "0.17.0"
|
||||
cust = "0.3.2"
|
||||
num = "0.4.1"
|
||||
crypto-bigint = "0.5.2"
|
||||
crypto-bigint = { version = "0.5.2", features = ["serde"] }
|
||||
paste = "1.0.14"
|
||||
thiserror = "1.0.44"
|
||||
naga = { version = "0.13.0", features = ["wgsl-in"] }
|
||||
|
||||
@@ -6,4 +6,3 @@ edition = "2021"
|
||||
[dependencies]
|
||||
sunscreen = { path = "../../sunscreen", features = ["linkedproofs"] }
|
||||
env_logger = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
//! where deterministic computation is performed on encrypted data. The linked proofs allow us to
|
||||
//! validate the encrypted inputs.
|
||||
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use sunscreen::{
|
||||
bulletproofs::BulletproofsBackend,
|
||||
fhe_program,
|
||||
linked::{LinkedProof, LogProofBuilder},
|
||||
linked::LinkedProof,
|
||||
types::{
|
||||
bfv::Signed,
|
||||
zkp::{
|
||||
@@ -19,21 +18,27 @@ use sunscreen::{
|
||||
Cipher,
|
||||
},
|
||||
zkp_program, zkp_var, Ciphertext, CompiledFheProgram, CompiledZkpProgram, Compiler,
|
||||
FheZkpApplication, FheZkpRuntime, Params, PrivateKey, PublicKey, Result, ZkpProgramInput,
|
||||
FheProgramInput, FheZkpApplication, FheZkpRuntime, Params, PrivateKey, PublicKey, Result,
|
||||
};
|
||||
|
||||
/// Subtract the transaction amount from the sender's balance.
|
||||
#[fhe_program(scheme = "bfv")]
|
||||
fn update_balance_sender(balance: Cipher<Signed>, tx: Cipher<Signed>) -> Cipher<Signed> {
|
||||
fn transfer_from(balance: Cipher<Signed>, tx: Cipher<Signed>) -> Cipher<Signed> {
|
||||
balance - tx
|
||||
}
|
||||
|
||||
/// Add the transaction amount to the receiver's balance.
|
||||
#[fhe_program(scheme = "bfv")]
|
||||
fn update_balance_receiver(balance: Cipher<Signed>, tx: Cipher<Signed>) -> Cipher<Signed> {
|
||||
fn transfer_to(balance: Cipher<Signed>, tx: Cipher<Signed>) -> Cipher<Signed> {
|
||||
balance + tx
|
||||
}
|
||||
|
||||
/// Add the public transaction amount to a user's balance.
|
||||
#[fhe_program(scheme = "bfv")]
|
||||
fn deposit_to(balance: Cipher<Signed>, deposit: Signed) -> Cipher<Signed> {
|
||||
balance + deposit
|
||||
}
|
||||
|
||||
/// Validate a transfer transaction.
|
||||
#[zkp_program]
|
||||
fn validate_transfer<F: FieldSpec>(
|
||||
@@ -49,10 +54,10 @@ fn validate_transfer<F: FieldSpec>(
|
||||
tx.constrain_le_bounded(sender_balance, 64);
|
||||
}
|
||||
|
||||
/// Validate deposit/registration. The deposit amount is public, but we must prove that the
|
||||
/// provided ciphertext encrypts the deposit amount.
|
||||
/// Validate registration. The deposit amount is public, but we must prove that the provided
|
||||
/// ciphertext encrypts the deposit amount.
|
||||
#[zkp_program]
|
||||
fn validate_deposit<F: FieldSpec>(
|
||||
fn validate_registration<F: FieldSpec>(
|
||||
#[linked] encrypted_deposit: BfvSigned<F>,
|
||||
#[public] public_deposit: Field<F>,
|
||||
) {
|
||||
@@ -109,7 +114,7 @@ impl User {
|
||||
receiver: U,
|
||||
) -> Result<Transfer> {
|
||||
let receiver = receiver.into();
|
||||
let mut builder = LogProofBuilder::new(&self.runtime);
|
||||
let mut builder = self.runtime.linkedproof_builder();
|
||||
|
||||
// Encrypt tx amount under sender's public key.
|
||||
println!(" {}: encrypting {} under own key", self.name, amount);
|
||||
@@ -123,7 +128,7 @@ impl User {
|
||||
self.name, amount
|
||||
);
|
||||
let recv_pk = chain.keys.get(&receiver).unwrap();
|
||||
let encrypted_amount_receiver = builder.encrypt_msg(&amount_linked, recv_pk)?;
|
||||
let encrypted_amount_receiver = builder.reencrypt(&amount_linked, recv_pk)?;
|
||||
|
||||
// Decrypt current balance, needed to prove tx validity
|
||||
let balance_enc = chain.balances.get(&self.name).unwrap();
|
||||
@@ -139,7 +144,7 @@ impl User {
|
||||
.zkp_program(self.app.get_transfer_zkp())?
|
||||
.linked_input(amount_linked)
|
||||
.linked_input(balance_linked)
|
||||
.build_linkedproof()?;
|
||||
.build()?;
|
||||
|
||||
Ok(Transfer {
|
||||
proof,
|
||||
@@ -151,33 +156,16 @@ impl User {
|
||||
}
|
||||
|
||||
/// Create a public deposit to a private balance.
|
||||
pub fn create_deposit(&self, amount: i64) -> Result<Deposit> {
|
||||
let mut builder = LogProofBuilder::new(&self.runtime);
|
||||
|
||||
// Encrypt deposit amount
|
||||
println!(" {}: encrypting and linking {}", self.name, amount);
|
||||
let (amount_enc, amount_linked) =
|
||||
builder.encrypt_returning_link(&Signed::from(amount), &self.public_key)?;
|
||||
|
||||
// Create deposit proof
|
||||
println!(" {}: creating deposit linkedproof", self.name);
|
||||
let proof = builder
|
||||
.zkp_program(self.app.get_deposit_zkp())?
|
||||
.linked_input(amount_linked)
|
||||
.public_input(BulletproofsField::from(amount))
|
||||
.build_linkedproof()?;
|
||||
|
||||
Ok(Deposit {
|
||||
proof,
|
||||
encrypted_amount: amount_enc,
|
||||
pub fn create_deposit(&self, amount: i64) -> Deposit {
|
||||
Deposit {
|
||||
public_amount: amount,
|
||||
name: self.name.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a refresh balance transaction.
|
||||
pub fn create_refresh_balance(&self, chain: &Chain) -> Result<RefreshBalance> {
|
||||
let mut builder = LogProofBuilder::new(&self.runtime);
|
||||
let mut builder = self.runtime.linkedproof_builder();
|
||||
|
||||
// Decrypt current balance, returning a link to the underlying message
|
||||
let balance_encrypted = chain.balances.get(&self.name).unwrap();
|
||||
@@ -191,12 +179,12 @@ impl User {
|
||||
|
||||
// Generate proof that the ciphertexts encrypt the same underlying message and that
|
||||
// the new one has a fresh noise budget and fresh encoding.
|
||||
println!(" {}: creating refresh balance logproof", self.name);
|
||||
println!(" {}: creating refresh balance linkedproof", self.name);
|
||||
let proof = builder
|
||||
.zkp_program(self.app.get_refresh_balance_zkp())?
|
||||
.linked_input(existing_link)
|
||||
.linked_input(fresh_link)
|
||||
.build_linkedproof()?;
|
||||
.build()?;
|
||||
|
||||
Ok(RefreshBalance {
|
||||
proof,
|
||||
@@ -206,29 +194,48 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn create_register(&self, initial_deposit: i64) -> Result<Register> {
|
||||
let deposit = self.create_deposit(initial_deposit)?;
|
||||
let mut builder = self.runtime.linkedproof_builder();
|
||||
|
||||
// Encrypt deposit amount
|
||||
println!(
|
||||
" {}: encrypting and linking {}",
|
||||
self.name, initial_deposit
|
||||
);
|
||||
let (amount_enc, amount_linked) =
|
||||
builder.encrypt_returning_link(&Signed::from(initial_deposit), &self.public_key)?;
|
||||
|
||||
// Create registration proof
|
||||
println!(" {}: creating registration linkedproof", self.name);
|
||||
let proof = builder
|
||||
.zkp_program(self.app.get_registration_zkp())?
|
||||
.linked_input(amount_linked)
|
||||
.public_input(BulletproofsField::from(initial_deposit))
|
||||
.build()?;
|
||||
|
||||
Ok(Register {
|
||||
proof,
|
||||
encrypted_amount: amount_enc,
|
||||
public_key: self.public_key.clone(),
|
||||
deposit,
|
||||
deposit: self.create_deposit(initial_deposit),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A register transaction is an initial deposit plus an identifying public key.
|
||||
#[derive(Clone)]
|
||||
pub struct Register {
|
||||
public_key: PublicKey,
|
||||
deposit: Deposit,
|
||||
}
|
||||
|
||||
/// A deposit transaction.
|
||||
/// A register transaction.
|
||||
///
|
||||
/// The SDLP in the linked proof proves that the ciphertext is a valid, fresh encryption. The R1CS
|
||||
/// ZKP in the linked proof proves that the amount encrypted matches the public amount deposited.
|
||||
#[derive(Clone)]
|
||||
pub struct Deposit {
|
||||
pub struct Register {
|
||||
proof: LinkedProof,
|
||||
public_key: PublicKey,
|
||||
encrypted_amount: Ciphertext,
|
||||
deposit: Deposit,
|
||||
}
|
||||
|
||||
/// A public deposit transaction.
|
||||
#[derive(Clone)]
|
||||
pub struct Deposit {
|
||||
public_amount: i64,
|
||||
name: Username,
|
||||
}
|
||||
@@ -305,51 +312,47 @@ impl Chain {
|
||||
pub fn register(&mut self, register: Register) -> Result<()> {
|
||||
self.ledger.push(Transaction::Register(register.clone()));
|
||||
let Register {
|
||||
proof,
|
||||
encrypted_amount,
|
||||
public_key,
|
||||
deposit,
|
||||
} = register;
|
||||
|
||||
// First, verify that the encrypted amount matches the public amount
|
||||
let mut builder = self.runtime.linkedproof_verification_builder();
|
||||
builder.encrypt_returning_link::<Signed>(&encrypted_amount, &public_key)?;
|
||||
builder
|
||||
.zkp_program(self.app.get_registration_zkp())?
|
||||
.proof(proof)
|
||||
.public_input(BulletproofsField::from(deposit.public_amount))
|
||||
.verify()?;
|
||||
|
||||
// Register the user's public key
|
||||
self.keys.insert(deposit.name.clone(), public_key);
|
||||
self.deposit(deposit)
|
||||
|
||||
// Set the initial encrypted balance
|
||||
self.balances.insert(deposit.name, encrypted_amount);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deposit(&mut self, deposit: Deposit) -> Result<()> {
|
||||
self.ledger.push(Transaction::Deposit(deposit.clone()));
|
||||
let Deposit {
|
||||
proof,
|
||||
encrypted_amount,
|
||||
public_amount,
|
||||
name,
|
||||
} = deposit;
|
||||
|
||||
// First, verify that the encrypted amount matches the public amount
|
||||
proof.verify(
|
||||
self.app.get_deposit_zkp(),
|
||||
vec![BulletproofsField::from(public_amount)],
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
// Then deposit into the user's balance
|
||||
// Deposit into the user's balance
|
||||
let pk = self.keys.get(&name).unwrap();
|
||||
match self.balances.entry(name) {
|
||||
// Update existing balance
|
||||
Entry::Occupied(mut entry) => {
|
||||
let curr = entry.get().clone();
|
||||
let updated = self
|
||||
.runtime
|
||||
.run(
|
||||
self.app.get_update_balance_receiver_fhe(),
|
||||
vec![curr, encrypted_amount],
|
||||
pk,
|
||||
)?
|
||||
.remove(0);
|
||||
entry.insert(updated);
|
||||
}
|
||||
// Or insert the amount if this is an initial deposit
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(encrypted_amount);
|
||||
}
|
||||
}
|
||||
let curr_bal = self.balances.get_mut(&name).unwrap();
|
||||
*curr_bal = self
|
||||
.runtime
|
||||
.run::<FheProgramInput>(
|
||||
self.app.get_deposit_to_fhe(),
|
||||
vec![curr_bal.clone().into(), Signed::from(public_amount).into()],
|
||||
pk,
|
||||
)?
|
||||
.remove(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -364,33 +367,45 @@ impl Chain {
|
||||
} = transfer;
|
||||
|
||||
// First verify the transfer is valid
|
||||
proof.verify::<ZkpProgramInput>(self.app.get_transfer_zkp(), vec![], vec![])?;
|
||||
let mut builder = self.runtime.linkedproof_verification_builder();
|
||||
let link = builder.encrypt_returning_link::<Signed>(
|
||||
&encrypted_amount_sender,
|
||||
self.keys.get(&sender).unwrap(),
|
||||
)?;
|
||||
builder.reencrypt(
|
||||
&link,
|
||||
&encrypted_amount_receiver,
|
||||
self.keys.get(&receiver).unwrap(),
|
||||
)?;
|
||||
builder.decrypt_returning_link::<Signed>(self.balances.get(&sender).unwrap())?;
|
||||
builder
|
||||
.zkp_program(self.app.get_transfer_zkp())?
|
||||
.proof(proof)
|
||||
.verify()?;
|
||||
|
||||
// Update the sender's balance:
|
||||
let sender_pk = self.keys.get(&sender).unwrap();
|
||||
let sender_balance = self.balances.get_mut(&sender).unwrap();
|
||||
let new_balance = self
|
||||
*sender_balance = self
|
||||
.runtime
|
||||
.run(
|
||||
self.app.get_update_balance_sender_fhe(),
|
||||
self.app.get_transfer_from_fhe(),
|
||||
vec![sender_balance.clone(), encrypted_amount_sender],
|
||||
sender_pk,
|
||||
)?
|
||||
.remove(0);
|
||||
*sender_balance = new_balance;
|
||||
|
||||
// Update receiver's balance
|
||||
let receiver_pk = self.keys.get(&receiver).unwrap();
|
||||
let receiver_balance = self.balances.get_mut(&receiver).unwrap();
|
||||
let new_balance = self
|
||||
*receiver_balance = self
|
||||
.runtime
|
||||
.run(
|
||||
self.app.get_update_balance_receiver_fhe(),
|
||||
self.app.get_transfer_to_fhe(),
|
||||
vec![receiver_balance.clone(), encrypted_amount_receiver],
|
||||
receiver_pk,
|
||||
)?
|
||||
.remove(0);
|
||||
*receiver_balance = new_balance;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -404,7 +419,13 @@ impl Chain {
|
||||
} = refresh_balance;
|
||||
|
||||
// Verify the balance refresh is valid
|
||||
proof.verify::<ZkpProgramInput>(self.app.get_refresh_balance_zkp(), vec![], vec![])?;
|
||||
let mut builder = self.runtime.linkedproof_verification_builder();
|
||||
builder.decrypt_returning_link::<Signed>(self.balances.get(&name).unwrap())?;
|
||||
builder.encrypt_returning_link::<Signed>(&fresh_balance, self.keys.get(&name).unwrap())?;
|
||||
builder
|
||||
.zkp_program(self.app.get_refresh_balance_zkp())?
|
||||
.proof(proof)
|
||||
.verify()?;
|
||||
|
||||
// Use the freshly encrypted balance
|
||||
self.balances
|
||||
@@ -432,53 +453,55 @@ impl Chain {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App(&'static Lazy<FheZkpApplication>);
|
||||
pub struct App(FheZkpApplication);
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Result<Self> {
|
||||
static APP: Lazy<FheZkpApplication> = Lazy::new(|| {
|
||||
Compiler::new()
|
||||
.fhe_program(update_balance_sender)
|
||||
.fhe_program(update_balance_receiver)
|
||||
// These params are not necessary to run the example, but they do shave a few
|
||||
// minutes off the runtime. In practice, you probably want to use the default
|
||||
// parameters provided by the compiler. The ones set here will result in balances
|
||||
// needing to be refreshed more often.
|
||||
.with_params(&Params {
|
||||
lattice_dimension: 1024,
|
||||
coeff_modulus: vec![0x7e00001],
|
||||
plain_modulus: 512,
|
||||
scheme_type: sunscreen::SchemeType::Bfv,
|
||||
security_level: sunscreen::SecurityLevel::TC128,
|
||||
})
|
||||
.zkp_backend::<BulletproofsBackend>()
|
||||
.zkp_program(validate_transfer)
|
||||
.zkp_program(validate_deposit)
|
||||
.zkp_program(validate_refresh_balance)
|
||||
.compile()
|
||||
.unwrap()
|
||||
});
|
||||
Ok(Self(&APP))
|
||||
let app = Compiler::new()
|
||||
.fhe_program(transfer_to)
|
||||
.fhe_program(transfer_from)
|
||||
.fhe_program(deposit_to)
|
||||
// These params are not necessary to run the example, but they do shave a few
|
||||
// minutes off the runtime. In practice, you probably want to use the default
|
||||
// parameters provided by the compiler. The ones set here will result in balances
|
||||
// needing to be refreshed more often.
|
||||
.with_params(&Params {
|
||||
lattice_dimension: 1024,
|
||||
coeff_modulus: vec![0x7e00001],
|
||||
plain_modulus: 512,
|
||||
scheme_type: sunscreen::SchemeType::Bfv,
|
||||
security_level: sunscreen::SecurityLevel::TC128,
|
||||
})
|
||||
.zkp_backend::<BulletproofsBackend>()
|
||||
.zkp_program(validate_transfer)
|
||||
.zkp_program(validate_registration)
|
||||
.zkp_program(validate_refresh_balance)
|
||||
.compile()?;
|
||||
Ok(Self(app))
|
||||
}
|
||||
|
||||
pub fn get_transfer_zkp(&self) -> &CompiledZkpProgram {
|
||||
self.0.get_zkp_program(validate_transfer).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_deposit_zkp(&self) -> &CompiledZkpProgram {
|
||||
self.0.get_zkp_program(validate_deposit).unwrap()
|
||||
pub fn get_registration_zkp(&self) -> &CompiledZkpProgram {
|
||||
self.0.get_zkp_program(validate_registration).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_refresh_balance_zkp(&self) -> &CompiledZkpProgram {
|
||||
self.0.get_zkp_program(validate_refresh_balance).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_update_balance_sender_fhe(&self) -> &CompiledFheProgram {
|
||||
self.0.get_fhe_program(update_balance_sender).unwrap()
|
||||
pub fn get_transfer_to_fhe(&self) -> &CompiledFheProgram {
|
||||
self.0.get_fhe_program(transfer_to).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_update_balance_receiver_fhe(&self) -> &CompiledFheProgram {
|
||||
self.0.get_fhe_program(update_balance_receiver).unwrap()
|
||||
pub fn get_transfer_from_fhe(&self) -> &CompiledFheProgram {
|
||||
self.0.get_fhe_program(transfer_from).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_deposit_to_fhe(&self) -> &CompiledFheProgram {
|
||||
self.0.get_fhe_program(deposit_to).unwrap()
|
||||
}
|
||||
|
||||
pub fn params(&self) -> &Params {
|
||||
@@ -501,7 +524,7 @@ fn main() -> Result<()> {
|
||||
chain.register(alice.create_register(deposit)?)?;
|
||||
let deposit = 50;
|
||||
println!("Depositing an extra {deposit}");
|
||||
chain.deposit(alice.create_deposit(deposit)?)?;
|
||||
chain.deposit(alice.create_deposit(deposit))?;
|
||||
|
||||
println!();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use bitvec::slice::BitSlice;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use digest::Digest;
|
||||
use rayon::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Borrow;
|
||||
use std::ops::{Add, Div, Index, IndexMut, Mul, Sub};
|
||||
use sunscreen_math::field::Field;
|
||||
@@ -14,7 +15,7 @@ use crate::crypto::CryptoHash;
|
||||
use crate::math::{ModSwitch, Tensor};
|
||||
use crate::rings::{FieldFrom, ZqRistretto};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
/**
|
||||
* An `m x n` matrix of elements.
|
||||
*
|
||||
|
||||
@@ -52,7 +52,7 @@ type MatrixPoly<Q> = Matrix<Polynomial<Q>>;
|
||||
/**
|
||||
* Bounds on the coefficients in the secret S (specified in number of bits).
|
||||
*/
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Bounds(pub Vec<u32>);
|
||||
|
||||
impl Zero for Bounds {
|
||||
@@ -76,7 +76,7 @@ impl std::ops::Deref for Bounds {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// The artifacts known to both the prover and verifier.
|
||||
pub struct VerifierKnowledge<Q>
|
||||
where
|
||||
|
||||
@@ -30,6 +30,7 @@ rustdoc-args = ["--html-in-header", "docs/assets/katex-header.html"]
|
||||
bumpalo = { workspace = true }
|
||||
crypto-bigint = { workspace = true }
|
||||
log = { workspace = true }
|
||||
logproof = { workspace = true, optional = true }
|
||||
num = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
petgraph = { workspace = true }
|
||||
@@ -64,7 +65,7 @@ serde_json = { workspace = true }
|
||||
[features]
|
||||
bulletproofs = ["sunscreen_zkp_backend/bulletproofs"]
|
||||
hexl = ["seal_fhe/hexl"]
|
||||
linkedproofs = ["bulletproofs", "sunscreen_runtime/linkedproofs"]
|
||||
linkedproofs = ["bulletproofs", "sunscreen_runtime/linkedproofs", "logproof"]
|
||||
transparent-ciphertexts = ["seal_fhe/transparent-ciphertexts"]
|
||||
deterministic = ["seal_fhe/deterministic", "sunscreen_runtime/deterministic"]
|
||||
|
||||
|
||||
@@ -19,11 +19,14 @@
|
||||
//! the value in an encrypted transaction and that the sender has enough funds to cover the
|
||||
//! transaction, without decrypting the transaction.
|
||||
//!
|
||||
//! How does this work in practice? If you use our [builder](`LogProofBuilder`), you
|
||||
//! How does this work in practice? If you use our [builder](`LinkedProofBuilder`), you
|
||||
//! can encrypt messages in a very similar way to our typical [runtime
|
||||
//! encryption](crate::FheRuntime::encrypt), while also opting to _share_ a message with a linked
|
||||
//! ZKP program. Under the hood, we'll handle the complicated bits of generating a linear relation
|
||||
//! for SDLP and sharing the secrets with the [`zkp_program`](crate::zkp_program).
|
||||
pub use logproof::Bounds;
|
||||
pub use sunscreen_runtime::{
|
||||
ExistingMessage, LinkWithZkp, LinkedMessage, LinkedProof, LogProofBuilder, Message, Sdlp,
|
||||
ExistingMessage, LinkWithZkp, LinkedMessage, LinkedProof, LinkedProofBuilder,
|
||||
LinkedProofVerificationBuilder, Message, MessageRef, Sdlp, SdlpBuilder,
|
||||
SdlpVerificationBuilder,
|
||||
};
|
||||
|
||||
@@ -53,52 +53,56 @@ impl<F: FieldSpec> Field<F> {
|
||||
* Converts a big-endian hex string into a native field.
|
||||
*/
|
||||
pub fn from_be_hex(hex_str: &str) -> Self {
|
||||
Self {
|
||||
val: BigInt::from_be_hex(hex_str),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
from_unsigned(BigInt::from_be_hex(hex_str))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldSpec> From<BigInt> for Field<F> {
|
||||
fn from(val: BigInt) -> Self {
|
||||
Self {
|
||||
val,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
fn from(x: BigInt) -> Self {
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldSpec> From<u8> for Field<F> {
|
||||
fn from(x: u8) -> Self {
|
||||
(u64::from(x)).into()
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldSpec> From<u16> for Field<F> {
|
||||
fn from(x: u16) -> Self {
|
||||
(u64::from(x)).into()
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldSpec> From<u32> for Field<F> {
|
||||
fn from(x: u32) -> Self {
|
||||
(u64::from(x)).into()
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldSpec> From<u64> for Field<F> {
|
||||
fn from(x: u64) -> Self {
|
||||
assert!(F::FIELD_MODULUS != BigInt::ZERO);
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
let m = NonZero::from_uint(*F::FIELD_MODULUS);
|
||||
impl<F: FieldSpec> From<u128> for Field<F> {
|
||||
fn from(x: u128) -> Self {
|
||||
from_unsigned(x)
|
||||
}
|
||||
}
|
||||
|
||||
// unwrap is okay here as we've ensured FIELD_MODULUS is
|
||||
// non-zero.
|
||||
Self {
|
||||
val: BigInt::from(BigInt::from(x).rem(&m)),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
fn from_unsigned<T: Into<BigInt>, F: FieldSpec>(x: T) -> Field<F> {
|
||||
assert!(F::FIELD_MODULUS != BigInt::ZERO);
|
||||
|
||||
let m = NonZero::from_uint(*F::FIELD_MODULUS);
|
||||
|
||||
// unwrap is okay here as we've ensured FIELD_MODULUS is
|
||||
// non-zero.
|
||||
Field {
|
||||
val: BigInt::from(x.into().rem(&m)),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ mod linked_tests {
|
||||
zkp_program, zkp_var, Compiler,
|
||||
};
|
||||
use sunscreen_fhe_program::SchemeType;
|
||||
use sunscreen_runtime::{FheZkpRuntime, LogProofBuilder, Params, ZkpProgramInput};
|
||||
use sunscreen_runtime::{
|
||||
FheZkpRuntime, LinkedProofBuilder, LinkedProofVerificationBuilder, Params,
|
||||
};
|
||||
use sunscreen_zkp_backend::bulletproofs::BulletproofsBackend;
|
||||
|
||||
lazy_static! {
|
||||
@@ -63,9 +65,9 @@ mod linked_tests {
|
||||
|
||||
// Try valid cases
|
||||
for tx in [5, 10] {
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
|
||||
let (_ct, tx_msg) = proof_builder
|
||||
let (ct, tx_msg) = proof_builder
|
||||
.encrypt_returning_link(&Signed::from(tx), &public_key)
|
||||
.unwrap();
|
||||
|
||||
@@ -75,17 +77,22 @@ mod linked_tests {
|
||||
.unwrap()
|
||||
.linked_input(tx_msg)
|
||||
.public_input(BulletproofsField::from(balance))
|
||||
.build_linkedproof()
|
||||
.build()
|
||||
.unwrap();
|
||||
println!("Linked proof done");
|
||||
|
||||
println!("Performing linked verify");
|
||||
lp.verify(
|
||||
valid_transaction_zkp,
|
||||
vec![BulletproofsField::from(balance)],
|
||||
vec![],
|
||||
)
|
||||
.expect("Failed to verify linked proof");
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Signed>(&ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(valid_transaction_zkp)
|
||||
.unwrap()
|
||||
.public_input(BulletproofsField::from(balance))
|
||||
.verify()
|
||||
.expect("Failed to verify linked proof");
|
||||
println!("Linked verify done");
|
||||
}
|
||||
}
|
||||
@@ -107,7 +114,7 @@ mod linked_tests {
|
||||
let balance = 10i64;
|
||||
|
||||
for tx in [-1, balance + 1] {
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (_ct, tx_msg) = proof_builder
|
||||
.encrypt_returning_link(&Signed::from(tx), &public_key)
|
||||
.unwrap();
|
||||
@@ -117,7 +124,7 @@ mod linked_tests {
|
||||
.linked_input(tx_msg)
|
||||
.public_input(BulletproofsField::from(balance));
|
||||
|
||||
let lp = proof_builder.build_linkedproof();
|
||||
let lp = proof_builder.build();
|
||||
assert!(lp.is_err());
|
||||
}
|
||||
}
|
||||
@@ -142,8 +149,8 @@ mod linked_tests {
|
||||
let (public_key, _secret_key) = rt.generate_keys().unwrap();
|
||||
|
||||
for val in [3, 0, -3] {
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct, val_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (ct, val_msg) = proof_builder
|
||||
.encrypt_returning_link(&Signed::from(val), &public_key)
|
||||
.unwrap();
|
||||
proof_builder
|
||||
@@ -152,7 +159,7 @@ mod linked_tests {
|
||||
.linked_input(val_msg)
|
||||
.public_input(BulletproofsField::from(val));
|
||||
|
||||
let lp = proof_builder.build_linkedproof().unwrap_or_else(|e| {
|
||||
let lp = proof_builder.build().unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to encode {} value; {e}",
|
||||
if val.is_positive() {
|
||||
@@ -162,7 +169,16 @@ mod linked_tests {
|
||||
}
|
||||
)
|
||||
});
|
||||
lp.verify(is_eq_zkp, vec![BulletproofsField::from(val)], vec![])
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Signed>(&ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(is_eq_zkp)
|
||||
.unwrap()
|
||||
.public_input(BulletproofsField::from(val))
|
||||
.verify()
|
||||
.expect("Failed to verify linked proof");
|
||||
}
|
||||
}
|
||||
@@ -197,8 +213,8 @@ mod linked_tests {
|
||||
// ensure denominator is positive
|
||||
let x_d = rand::random::<i64>().saturating_abs().saturating_add(1);
|
||||
let x = Rational::from(Rational64::new_raw(x_n, x_d));
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct, x_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (ct, x_msg) = proof_builder
|
||||
.encrypt_returning_link(&x, &public_key)
|
||||
.unwrap();
|
||||
proof_builder
|
||||
@@ -209,9 +225,18 @@ mod linked_tests {
|
||||
.private_input(BulletproofsField::from(x_d));
|
||||
|
||||
let lp = proof_builder
|
||||
.build_linkedproof()
|
||||
.build()
|
||||
.unwrap_or_else(|e| panic!("Failed to prove encoding of {x:?}; {e}"));
|
||||
lp.verify::<ZkpProgramInput>(is_eq_zkp, vec![], vec![])
|
||||
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Rational>(&ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(is_eq_zkp)
|
||||
.unwrap()
|
||||
.verify()
|
||||
.unwrap_or_else(|e| panic!("Failed to verify encoding of {x:?}; {e}"));
|
||||
}
|
||||
}
|
||||
@@ -243,11 +268,11 @@ mod linked_tests {
|
||||
let y = rand::random::<i32>() as i64;
|
||||
let (x, y) = (Signed::from(x.min(y)), Signed::from(x.max(y)));
|
||||
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct, x_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (x_ct, x_msg) = proof_builder
|
||||
.encrypt_returning_link(&x, &public_key)
|
||||
.unwrap();
|
||||
let (_ct, y_msg) = proof_builder
|
||||
let (y_ct, y_msg) = proof_builder
|
||||
.encrypt_symmetric_returning_link(&y, &private_key)
|
||||
.unwrap();
|
||||
proof_builder
|
||||
@@ -257,9 +282,21 @@ mod linked_tests {
|
||||
.linked_input(y_msg);
|
||||
|
||||
let lp = proof_builder
|
||||
.build_linkedproof()
|
||||
.build()
|
||||
.unwrap_or_else(|e| panic!("Failed to prove {x:?} <= {y:?}, {e}"));
|
||||
lp.verify::<ZkpProgramInput>(compare_signed_zkp, vec![], vec![])
|
||||
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Signed>(&x_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.encrypt_symmetric_returning_link::<Signed>(&y_ct)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(compare_signed_zkp)
|
||||
.unwrap()
|
||||
.verify()
|
||||
.unwrap_or_else(|e| panic!("Failed to verify {x:?} <= {y:?}; {e}"));
|
||||
}
|
||||
}
|
||||
@@ -298,11 +335,11 @@ mod linked_tests {
|
||||
let y = Rational64::new_raw(y_n, y_d);
|
||||
let (x, y) = (Rational::from(x.min(y)), Rational::from(x.max(y)));
|
||||
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct, x_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (x_ct, x_msg) = proof_builder
|
||||
.encrypt_returning_link(&x, &public_key)
|
||||
.unwrap();
|
||||
let (_ct, y_msg) = proof_builder
|
||||
let (y_ct, y_msg) = proof_builder
|
||||
.encrypt_returning_link(&y, &public_key)
|
||||
.unwrap();
|
||||
proof_builder
|
||||
@@ -312,9 +349,21 @@ mod linked_tests {
|
||||
.linked_input(y_msg);
|
||||
|
||||
let lp = proof_builder
|
||||
.build_linkedproof()
|
||||
.build()
|
||||
.unwrap_or_else(|e| panic!("Failed to prove {x:?} <= {y:?}, {e}"));
|
||||
lp.verify::<ZkpProgramInput>(compare_rational_zkp, vec![], vec![])
|
||||
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Rational>(&x_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Rational>(&y_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(compare_rational_zkp)
|
||||
.unwrap()
|
||||
.verify()
|
||||
.unwrap_or_else(|e| panic!("Failed to verify {x:?} <= {y:?}; {e}"));
|
||||
}
|
||||
}
|
||||
@@ -348,14 +397,14 @@ mod linked_tests {
|
||||
let (public_key, private_key) = rt.generate_keys().unwrap();
|
||||
|
||||
for val in [3, 0, -3] {
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct_x, x_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (x_ct, x_msg) = proof_builder
|
||||
.encrypt_returning_link(&Signed::from(val), &public_key)
|
||||
.unwrap();
|
||||
// proves same plaintext within SDLP
|
||||
let _ct_x1 = proof_builder.encrypt_msg(&x_msg, &public_key).unwrap();
|
||||
let x1_ct = proof_builder.reencrypt(&x_msg, &public_key).unwrap();
|
||||
// proves same value within ZKP
|
||||
let (_ct_y, y_msg) = proof_builder
|
||||
let (y_ct, y_msg) = proof_builder
|
||||
.encrypt_symmetric_returning_link(&Signed::from(val), &private_key)
|
||||
.unwrap();
|
||||
proof_builder
|
||||
@@ -365,8 +414,23 @@ mod linked_tests {
|
||||
.linked_input(y_msg)
|
||||
.private_input(BulletproofsField::from(val));
|
||||
|
||||
let lp = proof_builder.build_linkedproof().unwrap();
|
||||
lp.verify::<ZkpProgramInput>(is_eq_zkp, vec![], vec![])
|
||||
let lp = proof_builder.build().unwrap();
|
||||
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
let x_msg = verify_builder
|
||||
.encrypt_returning_link::<Signed>(&x_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.reencrypt(&x_msg, &x1_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.encrypt_symmetric_returning_link::<Signed>(&y_ct)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(is_eq_zkp)
|
||||
.unwrap()
|
||||
.verify()
|
||||
.expect("Failed to verify linked proof");
|
||||
}
|
||||
}
|
||||
@@ -397,11 +461,11 @@ mod linked_tests {
|
||||
// ensure denominator is positive
|
||||
let y_d = rand::random::<i64>().saturating_abs().saturating_add(1);
|
||||
let y = Rational::from(Rational64::new_raw(y_n, y_d));
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let (_ct, x_msg) = proof_builder
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (x_ct, x_msg) = proof_builder
|
||||
.encrypt_returning_link(&x, &public_key)
|
||||
.unwrap();
|
||||
let (_ct, y_msg) = proof_builder
|
||||
let (y_ct, y_msg) = proof_builder
|
||||
.encrypt_returning_link(&y, &public_key)
|
||||
.unwrap();
|
||||
let lp = proof_builder
|
||||
@@ -409,11 +473,23 @@ mod linked_tests {
|
||||
.unwrap()
|
||||
.linked_input(x_msg)
|
||||
.linked_input(y_msg)
|
||||
.build_linkedproof()
|
||||
.build()
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("Failed to prove fresh encodings of {x:?} and {y:?}; Error: {e}")
|
||||
});
|
||||
lp.verify::<ZkpProgramInput>(is_fresh_zkp, vec![], vec![])
|
||||
|
||||
let mut verify_builder = LinkedProofVerificationBuilder::new(&rt);
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Signed>(&x_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.encrypt_returning_link::<Rational>(&y_ct, &public_key)
|
||||
.unwrap();
|
||||
verify_builder
|
||||
.proof(lp)
|
||||
.zkp_program(is_fresh_zkp)
|
||||
.unwrap()
|
||||
.verify()
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("Failed to verify fresh encodings of {x:?} and {y:?}; Error: {e}")
|
||||
});
|
||||
@@ -479,7 +555,7 @@ mod linked_tests {
|
||||
.remove(0);
|
||||
}
|
||||
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let (_, x_msg) = proof_builder
|
||||
.decrypt_returning_link::<Signed>(&x_ct, &private_key)
|
||||
.unwrap();
|
||||
@@ -491,7 +567,7 @@ mod linked_tests {
|
||||
.unwrap()
|
||||
.linked_input(x_msg)
|
||||
.linked_input(y_msg)
|
||||
.build_linkedproof();
|
||||
.build();
|
||||
let (x, y) = if ix % 2 == 0 {
|
||||
(format!("double({x:?})"), format!("{y:?}"))
|
||||
} else {
|
||||
@@ -533,7 +609,7 @@ mod linked_tests {
|
||||
// but use runtime with modulus 4096
|
||||
let rt = FheZkpRuntime::new(&TEST_PARAMS, &BulletproofsBackend::new()).unwrap();
|
||||
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
let res = proof_builder.zkp_program(is_eq_zkp);
|
||||
assert!(matches!(
|
||||
res,
|
||||
@@ -555,7 +631,7 @@ mod linked_tests {
|
||||
let is_eq_zkp = app.get_zkp_program(is_eq_3).unwrap();
|
||||
|
||||
let (public_key, _secret_key) = rt.generate_keys().unwrap();
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
proof_builder.zkp_program(is_eq_zkp).unwrap();
|
||||
for _ in 0..num_linked_inputs {
|
||||
let (_ct, msg) = proof_builder
|
||||
@@ -566,7 +642,7 @@ mod linked_tests {
|
||||
for _ in 0..num_private_inputs {
|
||||
proof_builder.private_input(BulletproofsField::from(1));
|
||||
}
|
||||
let res = proof_builder.build_linkedproof();
|
||||
let res = proof_builder.build();
|
||||
assert!(matches!(
|
||||
res,
|
||||
Err(sunscreen::RuntimeError::ArgumentMismatch(_))
|
||||
@@ -591,7 +667,7 @@ mod linked_tests {
|
||||
let compare_signed_zkp = app.get_zkp_program(compare_signed).unwrap();
|
||||
|
||||
let (public_key, _secret_key) = rt.generate_keys().unwrap();
|
||||
let mut proof_builder = LogProofBuilder::new(&rt);
|
||||
let mut proof_builder = LinkedProofBuilder::new(&rt);
|
||||
proof_builder.zkp_program(compare_signed_zkp).unwrap();
|
||||
let (_ct, signed_msg) = proof_builder
|
||||
.encrypt_returning_link(&Signed::from(1), &public_key)
|
||||
@@ -602,7 +678,7 @@ mod linked_tests {
|
||||
proof_builder
|
||||
.linked_input(signed_msg)
|
||||
.linked_input(unsigned_msg);
|
||||
let res = proof_builder.build_linkedproof();
|
||||
let res = proof_builder.build();
|
||||
assert!(matches!(
|
||||
res,
|
||||
Err(sunscreen::RuntimeError::ArgumentMismatch(_))
|
||||
|
||||
@@ -9,7 +9,7 @@ mod sdlp_tests {
|
||||
use sunscreen_compiler_macros::fhe_program;
|
||||
use sunscreen_fhe_program::SchemeType;
|
||||
|
||||
use sunscreen_runtime::{FheRuntime, LogProofBuilder, Params};
|
||||
use sunscreen_runtime::{FheRuntime, Params, SdlpBuilder, SdlpVerificationBuilder};
|
||||
|
||||
lazy_static! {
|
||||
static ref TEST_PARAMS: Params = Params {
|
||||
@@ -25,75 +25,96 @@ mod sdlp_tests {
|
||||
fn prove_one_asymmetric_statement() {
|
||||
let rt = FheRuntime::new(&TEST_PARAMS).unwrap();
|
||||
let (public_key, _secret_key) = rt.generate_keys().unwrap();
|
||||
let mut logproof_builder = LogProofBuilder::new(&rt);
|
||||
let mut logproof_builder = SdlpBuilder::new(&rt);
|
||||
|
||||
let _ct = logproof_builder
|
||||
let ct = logproof_builder
|
||||
.encrypt(&Signed::from(3), &public_key)
|
||||
.unwrap();
|
||||
|
||||
let sdlp = logproof_builder.build_logproof().unwrap();
|
||||
sdlp.verify().unwrap();
|
||||
let sdlp = logproof_builder.build().unwrap();
|
||||
|
||||
let mut logproof_vk_builder = SdlpVerificationBuilder::new(&rt);
|
||||
logproof_vk_builder.encrypt(&ct, &public_key).unwrap();
|
||||
logproof_vk_builder.proof(sdlp).verify().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prove_one_symmetric_statement() {
|
||||
let rt = FheRuntime::new(&TEST_PARAMS).unwrap();
|
||||
let (_public_key, private_key) = rt.generate_keys().unwrap();
|
||||
let mut logproof_builder = LogProofBuilder::new(&rt);
|
||||
let mut logproof_builder = SdlpBuilder::new(&rt);
|
||||
|
||||
let _ct = logproof_builder
|
||||
let ct = logproof_builder
|
||||
.encrypt_symmetric(&Unsigned64::from(3), &private_key)
|
||||
.unwrap();
|
||||
|
||||
let sdlp = logproof_builder.build_logproof().unwrap();
|
||||
sdlp.verify().unwrap();
|
||||
let sdlp = logproof_builder.build().unwrap();
|
||||
|
||||
let mut logproof_vk_builder = SdlpVerificationBuilder::new(&rt);
|
||||
logproof_vk_builder.encrypt_symmetric(&ct).unwrap();
|
||||
logproof_vk_builder.proof(sdlp).verify().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prove_linked_statements() {
|
||||
let rt = FheRuntime::new(&TEST_PARAMS).unwrap();
|
||||
let (public_key, private_key) = rt.generate_keys().unwrap();
|
||||
let mut logproof_builder = LogProofBuilder::new(&rt);
|
||||
let mut logproof_builder = SdlpBuilder::new(&rt);
|
||||
|
||||
let (_a1, linked_a) = logproof_builder
|
||||
let (a1, linked_a) = logproof_builder
|
||||
.encrypt_returning_msg(&Fractional::<64>::from(3.23), &public_key)
|
||||
.unwrap();
|
||||
let _a2 = logproof_builder
|
||||
.encrypt_symmetric_msg(&linked_a, &private_key)
|
||||
let a2 = logproof_builder
|
||||
.reencrypt_symmetric(&linked_a, &private_key)
|
||||
.unwrap();
|
||||
let _other = logproof_builder
|
||||
let other = logproof_builder
|
||||
.encrypt(&Signed::from(2), &public_key)
|
||||
.unwrap();
|
||||
let sdlp = logproof_builder.build_logproof().unwrap();
|
||||
sdlp.verify().unwrap();
|
||||
|
||||
let sdlp = logproof_builder.build().unwrap();
|
||||
|
||||
let mut logproof_vk_builder = SdlpVerificationBuilder::new(&rt);
|
||||
let linked_a = logproof_vk_builder
|
||||
.encrypt_returning_msg(&a1, &public_key)
|
||||
.unwrap();
|
||||
logproof_vk_builder
|
||||
.reencrypt_symmetric(&linked_a, &a2)
|
||||
.unwrap();
|
||||
logproof_vk_builder.encrypt(&other, &public_key).unwrap();
|
||||
logproof_vk_builder.proof(sdlp).verify().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prove_refreshing_existing_ciphertext() {
|
||||
let rt = FheRuntime::new(&TEST_PARAMS).unwrap();
|
||||
let (public_key, private_key) = rt.generate_keys().unwrap();
|
||||
let mut logproof_builder = LogProofBuilder::new(&rt);
|
||||
|
||||
let initial_ct = rt.encrypt(Signed::from(100), &public_key).unwrap();
|
||||
|
||||
#[fhe_program(scheme = "bfv")]
|
||||
fn double(x: sunscreen::types::Cipher<Signed>) -> sunscreen::types::Cipher<Signed> {
|
||||
x + x
|
||||
}
|
||||
|
||||
let double_compiled = double.compile().unwrap();
|
||||
|
||||
let computed_ct = rt
|
||||
.run(&double_compiled, vec![initial_ct], &public_key)
|
||||
.unwrap()
|
||||
.remove(0);
|
||||
|
||||
let mut logproof_builder = SdlpBuilder::new(&rt);
|
||||
let (_, msg) = logproof_builder
|
||||
.decrypt_returning_msg::<Signed>(&computed_ct, &private_key)
|
||||
.unwrap();
|
||||
let _refreshed_ct = logproof_builder.encrypt_msg(&msg, &public_key).unwrap();
|
||||
let refreshed_ct = logproof_builder.reencrypt(&msg, &public_key).unwrap();
|
||||
|
||||
let sdlp = logproof_builder.build_logproof().unwrap();
|
||||
sdlp.verify().unwrap();
|
||||
let sdlp = logproof_builder.build().unwrap();
|
||||
|
||||
let mut logproof_vk_builder = SdlpVerificationBuilder::new(&rt);
|
||||
let msg = logproof_vk_builder
|
||||
.decrypt_returning_msg(&computed_ct)
|
||||
.unwrap();
|
||||
logproof_vk_builder
|
||||
.reencrypt(&msg, &refreshed_ct, &public_key)
|
||||
.unwrap();
|
||||
logproof_vk_builder.proof(sdlp).verify().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Submodule sunscreen_bulletproofs updated: fefc70bd9f...1a0e4215df
@@ -29,6 +29,7 @@ cust = { workspace = true, optional = true }
|
||||
num = { workspace = true }
|
||||
crypto-bigint = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
sunscreen_math_macros = { workspace = true }
|
||||
subtle = { workspace = true }
|
||||
log.workspace = true
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::ops::{Add, Index, IndexMut, Mul, Neg, Sub};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sunscreen_math_macros::refify_binary_op;
|
||||
|
||||
use crate::{ring::Ring, One, Zero};
|
||||
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
|
||||
/// A polynomial over the ring `T`.
|
||||
///
|
||||
/// # Remarks
|
||||
|
||||
@@ -3,6 +3,7 @@ use crypto_bigint::NonZero;
|
||||
pub use crypto_bigint::Uint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use num::traits::{ToBytes, WrappingAdd, WrappingMul, WrappingNeg, WrappingSub};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
ops::{Add, Mul, Neg, Sub},
|
||||
@@ -405,6 +406,97 @@ impl<const N: usize, B: ArithmeticBackend<N>> Clone for Zq<N, B> {
|
||||
|
||||
impl<const N: usize, B: ArithmeticBackend<N>> Copy for Zq<N, B> {}
|
||||
|
||||
impl<const N: usize, B: ArithmeticBackend<N>> Serialize for Zq<N, B> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
// Helper method that is valid when `N == M`, even though the compiler doesn't realize it.
|
||||
fn cast<const M: usize, const N: usize, B: ArithmeticBackend<N>>(x: &Zq<N, B>) -> Uint<M> {
|
||||
Uint::<M>::new(x.into_bigint().to_limbs()[..].try_into().unwrap())
|
||||
}
|
||||
// Support up to 16 limbs (U1024 on 64-bit, U512 on 32-bit), and panic loudly if we
|
||||
// ever exceed this.
|
||||
// Note that on 32-bit builds, Uint<LIMBS> impls the Encoding trait necessary for
|
||||
// serialization only for even LIMBS.
|
||||
match N {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
1 => cast::<1, N, B>(self).serialize(serializer),
|
||||
2 => cast::<2, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
3 => cast::<3, N, B>(self).serialize(serializer),
|
||||
4 => cast::<4, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
5 => cast::<5, N, B>(self).serialize(serializer),
|
||||
6 => cast::<6, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
7 => cast::<7, N, B>(self).serialize(serializer),
|
||||
8 => cast::<8, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
9 => cast::<9, N, B>(self).serialize(serializer),
|
||||
10 => cast::<10, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
11 => cast::<11, N, B>(self).serialize(serializer),
|
||||
12 => cast::<12, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
13 => cast::<13, N, B>(self).serialize(serializer),
|
||||
14 => cast::<14, N, B>(self).serialize(serializer),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
15 => cast::<15, N, B>(self).serialize(serializer),
|
||||
16 => cast::<16, N, B>(self).serialize(serializer),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize, B: ArithmeticBackend<N>> Deserialize<'de> for Zq<N, B> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// Helper method that is valid when `N == M`, even though the compiler doesn't realize it.
|
||||
fn cast<'de, const M: usize, const N: usize, B, D>(x: Uint<M>) -> Result<Zq<N, B>, D::Error>
|
||||
where
|
||||
B: ArithmeticBackend<N>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Zq::try_from(Uint::<N>::new(x.to_limbs()[..].try_into().unwrap()))
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
// Support up to 16 limbs (U1024 on 64-bit, U512 on 32-bit), and panic loudly if we
|
||||
// ever exceed this.
|
||||
// Note that on 32-bit builds, Uint<LIMBS> impls the Encoding trait necessary for
|
||||
// deserialization only for even LIMBS.
|
||||
match N {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
1 => cast::<'_, 1, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
2 => cast::<'_, 2, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
3 => cast::<'_, 3, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
4 => cast::<'_, 4, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
5 => cast::<'_, 5, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
6 => cast::<'_, 6, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
7 => cast::<'_, 7, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
8 => cast::<'_, 8, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
9 => cast::<'_, 9, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
10 => cast::<'_, 10, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
11 => cast::<'_, 11, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
12 => cast::<'_, 12, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
13 => cast::<'_, 13, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
14 => cast::<'_, 14, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
15 => cast::<'_, 15, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
16 => cast::<'_, 16, N, B, D>(Uint::deserialize(deserializer)?),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[refify_binary_op]
|
||||
impl<const N: usize, B> Add<&Zq<N, B>> for &Zq<N, B>
|
||||
where
|
||||
|
||||
@@ -206,7 +206,11 @@ impl<'r, 'p, 'a, T: marker::Zkp, B: ZkpBackend> VerificationBuilder<'r, 'p, 'a,
|
||||
}
|
||||
|
||||
#[cfg(feature = "linkedproofs")]
|
||||
pub use linked::{ExistingMessage, LinkWithZkp, LinkedMessage, LogProofBuilder, Message};
|
||||
pub use linked::{
|
||||
ExistingMessage, LinkWithZkp, LinkedMessage, LinkedProofBuilder,
|
||||
LinkedProofVerificationBuilder, LogProofBuilder, LogProofVerificationBuilder, Message,
|
||||
MessageRef, SdlpBuilder, SdlpVerificationBuilder,
|
||||
};
|
||||
|
||||
#[cfg(feature = "linkedproofs")]
|
||||
mod linked {
|
||||
@@ -218,7 +222,7 @@ mod linked {
|
||||
bfv_statement::{self, BfvMessage, BfvProofStatement, BfvWitness, StatementParams},
|
||||
math::Log2,
|
||||
rings::{SealQ128_1024, SealQ128_2048, SealQ128_4096, SealQ128_8192},
|
||||
Bounds, LogProofProverKnowledge,
|
||||
Bounds, LogProofProverKnowledge, LogProofVerifierKnowledge,
|
||||
};
|
||||
use sunscreen_compiler_common::{Type, TypeName};
|
||||
use sunscreen_math::ring::{BarrettBackend, BarrettConfig, Zq};
|
||||
@@ -228,9 +232,10 @@ mod linked {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
marker, Ciphertext, CompiledZkpProgram, GenericRuntime, LinkedProof, NumCiphertexts,
|
||||
Params, Plaintext, PrivateKey, PublicKey, Result, Sdlp, SealSdlpProverKnowledge,
|
||||
TryFromPlaintext, TryIntoPlaintext, ZkpProgramInput,
|
||||
marker, Ciphertext, CompiledZkpProgram, Fhe, FheRuntime, FheZkp, FheZkpRuntime,
|
||||
GenericRuntime, LinkedProof, NumCiphertexts, Params, Plaintext, PrivateKey, PublicKey,
|
||||
Result, Sdlp, SdlpProverKnowledge, SdlpVerifierKnowledge, TryFromPlaintext,
|
||||
TryIntoPlaintext, ZkpProgramInput,
|
||||
};
|
||||
|
||||
/// All FHE plaintext types can be used in a [`Sdlp`]. This trait indicates further that a
|
||||
@@ -277,12 +282,12 @@ mod linked {
|
||||
zkp_type: Z,
|
||||
}
|
||||
|
||||
/// A [`Plaintext`] message that can be [encrypted again](`LogProofBuilder::encrypt_msg`).
|
||||
/// A [`Plaintext`] message that can be [encrypted again](`LogProofBuilder::reencrypt`).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Message(MessageInternal<()>);
|
||||
|
||||
/// A [`Plaintext`] message that can be [encrypted again](`LogProofBuilder::encrypt_msg`) or
|
||||
/// [linked to a ZKP](`LogProofBuilder::linked_input`). Create this with
|
||||
/// A [`Plaintext`] message that can be [encrypted again](`LogProofBuilder::reencrypt`) or
|
||||
/// [linked to a ZKP program](`LogProofBuilder::linked_input`). Create this with
|
||||
/// [`LogProofBuilder::encrypt_returning_link`].
|
||||
#[derive(Debug)]
|
||||
pub struct LinkedMessage(MessageInternal<Type>);
|
||||
@@ -306,7 +311,7 @@ mod linked {
|
||||
}
|
||||
|
||||
/// Indicates that the message is already added to the SDLP, and hence can be used as an
|
||||
/// argument to [`LogProofBuilder::encrypt_msg`].
|
||||
/// argument to [`LogProofBuilder::reencrypt`].
|
||||
pub trait ExistingMessage: private::Sealed {
|
||||
/// Convert the message to the internal type.
|
||||
fn as_internal(&self) -> MessageInternal<()>;
|
||||
@@ -396,6 +401,7 @@ mod linked {
|
||||
statements: Vec<BfvProofStatement<'k>>,
|
||||
messages: Vec<BfvMessage>,
|
||||
witness: Vec<BfvWitness<'k>>,
|
||||
custom_bounds: Vec<((usize, usize), Bounds)>,
|
||||
|
||||
// linked proof fields
|
||||
compiled_zkp_program: Option<&'z CompiledZkpProgram>,
|
||||
@@ -405,14 +411,51 @@ mod linked {
|
||||
constant_inputs: Vec<ZkpProgramInput>,
|
||||
}
|
||||
|
||||
/// A builder for an [`Sdlp`] (without any linked ZKP program).
|
||||
pub type SdlpBuilder<'r, 'k> = LogProofBuilder<'r, 'k, 'static, Fhe, ()>;
|
||||
|
||||
impl<'r, 'k> SdlpBuilder<'r, 'k> {
|
||||
/// Create a new [`SdlpBuilder`].
|
||||
pub fn new(runtime: &'r FheRuntime) -> Self {
|
||||
LogProofBuilder::new_internal(runtime)
|
||||
}
|
||||
|
||||
/// Build the [`Sdlp`].
|
||||
pub fn build(self) -> Result<Sdlp> {
|
||||
self.build_logproof()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a [`LinkedProof`].
|
||||
pub type LinkedProofBuilder<'r, 'k, 'z> =
|
||||
LogProofBuilder<'r, 'k, 'z, FheZkp, BulletproofsBackend>;
|
||||
|
||||
impl<'r, 'k, 'z> LinkedProofBuilder<'r, 'k, 'z> {
|
||||
/// Create a new [`LinkedProofBuilder`].
|
||||
pub fn new(runtime: &'r FheZkpRuntime<BulletproofsBackend>) -> Self {
|
||||
LogProofBuilder::new_internal(runtime)
|
||||
}
|
||||
|
||||
/// Build just the [`Sdlp`] portion of the linked proof.
|
||||
pub fn build_sdlp(&self) -> Result<Sdlp> {
|
||||
self.build_logproof()
|
||||
}
|
||||
|
||||
/// Build the [`LinkedProof`].
|
||||
pub fn build(&mut self) -> Result<LinkedProof> {
|
||||
self.build_linkedproof()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'k, 'z, M: marker::Fhe, Z> LogProofBuilder<'r, 'k, 'z, M, Z> {
|
||||
/// Create a new [`LogProofBuilder`].
|
||||
pub fn new(runtime: &'r GenericRuntime<M, Z>) -> Self {
|
||||
fn new_internal(runtime: &'r GenericRuntime<M, Z>) -> Self {
|
||||
Self {
|
||||
runtime,
|
||||
statements: vec![],
|
||||
messages: vec![],
|
||||
witness: vec![],
|
||||
custom_bounds: vec![],
|
||||
compiled_zkp_program: None,
|
||||
linked_inputs: vec![],
|
||||
private_inputs: vec![],
|
||||
@@ -450,7 +493,7 @@ mod linked {
|
||||
}
|
||||
|
||||
/// Encrypt a plaintext, adding the encryption statement to the logproof and returning the
|
||||
/// message to optionally be [encrypted again](`Self::encrypt_msg`), that is, _shared_
|
||||
/// message to optionally be [encrypted again](`Self::reencrypt`), that is, _shared_
|
||||
/// with another logproof statement.
|
||||
///
|
||||
/// If you do not want to add the encryption statement to the proof, just use [the
|
||||
@@ -467,7 +510,7 @@ mod linked {
|
||||
}
|
||||
|
||||
/// Encrypt a plaintext, adding the encryption statement to the logproof and returning the
|
||||
/// message to optionally be [encrypted again](`Self::encrypt_msg`), that is, _shared_
|
||||
/// message to optionally be [encrypted again](`Self::reencrypt_symmetric`), that is, _shared_
|
||||
/// with another logproof statement.
|
||||
///
|
||||
/// If you do not want to add the encryption statement to the proof, just use [the
|
||||
@@ -516,13 +559,13 @@ mod linked {
|
||||
Ok((ct, Message(msg_internal)))
|
||||
}
|
||||
|
||||
/// Encrypt an existing message, adding the new encryption statement to the logproof.
|
||||
/// Re-encrypt an existing message, adding the new encryption statement to the logproof.
|
||||
///
|
||||
/// This method purposefully reveals that two ciphertexts encrypt the same underlying value. If
|
||||
/// this is not what you want, use [`Self::encrypt`].
|
||||
/// This method purposefully reveals that two ciphertexts encrypt the same underlying
|
||||
/// plaintext. If this is not what you want, use [`Self::encrypt`].
|
||||
///
|
||||
/// This method assumes that you've created the `message` argument with _this_ builder.
|
||||
pub fn encrypt_msg<E: ExistingMessage>(
|
||||
pub fn reencrypt<E: ExistingMessage>(
|
||||
&mut self,
|
||||
message: &E,
|
||||
public_key: &'k PublicKey,
|
||||
@@ -536,14 +579,14 @@ mod linked {
|
||||
)
|
||||
}
|
||||
|
||||
/// Encrypt an existing message symmetrically, adding the new encryption statement to the
|
||||
/// Re-encrypt an existing message symmetrically, adding the new encryption statement to the
|
||||
/// logproof.
|
||||
///
|
||||
/// This method purposefully reveals that two ciphertexts encrypt the same underlying value. If
|
||||
/// this is not what you want, use [`Self::encrypt_symmetric`].
|
||||
/// This method purposefully reveals that two ciphertexts encrypt the same underlying
|
||||
/// plaintext. If this is not what you want, use [`Self::encrypt_symmetric`].
|
||||
///
|
||||
/// This method assumes that you've created the `message` argument with _this_ builder.
|
||||
pub fn encrypt_symmetric_msg<E: ExistingMessage>(
|
||||
pub fn reencrypt_symmetric<E: ExistingMessage>(
|
||||
&mut self,
|
||||
message: &E,
|
||||
private_key: &'k PrivateKey,
|
||||
@@ -561,8 +604,14 @@ mod linked {
|
||||
/// message to be shared with another proof statement.
|
||||
///
|
||||
/// Use this method if you have an existing ciphertext and want to prove that it is well
|
||||
/// formed, and you intend to re-encrypt it, refreshing the noise but not the plaintext
|
||||
/// polynomial encoding.
|
||||
/// formed, and you intend to [reencrypt](Self::reencrypt) it, refreshing the noise but _not_
|
||||
/// the plaintext polynomial encoding.
|
||||
///
|
||||
/// # Remarks
|
||||
/// This method is only useful in niche scenarios; you probably want to [return a
|
||||
/// link](Self::decrypt_returning_link) instead, encrypt a new ciphertext with [fresh
|
||||
/// noise and a fresh encoding](Self::encrypt_returning_link), and prove equality within a
|
||||
/// linked ZKP program.
|
||||
pub fn decrypt_returning_msg<P>(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
@@ -698,32 +747,41 @@ mod linked {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build the [`Sdlp`] for the statements added to this builder.
|
||||
/// Customize bounds for a given entry in the secret `S`. Note the verifier must also
|
||||
/// provide the same custom bounds when building the verifier knowledge.
|
||||
///
|
||||
/// You can use this as a standalone proof, or alternatively see
|
||||
/// [`Self::build_linkedproof`] to prove additional properties about the underlying
|
||||
/// plaintexts.
|
||||
pub fn build_logproof(&self) -> Result<Sdlp> {
|
||||
/// # Remarks
|
||||
/// This is for advanced users that are familiar with the shape of the matrices documented
|
||||
/// in [`logproof::bfv_statement::generate_prover_knowledge`].
|
||||
pub fn add_custom_bounds(&mut self, row: usize, col: usize, bounds: Bounds) -> &mut Self {
|
||||
self.custom_bounds.push(((row, col), bounds));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Sdlp`] for the statements added to this builder.
|
||||
fn build_logproof(&self) -> Result<Sdlp> {
|
||||
Sdlp::create(&self.build_sdlp_pk()?)
|
||||
}
|
||||
|
||||
fn build_sdlp_pk(&self) -> Result<SealSdlpProverKnowledge> {
|
||||
fn build_sdlp_pk(&self) -> Result<SdlpProverKnowledge> {
|
||||
let params = self.runtime.params();
|
||||
match ¶ms.coeff_modulus[..] {
|
||||
SealQ128_1024::Q => Ok(SealSdlpProverKnowledge::from(
|
||||
self.build_sdlp_pk_generic::<1, SealQ128_1024>()?,
|
||||
)),
|
||||
SealQ128_2048::Q => Ok(SealSdlpProverKnowledge::from(
|
||||
self.build_sdlp_pk_generic::<1, SealQ128_2048>()?,
|
||||
)),
|
||||
SealQ128_4096::Q => Ok(SealSdlpProverKnowledge::from(
|
||||
self.build_sdlp_pk_generic::<2, SealQ128_4096>()?,
|
||||
)),
|
||||
SealQ128_8192::Q => Ok(SealSdlpProverKnowledge::from(
|
||||
self.build_sdlp_pk_generic::<3, SealQ128_8192>()?,
|
||||
)),
|
||||
_ => Err(BuilderError::UnsupportedParameters(Box::new(params.clone())).into()),
|
||||
let mut pk: SdlpProverKnowledge = match ¶ms.coeff_modulus[..] {
|
||||
SealQ128_1024::Q => Ok(self.build_sdlp_pk_generic::<1, SealQ128_1024>()?.into()),
|
||||
SealQ128_2048::Q => Ok(self.build_sdlp_pk_generic::<1, SealQ128_2048>()?.into()),
|
||||
SealQ128_4096::Q => Ok(self.build_sdlp_pk_generic::<2, SealQ128_4096>()?.into()),
|
||||
SealQ128_8192::Q => Ok(self.build_sdlp_pk_generic::<3, SealQ128_8192>()?.into()),
|
||||
_ => Err(BuilderError::UnsupportedParameters(Box::new(
|
||||
params.clone(),
|
||||
))),
|
||||
}?;
|
||||
|
||||
// Add the custom bounds, if any
|
||||
let bounds = pk.bounds_mut();
|
||||
for ((row, col), bound) in &self.custom_bounds {
|
||||
bounds[(*row, *col)] = bound.clone();
|
||||
}
|
||||
|
||||
Ok(pk)
|
||||
}
|
||||
|
||||
fn build_sdlp_pk_generic<const N: usize, B: BarrettConfig<N>>(
|
||||
@@ -739,20 +797,13 @@ mod linked {
|
||||
ctx,
|
||||
))
|
||||
}
|
||||
|
||||
fn mk_bounds<P: LinkWithZkp>(&self) -> Bounds {
|
||||
let params = self.runtime.params();
|
||||
let mut bounds = vec![params.plain_modulus.ceil_log2(); P::DEGREE_BOUND];
|
||||
bounds.resize(params.lattice_dimension as usize, 0);
|
||||
Bounds(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'k, 'z, M: marker::Fhe + marker::Zkp> LogProofBuilder<'r, 'k, 'z, M, BulletproofsBackend> {
|
||||
/// Encrypt a plaintext intended for linking.
|
||||
///
|
||||
/// The returned `LinkedMessage` can be used:
|
||||
/// 1. to add an encryption statement of ciphertext equality to the logproof (see [`Self::encrypt_msg`]).
|
||||
/// 1. to add an encryption statement of ciphertext equality to the logproof (see [`Self::reencrypt`]).
|
||||
/// 2. as a linked input to a ZKP program (see [`Self::linked_input`]).
|
||||
pub fn encrypt_returning_link<P>(
|
||||
&mut self,
|
||||
@@ -763,7 +814,7 @@ mod linked {
|
||||
P: LinkWithZkp + TryIntoPlaintext + TypeName,
|
||||
{
|
||||
// The user intends to link this message, so add a more conservative bound
|
||||
let bounds = self.mk_bounds::<P>();
|
||||
let bounds = mk_bounds::<P>(self.runtime.params());
|
||||
let (ct, msg) = self.encrypt_returning_msg_internal(
|
||||
message,
|
||||
Key::Public(public_key),
|
||||
@@ -776,7 +827,7 @@ mod linked {
|
||||
/// Symmetrically encrypt a plaintext intended for linking.
|
||||
///
|
||||
/// The returned `LinkedMessage` can be used:
|
||||
/// 1. to add an encryption statement of ciphertext equality to the logproof (see [`Self::encrypt_msg`]).
|
||||
/// 1. to add an encryption statement of ciphertext equality to the logproof (see [`Self::reencrypt_symmetric`]).
|
||||
/// 2. as a linked input to a ZKP program (see [`Self::linked_input`]).
|
||||
pub fn encrypt_symmetric_returning_link<P>(
|
||||
&mut self,
|
||||
@@ -787,7 +838,7 @@ mod linked {
|
||||
P: LinkWithZkp + TryIntoPlaintext + TypeName,
|
||||
{
|
||||
// The user intends to link this message, so add a more conservative bound
|
||||
let bounds = self.mk_bounds::<P>();
|
||||
let bounds = mk_bounds::<P>(self.runtime.params());
|
||||
let (ct, msg) = self.encrypt_returning_msg_internal(
|
||||
message,
|
||||
Key::Private(private_key),
|
||||
@@ -798,7 +849,13 @@ mod linked {
|
||||
}
|
||||
|
||||
/// Decrypt a ciphertext, adding the decryption statement to the logproof and returning the
|
||||
/// linked message to be shared with another logproof statement or linked to a ZKP program.
|
||||
/// linked message to be [linked to a ZKP program](Self::linked_input).
|
||||
///
|
||||
/// # Remarks
|
||||
/// You can [reencrypt](Self::reencrypt) the returned linked message, however while this
|
||||
/// will reset the noise level it will not refresh the plaintext polynomial encoding. Most likely,
|
||||
/// you want to use [`Self::encrypt_returning_link`] to encrypt a _new_ plaintext encoding
|
||||
/// instead, and prove equality within a linked ZKP program.
|
||||
pub fn decrypt_returning_link<P>(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
@@ -807,7 +864,7 @@ mod linked {
|
||||
where
|
||||
P: LinkWithZkp + TryIntoPlaintext + TryFromPlaintext + TypeName,
|
||||
{
|
||||
let bounds = self.mk_bounds::<P>();
|
||||
let bounds = mk_bounds::<P>(self.runtime.params());
|
||||
let (pt, msg) = self.decrypt_internal::<P>(ciphertext, private_key, Some(bounds))?;
|
||||
let zkp_type = P::ZkpType::<BulletproofsFieldSpec>::type_name();
|
||||
Ok((pt, LinkedMessage::from_message(msg, zkp_type)))
|
||||
@@ -815,7 +872,7 @@ mod linked {
|
||||
|
||||
/// Add a ZKP program to be linked with the logproof.
|
||||
///
|
||||
/// This method is required to call [`Self::build_linkedproof`].
|
||||
/// This method is required to call [`LinkedProofBuilder::build`].
|
||||
pub fn zkp_program(&mut self, program: &'z CompiledZkpProgram) -> Result<&mut Self> {
|
||||
let params = program.metadata.params.as_ref().ok_or_else(|| {
|
||||
BuilderError::user_error(
|
||||
@@ -859,7 +916,7 @@ mod linked {
|
||||
|
||||
/// Output a [`LinkedProof`] from the encryption statements and ZKP program and inputs added to
|
||||
/// this builder.
|
||||
pub fn build_linkedproof(&mut self) -> Result<crate::linked::LinkedProof> {
|
||||
fn build_linkedproof(&self) -> Result<crate::linked::LinkedProof> {
|
||||
let sdlp = self.build_sdlp_pk()?;
|
||||
let program = self.compiled_zkp_program.ok_or_else(|| {
|
||||
BuilderError::user_error("Cannot build linked proof without a compiled ZKP program. Use the `.zkp_program()` method")
|
||||
@@ -887,6 +944,12 @@ mod linked {
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_bounds<P: LinkWithZkp>(params: &Params) -> Bounds {
|
||||
let mut bounds = vec![params.plain_modulus.ceil_log2(); P::DEGREE_BOUND];
|
||||
bounds.resize(params.lattice_dimension as usize, 0);
|
||||
Bounds(bounds)
|
||||
}
|
||||
|
||||
impl StatementParams for Params {
|
||||
fn degree(&self) -> u64 {
|
||||
self.lattice_dimension
|
||||
@@ -900,4 +963,381 @@ mod linked {
|
||||
self.coeff_modulus.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// This message type is used as an analog to the `Message` and `LinkedMessage` of the proof
|
||||
/// builder. While the proof builder actually takes in the private messages, the verifier simply
|
||||
/// deals with "references" to messages, i.e. their indicies and ordering.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageRef {
|
||||
id: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// A builder for verifying an [`Sdlp`] or [`LinkedProof`].
|
||||
///
|
||||
/// The idea is that you call the same methods, in the same order, on the verification builder
|
||||
/// as you do on the proof builder. Library or protocol authors can add wrappers around this,
|
||||
/// via labels or other semantic attachments, but at this level, all that matters is the
|
||||
/// ordering.
|
||||
pub struct LogProofVerificationBuilder<'r, 'k, 'z, M, B> {
|
||||
runtime: &'r GenericRuntime<M, B>,
|
||||
|
||||
// log proof fields
|
||||
statements: Vec<BfvProofStatement<'k>>,
|
||||
message_bounds: Vec<Option<Bounds>>,
|
||||
sdlp: Option<Sdlp>,
|
||||
custom_bounds: Vec<((usize, usize), Bounds)>,
|
||||
|
||||
// linked proof fields
|
||||
compiled_zkp_program: Option<&'z CompiledZkpProgram>,
|
||||
public_inputs: Vec<ZkpProgramInput>,
|
||||
constant_inputs: Vec<ZkpProgramInput>,
|
||||
linkedproof: Option<LinkedProof>,
|
||||
}
|
||||
|
||||
/// A builder for verifying an [`Sdlp`].
|
||||
pub type SdlpVerificationBuilder<'r, 'k> =
|
||||
LogProofVerificationBuilder<'r, 'k, 'static, Fhe, ()>;
|
||||
|
||||
impl<'r, 'k> SdlpVerificationBuilder<'r, 'k> {
|
||||
/// Create a new [`SdlpVerificationBuilder`].
|
||||
pub fn new(runtime: &'r FheRuntime) -> Self {
|
||||
LogProofVerificationBuilder::new_internal(runtime)
|
||||
}
|
||||
|
||||
/// Set the SDLP to prove.
|
||||
pub fn proof(&mut self, proof: Sdlp) -> &mut Self {
|
||||
self.sdlp = Some(proof);
|
||||
self
|
||||
}
|
||||
|
||||
/// Verify the SDLP.
|
||||
pub fn verify(&mut self) -> Result<()> {
|
||||
let vk = self.build_sdlp_vk()?;
|
||||
let sdlp = self.sdlp.as_mut().ok_or_else(|| {
|
||||
BuilderError::user_error(
|
||||
"You must supply a proof to the verification builder before calling `verify`. Use the `.proof()` method.",
|
||||
)
|
||||
})?;
|
||||
sdlp.verify(&vk)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for verifying a [`LinkedProof`].
|
||||
pub type LinkedProofVerificationBuilder<'r, 'k, 'z> =
|
||||
LogProofVerificationBuilder<'r, 'k, 'z, FheZkp, BulletproofsBackend>;
|
||||
|
||||
impl<'r, 'k, 'z> LinkedProofVerificationBuilder<'r, 'k, 'z> {
|
||||
/// Create a new [`LinkedProofVerificationBuilder`].
|
||||
pub fn new(runtime: &'r FheZkpRuntime<BulletproofsBackend>) -> Self {
|
||||
LogProofVerificationBuilder::new_internal(runtime)
|
||||
}
|
||||
|
||||
/// Set the linked proof to prove.
|
||||
pub fn proof(&mut self, proof: LinkedProof) -> &mut Self {
|
||||
self.linkedproof = Some(proof);
|
||||
self
|
||||
}
|
||||
|
||||
/// Verify the linked proof.
|
||||
pub fn verify(&mut self) -> Result<()> {
|
||||
let vk = self.build_sdlp_vk()?;
|
||||
let linkedproof = self.linkedproof.as_mut().ok_or_else(|| {
|
||||
BuilderError::user_error(
|
||||
"You must supply a proof to the verification builder before calling `verify`. Use the `.proof()` method.",
|
||||
)
|
||||
})?;
|
||||
let program = self.compiled_zkp_program.ok_or_else(|| {
|
||||
BuilderError::user_error("Cannot build linked proof without a compiled ZKP program. Use the `.zkp_program()` method")
|
||||
})?;
|
||||
linkedproof.verify(
|
||||
&vk,
|
||||
program,
|
||||
self.public_inputs.drain(0..).collect(),
|
||||
self.constant_inputs.drain(0..).collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'k, 'z, M: marker::Fhe, Z> LogProofVerificationBuilder<'r, 'k, 'z, M, Z> {
|
||||
fn new_internal(runtime: &'r GenericRuntime<M, Z>) -> Self {
|
||||
Self {
|
||||
runtime,
|
||||
statements: vec![],
|
||||
message_bounds: vec![],
|
||||
custom_bounds: vec![],
|
||||
compiled_zkp_program: None,
|
||||
public_inputs: vec![],
|
||||
constant_inputs: vec![],
|
||||
sdlp: None,
|
||||
linkedproof: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::decrypt_returning_msg`].
|
||||
pub fn decrypt_returning_msg(&mut self, ciphertext: &Ciphertext) -> Result<MessageRef> {
|
||||
self.decrypt_internal(ciphertext, None)
|
||||
}
|
||||
|
||||
fn decrypt_internal(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
bounds: Option<Bounds>,
|
||||
) -> Result<MessageRef> {
|
||||
let start_idx = self.message_bounds.len();
|
||||
for ct in ciphertext.inner_as_seal_ciphertext()? {
|
||||
self.statements.push(BfvProofStatement::Decryption {
|
||||
message_id: self.message_bounds.len(),
|
||||
ciphertext: ct.data.clone(),
|
||||
});
|
||||
self.message_bounds.push(bounds.clone());
|
||||
}
|
||||
let end_idx = self.message_bounds.len();
|
||||
|
||||
Ok(MessageRef {
|
||||
id: start_idx,
|
||||
len: end_idx - start_idx,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt`].
|
||||
pub fn encrypt(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
public_key: &'k PublicKey,
|
||||
) -> Result<()> {
|
||||
self.encrypt_internal(ciphertext, public_key, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt_returning_msg`].
|
||||
pub fn encrypt_returning_msg(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
public_key: &'k PublicKey,
|
||||
) -> Result<MessageRef> {
|
||||
self.encrypt_internal(ciphertext, public_key, None)
|
||||
}
|
||||
|
||||
fn encrypt_internal(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
public_key: &'k PublicKey,
|
||||
bounds: Option<Bounds>,
|
||||
) -> Result<MessageRef> {
|
||||
let start_idx = self.message_bounds.len();
|
||||
for ct in ciphertext.inner_as_seal_ciphertext()? {
|
||||
self.statements
|
||||
.push(BfvProofStatement::PublicKeyEncryption {
|
||||
message_id: self.message_bounds.len(),
|
||||
ciphertext: ct.data.clone(),
|
||||
public_key: Cow::Borrowed(&public_key.public_key.data),
|
||||
});
|
||||
self.message_bounds.push(bounds.clone());
|
||||
}
|
||||
let end_idx = self.message_bounds.len();
|
||||
|
||||
Ok(MessageRef {
|
||||
id: start_idx,
|
||||
len: end_idx - start_idx,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt_symmetric`].
|
||||
pub fn encrypt_symmetric(&mut self, ciphertext: &Ciphertext) -> Result<()> {
|
||||
self.encrypt_symmetric_internal(ciphertext, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt_symmetric_returning_msg`].
|
||||
pub fn encrypt_symmetric_returning_msg(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
) -> Result<MessageRef> {
|
||||
self.encrypt_symmetric_internal(ciphertext, None)
|
||||
}
|
||||
|
||||
fn encrypt_symmetric_internal(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
bounds: Option<Bounds>,
|
||||
) -> Result<MessageRef> {
|
||||
let start_idx = self.message_bounds.len();
|
||||
for ct in ciphertext.inner_as_seal_ciphertext()? {
|
||||
self.statements
|
||||
.push(BfvProofStatement::PrivateKeyEncryption {
|
||||
message_id: self.message_bounds.len(),
|
||||
ciphertext: ct.data.clone(),
|
||||
});
|
||||
self.message_bounds.push(bounds.clone());
|
||||
}
|
||||
let end_idx = self.message_bounds.len();
|
||||
|
||||
Ok(MessageRef {
|
||||
id: start_idx,
|
||||
len: end_idx - start_idx,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::reencrypt`].
|
||||
pub fn reencrypt(
|
||||
&mut self,
|
||||
message: &MessageRef,
|
||||
ciphertext: &Ciphertext,
|
||||
public_key: &'k PublicKey,
|
||||
) -> Result<()> {
|
||||
let mut ct_size = 0;
|
||||
for (i, ct) in ciphertext.inner_as_seal_ciphertext()?.iter().enumerate() {
|
||||
self.statements
|
||||
.push(BfvProofStatement::PublicKeyEncryption {
|
||||
message_id: message.id + i,
|
||||
ciphertext: ct.data.clone(),
|
||||
public_key: Cow::Borrowed(&public_key.public_key.data),
|
||||
});
|
||||
ct_size += 1;
|
||||
}
|
||||
if ct_size != message.len {
|
||||
return Err(BuilderError::user_error(
|
||||
"The ciphertext's length does not match the existing message ref. This is likely a type mismatch.",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::reencrypt_symmetric`].
|
||||
pub fn reencrypt_symmetric(
|
||||
&mut self,
|
||||
message: &MessageRef,
|
||||
ciphertext: &Ciphertext,
|
||||
) -> Result<()> {
|
||||
let mut ct_size = 0;
|
||||
for (i, ct) in ciphertext.inner_as_seal_ciphertext()?.iter().enumerate() {
|
||||
self.statements
|
||||
.push(BfvProofStatement::PrivateKeyEncryption {
|
||||
message_id: message.id + i,
|
||||
ciphertext: ct.data.clone(),
|
||||
});
|
||||
ct_size += 1;
|
||||
}
|
||||
if ct_size != message.len {
|
||||
return Err(BuilderError::user_error(
|
||||
"The ciphertext's length does not match the existing message ref. This is likely a type mismatch.",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Customize bounds for a given entry in the secret `S`. Note these custom bounds must
|
||||
/// match those provided during the proof generation.
|
||||
///
|
||||
/// # Remarks
|
||||
/// This is for advanced users that are familiar with the shape of the matrices documented
|
||||
/// in [`logproof::bfv_statement::generate_prover_knowledge`].
|
||||
pub fn add_custom_bounds(&mut self, row: usize, col: usize, bounds: Bounds) -> &mut Self {
|
||||
self.custom_bounds.push(((row, col), bounds));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`SdlpVerifierKnowledge`] for the statements added to this builder.
|
||||
pub(crate) fn build_sdlp_vk(&self) -> Result<SdlpVerifierKnowledge> {
|
||||
let params = self.runtime.params();
|
||||
let mut vk: SdlpVerifierKnowledge = match ¶ms.coeff_modulus[..] {
|
||||
SealQ128_1024::Q => Ok(self.build_sdlp_vk_generic::<1, SealQ128_1024>()?.into()),
|
||||
SealQ128_2048::Q => Ok(self.build_sdlp_vk_generic::<1, SealQ128_2048>()?.into()),
|
||||
SealQ128_4096::Q => Ok(self.build_sdlp_vk_generic::<2, SealQ128_4096>()?.into()),
|
||||
SealQ128_8192::Q => Ok(self.build_sdlp_vk_generic::<3, SealQ128_8192>()?.into()),
|
||||
_ => Err(BuilderError::UnsupportedParameters(Box::new(
|
||||
params.clone(),
|
||||
))),
|
||||
}?;
|
||||
|
||||
// Add the custom bounds, if any
|
||||
let bounds = vk.bounds_mut();
|
||||
for ((row, col), bound) in &self.custom_bounds {
|
||||
bounds[(*row, *col)] = bound.clone();
|
||||
}
|
||||
|
||||
Ok(vk)
|
||||
}
|
||||
|
||||
fn build_sdlp_vk_generic<const N: usize, B: BarrettConfig<N>>(
|
||||
&self,
|
||||
) -> Result<LogProofVerifierKnowledge<Zq<N, BarrettBackend<N, B>>>> {
|
||||
Ok(bfv_statement::generate_verifier_knowledge(
|
||||
&self.statements,
|
||||
&self.message_bounds,
|
||||
self.runtime.params(),
|
||||
self.runtime.context(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'k, 'z, M: marker::Fhe + marker::Zkp>
|
||||
LogProofVerificationBuilder<'r, 'k, 'z, M, BulletproofsBackend>
|
||||
{
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt_returning_link`].
|
||||
pub fn encrypt_returning_link<P>(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
public_key: &'k PublicKey,
|
||||
) -> Result<MessageRef>
|
||||
where
|
||||
P: LinkWithZkp,
|
||||
{
|
||||
self.encrypt_internal(
|
||||
ciphertext,
|
||||
public_key,
|
||||
Some(mk_bounds::<P>(self.runtime.params())),
|
||||
)
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::encrypt_symmetric_returning_link`].
|
||||
pub fn encrypt_symmetric_returning_link<P>(
|
||||
&mut self,
|
||||
ciphertext: &Ciphertext,
|
||||
) -> Result<MessageRef>
|
||||
where
|
||||
P: LinkWithZkp,
|
||||
{
|
||||
self.encrypt_symmetric_internal(ciphertext, Some(mk_bounds::<P>(self.runtime.params())))
|
||||
}
|
||||
|
||||
/// Add verifier knowledge for [`LogProofBuilder::decrypt_returning_link`].
|
||||
pub fn decrypt_returning_link<P>(&mut self, ciphertext: &Ciphertext) -> Result<MessageRef>
|
||||
where
|
||||
P: LinkWithZkp,
|
||||
{
|
||||
self.decrypt_internal(ciphertext, Some(mk_bounds::<P>(self.runtime.params())))
|
||||
}
|
||||
|
||||
/// Add the ZKP program to verify.
|
||||
///
|
||||
/// This method is required to call [`Self::verify`].
|
||||
pub fn zkp_program(&mut self, program: &'z CompiledZkpProgram) -> Result<&mut Self> {
|
||||
let params = program.metadata.params.as_ref().ok_or_else(|| {
|
||||
BuilderError::user_error(
|
||||
"Cannot link a ZKP program without associated FHE parameters. Make sure your ZKP program has #[linked] parameters and is compiled alongside an FHE program.",
|
||||
)
|
||||
})?;
|
||||
if params != self.runtime.params() {
|
||||
return Err(BuilderError::user_error(
|
||||
"The FHE parameters of the ZKP program do not match the FHE parameters of the runtime.",
|
||||
));
|
||||
}
|
||||
self.compiled_zkp_program = Some(program);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Add a public input to the ZKP program.
|
||||
pub fn public_input(&mut self, input: impl Into<ZkpProgramInput>) -> &mut Self {
|
||||
self.public_inputs.push(input.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a constant input to the ZKP program.
|
||||
pub fn constant_input(&mut self, input: impl Into<ZkpProgramInput>) -> &mut Self {
|
||||
self.constant_inputs.push(input.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,16 @@ use bulletproofs::{BulletproofGens, GeneratorsChain, PedersenGens};
|
||||
use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar};
|
||||
use log::trace;
|
||||
use logproof::{
|
||||
linear_algebra::Matrix,
|
||||
math::rand256,
|
||||
rings::{ZqSeal128_1024, ZqSeal128_2048, ZqSeal128_4096, ZqSeal128_8192},
|
||||
InnerProductVerifierKnowledge, LogProof, LogProofGenerators, LogProofProverKnowledge,
|
||||
Bounds, InnerProductVerifierKnowledge, LogProof, LogProofGenerators, LogProofProverKnowledge,
|
||||
LogProofVerifierKnowledge, ProofError,
|
||||
};
|
||||
use merlin::Transcript;
|
||||
use paste::paste;
|
||||
use seq_macro::seq;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sunscreen_compiler_common::Type;
|
||||
use sunscreen_zkp_backend::{
|
||||
bulletproofs::{
|
||||
@@ -25,24 +27,23 @@ use sunscreen_zkp_backend::{
|
||||
|
||||
use crate::{CompiledZkpProgram, Result, TypeNameInstance, ZkpProgramInput, ZkpRuntime};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// SDLP proof and associated information for verification
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
/// SDLP proof
|
||||
pub struct Sdlp {
|
||||
proof: LogProof,
|
||||
vk: SealSdlpVerifierKnowledge,
|
||||
g: Vec<RistrettoPoint>,
|
||||
h: Vec<RistrettoPoint>,
|
||||
u: RistrettoPoint,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
/// R1CS BP proof and associated information for verification
|
||||
struct BP {
|
||||
proof: Proof,
|
||||
verifier_parameters: BulletproofVerifierParameters,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
/// A linked proof between an SDLP and R1CS BP
|
||||
pub struct LinkedProof {
|
||||
sdlp: Sdlp,
|
||||
@@ -96,7 +97,7 @@ impl LinkedProof {
|
||||
const TRANSCRIPT_LABEL: &'static [u8] = b"linked-sdlp-and-r1cs-bp";
|
||||
/// This function creates a linked proof.
|
||||
///
|
||||
/// Note that the [builder methods](`crate::LogProofBuilder`) offer an easier way to construct this
|
||||
/// Note that the [builder methods](`crate::LinkedProofBuilder`) offer an easier way to construct this
|
||||
/// proof. See the user documentation for more information.
|
||||
///
|
||||
/// Arguments:
|
||||
@@ -109,8 +110,8 @@ impl LinkedProof {
|
||||
/// * `private_inputs`: The private inputs to the ZKP program, not including the shared values
|
||||
/// * `public_inputs`: The public inputs to the ZKP program
|
||||
/// * `constant_inputs`: The constant inputs to the ZKP program
|
||||
pub fn create<I>(
|
||||
prover_knowledge: &SealSdlpProverKnowledge,
|
||||
pub(crate) fn create<I>(
|
||||
prover_knowledge: &SdlpProverKnowledge,
|
||||
shared_indices: &[(usize, usize)],
|
||||
shared_types: &[Type],
|
||||
program: &CompiledZkpProgram,
|
||||
@@ -159,7 +160,6 @@ impl LinkedProof {
|
||||
|
||||
let sdlp_package = Sdlp {
|
||||
proof: sdlp_proof,
|
||||
vk,
|
||||
g: gens.g,
|
||||
h: gens.h,
|
||||
u,
|
||||
@@ -245,8 +245,9 @@ impl LinkedProof {
|
||||
/// * `public_inputs`: The public inputs to the ZKP program
|
||||
/// * `constant_inputs`: The constant inputs to the ZKP program
|
||||
///
|
||||
pub fn verify<I>(
|
||||
pub(crate) fn verify<I>(
|
||||
&self,
|
||||
sdlp_vk: &SdlpVerifierKnowledge,
|
||||
program: &CompiledZkpProgram,
|
||||
public_inputs: Vec<I>,
|
||||
constant_inputs: Vec<I>,
|
||||
@@ -258,8 +259,7 @@ impl LinkedProof {
|
||||
|
||||
let mut transcript = Transcript::new(Self::TRANSCRIPT_LABEL);
|
||||
|
||||
self.sdlp
|
||||
.vk
|
||||
sdlp_vk
|
||||
.verify(
|
||||
&self.sdlp.proof,
|
||||
&mut transcript,
|
||||
@@ -297,17 +297,15 @@ impl Sdlp {
|
||||
/// used when only proving valid encryptions of known values, but _not_ for proving any
|
||||
/// properties of those underlying values.
|
||||
///
|
||||
/// The [builder methods](`crate::LogProofBuilder`) offer an easier way to construct this proof.
|
||||
pub fn create(prover_knowledge: &SealSdlpProverKnowledge) -> Result<Self> {
|
||||
/// The [builder methods](`crate::SdlpBuilder`) offer an easier way to construct this proof.
|
||||
pub(crate) fn create(prover_knowledge: &SdlpProverKnowledge) -> Result<Self> {
|
||||
let mut transcript = Transcript::new(Self::TRANSCRIPT_LABEL);
|
||||
let vk = prover_knowledge.vk();
|
||||
let gen = LogProofGenerators::new(vk.l() as usize);
|
||||
let gen = LogProofGenerators::new(prover_knowledge.vk().l() as usize);
|
||||
let u = InnerProductVerifierKnowledge::get_u();
|
||||
let proof = prover_knowledge.create_logproof(&mut transcript, &gen.g, &gen.h, &u);
|
||||
|
||||
Ok(Self {
|
||||
proof,
|
||||
vk,
|
||||
g: gen.g,
|
||||
h: gen.h,
|
||||
u,
|
||||
@@ -315,32 +313,31 @@ impl Sdlp {
|
||||
}
|
||||
|
||||
/// This function verifies a solo SDLP.
|
||||
pub fn verify(&self) -> Result<()> {
|
||||
pub(crate) fn verify(&self, vk: &SdlpVerifierKnowledge) -> Result<()> {
|
||||
let mut transcript = Transcript::new(Self::TRANSCRIPT_LABEL);
|
||||
|
||||
self.vk
|
||||
.verify(&self.proof, &mut transcript, &self.g, &self.h, &self.u)?;
|
||||
vk.verify(&self.proof, &mut transcript, &self.g, &self.h, &self.u)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The prover knowledge of an [`Sdlp`].
|
||||
pub struct SealSdlpProverKnowledge(pub(crate) SealSdlpProverKnowledgeInternal);
|
||||
pub(crate) struct SdlpProverKnowledge(pub(crate) SdlpProverKnowledgeInternal);
|
||||
|
||||
/// The verifier knowledge of an [`Sdlp`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SealSdlpVerifierKnowledge(pub(crate) SealSdlpVerifierKnowledgeInternal);
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct SdlpVerifierKnowledge(pub(crate) SdlpVerifierKnowledgeInternal);
|
||||
|
||||
pub(crate) enum SealSdlpProverKnowledgeInternal {
|
||||
pub(crate) enum SdlpProverKnowledgeInternal {
|
||||
LP1(LogProofProverKnowledge<ZqSeal128_1024>),
|
||||
LP2(LogProofProverKnowledge<ZqSeal128_2048>),
|
||||
LP3(LogProofProverKnowledge<ZqSeal128_4096>),
|
||||
LP4(LogProofProverKnowledge<ZqSeal128_8192>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum SealSdlpVerifierKnowledgeInternal {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum SdlpVerifierKnowledgeInternal {
|
||||
LP1(LogProofVerifierKnowledge<ZqSeal128_1024>),
|
||||
LP2(LogProofVerifierKnowledge<ZqSeal128_2048>),
|
||||
LP3(LogProofVerifierKnowledge<ZqSeal128_4096>),
|
||||
@@ -350,14 +347,14 @@ pub(crate) enum SealSdlpVerifierKnowledgeInternal {
|
||||
macro_rules! impl_from {
|
||||
($zq_type:ty, $variant:ident) => {
|
||||
paste! {
|
||||
impl From<LogProofProverKnowledge<$zq_type>> for SealSdlpProverKnowledge {
|
||||
impl From<LogProofProverKnowledge<$zq_type>> for SdlpProverKnowledge {
|
||||
fn from(k: LogProofProverKnowledge<$zq_type>) -> Self {
|
||||
Self(SealSdlpProverKnowledgeInternal::$variant(k))
|
||||
Self(SdlpProverKnowledgeInternal::$variant(k))
|
||||
}
|
||||
}
|
||||
impl From<LogProofVerifierKnowledge<$zq_type>> for SealSdlpVerifierKnowledge {
|
||||
impl From<LogProofVerifierKnowledge<$zq_type>> for SdlpVerifierKnowledge {
|
||||
fn from(k: LogProofVerifierKnowledge<$zq_type>) -> Self {
|
||||
Self(SealSdlpVerifierKnowledgeInternal::$variant(k))
|
||||
Self(SdlpVerifierKnowledgeInternal::$variant(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,15 +374,15 @@ macro_rules! seq_zq {
|
||||
)
|
||||
}
|
||||
|
||||
impl SealSdlpProverKnowledge {
|
||||
impl SdlpProverKnowledge {
|
||||
/// Get the binary expansion of a component of the witness matrix `S`.
|
||||
///
|
||||
/// Delegation to [`LogProofProverKnowledge::s_binary_by_index`].
|
||||
pub fn s_binary_by_index(&self, index: (usize, usize)) -> BitVec {
|
||||
pub(crate) fn s_binary_by_index(&self, index: (usize, usize)) -> BitVec {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpProverKnowledgeInternal::LP~N(pk) => pk.s_binary_by_index(index),
|
||||
SdlpProverKnowledgeInternal::LP~N(pk) => pk.s_binary_by_index(index),
|
||||
)*
|
||||
}
|
||||
})
|
||||
@@ -394,18 +391,29 @@ impl SealSdlpProverKnowledge {
|
||||
/// Get the verifier knowledge component.
|
||||
///
|
||||
/// Delegation to [`LogProofProverKnowledge::vk`].
|
||||
pub fn vk(&self) -> SealSdlpVerifierKnowledge {
|
||||
pub(crate) fn vk(&self) -> SdlpVerifierKnowledge {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpProverKnowledgeInternal::LP~N(pk) => {
|
||||
SealSdlpVerifierKnowledge(SealSdlpVerifierKnowledgeInternal::LP~N(pk.vk.clone()))
|
||||
SdlpProverKnowledgeInternal::LP~N(pk) => {
|
||||
SdlpVerifierKnowledge(SdlpVerifierKnowledgeInternal::LP~N(pk.vk.clone()))
|
||||
}
|
||||
)*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the bounds on the secret `S`.
|
||||
pub(crate) fn bounds_mut(&mut self) -> &mut Matrix<Bounds> {
|
||||
seq_zq!({
|
||||
match &mut self.0 {
|
||||
#(
|
||||
SdlpProverKnowledgeInternal::LP~N(pk) => &mut pk.vk.bounds,
|
||||
)*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a shared `LogProof`.
|
||||
///
|
||||
/// Delegation to [`LogProof::create_with_shared`].
|
||||
@@ -421,7 +429,7 @@ impl SealSdlpProverKnowledge {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpProverKnowledgeInternal::LP~N(pk) => {
|
||||
SdlpProverKnowledgeInternal::LP~N(pk) => {
|
||||
LogProof::create_with_shared(transcript, pk, g, h, u, half_rho, shared_indices)
|
||||
}
|
||||
)*
|
||||
@@ -442,14 +450,14 @@ impl SealSdlpProverKnowledge {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpProverKnowledgeInternal::LP~N(pk) => LogProof::create(transcript, pk, g, h, u),
|
||||
SdlpProverKnowledgeInternal::LP~N(pk) => LogProof::create(transcript, pk, g, h, u),
|
||||
)*
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SealSdlpVerifierKnowledge {
|
||||
impl SdlpVerifierKnowledge {
|
||||
/// Get the length in bits of the binary expansion of the serialized secret * vectors.
|
||||
///
|
||||
/// Delegate to [`LogProofVerifierKnowledge::l`].
|
||||
@@ -457,20 +465,31 @@ impl SealSdlpVerifierKnowledge {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpVerifierKnowledgeInternal::LP~N(vk) => vk.l(),
|
||||
SdlpVerifierKnowledgeInternal::LP~N(vk) => vk.l(),
|
||||
)*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the ranges in the serialized coefficients of `S` corresponding to the bounds
|
||||
/// Get the ranges in the serialized coefficients of `S` corresponding to the bounds.
|
||||
///
|
||||
/// Delegate to [`LogProofVerifierKnowledge::b_slices`].
|
||||
pub fn b_slices(&self) -> Vec<Vec<Range<usize>>> {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpVerifierKnowledgeInternal::LP~N(vk) => vk.b_slices(),
|
||||
SdlpVerifierKnowledgeInternal::LP~N(vk) => vk.b_slices(),
|
||||
)*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the bounds on the secret `S`.
|
||||
pub fn bounds_mut(&mut self) -> &mut Matrix<Bounds> {
|
||||
seq_zq!({
|
||||
match &mut self.0 {
|
||||
#(
|
||||
SdlpVerifierKnowledgeInternal::LP~N(vk) => &mut vk.bounds,
|
||||
)*
|
||||
}
|
||||
})
|
||||
@@ -490,7 +509,7 @@ impl SealSdlpVerifierKnowledge {
|
||||
seq_zq!({
|
||||
match &self.0 {
|
||||
#(
|
||||
SealSdlpVerifierKnowledgeInternal::LP~N(vk) => logproof.verify(transcript, vk, g, h, u),
|
||||
SdlpVerifierKnowledgeInternal::LP~N(vk) => logproof.verify(transcript, vk, g, h, u),
|
||||
)*
|
||||
}
|
||||
})
|
||||
|
||||
@@ -933,6 +933,21 @@ impl<B> FheZkpRuntime<B> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "linkedproofs")]
|
||||
impl FheZkpRuntime<sunscreen_zkp_backend::bulletproofs::BulletproofsBackend> {
|
||||
/// Create a new [`LinkedProofBuilder`](crate::LinkedProofBuilder).
|
||||
pub fn linkedproof_builder<'k, 'z>(&self) -> crate::LinkedProofBuilder<'_, 'k, 'z> {
|
||||
crate::LinkedProofBuilder::new(self)
|
||||
}
|
||||
|
||||
/// Create a new [`LinkedProofVerificationBuilder`](crate::LinkedProofVerificationBuilder).
|
||||
pub fn linkedproof_verification_builder<'k, 'z>(
|
||||
&self,
|
||||
) -> crate::LinkedProofVerificationBuilder<'_, 'k, 'z> {
|
||||
crate::LinkedProofVerificationBuilder::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A runtime capable of only FHE operations.
|
||||
*/
|
||||
@@ -947,6 +962,19 @@ impl FheRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "linkedproofs")]
|
||||
impl FheRuntime {
|
||||
/// Create a new [`SdlpBuilder`](crate::SdlpBuilder).
|
||||
pub fn sdlp_builder<'k>(&self) -> crate::SdlpBuilder<'_, 'k> {
|
||||
crate::SdlpBuilder::new(self)
|
||||
}
|
||||
|
||||
/// Create a new [`SdlpVerificationBuilder`](crate::SdlpVerificationBuilder).
|
||||
pub fn sdlp_verification_builder<'k>(&self) -> crate::SdlpVerificationBuilder<'_, 'k> {
|
||||
crate::SdlpVerificationBuilder::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A runtime capable of only ZKP operations.
|
||||
*/
|
||||
|
||||
@@ -440,7 +440,7 @@ fn constraint_count(graph: &ExecutableZkpProgram) -> Result<usize> {
|
||||
}
|
||||
|
||||
/// Parameters for verifying Bulletproof circuit.
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct BulletproofVerifierParameters {
|
||||
pedersen_generators: PedersenGens,
|
||||
bulletproof_generators: BulletproofGens,
|
||||
|
||||
Reference in New Issue
Block a user