1use std::collections::BTreeMap;
26
27use rand::RngCore;
28use serde::Deserialize;
29use serde::Serialize;
30
31use crate::dht::Did;
32use crate::ecc::elgamal::impls::secp256k1;
33use crate::ecc::group::Point;
34use crate::ecc::group::Secp256k1;
35use crate::ecc::PublicKey;
36use crate::ecc::SecretKey;
37use crate::error::Error;
38use crate::error::Result;
39
40pub const E2E_PLAINTEXT_BLOCK_LEN: usize = secp256k1::PLAINTEXT_BLOCK_SIZE;
42
43pub const DEFAULT_E2E_PLAINTEXT_FRAME_LEN: usize = E2E_PLAINTEXT_BLOCK_LEN * 16;
45
46pub const DEFAULT_E2E_REORDER_WINDOW_FRAMES: u64 = 64;
48
49pub type E2eStreamId = uuid::Uuid;
51
52#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
54pub struct E2eHandshakeRequest {
55 pub requester_public_key: PublicKey<33>,
57}
58
59#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
61pub struct E2eHandshakeResponse {
62 pub responder_public_key: PublicKey<33>,
64}
65
66#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
68pub struct E2eStreamFrame {
69 pub stream_id: E2eStreamId,
71 pub sender_public_key: PublicKey<33>,
73 pub sequence: u64,
75 pub is_final: bool,
77 pub ciphertext: Vec<secp256k1::CiphertextBlock>,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82struct E2ePlaintextFrame<'a> {
83 plaintext: &'a [u8],
84 is_final: bool,
85}
86
87struct E2ePlaintextFrames<'a> {
88 chunks: std::iter::Peekable<std::slice::Chunks<'a, u8>>,
89 plaintext_is_empty: bool,
90 emitted_empty_final: bool,
91}
92
93pub struct E2eStreamFrames<'a, R: RngCore> {
95 plaintext_frames: E2ePlaintextFrames<'a>,
96 encryptor: E2eStreamEncryptor,
97 rng: R,
98}
99
100pub struct E2eStreamEncryptor {
102 stream_id: E2eStreamId,
103 sender_public_key: PublicKey<33>,
104 recipient_public_key: PublicKey<33>,
105 next_sequence: u64,
106 closed: bool,
107}
108
109pub struct E2eStreamDecryptor {
111 stream_id: E2eStreamId,
112 expected_sender: Did,
113 recipient_secret_key: SecretKey,
114 next_sequence: u64,
115 final_sequence: Option<u64>,
116 seen_final: bool,
117 reorder_window: u64,
118 pending_frames: BTreeMap<u64, E2eStreamFrame>,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122enum FrameAcceptance {
123 New,
124 Duplicate,
125}
126
127impl<'a> E2ePlaintextFrames<'a> {
128 fn new(plaintext: &'a [u8], max_plaintext_frame_len: usize) -> Self {
129 Self {
130 chunks: plaintext.chunks(max_plaintext_frame_len.max(1)).peekable(),
131 plaintext_is_empty: plaintext.is_empty(),
132 emitted_empty_final: false,
133 }
134 }
135}
136
137impl<R: RngCore> Iterator for E2eStreamFrames<'_, R> {
138 type Item = Result<E2eStreamFrame>;
139
140 fn next(&mut self) -> Option<Self::Item> {
141 self.plaintext_frames
142 .next()
143 .map(|frame| self.encryptor.encrypt_plaintext_frame(frame, &mut self.rng))
144 }
145}
146
147impl<'a> Iterator for E2ePlaintextFrames<'a> {
148 type Item = E2ePlaintextFrame<'a>;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 if self.plaintext_is_empty {
152 if self.emitted_empty_final {
153 return None;
154 }
155 self.emitted_empty_final = true;
156 return Some(E2ePlaintextFrame {
157 plaintext: &[],
158 is_final: true,
159 });
160 }
161
162 let plaintext = self.chunks.next()?;
163 Some(E2ePlaintextFrame {
164 plaintext,
165 is_final: self.chunks.peek().is_none(),
166 })
167 }
168}
169
170impl E2eHandshakeRequest {
171 pub fn new(requester_public_key: PublicKey<33>) -> Self {
173 Self {
174 requester_public_key,
175 }
176 }
177
178 pub fn verify_requester(&self, requester: Did) -> Result<()> {
180 ensure_public_key_matches_did(self.requester_public_key, requester)
181 }
182}
183
184impl E2eHandshakeResponse {
185 pub fn new(responder_public_key: PublicKey<33>) -> Self {
187 Self {
188 responder_public_key,
189 }
190 }
191
192 pub fn verify_responder(&self, responder: Did) -> Result<()> {
194 ensure_public_key_matches_did(self.responder_public_key, responder)
195 }
196}
197
198impl E2eStreamFrame {
199 pub fn verify_sender(&self, sender: Did) -> Result<()> {
201 ensure_public_key_matches_did(self.sender_public_key, sender)
202 }
203}
204
205impl E2eStreamEncryptor {
206 pub fn new(
208 stream_id: E2eStreamId,
209 sender_public_key: PublicKey<33>,
210 recipient_public_key: PublicKey<33>,
211 ) -> Result<Self> {
212 ensure_valid_public_key(sender_public_key)?;
213 ensure_valid_public_key(recipient_public_key)?;
214 Ok(Self {
215 stream_id,
216 sender_public_key,
217 recipient_public_key,
218 next_sequence: 0,
219 closed: false,
220 })
221 }
222
223 pub fn encrypt_next(
225 &mut self,
226 plaintext: &[u8],
227 rng: &mut impl RngCore,
228 ) -> Result<E2eStreamFrame> {
229 self.encrypt_frame(plaintext, false, rng)
230 }
231
232 pub fn encrypt_next_with_default_rng(&mut self, plaintext: &[u8]) -> Result<E2eStreamFrame> {
234 let mut rng = rand::thread_rng();
235 self.encrypt_next(plaintext, &mut rng)
236 }
237
238 pub fn encrypt_final(
240 &mut self,
241 plaintext: &[u8],
242 rng: &mut impl RngCore,
243 ) -> Result<E2eStreamFrame> {
244 self.encrypt_frame(plaintext, true, rng)
245 }
246
247 pub fn encrypt_final_with_default_rng(&mut self, plaintext: &[u8]) -> Result<E2eStreamFrame> {
249 let mut rng = rand::thread_rng();
250 self.encrypt_final(plaintext, &mut rng)
251 }
252
253 fn encrypt_plaintext_frame(
254 &mut self,
255 frame: E2ePlaintextFrame<'_>,
256 rng: &mut impl RngCore,
257 ) -> Result<E2eStreamFrame> {
258 if frame.is_final {
259 self.encrypt_final(frame.plaintext, rng)
260 } else {
261 self.encrypt_next(frame.plaintext, rng)
262 }
263 }
264
265 fn encrypt_frame(
266 &mut self,
267 plaintext: &[u8],
268 is_final: bool,
269 rng: &mut impl RngCore,
270 ) -> Result<E2eStreamFrame> {
271 if self.closed {
272 return Err(Error::E2eFrameAfterFinal);
273 }
274
275 let sequence = self.next_sequence;
276 if is_final {
277 self.closed = true;
278 } else {
279 self.next_sequence = next_sequence(sequence)?;
280 }
281
282 let ciphertext =
283 secp256k1::encrypt_bytes_with_rng(plaintext, self.recipient_public_key, rng)?;
284 Ok(E2eStreamFrame {
285 stream_id: self.stream_id,
286 sender_public_key: self.sender_public_key,
287 sequence,
288 is_final,
289 ciphertext,
290 })
291 }
292}
293
294impl E2eStreamDecryptor {
295 pub fn new(
297 stream_id: E2eStreamId,
298 expected_sender: Did,
299 recipient_secret_key: SecretKey,
300 ) -> Self {
301 Self::with_reorder_window(
302 stream_id,
303 expected_sender,
304 recipient_secret_key,
305 DEFAULT_E2E_REORDER_WINDOW_FRAMES,
306 )
307 }
308
309 pub fn with_reorder_window(
311 stream_id: E2eStreamId,
312 expected_sender: Did,
313 recipient_secret_key: SecretKey,
314 reorder_window: u64,
315 ) -> Self {
316 Self {
317 stream_id,
318 expected_sender,
319 recipient_secret_key,
320 next_sequence: 0,
321 final_sequence: None,
322 seen_final: false,
323 reorder_window,
324 pending_frames: BTreeMap::new(),
325 }
326 }
327
328 pub fn decrypt_next(&mut self, frame: &E2eStreamFrame) -> Result<Vec<u8>> {
333 if self.validate_frame(frame)? == FrameAcceptance::Duplicate {
334 return Ok(Vec::new());
335 }
336
337 if frame.is_final {
338 self.final_sequence = Some(frame.sequence);
339 }
340 self.pending_frames.insert(frame.sequence, frame.clone());
341 self.decrypt_ready_frames()
342 }
343
344 pub fn finish(&self) -> Result<()> {
346 if self.seen_final {
347 Ok(())
348 } else {
349 Err(Error::E2eMissingFinalFrame)
350 }
351 }
352
353 fn validate_frame(&self, frame: &E2eStreamFrame) -> Result<FrameAcceptance> {
354 if frame.stream_id != self.stream_id {
355 return Err(Error::E2eStreamIdMismatch {
356 expected: self.stream_id,
357 actual: frame.stream_id,
358 });
359 }
360
361 frame.verify_sender(self.expected_sender)?;
362
363 if self.is_consumed_duplicate(frame) {
364 return Ok(FrameAcceptance::Duplicate);
365 }
366
367 if let Some(pending_frame) = self.pending_frames.get(&frame.sequence) {
368 if pending_frame == frame {
369 return Ok(FrameAcceptance::Duplicate);
370 }
371
372 return Err(Error::E2eFrameSequenceMismatch {
373 expected: self.next_sequence,
374 actual: frame.sequence,
375 });
376 }
377
378 if self.seen_final {
379 return Err(Error::E2eFrameAfterFinal);
380 }
381
382 if self.exceeds_reorder_window(frame.sequence) {
383 return Err(Error::E2eFrameReorderWindowExceeded {
384 next_sequence: self.next_sequence,
385 actual: frame.sequence,
386 window: self.reorder_window,
387 });
388 }
389
390 if let Some(final_sequence) = self.final_sequence {
391 if frame.sequence > final_sequence {
392 return Err(Error::E2eFrameAfterFinal);
393 }
394
395 if frame.is_final && frame.sequence != final_sequence {
396 return Err(Error::E2eFrameSequenceMismatch {
397 expected: final_sequence,
398 actual: frame.sequence,
399 });
400 }
401 }
402
403 if frame.is_final && self.has_pending_frame_after(frame.sequence) {
404 return Err(Error::E2eFrameAfterFinal);
405 }
406
407 Ok(FrameAcceptance::New)
408 }
409
410 fn decrypt_ready_frames(&mut self) -> Result<Vec<u8>> {
411 let mut plaintext = Vec::new();
412
413 while let Some(frame) = self.pending_frames.get(&self.next_sequence) {
414 let frame_plaintext =
415 secp256k1::decrypt_bytes(&frame.ciphertext, self.recipient_secret_key)?;
416 let is_final = frame.is_final;
417 plaintext.extend_from_slice(&frame_plaintext);
418 self.pending_frames.remove(&self.next_sequence);
419
420 if is_final {
421 self.seen_final = true;
422 break;
423 }
424
425 self.next_sequence = next_sequence(self.next_sequence)?;
426 }
427
428 Ok(plaintext)
429 }
430
431 fn has_pending_frame_after(&self, sequence: u64) -> bool {
432 self.pending_frames
433 .last_key_value()
434 .is_some_and(|(pending_sequence, _)| *pending_sequence > sequence)
435 }
436
437 fn is_consumed_duplicate(&self, frame: &E2eStreamFrame) -> bool {
438 if frame.sequence < self.next_sequence {
439 return true;
440 }
441
442 self.seen_final
443 && self
444 .final_sequence
445 .is_some_and(|final_sequence| frame.sequence <= final_sequence)
446 }
447
448 fn exceeds_reorder_window(&self, sequence: u64) -> bool {
449 sequence.saturating_sub(self.next_sequence) > self.reorder_window
450 }
451}
452
453fn plaintext_stream_frames(
454 plaintext: &[u8],
455 max_plaintext_frame_len: usize,
456) -> E2ePlaintextFrames<'_> {
457 E2ePlaintextFrames::new(plaintext, max_plaintext_frame_len)
458}
459
460pub fn encrypt_stream_frames_with_rng<R: RngCore>(
462 plaintext: &[u8],
463 stream_id: E2eStreamId,
464 sender_public_key: PublicKey<33>,
465 recipient_public_key: PublicKey<33>,
466 max_plaintext_frame_len: usize,
467 rng: R,
468) -> Result<E2eStreamFrames<'_, R>> {
469 Ok(E2eStreamFrames {
470 plaintext_frames: plaintext_stream_frames(plaintext, max_plaintext_frame_len),
471 encryptor: E2eStreamEncryptor::new(stream_id, sender_public_key, recipient_public_key)?,
472 rng,
473 })
474}
475
476pub fn encrypt_stream_frames(
478 plaintext: &[u8],
479 stream_id: E2eStreamId,
480 sender_public_key: PublicKey<33>,
481 recipient_public_key: PublicKey<33>,
482 max_plaintext_frame_len: usize,
483) -> Result<E2eStreamFrames<'_, rand::rngs::ThreadRng>> {
484 encrypt_stream_frames_with_rng(
485 plaintext,
486 stream_id,
487 sender_public_key,
488 recipient_public_key,
489 max_plaintext_frame_len,
490 rand::thread_rng(),
491 )
492}
493
494pub fn encrypt_stream_with_rng(
496 plaintext: &[u8],
497 stream_id: E2eStreamId,
498 sender_public_key: PublicKey<33>,
499 recipient_public_key: PublicKey<33>,
500 max_plaintext_frame_len: usize,
501 rng: &mut impl RngCore,
502) -> Result<Vec<E2eStreamFrame>> {
503 encrypt_stream_frames_with_rng(
504 plaintext,
505 stream_id,
506 sender_public_key,
507 recipient_public_key,
508 max_plaintext_frame_len,
509 rng,
510 )?
511 .collect()
512}
513
514pub fn decrypt_stream(
516 frames: &[E2eStreamFrame],
517 stream_id: E2eStreamId,
518 expected_sender: Did,
519 recipient_secret_key: SecretKey,
520) -> Result<Vec<u8>> {
521 let mut decryptor = E2eStreamDecryptor::new(stream_id, expected_sender, recipient_secret_key);
522 let mut plaintext = Vec::new();
523
524 for frame in frames {
525 let frame_plaintext = decryptor.decrypt_next(frame)?;
526 plaintext.extend_from_slice(&frame_plaintext);
527 }
528
529 decryptor.finish()?;
530 Ok(plaintext)
531}
532
533pub fn ensure_public_key_matches_did(public_key: PublicKey<33>, expected: Did) -> Result<()> {
535 let actual = Did::from(public_key.address());
536 if actual == expected {
537 Ok(())
538 } else {
539 Err(Error::E2ePublicKeyDidMismatch { expected, actual })
540 }
541}
542
543fn ensure_valid_public_key(public_key: PublicKey<33>) -> Result<()> {
544 let _: Point<Secp256k1> = public_key.try_into()?;
545 Ok(())
546}
547
548fn next_sequence(sequence: u64) -> Result<u64> {
549 sequence
550 .checked_add(1)
551 .ok_or(Error::E2eFrameSequenceOverflow)
552}
553
554#[cfg(test)]
555mod tests {
556 use rand::SeedableRng;
557 use rand_hc::Hc128Rng;
558
559 use super::*;
560
561 fn recipient_key() -> SecretKey {
562 SecretKey::try_from("65860affb4b570dba06db294aa7c676f68e04a5bf2721243ad3cbc05a79c68c0")
563 .unwrap()
564 }
565
566 fn sender_key() -> SecretKey {
567 SecretKey::try_from("1f9275dbafdfba81942eb3330b07f38cbee4ebb86bdc2174af9648d5f5509a54")
568 .unwrap()
569 }
570
571 #[test]
572 fn public_key_must_match_did() {
573 let sender = sender_key();
574 let recipient = recipient_key();
575
576 assert!(ensure_public_key_matches_did(sender.pubkey(), sender.address().into()).is_ok());
577 assert!(matches!(
578 ensure_public_key_matches_did(sender.pubkey(), recipient.address().into()),
579 Err(Error::E2ePublicKeyDidMismatch { .. })
580 ));
581 }
582
583 #[test]
584 fn handshake_messages_verify_signed_owner() {
585 let sender = sender_key();
586 let recipient = recipient_key();
587 let request = E2eHandshakeRequest::new(sender.pubkey());
588 let response = E2eHandshakeResponse::new(recipient.pubkey());
589
590 request.verify_requester(sender.address().into()).unwrap();
591 response
592 .verify_responder(recipient.address().into())
593 .unwrap();
594 assert!(request
595 .verify_requester(recipient.address().into())
596 .is_err());
597 assert!(response.verify_responder(sender.address().into()).is_err());
598 }
599
600 #[test]
601 fn round_trip_random_binary_payloads_and_frame_sizes() {
602 let sender = sender_key();
603 let recipient = recipient_key();
604 let mut rng = Hc128Rng::seed_from_u64(608);
605 let payload_lens = [0usize, 1, 15, 16, 17, 31, 32, 255, 1024, 4097];
606 let frame_limits = [1usize, E2E_PLAINTEXT_BLOCK_LEN, 64, 511];
607
608 for payload_len in payload_lens {
609 for frame_limit in frame_limits {
610 let mut payload = vec![0u8; payload_len];
611 rng.fill_bytes(&mut payload);
612 let stream_id = uuid::Uuid::new_v4();
613
614 let frames = encrypt_stream_with_rng(
615 &payload,
616 stream_id,
617 sender.pubkey(),
618 recipient.pubkey(),
619 frame_limit,
620 &mut rng,
621 )
622 .unwrap();
623 assert_eq!(
624 frames.len(),
625 payload_len.div_ceil(frame_limit.max(1)).max(1)
626 );
627 assert_eq!(
628 decrypt_stream(&frames, stream_id, sender.address().into(), recipient).unwrap(),
629 payload
630 );
631 }
632 }
633 }
634
635 #[test]
636 fn empty_plaintext_sends_final_stream_frame() {
637 let sender = sender_key();
638 let recipient = recipient_key();
639 let mut rng = Hc128Rng::seed_from_u64(7);
640 let stream_id = uuid::Uuid::new_v4();
641
642 let frames = encrypt_stream_with_rng(
643 &[],
644 stream_id,
645 sender.pubkey(),
646 recipient.pubkey(),
647 8,
648 &mut rng,
649 )
650 .unwrap();
651
652 assert_eq!(frames.len(), 1);
653 assert_eq!(frames[0].stream_id, stream_id);
654 assert_eq!(frames[0].sequence, 0);
655 assert!(frames[0].is_final);
656 assert_eq!(
657 decrypt_stream(&frames, stream_id, sender.address().into(), recipient).unwrap(),
658 Vec::<u8>::new()
659 );
660 }
661
662 #[test]
663 fn streaming_decryptor_accepts_ordered_final_frame() {
664 let sender = sender_key();
665 let recipient = recipient_key();
666 let stream_id = uuid::Uuid::new_v4();
667 let mut rng = Hc128Rng::seed_from_u64(42);
668 let mut encryptor =
669 E2eStreamEncryptor::new(stream_id, sender.pubkey(), recipient.pubkey()).unwrap();
670
671 let first = encryptor.encrypt_next(b"\0hello", &mut rng).unwrap();
672 let second = encryptor.encrypt_final(b"\xFFworld", &mut rng).unwrap();
673 let mut decryptor = E2eStreamDecryptor::new(stream_id, sender.address().into(), recipient);
674
675 let mut plaintext = decryptor.decrypt_next(&first).unwrap();
676 plaintext.extend_from_slice(&decryptor.decrypt_next(&second).unwrap());
677 decryptor.finish().unwrap();
678
679 assert_eq!(plaintext, b"\0hello\xFFworld");
680 }
681
682 #[test]
683 fn truncation_is_rejected_without_final_frame() {
684 let sender = sender_key();
685 let recipient = recipient_key();
686 let mut rng = Hc128Rng::seed_from_u64(9);
687 let stream_id = uuid::Uuid::new_v4();
688 let payload = vec![9u8; 96];
689 let mut frames = encrypt_stream_with_rng(
690 &payload,
691 stream_id,
692 sender.pubkey(),
693 recipient.pubkey(),
694 16,
695 &mut rng,
696 )
697 .unwrap();
698
699 frames.pop();
700
701 assert!(matches!(
702 decrypt_stream(&frames, stream_id, sender.address().into(), recipient),
703 Err(Error::E2eMissingFinalFrame)
704 ));
705 }
706
707 #[test]
708 fn reordered_frames_are_buffered_until_contiguous() {
709 let sender = sender_key();
710 let recipient = recipient_key();
711 let mut rng = Hc128Rng::seed_from_u64(10);
712 let stream_id = uuid::Uuid::new_v4();
713 let payload = vec![10u8; 96];
714 let mut frames = encrypt_stream_with_rng(
715 &payload,
716 stream_id,
717 sender.pubkey(),
718 recipient.pubkey(),
719 16,
720 &mut rng,
721 )
722 .unwrap();
723
724 frames.swap(0, 1);
725
726 assert_eq!(
727 decrypt_stream(&frames, stream_id, sender.address().into(), recipient).unwrap(),
728 payload
729 );
730 }
731
732 #[test]
733 fn replayed_consumed_frame_is_idempotent() {
734 let sender = sender_key();
735 let recipient = recipient_key();
736 let mut rng = Hc128Rng::seed_from_u64(16);
737 let stream_id = uuid::Uuid::new_v4();
738 let payload = vec![16u8; 96];
739 let frames = encrypt_stream_with_rng(
740 &payload,
741 stream_id,
742 sender.pubkey(),
743 recipient.pubkey(),
744 16,
745 &mut rng,
746 )
747 .unwrap();
748 let final_frame = frames.last().unwrap().clone();
749 let mut decryptor = E2eStreamDecryptor::new(stream_id, sender.address().into(), recipient);
750 let mut plaintext = Vec::new();
751
752 plaintext.extend_from_slice(&decryptor.decrypt_next(&frames[0]).unwrap());
753 assert_eq!(
754 decryptor.decrypt_next(&frames[0]).unwrap(),
755 Vec::<u8>::new()
756 );
757
758 for frame in &frames[1..] {
759 plaintext.extend_from_slice(&decryptor.decrypt_next(frame).unwrap());
760 }
761 decryptor.finish().unwrap();
762
763 assert_eq!(
764 decryptor.decrypt_next(&final_frame).unwrap(),
765 Vec::<u8>::new()
766 );
767 assert_eq!(plaintext, payload);
768 }
769
770 #[test]
771 fn replayed_buffered_frame_is_idempotent() {
772 let sender = sender_key();
773 let recipient = recipient_key();
774 let mut rng = Hc128Rng::seed_from_u64(17);
775 let stream_id = uuid::Uuid::new_v4();
776 let payload = vec![17u8; 96];
777 let frames = encrypt_stream_with_rng(
778 &payload,
779 stream_id,
780 sender.pubkey(),
781 recipient.pubkey(),
782 16,
783 &mut rng,
784 )
785 .unwrap();
786 let mut decryptor = E2eStreamDecryptor::new(stream_id, sender.address().into(), recipient);
787 let mut plaintext = Vec::new();
788
789 assert_eq!(
790 decryptor.decrypt_next(&frames[1]).unwrap(),
791 Vec::<u8>::new()
792 );
793 assert_eq!(
794 decryptor.decrypt_next(&frames[1]).unwrap(),
795 Vec::<u8>::new()
796 );
797
798 for frame in &frames {
799 plaintext.extend_from_slice(&decryptor.decrypt_next(frame).unwrap());
800 }
801 decryptor.finish().unwrap();
802
803 assert_eq!(plaintext, payload);
804 }
805
806 #[test]
807 fn future_frame_outside_reorder_window_is_rejected() {
808 let sender = sender_key();
809 let recipient = recipient_key();
810 let mut rng = Hc128Rng::seed_from_u64(18);
811 let stream_id = uuid::Uuid::new_v4();
812 let frames = encrypt_stream_with_rng(
813 &[18u8; 5],
814 stream_id,
815 sender.pubkey(),
816 recipient.pubkey(),
817 1,
818 &mut rng,
819 )
820 .unwrap();
821 let mut decryptor = E2eStreamDecryptor::with_reorder_window(
822 stream_id,
823 sender.address().into(),
824 recipient,
825 2,
826 );
827
828 assert!(matches!(
829 decryptor.decrypt_next(&frames[3]),
830 Err(Error::E2eFrameReorderWindowExceeded {
831 next_sequence: 0,
832 actual: 3,
833 window: 2
834 })
835 ));
836 }
837
838 #[test]
839 fn final_frame_can_arrive_before_gap_is_filled() {
840 let sender = sender_key();
841 let recipient = recipient_key();
842 let mut rng = Hc128Rng::seed_from_u64(14);
843 let stream_id = uuid::Uuid::new_v4();
844 let payload = vec![14u8; 96];
845 let mut frames = encrypt_stream_with_rng(
846 &payload,
847 stream_id,
848 sender.pubkey(),
849 recipient.pubkey(),
850 16,
851 &mut rng,
852 )
853 .unwrap();
854 frames.rotate_right(1);
855
856 let mut decryptor = E2eStreamDecryptor::new(stream_id, sender.address().into(), recipient);
857 let mut plaintext = Vec::new();
858
859 assert_eq!(
860 decryptor.decrypt_next(&frames[0]).unwrap(),
861 Vec::<u8>::new()
862 );
863 assert!(matches!(
864 decryptor.finish(),
865 Err(Error::E2eMissingFinalFrame)
866 ));
867
868 for frame in &frames[1..] {
869 plaintext.extend_from_slice(&decryptor.decrypt_next(frame).unwrap());
870 }
871 decryptor.finish().unwrap();
872
873 assert_eq!(plaintext, payload);
874 }
875
876 #[test]
877 fn frame_after_buffered_final_is_rejected() {
878 let sender = sender_key();
879 let recipient = recipient_key();
880 let mut rng = Hc128Rng::seed_from_u64(15);
881 let stream_id = uuid::Uuid::new_v4();
882 let frames = encrypt_stream_with_rng(
883 &[15u8; 96],
884 stream_id,
885 sender.pubkey(),
886 recipient.pubkey(),
887 16,
888 &mut rng,
889 )
890 .unwrap();
891 let final_frame = frames.last().unwrap();
892 let mut frame_after_final = frames.first().unwrap().clone();
893 frame_after_final.sequence = final_frame.sequence.checked_add(1).unwrap();
894
895 let mut decryptor = E2eStreamDecryptor::new(stream_id, sender.address().into(), recipient);
896 assert_eq!(
897 decryptor.decrypt_next(final_frame).unwrap(),
898 Vec::<u8>::new()
899 );
900 assert!(matches!(
901 decryptor.decrypt_next(&frame_after_final),
902 Err(Error::E2eFrameAfterFinal)
903 ));
904 }
905
906 #[test]
907 fn wrong_stream_id_is_rejected() {
908 let sender = sender_key();
909 let recipient = recipient_key();
910 let mut rng = Hc128Rng::seed_from_u64(12);
911 let stream_id = uuid::Uuid::new_v4();
912 let other_stream_id = uuid::Uuid::new_v4();
913 let frames = encrypt_stream_with_rng(
914 b"hello",
915 stream_id,
916 sender.pubkey(),
917 recipient.pubkey(),
918 16,
919 &mut rng,
920 )
921 .unwrap();
922
923 assert!(matches!(
924 decrypt_stream(&frames, other_stream_id, sender.address().into(), recipient),
925 Err(Error::E2eStreamIdMismatch { .. })
926 ));
927 }
928
929 #[test]
930 fn wrong_sender_key_is_rejected() {
931 let sender = sender_key();
932 let recipient = recipient_key();
933 let mut rng = Hc128Rng::seed_from_u64(11);
934 let stream_id = uuid::Uuid::new_v4();
935 let payload = vec![11u8; 32];
936 let mut frames = encrypt_stream_with_rng(
937 &payload,
938 stream_id,
939 sender.pubkey(),
940 recipient.pubkey(),
941 32,
942 &mut rng,
943 )
944 .unwrap();
945 frames[0].sender_public_key = recipient.pubkey();
946
947 assert!(matches!(
948 decrypt_stream(&frames, stream_id, sender.address().into(), recipient),
949 Err(Error::E2ePublicKeyDidMismatch { .. })
950 ));
951 }
952}