1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026-present, Structured World Foundation
//! Identity context threaded through the Block I/O API.
//!
//! Every call to [`crate::table::block::Block::write_into`] /
//! [`crate::table::block::Block::from_reader`] /
//! [`crate::table::block::Block::from_file`] carries a
//! `BlockIdentity` describing which
//! block, of which table, with which compression context. The
//! Block layer uses this to construct AAD (Additional
//! Authenticated Data) for AEAD encryption — see the AAD-bound
//! wire format spec for the cryptographic role each field plays.
//!
//! **Why a context struct vs. inline arguments.** The natural
//! alternative — pass `aad: &[u8]` directly to the Block API — got
//! a previous attempt into trouble: callers wrote `aad: &[]`
//! everywhere because composing the right AAD bytes at each call
//! site is fiddly and the type system couldn't enforce
//! correctness. With `BlockIdentity`, every call site contributes
//! its OWN local context (the writer/scanner/reader already knows
//! its table id, codec context, etc.) and the Block layer
//! computes AAD once, internally. Adding a new AAD-relevant field
//! later means adding it to `BlockIdentity` rather than chasing
//! down 90+ call sites.
//!
//! **Field requirements.** Production call sites SHOULD populate
//! every field with the real value from their local context. Test
//! call sites that don't exercise AAD-sensitive paths may use
//! [`BlockIdentity::for_test`] which defaults `dict_id` and
//! `window_log` to zero.
//!
//! **Allowed zero exceptions in production code** (each individually
//! documented at the call site):
//!
//! - `table_id = 0` is allowed when reading a META block that
//! itself CARRIES the `table_id` field — there's no way to
//! know the id before the block is parsed (chicken-and-egg).
//! Cross-store substitution is still prevented because the
//! meta payload's own id field is part of the verified body.
//!
//! **Neither block position nor tree id is part of the identity.**
//! AAD binds `table_id` plus the codec context, but never a per-block
//! byte offset nor the owning tree id. Offset-independent AAD lets a
//! writer encrypt every block of a table in parallel (the on-disk
//! offset isn't known until placement). The tree id is a
//! process-ephemeral counter, not durable across reopen, so binding it
//! would fail AEAD verify after a restart; cross-tree substitution is
//! instead prevented by per-tree key isolation (a tree's blocks decrypt
//! only under its own key). The cost of dropping the offset is that two
//! blocks of the SAME table are interchangeable at the AEAD layer;
//! block-position integrity is supplied one layer up by the
//! authenticated index (key-range -> offset) plus the structural file
//! layout, not by per-block AEAD.
use crateBlockType;
/// Identifies a block for encryption AAD and audit purposes.
///
/// Carried through the Block I/O API instead of separate
/// `block_type` / `aad: &[u8]` arguments — see the module
/// docstring for the rationale.