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, )