1use crate::chunks::{chunk_count, chunk_length};
4use crate::error::{bound, exact_length, require, Error, Rule};
5use crate::sealed;
6use crate::wire::{Decoder, Encoder, Wire, MAX_ITEMS, MAX_OBJECT};
7use coven_crypto::{Signature, SEALED_OBJECT_CHUNK_OVERHEAD, SIGNATURE_LEN};
8use coven_foundation::id_source::KeyId;
9
10pub fn sealed_length(header_length: usize, part_lengths: &[u64]) -> Result<u64, Error> {
13 validate_lengths(header_length, part_lengths)?;
14 let overhead = crate::STORED_CHUNK_OVERHEAD as u64;
15 let mut length = WriteObjectPrefix::encoded_length(part_lengths.len())? as u64
16 + header_length as u64
17 + overhead
18 + WriteObjectPrefix::tail_length(part_lengths.len()) as u64
19 + SIGNATURE_LEN as u64;
20 for part in part_lengths {
21 length = chunk_count(*part)
22 .checked_mul(overhead)
23 .and_then(|n| n.checked_add(*part))
24 .and_then(|n| n.checked_add(length))
25 .ok_or(Error::Invalid { field: "sealed write length", rule: Rule::StreamLength })?;
26 }
27 Ok(length)
28}
29
30fn validate_lengths(header_length: usize, part_lengths: &[u64]) -> Result<(), Error> {
31 bound(header_length, MAX_OBJECT, "write header")?;
32 require(header_length >= crate::FRAME_PREFIX_LEN, "write header", Rule::StreamLength)?;
33 bound(part_lengths.len(), MAX_ITEMS, "write parts")?;
34 for length in part_lengths {
35 require(
36 *length >= crate::FRAME_PREFIX_LEN as u64,
37 "write part length",
38 Rule::StreamLength,
39 )?;
40 }
41 Ok(())
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct HeaderTail {
47 pub roots: Vec<[u8; 32]>,
49 pub signature: Signature,
51}
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct WriteObjectPrefix {
56 pub format: crate::FormatVersion,
58 pub store_key: KeyId,
60 pub part_keys: Vec<KeyId>,
62}
63impl WriteObjectPrefix {
64 pub const MAX_HEADER_END: usize = 23
67 + 16 * MAX_ITEMS
68 + crate::STORED_CHUNK_OVERHEAD
69 + MAX_OBJECT
70 + 32 * MAX_ITEMS
71 + SIGNATURE_LEN;
72
73 pub fn header_end(bytes: &[u8]) -> Result<usize, Error> {
77 let prefix = Self::length(bytes)?;
78 let parts = (prefix - 23) / 16;
79 let header = bytes.get(prefix..).ok_or(Error::Truncated)?;
80 Ok(prefix + Self::header_chunk_length(header)? + Self::tail_length(parts))
81 }
82
83 pub fn tail_length(parts: usize) -> usize {
85 32 * parts + SIGNATURE_LEN
86 }
87
88 pub fn decode_tail(&self, bytes: &[u8]) -> Result<HeaderTail, Error> {
91 exact_length(bytes.len(), Self::tail_length(self.part_keys.len()))?;
92 let (roots, signature) = bytes.split_at(32 * self.part_keys.len());
93 Ok(HeaderTail {
94 roots: roots.chunks_exact(32).map(|root| root.try_into().expect("32 bytes")).collect(),
95 signature: sealed::signature(signature)?,
96 })
97 }
98
99 pub fn encoded_length(parts: usize) -> Result<usize, Error> {
101 bound(parts, MAX_ITEMS, "write section keys")?;
102 Ok(23 + 16 * parts)
103 }
104
105 pub fn encode(&self) -> Result<Vec<u8>, Error> {
107 let crate::FormatVersion::V1 = self.format;
108 let mut out = Encoder::new();
109 out.bytes(&self.format.prefix(crate::kinds::INNER_WRITE))?;
110 self.store_key.put(&mut out)?;
111 self.part_keys.put(&mut out)?;
112 Ok(out.bytes)
113 }
114
115 pub fn length(bytes: &[u8]) -> Result<usize, Error> {
118 sealed::prefix(bytes, crate::kinds::INNER_WRITE)?;
119 let count = bytes.get(19..23).ok_or(Error::Truncated)?;
120 let count = u32::from_be_bytes(count.try_into().expect("four bytes")) as usize;
121 Self::encoded_length(count)
122 }
123
124 pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
126 exact_length(bytes.len(), Self::length(bytes)?)?;
127 let mut input = Decoder::new(&bytes[3..])?;
128 let result = Self {
129 format: crate::FormatVersion::decode(bytes)?,
130 store_key: KeyId::get(&mut input)?,
131 part_keys: Vec::get(&mut input)?,
132 };
133 input.finish()?;
134 Ok(result)
135 }
136
137 pub fn header_chunk_length(prefix: &[u8]) -> Result<usize, Error> {
139 let length = sealed::chunk_length(prefix, MAX_OBJECT)?;
140 require(
141 length >= crate::FRAME_PREFIX_LEN + crate::STORED_CHUNK_OVERHEAD,
142 "sealed header frame",
143 Rule::Chunk,
144 )?;
145 Ok(length)
146 }
147
148 pub fn header_chunk(piece: &[u8]) -> Result<&[u8], Error> {
151 Self::header_chunk_length(piece)?;
152 sealed::chunk(piece, MAX_OBJECT)
153 }
154
155 pub fn opened_header(
160 self,
161 bytes: &[u8],
162 part_lengths: Vec<u64>,
163 ) -> Result<WriteObjectLayout, Error> {
164 let mut layout = WriteObjectLayout::new(self, bytes, part_lengths)?;
165 layout.advance();
166 layout.position = layout.after_tail();
167 Ok(layout)
168 }
169}
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173pub struct ObjectChunk {
174 pub key: KeyId,
176 pub section: u64,
178 pub index: u64,
180 pub plaintext_length: usize,
182}
183
184enum Position {
185 Chunk { section: usize, index: u64 },
186 Tail,
187 Signature,
188 End,
189}
190
191pub struct WriteObjectLayout {
196 prefix: WriteObjectPrefix,
197 part_lengths: Vec<u64>,
198 header_length: usize,
199 position: Position,
200}
201impl WriteObjectLayout {
202 pub fn new(
206 prefix: WriteObjectPrefix,
207 header: &[u8],
208 part_lengths: Vec<u64>,
209 ) -> Result<Self, Error> {
210 let header_length = header.len();
211 validate_lengths(header_length, &part_lengths)?;
212 require(
213 prefix.part_keys.len() == part_lengths.len(),
214 "write section key count",
215 Rule::StreamLength,
216 )?;
217 Ok(Self {
218 prefix,
219 part_lengths,
220 header_length,
221 position: Position::Chunk { section: 0, index: 0 },
222 })
223 }
224
225 pub fn prefix(&self) -> Result<Vec<u8>, Error> {
227 self.prefix.encode()
228 }
229
230 pub fn next_chunk(&self) -> Option<ObjectChunk> {
232 let Position::Chunk { section, index } = self.position else {
233 return None;
234 };
235 if section == 0 {
236 Some(ObjectChunk {
237 key: self.prefix.store_key,
238 section: 0,
239 index: 0,
240 plaintext_length: self.header_length,
241 })
242 } else {
243 Some(ObjectChunk {
244 key: self.prefix.part_keys[section - 1],
245 section: section as u64,
246 index,
247 plaintext_length: chunk_length(self.part_lengths[section - 1], index),
248 })
249 }
250 }
251
252 pub fn chunk_length(&self, prefix: &[u8]) -> Result<usize, Error> {
254 let chunk = self.next_chunk().ok_or(Error::TrailingBytes)?;
255 let length = sealed::chunk_length(prefix, chunk.plaintext_length)?;
256 require(
257 length == crate::STORED_CHUNK_OVERHEAD + chunk.plaintext_length,
258 "sealed write chunk length",
259 Rule::Chunk,
260 )?;
261 Ok(length)
262 }
263
264 pub fn encode_chunk(&mut self, bytes: &[u8]) -> Result<Vec<u8>, Error> {
266 let chunk = self.next_chunk().ok_or(Error::TrailingBytes)?;
267 require(
268 bytes.len() == SEALED_OBJECT_CHUNK_OVERHEAD + chunk.plaintext_length,
269 "sealed write chunk length",
270 Rule::Chunk,
271 )?;
272 let encoded = sealed::encode_chunk(bytes)?;
273 self.advance();
274 Ok(encoded)
275 }
276
277 pub fn decode_chunk<'a>(&mut self, piece: &'a [u8]) -> Result<(ObjectChunk, &'a [u8]), Error> {
280 let chunk = self.next_chunk().ok_or(Error::TrailingBytes)?;
281 exact_length(piece.len(), self.chunk_length(piece)?)?;
282 self.advance();
283 Ok((chunk, &piece[4..]))
284 }
285
286 pub fn tail(&mut self, tail: &HeaderTail) -> Result<Vec<u8>, Error> {
288 require(
289 matches!(self.position, Position::Tail) && tail.roots.len() == self.part_lengths.len(),
290 "write header tail",
291 Rule::StreamLength,
292 )?;
293 let mut bytes = Vec::with_capacity(WriteObjectPrefix::tail_length(tail.roots.len()));
294 for root in &tail.roots {
295 bytes.extend_from_slice(root);
296 }
297 bytes.extend_from_slice(tail.signature.as_bytes());
298 self.position = self.after_tail();
299 Ok(bytes)
300 }
301
302 fn after_tail(&self) -> Position {
303 if self.part_lengths.is_empty() {
304 Position::Signature
305 } else {
306 Position::Chunk { section: 1, index: 0 }
307 }
308 }
309
310 pub fn signature(&mut self, signature: &Signature) -> Result<[u8; SIGNATURE_LEN], Error> {
312 require(
313 matches!(self.position, Position::Signature),
314 "write signature position",
315 Rule::StreamLength,
316 )?;
317 self.position = Position::End;
318 Ok(*signature.as_bytes())
319 }
320
321 pub fn read_signature(&mut self, bytes: &[u8]) -> Result<Signature, Error> {
324 let signature = sealed::signature(bytes)?;
325 self.signature(&signature)?;
326 Ok(signature)
327 }
328
329 pub fn finish(&self, trailing: &[u8]) -> Result<(), Error> {
331 if !trailing.is_empty() {
332 return Err(Error::TrailingBytes);
333 }
334 require(matches!(self.position, Position::End), "write signature", Rule::StreamLength)
335 }
336
337 fn advance(&mut self) {
338 let Position::Chunk { section, index } = self.position else {
339 unreachable!("a chunk was checked");
340 };
341 if section == 0 {
342 self.position = Position::Tail;
343 } else if index + 1 < chunk_count(self.part_lengths[section - 1]) {
344 self.position = Position::Chunk { section, index: index + 1 };
345 } else if section < self.part_lengths.len() {
346 self.position = Position::Chunk { section: section + 1, index: 0 };
347 } else {
348 self.position = Position::Signature;
349 }
350 }
351}
352
353#[cfg(test)]
354#[path = "sealed_write_tests.rs"]
355mod tests;