Misc updates from linked-docs (#364)

This commit is contained in:
Sam Tay
2024-02-29 20:47:49 -05:00
committed by GitHub
parent 5e305c4dfa
commit d2dfd45347
19 changed files with 1045 additions and 319 deletions

19
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"] }

View File

@@ -6,4 +6,3 @@ edition = "2021"
[dependencies]
sunscreen = { path = "../../sunscreen", features = ["linkedproofs"] }
env_logger = { workspace = true }
once_cell = { workspace = true }

View File

@@ -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!();

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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"]

View File

@@ -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,
};

View File

@@ -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,
}
}

View File

@@ -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(_))

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 &params.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 &params.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 &params.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
}
}
}

View File

@@ -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),
)*
}
})

View File

@@ -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.
*/

View File

@@ -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,