Skip to main content

coven_format/
code_fields.rs

1//! The fields of a code's storage part (D13). `coven-storage` decides what
2//! they mean; this module packs them with the format's own primitives (D2),
3//! so a storage part has one encoding and a reader refuses a truncated or
4//! malformed one. Every owned buffer erases on drop.
5
6use crate::error::{bound, Error};
7use crate::wire::{get_text, Decoder, Wire};
8use coven_crypto::{SecretBytes, SecretText};
9use zeroize::Zeroizing;
10
11/// The largest storage part a restore or invite code carries.
12pub const MAX_STORAGE: usize = 16 * 1024;
13
14/// Writes one storage part's fields in order. [`pack`] runs the same fields
15/// twice: once to count their bytes, then into a buffer of exactly that size,
16/// so no secret is left behind in a grown allocation.
17pub struct FieldWriter {
18    bytes: Option<Zeroizing<Vec<u8>>>,
19    len: usize,
20}
21
22impl FieldWriter {
23    fn write(&mut self, part: &[u8]) -> Result<(), Error> {
24        self.len = self.len.saturating_add(part.len());
25        bound(self.len, MAX_STORAGE, "storage settings")?;
26        if let Some(bytes) = &mut self.bytes {
27            bytes.extend_from_slice(part);
28        }
29        Ok(())
30    }
31
32    /// A `u8`: a tag or a small number.
33    pub fn u8(&mut self, value: u8) -> Result<(), Error> {
34        self.write(&[value])
35    }
36
37    /// A `u16`, big-endian.
38    pub fn u16(&mut self, value: u16) -> Result<(), Error> {
39        self.write(&value.to_be_bytes())
40    }
41
42    /// A `text`: its UTF-8 length as a `u32`, then its bytes.
43    pub fn text(&mut self, value: &str) -> Result<(), Error> {
44        bound(value.len(), MAX_STORAGE, "storage text")?;
45        self.write(&(value.len() as u32).to_be_bytes())?;
46        self.write(value.as_bytes())
47    }
48
49    /// An `Option<T>`: `0`, or `1` and the value's fields.
50    pub fn option<T>(
51        &mut self,
52        value: Option<T>,
53        put: impl FnOnce(&mut Self, T) -> Result<(), Error>,
54    ) -> Result<(), Error> {
55        match value {
56            None => self.u8(0),
57            Some(value) => {
58                self.u8(1)?;
59                put(self, value)
60            }
61        }
62    }
63}
64
65/// Pack the fields `put` writes into one buffer erased on drop and
66/// allocated once. `put` must write the same fields both times it runs.
67pub fn pack(put: impl Fn(&mut FieldWriter) -> Result<(), Error>) -> Result<SecretBytes, Error> {
68    let mut count = FieldWriter { bytes: None, len: 0 };
69    put(&mut count)?;
70    let mut bytes = Zeroizing::new(Vec::new());
71    bytes.try_reserve_exact(count.len)?;
72    let mut writer = FieldWriter { bytes: Some(bytes), len: 0 };
73    put(&mut writer)?;
74    let mut bytes = writer.bytes.expect("the writing pass keeps its buffer");
75    assert_eq!(bytes.len(), count.len, "storage fields changed between passes");
76    Ok(SecretBytes::new(std::mem::take(&mut *bytes)))
77}
78
79/// Reads one storage part's fields in the order they were packed. Every
80/// length is checked against what remains before it is allocated, and
81/// [`FieldReader::finish`] refuses trailing bytes.
82pub struct FieldReader<'a>(Decoder<'a>);
83
84impl<'a> FieldReader<'a> {
85    /// Read a whole storage part, at most [`MAX_STORAGE`] bytes.
86    pub fn new(bytes: &'a [u8]) -> Result<Self, Error> {
87        bound(bytes.len(), MAX_STORAGE, "storage settings")?;
88        Ok(Self(Decoder::new(bytes)?))
89    }
90
91    /// A `u8`.
92    pub fn u8(&mut self) -> Result<u8, Error> {
93        u8::get(&mut self.0)
94    }
95
96    /// A big-endian `u16`.
97    pub fn u16(&mut self) -> Result<u16, Error> {
98        u16::get(&mut self.0)
99    }
100
101    /// A `text` that is not secret, such as a bucket or folder id.
102    pub fn text(&mut self, field: &'static str) -> Result<String, Error> {
103        get_text(&mut self.0, MAX_STORAGE, field)
104    }
105
106    /// A secret `text`, such as a key or share token, erased on drop.
107    pub fn secret_text(&mut self, field: &'static str) -> Result<SecretText, Error> {
108        Ok(SecretText::new(self.text(field)?))
109    }
110
111    /// An `Option<T>`: `0`, or `1` and the value's fields. Any other tag is
112    /// refused. `get` may fail with the caller's own error, such as a value
113    /// that reads but is not valid.
114    pub fn option<T, E: From<Error>>(
115        &mut self,
116        get: impl FnOnce(&mut Self) -> Result<T, E>,
117    ) -> Result<Option<T>, E> {
118        match self.u8()? {
119            0 => Ok(None),
120            1 => Ok(Some(get(self)?)),
121            tag => Err(Error::UnknownTag { field: "option", tag }.into()),
122        }
123    }
124
125    /// Refuse bytes after the last field.
126    pub fn finish(self) -> Result<(), Error> {
127        self.0.finish()
128    }
129}
130
131#[cfg(test)]
132#[path = "code_fields_tests.rs"]
133mod tests;