1use crate::arithmetic::group_elements::GroupElement;
5use crate::arithmetic::scalars::ScalarNonZero;
6use crate::core::elgamal::{ElGamal, ELGAMAL_LENGTH};
7use crate::data::traits::{Encryptable, Encrypted, Pseudonymizable, Rekeyable, Transcryptable};
8use crate::factors::TranscryptionInfo;
9use crate::factors::{
10 AttributeRekeyInfo, PseudonymRekeyInfo, PseudonymizationInfo, RerandomizeFactor,
11};
12use crate::keys::*;
13use derive_more::{Deref, From};
14use rand_core::{CryptoRng, RngCore};
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde", serde(transparent))]
22#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
23pub struct Pseudonym {
24 pub value: GroupElement,
25}
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[cfg_attr(feature = "serde", serde(transparent))]
30#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
31pub struct Attribute {
32 pub value: GroupElement,
33}
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[cfg_attr(feature = "serde", serde(transparent))]
37#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
38pub struct EncryptedPseudonym {
39 pub value: ElGamal,
40}
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[cfg_attr(feature = "serde", serde(transparent))]
44#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
45pub struct EncryptedAttribute {
46 pub value: ElGamal,
47}
48
49pub trait ElGamalEncrypted: Encrypted {
52 type UnencryptedType: ElGamalEncryptable<EncryptedType = Self>;
53
54 fn value(&self) -> &ElGamal;
56 fn from_value(value: ElGamal) -> Self
58 where
59 Self: Sized;
60
61 fn to_bytes(&self) -> [u8; ELGAMAL_LENGTH] {
63 self.value().to_bytes()
64 }
65
66 fn from_bytes(bytes: &[u8; ELGAMAL_LENGTH]) -> Option<Self>
68 where
69 Self: Sized,
70 {
71 ElGamal::from_bytes(bytes).map(Self::from_value)
72 }
73
74 fn from_slice(slice: &[u8]) -> Option<Self>
76 where
77 Self: Sized,
78 {
79 ElGamal::from_slice(slice).map(Self::from_value)
80 }
81
82 fn to_base64(&self) -> String {
84 self.value().to_base64()
85 }
86
87 fn from_base64(s: &str) -> Option<Self>
89 where
90 Self: Sized,
91 {
92 ElGamal::from_base64(s).map(Self::from_value)
93 }
94}
95
96pub trait ElGamalEncryptable: Encryptable {
99 fn value(&self) -> &GroupElement;
101 fn from_value(value: GroupElement) -> Self
103 where
104 Self: Sized;
105
106 fn from_point(value: GroupElement) -> Self
108 where
109 Self: Sized,
110 {
111 Self::from_value(value)
112 }
113
114 fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self
116 where
117 Self: Sized,
118 {
119 Self::from_point(GroupElement::random(rng))
120 }
121 fn to_bytes(&self) -> [u8; 32] {
124 self.value().to_bytes()
125 }
126 fn to_hex(&self) -> String {
128 self.value().to_hex()
129 }
130 fn from_bytes(bytes: &[u8; 32]) -> Option<Self>
133 where
134 Self: Sized,
135 {
136 GroupElement::from_bytes(bytes).map(Self::from_point)
137 }
138 fn from_slice(slice: &[u8]) -> Option<Self>
141 where
142 Self: Sized,
143 {
144 GroupElement::from_slice(slice).map(Self::from_point)
145 }
146 fn from_hex(hex: &str) -> Option<Self>
149 where
150 Self: Sized,
151 {
152 GroupElement::from_hex(hex).map(Self::from_point)
153 }
154 fn from_hash(hash: &[u8; 64]) -> Self
157 where
158 Self: Sized,
159 {
160 Self::from_point(GroupElement::from_hash(hash))
161 }
162 fn from_lizard(data: &[u8; 16]) -> Self
167 where
168 Self: Sized,
169 {
170 Self::from_point(GroupElement::from_lizard(data))
171 }
172 fn to_lizard(&self) -> Option<[u8; 16]> {
178 self.value().to_lizard()
179 }
180}
181
182impl Encryptable for Pseudonym {
183 type EncryptedType = EncryptedPseudonym;
184 type PublicKeyType = PseudonymSessionPublicKey;
185 #[cfg(feature = "offline")]
186 type GlobalPublicKeyType = PseudonymGlobalPublicKey;
187
188 fn encrypt<R>(&self, public_key: &Self::PublicKeyType, rng: &mut R) -> Self::EncryptedType
189 where
190 R: RngCore + CryptoRng,
191 {
192 EncryptedPseudonym::from_value(crate::core::elgamal::encrypt(
193 self.value(),
194 public_key.value(),
195 rng,
196 ))
197 }
198
199 #[cfg(feature = "offline")]
200 fn encrypt_global<R>(
201 &self,
202 public_key: &Self::GlobalPublicKeyType,
203 rng: &mut R,
204 ) -> Self::EncryptedType
205 where
206 R: RngCore + CryptoRng,
207 {
208 EncryptedPseudonym::from_value(crate::core::elgamal::encrypt(
209 self.value(),
210 public_key.value(),
211 rng,
212 ))
213 }
214}
215
216impl Encryptable for Attribute {
217 type EncryptedType = EncryptedAttribute;
218 type PublicKeyType = AttributeSessionPublicKey;
219 #[cfg(feature = "offline")]
220 type GlobalPublicKeyType = AttributeGlobalPublicKey;
221
222 fn encrypt<R>(&self, public_key: &Self::PublicKeyType, rng: &mut R) -> Self::EncryptedType
223 where
224 R: RngCore + CryptoRng,
225 {
226 EncryptedAttribute::from_value(crate::core::elgamal::encrypt(
227 self.value(),
228 public_key.value(),
229 rng,
230 ))
231 }
232
233 #[cfg(feature = "offline")]
234 fn encrypt_global<R>(
235 &self,
236 public_key: &Self::GlobalPublicKeyType,
237 rng: &mut R,
238 ) -> Self::EncryptedType
239 where
240 R: RngCore + CryptoRng,
241 {
242 EncryptedAttribute::from_value(crate::core::elgamal::encrypt(
243 self.value(),
244 public_key.value(),
245 rng,
246 ))
247 }
248}
249
250impl ElGamalEncryptable for Pseudonym {
251 fn value(&self) -> &GroupElement {
252 &self.value
253 }
254 fn from_value(value: GroupElement) -> Self
255 where
256 Self: Sized,
257 {
258 Self { value }
259 }
260}
261
262impl ElGamalEncryptable for Attribute {
263 fn value(&self) -> &GroupElement {
264 &self.value
265 }
266 fn from_value(value: GroupElement) -> Self
267 where
268 Self: Sized,
269 {
270 Self { value }
271 }
272}
273
274impl Encrypted for EncryptedPseudonym {
275 type UnencryptedType = Pseudonym;
276 type SecretKeyType = PseudonymSessionSecretKey;
277 #[cfg(all(feature = "offline", feature = "insecure"))]
278 type GlobalSecretKeyType = PseudonymGlobalSecretKey;
279
280 #[cfg(feature = "elgamal3")]
281 fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Option<Self::UnencryptedType> {
282 crate::core::elgamal::decrypt(self.value(), secret_key.value()).map(Pseudonym::from_value)
283 }
284
285 #[cfg(not(feature = "elgamal3"))]
286 fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Self::UnencryptedType {
287 Pseudonym::from_value(crate::core::elgamal::decrypt(
288 self.value(),
289 secret_key.value(),
290 ))
291 }
292
293 #[cfg(all(feature = "offline", feature = "insecure", feature = "elgamal3"))]
294 fn decrypt_global(
295 &self,
296 secret_key: &Self::GlobalSecretKeyType,
297 ) -> Option<Self::UnencryptedType> {
298 crate::core::elgamal::decrypt(self.value(), secret_key.value()).map(Pseudonym::from_value)
299 }
300
301 #[cfg(all(feature = "offline", feature = "insecure", not(feature = "elgamal3")))]
302 fn decrypt_global(&self, secret_key: &Self::GlobalSecretKeyType) -> Self::UnencryptedType {
303 Pseudonym::from_value(crate::core::elgamal::decrypt(
304 self.value(),
305 secret_key.value(),
306 ))
307 }
308
309 #[cfg(feature = "elgamal3")]
310 fn rerandomize<R>(&self, rng: &mut R) -> Self
311 where
312 R: RngCore + CryptoRng,
313 {
314 let r = ScalarNonZero::random(rng);
315 self.rerandomize_known(&RerandomizeFactor(r))
316 }
317
318 #[cfg(not(feature = "elgamal3"))]
319 fn rerandomize<R>(
320 &self,
321 public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
322 rng: &mut R,
323 ) -> Self
324 where
325 R: RngCore + CryptoRng,
326 {
327 let r = ScalarNonZero::random(rng);
328 self.rerandomize_known(public_key, &RerandomizeFactor(r))
329 }
330
331 #[cfg(feature = "elgamal3")]
332 fn rerandomize_known(&self, factor: &RerandomizeFactor) -> Self {
333 EncryptedPseudonym::from_value(crate::core::primitives::rerandomize(
334 self.value(),
335 &factor.0,
336 ))
337 }
338
339 #[cfg(not(feature = "elgamal3"))]
340 fn rerandomize_known(
341 &self,
342 public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
343 factor: &RerandomizeFactor,
344 ) -> Self {
345 EncryptedPseudonym::from_value(crate::core::primitives::rerandomize(
346 self.value(),
347 public_key.value(),
348 &factor.0,
349 ))
350 }
351}
352
353impl Encrypted for EncryptedAttribute {
354 type UnencryptedType = Attribute;
355 type SecretKeyType = AttributeSessionSecretKey;
356 #[cfg(all(feature = "offline", feature = "insecure"))]
357 type GlobalSecretKeyType = AttributeGlobalSecretKey;
358
359 #[cfg(feature = "elgamal3")]
360 fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Option<Self::UnencryptedType> {
361 crate::core::elgamal::decrypt(self.value(), secret_key.value()).map(Attribute::from_value)
362 }
363
364 #[cfg(not(feature = "elgamal3"))]
365 fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Self::UnencryptedType {
366 Attribute::from_value(crate::core::elgamal::decrypt(
367 self.value(),
368 secret_key.value(),
369 ))
370 }
371
372 #[cfg(all(feature = "offline", feature = "insecure", feature = "elgamal3"))]
373 fn decrypt_global(
374 &self,
375 secret_key: &Self::GlobalSecretKeyType,
376 ) -> Option<Self::UnencryptedType> {
377 crate::core::elgamal::decrypt(self.value(), secret_key.value()).map(Attribute::from_value)
378 }
379
380 #[cfg(all(feature = "offline", feature = "insecure", not(feature = "elgamal3")))]
381 fn decrypt_global(&self, secret_key: &Self::GlobalSecretKeyType) -> Self::UnencryptedType {
382 Attribute::from_value(crate::core::elgamal::decrypt(
383 self.value(),
384 secret_key.value(),
385 ))
386 }
387
388 #[cfg(feature = "elgamal3")]
389 fn rerandomize<R>(&self, rng: &mut R) -> Self
390 where
391 R: RngCore + CryptoRng,
392 {
393 let r = ScalarNonZero::random(rng);
394 self.rerandomize_known(&RerandomizeFactor(r))
395 }
396
397 #[cfg(not(feature = "elgamal3"))]
398 fn rerandomize<R>(
399 &self,
400 public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
401 rng: &mut R,
402 ) -> Self
403 where
404 R: RngCore + CryptoRng,
405 {
406 let r = ScalarNonZero::random(rng);
407 self.rerandomize_known(public_key, &RerandomizeFactor(r))
408 }
409
410 #[cfg(feature = "elgamal3")]
411 fn rerandomize_known(&self, factor: &RerandomizeFactor) -> Self {
412 EncryptedAttribute::from_value(crate::core::primitives::rerandomize(
413 self.value(),
414 &factor.0,
415 ))
416 }
417
418 #[cfg(not(feature = "elgamal3"))]
419 fn rerandomize_known(
420 &self,
421 public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
422 factor: &RerandomizeFactor,
423 ) -> Self {
424 EncryptedAttribute::from_value(crate::core::primitives::rerandomize(
425 self.value(),
426 public_key.value(),
427 &factor.0,
428 ))
429 }
430}
431
432impl ElGamalEncrypted for EncryptedPseudonym {
433 type UnencryptedType = Pseudonym;
434
435 fn value(&self) -> &ElGamal {
436 &self.value
437 }
438 fn from_value(value: ElGamal) -> Self
439 where
440 Self: Sized,
441 {
442 Self { value }
443 }
444}
445impl ElGamalEncrypted for EncryptedAttribute {
446 type UnencryptedType = Attribute;
447
448 fn value(&self) -> &ElGamal {
449 &self.value
450 }
451 fn from_value(value: ElGamal) -> Self
452 where
453 Self: Sized,
454 {
455 Self { value }
456 }
457}
458
459impl Pseudonymizable for EncryptedPseudonym {
462 fn pseudonymize(&self, info: &PseudonymizationInfo) -> Self {
463 EncryptedPseudonym::from_value(crate::core::primitives::rsk(
464 self.value(),
465 &info.s.0,
466 &info.k.0,
467 ))
468 }
469}
470
471impl Rekeyable for EncryptedPseudonym {
472 type RekeyInfo = PseudonymRekeyInfo;
473
474 fn rekey(&self, info: &Self::RekeyInfo) -> Self {
475 EncryptedPseudonym::from_value(crate::core::primitives::rekey(self.value(), &info.0))
476 }
477}
478
479impl Rekeyable for EncryptedAttribute {
480 type RekeyInfo = AttributeRekeyInfo;
481
482 fn rekey(&self, info: &Self::RekeyInfo) -> Self {
483 EncryptedAttribute::from_value(crate::core::primitives::rekey(self.value(), &info.0))
484 }
485}
486
487impl Transcryptable for EncryptedPseudonym {
488 fn transcrypt(&self, info: &TranscryptionInfo) -> Self {
489 self.pseudonymize(&info.pseudonym)
490 }
491}
492
493impl Transcryptable for EncryptedAttribute {
494 fn transcrypt(&self, info: &TranscryptionInfo) -> Self {
495 self.rekey(&info.attribute)
496 }
497}
498#[cfg(feature = "batch")]
499impl crate::data::traits::HasStructure for EncryptedPseudonym {
500 type Structure = ();
501
502 fn structure(&self) -> Self::Structure {}
503}
504
505#[cfg(feature = "batch")]
506impl crate::data::traits::HasStructure for EncryptedAttribute {
507 type Structure = ();
508
509 fn structure(&self) -> Self::Structure {}
510}
511
512#[cfg(test)]
513#[allow(clippy::unwrap_used, clippy::expect_used)]
514mod tests {
515 use super::*;
516 use crate::client::{decrypt, encrypt};
517 use crate::factors::contexts::EncryptionContext;
518 use crate::factors::EncryptionSecret;
519
520 #[test]
521 fn pseudonym_encode_decode() {
522 let mut rng = rand::rng();
523 let original = Pseudonym::random(&mut rng);
524 let encoded = original.to_bytes();
525 let decoded = Pseudonym::from_bytes(&encoded).expect("decoding should succeed");
526 assert_eq!(decoded, original);
527 }
528
529 #[test]
530 fn attribute_encode_decode() {
531 let mut rng = rand::rng();
532 let original = Attribute::random(&mut rng);
533 let encoded = original.to_bytes();
534 let decoded = Attribute::from_bytes(&encoded).expect("decoding should succeed");
535 assert_eq!(decoded, original);
536 }
537
538 #[test]
539 fn pseudonym_from_lizard_roundtrip() {
540 let data = b"test identifier!";
541 let pseudonym = Pseudonym::from_lizard(data);
542 let decoded = pseudonym
543 .to_lizard()
544 .expect("lizard encoding should succeed");
545 assert_eq!(decoded, *data);
546 }
547
548 #[test]
549 fn attribute_from_lizard_roundtrip() {
550 let data = b"some attribute!!";
551 let attribute = Attribute::from_lizard(data);
552 let decoded = attribute
553 .to_lizard()
554 .expect("lizard encoding should succeed");
555 assert_eq!(decoded, *data);
556 }
557
558 #[test]
559 fn pseudonym_hex_roundtrip() {
560 let mut rng = rand::rng();
561 let original = Pseudonym::random(&mut rng);
562 let hex = original.to_hex();
563 let decoded = Pseudonym::from_hex(&hex).expect("hex decoding should succeed");
564 assert_eq!(decoded, original);
565 }
566
567 #[test]
568 fn encrypt_decrypt_pseudonym() {
569 let mut rng = rand::rng();
570 let (_, global_secret) = make_pseudonym_global_keys(&mut rng);
571 let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
572 let session = EncryptionContext::from("session-1");
573 let (session_public, session_secret) =
574 make_pseudonym_session_keys(&global_secret, &session, &enc_secret);
575
576 let original = Pseudonym::random(&mut rng);
577 let encrypted = encrypt(&original, &session_public, &mut rng);
578 #[cfg(feature = "elgamal3")]
579 let decrypted = decrypt(&encrypted, &session_secret).expect("decryption should succeed");
580 #[cfg(not(feature = "elgamal3"))]
581 let decrypted = decrypt(&encrypted, &session_secret);
582
583 assert_eq!(decrypted, original);
584 }
585
586 #[test]
587 fn encrypt_decrypt_attribute() {
588 let mut rng = rand::rng();
589 let (_, global_secret) = make_attribute_global_keys(&mut rng);
590 let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
591 let session = EncryptionContext::from("session-1");
592 let (session_public, session_secret) =
593 make_attribute_session_keys(&global_secret, &session, &enc_secret);
594
595 let original = Attribute::random(&mut rng);
596 let encrypted = encrypt(&original, &session_public, &mut rng);
597 #[cfg(feature = "elgamal3")]
598 let decrypted = decrypt(&encrypted, &session_secret).expect("decryption should succeed");
599 #[cfg(not(feature = "elgamal3"))]
600 let decrypted = decrypt(&encrypted, &session_secret);
601
602 assert_eq!(decrypted, original);
603 }
604
605 #[test]
606 fn encrypted_pseudonym_base64_roundtrip() {
607 let mut rng = rand::rng();
608 let (_, global_secret) = make_pseudonym_global_keys(&mut rng);
609 let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
610 let session = EncryptionContext::from("session-1");
611 let (session_public, _) =
612 make_pseudonym_session_keys(&global_secret, &session, &enc_secret);
613
614 let pseudonym = Pseudonym::random(&mut rng);
615 let encrypted = encrypt(&pseudonym, &session_public, &mut rng);
616 let base64 = encrypted.to_base64();
617 let decoded =
618 EncryptedPseudonym::from_base64(&base64).expect("base64 decoding should succeed");
619
620 assert_eq!(decoded, encrypted);
621 }
622
623 #[test]
624 fn encrypted_attribute_serde_json() {
625 let mut rng = rand::rng();
626 let (_, global_secret) = make_attribute_global_keys(&mut rng);
627 let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
628 let session = EncryptionContext::from("session-1");
629 let (session_public, _) =
630 make_attribute_session_keys(&global_secret, &session, &enc_secret);
631
632 let attribute = Attribute::random(&mut rng);
633 let encrypted = encrypt(&attribute, &session_public, &mut rng);
634 let json = serde_json::to_string(&encrypted).expect("serialization should succeed");
635 let deserialized: EncryptedAttribute =
636 serde_json::from_str(&json).expect("deserialization should succeed");
637
638 assert_eq!(deserialized, encrypted);
639 }
640
641 #[test]
642 fn polymorphic_encrypt_decrypt() {
643 let mut rng = rand::rng();
644 let (_, global_secret) = make_pseudonym_global_keys(&mut rng);
645 let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
646 let session = EncryptionContext::from("session-1");
647 let (session_public, session_secret) =
648 make_pseudonym_session_keys(&global_secret, &session, &enc_secret);
649
650 let original = Pseudonym::random(&mut rng);
651 let encrypted = encrypt(&original, &session_public, &mut rng);
652 #[cfg(feature = "elgamal3")]
653 let decrypted = decrypt(&encrypted, &session_secret).expect("decryption should succeed");
654 #[cfg(not(feature = "elgamal3"))]
655 let decrypted = decrypt(&encrypted, &session_secret);
656
657 assert_eq!(decrypted, original);
658 }
659}