1#[cfg(feature = "alloc")]
82use alloc::vec::Vec;
83
84use zeroize::Zeroize;
85
86use crate::classic::crypto_secretstream_xchacha20poly1305::{
87 State, ciphertext_len_from_message_len, crypto_secretstream_xchacha20poly1305_init_pull,
88 crypto_secretstream_xchacha20poly1305_init_push, crypto_secretstream_xchacha20poly1305_pull,
89 crypto_secretstream_xchacha20poly1305_push, crypto_secretstream_xchacha20poly1305_rekey,
90 message_len_from_ciphertext_len,
91};
92use crate::constants::{
93 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES,
94 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES,
95};
96use crate::error::Error;
97use crate::types::*;
98
99mod tag;
100pub use tag::Tag;
101
102mod sealed {
103 pub trait Sealed {}
104}
105
106pub trait Mode: sealed::Sealed {}
110
111pub struct Push;
113pub struct Pull;
115
116impl sealed::Sealed for Push {}
117impl sealed::Sealed for Pull {}
118impl Mode for Push {}
119impl Mode for Pull {}
120
121pub type Key = StackByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES>;
123pub type Header = StackByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES>;
125
126#[cfg(any(
127 all(feature = "protected", any(unix, windows)),
128 all(doc, not(doctest), feature = "std")
129))]
130#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
131pub mod protected {
132 use super::*;
183 pub use crate::protected::*;
184
185 pub type Key = HeapByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES>;
188 pub type Header = HeapByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES>;
191}
192
193#[derive(PartialEq, Eq, Clone, Zeroize)]
195pub struct DryocStream<M> {
196 state: State,
197 phantom: core::marker::PhantomData<M>,
198}
199
200impl<M> Drop for DryocStream<M> {
201 fn drop(&mut self) {
202 self.state.zeroize()
203 }
204}
205
206impl<M> DryocStream<M> {
207 pub fn rekey(&mut self) {
216 crypto_secretstream_xchacha20poly1305_rekey(&mut self.state)
217 }
218}
219
220impl DryocStream<Push> {
221 #[must_use]
223 pub fn init_push<
224 Header: NewByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES>,
225 Key: ByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES>,
226 >(
227 key: &Key,
228 ) -> (Self, Header) {
229 let mut state = State::new();
230 let mut header = Header::new_byte_array();
231 crypto_secretstream_xchacha20poly1305_init_push(
232 &mut state,
233 header.as_mut_array(),
234 key.as_array(),
235 );
236 (
237 Self {
238 state,
239 phantom: core::marker::PhantomData,
240 },
241 header,
242 )
243 }
244
245 pub fn push<Output: NewBytes + ResizableBytes, Input: Bytes + ?Sized>(
254 &mut self,
255 message: &Input,
256 associated_data: Option<&[u8]>,
257 tag: Tag,
258 ) -> Result<Output, Error> {
259 let mut ciphertext = Output::new_bytes();
260 ciphertext.resize(
261 ciphertext_len_from_message_len(message.as_slice().len())?,
262 0,
263 );
264 crypto_secretstream_xchacha20poly1305_push(
265 &mut self.state,
266 ciphertext.as_mut_slice(),
267 message.as_slice(),
268 associated_data,
269 tag.bits(),
270 )?;
271 Ok(ciphertext)
272 }
273
274 #[cfg(feature = "alloc")]
282 pub fn push_to_vec<Input: Bytes + ?Sized>(
283 &mut self,
284 message: &Input,
285 associated_data: Option<&[u8]>,
286 tag: Tag,
287 ) -> Result<Vec<u8>, Error> {
288 self.push(message, associated_data, tag)
289 }
290}
291
292impl DryocStream<Pull> {
293 #[must_use]
295 pub fn init_pull<
296 Key: ByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES>,
297 Header: ByteArray<CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES>,
298 >(
299 key: &Key,
300 header: &Header,
301 ) -> Self {
302 let mut state = State::new();
303 crypto_secretstream_xchacha20poly1305_init_pull(
304 &mut state,
305 header.as_array(),
306 key.as_array(),
307 );
308 Self {
309 state,
310 phantom: core::marker::PhantomData,
311 }
312 }
313
314 pub fn pull<Output: NewBytes + ResizableBytes, Input: Bytes + ?Sized>(
326 &mut self,
327 ciphertext: &Input,
328 associated_data: Option<&[u8]>,
329 ) -> Result<(Output, Tag), Error> {
330 let message_len = message_len_from_ciphertext_len(ciphertext.as_slice().len())?;
331
332 let mut message = Output::new_bytes();
333 message.resize(message_len, 0);
334 let mut tag = 0u8;
335 let mut next_state = self.state.clone();
336 crypto_secretstream_xchacha20poly1305_pull(
337 &mut next_state,
338 message.as_mut_slice(),
339 &mut tag,
340 ciphertext.as_slice(),
341 associated_data,
342 )?;
343
344 let tag = match Tag::try_from(tag) {
345 Ok(tag) => tag,
346 Err(error) => {
347 message.as_mut_slice().zeroize();
348 return Err(error);
349 }
350 };
351 self.state = next_state;
352
353 Ok((message, tag))
354 }
355
356 #[cfg(feature = "alloc")]
367 pub fn pull_to_vec<Input: Bytes + ?Sized>(
368 &mut self,
369 ciphertext: &Input,
370 associated_data: Option<&[u8]>,
371 ) -> Result<(Vec<u8>, Tag), Error> {
372 self.pull(ciphertext, associated_data)
373 }
374}
375
376#[cfg(all(test, feature = "alloc"))]
377mod validation_tests {
378 use super::*;
379 use crate::constants::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
380
381 pub(super) const TAG_KAT_KEY: [u8; 32] = *b"dryoc secretstream tag kat key!!";
383 pub(super) const TAG_KAT_HEADER: [u8; 24] = *b"fixed secretstream hdr!!";
384 pub(super) type TagKat = (&'static [u8], Option<&'static [u8]>, Tag, &'static str);
386 pub(super) const TAG_KAT: [TagKat; 5] = [
390 (
391 b"message",
392 None,
393 Tag::Message,
394 "a59b40f34c0389665e80425058a5d5700805c1050cf44c1e",
395 ),
396 (
397 b"push",
398 Some(b"tag kat"),
399 Tag::Push,
400 "11f5df40c058fcf62f4caec9785bb132550a9ea0f7",
401 ),
402 (
403 b"rekey",
404 None,
405 Tag::Rekey,
406 "682581e6eb47fee588aa927a183e85660ed0f186de83",
407 ),
408 (
409 b"after rekey",
410 Some(b"tag kat"),
411 Tag::Message,
412 "54547a88d24cdb2d009376b265e49973dde328ac249762bfbc9cd83d",
413 ),
414 (
415 b"final",
416 None,
417 Tag::Final,
418 "84a19391176cc87a0e58212ec9cfe35d1d348223e901",
419 ),
420 ];
421
422 #[test]
423 fn every_tag_matches_libsodium_known_answers() {
424 let mut state = State::new();
428 crypto_secretstream_xchacha20poly1305_init_pull(&mut state, &TAG_KAT_HEADER, &TAG_KAT_KEY);
429 let mut push_stream = DryocStream::<Push> {
430 state,
431 phantom: core::marker::PhantomData,
432 };
433 let mut pull_stream =
434 DryocStream::init_pull(&Key::from(TAG_KAT_KEY), &Header::from(TAG_KAT_HEADER));
435
436 for (message, associated_data, tag, expected) in TAG_KAT {
437 let expected = hex::decode(expected).expect("hex");
438 let ciphertext = push_stream
439 .push_to_vec(message, associated_data, tag)
440 .expect("push failed");
441 assert_eq!(ciphertext, expected, "push {tag:?}");
442
443 let (decrypted, pulled_tag) = pull_stream
444 .pull_to_vec(&expected, associated_data)
445 .expect("pull failed");
446 assert_eq!((decrypted.as_slice(), pulled_tag), (message, tag));
447 }
448 }
449
450 #[test]
451 fn rustaceous_pull_rejects_every_unknown_tag_without_advancing_state() {
452 let key = Key::generate();
453 let mut push_state = State::new();
454 let mut raw_header =
455 [0u8; crate::constants::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES];
456 crypto_secretstream_xchacha20poly1305_init_push(
457 &mut push_state,
458 &mut raw_header,
459 key.as_array(),
460 );
461 let header = Header::try_from(raw_header.as_slice()).expect("header conversion failed");
462 let mut pull_stream = DryocStream::init_pull(&key, &header);
463 let original_pull_state = pull_stream.state.clone();
464 let message = b"authenticated unknown tag";
465
466 for bits in 4..=u8::MAX {
469 let mut invalid_ciphertext =
470 vec![0u8; message.len() + CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES];
471 crypto_secretstream_xchacha20poly1305_push(
472 &mut push_state.clone(),
473 &mut invalid_ciphertext,
474 message,
475 None,
476 bits,
477 )
478 .expect("classic push failed");
479
480 let error = pull_stream
481 .pull_to_vec(&invalid_ciphertext, None)
482 .expect_err("unknown tag must be rejected");
483 assert!(
484 matches!(
485 error,
486 Error::InvalidValue {
487 context: crate::ErrorContext::Tag,
488 actual,
489 ..
490 } if actual == u64::from(bits)
491 ),
492 "tag byte {bits:#04x}: {error:?}"
493 );
494 assert!(pull_stream.state == original_pull_state);
495 }
496
497 let mut valid_ciphertext =
498 vec![0u8; message.len() + CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES];
499 crypto_secretstream_xchacha20poly1305_push(
500 &mut push_state,
501 &mut valid_ciphertext,
502 message,
503 None,
504 Tag::Message.bits(),
505 )
506 .expect("classic push failed");
507 let (decrypted, tag) = pull_stream
508 .pull_to_vec(&valid_ciphertext, None)
509 .expect("state must remain usable after rejection");
510 assert_eq!(decrypted, message);
511 assert_eq!(tag, Tag::Message);
512 }
513
514 #[test]
515 fn rustaceous_pull_rejects_short_ciphertext_without_advancing_state() {
516 let key = Key::generate();
517 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
518 let c1 = push_stream
519 .push_to_vec(b"first", None, Tag::Message)
520 .expect("push failed");
521
522 let mut pull_stream = DryocStream::init_pull(&key, &header);
523 let original_state = pull_stream.state.clone();
524 for len in [0, 1, CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES - 1] {
525 let short: &[u8] = &c1[..len];
526 assert!(matches!(
527 pull_stream.pull_to_vec(&short, None),
528 Err(Error::InvalidLength {
529 context: crate::ErrorContext::Ciphertext,
530 actual,
531 ..
532 }) if actual == len
533 ));
534 assert!(pull_stream.state == original_state);
535 }
536
537 let (message, tag) = pull_stream.pull_to_vec(&c1, None).expect("pull failed");
538 assert_eq!(message, b"first");
539 assert_eq!(tag, Tag::Message);
540 }
541
542 #[test]
543 fn rustaceous_pull_out_of_order_fails_and_state_stays_usable() {
544 let key = Key::generate();
545 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
546 let aad: &[u8] = b"stream aad";
547 let c1 = push_stream
548 .push_to_vec(b"first", Some(aad), Tag::Message)
549 .expect("push failed");
550 let c2 = push_stream
551 .push_to_vec(b"second", Some(aad), Tag::Push)
552 .expect("push failed");
553 let c3 = push_stream
554 .push_to_vec(b"third", Some(aad), Tag::Final)
555 .expect("push failed");
556
557 let mut pull_stream = DryocStream::init_pull(&key, &header);
558 let initial_state = pull_stream.state.clone();
559
560 assert!(matches!(
562 pull_stream.pull_to_vec(&c2, Some(aad)),
563 Err(Error::AuthenticationFailed)
564 ));
565 assert!(matches!(
566 pull_stream.pull_to_vec(&c3, Some(aad)),
567 Err(Error::AuthenticationFailed)
568 ));
569 assert!(matches!(
571 pull_stream.pull_to_vec(&c1, None),
572 Err(Error::AuthenticationFailed)
573 ));
574 assert!(pull_stream.state == initial_state);
575
576 let (m1, t1) = pull_stream.pull_to_vec(&c1, Some(aad)).expect("pull c1");
577 assert_eq!((m1.as_slice(), t1), (&b"first"[..], Tag::Message));
578
579 let after_c1 = pull_stream.state.clone();
581 assert!(pull_stream.pull_to_vec(&c1, Some(aad)).is_err());
582 assert!(pull_stream.pull_to_vec(&c3, Some(aad)).is_err());
583 assert!(pull_stream.state == after_c1);
584
585 let (m2, t2) = pull_stream.pull_to_vec(&c2, Some(aad)).expect("pull c2");
586 assert_eq!((m2.as_slice(), t2), (&b"second"[..], Tag::Push));
587 let (m3, t3) = pull_stream.pull_to_vec(&c3, Some(aad)).expect("pull c3");
588 assert_eq!((m3.as_slice(), t3), (&b"third"[..], Tag::Final));
589 }
590
591 #[test]
592 fn manual_rekey_must_be_mirrored_by_the_pull_side() {
593 let key = Key::generate();
594 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
595 let c1 = push_stream
596 .push_to_vec(b"before rekey", None, Tag::Message)
597 .expect("push failed");
598 push_stream.rekey();
599 let c2 = push_stream
600 .push_to_vec(b"after rekey", None, Tag::Message)
601 .expect("push failed");
602 let c3 = push_stream
603 .push_to_vec(b"final", None, Tag::Final)
604 .expect("push failed");
605
606 let mut pull_stream = DryocStream::init_pull(&key, &header);
607 let (m1, _) = pull_stream.pull_to_vec(&c1, None).expect("pull c1");
608 assert_eq!(m1, b"before rekey");
609
610 let before_rekey = pull_stream.state.clone();
612 assert!(matches!(
613 pull_stream.pull_to_vec(&c2, None),
614 Err(Error::AuthenticationFailed)
615 ));
616 assert!(pull_stream.state == before_rekey);
617
618 pull_stream.rekey();
620 let (m2, t2) = pull_stream.pull_to_vec(&c2, None).expect("pull c2");
621 assert_eq!((m2.as_slice(), t2), (&b"after rekey"[..], Tag::Message));
622 let (m3, t3) = pull_stream.pull_to_vec(&c3, None).expect("pull c3");
623 assert_eq!((m3.as_slice(), t3), (&b"final"[..], Tag::Final));
624 }
625}
626
627#[cfg(all(test, dryoc_native_tests, feature = "alloc"))]
628mod tests {
629 use super::*;
630 use crate::native_test_util::{
631 SECRETSTREAM_TAG_FINAL, SECRETSTREAM_TAG_MESSAGE, SECRETSTREAM_TAG_PUSH,
632 SECRETSTREAM_TAG_REKEY, SecretStream as SOStream,
633 };
634
635 #[test]
638 fn tag_known_answers_are_libsodiums() {
639 use super::validation_tests::{TAG_KAT, TAG_KAT_HEADER, TAG_KAT_KEY};
640
641 for (tag, so_tag) in [
642 (Tag::Message, SECRETSTREAM_TAG_MESSAGE),
643 (Tag::Push, SECRETSTREAM_TAG_PUSH),
644 (Tag::Rekey, SECRETSTREAM_TAG_REKEY),
645 (Tag::Final, SECRETSTREAM_TAG_FINAL),
646 ] {
647 assert_eq!(tag.bits(), so_tag, "{tag:?}");
648 }
649
650 let mut so_push = SOStream::init_pull(&TAG_KAT_HEADER, &TAG_KAT_KEY).expect("init pull");
652 for (message, associated_data, tag, expected) in TAG_KAT {
653 assert_eq!(
654 so_push
655 .push(message, associated_data, tag.bits())
656 .expect("push"),
657 hex::decode(expected).expect("hex"),
658 "{tag:?}"
659 );
660 }
661 }
662
663 fn fixed_key() -> Key {
665 Key::from(*b"dryoc secretstream rekey key 32b")
666 }
667
668 #[test]
669 fn rekey_on_push_side_is_mirrored_by_libsodium_pull() {
670 let key = fixed_key();
671 let aad: &[u8] = b"associated";
672 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
673 let c1 = push_stream
674 .push_to_vec(b"before rekey", Some(aad), Tag::Message)
675 .expect("push failed");
676 push_stream.rekey();
677 let c2 = push_stream
678 .push_to_vec(b"after rekey", Some(aad), Tag::Push)
679 .expect("push failed");
680 let c3 = push_stream
681 .push_to_vec(b"final", Some(aad), Tag::Final)
682 .expect("push failed");
683
684 let so_header = header.as_slice();
685 let so_key = key.as_slice();
686
687 let mut unsynced = SOStream::init_pull(so_header, so_key).expect("init pull");
689 assert_eq!(
690 unsynced.pull(&c1, Some(aad)).expect("pull c1"),
691 (b"before rekey".to_vec(), SECRETSTREAM_TAG_MESSAGE)
692 );
693 assert!(unsynced.pull(&c2, Some(aad)).is_err());
694
695 let mut so_pull = SOStream::init_pull(so_header, so_key).expect("init pull");
696 assert_eq!(
697 so_pull.pull(&c1, Some(aad)).expect("pull c1"),
698 (b"before rekey".to_vec(), SECRETSTREAM_TAG_MESSAGE)
699 );
700 so_pull.rekey().expect("rekey");
701 assert_eq!(
702 so_pull.pull(&c2, Some(aad)).expect("pull c2"),
703 (b"after rekey".to_vec(), SECRETSTREAM_TAG_PUSH)
704 );
705 assert_eq!(
706 so_pull.pull(&c3, Some(aad)).expect("pull c3"),
707 (b"final".to_vec(), SECRETSTREAM_TAG_FINAL)
708 );
709 assert!(so_pull.is_finalized());
710 }
711
712 #[test]
713 fn rekey_on_libsodium_push_side_is_mirrored_by_rustaceous_pull() {
714 let key = fixed_key();
715 let (mut so_push, so_header) = SOStream::init_push(key.as_slice());
716 let c1 = so_push
717 .push(b"before rekey", None, SECRETSTREAM_TAG_MESSAGE)
718 .expect("push failed");
719 so_push.rekey().expect("rekey");
720 let c2 = so_push
721 .push(b"after rekey", None, SECRETSTREAM_TAG_REKEY)
722 .expect("push failed");
723 let c3 = so_push
724 .push(b"after tag rekey", None, SECRETSTREAM_TAG_MESSAGE)
725 .expect("push failed");
726 let c4 = so_push
727 .push(b"final", None, SECRETSTREAM_TAG_FINAL)
728 .expect("push failed");
729
730 let header = Header::try_from(so_header.as_slice()).expect("header");
731 let mut pull_stream = DryocStream::init_pull(&key, &header);
732 let (m1, t1) = pull_stream.pull_to_vec(&c1, None).expect("pull c1");
733 assert_eq!((m1.as_slice(), t1), (&b"before rekey"[..], Tag::Message));
734
735 let before_rekey = pull_stream.state.clone();
736 assert!(matches!(
737 pull_stream.pull_to_vec(&c2, None),
738 Err(Error::AuthenticationFailed)
739 ));
740 assert!(pull_stream.state == before_rekey);
741
742 pull_stream.rekey();
743 let (m2, t2) = pull_stream.pull_to_vec(&c2, None).expect("pull c2");
744 assert_eq!((m2.as_slice(), t2), (&b"after rekey"[..], Tag::Rekey));
745 let (m3, t3) = pull_stream.pull_to_vec(&c3, None).expect("pull c3");
747 assert_eq!((m3.as_slice(), t3), (&b"after tag rekey"[..], Tag::Message));
748 let (m4, t4) = pull_stream.pull_to_vec(&c4, None).expect("pull c4");
749 assert_eq!((m4.as_slice(), t4), (&b"final"[..], Tag::Final));
750 }
751
752 #[test]
753 fn libsodium_messages_pulled_out_of_order_fail_and_state_stays_usable() {
754 let key = fixed_key();
755 let (mut so_push, so_header) = SOStream::init_push(key.as_slice());
756 let c1 = so_push
757 .push(b"one", None, SECRETSTREAM_TAG_MESSAGE)
758 .expect("push");
759 let c2 = so_push
760 .push(b"two", None, SECRETSTREAM_TAG_MESSAGE)
761 .expect("push");
762 let c3 = so_push
763 .push(b"three", None, SECRETSTREAM_TAG_FINAL)
764 .expect("push");
765
766 let header = Header::try_from(so_header.as_slice()).expect("header");
767 let mut pull_stream = DryocStream::init_pull(&key, &header);
768
769 assert!(matches!(
770 pull_stream.pull_to_vec(&c2, None),
771 Err(Error::AuthenticationFailed)
772 ));
773 let (m1, _) = pull_stream.pull_to_vec(&c1, None).expect("pull c1");
774 assert_eq!(m1, b"one");
775 assert!(pull_stream.pull_to_vec(&c3, None).is_err());
776 let (m2, _) = pull_stream.pull_to_vec(&c2, None).expect("pull c2");
777 assert_eq!(m2, b"two");
778 let (m3, t3) = pull_stream.pull_to_vec(&c3, None).expect("pull c3");
779 assert_eq!((m3.as_slice(), t3), (&b"three"[..], Tag::Final));
780 }
781
782 #[test]
783 fn test_stream_push() {
784 let message1 = b"Arbitrary data to encrypt";
785 let message2 = b"split into";
786 let message3 = b"three messages";
787
788 let key = Key::generate();
790
791 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
793 let c1: Vec<u8> = push_stream
795 .push(message1, None, Tag::Message)
796 .expect("Encrypt failed");
797 let c2: Vec<u8> = push_stream
798 .push(message2, None, Tag::Message)
799 .expect("Encrypt failed");
800 let c3: Vec<u8> = push_stream
801 .push(message3, None, Tag::Final)
802 .expect("Encrypt failed");
803
804 let mut so_stream_pull =
806 SOStream::init_pull(header.as_slice(), key.as_slice()).expect("pull init failed");
807
808 let (m1, tag1) = so_stream_pull.pull(&c1, None).expect("decrypt failed");
809 let (m2, tag2) = so_stream_pull.pull(&c2, None).expect("decrypt failed");
810 let (m3, tag3) = so_stream_pull.pull(&c3, None).expect("decrypt failed");
811
812 assert_eq!(message1, m1.as_slice());
813 assert_eq!(message2, m2.as_slice());
814 assert_eq!(message3, m3.as_slice());
815
816 assert_eq!(tag1, SECRETSTREAM_TAG_MESSAGE);
817 assert_eq!(tag2, SECRETSTREAM_TAG_MESSAGE);
818 assert_eq!(tag3, SECRETSTREAM_TAG_FINAL);
819 }
820
821 #[test]
822 fn test_stream_pull() {
823 use core::convert::TryFrom;
824
825 let message1 = b"Arbitrary data to encrypt";
826 let message2 = b"split into";
827 let message3 = b"three messages";
828
829 let key = Key::generate();
831
832 let (mut so_push_stream, so_header) = SOStream::init_push(key.as_slice());
834 let c1: Vec<u8> = so_push_stream
836 .push(message1, None, SECRETSTREAM_TAG_MESSAGE)
837 .expect("Encrypt failed");
838 let c2: Vec<u8> = so_push_stream
839 .push(message2, None, SECRETSTREAM_TAG_MESSAGE)
840 .expect("Encrypt failed");
841 let c3: Vec<u8> = so_push_stream
842 .push(message3, None, SECRETSTREAM_TAG_FINAL)
843 .expect("Encrypt failed");
844
845 let mut pull_stream = DryocStream::init_pull(
847 &key,
848 &Header::try_from(so_header.as_slice()).expect("header"),
849 );
850
851 let (m1, tag1): (Vec<u8>, Tag) = pull_stream.pull(&c1, None).expect("Decrypt failed");
853 let (m2, tag2): (Vec<u8>, Tag) = pull_stream.pull(&c2, None).expect("Decrypt failed");
854 let (m3, tag3): (Vec<u8>, Tag) = pull_stream.pull(&c3, None).expect("Decrypt failed");
855
856 assert_eq!(message1, m1.as_slice());
857 assert_eq!(message2, m2.as_slice());
858 assert_eq!(message3, m3.as_slice());
859
860 assert_eq!(tag1, Tag::Message);
861 assert_eq!(tag2, Tag::Message);
862 assert_eq!(tag3, Tag::Final);
863 }
864
865 #[cfg(all(feature = "protected", any(unix, windows)))]
866 #[test]
867 fn test_protected_memory() {
868 use crate::protected::*;
869
870 let message1 = b"Arbitrary data to encrypt";
871 let message2 = b"split into";
872 let message3 = b"three messages";
873
874 let key = protected::Key::generate_locked().expect("generate locked");
876
877 let (mut push_stream, header): (_, Header) = DryocStream::init_push(&key);
879
880 let key = key
882 .munlock()
883 .expect("munlock")
884 .mprotect_noaccess()
885 .expect("mprotect");
886
887 let c1: Locked<HeapBytes> = push_stream
889 .push(message1, None, Tag::Message)
890 .expect("Encrypt failed");
891 let c2: Vec<u8> = push_stream
892 .push(message2, None, Tag::Message)
893 .expect("Encrypt failed");
894 let c3: Vec<u8> = push_stream
895 .push(message3, None, Tag::Final)
896 .expect("Encrypt failed");
897
898 let key = key.mprotect_readonly().expect("mprotect");
900
901 let mut pull_stream = DryocStream::init_pull(&key, &header);
903
904 let _key = key.mprotect_noaccess().expect("mprotect");
906
907 let (m1, tag1): (Locked<HeapBytes>, Tag) =
909 pull_stream.pull(&c1, None).expect("Decrypt failed");
910 let (m2, tag2): (Locked<HeapBytes>, Tag) =
911 pull_stream.pull(&c2, None).expect("Decrypt failed");
912 let (m3, tag3): (Locked<HeapBytes>, Tag) =
913 pull_stream.pull(&c3, None).expect("Decrypt failed");
914
915 assert_eq!(message1, m1.as_slice());
916 assert_eq!(message2, m2.as_slice());
917 assert_eq!(message3, m3.as_slice());
918
919 assert_eq!(tag1, Tag::Message);
920 assert_eq!(tag2, Tag::Message);
921 assert_eq!(tag3, Tag::Final);
922 }
923}