1use sha2::{Digest, Sha256};
2
3use crate::format::{
4 root_auth_spec_id_for_revision, AeadAlgo, BlockKind, CompressionAlgo, FecAlgo, FormatError,
5 KdfAlgo, FORMAT_VERSION, VOLUME_FORMAT_REV, VOLUME_FORMAT_REV_43, VOLUME_FORMAT_REV_44,
6};
7
8const ROOT_AUTH_DESCRIPTOR_DOMAIN: &[u8] = b"tzap-root-auth-descriptor-v1\0";
9const ARCHIVE_ROOT_DOMAIN_V43: &[u8] = b"tzap-archive-root-v43\0";
10const ARCHIVE_ROOT_DOMAIN_V44: &[u8] = b"tzap-archive-root-v44\0";
11const CRYPTO_HEADER_PRE_HMAC_DOMAIN_V43: &[u8] = b"tzap-crypto-header-pre-hmac-v43\0";
12const CRYPTO_HEADER_PRE_HMAC_DOMAIN_V44: &[u8] = b"tzap-crypto-header-pre-hmac-v44\0";
13const MANIFEST_FOOTER_GLOBAL_PRE_HMAC_DOMAIN_V43: &[u8] =
14 b"tzap-manifest-footer-global-pre-hmac-v43\0";
15const MANIFEST_FOOTER_GLOBAL_PRE_HMAC_DOMAIN_V44: &[u8] =
16 b"tzap-manifest-footer-global-pre-hmac-v44\0";
17const CRITICAL_METADATA_DOMAIN_V43: &[u8] = b"tzap-critical-metadata-v43\0";
18const CRITICAL_METADATA_DOMAIN_V44: &[u8] = b"tzap-critical-metadata-v44\0";
19const INDEX_ROOT_DOMAIN_V43: &[u8] = b"tzap-index-root-v43\0";
20const INDEX_ROOT_DOMAIN_V44: &[u8] = b"tzap-index-root-v44\0";
21const FEC_LAYOUT_DOMAIN_V43: &[u8] = b"tzap-fec-layout-v43\0";
22const FEC_LAYOUT_DOMAIN_V44: &[u8] = b"tzap-fec-layout-v44\0";
23const DATA_BLOCK_MERKLE_DOMAIN_V43: &[u8] = b"tzap-data-block-merkle-v43\0";
24const DATA_BLOCK_MERKLE_DOMAIN_V44: &[u8] = b"tzap-data-block-merkle-v44\0";
25const EMPTY_MERKLE_DOMAIN: &[u8] = b"tzap-empty-merkle-tree-v1\0";
26const SIGNER_IDENTITY_DOMAIN: &[u8] = b"tzap-signer-identity-v1\0";
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct FecLayoutObjectRow {
30 pub object_class: u8,
31 pub present: bool,
32 pub object_id: u64,
33 pub first_block_index: u64,
34 pub data_block_count: u32,
35 pub parity_block_count: u32,
36 pub encrypted_size: u32,
37 pub plain_size: u32,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct DataBlockMerkleLeaf {
42 pub block_index: u64,
43 pub kind: BlockKind,
44 pub flags: u8,
45 pub payload: Vec<u8>,
46}
47
48#[derive(Debug, Clone, Copy)]
49pub struct CriticalMetadataDigestInputs<'a> {
50 pub archive_uuid: [u8; 16],
51 pub session_id: [u8; 16],
52 pub format_version: u16,
53 pub volume_format_rev: u16,
54 pub stripe_width: u32,
55 pub total_volumes: u32,
56 pub compression_algo: CompressionAlgo,
57 pub aead_algo: AeadAlgo,
58 pub fec_algo: FecAlgo,
59 pub kdf_algo: KdfAlgo,
60 pub crypto_header_pre_hmac_bytes: &'a [u8],
61 pub chunk_size: u32,
62 pub envelope_target_size: u32,
63 pub block_size: u32,
64 pub fec_data_shards: u16,
65 pub fec_parity_shards: u16,
66 pub index_fec_data_shards: u16,
67 pub index_fec_parity_shards: u16,
68 pub index_root_fec_data_shards: u16,
69 pub index_root_fec_parity_shards: u16,
70 pub volume_loss_tolerance: u8,
71 pub bit_rot_buffer_pct: u8,
72 pub has_dictionary: u8,
73 pub manifest_footer_global_pre_hmac_bytes: &'a [u8],
74 pub index_root_first_block: u64,
75 pub index_root_data_block_count: u32,
76 pub index_root_parity_block_count: u32,
77 pub index_root_encrypted_size: u32,
78 pub index_root_decompressed_size: u32,
79 pub root_auth_descriptor_digest: [u8; 32],
80}
81
82#[derive(Debug, Clone, Copy)]
83pub struct ArchiveRootInputs {
84 pub archive_uuid: [u8; 16],
85 pub session_id: [u8; 16],
86 pub format_version: u16,
87 pub volume_format_rev: u16,
88 pub compression_algo: CompressionAlgo,
89 pub aead_algo: AeadAlgo,
90 pub fec_algo: FecAlgo,
91 pub kdf_algo: KdfAlgo,
92 pub critical_metadata_digest: [u8; 32],
93 pub index_digest: [u8; 32],
94 pub fec_layout_digest: [u8; 32],
95 pub total_data_block_count: u64,
96 pub data_block_merkle_root: [u8; 32],
97 pub root_auth_descriptor_digest: [u8; 32],
98 pub signer_identity_digest: [u8; 32],
99}
100
101#[derive(Debug, Clone, Copy)]
102struct RootAuthRevisionParams {
103 root_auth_spec_id: [u8; 24],
104 archive_root_domain: &'static [u8],
105 crypto_header_pre_hmac_domain: &'static [u8],
106 manifest_footer_global_pre_hmac_domain: &'static [u8],
107 critical_metadata_domain: &'static [u8],
108 index_root_domain: &'static [u8],
109 fec_layout_domain: &'static [u8],
110 data_block_merkle_domain: &'static [u8],
111}
112
113fn root_auth_revision_params(
114 format_version: u16,
115 volume_format_rev: u16,
116) -> Result<RootAuthRevisionParams, FormatError> {
117 let root_auth_spec_id = root_auth_spec_id_for_revision(format_version, volume_format_rev)?;
118 match volume_format_rev {
119 VOLUME_FORMAT_REV_43 => Ok(RootAuthRevisionParams {
120 root_auth_spec_id,
121 archive_root_domain: ARCHIVE_ROOT_DOMAIN_V43,
122 crypto_header_pre_hmac_domain: CRYPTO_HEADER_PRE_HMAC_DOMAIN_V43,
123 manifest_footer_global_pre_hmac_domain: MANIFEST_FOOTER_GLOBAL_PRE_HMAC_DOMAIN_V43,
124 critical_metadata_domain: CRITICAL_METADATA_DOMAIN_V43,
125 index_root_domain: INDEX_ROOT_DOMAIN_V43,
126 fec_layout_domain: FEC_LAYOUT_DOMAIN_V43,
127 data_block_merkle_domain: DATA_BLOCK_MERKLE_DOMAIN_V43,
128 }),
129 VOLUME_FORMAT_REV_44 => Ok(RootAuthRevisionParams {
130 root_auth_spec_id,
131 archive_root_domain: ARCHIVE_ROOT_DOMAIN_V44,
132 crypto_header_pre_hmac_domain: CRYPTO_HEADER_PRE_HMAC_DOMAIN_V44,
133 manifest_footer_global_pre_hmac_domain: MANIFEST_FOOTER_GLOBAL_PRE_HMAC_DOMAIN_V44,
134 critical_metadata_domain: CRITICAL_METADATA_DOMAIN_V44,
135 index_root_domain: INDEX_ROOT_DOMAIN_V44,
136 fec_layout_domain: FEC_LAYOUT_DOMAIN_V44,
137 data_block_merkle_domain: DATA_BLOCK_MERKLE_DOMAIN_V44,
138 }),
139 _ => unreachable!("root_auth_spec_id_for_revision accepted unsupported revision"),
140 }
141}
142
143pub fn root_auth_descriptor_digest(
144 authenticator_id: u16,
145 signer_identity_type: u16,
146 signer_identity_bytes: &[u8],
147 authenticator_value_length: u32,
148 footer_length: u32,
149) -> Result<[u8; 32], FormatError> {
150 root_auth_descriptor_digest_for_revision(
151 FORMAT_VERSION,
152 VOLUME_FORMAT_REV,
153 authenticator_id,
154 signer_identity_type,
155 signer_identity_bytes,
156 authenticator_value_length,
157 footer_length,
158 )
159}
160
161pub fn root_auth_descriptor_digest_for_revision(
162 format_version: u16,
163 volume_format_rev: u16,
164 authenticator_id: u16,
165 signer_identity_type: u16,
166 signer_identity_bytes: &[u8],
167 authenticator_value_length: u32,
168 footer_length: u32,
169) -> Result<[u8; 32], FormatError> {
170 let params = root_auth_revision_params(format_version, volume_format_rev)?;
171 let signer_identity_length = u32::try_from(signer_identity_bytes.len())
172 .map_err(|_| FormatError::InvalidArchive("root-auth signer identity length overflow"))?;
173 let signer_identity_hash = sha256_bytes(signer_identity_bytes);
174 let mut bytes = Vec::new();
175 bytes.extend_from_slice(ROOT_AUTH_DESCRIPTOR_DOMAIN);
176 bytes.extend_from_slice(¶ms.root_auth_spec_id);
177 push_u16(&mut bytes, authenticator_id);
178 push_u16(&mut bytes, signer_identity_type);
179 push_u32(&mut bytes, signer_identity_length);
180 bytes.extend_from_slice(&signer_identity_hash);
181 push_u32(&mut bytes, authenticator_value_length);
182 push_u32(&mut bytes, footer_length);
183 Ok(sha256_bytes(&bytes))
184}
185
186pub fn signer_identity_digest(
187 signer_identity_type: u16,
188 signer_identity_bytes: &[u8],
189) -> Result<[u8; 32], FormatError> {
190 let signer_identity_length = u32::try_from(signer_identity_bytes.len())
191 .map_err(|_| FormatError::InvalidArchive("root-auth signer identity length overflow"))?;
192 let mut bytes = Vec::new();
193 bytes.extend_from_slice(SIGNER_IDENTITY_DOMAIN);
194 push_u16(&mut bytes, signer_identity_type);
195 push_u32(&mut bytes, signer_identity_length);
196 bytes.extend_from_slice(signer_identity_bytes);
197 Ok(sha256_bytes(&bytes))
198}
199
200pub fn critical_metadata_digest(
201 inputs: CriticalMetadataDigestInputs<'_>,
202) -> Result<[u8; 32], FormatError> {
203 let params = root_auth_revision_params(inputs.format_version, inputs.volume_format_rev)?;
204 let crypto_header_pre_hmac_digest = len_prefixed_digest(
205 params.crypto_header_pre_hmac_domain,
206 inputs.crypto_header_pre_hmac_bytes,
207 )?;
208 let manifest_footer_global_pre_hmac_digest = len_prefixed_digest(
209 params.manifest_footer_global_pre_hmac_domain,
210 inputs.manifest_footer_global_pre_hmac_bytes,
211 )?;
212
213 let mut bytes = Vec::new();
214 bytes.extend_from_slice(params.critical_metadata_domain);
215 bytes.extend_from_slice(&inputs.archive_uuid);
216 bytes.extend_from_slice(&inputs.session_id);
217 push_u16(&mut bytes, inputs.format_version);
218 push_u16(&mut bytes, inputs.volume_format_rev);
219 push_u32(&mut bytes, inputs.stripe_width);
220 push_u32(&mut bytes, inputs.total_volumes);
221 push_u16(&mut bytes, inputs.compression_algo as u16);
222 push_u16(&mut bytes, inputs.aead_algo as u16);
223 push_u16(&mut bytes, inputs.fec_algo as u16);
224 push_u16(&mut bytes, inputs.kdf_algo as u16);
225 bytes.extend_from_slice(&crypto_header_pre_hmac_digest);
226 push_u32(&mut bytes, inputs.chunk_size);
227 push_u32(&mut bytes, inputs.envelope_target_size);
228 push_u32(&mut bytes, inputs.block_size);
229 push_u16(&mut bytes, inputs.fec_data_shards);
230 push_u16(&mut bytes, inputs.fec_parity_shards);
231 push_u16(&mut bytes, inputs.index_fec_data_shards);
232 push_u16(&mut bytes, inputs.index_fec_parity_shards);
233 push_u16(&mut bytes, inputs.index_root_fec_data_shards);
234 push_u16(&mut bytes, inputs.index_root_fec_parity_shards);
235 bytes.push(inputs.volume_loss_tolerance);
236 bytes.push(inputs.bit_rot_buffer_pct);
237 bytes.push(inputs.has_dictionary);
238 bytes.extend_from_slice(&manifest_footer_global_pre_hmac_digest);
239 push_u64(&mut bytes, inputs.index_root_first_block);
240 push_u32(&mut bytes, inputs.index_root_data_block_count);
241 push_u32(&mut bytes, inputs.index_root_parity_block_count);
242 push_u32(&mut bytes, inputs.index_root_encrypted_size);
243 push_u32(&mut bytes, inputs.index_root_decompressed_size);
244 bytes.extend_from_slice(&inputs.root_auth_descriptor_digest);
245 Ok(sha256_bytes(&bytes))
246}
247
248pub fn index_digest(index_root_plaintext: &[u8]) -> [u8; 32] {
249 index_digest_for_revision(FORMAT_VERSION, VOLUME_FORMAT_REV, index_root_plaintext)
250 .expect("default RootAuth revision is supported")
251}
252
253pub fn index_digest_for_revision(
254 format_version: u16,
255 volume_format_rev: u16,
256 index_root_plaintext: &[u8],
257) -> Result<[u8; 32], FormatError> {
258 let params = root_auth_revision_params(format_version, volume_format_rev)?;
259 let mut bytes = Vec::new();
260 bytes.extend_from_slice(params.index_root_domain);
261 bytes.extend_from_slice(index_root_plaintext);
262 Ok(sha256_bytes(&bytes))
263}
264
265pub fn fec_layout_digest(rows: &[FecLayoutObjectRow]) -> Result<[u8; 32], FormatError> {
266 fec_layout_digest_for_revision(FORMAT_VERSION, VOLUME_FORMAT_REV, rows)
267}
268
269pub fn fec_layout_digest_for_revision(
270 format_version: u16,
271 volume_format_rev: u16,
272 rows: &[FecLayoutObjectRow],
273) -> Result<[u8; 32], FormatError> {
274 let params = root_auth_revision_params(format_version, volume_format_rev)?;
275 let row_count = u32::try_from(rows.len())
276 .map_err(|_| FormatError::InvalidArchive("root-auth FEC row count overflow"))?;
277 let mut bytes = Vec::new();
278 bytes.extend_from_slice(params.fec_layout_domain);
279 push_u32(&mut bytes, row_count);
280 for row in rows {
281 bytes.push(row.object_class);
282 bytes.push(if row.present { 1 } else { 0 });
283 push_u16(&mut bytes, 0);
284 push_u64(&mut bytes, row.object_id);
285 push_u64(&mut bytes, row.first_block_index);
286 push_u32(&mut bytes, row.data_block_count);
287 push_u32(&mut bytes, row.parity_block_count);
288 push_u32(&mut bytes, row.encrypted_size);
289 push_u32(&mut bytes, row.plain_size);
290 }
291 Ok(sha256_bytes(&bytes))
292}
293
294pub fn data_block_merkle_root(leaves: &[DataBlockMerkleLeaf]) -> [u8; 32] {
295 data_block_merkle_root_for_revision(FORMAT_VERSION, VOLUME_FORMAT_REV, leaves)
296 .expect("default RootAuth revision is supported")
297}
298
299pub fn data_block_merkle_root_for_revision(
300 format_version: u16,
301 volume_format_rev: u16,
302 leaves: &[DataBlockMerkleLeaf],
303) -> Result<[u8; 32], FormatError> {
304 let params = root_auth_revision_params(format_version, volume_format_rev)?;
305 if leaves.is_empty() {
306 return Ok(empty_data_block_merkle_root(params));
307 }
308
309 let leaf_hashes = leaves
310 .iter()
311 .map(|leaf| {
312 data_block_merkle_leaf_hash_with_params(
313 params,
314 leaf.block_index,
315 leaf.kind,
316 leaf.flags,
317 &leaf.payload,
318 )
319 })
320 .collect::<Vec<_>>();
321 Ok(data_block_merkle_root_from_leaf_hashes_with_params(
322 params,
323 &leaf_hashes,
324 ))
325}
326
327pub fn data_block_merkle_leaf_hash(
328 block_index: u64,
329 kind: BlockKind,
330 flags: u8,
331 payload: &[u8],
332) -> [u8; 32] {
333 data_block_merkle_leaf_hash_for_revision(
334 FORMAT_VERSION,
335 VOLUME_FORMAT_REV,
336 block_index,
337 kind,
338 flags,
339 payload,
340 )
341 .expect("default RootAuth revision is supported")
342}
343
344pub fn data_block_merkle_leaf_hash_for_revision(
345 format_version: u16,
346 volume_format_rev: u16,
347 block_index: u64,
348 kind: BlockKind,
349 flags: u8,
350 payload: &[u8],
351) -> Result<[u8; 32], FormatError> {
352 let params = root_auth_revision_params(format_version, volume_format_rev)?;
353 Ok(data_block_merkle_leaf_hash_with_params(
354 params,
355 block_index,
356 kind,
357 flags,
358 payload,
359 ))
360}
361
362fn data_block_merkle_leaf_hash_with_params(
363 params: RootAuthRevisionParams,
364 block_index: u64,
365 kind: BlockKind,
366 flags: u8,
367 payload: &[u8],
368) -> [u8; 32] {
369 let mut leaf_payload = Vec::with_capacity(10 + payload.len());
370 push_u64(&mut leaf_payload, block_index);
371 leaf_payload.push(kind as u8);
372 leaf_payload.push(flags);
373 leaf_payload.extend_from_slice(payload);
374
375 let mut bytes = Vec::new();
376 bytes.push(0x00);
377 bytes.extend_from_slice(params.data_block_merkle_domain);
378 bytes.extend_from_slice(&leaf_payload);
379 sha256_bytes(&bytes)
380}
381
382pub fn data_block_merkle_root_from_leaf_hashes(leaf_hashes: &[[u8; 32]]) -> [u8; 32] {
383 data_block_merkle_root_from_leaf_hashes_for_revision(
384 FORMAT_VERSION,
385 VOLUME_FORMAT_REV,
386 leaf_hashes,
387 )
388 .expect("default RootAuth revision is supported")
389}
390
391pub fn data_block_merkle_root_from_leaf_hashes_for_revision(
392 format_version: u16,
393 volume_format_rev: u16,
394 leaf_hashes: &[[u8; 32]],
395) -> Result<[u8; 32], FormatError> {
396 let params = root_auth_revision_params(format_version, volume_format_rev)?;
397 Ok(data_block_merkle_root_from_leaf_hashes_with_params(
398 params,
399 leaf_hashes,
400 ))
401}
402
403fn data_block_merkle_root_from_leaf_hashes_with_params(
404 params: RootAuthRevisionParams,
405 leaf_hashes: &[[u8; 32]],
406) -> [u8; 32] {
407 if leaf_hashes.is_empty() {
408 return empty_data_block_merkle_root(params);
409 }
410
411 let mut level = leaf_hashes.to_vec();
412
413 while level.len() > 1 {
414 let mut next = Vec::with_capacity(level.len().div_ceil(2));
415 let mut idx = 0usize;
416 while idx < level.len() {
417 if idx + 1 == level.len() {
418 next.push(level[idx]);
419 } else {
420 let mut bytes = Vec::new();
421 bytes.push(0x01);
422 bytes.extend_from_slice(params.data_block_merkle_domain);
423 bytes.extend_from_slice(&level[idx]);
424 bytes.extend_from_slice(&level[idx + 1]);
425 next.push(sha256_bytes(&bytes));
426 }
427 idx += 2;
428 }
429 level = next;
430 }
431 level[0]
432}
433
434fn empty_data_block_merkle_root(params: RootAuthRevisionParams) -> [u8; 32] {
435 let mut bytes = Vec::new();
436 bytes.extend_from_slice(EMPTY_MERKLE_DOMAIN);
437 bytes.extend_from_slice(params.data_block_merkle_domain);
438 sha256_bytes(&bytes)
439}
440
441pub fn archive_root(inputs: ArchiveRootInputs) -> [u8; 32] {
442 archive_root_for_revision(inputs).expect("ArchiveRootInputs revision is supported")
443}
444
445pub fn archive_root_for_revision(inputs: ArchiveRootInputs) -> Result<[u8; 32], FormatError> {
446 let params = root_auth_revision_params(inputs.format_version, inputs.volume_format_rev)?;
447 let mut bytes = Vec::new();
448 bytes.extend_from_slice(params.archive_root_domain);
449 bytes.extend_from_slice(¶ms.root_auth_spec_id);
450 bytes.extend_from_slice(&inputs.archive_uuid);
451 bytes.extend_from_slice(&inputs.session_id);
452 push_u16(&mut bytes, inputs.format_version);
453 push_u16(&mut bytes, inputs.volume_format_rev);
454 push_u16(&mut bytes, inputs.compression_algo as u16);
455 push_u16(&mut bytes, inputs.aead_algo as u16);
456 push_u16(&mut bytes, inputs.fec_algo as u16);
457 push_u16(&mut bytes, inputs.kdf_algo as u16);
458 bytes.extend_from_slice(&inputs.critical_metadata_digest);
459 bytes.extend_from_slice(&inputs.index_digest);
460 bytes.extend_from_slice(&inputs.fec_layout_digest);
461 push_u64(&mut bytes, inputs.total_data_block_count);
462 bytes.extend_from_slice(&inputs.data_block_merkle_root);
463 bytes.extend_from_slice(&inputs.root_auth_descriptor_digest);
464 bytes.extend_from_slice(&inputs.signer_identity_digest);
465 Ok(sha256_bytes(&bytes))
466}
467
468fn len_prefixed_digest(domain: &[u8], payload: &[u8]) -> Result<[u8; 32], FormatError> {
469 let length = u32::try_from(payload.len())
470 .map_err(|_| FormatError::InvalidArchive("root-auth digest input length overflow"))?;
471 let mut bytes = Vec::new();
472 bytes.extend_from_slice(domain);
473 push_u32(&mut bytes, length);
474 bytes.extend_from_slice(payload);
475 Ok(sha256_bytes(&bytes))
476}
477
478fn sha256_bytes(bytes: &[u8]) -> [u8; 32] {
479 let digest = Sha256::digest(bytes);
480 let mut out = [0u8; 32];
481 out.copy_from_slice(&digest);
482 out
483}
484
485fn push_u16(bytes: &mut Vec<u8>, value: u16) {
486 bytes.extend_from_slice(&value.to_le_bytes());
487}
488
489fn push_u32(bytes: &mut Vec<u8>, value: u32) {
490 bytes.extend_from_slice(&value.to_le_bytes());
491}
492
493fn push_u64(bytes: &mut Vec<u8>, value: u64) {
494 bytes.extend_from_slice(&value.to_le_bytes());
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 fn sample_archive_inputs(
502 volume_format_rev: u16,
503 kdf_algo: KdfAlgo,
504 critical_metadata_digest: [u8; 32],
505 ) -> ArchiveRootInputs {
506 ArchiveRootInputs {
507 archive_uuid: [1; 16],
508 session_id: [2; 16],
509 format_version: FORMAT_VERSION,
510 volume_format_rev,
511 compression_algo: CompressionAlgo::None,
512 aead_algo: AeadAlgo::None,
513 fec_algo: FecAlgo::None,
514 kdf_algo,
515 critical_metadata_digest,
516 index_digest: [3; 32],
517 fec_layout_digest: [4; 32],
518 total_data_block_count: 2,
519 data_block_merkle_root: [5; 32],
520 root_auth_descriptor_digest: [6; 32],
521 signer_identity_digest: [7; 32],
522 }
523 }
524
525 fn sample_critical_inputs<'a>(
526 volume_format_rev: u16,
527 kdf_algo: KdfAlgo,
528 crypto_header_pre_hmac_bytes: &'a [u8],
529 root_auth_descriptor_digest: [u8; 32],
530 ) -> CriticalMetadataDigestInputs<'a> {
531 CriticalMetadataDigestInputs {
532 archive_uuid: [1; 16],
533 session_id: [2; 16],
534 format_version: FORMAT_VERSION,
535 volume_format_rev,
536 stripe_width: 1,
537 total_volumes: 1,
538 compression_algo: CompressionAlgo::None,
539 aead_algo: AeadAlgo::None,
540 fec_algo: FecAlgo::None,
541 kdf_algo,
542 crypto_header_pre_hmac_bytes,
543 chunk_size: 256 * 1024,
544 envelope_target_size: 1024 * 1024,
545 block_size: 64 * 1024,
546 fec_data_shards: 1,
547 fec_parity_shards: 0,
548 index_fec_data_shards: 1,
549 index_fec_parity_shards: 0,
550 index_root_fec_data_shards: 1,
551 index_root_fec_parity_shards: 0,
552 volume_loss_tolerance: 0,
553 bit_rot_buffer_pct: 0,
554 has_dictionary: 0,
555 manifest_footer_global_pre_hmac_bytes: b"manifest-footer-pre-hmac",
556 index_root_first_block: 0,
557 index_root_data_block_count: 1,
558 index_root_parity_block_count: 0,
559 index_root_encrypted_size: 4096,
560 index_root_decompressed_size: 256,
561 root_auth_descriptor_digest,
562 }
563 }
564
565 #[test]
566 fn root_auth_wrappers_use_current_revision_domains() {
567 let descriptor = root_auth_descriptor_digest(1, 1, b"identity", 64, 512).unwrap();
568 assert_eq!(
569 descriptor,
570 root_auth_descriptor_digest_for_revision(
571 FORMAT_VERSION,
572 VOLUME_FORMAT_REV,
573 1,
574 1,
575 b"identity",
576 64,
577 512,
578 )
579 .unwrap()
580 );
581
582 assert_eq!(
583 index_digest(b"index-root"),
584 index_digest_for_revision(FORMAT_VERSION, VOLUME_FORMAT_REV, b"index-root").unwrap()
585 );
586 assert_eq!(
587 data_block_merkle_root_from_leaf_hashes(&[[9; 32], [10; 32]]),
588 data_block_merkle_root_from_leaf_hashes_for_revision(
589 FORMAT_VERSION,
590 VOLUME_FORMAT_REV,
591 &[[9; 32], [10; 32]],
592 )
593 .unwrap()
594 );
595
596 let inputs = sample_archive_inputs(VOLUME_FORMAT_REV, KdfAlgo::None, [8; 32]);
597 assert_eq!(
598 archive_root(inputs),
599 archive_root_for_revision(inputs).unwrap()
600 );
601 }
602
603 #[test]
604 fn v44_no_key_archive_root_uses_v44_domains() {
605 let v43 = archive_root_for_revision(sample_archive_inputs(
606 VOLUME_FORMAT_REV_43,
607 KdfAlgo::None,
608 [8; 32],
609 ))
610 .unwrap();
611 let v44 = archive_root_for_revision(sample_archive_inputs(
612 VOLUME_FORMAT_REV_44,
613 KdfAlgo::None,
614 [8; 32],
615 ))
616 .unwrap();
617
618 assert_ne!(v43, v44);
619 }
620
621 #[test]
622 fn v44_recipient_wrap_archive_root_commits_keywrap_table_digest() {
623 let descriptor = root_auth_descriptor_digest_for_revision(
624 FORMAT_VERSION,
625 VOLUME_FORMAT_REV_44,
626 1,
627 1,
628 b"identity",
629 64,
630 512,
631 )
632 .unwrap();
633 let critical_a = critical_metadata_digest(sample_critical_inputs(
634 VOLUME_FORMAT_REV_44,
635 KdfAlgo::RecipientWrap,
636 b"crypto-header-pre-hmac keywrap-table-digest A",
637 descriptor,
638 ))
639 .unwrap();
640 let critical_b = critical_metadata_digest(sample_critical_inputs(
641 VOLUME_FORMAT_REV_44,
642 KdfAlgo::RecipientWrap,
643 b"crypto-header-pre-hmac keywrap-table-digest B",
644 descriptor,
645 ))
646 .unwrap();
647
648 assert_ne!(critical_a, critical_b);
649 assert_ne!(
650 archive_root_for_revision(sample_archive_inputs(
651 VOLUME_FORMAT_REV_44,
652 KdfAlgo::RecipientWrap,
653 critical_a,
654 ))
655 .unwrap(),
656 archive_root_for_revision(sample_archive_inputs(
657 VOLUME_FORMAT_REV_44,
658 KdfAlgo::RecipientWrap,
659 critical_b,
660 ))
661 .unwrap(),
662 );
663 }
664}