diff --git a/ipa-core/src/cli/crypto/hybrid_decrypt.rs b/ipa-core/src/cli/crypto/hybrid_decrypt.rs index be705e062..db4b33877 100644 --- a/ipa-core/src/cli/crypto/hybrid_decrypt.rs +++ b/ipa-core/src/cli/crypto/hybrid_decrypt.rs @@ -14,7 +14,10 @@ use crate::{ U128Conversions, }, hpke::{KeyRegistry, PrivateKeyOnly}, - report::hybrid::{EncryptedHybridReport, HybridReport}, + report::{ + hybrid::{EncryptedHybridReport, HybridReport}, + hybrid_info::HybridInfo, + }, test_fixture::Reconstruct, }; @@ -104,7 +107,7 @@ impl HybridDecryptArgs { for (dec_report1, (dec_report2, dec_report3)) in decrypted_reports1.zip(decrypted_reports2.zip(decrypted_reports3)) { - match (dec_report1, dec_report2, dec_report3) { + match (dec_report1.0, dec_report2.0, dec_report3.0) { ( HybridReport::Impression(impression_report1), HybridReport::Impression(impression_report2), @@ -125,7 +128,7 @@ impl HybridDecryptArgs { ] .reconstruct() .as_u128(); - let key_id = impression_report1.info.key_id; + let key_id = dec_report1.1.impression.key_id; writeln!(writer, "i,{match_key},{breakdown_key},{key_id}")?; } @@ -149,11 +152,13 @@ impl HybridDecryptArgs { ] .reconstruct() .as_u128(); - let key_id = conversion_report1.info.key_id; - let conversion_site_domain = conversion_report1.info.conversion_site_domain; - let timestamp = conversion_report1.info.timestamp; - let epsilon = conversion_report1.info.epsilon; - let sensitivity = conversion_report1.info.sensitivity; + + let key_id = dec_report1.1.conversion.key_id; + let conversion_site_domain = dec_report1.1.conversion.conversion_site_domain; + let timestamp = dec_report1.1.conversion.timestamp; + let epsilon = dec_report1.1.conversion.epsilon; + let sensitivity = dec_report1.1.conversion.sensitivity; + writeln!(writer, "c,{match_key},{value},{key_id},{conversion_site_domain},{timestamp},{epsilon},{sensitivity}")?; } _ => { @@ -172,7 +177,7 @@ struct DecryptedHybridReports { } impl Iterator for DecryptedHybridReports { - type Item = HybridReport; + type Item = (HybridReport, HybridInfo); fn next(&mut self) -> Option { let mut line = String::new(); @@ -180,9 +185,10 @@ impl Iterator for DecryptedHybridReports { let encrypted_report_bytes = hex::decode(line.trim()).unwrap(); let enc_report = EncryptedHybridReport::from_bytes(encrypted_report_bytes.into()).unwrap(); - let dec_report: HybridReport = - enc_report.decrypt(&self.key_registry).unwrap(); - Some(dec_report) + let (dec_report, info) = enc_report + .decrypt_and_return_info(&self.key_registry) + .unwrap(); + Some((dec_report, info)) } else { None } diff --git a/ipa-core/src/cli/crypto/hybrid_encrypt.rs b/ipa-core/src/cli/crypto/hybrid_encrypt.rs index 1a60d6944..658d5d8b0 100644 --- a/ipa-core/src/cli/crypto/hybrid_encrypt.rs +++ b/ipa-core/src/cli/crypto/hybrid_encrypt.rs @@ -21,14 +21,22 @@ use crate::{ config::{KeyRegistries, NetworkConfig}, error::BoxError, hpke::{KeyRegistry, PublicKeyOnly}, - report::hybrid::{HybridReport, DEFAULT_KEY_ID}, + report::{ + hybrid::{HybridReport, DEFAULT_KEY_ID}, + hybrid_info::HybridInfo, + }, secret_sharing::IntoShares, test_fixture::hybrid::TestHybridRecord, }; -/// Encryptor takes 3 arguments: `report_id`, helper that the shares must be encrypted towards -/// and the actual share ([`HybridReport`]) to encrypt. -type EncryptorInput = (usize, usize, HybridReport); +/// Encryptor takes 4 arguments: `report_id`, helper that the shares must be encrypted towards, +/// AAD info, and the actual share ([`HybridReport`]) to encrypt. +type EncryptorInput = ( + usize, + usize, + HybridReport, + HybridInfo, +); /// Encryptor sends report id and encrypted bytes down to file worker to write those bytes /// down type EncryptorOutput = (usize, Vec); @@ -91,7 +99,8 @@ impl HybridEncryptArgs { let mut worker_pool = ReportWriter::new(key_registries, &self.output_dir); for (report_id, record) in input.iter::().enumerate() { - worker_pool.submit(report_id, record.share())?; + let info = record.create_hybrid_info(); + worker_pool.submit(report_id, record.share(), &info)?; } worker_pool.join()?; @@ -130,11 +139,12 @@ impl EncryptorPool { std::thread::Builder::new() .name(format!("encryptor-{i}")) .spawn(move || { - for (i, helper_id, report) in rx { + for (i, helper_id, report, info) in rx { let key_registry = &key_registries[helper_id]; let output = report.encrypt( DEFAULT_KEY_ID, key_registry, + &info, &mut thread_rng(), )?; file_writer[helper_id].send((i, output))?; @@ -206,9 +216,12 @@ impl ReportWriter { &mut self, report_id: usize, shares: [HybridReport; 3], + info: &HybridInfo, ) -> UnitResult { for (i, share) in shares.into_iter().enumerate() { - self.encryptor_pool.encrypt_share((report_id, i, share))?; + // todo: maybe a smart pointer to avoid cloning + self.encryptor_pool + .encrypt_share((report_id, i, share, info.clone()))?; } Ok(()) diff --git a/ipa-core/src/query/runner/hybrid.rs b/ipa-core/src/query/runner/hybrid.rs index 7ed56c69b..05a439280 100644 --- a/ipa-core/src/query/runner/hybrid.rs +++ b/ipa-core/src/query/runner/hybrid.rs @@ -46,6 +46,7 @@ use crate::{ replicated::semi_honest::AdditiveShare as Replicated, BitDecomposed, TransposeFrom, Vectorizable, }, + seq_join::seq_join, sharding::{ShardConfiguration, Sharded}, }; @@ -130,10 +131,12 @@ where })) }) .try_flatten() - .take(sz); + .take(sz) + .map(|v| async move { v }); + let (decrypted_reports, resharded_tags) = reshard_aad( ctx.narrow(&HybridStep::ReshardByTag), - stream, + seq_join(ctx.active_work(), stream), |ctx, _, tag| tag.shard_picker(ctx.shard_count()), ) .await?; @@ -242,8 +245,15 @@ mod tests { let shares: [Vec>; 3] = records.iter().cloned().share(); for (buf, shares) in zip(&mut buffers, shares) { for (i, share) in shares.into_iter().enumerate() { + let info = records[i].create_hybrid_info(); share - .delimited_encrypt_to(key_id, key_registry.as_ref(), &mut rng, &mut buf[i % s]) + .delimited_encrypt_to( + key_id, + key_registry.as_ref(), + &info, + &mut rng, + &mut buf[i % s], + ) .unwrap(); } } diff --git a/ipa-core/src/report/hybrid.rs b/ipa-core/src/report/hybrid.rs index bcaca20aa..c8cccd184 100644 --- a/ipa-core/src/report/hybrid.rs +++ b/ipa-core/src/report/hybrid.rs @@ -49,7 +49,7 @@ use crate::{ PublicKeyRegistry, TagSize, }, protocol::ipa_prf::{boolean_ops::expand_shared_array_in_place, shuffle::Shuffleable}, - report::hybrid_info::{HybridConversionInfo, HybridImpressionInfo}, + report::hybrid_info::{HybridConversionInfo, HybridImpressionInfo, HybridInfo}, secret_sharing::{ replicated::{semi_honest::AdditiveShare as Replicated, ReplicatedSecretSharing}, SharedValue, @@ -121,7 +121,6 @@ where { pub match_key: Replicated, pub breakdown_key: Replicated, - pub info: HybridImpressionInfo, } impl HybridImpressionReport @@ -142,7 +141,6 @@ where buf.put_slice(&plaintext_mk); buf.put_slice(&plaintext_bk); - buf.put_slice(&self.info.to_bytes()); } /// # Errors @@ -155,9 +153,8 @@ where let breakdown_key = Replicated::::deserialize(GenericArray::from_slice(&buf[mk_sz..mk_sz + bk_sz])) .map_err(|e| InvalidHybridReportError::DeserializationError("breakdown_key", e.into()))?; - let info = HybridImpressionInfo::from_bytes(&buf[mk_sz + bk_sz..])?; - Ok(Self { match_key, breakdown_key, info }) + Ok(Self { match_key, breakdown_key }) } #[must_use] @@ -185,7 +182,7 @@ where /// # Panics /// If report length does not fit in `u16`. pub fn encrypted_len(&self) -> u16 { - self.ciphertext_len() + u16::try_from(self.info.byte_len()).unwrap() + self.ciphertext_len() } /// # Errors @@ -194,11 +191,12 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridImpressionInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { - out.put_u16_le(self.encrypted_len()); - self.encrypt_to(key_id, key_registry, rng, out) + out.put_u16_le(self.encrypted_len() + info.byte_len()); + self.encrypt_to(key_id, key_registry, info, rng, out) } /// # Errors @@ -207,11 +205,12 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridImpressionInfo, rng: &mut R, ) -> Result, InvalidHybridReportError> { - let mut out = Vec::with_capacity(usize::from(self.encrypted_len())); - self.encrypt_to(key_id, key_registry, rng, &mut out)?; - debug_assert_eq!(out.len(), usize::from(self.encrypted_len())); + let mut out = Vec::with_capacity(usize::from(self.encrypted_len() + info.byte_len())); + self.encrypt_to(key_id, key_registry, info, rng, &mut out)?; + debug_assert_eq!(out.len(), usize::from(self.encrypted_len() + info.byte_len())); Ok(out) } @@ -221,6 +220,7 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridImpressionInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { @@ -232,8 +232,8 @@ where .serialize(GenericArray::from_mut_slice(&mut plaintext_btt[..])); let pk = key_registry.public_key(key_id).ok_or(CryptError::NoSuchKey(key_id))?; - let info_enc_bytes = self.info.to_enc_bytes(); - let info_bytes = self.info.to_bytes(); + let info_enc_bytes = info.to_enc_bytes(); + let info_bytes = info.to_bytes(); let (encap_key_mk, ciphertext_mk, tag_mk) = seal_in_place( pk, @@ -270,7 +270,6 @@ where { pub match_key: Replicated, pub value: Replicated, - pub info: HybridConversionInfo, } impl HybridConversionReport @@ -291,7 +290,6 @@ where buf.put_slice(&plaintext_mk); buf.put_slice(&plaintext_v); - buf.put_slice(&self.info.to_bytes()); } /// # Errors @@ -304,8 +302,7 @@ where let value = Replicated::::deserialize(GenericArray::from_slice(&buf[mk_sz..mk_sz + v_sz])) .map_err(|e| InvalidHybridReportError::DeserializationError("breakdown_key", e.into()))?; - let info = HybridConversionInfo::from_bytes(&buf[mk_sz + v_sz..])?; - Ok(Self { match_key, value, info }) + Ok(Self { match_key, value }) } #[must_use] @@ -333,7 +330,7 @@ where /// # Panics /// If report length does not fit in `u16`. pub fn encrypted_len(&self) -> u16 { - self.ciphertext_len() + u16::try_from(self.info.byte_len()).unwrap() + self.ciphertext_len() } /// # Errors @@ -342,11 +339,12 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridConversionInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { - out.put_u16_le(self.encrypted_len()); - self.encrypt_to(key_id, key_registry, rng, out) + out.put_u16_le(self.encrypted_len() + info.byte_len()); + self.encrypt_to(key_id, key_registry, info, rng, out) } /// # Errors @@ -355,11 +353,12 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridConversionInfo, rng: &mut R, ) -> Result, InvalidHybridReportError> { - let mut out = Vec::with_capacity(usize::from(self.encrypted_len())); - self.encrypt_to(key_id, key_registry, rng, &mut out)?; - debug_assert_eq!(out.len(), usize::from(self.encrypted_len())); + let mut out = Vec::with_capacity(usize::from(self.ciphertext_len() + info.byte_len())); + self.encrypt_to(key_id, key_registry, info, rng, &mut out)?; + debug_assert_eq!(out.len(), usize::from(self.encrypted_len() + info.byte_len())); Ok(out) } @@ -369,6 +368,7 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridConversionInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { @@ -381,8 +381,8 @@ where .serialize(GenericArray::from_mut_slice(&mut plaintext_btt[..])); let pk = key_registry.public_key(key_id).ok_or(CryptError::NoSuchKey(key_id))?; - let info_enc_bytes = self.info.to_enc_bytes(); - let info_bytes = self.info.to_bytes(); + let info_enc_bytes = info.to_enc_bytes(); + let info_bytes = info.to_bytes(); let (encap_key_mk, ciphertext_mk, tag_mk) = seal_in_place( pk, @@ -452,19 +452,20 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { match self { HybridReport::Impression(impression_report) => { - out.put_u16_le(self.encrypted_len()); + out.put_u16_le(self.encrypted_len() + info.impression.byte_len()); out.put_u8(HybridEventType::Impression as u8); - impression_report.encrypt_to(key_id, key_registry, rng, out) + impression_report.encrypt_to(key_id, key_registry, &info.impression, rng, out) }, HybridReport::Conversion(conversion_report) => { - out.put_u16_le(self.encrypted_len()); + out.put_u16_le(self.encrypted_len() + info.conversion.byte_len()); out.put_u8(HybridEventType::Conversion as u8); - conversion_report.encrypt_to(key_id, key_registry, rng, out) + conversion_report.encrypt_to(key_id, key_registry, &info.conversion, rng, out) }, } } @@ -475,10 +476,11 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridInfo, rng: &mut R, ) -> Result, InvalidHybridReportError> { let mut buf = Vec::new(); - self.encrypt_to(key_id, key_registry, rng, &mut buf)?; + self.encrypt_to(key_id, key_registry, info, rng, &mut buf)?; Ok(buf) } @@ -488,17 +490,18 @@ where &self, key_id: KeyIdentifier, key_registry: &impl PublicKeyRegistry, + info: &HybridInfo, rng: &mut R, out: &mut B, ) -> Result<(), InvalidHybridReportError> { match self { HybridReport::Impression(impression_report) =>{ out.put_u8(HybridEventType::Impression as u8); - impression_report.encrypt_to(key_id, key_registry, rng, out) + impression_report.encrypt_to(key_id, key_registry, &info.impression, rng, out) }, HybridReport::Conversion(conversion_report) => { out.put_u8(HybridEventType::Conversion as u8); - conversion_report.encrypt_to(key_id, key_registry, rng, out) + conversion_report.encrypt_to(key_id, key_registry, &info.conversion, rng, out) }, } } @@ -574,10 +577,10 @@ where /// ## Panics /// Should not panic. Only panics if a `Report` constructor failed to validate the /// contents properly, which would be a bug. - pub fn decrypt( + pub fn decrypt_and_return_info( &self, key_registry: &P, - ) -> Result, InvalidHybridReportError> { + ) -> Result<(HybridImpressionReport, HybridImpressionInfo), InvalidHybridReportError> { type CTMKLength = Sum< as Serializable>::Size, TagSize>; type CTBTTLength = < as Serializable>::Size as Add>::Output; @@ -598,16 +601,34 @@ where let plaintext_btt = open_in_place(sk, self.encap_key_btt(), &mut ct_btt, &info_enc_bytes)?; - Ok(HybridImpressionReport:: { - match_key: Replicated::::deserialize_infallible(GenericArray::from_slice( - plaintext_mk, - )), - breakdown_key: Replicated::::deserialize(GenericArray::from_slice(plaintext_btt)) + Ok(( + HybridImpressionReport:: { + match_key: Replicated::::deserialize_infallible(GenericArray::from_slice( + plaintext_mk, + )), + breakdown_key: Replicated::::deserialize(GenericArray::from_slice( + plaintext_btt, + )) .map_err(|e| { - InvalidHybridReportError::DeserializationError("is_trigger", e.into()) - })?, + InvalidHybridReportError::DeserializationError("is_trigger", e.into()) + })?, + }, info, - }) + )) + } + + /// ## Errors + /// If the match key shares in the report cannot be decrypted (e.g. due to a + /// failure of the authenticated encryption). + /// ## Panics + /// Should not panic. Only panics if a `Report` constructor failed to validate the + /// contents properly, which would be a bug. + pub fn decrypt( + &self, + key_registry: &P, + ) -> Result, InvalidHybridReportError> { + let (report, _) = self.decrypt_and_return_info(key_registry)?; + Ok(report) } } @@ -679,10 +700,10 @@ where /// ## Panics /// Should not panic. Only panics if a `Report` constructor failed to validate the /// contents properly, which would be a bug. - pub fn decrypt( + pub fn decrypt_and_return_info( &self, key_registry: &P, - ) -> Result, InvalidHybridReportError> { + ) -> Result<(HybridConversionReport, HybridConversionInfo), InvalidHybridReportError> { type CTMKLength = Sum< as Serializable>::Size, TagSize>; type CTBTTLength = < as Serializable>::Size as Add>::Output; @@ -702,15 +723,32 @@ where GenericArray::from_slice(self.btt_ciphertext()).clone(); let plaintext_btt = open_in_place(sk, self.encap_key_btt(), &mut ct_btt, &info_enc_bytes)?; - Ok(HybridConversionReport:: { - match_key: Replicated::::deserialize_infallible(GenericArray::from_slice( - plaintext_mk, - )), - value: Replicated::::deserialize(GenericArray::from_slice(plaintext_btt)).map_err( - |e| InvalidHybridReportError::DeserializationError("trigger_value", e.into()), - )?, + Ok(( + HybridConversionReport:: { + match_key: Replicated::::deserialize_infallible(GenericArray::from_slice( + plaintext_mk, + )), + value: Replicated::::deserialize(GenericArray::from_slice(plaintext_btt)) + .map_err(|e| { + InvalidHybridReportError::DeserializationError("trigger_value", e.into()) + })?, + }, info, - }) + )) + } + + /// ## Errors + /// If the match key shares in the report cannot be decrypted (e.g. due to a + /// failure of the authenticated encryption). + /// ## Panics + /// Should not panic. Only panics if a `Report` constructor failed to validate the + /// contents properly, which would be a bug. + pub fn decrypt( + &self, + key_registry: &P, + ) -> Result, InvalidHybridReportError> { + let (report, _) = self.decrypt_and_return_info(key_registry)?; + Ok(report) } } @@ -1076,6 +1114,28 @@ where } } } + /// ## Errors + /// If the match key shares in the report cannot be decrypted (e.g. due to a + /// failure of the authenticated encryption). + /// ## Panics + /// Should not panic. Only panics if a `Report` constructor failed to validate the + /// contents properly, which would be a bug. + pub fn decrypt_and_return_info( + &self, + key_registry: &P, + ) -> Result<(HybridReport, HybridInfo), InvalidHybridReportError> { + match self { + EncryptedHybridReport::Impression(impression_report) => { + let (report, info) = impression_report.decrypt_and_return_info(key_registry)?; + Ok((HybridReport::Impression(report), info.into())) + } + EncryptedHybridReport::Conversion(conversion_report) => { + let (report, info) = conversion_report.decrypt_and_return_info(key_registry)?; + Ok((HybridReport::Conversion(report), info.into())) + } + } + } + /// ## Errors /// If the match key shares in the report cannot be decrypted (e.g. due to a /// failure of the authenticated encryption). @@ -1249,7 +1309,7 @@ mod test { hpke::{KeyPair, KeyRegistry}, report::{ hybrid::{EncryptedHybridConversionReport, HybridEventType}, - hybrid_info::{HybridConversionInfo, HybridImpressionInfo}, + hybrid_info::{HybridConversionInfo, HybridImpressionInfo, HybridInfo}, }, secret_sharing::replicated::{ semi_honest::{AdditiveShare as Replicated, AdditiveShare}, @@ -1267,21 +1327,12 @@ mod test { HybridReport::Impression(HybridImpressionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), breakdown_key: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridImpressionInfo::new(0), }) } HybridEventType::Conversion => { HybridReport::Conversion(HybridConversionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), value: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridConversionInfo::new( - 0, - "https://www.example2.com", - rng.gen(), - 0.0, - 0.0, - ) - .unwrap(), }) } } @@ -1308,8 +1359,6 @@ mod test { let conversion_report = HybridConversionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), value: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridConversionInfo::new(0, "https://www.example2.com", 1_234_567, 0.0, 0.0) - .unwrap(), }; let indistinguishable_report: IndistinguishableHybridReport = conversion_report.clone().into(); @@ -1339,7 +1388,6 @@ mod test { let impression_report = HybridImpressionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), breakdown_key: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridImpressionInfo::new(0), }; let indistinguishable_report: IndistinguishableHybridReport = impression_report.clone().into(); @@ -1389,7 +1437,6 @@ mod test { let hybrid_impression_report = HybridImpressionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), breakdown_key: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridImpressionInfo::new(0), }; let mut hybrid_impression_report_bytes = Vec::with_capacity(HybridImpressionReport::::serialized_len()); @@ -1408,8 +1455,6 @@ mod test { let hybrid_conversion_report = HybridConversionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), value: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridConversionInfo::new(0, "https://www.example2.com", 1_234_567, 0.0, 0.0) - .unwrap(), }; let mut hybrid_conversion_report_bytes = Vec::with_capacity(HybridImpressionReport::::serialized_len()); @@ -1431,11 +1476,11 @@ mod test { let hybrid_impression_report = HybridImpressionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), breakdown_key: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridImpressionInfo::new(key_id), }; + let impression_info = HybridImpressionInfo::new(key_id); let enc_report_bytes = hybrid_impression_report - .encrypt(key_id, &key_registry, &mut rng) + .encrypt(key_id, &key_registry, &impression_info, &mut rng) .unwrap(); let enc_report = @@ -1454,14 +1499,15 @@ mod test { let hybrid_conversion_report = HybridConversionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), value: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridConversionInfo::new(0, "meta.com", 1_729_707_432, 5.0, 1.1).unwrap(), }; let key_registry = KeyRegistry::::random(1, &mut rng); let key_id = 0; + let conversion_info = + HybridConversionInfo::new(0, "meta.com", 1_729_707_432, 5.0, 1.1).unwrap(); let enc_report_bytes = hybrid_conversion_report - .encrypt(key_id, &key_registry, &mut rng) + .encrypt(key_id, &key_registry, &conversion_info, &mut rng) .unwrap(); let enc_report = @@ -1482,9 +1528,10 @@ mod test { let key_registry = KeyRegistry::::random(1, &mut rng); let key_id = 0; + let info = HybridInfo::new(0, "meta.com", 1_729_707_432, 5.0, 1.1).unwrap(); let enc_report_bytes = hybrid_report - .encrypt(key_id, &key_registry, &mut rng) + .encrypt(key_id, &key_registry, &info, &mut rng) .unwrap(); let enc_report = @@ -1501,14 +1548,15 @@ mod test { let hybrid_conversion_report = HybridConversionReport:: { match_key: AdditiveShare::new(rng.gen(), rng.gen()), value: AdditiveShare::new(rng.gen(), rng.gen()), - info: HybridConversionInfo::new(0, "meta.com", 1_729_707_432, 5.0, 1.1).unwrap(), }; let key_registry = KeyRegistry::::random(1, &mut rng); let key_id = 0; + let conversion_info = + HybridConversionInfo::new(0, "meta.com", 1_729_707_432, 5.0, 1.1).unwrap(); let enc_report_bytes = hybrid_conversion_report - .encrypt(key_id, &key_registry, &mut rng) + .encrypt(key_id, &key_registry, &conversion_info, &mut rng) .unwrap(); let mut enc_report_bytes2 = enc_report_bytes.clone(); diff --git a/ipa-core/src/report/hybrid_info.rs b/ipa-core/src/report/hybrid_info.rs index 31da9f68e..51ffb71f5 100644 --- a/ipa-core/src/report/hybrid_info.rs +++ b/ipa-core/src/report/hybrid_info.rs @@ -18,10 +18,12 @@ impl HybridImpressionInfo { } #[must_use] - pub fn byte_len(&self) -> usize { + /// # Panics + /// If report length does not fit in `u16`. + pub fn byte_len(&self) -> u16 { let out_len = std::mem::size_of_val(&self.key_id); debug_assert_eq!(out_len, self.to_bytes().len(), "Serialization length estimation is incorrect and leads to extra allocation or wasted memory"); - out_len + out_len.try_into().unwrap() } // Converts this instance into an owned byte slice. DO NOT USE AS INPUT TO HPKE @@ -99,7 +101,9 @@ impl HybridConversionInfo { } #[must_use] - pub fn byte_len(&self) -> usize { + /// # Panics + /// If report length does not fit in `u16`. + pub fn byte_len(&self) -> u16 { let out_len = std::mem::size_of_val(&self.key_id) + 1 // delimiter + self.conversion_site_domain.len() @@ -107,7 +111,7 @@ impl HybridConversionInfo { + std::mem::size_of_val(&self.epsilon) + std::mem::size_of_val(&self.sensitivity); debug_assert_eq!(out_len, self.to_bytes().len(), "Serialization length estimation is incorrect and leads to extra allocation or wasted memory"); - out_len + out_len.try_into().unwrap() } // Converts this instance into an owned byte slice. DO NOT USE AS INPUT TO HPKE @@ -250,6 +254,34 @@ impl HybridInfo { } } +impl From for HybridInfo { + fn from(impression: HybridImpressionInfo) -> Self { + let conversion = HybridConversionInfo { + key_id: impression.key_id, + conversion_site_domain: String::new(), + timestamp: 0, + epsilon: 0.0, + sensitivity: 0.0, + }; + Self { + impression, + conversion, + } + } +} + +impl From for HybridInfo { + fn from(conversion: HybridConversionInfo) -> Self { + let impression = HybridImpressionInfo { + key_id: conversion.key_id, + }; + Self { + impression, + conversion, + } + } +} + #[cfg(all(test, unit_test))] mod test { use super::*; diff --git a/ipa-core/src/test_fixture/hybrid.rs b/ipa-core/src/test_fixture/hybrid.rs index 335c8bd5f..2a4681309 100644 --- a/ipa-core/src/test_fixture/hybrid.rs +++ b/ipa-core/src/test_fixture/hybrid.rs @@ -11,7 +11,7 @@ use crate::{ AggregateableHybridReport, HybridConversionReport, HybridImpressionReport, HybridReport, IndistinguishableHybridReport, KeyIdentifier, }, - hybrid_info::{HybridConversionInfo, HybridImpressionInfo}, + hybrid_info::{HybridConversionInfo, HybridImpressionInfo, HybridInfo}, }, secret_sharing::{replicated::semi_honest::AdditiveShare as Replicated, IntoShares}, test_fixture::sharing::Reconstruct, @@ -133,7 +133,7 @@ where TestHybridRecord::TestImpression { match_key, breakdown_key, - key_id, + key_id: _, } => { let ba_match_key = BA64::try_from(u128::from(match_key)) .unwrap() @@ -146,7 +146,7 @@ where HybridReport::Impression::(HybridImpressionReport { match_key: match_key_share, breakdown_key: breakdown_key_share, - info: HybridImpressionInfo::new(key_id), + //info: HybridImpressionInfo::new(key_id), }) }) .collect::>() @@ -156,11 +156,11 @@ where TestHybridRecord::TestConversion { match_key, value, - key_id, - conversion_site_domain, - timestamp, - epsilon, - sensitivity, + key_id: _, + conversion_site_domain: _, + timestamp: _, + epsilon: _, + sensitivity: _, } => { let ba_match_key = BA64::try_from(u128::from(match_key)) .unwrap() @@ -171,14 +171,6 @@ where HybridReport::Conversion::(HybridConversionReport { match_key: match_key_share, value: value_share, - info: HybridConversionInfo::new( - key_id, - &conversion_site_domain, - timestamp, - epsilon, - sensitivity, - ) - .unwrap(), }) }) .collect::>() @@ -189,6 +181,59 @@ where } } +impl TestHybridRecord { + #[must_use] + pub fn create_hybrid_info(&self) -> HybridInfo { + match self { + TestHybridRecord::TestImpression { + match_key: _, + breakdown_key: _, + key_id, + } => { + let conversion = HybridConversionInfo { + key_id: *key_id, + conversion_site_domain: String::new(), + timestamp: 0, + epsilon: 0.0, + sensitivity: 0.0, + }; + let impression = HybridImpressionInfo { key_id: *key_id }; + HybridInfo { + impression, + conversion, + } + } + TestHybridRecord::TestConversion { + match_key: _, + value: _, + key_id, + conversion_site_domain, + timestamp, + epsilon, + sensitivity, + } => { + let key_id = *key_id; + let conversion_site_domain = conversion_site_domain.to_string(); + let timestamp = *timestamp; + let epsilon = *epsilon; + let sensitivity = *sensitivity; + let impression = HybridImpressionInfo { key_id }; + let conversion = HybridConversionInfo { + key_id, + conversion_site_domain, + timestamp, + epsilon, + sensitivity, + }; + HybridInfo { + impression, + conversion, + } + } + } + } +} + enum MatchEntry { SingleImpression { breakdown_key: u32 }, SingleConversion { value: u32 },