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

Implement simple tDec decryption share verification #46

Merged
merged 5 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions ferveo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod test_dkg_full {
use crate::dkg::pv::test_common::*;
use ark_bls12_381::{Bls12_381 as EllipticCurve, Bls12_381, G2Projective};
use ark_ec::bls12::G2Affine;
use ark_ec::group::Group;
use ark_ff::{Fp12, UniformRand};
use ferveo_common::{ExternalValidator, Keypair};
use group_threshold_cryptography as tpke;
Expand All @@ -57,16 +58,22 @@ mod test_dkg_full {
let msg: &[u8] = "abc".as_bytes();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = dkg.final_key();
let g_inv = dkg.pvss_params.g_inv();

let ciphertext = tpke::encrypt::<_, E>(msg, aad, &public_key, rng);

let aggregate = aggregate_for_decryption(&dkg);
let share_aggregate = aggregate_for_decryption(&dkg);
// Aggregate contains only one set of shares
assert_eq!(aggregate, dkg.vss.get(&0).unwrap().shares);
assert_eq!(share_aggregate, dkg.vss.get(&0).unwrap().shares);

let validator_keypairs = gen_n_keypairs(1);
let decryption_shares =
make_decryption_shares(&ciphertext, validator_keypairs, aggregate);
let decryption_shares = make_decryption_shares(
&ciphertext,
&validator_keypairs,
&share_aggregate,
aad,
&g_inv,
);

let shares_x = &dkg
.domain
Expand Down Expand Up @@ -99,11 +106,9 @@ mod test_dkg_full {
let aad: &[u8] = "my-aad".as_bytes();
let public_key = dkg.final_key();
let ciphertext = tpke::encrypt::<_, E>(msg, aad, &public_key, rng);
let g_inv = dkg.pvss_params.g_inv();

let aggregate = aggregate_for_decryption(&dkg);

// TODO: Before creating decryption shares, check ciphertext validity
// See: https://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security
let share_aggregate = aggregate_for_decryption(&dkg);

let validator_keypairs = gen_n_keypairs(4);
// Make sure validators are in the same order dkg is by comparing their public keys
Expand All @@ -113,13 +118,18 @@ mod test_dkg_full {
.for_each(|(v, k)| {
assert_eq!(v.validator.public_key, k.public());
});
let decryption_shares =
make_decryption_shares(&ciphertext, validator_keypairs, aggregate);
let decryption_shares = make_decryption_shares(
&ciphertext,
&validator_keypairs,
&share_aggregate,
aad,
&g_inv,
);

let shares_x = &dkg
.domain
.elements()
.take(decryption_shares.len())
.take(decryption_shares.len()) // TODO: Assert length instead?
.collect::<Vec<_>>();
let lagrange_coeffs = tpke::prepare_combine_simple::<E>(shares_x);

Expand All @@ -128,6 +138,8 @@ mod test_dkg_full {
&lagrange_coeffs,
);

// Combination works, let's decrypt

let plaintext = tpke::checked_decrypt_with_shared_secret(
&ciphertext,
aad,
Expand All @@ -136,5 +148,17 @@ mod test_dkg_full {
)
.unwrap();
assert_eq!(plaintext, msg);

// Testing green-path decryption share verification
izip!(decryption_shares, share_aggregate, validator_keypairs).for_each(
|(decryption_share, y_i, validator_keypair)| {
assert!(decryption_share.verify(
&y_i,
&validator_keypair.public().encryption_key,
&dkg.pvss_params.h,
&ciphertext
));
},
);
}
}
39 changes: 25 additions & 14 deletions ferveo/src/vss/pvss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ use ark_ec::PairingEngine;
use ark_ff::UniformRand;
use ark_serialize::*;
use ferveo_common::{Keypair, PublicKey};
use group_threshold_cryptography::{Ciphertext, DecryptionShareSimple};
use group_threshold_cryptography::{
Ciphertext, DecryptionShareSimple, PrivateKeyShare,
};
use itertools::{zip_eq, Itertools};
use subproductdomain::fast_multiexp;

/// These are the blinded evaluations of weight shares of a single random polynomial
/// These are the blinded evaluations of shares of a single random polynomial
pub type ShareEncryptions<E> = <E as PairingEngine>::G2Affine;

/// Marker struct for unaggregated PVSS transcripts
Expand Down Expand Up @@ -261,26 +263,35 @@ pub fn aggregate_for_decryption<E: PairingEngine>(

pub fn make_decryption_shares<E: PairingEngine>(
ciphertext: &Ciphertext<E>,
validator_keypairs: Vec<Keypair<E>>,
aggregate: Vec<E::G2Affine>,
validator_keypairs: &[Keypair<E>],
aggregate: &[E::G2Affine],
aad: &[u8],
g_inv: &E::G1Prepared,
) -> Vec<DecryptionShareSimple<E>> {
// TODO: Calculate separately for each validator
aggregate
.iter()
.zip_eq(validator_keypairs.iter())
.map(|(encrypted_share, keypair)| {
.enumerate()
.map(|(decrypter_index, (encrypted_share, keypair))| {
// Decrypt private key shares https://nikkolasg.github.io/ferveo/pvss.html#validator-decryption-of-private-key-shares
let z_i = encrypted_share
.mul(keypair.decryption_key.inverse().unwrap().into_repr());
let u = ciphertext.commitment;
E::pairing(u, z_i)
})
.enumerate()
.map(
|(decrypter_index, decryption_share)| DecryptionShareSimple {
// TODO: Consider using "container" structs from `tpke` for other primitives
let private_key_share = PrivateKeyShare {
private_key_share: z_i.into_affine(),
};

DecryptionShareSimple::create(
decrypter_index,
decryption_share,
},
)
&keypair.decryption_key,
&private_key_share,
ciphertext,
aad,
g_inv,
)
.unwrap() // Unwrapping here only because this is a test method!
})
.collect::<Vec<_>>()
}

Expand Down
88 changes: 72 additions & 16 deletions tpke/benches/tpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,7 @@ impl SetupSimple {
// Creating decryption shares
let decryption_shares: Vec<_> = contexts
.iter()
.map(|context| {
context
.create_share(
&ciphertext,
aad,
&contexts[0].setup_params.g_inv,
)
.unwrap()
})
.map(|context| context.create_share(&ciphertext, aad).unwrap())
.collect();

let pub_contexts = contexts[0].clone().public_decryption_contexts;
Expand Down Expand Up @@ -192,7 +184,6 @@ pub fn bench_create_decryption_share(c: &mut Criterion) {
ctx.create_share(
&setup.shared.ciphertext,
&setup.shared.aad,
&setup.contexts[0].setup_params.g_inv,
)
})
.collect::<Vec<_>>()
Expand Down Expand Up @@ -376,15 +367,15 @@ pub fn bench_share_encrypt_decrypt(c: &mut Criterion) {
}
}

pub fn bench_validity_checks(c: &mut Criterion) {
let mut group = c.benchmark_group("VALIDITY CHECKS");
pub fn bench_ciphertext_validity_checks(c: &mut Criterion) {
let mut group = c.benchmark_group("CIPHERTEXT VERIFICATION");
group.sample_size(10);

let rng = &mut StdRng::seed_from_u64(0);
let shares_num = NUM_SHARES_CASES[0];

for msg_size in MSG_SIZE_CASES {
let ciphertext_validity = {
let ciphertext_verification = {
let mut rng = rng.clone();
let setup = SetupFast::new(shares_num, msg_size, &mut rng);
move || {
Expand All @@ -397,8 +388,72 @@ pub fn bench_validity_checks(c: &mut Criterion) {
}
};
group.bench_function(
BenchmarkId::new("check_ciphertext_validity", msg_size),
|b| b.iter(|| ciphertext_validity()),
BenchmarkId::new("ciphertext_verification", msg_size),
|b| b.iter(|| ciphertext_verification()),
);
}
}

pub fn bench_decryption_share_validity_checks(c: &mut Criterion) {
let mut group = c.benchmark_group("DECRYPTION SHARE VERIFICATION");
group.sample_size(10);

let rng = &mut StdRng::seed_from_u64(0);
let msg_size = MSG_SIZE_CASES[0];

for shares_num in NUM_SHARES_CASES {
let share_fast_verification = {
let mut rng = rng.clone();
let setup = SetupFast::new(shares_num, msg_size, &mut rng);
move || {
black_box(verify_decryption_shares_fast(
&setup.pub_contexts,
&setup.shared.ciphertext,
&setup.decryption_shares,
))
}
};
group.bench_function(
BenchmarkId::new("share_fast_verification", shares_num),
|b| b.iter(|| share_fast_verification()),
);

let mut share_fast_batch_verification = {
let mut rng = rng.clone();
let setup = SetupFast::new(shares_num, msg_size, &mut rng);
// We need to repackage a bunch of variables here to avoid borrowing issues:
let ciphertext = setup.shared.ciphertext.clone();
let ciphertexts = vec![ciphertext];
let decryption_shares = setup.decryption_shares.clone();
let decryption_shares = vec![decryption_shares];
move || {
black_box(batch_verify_decryption_shares(
&setup.pub_contexts,
&ciphertexts,
&decryption_shares,
&mut rng,
))
}
};
group.bench_function(
BenchmarkId::new("share_fast_batch_verification", shares_num),
|b| b.iter(|| share_fast_batch_verification()),
);

let share_simple_verification = {
let mut rng = rng.clone();
let setup = SetupSimple::new(shares_num, msg_size, &mut rng);
move || {
black_box(verify_decryption_shares_simple(
&setup.pub_contexts,
&setup.shared.ciphertext,
&setup.decryption_shares,
))
}
};
group.bench_function(
BenchmarkId::new("share_simple_verification", shares_num),
|b| b.iter(|| share_simple_verification()),
);
}
}
Expand Down Expand Up @@ -470,7 +525,8 @@ criterion_group!(
bench_share_prepare,
bench_share_combine,
bench_share_encrypt_decrypt,
bench_validity_checks,
bench_ciphertext_validity_checks,
bench_decryption_share_validity_checks,
bench_recover_share_at_point,
bench_refresh_shares,
);
Expand Down
3 changes: 2 additions & 1 deletion tpke/src/ciphertext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ pub fn encrypt<R: RngCore, E: PairingEngine>(
}

/// Implements the check section 4.4.2 of the Ferveo paper, 'TPKE.CheckCiphertextValidity(U,W,aad)'
/// See https://eprint.iacr.org/2022/898.pdf
/// See: https://eprint.iacr.org/2022/898.pdf
/// See: https://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security
pub fn check_ciphertext_validity<E: PairingEngine>(
c: &Ciphertext<E>,
aad: &[u8],
Expand Down
1 change: 0 additions & 1 deletion tpke/src/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ pub fn checked_share_combine_fast<E: PairingEngine>(
Ok(share_combine_fast(decryption_shares, prepared_key_shares))
}

// TODO: Hide this from external users. Currently blocked by usage in benchmarks.
pub fn share_combine_simple<E: PairingEngine>(
decryption_shares: &[DecryptionShareSimple<E>],
lagrange_coeffs: &[E::Fr],
Expand Down
24 changes: 13 additions & 11 deletions tpke/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct PublicDecryptionContextSimple<E: PairingEngine> {
pub domain: E::Fr,
pub public_key_share: PublicKeyShare<E>,
pub blinded_key_share: BlindedKeyShare<E>,
pub h: E::G2Affine,
pub validator_public_key: E::G2Projective,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -63,6 +65,8 @@ pub struct PrivateDecryptionContextSimple<E: PairingEngine> {
pub setup_params: SetupParams<E>,
pub private_key_share: PrivateKeyShare<E>,
pub public_decryption_contexts: Vec<PublicDecryptionContextSimple<E>>,
// TODO: Remove/replace with `setup_params.b` after refactoring
pub validator_private_key: E::Fr,
}

impl<E: PairingEngine> PrivateDecryptionContextSimple<E> {
Expand All @@ -71,18 +75,15 @@ impl<E: PairingEngine> PrivateDecryptionContextSimple<E> {
&self,
ciphertext: &Ciphertext<E>,
aad: &[u8],
g_inv: &E::G1Prepared,
) -> Result<DecryptionShareSimple<E>> {
check_ciphertext_validity::<E>(ciphertext, aad, g_inv)?;

let u = ciphertext.commitment;
let z_i = self.private_key_share.private_key_share;
// C_i = e(U, Z_i)
let c_i = E::pairing(u, z_i);
Ok(DecryptionShareSimple {
decrypter_index: self.index,
decryption_share: c_i,
})
DecryptionShareSimple::create(
self.index,
&self.validator_private_key,
&self.private_key_share,
ciphertext,
aad,
&self.setup_params.g_inv,
)
}

pub fn create_share_precomputed(
Expand All @@ -96,6 +97,7 @@ impl<E: PairingEngine> PrivateDecryptionContextSimple<E> {
let z_i = self.private_key_share.private_key_share;
// C_{λ_i} = e(U_{λ_i}, Z_i)
let c_i = E::pairing(u_to_lagrange_coeff, z_i);

DecryptionShareSimplePrecomputed {
decrypter_index: self.index,
decryption_share: c_i,
Expand Down
Loading