1use crate::{codec, context::LocalSharedStatePackageContext};
4use anyhow::{Context, Result, ensure};
5
6pub const CHUNK_HEADER_BYTES: usize = 198;
7pub const CHUNK_RECORD_OVERHEAD: usize = 222;
8
9#[allow(clippy::too_many_arguments)]
10pub fn chunk_header(
11 context: LocalSharedStatePackageContext,
12 stream_id: [u8; 32],
13 artifact_id: [u8; 32],
14 family: u8,
15 class: u8,
16 index: u32,
17 count: u32,
18 total: u64,
19 nonce: [u8; 24],
20) -> Result<Vec<u8>> {
21 let mut header = Vec::with_capacity(CHUNK_HEADER_BYTES);
22 header.extend_from_slice(b"AVEN");
23 header.push(2);
24 header.extend_from_slice(&1_u16.to_be_bytes());
25 header.push(1);
26 codec::bytes(&mut header, &context.vault_id);
27 codec::bytes(&mut header, &stream_id);
28 codec::bytes(&mut header, &context.generation_id);
29 codec::bytes(&mut header, &artifact_id);
30 header.push(family);
31 header.push(class);
32 header.extend_from_slice(&index.to_be_bytes());
33 header.extend_from_slice(&count.to_be_bytes());
34 header.extend_from_slice(&total.to_be_bytes());
35 codec::bytes(&mut header, &nonce);
36 ensure!(header.len() == CHUNK_HEADER_BYTES, "invalid chunk header");
37 Ok(header)
38}
39
40#[allow(clippy::too_many_arguments)]
41pub fn validate_chunk_header(
42 header: &[u8],
43 context: LocalSharedStatePackageContext,
44 stream_id: [u8; 32],
45 artifact_id: [u8; 32],
46 family: u8,
47 class: u8,
48 index: u32,
49 count: u32,
50 total: u64,
51) -> Result<[u8; 24]> {
52 ensure!(
53 header.len() == CHUNK_HEADER_BYTES,
54 "error encrypted-local-shared-package-header-size"
55 );
56 let nonce_offset = CHUNK_HEADER_BYTES - 28;
57 ensure!(
58 u32::from_be_bytes(header[nonce_offset..nonce_offset + 4].try_into()?) == 24,
59 "error encrypted-local-shared-package-nonce-size"
60 );
61 let nonce: [u8; 24] = header[nonce_offset + 4..].try_into()?;
62 let expected = chunk_header(
63 context,
64 stream_id,
65 artifact_id,
66 family,
67 class,
68 index,
69 count,
70 total,
71 nonce,
72 )?;
73 ensure!(
74 header == expected,
75 "error encrypted-local-shared-package-header-mismatch"
76 );
77 Ok(nonce)
78}
79
80pub fn split_record(record: &[u8]) -> Result<(&[u8], &[u8])> {
81 ensure!(
82 record.len() >= CHUNK_RECORD_OVERHEAD,
83 "error encrypted-local-shared-package-record-truncated"
84 );
85 let header_len = usize::try_from(u32::from_be_bytes(record[0..4].try_into()?))?;
86 ensure!(
87 header_len == CHUNK_HEADER_BYTES,
88 "error encrypted-local-shared-package-header-size"
89 );
90 let body_len_offset = 4_usize
91 .checked_add(header_len)
92 .context("record offset overflow")?;
93 let body_start = body_len_offset
94 .checked_add(4)
95 .context("record offset overflow")?;
96 ensure!(
97 record.len() >= body_start,
98 "error encrypted-local-shared-package-record-truncated"
99 );
100 let body_len = usize::try_from(u32::from_be_bytes(
101 record[body_len_offset..body_start].try_into()?,
102 ))?;
103 ensure!(
104 body_len >= 16 && body_start.checked_add(body_len) == Some(record.len()),
105 "error encrypted-local-shared-package-record-length"
106 );
107 Ok((&record[4..body_len_offset], &record[body_start..]))
108}