1use core::future::Future;
32use core::marker::PhantomData;
33
34use vitaminc_protected::{Controlled, Protected};
35
36use crate::frame::PassthroughFrame;
37use crate::leaf::{self, ScalarError};
38use crate::plaintext::{
39 Encrypt, Error, FixedType, Logical, MapSink, Physical, SeqSink, Sink, SourceError, Step,
40};
41use crate::seal::{
42 seal_all, seal_all_async, AsyncCipher, Cipher, LeafCount, Node, Pending, SealError,
43};
44use crate::shape::{
45 self, refuse, Locate, MapRules, Refusal, Root, SeqRules, Settle, Unencryptable,
46};
47use crate::{CipherText, Context, IntoAad};
48
49#[derive(Debug, thiserror::Error)]
76pub enum EncryptError<E> {
77 #[error("cipher failed: {0}")]
79 Cipher(E),
80 #[error("source failed: {0}")]
83 Source(#[source] SourceError),
84 #[error("cannot encrypt: {0}")]
87 Shape(Refusal),
88 #[error("cipher returned {0}")]
91 Count(LeafCount),
92}
93
94impl<E> Error for EncryptError<E> {
95 fn source(error: SourceError) -> Self {
96 Self::Source(error)
97 }
98}
99
100impl<E> From<Refusal> for EncryptError<E> {
101 fn from(refusal: Refusal) -> Self {
102 Self::Shape(refusal)
103 }
104}
105
106impl<E> From<SealError<E>> for EncryptError<E> {
107 fn from(error: SealError<E>) -> Self {
108 match error {
109 SealError::Cipher(e) => Self::Cipher(e),
110 SealError::Count(count) => Self::Count(count),
111 }
112 }
113}
114
115pub fn plan<'a, P, E, A>(value: P, aad: A) -> Result<Node<Pending>, EncryptError<E>>
118where
119 P: Encrypt,
120 A: IntoAad<'a>,
121{
122 value.visit(EncryptSink::root(aad))
123}
124
125pub fn encrypt<'a, P, C, A>(
127 value: P,
128 cipher: &C,
129 aad: A,
130) -> Result<Node<C::Leaf>, EncryptError<C::Error>>
131where
132 P: Encrypt,
133 C: Cipher,
134 A: IntoAad<'a>,
135{
136 Ok(seal_all(cipher, plan(value, aad)?)?)
137}
138
139pub fn encrypt_async<'a, 'c, P, C, A>(
146 value: P,
147 cipher: &'c C,
148 aad: A,
149) -> impl Future<Output = Result<Node<C::Leaf>, EncryptError<C::Error>>> + Send + 'c
150where
151 P: Encrypt,
152 C: AsyncCipher,
153 A: IntoAad<'a>,
154{
155 let planned = plan(value, aad);
156 async move { Ok(seal_all_async(cipher, planned?).await?) }
157}
158
159pub trait Slot: Locate {
162 type Ok;
163 fn put(self, node: Node<Pending>) -> Self::Ok;
164}
165
166impl Slot for Root {
167 type Ok = Node<Pending>;
168 fn put(self, node: Node<Pending>) -> Node<Pending> {
169 node
170 }
171}
172
173fn settle(settle: Settle<'_>, node: &Node<Pending>) {
175 if matches!(node, CipherText::Passthrough(_)) {
176 settle.passthrough();
177 } else {
178 settle.sealed();
179 }
180}
181
182pub struct Push<'s> {
185 items: &'s mut Vec<Node<Pending>>,
186 settle: Settle<'s>,
187 parent: &'s dyn Locate,
188}
189
190impl Locate for Push<'_> {
191 fn locate(&self, steps: &mut Vec<Step>) {
192 self.settle.locate_under(self.parent, steps);
193 }
194}
195
196impl Slot for Push<'_> {
197 type Ok = ();
198 fn put(self, node: Node<Pending>) {
199 settle(self.settle, &node);
200 self.items.push(node);
201 }
202}
203
204pub struct Entry<'s> {
208 entries: &'s mut Vec<(String, Node<Pending>)>,
209 settle: Settle<'s>,
210 parent: &'s dyn Locate,
211}
212
213impl Locate for Entry<'_> {
214 fn locate(&self, steps: &mut Vec<Step>) {
215 self.settle.locate_under(self.parent, steps);
216 }
217}
218
219impl Slot for Entry<'_> {
220 type Ok = ();
221 fn put(self, node: Node<Pending>) {
224 settle(self.settle, &node);
225 self.entries.push((String::new(), node));
226 }
227}
228
229pub struct EncryptSink<E, O = Root> {
235 aad: Context<'static>,
236 out: O,
237 depth: usize,
239 _error: PhantomData<E>,
240}
241
242impl<E> EncryptSink<E, Root> {
243 pub fn root<'a>(aad: impl IntoAad<'a>) -> Self {
245 Self {
246 aad: aad.into_aad().into_owned(),
247 out: Root,
248 depth: 0,
249 _error: PhantomData,
250 }
251 }
252}
253
254impl<E, O: Slot> EncryptSink<E, O> {
255 fn leaf(self, ty: Option<Logical>, v: Physical<'_>) -> Result<O::Ok, EncryptError<E>> {
257 shape::scalar(self.depth, ty, &v, &self.out)?;
258 let mut plaintext = Protected::new(Vec::with_capacity(leaf::max_len(&v)));
260 leaf::write_leaf(ty, v, plaintext.risky_inner_mut()).map_err(|e| {
261 refuse(
262 &self.out,
263 match e {
264 ScalarError::Invalid => Unencryptable::InvalidValue,
265 ScalarError::TooLong | ScalarError::Malformed => Unencryptable::Unwritable,
266 },
267 )
268 })?;
269 Ok(self.out.put(CipherText::Single(Pending {
270 plaintext,
271 aad: self.aad,
272 })))
273 }
274
275 fn typed_null_leaf(self, ty: FixedType) -> Result<O::Ok, EncryptError<E>> {
278 shape::scalar(self.depth, None, &Physical::Null, &self.out)?;
279 let mut plaintext = Protected::new(Vec::with_capacity(leaf::typed_null_len(ty)));
280 leaf::write_typed_null(ty, plaintext.risky_inner_mut());
281 Ok(self.out.put(CipherText::Single(Pending {
282 plaintext,
283 aad: self.aad,
284 })))
285 }
286}
287
288fn marker(aad: Context<'static>) -> Pending {
289 Pending {
290 plaintext: Protected::new(Vec::new()),
291 aad,
292 }
293}
294
295impl<E, O: Slot> Sink for EncryptSink<E, O> {
296 type Ok = O::Ok;
297 type Error = EncryptError<E>;
298 type Seq = EncryptSeq<E, O>;
299 type Map = EncryptMap<E, O>;
300
301 fn scalar(self, ty: Option<Logical>, v: Physical<'_>) -> Result<O::Ok, Self::Error> {
302 self.leaf(ty, v)
303 }
304
305 fn typed_null(self, ty: FixedType) -> Result<O::Ok, Self::Error> {
306 self.typed_null_leaf(ty)
307 }
308
309 fn seq(self, len: Option<usize>) -> Result<Self::Seq, Self::Error> {
310 shape::container(self.depth, &self.out)?;
311 Ok(EncryptSeq {
312 element_aad: self.aad.for_sequence_element(),
313 aad: self.aad,
314 items: Vec::with_capacity(len.unwrap_or(0).min(1024)),
315 rules: SeqRules::new(),
316 out: self.out,
317 depth: self.depth,
318 _error: PhantomData,
319 })
320 }
321
322 fn map(self, _len: Option<usize>) -> Result<Self::Map, Self::Error> {
323 shape::container(self.depth, &self.out)?;
324 Ok(EncryptMap {
325 aad: self.aad,
326 entries: Vec::new(),
327 rules: MapRules::new(),
328 out: self.out,
329 depth: self.depth,
330 _error: PhantomData,
331 })
332 }
333
334 fn passthrough<P: Encrypt + Clone>(self, value: P) -> Result<O::Ok, Self::Error> {
336 let frame = shape::frame_passthrough(value, self.depth, &self.out, EncryptError::Shape)?;
337 Ok(self.out.put(CipherText::Passthrough(PassthroughFrame(
338 frame.risky_ref().to_vec(),
339 ))))
340 }
341}
342
343pub struct EncryptSeq<E, O> {
344 aad: Context<'static>,
345 element_aad: Context<'static>,
346 items: Vec<Node<Pending>>,
347 rules: SeqRules,
348 out: O,
349 depth: usize,
350 _error: PhantomData<E>,
351}
352
353impl<E, O: Slot> SeqSink for EncryptSeq<E, O> {
354 type Ok = O::Ok;
355 type Error = EncryptError<E>;
356 type Element<'s>
357 = EncryptSink<E, Push<'s>>
358 where
359 Self: 's;
360
361 fn element(&mut self) -> Result<Self::Element<'_>, Self::Error> {
362 Ok(EncryptSink {
363 aad: self.element_aad.clone(),
364 out: Push {
365 settle: self.rules.element(&self.out)?,
366 items: &mut self.items,
367 parent: &self.out,
368 },
369 depth: self.depth + 1,
370 _error: PhantomData,
371 })
372 }
373
374 fn end(self) -> Result<O::Ok, Self::Error> {
375 self.rules.end(&self.out)?;
376 let node = if self.items.is_empty() {
377 CipherText::EmptySequence(marker(self.aad.for_empty_sequence()))
378 } else {
379 CipherText::Sequence(self.items)
380 };
381 Ok(self.out.put(node))
382 }
383}
384
385pub struct EncryptMap<E, O> {
386 aad: Context<'static>,
387 entries: Vec<(String, Node<Pending>)>,
389 rules: MapRules,
391 out: O,
392 depth: usize,
393 _error: PhantomData<E>,
394}
395
396impl<E, O: Slot> MapSink for EncryptMap<E, O> {
397 type Ok = O::Ok;
398 type Error = EncryptError<E>;
399 type Entry<'s>
400 = EncryptSink<E, Entry<'s>>
401 where
402 Self: 's;
403
404 fn entry(&mut self, key: &str) -> Result<Self::Entry<'_>, Self::Error> {
405 Ok(EncryptSink {
406 aad: self.aad.for_map_entry(key),
407 out: Entry {
408 settle: self.rules.entry(key, &self.out)?,
409 entries: &mut self.entries,
410 parent: &self.out,
411 },
412 depth: self.depth + 1,
413 _error: PhantomData,
414 })
415 }
416
417 fn end(mut self) -> Result<O::Ok, Self::Error> {
418 self.rules.end(&self.out)?;
419 for (key, at) in self.rules.into_keys() {
420 if let Some((slot, _)) = self.entries.get_mut(at) {
421 *slot = key;
422 }
423 }
424 let node = if self.entries.is_empty() {
425 CipherText::EmptyMap(marker(self.aad.for_empty_map()))
426 } else {
427 CipherText::Map(self.entries)
428 };
429 Ok(self.out.put(node))
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use crate::frame::{FrameWriteError, MAX_DEPTH};
437 use crate::plaintext::{render, visit_seq, Int, Raw};
438 use crate::seal::leaves_with_aad;
439 use crate::seal::mock::{Mock, Refused};
440 use crate::Tee;
441 use std::collections::BTreeMap;
442
443 #[test]
444 fn a_source_error_inside_a_passthrough_stays_a_source_error() {
445 #[derive(Clone)]
446 struct Throws;
447 impl Encrypt for Throws {
448 fn visit<S: Sink>(self, _: S) -> Result<S::Ok, S::Error> {
449 Err(S::Error::source(SourceError::new(core::fmt::Error)))
450 }
451 }
452 struct Doc;
453 impl Encrypt for Doc {
454 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
455 sink.passthrough(Throws)
456 }
457 }
458 assert!(matches!(
459 plan::<_, (), _>(Doc, "ctx"),
460 Err(EncryptError::Source(_))
461 ));
462 }
463
464 fn planned<P: Encrypt>(p: P) -> Result<Node<Pending>, EncryptError<Refused>> {
465 plan(p, "ctx")
466 }
467
468 fn refusal<T, E>(planned: Result<T, EncryptError<E>>) -> Option<(Unencryptable, String)> {
471 match planned {
472 Err(EncryptError::Shape(refusal)) => Some((refusal.reason(), render(refusal.path()))),
473 _ => None,
474 }
475 }
476
477 fn refused(reason: Unencryptable, at: &str) -> Option<(Unencryptable, String)> {
478 Some((reason, at.to_owned()))
479 }
480
481 #[test]
482 fn a_scalar_is_one_tagged_leaf_under_the_bare_aad() {
483 let node = planned(34u32).expect("plans");
484 let leaves = leaves_with_aad(node, &"ctx".into_aad());
485 assert_eq!(leaves.len(), 1);
486 assert_eq!(leaves[0].0.plaintext.risky_ref(), &[0x06, 34, 0, 0, 0]);
487 assert_eq!(leaves[0].1, "ctx".into_aad());
488 }
489
490 #[test]
491 fn none_is_a_null_leaf_not_a_marker() {
492 let node = planned(None::<String>).expect("plans");
493 assert!(matches!(node, CipherText::Single(_)));
494 let leaves = leaves_with_aad(node, &"ctx".into_aad());
495 assert_eq!(leaves[0].0.plaintext.risky_ref(), &[0x00]);
496 assert_eq!(leaves[0].1, "ctx".into_aad());
497 }
498
499 #[test]
500 fn a_fixed_width_none_is_a_typed_null_as_long_as_a_some() {
501 let node = planned(None::<u32>).expect("plans");
502 assert!(matches!(node, CipherText::Single(_)));
503 let leaves = leaves_with_aad(node, &"ctx".into_aad());
504 assert_eq!(leaves[0].0.plaintext.risky_ref(), &[0x15, 0x06, 0, 0, 0]);
505 assert_eq!(leaves[0].1, "ctx".into_aad());
506 let some = leaves_with_aad(planned(Some(34u32)).expect("plans"), &"ctx".into_aad());
507 assert_eq!(some[0].0.plaintext.risky_ref().len(), 5);
508 }
509
510 #[test]
511 fn a_typed_null_too_deep_is_refused_like_a_scalar() {
512 use crate::plaintext::{FixedType, Width};
513 let sink = EncryptSink::<Refused, Root> {
514 aad: "ctx".into_aad().into_owned(),
515 out: Root,
516 depth: crate::frame::MAX_DEPTH + 1,
517 _error: PhantomData,
518 };
519 let err = sink
520 .typed_null(FixedType::plain(Width::I8).expect("fixed"))
521 .err()
522 .expect("too deep");
523 assert!(matches!(err, EncryptError::Shape(_)), "{err:?}");
524 }
525
526 #[test]
527 fn empty_containers_are_markers() {
528 let node = planned(Vec::<u32>::new()).expect("plans");
529 assert!(matches!(node, CipherText::EmptySequence(_)));
530 let node = planned(BTreeMap::<&str, u32>::new()).expect("plans");
531 assert!(matches!(node, CipherText::EmptyMap(_)));
532 }
533
534 #[test]
535 fn duplicate_keys_are_refused() {
536 assert_eq!(
538 refusal(planned(Dup)),
539 refused(Unencryptable::DuplicateKey, ".k")
540 );
541 }
542
543 #[test]
544 fn an_all_passthrough_container_is_refused() {
545 struct Pass;
546 impl Encrypt for Pass {
547 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
548 sink.passthrough("public")
549 }
550 }
551 assert_eq!(
552 refusal(planned(vec![Pass])),
553 refused(Unencryptable::SequenceHasNothingSealed, "")
554 );
555 let mut map = BTreeMap::new();
556 map.insert("note", Pass);
557 assert_eq!(
558 refusal(planned(map)),
559 refused(Unencryptable::MapHasNothingSealed, "")
560 );
561 struct Mixed;
563 impl Encrypt for Mixed {
564 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
565 let mut seq = sink.seq(Some(2))?;
566 Pass.visit(seq.element()?)?;
567 1u32.visit(seq.element()?)?;
568 seq.end()
569 }
570 }
571 let node = planned(Mixed).expect("plans");
572 match node {
573 CipherText::Sequence(items) => {
574 assert!(
575 matches!(items[0], CipherText::Passthrough(ref f) if f.0 == [0x0A, 6, 0, 0, 0, b'p', b'u', b'b', b'l', b'i', b'c'])
576 );
577 assert!(matches!(items[1], CipherText::Single(_)));
578 }
579 _ => panic!("expected a sequence"),
580 }
581 }
582
583 #[test]
584 fn an_invalid_logical_value_is_refused() {
585 let bad = Raw(Some(Logical::Uuid), Physical::Bytes(&[0; 3]));
586 assert_eq!(
587 refusal(planned(bad)),
588 refused(Unencryptable::InvalidValue, "")
589 );
590 }
591
592 #[test]
593 fn a_source_error_stops_the_walk() {
594 struct Failing;
595 impl Encrypt for Failing {
596 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
597 let mut seq = sink.seq(None)?;
598 1u32.visit(seq.element()?)?;
599 Err(S::Error::source(SourceError::msg("getter threw")))
600 }
601 }
602 match encrypt(Failing, &Mock, ()) {
603 Err(EncryptError::Source(e)) => assert_eq!(e.to_string(), "getter threw"),
604 other => panic!("{other:?}"),
605 }
606 }
607
608 struct Count<'c, E>(&'c mut usize, PhantomData<E>);
611
612 impl<E: Error> Sink for Count<'_, E> {
613 type Ok = ();
614 type Error = E;
615 type Seq = Self;
616 type Map = Self;
617 fn scalar(self, _: Option<Logical>, _: Physical<'_>) -> Result<(), E> {
618 *self.0 += 1;
619 Ok(())
620 }
621 fn typed_null(self, _: FixedType) -> Result<(), E> {
622 *self.0 += 1;
623 Ok(())
624 }
625 fn seq(self, _: Option<usize>) -> Result<Self, E> {
626 Ok(self)
627 }
628 fn map(self, _: Option<usize>) -> Result<Self, E> {
629 Ok(self)
630 }
631 fn passthrough<P: Encrypt + Clone>(self, _: P) -> Result<(), E> {
632 Ok(())
633 }
634 }
635
636 impl<E: Error> SeqSink for Count<'_, E> {
637 type Ok = ();
638 type Error = E;
639 type Element<'s>
640 = Count<'s, E>
641 where
642 Self: 's;
643 fn element(&mut self) -> Result<Count<'_, E>, E> {
644 Ok(Count(self.0, PhantomData))
645 }
646 fn end(self) -> Result<(), E> {
647 Ok(())
648 }
649 }
650
651 impl<E: Error> MapSink for Count<'_, E> {
652 type Ok = ();
653 type Error = E;
654 type Entry<'s>
655 = Count<'s, E>
656 where
657 Self: 's;
658 fn entry(&mut self, _: &str) -> Result<Count<'_, E>, E> {
659 Ok(Count(self.0, PhantomData))
660 }
661 fn end(self) -> Result<(), E> {
662 Ok(())
663 }
664 }
665
666 #[test]
667 fn the_sink_can_be_teed_before_any_cipher_exists() {
668 let mut scalars = 0;
669 let (node, ()) = vec![1u32, 2]
670 .visit(Tee(
671 EncryptSink::<Refused>::root("ctx"),
672 Count(&mut scalars, PhantomData),
673 ))
674 .expect("walks");
675 assert_eq!(scalars, 2);
676 assert_eq!(leaves_with_aad(node, &"ctx".into_aad()).len(), 2);
677 }
678
679 #[test]
680 fn a_length_hint_is_only_a_hint() {
681 struct Unsized;
682 impl Encrypt for Unsized {
683 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
684 visit_seq(
685 sink,
686 None,
687 vec![Int::U8(1), Int::U8(2)]
688 .into_iter()
689 .map(|i| Raw(None, Physical::Int(i))),
690 )
691 }
692 }
693 let node = planned(Unsized).expect("plans");
694 assert_eq!(leaves_with_aad(node, &"ctx".into_aad()).len(), 2);
695 }
696
697 #[derive(Clone)]
699 struct DropsAChild {
700 map: bool,
701 }
702
703 impl Encrypt for DropsAChild {
704 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
705 if self.map {
706 let mut map = sink.map(None)?;
707 let _ = map.entry("a")?;
708 1u16.visit(map.entry("b")?)?;
709 map.end()
710 } else {
711 let mut seq = sink.seq(None)?;
712 1u16.visit(seq.element()?)?;
713 let _ = seq.element()?;
714 seq.end()
715 }
716 }
717 }
718
719 #[test]
720 fn a_dropped_child_sink_is_refused_as_the_frame_writer_refuses_it() {
721 for map in [false, true] {
722 assert_eq!(
723 refusal(planned(DropsAChild { map })),
724 refused(Unencryptable::DroppedChild, "")
725 );
726 assert!(matches!(
727 crate::frame::to_frame(DropsAChild { map }),
728 Err(FrameWriteError::Unwritten)
729 ));
730 }
731 struct DropsThenWrites;
733 impl Encrypt for DropsThenWrites {
734 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
735 let mut seq = sink.seq(None)?;
736 let _ = seq.element()?;
737 1u16.visit(seq.element()?)?;
738 seq.end()
739 }
740 }
741 assert_eq!(
742 refusal(planned(DropsThenWrites)),
743 refused(Unencryptable::DroppedChild, "")
744 );
745 }
746
747 #[derive(Clone)]
749 struct Unclosed;
750
751 impl Encrypt for Unclosed {
752 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
753 let mut outer = sink.seq(None)?;
754 let mut inner = outer.element()?.seq(None)?;
755 1u32.visit(inner.element()?)?;
756 drop(inner);
758 outer.end()
759 }
760 }
761
762 #[test]
763 fn a_passthrough_that_does_not_frame_is_refused_at_encryption() {
764 struct Doc;
765 impl Encrypt for Doc {
766 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
767 let mut seq = sink.seq(Some(2))?;
768 1u32.visit(seq.element()?)?;
769 seq.element()?.passthrough(Unclosed)?;
770 seq.end()
771 }
772 }
773 assert!(matches!(
774 crate::frame::to_frame(Unclosed),
775 Err(FrameWriteError::Unfinished)
776 ));
777 assert_eq!(
779 refusal(planned(Doc)),
780 refused(Unencryptable::UnframablePassthrough, "[1]")
781 );
782 }
783
784 #[derive(Clone, Copy)]
785 enum Kind {
786 Seq,
787 Map,
788 Pass,
789 Scalar,
790 }
791
792 #[derive(Clone, Copy)]
796 struct Nest(Kind, usize);
797
798 impl Encrypt for Nest {
799 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
800 let Nest(kind, n) = self;
801 if n == 0 {
802 return match kind {
803 Kind::Scalar => 1u16.visit(sink),
804 _ => sink.seq(Some(0))?.end(),
805 };
806 }
807 let inner = Nest(kind, n - 1);
808 match kind {
809 Kind::Seq | Kind::Scalar => {
810 let mut seq = sink.seq(Some(1))?;
811 inner.visit(seq.element()?)?;
812 seq.end()
813 }
814 Kind::Map => {
815 let mut map = sink.map(Some(1))?;
816 inner.visit(map.entry("k")?)?;
817 map.end()
818 }
819 Kind::Pass => sink.passthrough(inner),
820 }
821 }
822 }
823
824 #[test]
825 fn depth_is_bounded_as_the_transport_decoder_bounds_it() {
826 for (kind, step) in [
829 (Kind::Seq, "[0]"),
830 (Kind::Map, ".k"),
831 (Kind::Pass, ""),
832 (Kind::Scalar, "[0]"),
833 ] {
834 assert!(planned(Nest(kind, MAX_DEPTH)).is_ok());
835 assert_eq!(
836 refusal(planned(Nest(kind, MAX_DEPTH + 1))),
837 refused(Unencryptable::TooDeep, &step.repeat(MAX_DEPTH + 1))
838 );
839 }
840 struct Wrapped(usize);
844 impl Encrypt for Wrapped {
845 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
846 let mut seq = sink.seq(Some(2))?;
847 1u16.visit(seq.element()?)?;
848 seq.element()?.passthrough(Nest(Kind::Seq, self.0))?;
849 seq.end()
850 }
851 }
852 assert!(planned(Wrapped(MAX_DEPTH - 2)).is_ok());
853 assert_eq!(
854 refusal(planned(Wrapped(MAX_DEPTH - 1))),
855 refused(Unencryptable::TooDeep, "[1]")
856 );
857 }
858
859 struct Users<P>(P);
862
863 impl<P: Encrypt> Encrypt for Users<P> {
864 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
865 let mut map = sink.map(Some(2))?;
866 1u32.visit(map.entry("id")?)?;
867 let mut users = map.entry("users")?.seq(Some(2))?;
868 "ada".visit(users.element()?)?;
869 self.0.visit(users.element()?)?;
870 users.end()?;
871 map.end()
872 }
873 }
874
875 struct Dup;
877
878 impl Encrypt for Dup {
879 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
880 let mut map = sink.map(None)?;
881 1u32.visit(map.entry("k")?)?;
882 2u32.visit(map.entry("k")?)?;
883 map.end()
884 }
885 }
886
887 #[derive(Clone)]
889 struct OnlyPass {
890 map: bool,
891 }
892
893 impl Encrypt for OnlyPass {
894 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
895 if self.map {
896 let mut map = sink.map(Some(1))?;
897 map.entry("note")?.passthrough("public")?;
898 map.end()
899 } else {
900 let mut seq = sink.seq(Some(1))?;
901 seq.element()?.passthrough("public")?;
902 seq.end()
903 }
904 }
905 }
906
907 #[test]
908 fn a_refusal_names_the_node_below_the_root() {
909 let invalid = Raw(Some(Logical::Uuid), Physical::Bytes(&[0; 3]));
910 assert_eq!(
911 refusal(planned(Users(invalid))),
912 refused(Unencryptable::InvalidValue, ".users[1]")
913 );
914 assert_eq!(
915 refusal(planned(Users(Dup))),
916 refused(Unencryptable::DuplicateKey, ".users[1].k")
917 );
918 assert_eq!(
919 refusal(planned(Users(OnlyPass { map: false }))),
920 refused(Unencryptable::SequenceHasNothingSealed, ".users[1]")
921 );
922 assert_eq!(
923 refusal(planned(Users(OnlyPass { map: true }))),
924 refused(Unencryptable::MapHasNothingSealed, ".users[1]")
925 );
926 for map in [false, true] {
927 assert_eq!(
928 refusal(planned(Users(DropsAChild { map }))),
929 refused(Unencryptable::DroppedChild, ".users[1]")
930 );
931 }
932 assert_eq!(
933 refusal(planned(Users(Nest(Kind::Seq, MAX_DEPTH - 1)))),
934 refused(
935 Unencryptable::TooDeep,
936 &format!(".users[1]{}", "[0]".repeat(MAX_DEPTH - 1))
937 )
938 );
939 let mut scalars = 0;
941 let invalid = Raw(Some(Logical::Uuid), Physical::Bytes(&[0; 3]));
942 let teed = Users(invalid).visit(Tee(
943 EncryptSink::<Refused>::root("ctx"),
944 Count(&mut scalars, PhantomData),
945 ));
946 assert_eq!(
947 refusal(teed),
948 refused(Unencryptable::InvalidValue, ".users[1]")
949 );
950 }
951
952 #[test]
953 fn a_refusal_shows_its_path_only_on_request() {
954 let err = plan::<_, &str, _>(Users(Dup), "ctx")
955 .err()
956 .expect("refused");
957 assert_eq!(err.to_string(), "cannot encrypt: duplicate key");
960 let debug = format!("{err:?}");
961 assert!(!debug.contains("users"), "{debug}");
962 assert!(debug.contains("DuplicateKey"), "{debug}");
963 assert!(debug.contains("Index(1)"), "{debug}");
964 assert!(debug.contains("Key(\"***\")"), "{debug}");
965 let EncryptError::Shape(refusal) = err else {
966 panic!("{err:?}")
967 };
968 assert_eq!(
969 refusal.path().steps(),
970 [
971 Step::Key("users".into()),
972 Step::Index(1),
973 Step::Key("k".into())
974 ]
975 );
976 }
977
978 #[test]
979 fn errors_say_what_failed_without_the_value() {
980 struct Secret;
981 impl Locate for Secret {
982 fn locate(&self, steps: &mut Vec<Step>) {
983 steps.push(Step::Key("secret".into()));
984 }
985 }
986 let shape = |reason| EncryptError::<&str>::Shape(refuse(&Secret, reason)).to_string();
987 assert_eq!(
988 shape(Unencryptable::DuplicateKey),
989 "cannot encrypt: duplicate key"
990 );
991 assert_eq!(
992 shape(Unencryptable::SequenceHasNothingSealed),
993 "cannot encrypt: sequence has nothing sealed"
994 );
995 assert_eq!(
996 shape(Unencryptable::MapHasNothingSealed),
997 "cannot encrypt: map has nothing sealed"
998 );
999 assert_eq!(
1000 shape(Unencryptable::InvalidValue),
1001 "cannot encrypt: value is not valid for its logical type"
1002 );
1003 assert_eq!(
1004 shape(Unencryptable::Unwritable),
1005 "cannot encrypt: value cannot be written as a leaf"
1006 );
1007 assert_eq!(
1008 shape(Unencryptable::UnframablePassthrough),
1009 "cannot encrypt: passthrough subtree cannot be framed"
1010 );
1011 assert_eq!(
1012 shape(Unencryptable::DroppedChild),
1013 "cannot encrypt: a child sink was dropped without a value"
1014 );
1015 assert_eq!(
1016 shape(Unencryptable::TooDeep),
1017 "cannot encrypt: value nests deeper than 128 levels"
1018 );
1019 assert_eq!(
1020 EncryptError::<&str>::Count(LeafCount::TooFew).to_string(),
1021 "cipher returned too few leaves"
1022 );
1023 assert_eq!(
1024 EncryptError::<&str>::Cipher("down").to_string(),
1025 "cipher failed: down"
1026 );
1027 assert_eq!(
1028 EncryptError::<&str>::Source(SourceError::msg("getter threw")).to_string(),
1029 "source failed: getter threw"
1030 );
1031 fn is_error<E: core::error::Error>(_: &E) {}
1032 is_error(&EncryptError::<&str>::Cipher("down"));
1033 use core::error::Error as _;
1036 let sourced = EncryptError::<&str>::Source(SourceError::msg("getter threw"));
1037 assert_eq!(
1038 sourced.source().map(|cause| cause.to_string()),
1039 Some("getter threw".into())
1040 );
1041 assert!(EncryptError::<&str>::Cipher("down").source().is_none());
1042 }
1043}