1use super::codec::*;
2use super::{EncryptedArtifact, EncryptedChunk};
3use super::{Error, Result};
4use crate::{context::LocalSharedStatePackageContext, record as crypto};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct Artifact {
8 pub total: u64,
9 pub aggregate: [u8; 32],
10 pub chunks: Vec<Chunk>,
11}
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Chunk {
15 pub length: u64,
16 pub hash: [u8; 32],
17 pub nonce: [u8; 24],
18}
19
20impl Artifact {
21 pub fn from_encrypted(artifact: &EncryptedArtifact<'_>) -> Result<Self> {
22 let chunks = artifact
23 .chunks
24 .iter()
25 .map(|chunk| {
26 let (header, _) =
27 crypto::split_record(&chunk.record).map_err(|_| Error::Invalid)?;
28 Ok(Chunk {
29 length: number(chunk.record.len())?,
30 hash: chunk.record_commitment,
31 nonce: header[174..198].try_into().map_err(|_| Error::Invalid)?,
32 })
33 })
34 .collect::<Result<Vec<_>>>()?;
35 Ok(Self {
36 total: artifact.total_plaintext_bytes,
37 aggregate: artifact.aggregate_commitment,
38 chunks,
39 })
40 }
41 pub fn shape(&self, maximum: u64, image: bool) -> Result<()> {
42 bound(self.total, maximum)?;
43 valid(!image || self.total > 0)?;
44 valid(number(self.chunks.len())? == count(self.total))?;
45 for (i, chunk) in self.chunks.iter().enumerate() {
46 let offset = number(i)?.checked_mul(CHUNK).ok_or(Error::Invalid)?;
47 let expected = self
48 .total
49 .checked_sub(offset)
50 .ok_or(Error::Invalid)?
51 .min(CHUNK);
52 valid(chunk.length == add(expected, 222)?)?;
53 }
54 Ok(())
55 }
56 pub fn write(&self, out: &mut Vec<u8>) -> Result<()> {
57 u64_bytes(out, self.total);
58 out.extend_from_slice(&self.aggregate);
59 u64_bytes(out, number(self.chunks.len())?);
60 for (i, chunk) in self.chunks.iter().enumerate() {
61 u64_bytes(out, number(i)?);
62 u64_bytes(out, chunk.length);
63 out.extend_from_slice(&chunk.hash);
64 out.extend_from_slice(&chunk.nonce);
65 }
66 Ok(())
67 }
68 pub fn read(r: &mut Reader<'_>, maximum: u64, image: bool) -> Result<Self> {
69 let total = r.u64()?;
70 bound(total, maximum)?;
71 let aggregate = r.array()?;
72 let n = r.u64()?;
73 valid(n == count(total))?;
74 let mut chunks = Vec::new();
75 for index in 0..n {
76 valid(r.u64()? == index)?;
77 chunks.push(Chunk {
78 length: r.u64()?,
79 hash: r.array()?,
80 nonce: r.array()?,
81 });
82 }
83 let value = Self {
84 total,
85 aggregate,
86 chunks,
87 };
88 value.shape(maximum, image)?;
89 Ok(value)
90 }
91 #[allow(clippy::too_many_arguments)]
92 pub fn verify(
93 &self,
94 records: &[Vec<u8>],
95 context: LocalSharedStatePackageContext,
96 stream: [u8; 32],
97 id: [u8; 32],
98 family: u8,
99 class: u8,
100 ) -> Result<()> {
101 valid(records.len() == self.chunks.len())?;
102 let mut digest = sha2::Sha256::new();
103 use sha2::Digest;
104 for (index, record) in records.iter().enumerate() {
105 self.verify_chunk(record, index, context, stream, id, family, class)?;
106 digest.update(record);
107 }
108 valid(<[u8; 32]>::from(digest.finalize()) == self.aggregate)
109 }
110 #[allow(clippy::too_many_arguments)]
111 pub fn verify_chunk(
112 &self,
113 record: &[u8],
114 index: usize,
115 context: LocalSharedStatePackageContext,
116 stream: [u8; 32],
117 id: [u8; 32],
118 family: u8,
119 class: u8,
120 ) -> Result<()> {
121 let chunk = self.chunks.get(index).ok_or(Error::Invalid)?;
122 valid(number(record.len())? == chunk.length && crate::codec::hash(record) == chunk.hash)?;
123 let (header, _) = crypto::split_record(record).map_err(|_| Error::Invalid)?;
124 let nonce = crypto::validate_chunk_header(
125 header,
126 context,
127 stream,
128 id,
129 family,
130 class,
131 u32::try_from(index).map_err(|_| Error::Invalid)?,
132 u32::try_from(self.chunks.len()).map_err(|_| Error::Invalid)?,
133 self.total,
134 )
135 .map_err(|_| Error::Invalid)?;
136 valid(nonce == chunk.nonce)
137 }
138 pub fn encrypted<'a>(&self, records: &'a [Vec<u8>]) -> EncryptedArtifact<'a> {
139 EncryptedArtifact {
140 total_plaintext_bytes: self.total,
141 aggregate_commitment: self.aggregate,
142 chunks: records
143 .iter()
144 .zip(&self.chunks)
145 .map(|(record, chunk)| EncryptedChunk {
146 record_commitment: chunk.hash,
147 record: record.as_slice().into(),
148 })
149 .collect(),
150 }
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
155pub struct Declaration {
156 pub count: u64,
157 pub length: u64,
158 pub hash: [u8; 32],
159 pub slices: Vec<[u8; 32]>,
161}
162impl Declaration {
163 pub fn new(bytes: &[u8], class: u8) -> Result<Self> {
164 Ok(Self {
165 count: number(read_stream(bytes, class)?.len())?,
166 length: number(bytes.len())?,
167 hash: crate::codec::hash(bytes),
168 slices: bytes.chunks(size(CHUNK)?).map(crate::codec::hash).collect(),
169 })
170 }
171 pub fn write(&self, out: &mut Vec<u8>) {
172 u64_bytes(out, self.count);
173 u64_bytes(out, self.length);
174 out.extend_from_slice(&self.hash);
175 for slice in &self.slices {
176 out.extend_from_slice(slice);
177 }
178 }
179 pub fn read(r: &mut Reader<'_>) -> Result<Self> {
180 let n = r.u64()?;
181 let length = r.u64()?;
182 bound(n, RECORD_LIMIT)?;
183 bound(length, CATALOG_LIMIT)?;
184 valid(length >= 15)?;
185 let hash = r.array()?;
186 let slices = (0..count(length))
187 .map(|_| r.array())
188 .collect::<Result<_>>()?;
189 Ok(Self {
190 count: n,
191 length,
192 hash,
193 slices,
194 })
195 }
196 pub fn slice_lengths(&self) -> Vec<u64> {
197 (0..count(self.length))
198 .map(|i| (self.length - i * CHUNK).min(CHUNK))
199 .collect()
200 }
201 pub fn verify_slice(&self, index: usize, bytes: &[u8]) -> Result<()> {
203 valid(
204 self.slice_lengths().get(index) == Some(&number(bytes.len())?)
205 && self.slices.get(index) == Some(&crate::codec::hash(bytes)),
206 )
207 }
208 pub fn verify(&self, bytes: &[u8], class: u8) -> Result<()> {
210 valid(number(bytes.len())? == self.length && crate::codec::hash(bytes) == self.hash)?;
211 valid(Self::new(bytes, class)? == *self)
212 }
213}
214
215#[derive(Clone, Debug, PartialEq, Eq)]
216pub struct Parent {
217 pub workspace: String,
218 pub task: String,
219 pub deleted: bool,
220 pub version: Option<String>,
221 pub protected: bool,
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
225pub struct Reference {
226 pub workspace: String,
227 pub task: String,
228 pub reference: String,
229 pub deleted: bool,
230 pub object: Option<[u8; 32]>,
231}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
234pub struct Image {
235 pub id: [u8; 32],
236 pub selection: u8,
238 pub artifact: Artifact,
239}
240
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub struct Images {
243 pub objects: Vec<Image>,
244 pub parents: Vec<Parent>,
245 pub references: Vec<Reference>,
246}
247
248impl Images {
249 pub fn encode(&self) -> Result<Vec<u8>> {
250 let mut rows = Vec::new();
251 for object in &self.objects {
252 let mut row = vec![1];
253 row.extend_from_slice(&object.id);
254 row.push(object.selection);
255 object.artifact.write(&mut row)?;
256 rows.push(row);
257 }
258 for parent in &self.parents {
259 let mut row = vec![2];
260 text(&mut row, &parent.workspace)?;
261 text(&mut row, &parent.task)?;
262 row.push(u8::from(parent.deleted));
263 row.push(u8::from(parent.protected));
264 row.push(u8::from(parent.version.is_some()));
265 if let Some(version) = &parent.version {
266 text(&mut row, version)?;
267 }
268 rows.push(row);
269 }
270 for reference in &self.references {
271 let mut row = vec![3];
272 text(&mut row, &reference.workspace)?;
273 text(&mut row, &reference.task)?;
274 text(&mut row, &reference.reference)?;
275 row.push(u8::from(reference.deleted));
276 row.push(u8::from(reference.object.is_some()));
277 if let Some(object) = reference.object {
278 row.extend_from_slice(&object);
279 }
280 rows.push(row);
281 }
282 stream(3, &rows)
283 }
284 pub fn decode(bytes: &[u8]) -> Result<Self> {
285 let mut result = Self {
286 objects: Vec::new(),
287 parents: Vec::new(),
288 references: Vec::new(),
289 };
290 let mut previous_kind = 0;
291 let mut total = 0;
292 for row in read_stream(bytes, 3)? {
293 let mut r = Reader(row);
294 let kind = r.byte()?;
295 valid(kind >= previous_kind)?;
296 previous_kind = kind;
297 match kind {
298 1 => {
299 bound(number(result.objects.len())? + 1, IMAGE_COUNT)?;
300 let id = r.array()?;
301 valid(
302 result
303 .objects
304 .last()
305 .is_none_or(|last: &Image| last.id < id),
306 )?;
307 let selection = r.byte()?;
308 valid(matches!(selection, 1 | 2))?;
309 let artifact = Artifact::read(&mut r, IMAGE_LIMIT, true)?;
310 total = add(total, artifact.total)?;
311 bound(total, STATE_LIMIT)?;
312 result.objects.push(Image {
313 id,
314 selection,
315 artifact,
316 });
317 }
318 2 => {
319 let workspace = r.text()?;
320 let task = r.text()?;
321 valid(result.parents.last().is_none_or(|last: &Parent| {
322 (&last.workspace, &last.task) < (&workspace, &task)
323 }))?;
324 let deleted = r.flag()?;
325 let protected = r.flag()?;
326 let version = if r.flag()? { Some(r.text()?) } else { None };
327 valid(version.is_some() || protected)?;
328 result.parents.push(Parent {
329 workspace,
330 task,
331 deleted,
332 protected,
333 version,
334 });
335 }
336 3 => {
337 let workspace = r.text()?;
338 let task = r.text()?;
339 let reference = r.text()?;
340 valid(result.references.last().is_none_or(|last: &Reference| {
341 (&last.workspace, &last.reference) < (&workspace, &reference)
342 }))?;
343 let deleted = r.flag()?;
344 let object = if r.flag()? { Some(r.array()?) } else { None };
345 result.references.push(Reference {
346 workspace,
347 task,
348 reference,
349 deleted,
350 object,
351 });
352 }
353 _ => return Err(Error::Invalid),
354 }
355 r.end()?;
356 }
357 for reference in &result.references {
358 valid(
359 result
360 .parents
361 .binary_search_by(|p| {
362 (&p.workspace, &p.task).cmp(&(&reference.workspace, &reference.task))
363 })
364 .is_ok(),
365 )?;
366 if let Some(object) = reference.object {
367 valid(
368 result
369 .objects
370 .binary_search_by_key(&object, |o| o.id)
371 .is_ok(),
372 )?;
373 }
374 }
375 let mut current = std::collections::HashSet::new();
376 for reference in &result.references {
377 let parent = result
378 .parents
379 .binary_search_by(|p| {
380 (&p.workspace, &p.task).cmp(&(&reference.workspace, &reference.task))
381 })
382 .map_err(|_| Error::Invalid)?;
383 if !reference.deleted
384 && !result.parents[parent].deleted
385 && let Some(object) = reference.object
386 {
387 current.insert(object);
388 }
389 }
390 for object in &result.objects {
391 valid((object.selection == 1) == current.contains(&object.id))?;
392 }
393 Ok(result)
394 }
395}
396
397pub fn prefix_encode(rows: &[(u64, String)]) -> Result<Vec<u8>> {
398 let mut records = Vec::new();
399 for (rank, id) in rows {
400 let mut record = Vec::new();
401 u64_bytes(&mut record, *rank);
402 text(&mut record, id)?;
403 records.push(record);
404 }
405 stream(2, &records)
406}
407
408pub fn prefix_decode(bytes: &[u8], expected: u64) -> Result<Vec<(u64, String)>> {
409 valid(expected < i64::MAX as u64)?;
410 bound(expected, RECORD_LIMIT)?;
411 let records = read_stream(bytes, 2)?;
412 valid(number(records.len())? == expected)?;
413 let mut ids = std::collections::HashSet::new();
414 let mut result = Vec::new();
415 for (index, record) in records.iter().enumerate() {
416 let mut r = Reader(record);
417 let rank = r.u64()?;
418 valid(rank == add(number(index)?, 1)?)?;
419 let id = r.text()?;
420 valid(ids.insert(id.clone()))?;
421 r.end()?;
422 result.push((rank, id));
423 }
424 Ok(result)
425}