Skip to main content

vitaminc_aead/
frame.rs

1//! The transport frame: one value, as bytes, for crossing an FFI boundary
2//! and for carrying a passthrough subtree in the clear.
3//!
4//! A frame is a transport encoding, not a storage format. Scalars use the
5//! leaf codec in [`leaf`] in its frame form (text and bytes
6//! carry a `u32` length). Containers are framed with the tags here: an
7//! [`ARRAY`] or [`OBJECT`] byte, a `u32` little-endian count, then the
8//! children; object keys are a `u32` length and UTF-8 bytes. A
9//! [`PASSTHROUGH`] byte marks the next node as a subtree that travels in
10//! the clear.
11//!
12//! [`Frame`] is a validated, borrowed frame that implements [`Encrypt`]
13//! by walking its own bytes in place, so a host that ships frames (Go) is
14//! a source with no intermediate value. [`FrameWriter`] is a [`Sink`] that
15//! writes frame bytes, so the same host decrypts straight into a frame.
16//! [`PassthroughFrame`] is the owned frame a ciphertext carries for a
17//! passthrough subtree.
18
19use std::collections::HashSet;
20
21use vitaminc_protected::{Controlled, OpaqueDebug, Protected, TimingSafeEq};
22use zeroize::Zeroize;
23
24use crate::leaf::{self, Form, Item, ScalarError};
25use crate::plaintext::{
26    Encrypt, Error, FixedType, Logical, MapSink, Physical, SeqSink, Sink, SourceError,
27};
28
29/// Framing tag for a sequence: `ARRAY`, `u32` count, then the elements.
30pub const ARRAY: u8 = 0xF0;
31/// Framing tag for a map: `OBJECT`, `u32` count, then `u32` key length,
32/// key bytes and value for each entry.
33pub const OBJECT: u8 = 0xF1;
34/// Framing tag for a passthrough subtree: `PASSTHROUGH`, then one node
35/// that travels in the clear.
36pub const PASSTHROUGH: u8 = 0xF2;
37
38/// Maximum nesting depth of a frame. Bounds recursion so a deeply nested
39/// input cannot overflow the stack. The same limit, counted the same way,
40/// as the host bindings', so any frame they accept parses here.
41pub const MAX_DEPTH: usize = 128;
42
43/// Exactly one validated value node, borrowed.
44///
45/// A frame of plaintext is plaintext, so `Debug` is redacted and `==` is
46/// constant time.
47#[derive(Clone, Copy, OpaqueDebug, TimingSafeEq)]
48pub struct Frame<'a> {
49    bytes: &'a [u8],
50}
51
52/// The bytes are not a well-formed frame.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
54#[error("malformed frame")]
55pub struct FrameError;
56
57impl<'a> Frame<'a> {
58    /// Validate `bytes` as exactly one value node.
59    ///
60    /// The whole frame is checked up front, so [`visit`](Encrypt::visit)
61    /// never has to report a malformed frame part way through a walk.
62    pub fn parse(bytes: &'a [u8]) -> Result<Self, FrameError> {
63        let (frame, rest) = Self::parse_nested(bytes, 0)?;
64        if !rest.is_empty() {
65            return Err(FrameError);
66        }
67        Ok(frame)
68    }
69
70    /// Validate the value node at the start of `bytes`, and return it with
71    /// the bytes after it.
72    ///
73    /// For a frame embedded in a larger structure, such as the passthrough
74    /// payload of a ciphertext node. `depth` is how many levels the carrier
75    /// already nests the node, so the [`MAX_DEPTH`] bound counts the whole
76    /// structure and not the frame alone.
77    pub fn parse_nested(bytes: &'a [u8], depth: usize) -> Result<(Self, &'a [u8]), FrameError> {
78        let mut cursor = Cursor { buf: bytes, pos: 0 };
79        skip(&mut cursor, depth)?;
80        let (frame, rest) = bytes.split_at(cursor.pos);
81        Ok((Self { bytes: frame }, rest))
82    }
83
84    pub fn as_bytes(&self) -> &'a [u8] {
85        self.bytes
86    }
87}
88
89struct Cursor<'a> {
90    buf: &'a [u8],
91    pos: usize,
92}
93
94impl<'a> Cursor<'a> {
95    fn peek(&self) -> Result<u8, FrameError> {
96        self.buf.get(self.pos).copied().ok_or(FrameError)
97    }
98
99    fn byte(&mut self) -> Result<u8, FrameError> {
100        let b = self.peek()?;
101        self.pos += 1;
102        Ok(b)
103    }
104
105    fn take(&mut self, n: usize) -> Result<&'a [u8], FrameError> {
106        let end = self.pos.checked_add(n).ok_or(FrameError)?;
107        let s = self.buf.get(self.pos..end).ok_or(FrameError)?;
108        self.pos = end;
109        Ok(s)
110    }
111
112    fn count(&mut self) -> Result<usize, FrameError> {
113        let b = self.take(4)?;
114        Ok(u32::from_le_bytes(b.try_into().map_err(|_| FrameError)?) as usize)
115    }
116
117    fn key(&mut self) -> Result<&'a str, FrameError> {
118        let len = self.count()?;
119        core::str::from_utf8(self.take(len)?).map_err(|_| FrameError)
120    }
121
122    fn scalar(&mut self) -> Result<Item<'a>, FrameError> {
123        let (item, used) =
124            leaf::read_item(&self.buf[self.pos..], Form::Frame).map_err(|_| FrameError)?;
125        self.pos += used;
126        Ok(item)
127    }
128
129    /// The next whole value node, as its own frame.
130    fn node(&mut self, depth: usize) -> Result<Frame<'a>, FrameError> {
131        let start = self.pos;
132        skip(self, depth)?;
133        Ok(Frame {
134            bytes: &self.buf[start..self.pos],
135        })
136    }
137}
138
139/// Step over one value node, validating it.
140fn skip(c: &mut Cursor<'_>, depth: usize) -> Result<(), FrameError> {
141    if depth > MAX_DEPTH {
142        return Err(FrameError);
143    }
144    match c.peek()? {
145        ARRAY => {
146            c.byte()?;
147            for _ in 0..c.count()? {
148                skip(c, depth + 1)?;
149            }
150        }
151        OBJECT => {
152            c.byte()?;
153            let count = c.count()?;
154            let mut seen = HashSet::new();
155            for _ in 0..count {
156                let key = c.key()?;
157                if !seen.insert(key) {
158                    return Err(FrameError);
159                }
160                skip(c, depth + 1)?;
161            }
162        }
163        PASSTHROUGH => {
164            c.byte()?;
165            skip(c, depth + 1)?;
166        }
167        _ => {
168            c.scalar()?;
169        }
170    }
171    Ok(())
172}
173
174// Everything below runs on a frame `parse` accepted, so a read cannot fail.
175const VALIDATED: &str = "frame was validated by Frame::parse";
176
177/// A frame walked in place: no scalar leaves the buffer except to go
178/// straight to the sink.
179impl Encrypt for Frame<'_> {
180    fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
181        visit_node(
182            &mut Cursor {
183                buf: self.bytes,
184                pos: 0,
185            },
186            sink,
187        )
188    }
189}
190
191/// A borrowed frame is the same frame: it is a view, and `Copy`.
192impl Encrypt for &Frame<'_> {
193    fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
194        (*self).visit(sink)
195    }
196}
197
198/// Walk the node at the cursor into `sink`, leaving the cursor after it.
199/// One pass: each byte is read once, except that a passthrough subtree is
200/// stepped over to find its end before the sink walks it.
201fn visit_node<S: Sink>(c: &mut Cursor<'_>, sink: S) -> Result<S::Ok, S::Error> {
202    match c.peek().expect(VALIDATED) {
203        ARRAY => {
204            c.byte().expect(VALIDATED);
205            let count = c.count().expect(VALIDATED);
206            let mut seq = sink.seq(Some(count))?;
207            for _ in 0..count {
208                visit_node(c, seq.element()?)?;
209            }
210            seq.end()
211        }
212        OBJECT => {
213            c.byte().expect(VALIDATED);
214            let count = c.count().expect(VALIDATED);
215            let mut map = sink.map(Some(count))?;
216            for _ in 0..count {
217                let key = c.key().expect(VALIDATED);
218                visit_node(c, map.entry(key)?)?;
219            }
220            map.end()
221        }
222        PASSTHROUGH => {
223            c.byte().expect(VALIDATED);
224            sink.passthrough(c.node(0).expect(VALIDATED))
225        }
226        _ => match c.scalar().expect(VALIDATED) {
227            Item::Scalar(ty, v) => sink.scalar(ty, v),
228            Item::TypedNull(ty) => sink.typed_null(ty),
229        },
230    }
231}
232
233/// An owned frame: the payload a ciphertext carries for a passthrough
234/// subtree. Non-sensitive by contract; neither sealed nor authenticated,
235/// and never wiped. `Debug` and `==` match [`Frame`]'s all the same, so a
236/// frame prints and compares one way whichever form it is in.
237#[derive(Clone, OpaqueDebug, TimingSafeEq)]
238pub struct PassthroughFrame(pub Vec<u8>);
239
240impl PassthroughFrame {
241    /// The frame, validated.
242    pub fn parse(&self) -> Result<Frame<'_>, FrameError> {
243        Frame::parse(&self.0)
244    }
245}
246
247impl Encrypt for PassthroughFrame {
248    fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
249        self.parse()
250            .map_err(|e| S::Error::source(SourceError::new(e)))?
251            .visit(sink)
252    }
253}
254
255impl Encrypt for &PassthroughFrame {
256    fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
257        self.parse()
258            .map_err(|e| S::Error::source(SourceError::new(e)))?
259            .visit(sink)
260    }
261}
262
263/// Why a value could not be written as a frame.
264#[derive(Debug, thiserror::Error)]
265pub enum FrameWriteError {
266    /// Nested deeper than [`MAX_DEPTH`].
267    #[error("value nests deeper than {MAX_DEPTH} levels")]
268    TooDeep,
269    /// Text, bytes, a key or a count longer than a `u32`.
270    #[error("text, bytes, a key or a count is too long for a u32")]
271    TooLong,
272    /// A value that is not valid for its logical type.
273    #[error("value is not valid for its logical type")]
274    Invalid,
275    /// A container's declared length did not match the children written.
276    #[error("container length does not match its children")]
277    CountMismatch,
278    /// A map wrote the same key twice.
279    #[error("duplicate key")]
280    DuplicateKey,
281    /// A child sink was dropped without a value written into it, which
282    /// would leave the container's count ahead of its children.
283    #[error("a child sink was dropped without a value")]
284    Unwritten,
285    /// A container was never ended, so its count was never written and
286    /// the bytes are not a frame.
287    #[error("a container was never ended")]
288    Unfinished,
289    /// The source stopped the walk.
290    #[error("source failed: {0}")]
291    Source(#[source] SourceError),
292}
293
294impl Error for FrameWriteError {
295    fn source(error: SourceError) -> Self {
296        Self::Source(error)
297    }
298}
299
300/// Write any source as a frame. The buffer is `Protected`, since a frame
301/// of plaintext is plaintext, and it grows without leaving copies behind.
302///
303/// The result is checked with [`Frame::parse`] before it is returned, so a
304/// source that opens a container and never ends it is refused here, not by
305/// every later reader of the frame.
306pub fn to_frame<P: Encrypt>(p: P) -> Result<Protected<Vec<u8>>, FrameWriteError> {
307    to_frame_at(p, 0)
308}
309
310/// [`to_frame`] for a subtree whose root sits `depth` levels below the top
311/// of a document, so [`MAX_DEPTH`] counts from the top, as the transport
312/// decoder counts it.
313pub(crate) fn to_frame_at<P: Encrypt>(
314    p: P,
315    depth: usize,
316) -> Result<Protected<Vec<u8>>, FrameWriteError> {
317    let mut out = Protected::new(Vec::new());
318    p.visit(FrameWriter {
319        out: out.risky_inner_mut(),
320        depth,
321    })?;
322    Frame::parse(out.risky_ref()).map_err(|_| FrameWriteError::Unfinished)?;
323    Ok(out)
324}
325
326/// A sink that writes one value node onto the end of a buffer. Unknown
327/// container lengths are patched in at `end`.
328pub struct FrameWriter<'o> {
329    out: &'o mut Vec<u8>,
330    depth: usize,
331}
332
333impl<'o> FrameWriter<'o> {
334    pub fn new(out: &'o mut Vec<u8>) -> Self {
335        Self { out, depth: 0 }
336    }
337
338    /// A writer for a node that sits `depth` levels below the top of a
339    /// document, so [`MAX_DEPTH`] counts from the top: for a subtree
340    /// written to a buffer of its own and copied into the document later.
341    pub fn at_depth(out: &'o mut Vec<u8>, depth: usize) -> Self {
342        Self { out, depth }
343    }
344
345    /// How many levels below the top of its document this writer's node
346    /// sits.
347    pub fn depth(&self) -> usize {
348        self.depth
349    }
350
351    fn node(self, tag: u8) -> Result<&'o mut Vec<u8>, FrameWriteError> {
352        if self.depth > MAX_DEPTH {
353            return Err(FrameWriteError::TooDeep);
354        }
355        reserve(self.out, 1);
356        self.out.push(tag);
357        Ok(self.out)
358    }
359
360    fn container(self, tag: u8) -> Result<Count<'o>, FrameWriteError> {
361        let depth = self.depth;
362        reserve(self.out, 5);
363        let out = self.node(tag)?;
364        let at = out.len();
365        out.extend_from_slice(&[0; 4]);
366        Ok(Count {
367            out,
368            depth,
369            at,
370            n: 0,
371            open: None,
372        })
373    }
374}
375
376/// Make room for `additional` more bytes. A `Vec` that regrows frees its
377/// old allocation without wiping it, and a frame is plaintext, so grow into
378/// a fresh allocation and wipe the old one instead.
379pub(crate) fn reserve(out: &mut Vec<u8>, additional: usize) {
380    if out.capacity() - out.len() >= additional {
381        return;
382    }
383    // Doubling the length, not the capacity, still amortises a full
384    // buffer's growth, and never balloons a buffer that had room.
385    let capacity = out
386        .len()
387        .saturating_add(additional)
388        .max(out.len().saturating_mul(2))
389        .max(64);
390    let mut grown = Vec::with_capacity(capacity);
391    grown.extend_from_slice(out);
392    out.zeroize();
393    *out = grown;
394}
395
396fn write_len(out: &mut Vec<u8>, len: usize) -> Result<(), FrameWriteError> {
397    let len = u32::try_from(len).map_err(|_| FrameWriteError::TooLong)?;
398    out.extend_from_slice(&len.to_le_bytes());
399    Ok(())
400}
401
402/// A container whose count is written once the children are known.
403struct Count<'o> {
404    out: &'o mut Vec<u8>,
405    depth: usize,
406    at: usize,
407    n: usize,
408    /// Where the last child sink handed out would start writing. Every
409    /// node writes at least its tag, so if the buffer has not grown past
410    /// it by the next child or the end, that sink was dropped unused.
411    open: Option<usize>,
412}
413
414impl Count<'_> {
415    fn settle(&mut self) -> Result<(), FrameWriteError> {
416        match self.open.take() {
417            Some(start) if self.out.len() == start => Err(FrameWriteError::Unwritten),
418            _ => Ok(()),
419        }
420    }
421
422    fn child(&mut self) -> FrameWriter<'_> {
423        self.n += 1;
424        self.open = Some(self.out.len());
425        FrameWriter {
426            out: &mut *self.out,
427            depth: self.depth + 1,
428        }
429    }
430
431    fn end(mut self, expected: Option<usize>) -> Result<(), FrameWriteError> {
432        self.settle()?;
433        if expected.is_some_and(|e| e != self.n) {
434            return Err(FrameWriteError::CountMismatch);
435        }
436        let n = u32::try_from(self.n).map_err(|_| FrameWriteError::TooLong)?;
437        self.out[self.at..self.at + 4].copy_from_slice(&n.to_le_bytes());
438        Ok(())
439    }
440}
441
442impl<'o> Sink for FrameWriter<'o> {
443    type Ok = ();
444    type Error = FrameWriteError;
445    type Seq = FrameSeq<'o>;
446    type Map = FrameMap<'o>;
447
448    fn scalar(self, ty: Option<Logical>, v: Physical<'_>) -> Result<(), FrameWriteError> {
449        if self.depth > MAX_DEPTH {
450            return Err(FrameWriteError::TooDeep);
451        }
452        reserve(self.out, leaf::max_len(&v));
453        leaf::write(ty, v, Form::Frame, self.out).map_err(|e| match e {
454            ScalarError::Invalid => FrameWriteError::Invalid,
455            ScalarError::TooLong | ScalarError::Malformed => FrameWriteError::TooLong,
456        })
457    }
458
459    /// Written as the plain null. A frame is plaintext, never sealed, so
460    /// padding hides nothing in it: what a frame writer produces goes to a
461    /// host (the frame [`decrypt_to_frame`](crate::decrypt_to_frame)
462    /// returns) or into the clear (a passthrough subtree), where the plain
463    /// null is what every reader already knows. A frame a host sends may
464    /// still carry a typed null, which [`Frame`] reads, so the host's
465    /// absent values are padded when sealed.
466    fn typed_null(self, _: FixedType) -> Result<(), FrameWriteError> {
467        self.scalar(None, Physical::Null)
468    }
469
470    fn seq(self, len: Option<usize>) -> Result<FrameSeq<'o>, FrameWriteError> {
471        Ok(FrameSeq {
472            count: self.container(ARRAY)?,
473            expected: len,
474        })
475    }
476
477    fn map(self, len: Option<usize>) -> Result<FrameMap<'o>, FrameWriteError> {
478        Ok(FrameMap {
479            count: self.container(OBJECT)?,
480            expected: len,
481            seen: HashSet::new(),
482        })
483    }
484
485    fn passthrough<P: Encrypt + Clone>(self, value: P) -> Result<(), FrameWriteError> {
486        let depth = self.depth;
487        let out = self.node(PASSTHROUGH)?;
488        value.visit(FrameWriter {
489            out,
490            depth: depth + 1,
491        })
492    }
493}
494
495pub struct FrameSeq<'o> {
496    count: Count<'o>,
497    expected: Option<usize>,
498}
499
500impl SeqSink for FrameSeq<'_> {
501    type Ok = ();
502    type Error = FrameWriteError;
503    type Element<'s>
504        = FrameWriter<'s>
505    where
506        Self: 's;
507
508    fn element(&mut self) -> Result<FrameWriter<'_>, FrameWriteError> {
509        self.count.settle()?;
510        Ok(self.count.child())
511    }
512
513    fn end(self) -> Result<(), FrameWriteError> {
514        self.count.end(self.expected)
515    }
516}
517
518pub struct FrameMap<'o> {
519    count: Count<'o>,
520    expected: Option<usize>,
521    seen: HashSet<String>,
522}
523
524impl MapSink for FrameMap<'_> {
525    type Ok = ();
526    type Error = FrameWriteError;
527    type Entry<'s>
528        = FrameWriter<'s>
529    where
530        Self: 's;
531
532    fn entry(&mut self, key: &str) -> Result<FrameWriter<'_>, FrameWriteError> {
533        self.count.settle()?;
534        if !self.seen.insert(key.to_owned()) {
535            return Err(FrameWriteError::DuplicateKey);
536        }
537        reserve(self.count.out, key.len().saturating_add(4));
538        write_len(self.count.out, key.len())?;
539        self.count.out.extend_from_slice(key.as_bytes());
540        Ok(self.count.child())
541    }
542
543    fn end(self) -> Result<(), FrameWriteError> {
544        self.count.end(self.expected)
545    }
546}
547
548#[cfg(test)]
549mod tests {
550    use super::*;
551    use crate::plaintext::record::{record, Event, Owned};
552    use crate::plaintext::{visit_seq, Int};
553    use std::collections::BTreeMap;
554
555    fn frame_of<P: Encrypt>(p: P) -> Vec<u8> {
556        to_frame(p).expect("frames").risky_ref().clone()
557    }
558
559    #[test]
560    fn a_frame_round_trips_through_write_and_walk() {
561        let mut value = BTreeMap::new();
562        value.insert("name", vec![Some("Ada"), None]);
563        value.insert("empty", vec![]);
564        let bytes = frame_of(value.clone());
565        let frame = Frame::parse(&bytes).expect("parses");
566        assert_eq!(record(frame).expect("walks"), record(value).expect("walks"));
567        // A borrowed frame walks as the frame itself.
568        let borrowed: &Frame<'_> = &frame;
569        assert_eq!(
570            record(borrowed).expect("walks"),
571            record(frame).expect("walks")
572        );
573        // Writing the walk of a frame gives the same frame.
574        assert_eq!(frame_of(frame), bytes);
575    }
576
577    #[test]
578    fn container_framing_is_tag_count_children() {
579        assert_eq!(
580            frame_of(vec![1u16, 2]),
581            [ARRAY, 2, 0, 0, 0, 0x0F, 1, 0, 0x0F, 2, 0]
582        );
583        let mut map = BTreeMap::new();
584        map.insert("k", true);
585        assert_eq!(frame_of(map), [OBJECT, 1, 0, 0, 0, 1, 0, 0, 0, b'k', 0x03]);
586    }
587
588    /// A source that asks for a child sink and drops it unused.
589    #[derive(Clone)]
590    struct DropsAChild {
591        map: bool,
592    }
593
594    impl Encrypt for DropsAChild {
595        fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
596            if self.map {
597                let mut map = sink.map(None)?;
598                let _ = map.entry("a")?;
599                1u16.visit(map.entry("b")?)?;
600                map.end()
601            } else {
602                let mut seq = sink.seq(None)?;
603                1u16.visit(seq.element()?)?;
604                let _ = seq.element()?;
605                seq.end()
606            }
607        }
608    }
609
610    #[test]
611    fn a_dropped_child_sink_is_refused_not_framed() {
612        for map in [false, true] {
613            assert!(matches!(
614                to_frame(DropsAChild { map }),
615                Err(FrameWriteError::Unwritten)
616            ));
617        }
618        // And so a passthrough that drops one cannot be sealed into a
619        // document that would never decrypt.
620        struct Doc;
621        impl Encrypt for Doc {
622            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
623                let mut seq = sink.seq(Some(2))?;
624                1u16.visit(seq.element()?)?;
625                seq.element()?.passthrough(DropsAChild { map: false })?;
626                seq.end()
627            }
628        }
629        assert!(matches!(
630            crate::plan::<_, (), _>(Doc, "ctx"),
631            Err(crate::EncryptError::Shape(_))
632        ));
633    }
634
635    #[test]
636    fn reserve_moves_to_a_bigger_buffer_and_keeps_the_bytes() {
637        let mut out = vec![1u8, 2, 3];
638        out.shrink_to_fit();
639        reserve(&mut out, 10);
640        assert_eq!(out, [1, 2, 3]);
641        assert!(out.capacity() >= 13);
642        let before = out.as_ptr();
643        reserve(&mut out, 10);
644        assert_eq!(out.as_ptr(), before, "enough room already: no move");
645
646        // A full buffer grows, even by one byte.
647        let mut full = vec![7u8; 100];
648        full.shrink_to_fit();
649        reserve(&mut full, 1);
650        assert!(full.capacity() >= 101);
651        assert_eq!(full, [7u8; 100]);
652    }
653
654    #[test]
655    fn a_parsed_frame_is_its_bytes() {
656        let bytes = frame_of(vec![1u8, 2]);
657        let frame = Frame::parse(&bytes).expect("parses");
658        assert_eq!(frame.as_bytes(), &bytes[..]);
659        assert_eq!(FrameError.to_string(), "malformed frame");
660    }
661
662    #[test]
663    fn passthrough_is_framed_in_the_clear() {
664        struct Pass;
665        impl Encrypt for Pass {
666            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
667                sink.passthrough("public")
668            }
669        }
670        let bytes = frame_of(Pass);
671        assert_eq!(
672            bytes,
673            [
674                PASSTHROUGH,
675                0x0A,
676                6,
677                0,
678                0,
679                0,
680                b'p',
681                b'u',
682                b'b',
683                b'l',
684                b'i',
685                b'c'
686            ]
687        );
688        assert_eq!(
689            record(Frame::parse(&bytes).expect("parses")).expect("walks"),
690            [
691                Event::Passthrough,
692                Event::Scalar(None, Owned::Text("public".into()))
693            ]
694        );
695        let owned = PassthroughFrame(bytes.clone());
696        assert_eq!(frame_of(owned.clone()), bytes);
697        assert_eq!(frame_of(&owned), bytes);
698    }
699
700    #[test]
701    fn unknown_lengths_are_patched_in() {
702        struct Unsized(Vec<u32>);
703        impl Encrypt for Unsized {
704            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
705                visit_seq(sink, None, self.0)
706            }
707        }
708        assert_eq!(frame_of(Unsized(vec![1, 2, 3])), frame_of(vec![1u32, 2, 3]));
709    }
710
711    #[test]
712    fn a_wrong_length_hint_is_refused() {
713        struct Lies;
714        impl Encrypt for Lies {
715            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
716                visit_seq(sink, Some(5), vec![1u32])
717            }
718        }
719        assert!(matches!(
720            to_frame(Lies),
721            Err(FrameWriteError::CountMismatch)
722        ));
723    }
724
725    #[test]
726    fn duplicate_keys_are_refused_both_ways() {
727        struct Dup;
728        impl Encrypt for Dup {
729            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
730                let mut map = sink.map(None)?;
731                1u32.visit(map.entry("k")?)?;
732                2u32.visit(map.entry("k")?)?;
733                map.end()
734            }
735        }
736        assert!(matches!(to_frame(Dup), Err(FrameWriteError::DuplicateKey)));
737        let bytes = [
738            OBJECT, 2, 0, 0, 0, 1, 0, 0, 0, b'k', 0x00, 1, 0, 0, 0, b'k', 0x00,
739        ];
740        assert_eq!(Frame::parse(&bytes), Err(FrameError));
741    }
742
743    #[test]
744    fn malformed_frames_are_refused() {
745        assert_eq!(Frame::parse(&[]), Err(FrameError));
746        assert_eq!(Frame::parse(&[0x00, 0x00]), Err(FrameError));
747        assert_eq!(Frame::parse(&[ARRAY, 1, 0, 0, 0]), Err(FrameError));
748        assert_eq!(
749            Frame::parse(&[OBJECT, 1, 0, 0, 0, 1, 0, 0, 0, 0xFF, 0x00]),
750            Err(FrameError)
751        );
752        assert_eq!(Frame::parse(&[PASSTHROUGH]), Err(FrameError));
753        assert_eq!(
754            Frame::parse(&[0x0A, 2, 0, 0, 0, b'a']).err(),
755            Some(FrameError)
756        );
757    }
758
759    #[derive(Clone, Copy)]
760    enum Kind {
761        Seq,
762        Map,
763        Pass,
764    }
765
766    /// `n` levels of one kind of container around an empty sequence, so
767    /// the innermost value is itself a container at depth `n`.
768    #[derive(Clone, Copy)]
769    struct Nest(Kind, usize);
770
771    impl Encrypt for Nest {
772        fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
773            let Nest(kind, n) = self;
774            if n == 0 {
775                return sink.seq(Some(0))?.end();
776            }
777            let inner = Nest(kind, n - 1);
778            match kind {
779                Kind::Seq => {
780                    let mut seq = sink.seq(Some(1))?;
781                    inner.visit(seq.element()?)?;
782                    seq.end()
783                }
784                Kind::Map => {
785                    let mut map = sink.map(Some(1))?;
786                    inner.visit(map.entry("k")?)?;
787                    map.end()
788                }
789                Kind::Pass => sink.passthrough(inner),
790            }
791        }
792    }
793
794    #[test]
795    fn depth_is_bounded_both_ways() {
796        for kind in [Kind::Seq, Kind::Map, Kind::Pass] {
797            // A container at exactly `MAX_DEPTH` is written and parsed.
798            let bytes = frame_of(Nest(kind, MAX_DEPTH));
799            assert!(Frame::parse(&bytes).is_ok());
800
801            // One more level is refused by both.
802            assert!(matches!(
803                to_frame(Nest(kind, MAX_DEPTH + 1)),
804                Err(FrameWriteError::TooDeep)
805            ));
806            let mut deeper = match kind {
807                Kind::Seq => vec![ARRAY, 1, 0, 0, 0],
808                Kind::Map => vec![OBJECT, 1, 0, 0, 0, 1, 0, 0, 0, b'k'],
809                Kind::Pass => vec![PASSTHROUGH],
810            };
811            deeper.extend_from_slice(&bytes);
812            assert_eq!(Frame::parse(&deeper), Err(FrameError));
813        }
814
815        struct Deep(usize);
816        impl Encrypt for Deep {
817            fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
818                if self.0 == 0 {
819                    return sink.scalar(None, Physical::Int(Int::U8(0)));
820                }
821                let mut seq = sink.seq(Some(1))?;
822                Deep(self.0 - 1).visit(seq.element()?)?;
823                seq.end()
824            }
825        }
826        assert!(to_frame(Deep(MAX_DEPTH)).is_ok());
827        assert!(matches!(
828            to_frame(Deep(MAX_DEPTH + 1)),
829            Err(FrameWriteError::TooDeep)
830        ));
831    }
832
833    #[test]
834    fn parse_nested_takes_one_node_and_returns_the_rest() {
835        let mut bytes = frame_of(vec![1u16, 2]);
836        let trailing = frame_of("after");
837        bytes.extend_from_slice(&trailing);
838
839        // `parse` wants exactly one node; `parse_nested` hands back the rest.
840        assert_eq!(Frame::parse(&bytes), Err(FrameError));
841        let (frame, rest) = Frame::parse_nested(&bytes, 0).expect("parses the prefix");
842        assert_eq!(frame.as_bytes(), &frame_of(vec![1u16, 2])[..]);
843        assert_eq!(rest, &trailing[..]);
844
845        // The rest is itself one node, and after it nothing is left.
846        let (frame, rest) = Frame::parse_nested(rest, 0).expect("parses the rest");
847        assert_eq!(frame.as_bytes(), &trailing[..]);
848        assert!(rest.is_empty());
849
850        // An empty buffer holds no node.
851        assert_eq!(Frame::parse_nested(&[], 0), Err(FrameError));
852    }
853
854    #[test]
855    fn parse_nested_counts_the_carrier_in_the_depth_bound() {
856        // A node at exactly `MAX_DEPTH` parses at depth 0, but not when its
857        // carrier already nests it one level.
858        let bytes = frame_of(Nest(Kind::Seq, MAX_DEPTH));
859        assert!(Frame::parse_nested(&bytes, 0).is_ok());
860        assert_eq!(Frame::parse_nested(&bytes, 1), Err(FrameError));
861        // A scalar sits at the bound too.
862        let scalar = frame_of(1u16);
863        assert!(Frame::parse_nested(&scalar, MAX_DEPTH).is_ok());
864        assert_eq!(Frame::parse_nested(&scalar, MAX_DEPTH + 1), Err(FrameError));
865    }
866
867    #[test]
868    fn a_writer_at_depth_counts_from_the_top() {
869        let mut out = Vec::new();
870        let writer = FrameWriter::at_depth(&mut out, 7);
871        assert_eq!(writer.depth(), 7);
872        let mut seq = writer.seq(Some(1)).expect("seq");
873        assert_eq!(seq.element().expect("element").depth(), 8);
874
875        // A scalar fits at the bound, and not below it.
876        let mut out = Vec::new();
877        assert!(FrameWriter::at_depth(&mut out, MAX_DEPTH)
878            .scalar(None, Physical::Bool(true))
879            .is_ok());
880        assert!(matches!(
881            FrameWriter::at_depth(&mut out, MAX_DEPTH + 1).scalar(None, Physical::Bool(true)),
882            Err(FrameWriteError::TooDeep)
883        ));
884    }
885
886    #[test]
887    fn a_frame_debugs_redacted_and_compares_by_value() {
888        let bytes = frame_of("secret");
889        let frame = Frame::parse(&bytes).expect("parses");
890        let shown = format!("{frame:?}");
891        assert!(
892            shown.contains("***") && !shown.contains("secret"),
893            "{shown}"
894        );
895        assert!(!shown.contains(&bytes.len().to_string()), "{shown}");
896        let pass = format!("{:?}", PassthroughFrame(bytes.clone()));
897        assert!(pass.contains("***") && !pass.contains("secret"), "{pass}");
898
899        let same = bytes.clone();
900        let other = frame_of("public");
901        assert_eq!(frame, Frame::parse(&same).expect("parses"));
902        assert_ne!(frame, Frame::parse(&other).expect("parses"));
903        assert_eq!(PassthroughFrame(bytes.clone()), PassthroughFrame(same));
904        assert_ne!(PassthroughFrame(bytes), PassthroughFrame(other));
905    }
906
907    #[test]
908    fn frame_write_errors_say_what_failed() {
909        let cases = [
910            (
911                FrameWriteError::TooDeep,
912                "value nests deeper than 128 levels",
913            ),
914            (
915                FrameWriteError::TooLong,
916                "text, bytes, a key or a count is too long for a u32",
917            ),
918            (
919                FrameWriteError::Invalid,
920                "value is not valid for its logical type",
921            ),
922            (
923                FrameWriteError::CountMismatch,
924                "container length does not match its children",
925            ),
926            (FrameWriteError::DuplicateKey, "duplicate key"),
927            (
928                FrameWriteError::Unwritten,
929                "a child sink was dropped without a value",
930            ),
931            (FrameWriteError::Unfinished, "a container was never ended"),
932            (
933                FrameWriteError::Source(SourceError::msg("getter threw")),
934                "source failed: getter threw",
935            ),
936        ];
937        for (error, message) in cases {
938            assert_eq!(error.to_string(), message);
939        }
940        fn is_error<E: core::error::Error>(_: &E) {}
941        is_error(&FrameWriteError::TooDeep);
942        use core::error::Error as _;
943        let sourced = FrameWriteError::Source(SourceError::msg("getter threw"));
944        assert_eq!(
945            sourced.source().map(|cause| cause.to_string()),
946            Some("getter threw".into())
947        );
948        assert!(FrameWriteError::TooDeep.source().is_none());
949    }
950}