Skip to content

Commit

Permalink
check ciphertext validity before creating a decryption share
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jan 17, 2023
1 parent 25c745e commit dab1a40
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 36 deletions.
1 change: 1 addition & 0 deletions tpke-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub fn decrypt(ciphertext: &Ciphertext, private_key: &PrivateKey) -> Vec<u8> {
&ciphertext.aad,
private_key.0,
)
.unwrap()
}

#[wasm_bindgen]
Expand Down
19 changes: 15 additions & 4 deletions tpke/benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ impl SetupFast {

let mut decryption_shares: Vec<DecryptionShareFast<E>> = vec![];
for context in contexts.iter() {
decryption_shares.push(context.create_share(&ciphertext));
decryption_shares
.push(context.create_share(&ciphertext, aad).unwrap());
}

let pub_contexts = contexts[0].clone().public_decryption_contexts;
Expand Down Expand Up @@ -101,7 +102,7 @@ impl SetupSimple {
// Creating decryption shares
let decryption_shares: Vec<_> = contexts
.iter()
.map(|context| context.create_share(&ciphertext))
.map(|context| context.create_share(&ciphertext, aad).unwrap())
.collect();

let pub_contexts = contexts[0].clone().public_decryption_contexts;
Expand Down Expand Up @@ -147,7 +148,12 @@ pub fn bench_create_decryption_share(c: &mut Criterion) {
setup
.contexts
.iter()
.map(|ctx| ctx.create_share(&setup.shared.ciphertext))
.map(|ctx| {
ctx.create_share(
&setup.shared.ciphertext,
&setup.shared.aad,
)
})
.collect::<Vec<_>>()
})
}
Expand All @@ -161,7 +167,12 @@ pub fn bench_create_decryption_share(c: &mut Criterion) {
setup
.contexts
.iter()
.map(|ctx| ctx.create_share(&setup.shared.ciphertext))
.map(|ctx| {
ctx.create_share(
&setup.shared.ciphertext,
&setup.shared.aad,
)
})
.collect::<Vec<_>>()
})
}
Expand Down
2 changes: 1 addition & 1 deletion tpke/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl ParticipantPayload {
}

pub fn to_decryption_share(&self) -> DecryptionShare {
// TODO: Add verification steps
// TODO: Update how decryption share is constructed in this API
let decryption_share = self
.ciphertext
.commitment
Expand Down
35 changes: 21 additions & 14 deletions tpke/src/ciphertext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ use crate::{construct_tag_hash, hash_to_g2};

#[derive(Clone, Debug)]
pub struct Ciphertext<E: PairingEngine> {
pub commitment: E::G1Affine, // U
pub auth_tag: E::G2Affine, // W
pub ciphertext: Vec<u8>, // V
pub commitment: E::G1Affine,
// U
pub auth_tag: E::G2Affine,
// W
pub ciphertext: Vec<u8>, // V
}

impl<E: PairingEngine> Ciphertext<E> {
Expand Down Expand Up @@ -94,28 +96,37 @@ pub fn encrypt<R: RngCore, E: PairingEngine>(
.mul(rand_element)
.into();

// TODO: Consider adding aad to the Ciphertext struct
Ciphertext::<E> {
commitment,
ciphertext,
auth_tag,
}
}

/// Implements the check section 4.4.2 of the Ferveo paper, 'TPKE.CheckCiphertextValidity(U,W,aad)'
/// See https://eprint.iacr.org/2022/898.pdf
pub fn check_ciphertext_validity<E: PairingEngine>(
c: &Ciphertext<E>,
aad: &[u8],
) -> bool {
) -> Result<()> {
let g_inv = E::G1Prepared::from(-E::G1Affine::prime_subgroup_generator());
let hash_g2 = E::G2Prepared::from(construct_tag_hash::<E>(
c.commitment,
&c.ciphertext[..],
aad,
));

E::product_of_pairings(&[
let is_ciphertext_valid = E::product_of_pairings(&[
(E::G1Prepared::from(c.commitment), hash_g2),
(g_inv, E::G2Prepared::from(c.auth_tag)),
]) == E::Fqk::one()
]) == E::Fqk::one();

if is_ciphertext_valid {
Ok(())
} else {
Err(ThresholdEncryptionError::CiphertextVerificationFailed)
}
}

fn decrypt<E: PairingEngine>(
Expand All @@ -133,15 +144,13 @@ pub fn checked_decrypt<E: PairingEngine>(
ciphertext: &Ciphertext<E>,
aad: &[u8],
privkey: E::G2Affine,
) -> Vec<u8> {
if !check_ciphertext_validity(ciphertext, aad) {
panic!("Ciphertext is invalid");
}
) -> Result<Vec<u8>> {
check_ciphertext_validity(ciphertext, aad)?;
let s = E::product_of_pairings(&[(
E::G1Prepared::from(ciphertext.commitment),
E::G2Prepared::from(privkey),
)]);
decrypt_with_shared_secret(ciphertext, &s)
Ok(decrypt_with_shared_secret(ciphertext, &s))
}

fn decrypt_with_shared_secret<E: PairingEngine>(
Expand All @@ -162,9 +171,7 @@ pub fn checked_decrypt_with_shared_secret<E: PairingEngine>(
aad: &[u8],
s: &E::Fqk,
) -> Result<Vec<u8>> {
if !check_ciphertext_validity(ciphertext, aad) {
return Err(ThresholdEncryptionError::CiphertextVerificationFailed);
}
check_ciphertext_validity(ciphertext, aad)?;
Ok(decrypt_with_shared_secret(ciphertext, s))
}

Expand Down
21 changes: 15 additions & 6 deletions tpke/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,21 @@ impl<E: PairingEngine> PrivateDecryptionContextFast<E> {
pub fn create_share(
&self,
ciphertext: &Ciphertext<E>,
) -> DecryptionShareFast<E> {
aad: &[u8],
) -> Result<DecryptionShareFast<E>> {
check_ciphertext_validity::<E>(ciphertext, aad)?;

// let decryption_share =
// ciphertext.commitment.mul(self.b_inv).into_affine();
let decryption_share = ciphertext.commitment;

DecryptionShareFast {
Ok(DecryptionShareFast {
decrypter_index: self.index,
decryption_share,
}
})
}

// TODO: Figure out what this check is doing and how to use it
pub fn batch_verify_decryption_shares<R: RngCore>(
&self,
ciphertexts: &[Ciphertext<E>],
Expand Down Expand Up @@ -126,18 +131,22 @@ pub struct PrivateDecryptionContextSimple<E: PairingEngine> {
}

impl<E: PairingEngine> PrivateDecryptionContextSimple<E> {
// TODO: Rename to checked_create_share? Or get rid of this "checked_ notation"?
pub fn create_share(
&self,
ciphertext: &Ciphertext<E>,
) -> DecryptionShareSimple<E> {
aad: &[u8],
) -> Result<DecryptionShareSimple<E>> {
check_ciphertext_validity::<E>(ciphertext, aad)?;

let u = ciphertext.commitment;
let z_i = self.private_key_share.clone();
let z_i = z_i.private_key_shares[0];
// C_i = e(U, Z_i)
let c_i = E::pairing(u, z_i);
DecryptionShareSimple {
Ok(DecryptionShareSimple {
decrypter_index: self.index,
decryption_share: c_i,
}
})
}
}
55 changes: 44 additions & 11 deletions tpke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ mod tests {
let (pubkey, privkey, _) = setup_fast::<E>(threshold, shares_num, rng);

let ciphertext = encrypt::<StdRng, E>(msg, aad, &pubkey, rng);
let plaintext = checked_decrypt(&ciphertext, aad, privkey);
let plaintext = checked_decrypt(&ciphertext, aad, privkey).unwrap();

assert_eq!(msg, plaintext)
}
Expand Down Expand Up @@ -419,15 +419,46 @@ mod tests {
let mut ciphertext = encrypt::<StdRng, E>(msg, aad, &pubkey, rng);

// So far, the ciphertext is valid
assert!(check_ciphertext_validity(&ciphertext, aad));
assert!(check_ciphertext_validity(&ciphertext, aad).is_ok());

// Malformed the ciphertext
ciphertext.ciphertext[0] += 1;
assert!(!check_ciphertext_validity(&ciphertext, aad));
assert!(check_ciphertext_validity(&ciphertext, aad).is_err());

// Malformed the AAD
let aad = "bad aad".as_bytes();
assert!(!check_ciphertext_validity(&ciphertext, aad));
assert!(check_ciphertext_validity(&ciphertext, aad).is_err());
}

#[test]
fn fast_decryption_share_validation() {
let rng = &mut test_rng();
let shares_num = 16;
let threshold = shares_num * 2 / 3;
let msg: &[u8] = "abc".as_bytes();
let aad: &[u8] = "my-aad".as_bytes();

let (pubkey, _, contexts) = setup_fast::<E>(threshold, shares_num, rng);
let ciphertext = encrypt::<StdRng, E>(msg, aad, &pubkey, rng);

let bad_aad = "bad aad".as_bytes();
assert!(contexts[0].create_share(&ciphertext, bad_aad).is_err());
}

#[test]
fn simple_decryption_share_validation() {
let rng = &mut test_rng();
let shares_num = 16;
let threshold = shares_num * 2 / 3;
let msg: &[u8] = "abc".as_bytes();
let aad: &[u8] = "my-aad".as_bytes();

let (pubkey, _, contexts) =
setup_simple::<E>(threshold, shares_num, rng);
let ciphertext = encrypt::<StdRng, E>(msg, aad, &pubkey, rng);

let bad_aad = "bad aad".as_bytes();
assert!(contexts[0].create_share(&ciphertext, bad_aad).is_err());
}

#[test]
Expand All @@ -438,15 +469,16 @@ mod tests {
let msg: &[u8] = "abc".as_bytes();
let aad: &[u8] = "my-aad".as_bytes();

let (pubkey, _privkey, contexts) =
let (pubkey, _, contexts) =
setup_fast::<E>(threshold, shares_num, &mut rng);
let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng);

let mut shares: Vec<DecryptionShareFast<E>> = vec![];
for context in contexts.iter() {
shares.push(context.create_share(&ciphertext));
shares.push(context.create_share(&ciphertext, aad).unwrap());
}

// TODO: Verify and enable this check
/*for pub_context in contexts[0].public_decryption_contexts.iter() {
assert!(pub_context
.blinded_key_shares
Expand Down Expand Up @@ -479,7 +511,7 @@ mod tests {
// Create decryption shares
let decryption_shares: Vec<_> = contexts
.iter()
.map(|c| c.create_share(&ciphertext))
.map(|c| c.create_share(&ciphertext, aad).unwrap())
.collect();
let pub_contexts = &contexts[0].public_decryption_contexts;
let lagrange = prepare_combine_simple::<E>(pub_contexts);
Expand Down Expand Up @@ -532,10 +564,11 @@ mod tests {
fn make_shared_secret_from_contexts<E: PairingEngine>(
contexts: &[PrivateDecryptionContextSimple<E>],
ciphertext: &Ciphertext<E>,
aad: &[u8],
) -> E::Fqk {
let decryption_shares: Vec<_> = contexts
.iter()
.map(|c| c.create_share(ciphertext))
.map(|c| c.create_share(ciphertext, aad).unwrap())
.collect();
make_shared_secret(
&contexts[0].public_decryption_contexts,
Expand Down Expand Up @@ -567,7 +600,7 @@ mod tests {

// Create an initial shared secret
let old_shared_secret =
make_shared_secret_from_contexts(&contexts, &ciphertext);
make_shared_secret_from_contexts(&contexts, &ciphertext, aad);

// Now, we're going to recover a new share at a random point and check that the shared secret is still the same

Expand All @@ -593,7 +626,7 @@ mod tests {
// Creating decryption shares
let mut decryption_shares: Vec<_> = remaining_participants
.iter()
.map(|c| c.create_share(&ciphertext))
.map(|c| c.create_share(&ciphertext, aad).unwrap())
.collect();
decryption_shares.push(DecryptionShareSimple {
decrypter_index: removed_participant.index,
Expand Down Expand Up @@ -630,7 +663,7 @@ mod tests {

// Create an initial shared secret
let old_shared_secret =
make_shared_secret_from_contexts(&contexts, &ciphertext);
make_shared_secret_from_contexts(&contexts, &ciphertext, aad);

// Now, we're going to refresh the shares and check that the shared secret is the same

Expand Down

0 comments on commit dab1a40

Please sign in to comment.