Skip to main content

coven_format/
sealed_snapshot.rs

1//! Authenticated snapshot routing and coverage (D9).
2
3use crate::error::{exact_length, Error};
4use crate::sealed;
5use crate::sequence::Purpose;
6use crate::wire::{Decoder, Encoder, Wire};
7use coven_crypto::{Signature, SIGNATURE_LEN};
8use coven_foundation::id_source::KeyId;
9use coven_merge::Audience;
10
11/// Clear snapshot routing. Verify the prefix signature before using coverage.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct SnapshotObjectPrefix {
14    /// The audience of its rows and header.
15    pub audience: Audience,
16    /// The audience key sealing section zero.
17    pub key: KeyId,
18    /// Last included slot, including consumed refusals.
19    pub covers: u64,
20    /// Ordinary snapshot or prepared admin state.
21    pub purpose: Purpose,
22}
23impl SnapshotObjectPrefix {
24    /// Greatest signed prefix end, including a circle and raised version.
25    pub const MAX_SIGNED_PREFIX: usize = 3 + 17 + 16 + 8 + 5 + SIGNATURE_LEN;
26    /// Bytes through the prefix signature.
27    pub fn signed_length(bytes: &[u8]) -> Result<usize, Error> {
28        Ok(Self::length(bytes)? + SIGNATURE_LEN)
29    }
30    /// Plaintext size from the complete encrypted object size.
31    pub fn plaintext_length(&self, object_length: u64) -> Result<u64, Error> {
32        crate::sealed_stream::plaintext_length(
33            object_length.checked_sub(self.encode()?.len() as u64 + 128).ok_or(Error::Truncated)?,
34        )
35    }
36    /// Encode kind, format, routing, coverage and purpose.
37    pub fn encode(&self) -> Result<Vec<u8>, Error> {
38        let mut out = Encoder::new();
39        out.bytes(&crate::FormatVersion::CURRENT.prefix(crate::kinds::SNAPSHOT))?;
40        self.audience.put(&mut out)?;
41        self.key.put(&mut out)?;
42        self.covers.put(&mut out)?;
43        self.purpose.put(&mut out)?;
44        Ok(out.bytes)
45    }
46    /// Determine the bounded prefix length without allocation.
47    pub fn length(bytes: &[u8]) -> Result<usize, Error> {
48        sealed::prefix(bytes, crate::kinds::SNAPSHOT)?;
49        let purpose = match *bytes.get(3).ok_or(Error::Truncated)? {
50            0 => 28,
51            1 => 44,
52            tag => return Err(Error::UnknownTag { field: "audience", tag }),
53        };
54        match *bytes.get(purpose).ok_or(Error::Truncated)? {
55            0 | 2 => Ok(purpose + 1),
56            1 => Ok(purpose + 5),
57            tag => Err(Error::UnknownTag { field: "snapshot purpose", tag }),
58        }
59    }
60    /// Decode exactly the prefix and its signature, without authenticating them.
61    pub fn decode_signed(bytes: &[u8]) -> Result<(Self, Signature), Error> {
62        let length = Self::length(bytes)?;
63        let prefix = Self::decode(bytes.get(..length).ok_or(Error::Truncated)?)?;
64        Ok((prefix, sealed::signature(&bytes[length..])?))
65    }
66    /// Decode exactly the clear prefix.
67    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
68        exact_length(bytes.len(), Self::length(bytes)?)?;
69        let mut input = Decoder::new(&bytes[3..])?;
70        let prefix = Self {
71            audience: Wire::get(&mut input)?,
72            key: Wire::get(&mut input)?,
73            covers: Wire::get(&mut input)?,
74            purpose: Wire::get(&mut input)?,
75        };
76        input.finish()?;
77        Ok(prefix)
78    }
79}
80
81#[cfg(test)]
82#[path = "sealed_snapshot_tests.rs"]
83mod tests;