Skip to content

Commit

Permalink
Merge pull request anoma#46 from nucypher/verify-simple-tdec-shares
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec authored Feb 6, 2023
2 parents 9050db0 + 48a2513 commit 530de97
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 72 deletions.
46 changes: 35 additions & 11 deletions ferveo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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 @@ -54,16 +55,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 @@ -96,11 +103,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 @@ -110,13 +115,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 @@ -125,6 +135,8 @@ mod test_dkg_full {
&lagrange_coeffs,
);

// Combination works, let's decrypt

let plaintext = tpke::checked_decrypt_with_shared_secret(
&ciphertext,
aad,
Expand All @@ -133,5 +145,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

0 comments on commit 530de97

Please sign in to comment.