Skip to content

Commit 06f2f22

Browse files
committed
Fix json encryption
1 parent de88545 commit 06f2f22

File tree

4 files changed

+98
-37
lines changed

4 files changed

+98
-37
lines changed

benches/quick.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ fn bench_json_roundtrip(c: &mut Criterion) {
315315
.collect();
316316
let encrypted: Vec<_> = json_values
317317
.iter()
318-
.map(|j| client_a.encrypt_json(j, rng))
318+
.map(|j| client_a.encrypt(j, rng))
319319
.collect();
320320

321321
c.bench_function("json_roundtrip_100", |b| {
@@ -353,7 +353,7 @@ fn bench_json_roundtrip_batch(c: &mut Criterion) {
353353
.collect();
354354
let encrypted_base: Vec<_> = json_values
355355
.iter()
356-
.map(|j| client_a.encrypt_json(j, rng_setup))
356+
.map(|j| client_a.encrypt(j, rng_setup))
357357
.collect();
358358

359359
c.bench_function("json_roundtrip_batch_100", |b| {

src/lib/client/types.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -60,40 +60,6 @@ impl Client {
6060
encrypted.decrypt(self.keys.get_key())
6161
}
6262

63-
/// Encrypt a JSON value with the session keys.
64-
/// JSON values require the full SessionKeys struct, not individual keys.
65-
#[cfg(feature = "json")]
66-
pub fn encrypt_json<R>(
67-
&self,
68-
message: &crate::data::json::data::PEPJSONValue,
69-
rng: &mut R,
70-
) -> crate::data::json::data::EncryptedPEPJSONValue
71-
where
72-
R: RngCore + CryptoRng,
73-
{
74-
message.encrypt(&self.keys, rng)
75-
}
76-
77-
/// Decrypt a JSON value with the session keys.
78-
/// JSON values require the full SessionKeys struct, not individual keys.
79-
#[cfg(all(feature = "json", feature = "elgamal3"))]
80-
pub fn decrypt_json(
81-
&self,
82-
encrypted: &crate::data::json::data::EncryptedPEPJSONValue,
83-
) -> Option<crate::data::json::data::PEPJSONValue> {
84-
encrypted.decrypt(&self.keys)
85-
}
86-
87-
/// Decrypt a JSON value with the session keys.
88-
/// JSON values require the full SessionKeys struct, not individual keys.
89-
#[cfg(all(feature = "json", not(feature = "elgamal3")))]
90-
pub fn decrypt_json(
91-
&self,
92-
encrypted: &crate::data::json::data::EncryptedPEPJSONValue,
93-
) -> crate::data::json::data::PEPJSONValue {
94-
encrypted.decrypt(&self.keys)
95-
}
96-
9763
/// Encrypt a batch of messages with the appropriate session public key.
9864
/// Automatically selects the correct key (pseudonym or attribute) based on the message type.
9965
#[cfg(feature = "batch")]

src/lib/keys/traits.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ impl KeyProvider<AttributeSessionSecretKey> for SessionKeys {
145145
}
146146
}
147147

148+
impl KeyProvider<SessionKeys> for SessionKeys {
149+
fn get_key(&self) -> &SessionKeys {
150+
self
151+
}
152+
}
153+
148154
impl KeyProvider<PseudonymGlobalPublicKey> for GlobalPublicKeys {
149155
fn get_key(&self) -> &PseudonymGlobalPublicKey {
150156
&self.pseudonym
@@ -156,3 +162,9 @@ impl KeyProvider<AttributeGlobalPublicKey> for GlobalPublicKeys {
156162
&self.attribute
157163
}
158164
}
165+
166+
impl KeyProvider<GlobalPublicKeys> for GlobalPublicKeys {
167+
fn get_key(&self) -> &GlobalPublicKeys {
168+
self
169+
}
170+
}

tests/json.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ fn test_json_transcryption_with_builder() {
131131
#[cfg(feature = "elgamal3")]
132132
let decrypted_transcrypted = transcrypted.decrypt(&session_keys).unwrap();
133133
#[cfg(not(feature = "elgamal3"))]
134-
let decrypted_transcrypted = transcrypted.decrypt(&session_keys);
134+
let decrypted_transcrypted = decrypt(&transcrypted, &session_keys);
135135
let json_transcrypted = decrypted_transcrypted
136136
.to_value()
137137
.expect("Should convert to JSON");
@@ -329,3 +329,86 @@ fn test_json_batch_transcryption_different_structures() {
329329
_ => panic!("Expected InconsistentStructure error"),
330330
}
331331
}
332+
333+
#[test]
334+
fn test_json_transcryption_with_client_and_transcryptor() {
335+
let mut rng = rand::rng();
336+
337+
// Setup keys and secrets
338+
let (_global_public, global_secret) = make_global_keys(&mut rng);
339+
let pseudo_secret = PseudonymizationSecret::from("pseudo-secret".as_bytes().to_vec());
340+
let enc_secret = EncryptionSecret::from("encryption-secret".as_bytes().to_vec());
341+
342+
let domain_a = PseudonymizationDomain::from("hospital-a");
343+
let domain_b = PseudonymizationDomain::from("hospital-b");
344+
let session = EncryptionContext::from("session-1");
345+
346+
let session_keys = make_session_keys(&global_secret, &session, &enc_secret);
347+
348+
// Create client and transcryptor
349+
let client = libpep::client::Client::new(session_keys);
350+
let transcryptor =
351+
libpep::transcryptor::Transcryptor::new(pseudo_secret.clone(), enc_secret.clone());
352+
353+
// Create patient record JSON data
354+
let patient_data = json!({
355+
"patient_id": "patient-54321",
356+
"name": "John Doe",
357+
"diagnosis": "Healthy",
358+
"temperature": 36.6
359+
});
360+
361+
// Convert to PEP JSON, marking "patient_id" as a pseudonym field
362+
let patient_record = PEPJSONBuilder::from_json(&patient_data, &["patient_id"])
363+
.expect("Should create PEP JSON from existing JSON")
364+
.build();
365+
366+
// Encrypt using the client
367+
let encrypted = client.encrypt(&patient_record, &mut rng);
368+
369+
// Decrypt to verify original
370+
#[cfg(feature = "elgamal3")]
371+
let decrypted_original = client.decrypt(&encrypted).unwrap();
372+
#[cfg(not(feature = "elgamal3"))]
373+
let decrypted_original = client.decrypt(&encrypted);
374+
375+
let json_original = decrypted_original
376+
.to_value()
377+
.expect("Should convert to JSON");
378+
assert_eq!(json_original["patient_id"], "patient-54321");
379+
assert_eq!(json_original["name"], "John Doe");
380+
assert_eq!(json_original["diagnosis"], "Healthy");
381+
assert_eq!(json_original["temperature"].as_f64().unwrap(), 36.6);
382+
383+
// Transcrypt from hospital A to hospital B using the transcryptor
384+
let transcryption_info =
385+
transcryptor.transcryption_info(&domain_a, &domain_b, &session, &session);
386+
387+
let transcrypted = transcryptor.transcrypt(&encrypted, &transcryption_info);
388+
389+
// Verify that the encrypted structures are different after transcryption
390+
assert_ne!(
391+
format!("{:?}", encrypted),
392+
format!("{:?}", transcrypted),
393+
"Encrypted values should be different after transcryption"
394+
);
395+
396+
// Decrypt transcrypted data
397+
#[cfg(feature = "elgamal3")]
398+
let decrypted_transcrypted = client.decrypt(&transcrypted).unwrap();
399+
#[cfg(not(feature = "elgamal3"))]
400+
let decrypted_transcrypted = client.decrypt(&transcrypted);
401+
402+
let json_transcrypted = decrypted_transcrypted
403+
.to_value()
404+
.expect("Should convert to JSON");
405+
406+
// Attributes should remain the same, but pseudonym should be different
407+
assert_eq!(json_transcrypted["name"], "John Doe");
408+
assert_eq!(json_transcrypted["diagnosis"], "Healthy");
409+
assert_eq!(json_transcrypted["temperature"].as_f64().unwrap(), 36.6);
410+
assert_ne!(
411+
json_transcrypted["patient_id"], "patient-54321",
412+
"Pseudonym should be different after cross-domain transcryption"
413+
);
414+
}

0 commit comments

Comments
 (0)