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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Serde shape of the `lys/verification-bundle/v1` container.
//!
//! # Invariants
//!
//! - **Field declaration order below IS the serialization order** and matches
//! the wire contract exactly, following the [`crate::tlog`] artifacts'
//! precedent. The shape freezes once bundles are emitted; a `v2` gets a new
//! `format` string, never field changes here.
//! - **`deny_unknown_fields`**: unknown fields in a v1 bundle are not valid v1.
//! There is no field smuggling into a frozen shape, and duplicate JSON keys
//! are rejected (serde-derive behaviour, pinned by test).
//! - **There is no signing path, at all.** Not by convention — by absence.
//! [`VerificationBundle`] has no signature field, no `sign` function exists
//! anywhere in this module, and nothing here holds a private key. A signature
//! over the container would invite verifiers to check the wrapper and skip
//! the contents, which is the exact failure mode where something reports
//! success having verified nothing.
//! - **The bundle carries no keys.** Not the log's, not any anchor's. Trust
//! inputs are the verifier's own, supplied at verification time — a container
//! that named the keys to trust would be asserting who is trustworthy, and
//! the bundle is packaging, never a trust statement.
//! - Embedded artifacts are carried **verbatim**: the inclusion proof as the
//! `lys/log-inclusion-proof/v1` structure it already is, and each receipt as
//! base64 of its exact `COSE_Sign1` bytes. Re-encoding a frozen artifact
//! inside a container is how byte-identity gets lost.
use Engine;
use STANDARD;
use ;
/// `format` value of every verification bundle. FROZEN.
pub const VERIFICATION_BUNDLE_FORMAT: &str = "lys/verification-bundle/v1";
/// A self-contained provenance chain: the artifacts a stranger needs to check
/// that a leaf was logged and that the log's checkpoint was notarized.
///
/// The bundle adds nothing cryptographic. Every security property comes from
/// the artifacts inside; the container's only job is to not lose or reorder
/// them, and to make the *relationships* between them checkable.
/// One rung of the notarization chain: an anchor's receipt, together with the
/// exact bytes that receipt proves.
///
/// Both halves are needed and neither is redundant. A receipt's payload is the
/// **detached** Merkle root, so verifying one requires the leaf bytes it proves
/// — and those bytes are a signed checkpoint, which is what makes the *next*
/// link's comparison possible. A bundle carrying receipts alone would be
/// unverifiable beyond its first link: an anchor's own checkpoint appears
/// nowhere in its receipts, and cannot be reconstructed from one (a receipt
/// yields a root and a size, but deliberately carries no origin and no
/// signature).