1use alloc::boxed::Box;
2use alloc::string::String;
3use core::fmt;
4
5use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng};
6use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
7use ferveo::api::{CiphertextHeader, FerveoVariant};
8use generic_array::typenum::Unsigned;
9use serde::{Deserialize, Serialize};
10use umbral_pre::serde_bytes; use crate::access_control::AccessControlPolicy;
13use crate::conditions::Context;
14use crate::dkg::session::{SessionSharedSecret, SessionStaticKey};
15use crate::versioning::{
16 messagepack_deserialize, messagepack_serialize, DeserializationError, ProtocolObject,
17 ProtocolObjectInner,
18};
19
20#[derive(Debug)]
22pub enum EncryptionError {
23 PlaintextTooLarge,
25}
26
27impl fmt::Display for EncryptionError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::PlaintextTooLarge => write!(f, "Plaintext is too large to encrypt"),
31 }
32 }
33}
34
35#[derive(Debug)]
37pub enum DecryptionError {
38 CiphertextTooShort,
40 AuthenticationFailed,
46 DeserializationFailed(DeserializationError),
48}
49
50impl fmt::Display for DecryptionError {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 Self::CiphertextTooShort => write!(f, "The ciphertext must include the nonce"),
54 Self::AuthenticationFailed => write!(
55 f,
56 "Decryption of ciphertext failed: \
57 either someone tampered with the ciphertext or \
58 you are using an incorrect decryption key."
59 ),
60 Self::DeserializationFailed(err) => write!(f, "deserialization failed: {err}"),
61 }
62 }
63}
64
65type NonceSize = <ChaCha20Poly1305 as AeadCore>::NonceSize;
66
67fn encrypt_with_shared_secret(
68 shared_secret: &SessionSharedSecret,
69 plaintext: &[u8],
70) -> Result<Box<[u8]>, EncryptionError> {
71 let key = Key::from_slice(shared_secret.as_ref());
72 let cipher = ChaCha20Poly1305::new(key);
73 let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
74 let mut result = nonce.to_vec();
75 let ciphertext = cipher
76 .encrypt(&nonce, plaintext.as_ref())
77 .map_err(|_err| EncryptionError::PlaintextTooLarge)?;
78 result.extend(ciphertext);
79 Ok(result.into_boxed_slice())
80}
81
82fn decrypt_with_shared_secret(
83 shared_secret: &SessionSharedSecret,
84 ciphertext: &[u8],
85) -> Result<Box<[u8]>, DecryptionError> {
86 let nonce_size = <NonceSize as Unsigned>::to_usize();
87 let buf_size = ciphertext.len();
88 if buf_size < nonce_size {
89 return Err(DecryptionError::CiphertextTooShort);
90 }
91 let nonce = Nonce::from_slice(&ciphertext[..nonce_size]);
92 let encrypted_data = &ciphertext[nonce_size..];
93
94 let key = Key::from_slice(shared_secret.as_ref());
95 let cipher = ChaCha20Poly1305::new(key);
96 let plaintext = cipher
97 .decrypt(nonce, encrypted_data)
98 .map_err(|_err| DecryptionError::AuthenticationFailed)?;
99 Ok(plaintext.into_boxed_slice())
100}
101
102pub mod session {
104 use alloc::boxed::Box;
105 use alloc::string::String;
106 use core::fmt;
107
108 use generic_array::{
109 typenum::{Unsigned, U32},
110 GenericArray,
111 };
112 use rand::SeedableRng;
113 use rand_chacha::ChaCha20Rng;
114 use rand_core::{CryptoRng, OsRng, RngCore};
115 use serde::{Deserialize, Deserializer, Serialize, Serializer};
116 use umbral_pre::serde_bytes;
117 use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
118 use zeroize::ZeroizeOnDrop;
119
120 use crate::secret_box::{kdf, SecretBox};
121 use crate::versioning::{
122 messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
123 };
124
125 #[derive(ZeroizeOnDrop)]
127 pub struct SessionSharedSecret {
128 derived_bytes: [u8; 32],
129 }
130
131 impl SessionSharedSecret {
133 pub fn new(shared_secret: SharedSecret) -> Self {
135 let info = b"SESSION_SHARED_SECRET_DERIVATION/";
136 let derived_key = kdf::<U32>(shared_secret.as_bytes(), Some(info));
137 let derived_bytes = <[u8; 32]>::try_from(derived_key.as_secret().as_slice()).unwrap();
138 Self { derived_bytes }
139 }
140
141 pub fn as_bytes(&self) -> &[u8; 32] {
143 &self.derived_bytes
144 }
145 }
146
147 impl AsRef<[u8]> for SessionSharedSecret {
148 fn as_ref(&self) -> &[u8] {
150 self.as_bytes()
151 }
152 }
153
154 impl fmt::Display for SessionSharedSecret {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "SessionSharedSecret...")
158 }
159 }
160
161 #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
163 pub struct SessionStaticKey(PublicKey);
164
165 impl SessionStaticKey {
167 pub fn to_bytes(&self) -> [u8; 32] {
169 self.0.to_bytes()
170 }
171 }
172
173 impl AsRef<[u8]> for SessionStaticKey {
174 fn as_ref(&self) -> &[u8] {
176 self.0.as_bytes()
177 }
178 }
179
180 impl fmt::Display for SessionStaticKey {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(f, "SessionStaticKey: {}", hex::encode(&self.as_ref()[..8]))
184 }
185 }
186
187 impl Serialize for SessionStaticKey {
188 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
189 where
190 S: Serializer,
191 {
192 serde_bytes::as_hex::serialize(self.0.as_bytes(), serializer)
193 }
194 }
195
196 impl serde_bytes::TryFromBytes for SessionStaticKey {
197 type Error = core::array::TryFromSliceError;
198 fn try_from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
199 let array: [u8; 32] = bytes.try_into()?;
200 Ok(SessionStaticKey(PublicKey::from(array)))
201 }
202 }
203
204 impl<'a> Deserialize<'a> for SessionStaticKey {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'a>,
208 {
209 serde_bytes::as_hex::deserialize(deserializer)
210 }
211 }
212
213 impl<'a> ProtocolObjectInner<'a> for SessionStaticKey {
214 fn version() -> (u16, u16) {
215 (2, 0)
216 }
217
218 fn brand() -> [u8; 4] {
219 *b"TSSk"
220 }
221
222 fn unversioned_to_bytes(&self) -> Box<[u8]> {
223 messagepack_serialize(&self)
224 }
225
226 fn unversioned_from_bytes(
227 minor_version: u16,
228 bytes: &[u8],
229 ) -> Option<Result<Self, String>> {
230 if minor_version == 0 {
231 Some(messagepack_deserialize(bytes))
232 } else {
233 None
234 }
235 }
236 }
237
238 impl<'a> ProtocolObject<'a> for SessionStaticKey {}
239
240 #[derive(ZeroizeOnDrop)]
242 pub struct SessionStaticSecret(pub(crate) StaticSecret);
243
244 impl SessionStaticSecret {
245 pub fn derive_shared_secret(
247 &self,
248 their_public_key: &SessionStaticKey,
249 ) -> SessionSharedSecret {
250 let shared_secret = self.0.diffie_hellman(&their_public_key.0);
251 SessionSharedSecret::new(shared_secret)
252 }
253
254 pub fn random_from_rng(csprng: &mut (impl RngCore + CryptoRng)) -> Self {
256 let secret_key = StaticSecret::random_from_rng(csprng);
257 Self(secret_key)
258 }
259
260 pub fn random() -> Self {
262 Self::random_from_rng(&mut OsRng)
263 }
264
265 pub fn public_key(&self) -> SessionStaticKey {
267 let public_key = PublicKey::from(&self.0);
268 SessionStaticKey(public_key)
269 }
270 }
271
272 impl fmt::Display for SessionStaticSecret {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 write!(f, "SessionStaticSecret:...")
276 }
277 }
278
279 type SessionSecretFactorySeedSize = U32;
281 type SessionSecretFactoryDerivedKeySize = U32;
283 type SessionSecretFactorySeed = GenericArray<u8, SessionSecretFactorySeedSize>;
284
285 #[derive(Debug)]
287 pub struct InvalidSessionSecretFactorySeedLength;
288
289 impl fmt::Display for InvalidSessionSecretFactorySeedLength {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 write!(f, "Invalid seed length")
292 }
293 }
294
295 #[derive(Clone, ZeroizeOnDrop, PartialEq)]
298 pub struct SessionSecretFactory(SecretBox<SessionSecretFactorySeed>);
299
300 impl SessionSecretFactory {
301 pub fn random_with_rng(rng: &mut (impl CryptoRng + RngCore)) -> Self {
303 let mut bytes = SecretBox::new(SessionSecretFactorySeed::default());
304 rng.fill_bytes(bytes.as_mut_secret());
305 Self(bytes)
306 }
307
308 pub fn random() -> Self {
310 Self::random_with_rng(&mut OsRng)
311 }
312
313 pub fn seed_size() -> usize {
315 SessionSecretFactorySeedSize::to_usize()
316 }
317
318 pub fn from_secure_randomness(
323 seed: &[u8],
324 ) -> Result<Self, InvalidSessionSecretFactorySeedLength> {
325 if seed.len() != Self::seed_size() {
326 return Err(InvalidSessionSecretFactorySeedLength);
327 }
328 Ok(Self(SecretBox::new(*SessionSecretFactorySeed::from_slice(
329 seed,
330 ))))
331 }
332
333 pub fn make_key(&self, label: &[u8]) -> SessionStaticSecret {
335 let prefix = b"SESSION_KEY_DERIVATION/";
336 let info = [prefix, label].concat();
337 let seed = kdf::<SessionSecretFactoryDerivedKeySize>(self.0.as_secret(), Some(&info));
338 let mut rng =
339 ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap());
340 SessionStaticSecret::random_from_rng(&mut rng)
341 }
342 }
343
344 impl fmt::Display for SessionSecretFactory {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 write!(f, "SessionSecretFactory:...")
347 }
348 }
349}
350
351#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
353pub struct ThresholdDecryptionRequest {
354 pub ritual_id: u32,
356 pub ciphertext_header: CiphertextHeader,
358 pub acp: AccessControlPolicy,
360 pub context: Option<Context>,
362 pub variant: FerveoVariant,
364}
365
366impl ThresholdDecryptionRequest {
367 pub fn new(
369 ritual_id: u32,
370 ciphertext_header: &CiphertextHeader,
371 acp: &AccessControlPolicy,
372 context: Option<&Context>,
373 variant: FerveoVariant,
374 ) -> Self {
375 Self {
376 ritual_id,
377 ciphertext_header: ciphertext_header.clone(),
378 acp: acp.clone(),
379 context: context.cloned(),
380 variant,
381 }
382 }
383
384 pub fn encrypt(
386 &self,
387 shared_secret: &SessionSharedSecret,
388 requester_public_key: &SessionStaticKey,
389 ) -> EncryptedThresholdDecryptionRequest {
390 EncryptedThresholdDecryptionRequest::new(self, shared_secret, requester_public_key)
391 }
392}
393
394impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionRequest {
395 fn version() -> (u16, u16) {
396 (4, 0)
397 }
398
399 fn brand() -> [u8; 4] {
400 *b"ThRq"
401 }
402
403 fn unversioned_to_bytes(&self) -> Box<[u8]> {
404 messagepack_serialize(&self)
405 }
406
407 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
408 if minor_version == 0 {
409 Some(messagepack_deserialize(bytes))
410 } else {
411 None
412 }
413 }
414}
415
416impl<'a> ProtocolObject<'a> for ThresholdDecryptionRequest {}
417
418#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
420pub struct EncryptedThresholdDecryptionRequest {
421 pub ritual_id: u32,
423
424 pub requester_public_key: SessionStaticKey,
426
427 #[serde(with = "serde_bytes::as_base64")]
428 ciphertext: Box<[u8]>,
430}
431
432impl EncryptedThresholdDecryptionRequest {
433 fn new(
434 request: &ThresholdDecryptionRequest,
435 shared_secret: &SessionSharedSecret,
436 requester_public_key: &SessionStaticKey,
437 ) -> Self {
438 let ciphertext = encrypt_with_shared_secret(shared_secret, &request.to_bytes())
439 .expect("encryption failed - out of memory?");
440 Self {
441 ritual_id: request.ritual_id,
442 requester_public_key: *requester_public_key,
443 ciphertext,
444 }
445 }
446
447 pub fn decrypt(
449 &self,
450 shared_secret: &SessionSharedSecret,
451 ) -> Result<ThresholdDecryptionRequest, DecryptionError> {
452 let decryption_request_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?;
453 let decryption_request = ThresholdDecryptionRequest::from_bytes(&decryption_request_bytes)
454 .map_err(DecryptionError::DeserializationFailed)?;
455 Ok(decryption_request)
456 }
457}
458
459impl<'a> ProtocolObjectInner<'a> for EncryptedThresholdDecryptionRequest {
460 fn version() -> (u16, u16) {
461 (2, 0)
462 }
463
464 fn brand() -> [u8; 4] {
465 *b"ETRq"
466 }
467
468 fn unversioned_to_bytes(&self) -> Box<[u8]> {
469 messagepack_serialize(&self)
470 }
471
472 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
473 if minor_version == 0 {
474 Some(messagepack_deserialize(bytes))
475 } else {
476 None
477 }
478 }
479}
480
481impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionRequest {}
482
483#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
485pub struct ThresholdDecryptionResponse {
486 pub ritual_id: u32,
488
489 #[serde(with = "serde_bytes::as_base64")]
491 pub decryption_share: Box<[u8]>,
492}
493
494impl ThresholdDecryptionResponse {
495 pub fn new(ritual_id: u32, decryption_share: &[u8]) -> Self {
497 ThresholdDecryptionResponse {
498 ritual_id,
499 decryption_share: decryption_share.to_vec().into(),
500 }
501 }
502
503 pub fn encrypt(
505 &self,
506 shared_secret: &SessionSharedSecret,
507 ) -> EncryptedThresholdDecryptionResponse {
508 EncryptedThresholdDecryptionResponse::new(self, shared_secret)
509 }
510}
511
512impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionResponse {
513 fn version() -> (u16, u16) {
514 (2, 0)
515 }
516
517 fn brand() -> [u8; 4] {
518 *b"ThRs"
519 }
520
521 fn unversioned_to_bytes(&self) -> Box<[u8]> {
522 messagepack_serialize(&self)
523 }
524
525 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
526 if minor_version == 0 {
527 Some(messagepack_deserialize(bytes))
528 } else {
529 None
530 }
531 }
532}
533
534impl<'a> ProtocolObject<'a> for ThresholdDecryptionResponse {}
535
536#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
538pub struct EncryptedThresholdDecryptionResponse {
539 pub ritual_id: u32,
541
542 #[serde(with = "serde_bytes::as_base64")]
543 ciphertext: Box<[u8]>,
544}
545
546impl EncryptedThresholdDecryptionResponse {
547 fn new(response: &ThresholdDecryptionResponse, shared_secret: &SessionSharedSecret) -> Self {
548 let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes())
549 .expect("encryption failed - out of memory?");
550 Self {
551 ritual_id: response.ritual_id,
552 ciphertext,
553 }
554 }
555
556 pub fn decrypt(
558 &self,
559 shared_secret: &SessionSharedSecret,
560 ) -> Result<ThresholdDecryptionResponse, DecryptionError> {
561 let decryption_response_bytes =
562 decrypt_with_shared_secret(shared_secret, &self.ciphertext)?;
563 let decryption_response =
564 ThresholdDecryptionResponse::from_bytes(&decryption_response_bytes)
565 .map_err(DecryptionError::DeserializationFailed)?;
566 Ok(decryption_response)
567 }
568}
569
570impl<'a> ProtocolObjectInner<'a> for EncryptedThresholdDecryptionResponse {
571 fn version() -> (u16, u16) {
572 (2, 0)
573 }
574
575 fn brand() -> [u8; 4] {
576 *b"ETRs"
577 }
578
579 fn unversioned_to_bytes(&self) -> Box<[u8]> {
580 messagepack_serialize(&self)
581 }
582
583 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
584 if minor_version == 0 {
585 Some(messagepack_deserialize(bytes))
586 } else {
587 None
588 }
589 }
590}
591
592impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionResponse {}
593
594#[cfg(test)]
595mod tests {
596 use ferveo::api::{encrypt as ferveo_encrypt, FerveoVariant, SecretBox};
597 use generic_array::typenum::Unsigned;
598 use rand_core::RngCore;
599
600 use crate::access_control::AccessControlPolicy;
601 use crate::conditions::{Conditions, Context};
602 use crate::dkg::session::SessionStaticSecret;
603 use crate::dkg::{
604 decrypt_with_shared_secret, encrypt_with_shared_secret, DecryptionError, NonceSize,
605 };
606 use crate::test_utils::util::random_dkg_pubkey;
607 use crate::versioning::{ProtocolObject, ProtocolObjectInner};
608 use crate::{
609 AuthenticatedData, EncryptedThresholdDecryptionRequest,
610 EncryptedThresholdDecryptionResponse, SessionSecretFactory, SessionStaticKey,
611 ThresholdDecryptionRequest, ThresholdDecryptionResponse,
612 };
613
614 #[test]
615 fn decryption_with_shared_secret() {
616 let service_secret = SessionStaticSecret::random();
617
618 let requester_secret = SessionStaticSecret::random();
619 let requester_public_key = requester_secret.public_key();
620
621 let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key);
622
623 let ciphertext = b"1".to_vec().into_boxed_slice(); let nonce_size = <NonceSize as Unsigned>::to_usize();
625 assert!(ciphertext.len() < nonce_size);
626
627 assert!(matches!(
628 decrypt_with_shared_secret(&service_shared_secret, &ciphertext).unwrap_err(),
629 DecryptionError::CiphertextTooShort
630 ));
631 }
632
633 #[test]
634 fn request_key_factory() {
635 let secret_factory = SessionSecretFactory::random();
636
637 let label_1 = b"label_1".to_vec().into_boxed_slice();
639 let service_secret_key = secret_factory.make_key(label_1.as_ref());
640 let service_public_key = service_secret_key.public_key();
641
642 let label_2 = b"label_2".to_vec().into_boxed_slice();
643 let requester_secret_key = secret_factory.make_key(label_2.as_ref());
644 let requester_public_key = requester_secret_key.public_key();
645
646 let service_shared_secret = service_secret_key.derive_shared_secret(&requester_public_key);
647 let requester_shared_secret =
648 requester_secret_key.derive_shared_secret(&service_public_key);
649
650 let data_to_encrypt = b"The Tyranny of Merit".to_vec().into_boxed_slice();
651 let ciphertext =
652 encrypt_with_shared_secret(&requester_shared_secret, data_to_encrypt.as_ref()).unwrap();
653 let decrypted_data =
654 decrypt_with_shared_secret(&service_shared_secret, &ciphertext).unwrap();
655 assert_eq!(decrypted_data, data_to_encrypt);
656
657 let same_requester_secret_key = secret_factory.make_key(label_2.as_ref());
659 let same_requester_public_key = same_requester_secret_key.public_key();
660 assert_eq!(requester_public_key, same_requester_public_key);
661
662 let other_secret_factory = SessionSecretFactory::random();
664 let not_same_requester_secret_key = other_secret_factory.make_key(label_2.as_ref());
665 let not_same_requester_public_key = not_same_requester_secret_key.public_key();
666 assert_ne!(requester_public_key, not_same_requester_public_key);
667
668 let mut secret_factory_seed = [0u8; 32];
670 rand::thread_rng().fill_bytes(&mut secret_factory_seed);
671 let seeded_secret_factory_1 =
672 SessionSecretFactory::from_secure_randomness(&secret_factory_seed).unwrap();
673 let seeded_secret_factory_2 =
674 SessionSecretFactory::from_secure_randomness(&secret_factory_seed).unwrap();
675
676 let key_label = b"seeded_factory_key_label".to_vec().into_boxed_slice();
677 let sk_1 = seeded_secret_factory_1.make_key(&key_label);
678 let pk_1 = sk_1.public_key();
679
680 let sk_2 = seeded_secret_factory_2.make_key(&key_label);
681 let pk_2 = sk_2.public_key();
682
683 assert_eq!(pk_1, pk_2);
684
685 let bytes = [0u8; 32];
687 let factory = SessionSecretFactory::from_secure_randomness(&bytes);
688 assert!(factory.is_ok());
689
690 let bytes = [0u8; 31];
691 let factory = SessionSecretFactory::from_secure_randomness(&bytes);
692 assert!(factory.is_err());
693 }
694
695 #[test]
696 fn session_static_key() {
697 let public_key_1: SessionStaticKey = SessionStaticSecret::random().public_key();
698 let public_key_2: SessionStaticKey = SessionStaticSecret::random().public_key();
699
700 let public_key_1_bytes = public_key_1.unversioned_to_bytes();
701 let public_key_2_bytes = public_key_2.unversioned_to_bytes();
702
703 assert_eq!(public_key_1_bytes.len(), public_key_2_bytes.len());
705
706 let deserialized_public_key_1 =
707 SessionStaticKey::unversioned_from_bytes(0, &public_key_1_bytes)
708 .unwrap()
709 .unwrap();
710 let deserialized_public_key_2 =
711 SessionStaticKey::unversioned_from_bytes(0, &public_key_2_bytes)
712 .unwrap()
713 .unwrap();
714
715 assert_eq!(public_key_1, deserialized_public_key_1);
716 assert_eq!(public_key_2, deserialized_public_key_2);
717 }
718
719 #[test]
720 fn threshold_decryption_request() {
721 for variant in [FerveoVariant::Simple, FerveoVariant::Precomputed] {
722 let ritual_id = 0;
723
724 let service_secret = SessionStaticSecret::random();
725
726 let requester_secret = SessionStaticSecret::random();
727 let requester_public_key = requester_secret.public_key();
728
729 let dkg_pk = random_dkg_pubkey();
730 let message = "The Tyranny of Merit".as_bytes().to_vec();
731 let aad = "my-add".as_bytes();
732 let ciphertext = ferveo_encrypt(SecretBox::new(message), aad, &dkg_pk).unwrap();
733
734 let auth_data = AuthenticatedData::new(&dkg_pk, &Conditions::new("abcd"));
735
736 let authorization = b"self_authorization";
737 let acp = AccessControlPolicy::new(&auth_data, authorization);
738
739 let ciphertext_header = ciphertext.header().unwrap();
740
741 let request = ThresholdDecryptionRequest::new(
742 ritual_id,
743 &ciphertext_header,
744 &acp,
745 Some(&Context::new("efgh")),
746 variant,
747 );
748
749 let service_public_key = service_secret.public_key();
751 let requester_shared_secret =
752 requester_secret.derive_shared_secret(&service_public_key);
753 let encrypted_request =
754 request.encrypt(&requester_shared_secret, &requester_public_key);
755
756 let encrypted_request_bytes = encrypted_request.to_bytes();
758 let encrypted_request_from_bytes =
759 EncryptedThresholdDecryptionRequest::from_bytes(&encrypted_request_bytes).unwrap();
760
761 assert_eq!(encrypted_request_from_bytes.ritual_id, ritual_id);
762 assert_eq!(
763 encrypted_request_from_bytes.requester_public_key,
764 requester_public_key
765 );
766
767 let service_shared_secret = service_secret
769 .derive_shared_secret(&encrypted_request_from_bytes.requester_public_key);
770 assert_eq!(
771 service_shared_secret.as_bytes(),
772 requester_shared_secret.as_bytes()
773 );
774 let decrypted_request = encrypted_request_from_bytes
775 .decrypt(&service_shared_secret)
776 .unwrap();
777 assert_eq!(decrypted_request, request);
778
779 let random_secret_key = SessionStaticSecret::random();
781 let random_shared_secret =
782 random_secret_key.derive_shared_secret(&requester_public_key);
783 assert!(encrypted_request_from_bytes
784 .decrypt(&random_shared_secret)
785 .is_err());
786 }
787 }
788
789 #[test]
790 fn threshold_decryption_response() {
791 let ritual_id = 5;
792
793 let service_secret = SessionStaticSecret::random();
794 let requester_secret = SessionStaticSecret::random();
795
796 let decryption_share = b"The Tyranny of Merit";
797
798 let response = ThresholdDecryptionResponse::new(ritual_id, decryption_share);
799
800 let requester_public_key = requester_secret.public_key();
802
803 let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key);
804 let encrypted_response = response.encrypt(&service_shared_secret);
805 assert_eq!(encrypted_response.ritual_id, ritual_id);
806
807 let encrypted_response_bytes = encrypted_response.to_bytes();
809 let encrypted_response_from_bytes =
810 EncryptedThresholdDecryptionResponse::from_bytes(&encrypted_response_bytes).unwrap();
811
812 let service_public_key = service_secret.public_key();
814 let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key);
815 assert_eq!(
816 requester_shared_secret.as_bytes(),
817 service_shared_secret.as_bytes()
818 );
819 let decrypted_response = encrypted_response_from_bytes
820 .decrypt(&requester_shared_secret)
821 .unwrap();
822 assert_eq!(response, decrypted_response);
823 assert_eq!(response.ritual_id, ritual_id);
824 assert_eq!(
825 response.decryption_share,
826 decrypted_response.decryption_share
827 );
828
829 let random_secret_key = SessionStaticSecret::random();
831 let random_shared_secret = random_secret_key.derive_shared_secret(&requester_public_key);
832 assert!(encrypted_response_from_bytes
833 .decrypt(&random_shared_secret)
834 .is_err());
835 }
836}