Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose symmetric encryptions #343

Merged
merged 15 commits into from
Feb 11, 2024
2 changes: 1 addition & 1 deletion benchmarks/cannonical_norm_noise_model/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn make_seal_things(
SecretKey,
Option<RelinearizationKeys>,
Option<GaloisKeys>,
Encryptor,
AsymmetricEncryptor,
Decryptor,
BFVEvaluator,
) {
Expand Down
188 changes: 114 additions & 74 deletions logproof/src/bfv_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::{borrow::Cow, fmt::Debug, ops::Neg};

use crypto_bigint::{NonZero, Uint};
use seal_fhe::{
Ciphertext, Context, EncryptionParameters, Plaintext, PolynomialArray, PublicKey, SecretKey,
AsymmetricComponents, Ciphertext, Context, EncryptionParameters, Plaintext, PolynomialArray,
PublicKey, SecretKey, SymmetricComponents,
};
use sunscreen_math::{
poly::Polynomial,
Expand Down Expand Up @@ -88,30 +89,11 @@ pub enum BfvWitness<'s> {
PrivateKeyEncryption {
/// The private key used for the encryption.
private_key: Cow<'s, SecretKey>,
/// Gaussian error polynomial
///
/// N.B. this polynomial array should always have size one, see note below.
e: PolynomialArray,
/// Rounding component after scaling the message by delta.
r: Plaintext,
/// The symmetric encryption components.
components: SymmetricComponents,
},
/// A witness for the [`BfvProofStatement::PublicKeyEncryption`] variant.
PublicKeyEncryption {
/// Uniform ternary polynomial.
///
/// N.B. this polynomial array should always have size one, i.e. it is a single
/// polynomial. I believe the type is simply the only reasonable one exported by
/// `seal_fhe`.
u: PolynomialArray,
/// Gaussian error polynomial.
///
/// Note that we currently assume that ciphertexts have length two, i.e.
/// relinearization happens after every multiplication, and hence this error
/// polynomial array should also have size two.
e: PolynomialArray,
/// Rounding component after scaling the message by delta.
r: Plaintext,
},
PublicKeyEncryption(AsymmetricComponents),
}

/// A BFV message, which is a SEAL plaintext and an optional coefficient bound.
Expand Down Expand Up @@ -218,7 +200,7 @@ where
.collect::<Vec<_>>();
let vk = generate_verifier_knowledge(statements, &bounds, params, ctx);

let s = compute_s(statements, messages, witness);
let s = compute_s(statements, messages, witness, ctx);

LogProofProverKnowledge { vk, s }
}
Expand Down Expand Up @@ -281,7 +263,7 @@ where
match s {
// sk, e blocks
BfvProofStatement::PrivateKeyEncryption { ciphertext, .. } => {
let c1 = (ctx, ciphertext).as_poly_vec().pop().unwrap();
let c1 = WithCtx(ctx, ciphertext).as_poly_vec().pop().unwrap();
a.set(row, offsets.private_a, c1);
a.set(row, offsets.private_e, Polynomial::one());
offsets.inc_private();
Expand All @@ -290,7 +272,7 @@ where
}
// pk, e0, e1 blocks
BfvProofStatement::PublicKeyEncryption { public_key, .. } => {
let mut pk = (ctx, public_key.as_ref()).as_poly_vec();
let mut pk = WithCtx(ctx, public_key.as_ref()).as_poly_vec();
let p1 = pk.pop().unwrap();
let p0 = pk.pop().unwrap();
a.set(row, offsets.public_key, p0);
Expand All @@ -311,6 +293,7 @@ fn compute_s<B, const N: usize>(
statements: &[BfvProofStatement<'_>],
messages: &[BfvMessage],
witness: &[BfvWitness<'_>],
ctx: &Context,
) -> PolynomialMatrix<Z<N, B>>
where
B: BarrettConfig<N>,
Expand All @@ -327,17 +310,20 @@ where
for w in witness {
match w {
// sk, e
BfvWitness::PrivateKeyEncryption { private_key, e, r } => {
BfvWitness::PrivateKeyEncryption {
private_key,
components: SymmetricComponents { e, r },
} => {
let r = r.as_poly();
let sk = private_key.as_ref().as_poly();
let sk = WithCtx(ctx, private_key.as_ref()).as_poly();
let e = e.as_poly_vec().pop().unwrap();
s.set(offsets.remainder, 0, r);
s.set(offsets.private_a, 0, sk.neg());
s.set(offsets.private_e, 0, e.neg());
offsets.inc_private();
}
// r_i, u_i, e_i
BfvWitness::PublicKeyEncryption { u, e, r } => {
BfvWitness::PublicKeyEncryption(AsymmetricComponents { u, e, r }) => {
let r = r.as_poly();
let u = u.as_poly_vec().pop().unwrap();
let mut e = e.as_poly_vec();
Expand Down Expand Up @@ -366,7 +352,7 @@ where
let rows = statements
.iter()
.flat_map(|s| {
let mut c = (ctx, s.ciphertext()).as_poly_vec();
let mut c = WithCtx(ctx, s.ciphertext()).as_poly_vec();
// only include first ciphertext element for private statements
if s.is_private() {
c.pop().unwrap();
Expand Down Expand Up @@ -522,6 +508,8 @@ trait AsPolynomials<R: Ring> {
fn as_poly_vec(&self) -> Vec<Polynomial<R>>;
}

struct WithCtx<'a, T>(&'a Context, &'a T);

impl<const N: usize, B: BarrettConfig<N>> AsPolynomial<Z<N, B>> for Plaintext {
fn as_poly(&self) -> Polynomial<Z<N, B>> {
Polynomial {
Expand All @@ -535,9 +523,10 @@ impl<const N: usize, B: BarrettConfig<N>> AsPolynomial<Z<N, B>> for Plaintext {
}
}

impl<const N: usize, B: BarrettConfig<N>> AsPolynomial<Z<N, B>> for SecretKey {
impl<const N: usize, B: BarrettConfig<N>> AsPolynomial<Z<N, B>> for WithCtx<'_, SecretKey> {
fn as_poly(&self) -> Polynomial<Z<N, B>> {
todo!()
let poly_array = PolynomialArray::new_from_secret_key(self.0, self.1).unwrap();
poly_array.as_poly_vec().pop().unwrap()
samtay marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -570,14 +559,14 @@ impl<const N: usize, B: BarrettConfig<N>> AsPolynomials<Z<N, B>> for PolynomialA
}
}

impl<const N: usize, B: BarrettConfig<N>> AsPolynomials<Z<N, B>> for (&Context, &Ciphertext) {
impl<const N: usize, B: BarrettConfig<N>> AsPolynomials<Z<N, B>> for WithCtx<'_, Ciphertext> {
fn as_poly_vec(&self) -> Vec<Polynomial<Z<N, B>>> {
let poly_array = PolynomialArray::new_from_ciphertext(self.0, self.1).unwrap();
poly_array.as_poly_vec()
}
}

impl<const N: usize, B: BarrettConfig<N>> AsPolynomials<Z<N, B>> for (&Context, &PublicKey) {
impl<const N: usize, B: BarrettConfig<N>> AsPolynomials<Z<N, B>> for WithCtx<'_, PublicKey> {
fn as_poly_vec(&self) -> Vec<Polynomial<Z<N, B>>> {
let poly_array = PolynomialArray::new_from_public_key(self.0, self.1).unwrap();
poly_array.as_poly_vec()
Expand Down Expand Up @@ -663,7 +652,7 @@ mod tests {
use rand::Rng;
use seal_fhe::{
BfvEncryptionParametersBuilder, CoefficientModulus, Context, Encryptor, KeyGenerator,
PlainModulus, SecurityLevel,
PlainModulus, SecurityLevel, SymAsym,
};

use crate::{
Expand All @@ -675,17 +664,16 @@ mod tests {

#[test]
fn idx_offsets() {
// TODO create the example in the docs after symmetric unlocked. for now, just public.
let ctx = BFVTestContext::new();
let test_fixture = ctx.random_fixture_with(3, 1);
let test_fixture = ctx.random_fixture_with(2, 0, 0, 1);
let idx_offsets = IdxOffsets::new(&test_fixture.statements);

assert_eq!(idx_offsets.remainder, 2);
assert_eq!(idx_offsets.public_key, 2 + 3);
assert_eq!(idx_offsets.public_e_0, 2 + 3 + 3);
assert_eq!(idx_offsets.public_e_1, 2 + 3 + 3 + 3);
assert_eq!(idx_offsets.private_a, 2 + 3 + 3 + 3 + 3);
assert_eq!(idx_offsets.private_e, 2 + 3 + 3 + 3 + 3);
assert_eq!(idx_offsets.public_e_0, 2 + 3 + 2);
assert_eq!(idx_offsets.public_e_1, 2 + 3 + 2 + 2);
assert_eq!(idx_offsets.private_a, 2 + 3 + 2 + 2 + 2);
assert_eq!(idx_offsets.private_e, 2 + 3 + 2 + 2 + 2 + 1);
}

#[test]
Expand All @@ -699,23 +687,38 @@ mod tests {
}

#[test]
fn one_statement() {
test_statements_with(1, 0)
fn one_public_statement() {
test_statements_with(1, 0, 0, 0)
}

#[test]
fn one_private_statement() {
test_statements_with(0, 1, 0, 0)
}

#[test]
fn two_separate_statements() {
test_statements_with(2, 0)
fn private_and_public_statement_about_separate_msgs() {
test_statements_with(1, 1, 0, 0)
}

#[test]
fn two_statements_about_same_msg() {
test_statements_with(2, 1)
fn private_and_public_statement_about_same_msg() {
test_statements_with(1, 0, 0, 1)
}

fn test_statements_with(num_statements: usize, num_duplicate_msgs: usize) {
fn test_statements_with(
num_public_statements: usize,
num_private_statements: usize,
num_duplicate_public_msgs: usize,
num_duplicate_private_msgs: usize,
) {
let ctx = BFVTestContext::new();
let test_fixture = ctx.random_fixture_with(num_statements, num_duplicate_msgs);
let test_fixture = ctx.random_fixture_with(
num_public_statements,
num_private_statements,
num_duplicate_public_msgs,
num_duplicate_private_msgs,
);
let prover_knowledge = generate_prover_knowledge(
&test_fixture.statements,
&test_fixture.messages,
Expand Down Expand Up @@ -759,7 +762,8 @@ mod tests {
ctx: Context,
params: EncryptionParameters,
public_key: PublicKey,
encryptor: Encryptor,
secret_key: SecretKey,
encryptor: Encryptor<SymAsym>,
}

impl BFVTestContext {
Expand All @@ -784,6 +788,7 @@ mod tests {
ctx,
params,
public_key,
secret_key,
encryptor,
}
}
Expand All @@ -792,23 +797,28 @@ mod tests {
#[allow(unused)]
fn random_fixture(&self) -> TestFixture {
let mut rng = rand::thread_rng();
let num_statements = rng.gen_range(1..=3);
let num_duplicate_msgs = rng.gen_range(0..num_statements);
self.random_fixture_with(num_statements, num_duplicate_msgs)
let num_public_statements = rng.gen_range(1..=3);
let num_private_statements = rng.gen_range(1..=3);
let num_duplicate_public_msgs = rng.gen_range(0..num_public_statements);
let num_duplicate_private_msgs = rng.gen_range(0..num_private_statements);
self.random_fixture_with(
num_public_statements,
num_private_statements,
num_duplicate_public_msgs,
num_duplicate_private_msgs,
)
samtay marked this conversation as resolved.
Show resolved Hide resolved
}

/// Note that `num_duplicate_msgs` is in _addition_ to the first message about a given
/// index. So [0, 0, 0] contains `num_duplicate_msgs == 2`.
//
// TODO add private statements once we have a way to generate them, accepting
// num_public_statements, num_private_statements.
fn random_fixture_with(
&self,
num_statements: usize,
num_duplicate_msgs: usize,
num_unique_public_statements: usize,
num_unique_private_statements: usize,
num_duplicate_public_msgs: usize,
num_duplicate_private_msgs: usize,
) -> TestFixture {
assert!(num_duplicate_msgs < num_statements);
let num_msgs = num_statements - num_duplicate_msgs;
let num_msgs = num_unique_public_statements + num_unique_private_statements;
let num_duplicate_msgs = num_duplicate_public_msgs + num_duplicate_private_msgs;
let num_statements = num_msgs + num_duplicate_msgs;

// all the messages
let messages = (0..num_msgs)
Expand All @@ -823,32 +833,62 @@ mod tests {

// statements without duplicate messages
for (i, m) in messages.iter().enumerate() {
let (ct, u, e, r) = self
if i < num_unique_public_statements {
let (ct, components) = self
.encryptor
.encrypt_return_components(&m.plaintext)
samtay marked this conversation as resolved.
Show resolved Hide resolved
.unwrap();
statements.push(BfvProofStatement::PublicKeyEncryption {
message_id: i,
ciphertext: ct,
public_key: Cow::Borrowed(&self.public_key),
});
witness.push(BfvWitness::PublicKeyEncryption(components));
} else {
let (ct, components) = self
.encryptor
.encrypt_symmetric_return_components(&m.plaintext)
.unwrap();
statements.push(BfvProofStatement::PrivateKeyEncryption {
message_id: i,
ciphertext: ct,
});
witness.push(BfvWitness::PrivateKeyEncryption {
private_key: Cow::Borrowed(&self.secret_key),
components,
});
}
}

// add in the public statements about existing messages
let mut rng = rand::thread_rng();
for _ in 0..num_duplicate_public_msgs {
let i = rng.gen_range(0..num_msgs);
let (ct, components) = self
.encryptor
.encrypt_return_components(&m.plaintext)
.encrypt_return_components(&messages[i].plaintext)
.unwrap();
statements.push(BfvProofStatement::PublicKeyEncryption {
message_id: i,
ciphertext: ct,
public_key: Cow::Borrowed(&self.public_key),
});
witness.push(BfvWitness::PublicKeyEncryption { u, e, r });
witness.push(BfvWitness::PublicKeyEncryption(components));
}

// add in the statements about existing messages
let mut rng = rand::thread_rng();
for _ in 0..num_duplicate_msgs {
for _ in 0..num_duplicate_private_msgs {
let i = rng.gen_range(0..num_msgs);
let (ct, u, e, r) = self
let (ct, components) = self
.encryptor
.encrypt_return_components(&messages[i].plaintext)
.encrypt_symmetric_return_components(&messages[i].plaintext)
.unwrap();
statements.push(BfvProofStatement::PublicKeyEncryption {
statements.push(BfvProofStatement::PrivateKeyEncryption {
message_id: i,
ciphertext: ct,
public_key: Cow::Borrowed(&self.public_key),
});
witness.push(BfvWitness::PublicKeyEncryption { u, e, r });
witness.push(BfvWitness::PrivateKeyEncryption {
private_key: Cow::Borrowed(&self.secret_key),
components,
});
}

TestFixture {
Expand Down
4 changes: 2 additions & 2 deletions logproof/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ where
// Generate plaintext data
let plaintext = encoder.encode_unsigned(message).unwrap();

let (ciphertext, u, e, r) = encryptor.encrypt_return_components(&plaintext).unwrap();
let (ciphertext, components) = encryptor.encrypt_return_components(&plaintext).unwrap();

let decrypted = decryptor.decrypt(&ciphertext).unwrap();
let data = encoder.decode_unsigned(&decrypted).unwrap();
Expand All @@ -86,6 +86,6 @@ where
ciphertext,
public_key: Cow::Borrowed(&public_key),
};
let witness = BfvWitness::PublicKeyEncryption { u, e, r };
let witness = BfvWitness::PublicKeyEncryption(components);
generate_prover_knowledge(&[statement], &[message], &[witness], &params, &ctx)
}
Loading
Loading