Skip to main content

coven_format/
sealed_single.rs

1//! The single-chunk join request envelope (D9).
2
3use crate::error::{bound, require, Error, Rule};
4use crate::sealed;
5use crate::wire::MAX_OBJECT;
6use coven_crypto::{Signature, SEALED_OBJECT_CHUNK_OVERHEAD};
7
8/// Join requests have no clear routing beyond their kind and format.
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub enum SingleChunkPrefix {
11    /// Kind 36; the path identifies the invite key.
12    JoinRequest,
13}
14impl SingleChunkPrefix {
15    /// Encode the exact associated-data prefix.
16    pub fn encode(&self) -> Result<Vec<u8>, Error> {
17        self.encode_in(crate::FormatVersion::CURRENT)
18    }
19    /// Encode in the recorded format.
20    pub fn encode_in(&self, format: crate::FormatVersion) -> Result<Vec<u8>, Error> {
21        Ok(format.prefix(crate::kinds::JOIN_REQUEST).to_vec())
22    }
23    /// Frame one sealed plaintext frame, before its signature.
24    pub fn encode_chunk(&self, chunk: &[u8]) -> Result<Vec<u8>, Error> {
25        self.encode_chunk_in(crate::FormatVersion::CURRENT, chunk)
26    }
27    /// Frame the chunk in the recorded format.
28    pub fn encode_chunk_in(
29        &self,
30        format: crate::FormatVersion,
31        chunk: &[u8],
32    ) -> Result<Vec<u8>, Error> {
33        check_frame_length(chunk)?;
34        let mut bytes = self.encode_in(format)?;
35        bytes.extend_from_slice(&sealed::encode_chunk(chunk)?);
36        Ok(bytes)
37    }
38}
39
40/// A borrowed, structurally checked join-request envelope.
41#[derive(Debug)]
42pub enum SingleChunkObject<'a> {
43    /// The chunk and signature require authentication before using the request.
44    JoinRequest {
45        /// Nonce, ciphertext and tag.
46        chunk: &'a [u8],
47        /// Signature by the supplied member key.
48        signature: Signature,
49    },
50}
51impl<'a> SingleChunkObject<'a> {
52    /// Decode exactly one join-request envelope.
53    pub fn decode(bytes: &'a [u8]) -> Result<Self, Error> {
54        sealed::prefix(bytes, crate::kinds::JOIN_REQUEST)?;
55        let body = &bytes[3..];
56        let length = sealed::chunk_length(body, MAX_OBJECT)?;
57        let chunk = body.get(4..length).ok_or(Error::Truncated)?;
58        check_frame_length(chunk)?;
59        let signature = sealed::signature(body.get(length..).ok_or(Error::Truncated)?)?;
60        Ok(Self::JoinRequest { chunk, signature })
61    }
62    /// Clear routing prefix.
63    pub fn prefix(&self) -> SingleChunkPrefix {
64        SingleChunkPrefix::JoinRequest
65    }
66    /// The sealed frame bytes.
67    pub fn chunk(&self) -> &'a [u8] {
68        let Self::JoinRequest { chunk, .. } = self;
69        chunk
70    }
71    /// The required signature.
72    pub fn signature(&self) -> &Signature {
73        let Self::JoinRequest { signature, .. } = self;
74        signature
75    }
76    /// Reproduce the complete envelope.
77    pub fn encode(&self) -> Result<Vec<u8>, Error> {
78        let mut bytes = self.signed_bytes()?;
79        bytes.extend_from_slice(self.signature().as_bytes());
80        Ok(bytes)
81    }
82    /// Every byte before the signature.
83    pub fn signed_bytes(&self) -> Result<Vec<u8>, Error> {
84        self.prefix().encode_chunk(self.chunk())
85    }
86}
87
88fn check_frame_length(chunk: &[u8]) -> Result<(), Error> {
89    let length = chunk.len().checked_sub(SEALED_OBJECT_CHUNK_OVERHEAD).ok_or(Error::Truncated)?;
90    bound(length, MAX_OBJECT, "sealed frame")?;
91    require(length >= crate::FRAME_PREFIX_LEN, "sealed frame", Rule::Chunk)
92}
93
94#[cfg(test)]
95#[path = "sealed_single_tests.rs"]
96mod tests;