From 41e5be5dc0edc3c81e82d34f523108a7d06bd09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Fri, 28 Jul 2023 16:49:43 +0200 Subject: [PATCH 01/10] Refactor Ciphertext implementation. Fixes #144 * Remove unused Ciphertext.construct_tag_hash() * Refactor Ciphertext.check() to take the functionality of check_ciphertext_validity() function --- tpke/benches/tpke.rs | 3 +- tpke/src/ciphertext.rs | 73 ++++++++++++++++-------------------------- tpke/src/context.rs | 12 +++---- tpke/src/decryption.rs | 8 ++--- 4 files changed, 36 insertions(+), 60 deletions(-) diff --git a/tpke/benches/tpke.rs b/tpke/benches/tpke.rs index 952c4947..89fef191 100644 --- a/tpke/benches/tpke.rs +++ b/tpke/benches/tpke.rs @@ -388,8 +388,7 @@ pub fn bench_ciphertext_validity_checks(c: &mut Criterion) { let mut rng = rng.clone(); let setup = SetupFast::new(shares_num, msg_size, &mut rng); move || { - black_box(check_ciphertext_validity( - &setup.shared.ciphertext, + black_box(setup.shared.ciphertext.check( &setup.shared.aad, &setup.contexts[0].setup_params.g_inv, )) diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index 814a72d2..fe6af03d 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -32,22 +32,32 @@ pub struct Ciphertext { } impl Ciphertext { - pub fn check(&self, g_inv: &E::G1Prepared) -> Result { - let hash_g2 = E::G2Prepared::from(self.construct_tag_hash()?); - - Ok(E::multi_pairing( + pub fn check(&self, aad: &[u8], g_inv: &E::G1Prepared) -> Result { + // Implements a variant of the check in section 4.4.2 of the Ferveo paper: + // 'TPKE.CheckCiphertextValidity(U,W,aad)' + // See: https://eprint.iacr.org/2022/898.pdf + // See: https://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security + + // H_G2(U, sym_ctxt, aad) + let hash_g2 = E::G2Prepared::from(construct_tag_hash::( + self.commitment, + &self.ciphertext[..], + aad, + )?); + + let is_ciphertext_valid = E::multi_pairing( + // e(U, H_G2(U, sym_ctxt, aad)) = e(G, W) ==> + // e(U, H_G2(U, sym_ctxt, aad)) * e(G_inv, W) = 1 [self.commitment.into(), g_inv.to_owned()], [hash_g2, self.auth_tag.into()], ) - .0 == E::TargetField::one()) - } - - fn construct_tag_hash(&self) -> Result { - let mut hash_input = Vec::::new(); - self.commitment.serialize_compressed(&mut hash_input)?; - hash_input.extend_from_slice(&self.ciphertext); + .0 == E::TargetField::one(); - hash_to_g2(&hash_input) + if is_ciphertext_valid { + Ok(true) + } else { + Err(Error::CiphertextVerificationFailed) + } } pub fn serialized_length(&self) -> usize { @@ -95,42 +105,13 @@ pub fn encrypt( }) } -/// 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://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security -pub fn check_ciphertext_validity( - c: &Ciphertext, - aad: &[u8], - g_inv: &E::G1Prepared, -) -> Result<()> { - // H_G2(U, aad) - let hash_g2 = E::G2Prepared::from(construct_tag_hash::( - c.commitment, - &c.ciphertext[..], - aad, - )?); - - let is_ciphertext_valid = E::multi_pairing( - // e(U, H_G2(U, aad)) = e(G, W) - [c.commitment.into(), g_inv.to_owned()], - [hash_g2, c.auth_tag.into()], - ) - .0 == E::TargetField::one(); - - if is_ciphertext_valid { - Ok(()) - } else { - Err(Error::CiphertextVerificationFailed) - } -} - pub fn decrypt_symmetric( ciphertext: &Ciphertext, aad: &[u8], private_key: &E::G2Affine, g_inv: &E::G1Prepared, ) -> Result> { - check_ciphertext_validity(ciphertext, aad, g_inv)?; + ciphertext.check(aad, g_inv)?; let shared_secret = E::pairing( E::G1Prepared::from(ciphertext.commitment), E::G2Prepared::from(*private_key), @@ -161,7 +142,7 @@ pub fn decrypt_with_shared_secret( shared_secret: &SharedSecret, g_inv: &E::G1Prepared, ) -> Result> { - check_ciphertext_validity(ciphertext, aad, g_inv)?; + ciphertext.check(aad, g_inv)?; decrypt_with_shared_secret_unchecked(ciphertext, shared_secret) } @@ -267,14 +248,14 @@ mod tests { encrypt::(SecretBox::new(msg), aad, &pubkey, rng).unwrap(); // So far, the ciphertext is valid - assert!(check_ciphertext_validity(&ciphertext, aad, &g_inv).is_ok()); + assert!(ciphertext.check(aad, &g_inv).is_ok()); // Malformed the ciphertext ciphertext.ciphertext[0] += 1; - assert!(check_ciphertext_validity(&ciphertext, aad, &g_inv).is_err()); + assert!(ciphertext.check(aad, &g_inv).is_err()); // Malformed the AAD let aad = "bad aad".as_bytes(); - assert!(check_ciphertext_validity(&ciphertext, aad, &g_inv).is_err()); + assert!(ciphertext.check(aad, &g_inv).is_err()); } } diff --git a/tpke/src/context.rs b/tpke/src/context.rs index 74f60c07..4a471925 100644 --- a/tpke/src/context.rs +++ b/tpke/src/context.rs @@ -3,9 +3,9 @@ use std::ops::Mul; use ark_ec::{pairing::Pairing, CurveGroup}; use crate::{ - check_ciphertext_validity, prepare_combine_simple, BlindedKeyShare, - Ciphertext, DecryptionShareFast, DecryptionSharePrecomputed, - DecryptionShareSimple, PrivateKeyShare, PublicKeyShare, Result, + prepare_combine_simple, BlindedKeyShare, Ciphertext, DecryptionShareFast, + DecryptionSharePrecomputed, DecryptionShareSimple, PrivateKeyShare, + PublicKeyShare, Result, }; #[derive(Clone, Debug)] @@ -51,11 +51,7 @@ impl PrivateDecryptionContextFast { ciphertext: &Ciphertext, aad: &[u8], ) -> Result> { - check_ciphertext_validity::( - ciphertext, - aad, - &self.setup_params.g_inv, - )?; + ciphertext.check(aad, &self.setup_params.g_inv)?; let decryption_share = ciphertext .commitment diff --git a/tpke/src/decryption.rs b/tpke/src/decryption.rs index c3b85eb5..ad319783 100644 --- a/tpke/src/decryption.rs +++ b/tpke/src/decryption.rs @@ -9,8 +9,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_with::serde_as; use crate::{ - check_ciphertext_validity, generate_random, Ciphertext, PrivateKeyShare, - PublicDecryptionContextFast, PublicDecryptionContextSimple, Result, + generate_random, Ciphertext, PrivateKeyShare, PublicDecryptionContextFast, + PublicDecryptionContextSimple, Result, }; #[serde_as] @@ -94,7 +94,7 @@ impl DecryptionShareSimple { aad: &[u8], g_inv: &E::G1Prepared, ) -> Result { - check_ciphertext_validity::(ciphertext, aad, g_inv)?; + ciphertext.check(aad, g_inv)?; Self::create_unchecked( validator_decryption_key, private_key_share, @@ -165,7 +165,7 @@ impl DecryptionSharePrecomputed { lagrange_coeff: &E::ScalarField, g_inv: &E::G1Prepared, ) -> Result { - check_ciphertext_validity::(ciphertext, aad, g_inv)?; + ciphertext.check(aad, g_inv)?; Self::create_unchecked( validator_index, validator_decryption_key, From 396b1d28297ea1a840c0b9dc4f6c6be061cd6bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Fri, 28 Jul 2023 17:15:44 +0200 Subject: [PATCH 02/10] Use symmetric ciphertext hash when creating/checking the auth_tag Closes #147 --- tpke/src/ciphertext.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index fe6af03d..490e0511 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -38,16 +38,17 @@ impl Ciphertext { // See: https://eprint.iacr.org/2022/898.pdf // See: https://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security - // H_G2(U, sym_ctxt, aad) + // H_G2(U, sym_ctxt_digest, aad) + let ciphertext_hash = sha256(&self.ciphertext[..]); let hash_g2 = E::G2Prepared::from(construct_tag_hash::( self.commitment, - &self.ciphertext[..], + &ciphertext_hash, aad, )?); let is_ciphertext_valid = E::multi_pairing( - // e(U, H_G2(U, sym_ctxt, aad)) = e(G, W) ==> - // e(U, H_G2(U, sym_ctxt, aad)) * e(G_inv, W) = 1 + // e(U, H_G2(U, sym_ctxt_digest, aad)) == e(G, W) ==> + // e(U, H_G2(U, sym_ctxt_digest, aad)) * e(G_inv, W) == 1 [self.commitment.into(), g_inv.to_owned()], [hash_g2, self.auth_tag.into()], ) @@ -92,8 +93,10 @@ pub fn encrypt( .encrypt(&nonce.0, message.as_secret().as_ref()) .map_err(Error::SymmetricEncryptionError)? .to_vec(); + let ciphertext_hash = sha256(&ciphertext); + // w - let auth_tag = construct_tag_hash::(commitment, &ciphertext, aad)? + let auth_tag = construct_tag_hash::(commitment, &ciphertext_hash, aad)? .mul(rand_element) .into(); @@ -195,12 +198,12 @@ fn hash_to_g2( fn construct_tag_hash( commitment: E::G1Affine, - stream_ciphertext: &[u8], + ciphertext_hash: &[u8], aad: &[u8], ) -> Result { let mut hash_input = Vec::::new(); commitment.serialize_compressed(&mut hash_input)?; - hash_input.extend_from_slice(stream_ciphertext); + hash_input.extend_from_slice(ciphertext_hash); hash_input.extend_from_slice(aad); hash_to_g2(&hash_input) } From e662e178d80f26b9a727b790e6d3e6f3456a6085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Mon, 31 Jul 2023 10:59:42 +0200 Subject: [PATCH 03/10] Use AEAD payloads (message + AAD) as input to chacha20poly1305. Fix #146 See https://docs.rs/aead/0.5.1/aead/struct.Payload.html --- tpke/src/ciphertext.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index 490e0511..a358ff93 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -4,7 +4,7 @@ use ark_ec::{pairing::Pairing, AffineRepr}; use ark_ff::{One, UniformRand}; use ark_serialize::{CanonicalSerialize, Compress}; use chacha20poly1305::{ - aead::{generic_array::GenericArray, Aead, KeyInit}, + aead::{generic_array::GenericArray, Aead, KeyInit, Payload}, ChaCha20Poly1305, }; use ferveo_common::serialization; @@ -89,8 +89,13 @@ pub fn encrypt( let nonce = Nonce::from_commitment::(commitment)?; let shared_secret = SharedSecret::(product); + + let payload = Payload { + msg: message.as_secret().as_ref(), + aad, + }; let ciphertext = shared_secret_to_chacha(&shared_secret)? - .encrypt(&nonce.0, message.as_secret().as_ref()) + .encrypt(&nonce.0, payload) .map_err(Error::SymmetricEncryptionError)? .to_vec(); let ciphertext_hash = sha256(&ciphertext); @@ -121,18 +126,22 @@ pub fn decrypt_symmetric( ) .0; let shared_secret = SharedSecret(shared_secret); - decrypt_with_shared_secret_unchecked(ciphertext, &shared_secret) + decrypt_with_shared_secret_unchecked(ciphertext, aad, &shared_secret) } fn decrypt_with_shared_secret_unchecked( ciphertext: &Ciphertext, + aad: &[u8], shared_secret: &SharedSecret, ) -> Result> { let nonce = Nonce::from_commitment::(ciphertext.commitment)?; - let ciphertext = ciphertext.ciphertext.to_vec(); - + let ctxt = ciphertext.ciphertext.to_vec(); + let payload = Payload { + msg: ctxt.as_ref(), + aad, + }; let plaintext = shared_secret_to_chacha(shared_secret)? - .decrypt(&nonce.0, ciphertext.as_ref()) + .decrypt(&nonce.0, payload) .map_err(|_| Error::CiphertextVerificationFailed)? .to_vec(); @@ -146,7 +155,7 @@ pub fn decrypt_with_shared_secret( g_inv: &E::G1Prepared, ) -> Result> { ciphertext.check(aad, g_inv)?; - decrypt_with_shared_secret_unchecked(ciphertext, shared_secret) + decrypt_with_shared_secret_unchecked(ciphertext, aad, shared_secret) } fn sha256(input: &[u8]) -> Vec { From e51e6ec6632720409c0fc9a525265c0ca1a0404b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Tue, 1 Aug 2023 11:47:21 +0200 Subject: [PATCH 04/10] Test for bad AAD input --- tpke/src/ciphertext.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index a358ff93..9e60199f 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -244,7 +244,11 @@ mod tests { let plaintext = decrypt_symmetric(&ciphertext, aad, &privkey, g_inv).unwrap(); - assert_eq!(msg, plaintext) + assert_eq!(msg, plaintext); + + let bad: &[u8] = "bad-aad".as_bytes(); + + assert!(decrypt_symmetric(&ciphertext, bad, &privkey, g_inv).is_err()); } #[test] From 0ec84b6b3bd6d583eef7fb928a9ffb5352d50bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Tue, 1 Aug 2023 11:50:43 +0200 Subject: [PATCH 05/10] Bump MSRV to 1.67.0 --- README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7d1a9a1..1f6e0127 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ A preprint paper describing the construction of Ferveo and the novel cryptosyste ## Build -A Rust toolchain with version `>= 1.65.0` is required. In the future, Ferveo will target the `stable` toolchain. +A Rust toolchain with version `>= 1.67.0` is required. In the future, Ferveo will target the `stable` toolchain. Installation via [rustup](https://rustup.rs/) is recommended. Run `cargo build --release` to build. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 27f165c4..8456888c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] profile = "default" -channel = "1.65.0" +channel = "1.67.0" components = ["rustfmt", "clippy"] targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] From f427f0d12043f751905827ea31c7a179bd9a6180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Tue, 1 Aug 2023 13:40:44 +0200 Subject: [PATCH 06/10] Remove unused & incorrect ciphertext length method --- tpke/src/ciphertext.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index 9e60199f..f7137dbc 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -2,7 +2,7 @@ use std::ops::Mul; use ark_ec::{pairing::Pairing, AffineRepr}; use ark_ff::{One, UniformRand}; -use ark_serialize::{CanonicalSerialize, Compress}; +use ark_serialize::CanonicalSerialize; use chacha20poly1305::{ aead::{generic_array::GenericArray, Aead, KeyInit, Payload}, ChaCha20Poly1305, @@ -60,12 +60,6 @@ impl Ciphertext { Err(Error::CiphertextVerificationFailed) } } - - pub fn serialized_length(&self) -> usize { - self.commitment.serialized_size(Compress::No) - + self.auth_tag.serialized_size(Compress::No) - + self.ciphertext.len() - } } pub fn encrypt( From 4337c3c312719987405f620f2e377cf493ece6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Tue, 1 Aug 2023 14:06:16 +0200 Subject: [PATCH 07/10] Clippy stuff --- ferveo-common/src/lib.rs | 7 +++---- ferveo/src/bindings_python.rs | 9 +++------ ferveo/src/bindings_wasm.rs | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ferveo-common/src/lib.rs b/ferveo-common/src/lib.rs index c041b6da..b6ddc7c2 100644 --- a/ferveo-common/src/lib.rs +++ b/ferveo-common/src/lib.rs @@ -19,15 +19,14 @@ impl fmt::Display for Error { Error::InvalidByteLength(expected, actual) => { write!( f, - "Invalid byte length: expected {}, actual {}", - expected, actual + "Invalid byte length: expected {expected}, actual {actual}" ) } Error::SerializationError(e) => { - write!(f, "Serialization error: {}", e) + write!(f, "Serialization error: {e}") } Error::InvalidSeedLength(len) => { - write!(f, "Invalid seed length: {}", len) + write!(f, "Invalid seed length: {len}") } } } diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs index 7d1cb93b..3cc88202 100644 --- a/ferveo/src/bindings_python.rs +++ b/ferveo/src/bindings_python.rs @@ -65,14 +65,12 @@ impl From for PyErr { expected, actual, ) => InsufficientTranscriptsForAggregate::new_err(format!( - "expected: {}, actual: {}", - expected, actual + "expected: {expected}, actual: {actual}" )), Error::InvalidDkgPublicKey => InvalidDkgPublicKey::new_err(""), Error::InsufficientValidators(expected, actual) => { InsufficientValidators::new_err(format!( - "expected: {}, actual: {}", - expected, actual + "expected: {expected}, actual: {actual}" )) } Error::InvalidTranscriptAggregate => { @@ -90,8 +88,7 @@ impl From for PyErr { } Error::InvalidByteLength(expected, actual) => { InvalidByteLength::new_err(format!( - "expected: {}, actual: {}", - expected, actual + "expected: {expected}, actual: {actual}" )) } Error::InvalidVariant(variant) => { diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs index 7b9ae484..b31866c4 100644 --- a/ferveo/src/bindings_wasm.rs +++ b/ferveo/src/bindings_wasm.rs @@ -30,7 +30,7 @@ pub fn set_panic_hook() { } pub fn map_js_err(err: T) -> Error { - Error::new(&format!("{}", err)) + Error::new(&format!("{err}")) } pub fn to_js_bytes(t: &T) -> Result, Error> { @@ -577,7 +577,7 @@ pub mod test_common { } pub fn gen_address(i: usize) -> EthereumAddress { - EthereumAddress::from_string(&format!("0x{:040}", i)).unwrap() + EthereumAddress::from_string(&format!("0x{i:040}")).unwrap() } pub fn gen_validator(i: usize, keypair: &Keypair) -> Validator { From 1800d3c5db164947c7cae35433fb8e3ad2650b66 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 14 Aug 2023 15:21:28 +0200 Subject: [PATCH 08/10] feat!: add ciphertext header to ciphertext api --- .../examples/server_api_precomputed.py | 2 +- ferveo-python/examples/server_api_simple.py | 2 +- ferveo-python/ferveo/__init__.py | 1 + ferveo-python/ferveo/__init__.pyi | 17 +++++- ferveo-python/test/test_ferveo.py | 2 +- ferveo-wasm/examples/node/src/main.test.ts | 10 ++-- ferveo-wasm/tests/node.rs | 4 +- ferveo/benches/benchmarks/validity_checks.rs | 2 +- ferveo/examples/bench_ark_sizes.rs | 7 +-- ferveo/examples/bench_primitives_size.rs | 14 ++--- ferveo/src/api.rs | 58 +++++++++++-------- ferveo/src/bindings_python.rs | 45 +++++++++++--- ferveo/src/bindings_wasm.rs | 36 ++++++++++-- ferveo/src/dkg.rs | 2 +- ferveo/src/lib.rs | 24 ++++---- ferveo/src/pvss.rs | 12 ++-- tpke/benches/tpke.rs | 12 ++-- tpke/src/api.rs | 3 + tpke/src/ciphertext.rs | 40 +++++++++++-- tpke/src/context.rs | 14 ++--- tpke/src/decryption.rs | 41 +++++++------ tpke/src/lib.rs | 31 +++++++--- 22 files changed, 256 insertions(+), 123 deletions(-) diff --git a/ferveo-python/examples/server_api_precomputed.py b/ferveo-python/examples/server_api_precomputed.py index 0916738d..7c433e96 100644 --- a/ferveo-python/examples/server_api_precomputed.py +++ b/ferveo-python/examples/server_api_precomputed.py @@ -82,7 +82,7 @@ def gen_eth_addr(i: int) -> str: # Create a decryption share for the ciphertext decryption_share = aggregate.create_decryption_share_precomputed( - dkg, ciphertext, aad, validator_keypair + dkg, ciphertext.header, aad, validator_keypair ) decryption_shares.append(decryption_share) diff --git a/ferveo-python/examples/server_api_simple.py b/ferveo-python/examples/server_api_simple.py index e41c9f24..4f9e8447 100644 --- a/ferveo-python/examples/server_api_simple.py +++ b/ferveo-python/examples/server_api_simple.py @@ -85,7 +85,7 @@ def gen_eth_addr(i: int) -> str: # Create a decryption share for the ciphertext decryption_share = aggregate.create_decryption_share_simple( - dkg, ciphertext, aad, validator_keypair + dkg, ciphertext.header, aad, validator_keypair ) decryption_shares.append(decryption_share) diff --git a/ferveo-python/ferveo/__init__.py b/ferveo-python/ferveo/__init__.py index f89aa778..478628b1 100644 --- a/ferveo-python/ferveo/__init__.py +++ b/ferveo-python/ferveo/__init__.py @@ -9,6 +9,7 @@ Transcript, Dkg, Ciphertext, + CiphertextHeader, DecryptionShareSimple, DecryptionSharePrecomputed, AggregatedTranscript, diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi index 1dfab2f0..3549d751 100644 --- a/ferveo-python/ferveo/__init__.pyi +++ b/ferveo-python/ferveo/__init__.pyi @@ -119,6 +119,19 @@ class Dkg: @final class Ciphertext: + header: CiphertextHeader + payload: bytes + + @staticmethod + def from_bytes(data: bytes) -> Ciphertext: + ... + + def __bytes__(self) -> bytes: + ... + + +@final +class CiphertextHeader: @staticmethod def from_bytes(data: bytes) -> Ciphertext: ... @@ -159,7 +172,7 @@ class AggregatedTranscript: def create_decryption_share_simple( self, dkg: Dkg, - ciphertext: Ciphertext, + ciphertext_header: CiphertextHeader, aad: bytes, validator_keypair: Keypair ) -> DecryptionShareSimple: @@ -168,7 +181,7 @@ class AggregatedTranscript: def create_decryption_share_precomputed( self, dkg: Dkg, - ciphertext: Ciphertext, + ciphertext_header: CiphertextHeader, aad: bytes, validator_keypair: Keypair ) -> DecryptionSharePrecomputed: diff --git a/ferveo-python/test/test_ferveo.py b/ferveo-python/test/test_ferveo.py index 7d93c637..42a82a84 100644 --- a/ferveo-python/test/test_ferveo.py +++ b/ferveo-python/test/test_ferveo.py @@ -91,7 +91,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t assert pvss_aggregated.verify(shares_num, messages) decryption_share = decryption_share_for_variant(variant, pvss_aggregated)( - dkg, ciphertext, aad, validator_keypair + dkg, ciphertext.header, aad, validator_keypair ) decryption_shares.append(decryption_share) diff --git a/ferveo-wasm/examples/node/src/main.test.ts b/ferveo-wasm/examples/node/src/main.test.ts index 69ec9f8a..28144861 100644 --- a/ferveo-wasm/examples/node/src/main.test.ts +++ b/ferveo-wasm/examples/node/src/main.test.ts @@ -27,11 +27,11 @@ function setupTest() { const sharesNum = 4; const threshold = Math.floor((sharesNum * 2) / 3); - const validator_keypairs: Keypair[] = []; + const validatorKeypairs: Keypair[] = []; const validators: Validator[] = []; for (let i = 0; i < sharesNum; i++) { const keypair = Keypair.random(); - validator_keypairs.push(keypair); + validatorKeypairs.push(keypair); const validator = new Validator(genEthAddr(i), keypair.publicKey); validators.push(validator); } @@ -66,7 +66,7 @@ function setupTest() { tau, sharesNum, threshold, - validatorKeypairs: validator_keypairs, + validatorKeypairs, validators, dkg, messages, @@ -103,7 +103,7 @@ describe("ferveo-wasm", () => { const decryptionShare = aggregate.createDecryptionShareSimple( dkg, - ciphertext, + ciphertext.header, aad, keypair ); @@ -150,7 +150,7 @@ describe("ferveo-wasm", () => { const decryptionShare = aggregate.createDecryptionSharePrecomputed( dkg, - ciphertext, + ciphertext.header, aad, keypair ); diff --git a/ferveo-wasm/tests/node.rs b/ferveo-wasm/tests/node.rs index c1564c82..b4234d07 100644 --- a/ferveo-wasm/tests/node.rs +++ b/ferveo-wasm/tests/node.rs @@ -125,7 +125,7 @@ fn tdec_simple() { aggregate .create_decryption_share_simple( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), &aad, &keypair, ) @@ -179,7 +179,7 @@ fn tdec_precomputed() { aggregate .create_decryption_share_precomputed( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), &aad, &keypair, ) diff --git a/ferveo/benches/benchmarks/validity_checks.rs b/ferveo/benches/benchmarks/validity_checks.rs index 00972c05..a6dd9f48 100644 --- a/ferveo/benches/benchmarks/validity_checks.rs +++ b/ferveo/benches/benchmarks/validity_checks.rs @@ -22,7 +22,7 @@ fn gen_keypairs(num: u32) -> Vec> { } pub fn gen_address(i: usize) -> EthereumAddress { - EthereumAddress::from_str(&format!("0x{:040}", i)).unwrap() + EthereumAddress::from_str(&format!("0x{i:040}")).unwrap() } fn gen_validators( diff --git a/ferveo/examples/bench_ark_sizes.rs b/ferveo/examples/bench_ark_sizes.rs index 7b211597..95ad35f6 100644 --- a/ferveo/examples/bench_ark_sizes.rs +++ b/ferveo/examples/bench_ark_sizes.rs @@ -47,8 +47,7 @@ pub fn save_data( let mut file = OpenOptions::new().append(true).open(&file_path).unwrap(); writeln!( file, - "{}|{}|{}|", - n_of_elements, type_of_element, serialized_size_in_bytes + "{n_of_elements}|{type_of_element}|{serialized_size_in_bytes}|" ) .unwrap(); } @@ -66,10 +65,10 @@ fn main() { .map(|(n, element)| (n, element)) .collect::>(); - println!("Running benchmarks for {:?}", configs); + println!("Running benchmarks for {configs:?}"); for (n, element) in configs { - println!("number_of_elements: {}, type_of_elements: {}", n, element); + println!("number_of_elements: {n}, type_of_elements: {element}"); let g1_affine = (0..*n).map(|_| G1Affine::rand(rng)).collect::>(); diff --git a/ferveo/examples/bench_primitives_size.rs b/ferveo/examples/bench_primitives_size.rs index e84c64a6..18adf673 100644 --- a/ferveo/examples/bench_primitives_size.rs +++ b/ferveo/examples/bench_primitives_size.rs @@ -42,12 +42,8 @@ pub fn save_data( eprintln!("Appending to file: {}", file_path.display()); let mut file = OpenOptions::new().append(true).open(&file_path).unwrap(); - writeln!( - file, - "{}|{}|{}|", - shares_num, threshold, transcript_size_bytes - ) - .unwrap(); + writeln!(file, "{shares_num}|{threshold}|{transcript_size_bytes}|") + .unwrap(); } // TODO: Find a way to deduplicate the following methods with benchmarks and test setup @@ -60,7 +56,7 @@ fn gen_keypairs(num: u32) -> Vec> { } pub fn gen_address(i: usize) -> EthereumAddress { - EthereumAddress::from_str(&format!("0x{:040}", i)).unwrap() + EthereumAddress::from_str(&format!("0x{i:040}")).unwrap() } fn gen_validators( @@ -132,10 +128,10 @@ fn main() { }) .collect::>(); - println!("Running benchmarks for {:?}", configs); + println!("Running benchmarks for {configs:?}"); for (shares_num, threshold) in configs { - println!("shares_num: {}, threshold: {}", shares_num, threshold); + println!("shares_num: {shares_num}, threshold: {threshold}"); let dkg = setup(*shares_num as u32, threshold, rng); let transcript = &dkg.vss.values().next().unwrap(); let transcript_bytes = bincode::serialize(&transcript).unwrap(); diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs index ccf9ff5f..16ede060 100644 --- a/ferveo/src/api.rs +++ b/ferveo/src/api.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; pub use tpke::api::{ prepare_combine_simple, share_combine_precomputed, share_combine_simple, - Ciphertext, Fr, G1Affine, G1Prepared, SecretBox, E, + Fr, G1Affine, G1Prepared, G2Affine, SecretBox, E, }; pub type PublicKey = ferveo_common::PublicKey; @@ -55,7 +55,7 @@ pub fn encrypt( ) -> Result { let mut rng = rand::thread_rng(); let ciphertext = tpke::api::encrypt(message, aad, &pubkey.0, &mut rng)?; - Ok(ciphertext) + Ok(Ciphertext(ciphertext)) } pub fn decrypt_with_shared_secret( @@ -65,7 +65,7 @@ pub fn decrypt_with_shared_secret( ) -> Result> { let dkg_public_params = DkgPublicParameters::default(); tpke::api::decrypt_with_shared_secret( - ciphertext, + &ciphertext.0, aad, &shared_secret.0, &dkg_public_params.g1_inv, @@ -73,6 +73,23 @@ pub fn decrypt_with_shared_secret( .map_err(Error::from) } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Eq)] +pub struct Ciphertext(tpke::api::Ciphertext); + +impl Ciphertext { + pub fn header(&self) -> Result { + Ok(CiphertextHeader(self.0.header()?)) + } + + pub fn payload(&self) -> Vec { + self.0.payload() + } +} + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct CiphertextHeader(tpke::api::CiphertextHeader); + /// The ferveo variant to use for the decryption share derivation. #[derive( PartialEq, Eq, Debug, Serialize, Deserialize, Copy, Clone, PartialOrd, @@ -286,7 +303,7 @@ impl AggregatedTranscript { pub fn create_decryption_share_precomputed( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> Result { @@ -297,7 +314,7 @@ impl AggregatedTranscript { .take(dkg.0.dkg_params.shares_num as usize) .collect(); self.0.make_decryption_share_simple_precomputed( - ciphertext, + &ciphertext_header.0, aad, &validator_keypair.decryption_key, dkg.0.me.share_index, @@ -309,12 +326,12 @@ impl AggregatedTranscript { pub fn create_decryption_share_simple( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> Result { let share = self.0.make_decryption_share_simple( - ciphertext, + &ciphertext_header.0, aad, &validator_keypair.decryption_key, dkg.0.me.share_index, @@ -458,14 +475,10 @@ mod test_ferveo_api { // In the meantime, the client creates a ciphertext and decryption request let msg = "my-msg".as_bytes().to_vec(); let aad: &[u8] = "my-aad".as_bytes(); - let rng = &mut thread_rng(); - let ciphertext = tpke::api::encrypt( - SecretBox::new(msg.clone()), - aad, - &dkg_public_key.0, - rng, - ) - .unwrap(); + let _rng = &mut thread_rng(); + let ciphertext = + encrypt(SecretBox::new(msg.clone()), aad, &dkg_public_key) + .unwrap(); // Having aggregated the transcripts, the validators can now create decryption shares let decryption_shares: Vec<_> = @@ -490,7 +503,7 @@ mod test_ferveo_api { aggregate .create_decryption_share_precomputed( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), aad, validator_keypair, ) @@ -557,14 +570,9 @@ mod test_ferveo_api { // In the meantime, the client creates a ciphertext and decryption request let msg = "my-msg".as_bytes().to_vec(); let aad: &[u8] = "my-aad".as_bytes(); - let rng = &mut thread_rng(); - let ciphertext = tpke::api::encrypt( - SecretBox::new(msg.clone()), - aad, - &public_key.0, - rng, - ) - .unwrap(); + let _rng = &mut thread_rng(); + let ciphertext = + encrypt(SecretBox::new(msg.clone()), aad, &public_key).unwrap(); // Having aggregated the transcripts, the validators can now create decryption shares let decryption_shares: Vec<_> = @@ -587,7 +595,7 @@ mod test_ferveo_api { aggregate .create_decryption_share_simple( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), aad, validator_keypair, ) diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs index 3cc88202..ed965f3e 100644 --- a/ferveo/src/bindings_python.rs +++ b/ferveo/src/bindings_python.rs @@ -509,8 +509,38 @@ impl Dkg { )] pub struct Ciphertext(api::Ciphertext); +#[pymethods] +impl Ciphertext { + #[getter] + pub fn header(&self) -> PyResult { + let header = self.0.header().map_err(FerveoPythonError::from)?; + Ok(CiphertextHeader(header)) + } + + #[getter] + pub fn payload(&self) -> Vec { + self.0.payload().to_vec() + } +} + generate_bytes_serialization!(Ciphertext); +#[pyclass(module = "ferveo")] +#[derive( + Clone, + Debug, + PartialEq, + Eq, + Serialize, + Deserialize, + derive_more::From, + derive_more::AsRef, + derive_more::Into, +)] +pub struct CiphertextHeader(api::CiphertextHeader); + +generate_bytes_serialization!(CiphertextHeader); + #[pyclass(module = "ferveo")] #[derive(Clone, derive_more::AsRef, derive_more::From)] pub struct DecryptionShareSimple(api::DecryptionShareSimple); @@ -555,7 +585,7 @@ impl AggregatedTranscript { pub fn create_decryption_share_precomputed( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> PyResult { @@ -563,7 +593,7 @@ impl AggregatedTranscript { .0 .create_decryption_share_precomputed( &dkg.0, - &ciphertext.0, + &ciphertext_header.0, aad, &validator_keypair.0, ) @@ -574,7 +604,7 @@ impl AggregatedTranscript { pub fn create_decryption_share_simple( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> PyResult { @@ -582,7 +612,7 @@ impl AggregatedTranscript { .0 .create_decryption_share_simple( &dkg.0, - &ciphertext.0, + &ciphertext_header.0, aad, &validator_keypair.0, ) @@ -628,6 +658,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; @@ -712,7 +743,7 @@ mod test_ferveo_python { .iter() .enumerate() .map(|(i, keypair)| { - Validator::new(format!("0x{:040}", i), &keypair.public_key()) + Validator::new(format!("0x{i:040}"), &keypair.public_key()) .unwrap() }) .collect(); @@ -799,7 +830,7 @@ mod test_ferveo_python { aggregate .create_decryption_share_precomputed( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), aad, validator_keypair, ) @@ -876,7 +907,7 @@ mod test_ferveo_python { aggregate .create_decryption_share_simple( &dkg, - &ciphertext, + &ciphertext.header().unwrap(), aad, validator_keypair, ) diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs index b31866c4..e686f8a9 100644 --- a/ferveo/src/bindings_wasm.rs +++ b/ferveo/src/bindings_wasm.rs @@ -229,8 +229,36 @@ generate_boxed_bytes_serialization!(FerveoPublicKey, InnerPublicKey); )] pub struct Ciphertext(api::Ciphertext); +#[wasm_bindgen] +impl Ciphertext { + #[wasm_bindgen(js_name = "header", getter)] + pub fn header(&self) -> JsResult { + let header = self.0.header().map_err(map_js_err)?; + Ok(CiphertextHeader(header)) + } + + #[wasm_bindgen(js_name = "payload", getter)] + pub fn payload(&self) -> Vec { + self.0.payload() + } +} + generate_common_methods!(Ciphertext); +#[wasm_bindgen] +#[derive( + Clone, + Debug, + PartialEq, + Eq, + derive_more::From, + derive_more::AsRef, + derive_more::Into, +)] +pub struct CiphertextHeader(api::CiphertextHeader); + +generate_common_methods!(CiphertextHeader); + #[wasm_bindgen(js_name = "ferveoEncrypt")] pub fn ferveo_encrypt( message: &[u8], @@ -497,7 +525,7 @@ impl AggregatedTranscript { pub fn create_decryption_share_precomputed( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> JsResult { @@ -506,7 +534,7 @@ impl AggregatedTranscript { .0 .create_decryption_share_precomputed( &dkg.0, - &ciphertext.0, + &ciphertext_header.0, aad, &validator_keypair.0, ) @@ -518,7 +546,7 @@ impl AggregatedTranscript { pub fn create_decryption_share_simple( &self, dkg: &Dkg, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, ) -> JsResult { @@ -527,7 +555,7 @@ impl AggregatedTranscript { .0 .create_decryption_share_simple( &dkg.0, - &ciphertext.0, + &ciphertext_header.0, aad, &validator_keypair.0, ) diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs index 07dee015..ed183b72 100644 --- a/ferveo/src/dkg.rs +++ b/ferveo/src/dkg.rs @@ -333,7 +333,7 @@ pub(crate) mod test_common { } pub fn gen_address(i: usize) -> EthereumAddress { - EthereumAddress::from_str(&format!("0x{:040}", i)).unwrap() + EthereumAddress::from_str(&format!("0x{i:040}")).unwrap() } pub fn gen_validators(keypairs: &[Keypair]) -> Vec> { diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs index 214f9444..1ddd54e0 100644 --- a/ferveo/src/lib.rs +++ b/ferveo/src/lib.rs @@ -127,8 +127,8 @@ mod test_dkg_full { use ferveo_common::Keypair; use group_threshold_cryptography as tpke; use group_threshold_cryptography::{ - Ciphertext, DecryptionSharePrecomputed, DecryptionShareSimple, - SecretBox, SharedSecret, + DecryptionSharePrecomputed, DecryptionShareSimple, SecretBox, + SharedSecret, }; use itertools::izip; @@ -140,7 +140,7 @@ mod test_dkg_full { fn make_shared_secret_simple_tdec( dkg: &PubliclyVerifiableDkg, aad: &[u8], - ciphertext: &Ciphertext, + ciphertext_header: &tpke::CiphertextHeader, validator_keypairs: &[Keypair], ) -> ( PubliclyVerifiableSS, @@ -159,7 +159,7 @@ mod test_dkg_full { .unwrap(); pvss_aggregated .make_decryption_share_simple( - ciphertext, + ciphertext_header, aad, &validator_keypair.decryption_key, validator.share_index, @@ -211,7 +211,7 @@ mod test_dkg_full { let (_, _, shared_secret) = make_shared_secret_simple_tdec( &dkg, aad, - &ciphertext, + &ciphertext.header().unwrap(), &validator_keypairs, ); @@ -264,7 +264,7 @@ mod test_dkg_full { .unwrap(); pvss_aggregated .make_decryption_share_simple_precomputed( - &ciphertext, + &ciphertext.header().unwrap(), aad, &validator_keypair.decryption_key, validator.share_index, @@ -307,7 +307,7 @@ mod test_dkg_full { make_shared_secret_simple_tdec( &dkg, aad, - &ciphertext, + &ciphertext.header().unwrap(), &validator_keypairs, ); @@ -367,7 +367,7 @@ mod test_dkg_full { let (_, _, old_shared_secret) = make_shared_secret_simple_tdec( &dkg, aad, - &ciphertext, + &ciphertext.header().unwrap(), &validator_keypairs, ); @@ -443,7 +443,7 @@ mod test_dkg_full { .map(|(share_index, validator_keypair)| { pvss_aggregated .make_decryption_share_simple( - &ciphertext, + &ciphertext.header().unwrap(), aad, &validator_keypair.decryption_key, share_index, @@ -459,7 +459,7 @@ mod test_dkg_full { DecryptionShareSimple::create( &new_validator_decryption_key, &new_private_key_share, - &ciphertext, + &ciphertext.header().unwrap(), aad, &dkg.pvss_params.g_inv(), ) @@ -491,7 +491,7 @@ mod test_dkg_full { let (_, _, old_shared_secret) = make_shared_secret_simple_tdec( &dkg, aad, - &ciphertext, + &ciphertext.header().unwrap(), &validator_keypairs, ); @@ -514,7 +514,7 @@ mod test_dkg_full { .map(|(validator_address, validator_keypair)| { pvss_aggregated .refresh_decryption_share( - &ciphertext, + &ciphertext.header().unwrap(), aad, &validator_keypair.decryption_key, validator_address, diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs index 0d6433fd..547bb16c 100644 --- a/ferveo/src/pvss.rs +++ b/ferveo/src/pvss.rs @@ -14,7 +14,7 @@ use serde_with::serde_as; use subproductdomain::fast_multiexp; use tpke::{ prepare_combine_simple, refresh_private_key_share, - update_share_for_recovery, Ciphertext, DecryptionSharePrecomputed, + update_share_for_recovery, CiphertextHeader, DecryptionSharePrecomputed, DecryptionShareSimple, PrivateKeyShare, }; use zeroize::{self, Zeroize, ZeroizeOnDrop}; @@ -329,7 +329,7 @@ impl PubliclyVerifiableSS { pub fn make_decryption_share_simple( &self, - ciphertext: &Ciphertext, + ciphertext: &CiphertextHeader, aad: &[u8], validator_decryption_key: &E::ScalarField, share_index: usize, @@ -349,7 +349,7 @@ impl PubliclyVerifiableSS { pub fn make_decryption_share_simple_precomputed( &self, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_decryption_key: &E::ScalarField, share_index: usize, @@ -366,7 +366,7 @@ impl PubliclyVerifiableSS { share_index, validator_decryption_key, &private_key_share, - ciphertext, + ciphertext_header, aad, &lagrange_coeffs[share_index], g_inv, @@ -376,7 +376,7 @@ impl PubliclyVerifiableSS { pub fn refresh_decryption_share( &self, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], validator_decryption_key: &E::ScalarField, share_index: usize, @@ -396,7 +396,7 @@ impl PubliclyVerifiableSS { DecryptionShareSimple::create( validator_decryption_key, &refreshed_private_key_share, - ciphertext, + ciphertext_header, aad, &dkg.pvss_params.g_inv(), ) diff --git a/tpke/benches/tpke.rs b/tpke/benches/tpke.rs index 89fef191..c6ad85ae 100644 --- a/tpke/benches/tpke.rs +++ b/tpke/benches/tpke.rs @@ -118,7 +118,11 @@ impl SetupSimple { // Creating decryption shares let decryption_shares: Vec<_> = contexts .iter() - .map(|context| context.create_share(&ciphertext, aad).unwrap()) + .map(|context| { + context + .create_share(&ciphertext.header().unwrap(), aad) + .unwrap() + }) .collect(); let pub_contexts = contexts[0].clone().public_decryption_contexts; @@ -190,7 +194,7 @@ pub fn bench_create_decryption_share(c: &mut Criterion) { DecryptionShareSimple::create_unchecked( &ctx.validator_private_key, &ctx.private_key_share, - &setup.shared.ciphertext, + &setup.shared.ciphertext.header().unwrap(), ) }) .collect::>() @@ -206,7 +210,7 @@ pub fn bench_create_decryption_share(c: &mut Criterion) { .iter() .map(|context| { context.create_share_precomputed( - &setup.shared.ciphertext, + &setup.shared.ciphertext.header().unwrap(), &setup.shared.aad, ) }) @@ -301,7 +305,7 @@ pub fn bench_share_combine(c: &mut Criterion) { .map(|context| { context .create_share_precomputed( - &setup.shared.ciphertext, + &setup.shared.ciphertext.header().unwrap(), &setup.shared.aad, ) .unwrap() diff --git a/tpke/src/api.rs b/tpke/src/api.rs index 3ebba690..3e283d77 100644 --- a/tpke/src/api.rs +++ b/tpke/src/api.rs @@ -3,6 +3,7 @@ pub type E = ark_bls12_381::Bls12_381; pub type G1Prepared = ::G1Prepared; pub type G1Affine = ::G1Affine; +pub type G2Affine = ::G2Affine; pub type Fr = ark_bls12_381::Fr; pub type PrivateKey = ark_bls12_381::G2Affine; pub type Result = crate::Result; @@ -11,6 +12,8 @@ pub type PrivateDecryptionContextSimple = pub type DecryptionSharePrecomputed = crate::DecryptionSharePrecomputed; pub type DecryptionShareSimple = crate::DecryptionShareSimple; pub type Ciphertext = crate::Ciphertext; + +pub type CiphertextHeader = crate::CiphertextHeader; pub type TargetField = ::TargetField; pub use crate::{ diff --git a/tpke/src/ciphertext.rs b/tpke/src/ciphertext.rs index f7137dbc..81f79389 100644 --- a/tpke/src/ciphertext.rs +++ b/tpke/src/ciphertext.rs @@ -32,17 +32,47 @@ pub struct Ciphertext { } impl Ciphertext { + pub fn check(&self, aad: &[u8], g_inv: &E::G1Prepared) -> Result { + self.header()?.check(aad, g_inv) + } + + pub fn ciphertext_hash(&self) -> [u8; 32] { + sha256(&self.ciphertext) + } + + pub fn header(&self) -> Result> { + Ok(CiphertextHeader { + commitment: self.commitment, + auth_tag: self.auth_tag, + ciphertext_hash: self.ciphertext_hash(), + }) + } + pub fn payload(&self) -> Vec { + self.ciphertext.clone() + } +} + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct CiphertextHeader { + #[serde_as(as = "serialization::SerdeAs")] + pub commitment: E::G1Affine, + #[serde_as(as = "serialization::SerdeAs")] + pub auth_tag: E::G2Affine, + pub ciphertext_hash: [u8; 32], +} + +impl CiphertextHeader { pub fn check(&self, aad: &[u8], g_inv: &E::G1Prepared) -> Result { // Implements a variant of the check in section 4.4.2 of the Ferveo paper: - // 'TPKE.CheckCiphertextValidity(U,W,aad)' + // 'TPKE.CheckCiphertextValidity(U,W,aad)' // See: https://eprint.iacr.org/2022/898.pdf // See: https://nikkolasg.github.io/ferveo/tpke.html#to-validate-ciphertext-for-ind-cca2-security // H_G2(U, sym_ctxt_digest, aad) - let ciphertext_hash = sha256(&self.ciphertext[..]); let hash_g2 = E::G2Prepared::from(construct_tag_hash::( self.commitment, - &ciphertext_hash, + &self.ciphertext_hash, aad, )?); @@ -152,11 +182,11 @@ pub fn decrypt_with_shared_secret( decrypt_with_shared_secret_unchecked(ciphertext, aad, shared_secret) } -fn sha256(input: &[u8]) -> Vec { +fn sha256(input: &[u8]) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(input); let result = hasher.finalize(); - result.to_vec() + result.into() } pub fn shared_secret_to_chacha( diff --git a/tpke/src/context.rs b/tpke/src/context.rs index 4a471925..32fa91fb 100644 --- a/tpke/src/context.rs +++ b/tpke/src/context.rs @@ -3,9 +3,9 @@ use std::ops::Mul; use ark_ec::{pairing::Pairing, CurveGroup}; use crate::{ - prepare_combine_simple, BlindedKeyShare, Ciphertext, DecryptionShareFast, - DecryptionSharePrecomputed, DecryptionShareSimple, PrivateKeyShare, - PublicKeyShare, Result, + prepare_combine_simple, BlindedKeyShare, Ciphertext, CiphertextHeader, + DecryptionShareFast, DecryptionSharePrecomputed, DecryptionShareSimple, + PrivateKeyShare, PublicKeyShare, Result, }; #[derive(Clone, Debug)] @@ -78,13 +78,13 @@ pub struct PrivateDecryptionContextSimple { impl PrivateDecryptionContextSimple { pub fn create_share( &self, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], ) -> Result> { DecryptionShareSimple::create( &self.validator_private_key, &self.private_key_share, - ciphertext, + ciphertext_header, aad, &self.setup_params.g_inv, ) @@ -92,7 +92,7 @@ impl PrivateDecryptionContextSimple { pub fn create_share_precomputed( &self, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], ) -> Result> { let domain = self @@ -106,7 +106,7 @@ impl PrivateDecryptionContextSimple { self.index, &self.validator_private_key, &self.private_key_share, - ciphertext, + ciphertext_header, aad, &lagrange_coeffs[self.index], &self.setup_params.g_inv, diff --git a/tpke/src/decryption.rs b/tpke/src/decryption.rs index ad319783..01ae5df7 100644 --- a/tpke/src/decryption.rs +++ b/tpke/src/decryption.rs @@ -9,8 +9,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_with::serde_as; use crate::{ - generate_random, Ciphertext, PrivateKeyShare, PublicDecryptionContextFast, - PublicDecryptionContextSimple, Result, + generate_random, Ciphertext, CiphertextHeader, PrivateKeyShare, + PublicDecryptionContextFast, PublicDecryptionContextSimple, Result, }; #[serde_as] @@ -31,10 +31,10 @@ pub struct ValidatorShareChecksum { impl ValidatorShareChecksum { pub fn new( validator_decryption_key: &E::ScalarField, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, ) -> Result { // C_i = dk_i^{-1} * U - let checksum = ciphertext + let checksum = ciphertext_header .commitment // TODO: Should we panic here? I think we should since that would mean that the decryption key is invalid. // And so, the validator should not be able to create a decryption share. @@ -90,15 +90,15 @@ impl DecryptionShareSimple { pub fn create( validator_decryption_key: &E::ScalarField, private_key_share: &PrivateKeyShare, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], g_inv: &E::G1Prepared, ) -> Result { - ciphertext.check(aad, g_inv)?; + ciphertext_header.check(aad, g_inv)?; Self::create_unchecked( validator_decryption_key, private_key_share, - ciphertext, + ciphertext_header, ) } @@ -107,17 +107,19 @@ impl DecryptionShareSimple { pub fn create_unchecked( validator_decryption_key: &E::ScalarField, private_key_share: &PrivateKeyShare, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, ) -> Result { // D_i = e(U, Z_i) let decryption_share = E::pairing( - ciphertext.commitment, + ciphertext_header.commitment, private_key_share.private_key_share, ) .0; - let validator_checksum = - ValidatorShareChecksum::new(validator_decryption_key, ciphertext)?; + let validator_checksum = ValidatorShareChecksum::new( + validator_decryption_key, + ciphertext_header, + )?; Ok(Self { decryption_share, @@ -160,17 +162,17 @@ impl DecryptionSharePrecomputed { validator_index: usize, validator_decryption_key: &E::ScalarField, private_key_share: &PrivateKeyShare, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, aad: &[u8], lagrange_coeff: &E::ScalarField, g_inv: &E::G1Prepared, ) -> Result { - ciphertext.check(aad, g_inv)?; + ciphertext_header.check(aad, g_inv)?; Self::create_unchecked( validator_index, validator_decryption_key, private_key_share, - ciphertext, + ciphertext_header, lagrange_coeff, ) } @@ -179,11 +181,12 @@ impl DecryptionSharePrecomputed { validator_index: usize, validator_decryption_key: &E::ScalarField, private_key_share: &PrivateKeyShare, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, lagrange_coeff: &E::ScalarField, ) -> Result { // U_{λ_i} = [λ_{i}(0)] U - let u_to_lagrange_coeff = ciphertext.commitment.mul(lagrange_coeff); + let u_to_lagrange_coeff = + ciphertext_header.commitment.mul(lagrange_coeff); // C_{λ_i} = e(U_{λ_i}, Z_i) let decryption_share = E::pairing( u_to_lagrange_coeff, @@ -191,8 +194,10 @@ impl DecryptionSharePrecomputed { ) .0; - let validator_checksum = - ValidatorShareChecksum::new(validator_decryption_key, ciphertext)?; + let validator_checksum = ValidatorShareChecksum::new( + validator_decryption_key, + ciphertext_header, + )?; Ok(Self { decrypter_index: validator_index, diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 651935ae..46a78f24 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -309,7 +309,9 @@ mod tests { ) -> SharedSecret { let decryption_shares: Vec<_> = contexts .iter() - .map(|c| c.create_share(ciphertext, aad).unwrap()) + .map(|c| { + c.create_share(&ciphertext.header().unwrap(), aad).unwrap() + }) .collect(); make_shared_secret( &contexts[0].public_decryption_contexts, @@ -459,7 +461,9 @@ mod tests { encrypt::(SecretBox::new(msg), aad, &pubkey, rng).unwrap(); let bad_aad = "bad aad".as_bytes(); - assert!(contexts[0].create_share(&ciphertext, bad_aad).is_err()); + assert!(contexts[0] + .create_share(&ciphertext.header().unwrap(), bad_aad) + .is_err()); } #[test] @@ -531,7 +535,9 @@ mod tests { // We need at least threshold shares to decrypt let decryption_shares: Vec<_> = contexts .iter() - .map(|c| c.create_share(&ciphertext, aad).unwrap()) + .map(|c| { + c.create_share(&ciphertext.header().unwrap(), aad).unwrap() + }) .take(threshold) .collect(); let pub_contexts = @@ -575,7 +581,12 @@ mod tests { let decryption_shares: Vec<_> = contexts .iter() .map(|context| { - context.create_share_precomputed(&ciphertext, aad).unwrap() + context + .create_share_precomputed( + &ciphertext.header().unwrap(), + aad, + ) + .unwrap() }) .collect(); @@ -620,7 +631,9 @@ mod tests { let decryption_shares: Vec<_> = contexts .iter() - .map(|c| c.create_share(&ciphertext, aad).unwrap()) + .map(|c| { + c.create_share(&ciphertext.header().unwrap(), aad).unwrap() + }) .collect(); // In simple tDec variant, we verify decryption shares only after decryption fails. @@ -772,7 +785,9 @@ mod tests { // Get decryption shares from remaining participants let mut decryption_shares: Vec<_> = remaining_participants .iter() - .map(|c| c.create_share(&ciphertext, aad).unwrap()) + .map(|c| { + c.create_share(&ciphertext.header().unwrap(), aad).unwrap() + }) .collect(); // Create a decryption share from a recovered private key share @@ -781,7 +796,7 @@ mod tests { DecryptionShareSimple::create( &new_validator_decryption_key, &new_private_key_share, - &ciphertext, + &ciphertext.header().unwrap(), aad, g_inv, ) @@ -845,7 +860,7 @@ mod tests { DecryptionShareSimple::create( &p.validator_private_key, &private_key_share, - &ciphertext, + &ciphertext.header().unwrap(), aad, g_inv, ) From 191b81fc6ea0d62be671c4456e3d82c567447acd Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Mon, 21 Aug 2023 17:35:46 +0200 Subject: [PATCH 09/10] Update ferveo-python/ferveo/__init__.pyi Co-authored-by: Derek Pierre --- ferveo-python/ferveo/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi index 3549d751..ff17fd19 100644 --- a/ferveo-python/ferveo/__init__.pyi +++ b/ferveo-python/ferveo/__init__.pyi @@ -133,7 +133,7 @@ class Ciphertext: @final class CiphertextHeader: @staticmethod - def from_bytes(data: bytes) -> Ciphertext: + def from_bytes(data: bytes) -> CiphertextHeader: ... def __bytes__(self) -> bytes: From c06217c06e16df17d0525027312d5c368f443cb6 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 23 Aug 2023 16:21:41 +0200 Subject: [PATCH 10/10] apply pr suggestions --- ferveo/src/api.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs index 16ede060..b2179ee6 100644 --- a/ferveo/src/api.rs +++ b/ferveo/src/api.rs @@ -394,7 +394,7 @@ pub struct SharedSecret(pub tpke::api::SharedSecret); #[cfg(test)] mod test_ferveo_api { use itertools::izip; - use rand::{prelude::StdRng, thread_rng, SeedableRng}; + use rand::{prelude::StdRng, SeedableRng}; use tpke::SecretBox; use crate::{api::*, dkg::test_common::*}; @@ -475,7 +475,6 @@ mod test_ferveo_api { // In the meantime, the client creates a ciphertext and decryption request let msg = "my-msg".as_bytes().to_vec(); let aad: &[u8] = "my-aad".as_bytes(); - let _rng = &mut thread_rng(); let ciphertext = encrypt(SecretBox::new(msg.clone()), aad, &dkg_public_key) .unwrap(); @@ -570,7 +569,6 @@ mod test_ferveo_api { // In the meantime, the client creates a ciphertext and decryption request let msg = "my-msg".as_bytes().to_vec(); let aad: &[u8] = "my-aad".as_bytes(); - let _rng = &mut thread_rng(); let ciphertext = encrypt(SecretBox::new(msg.clone()), aad, &public_key).unwrap();