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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! Verification of `lys/verification-bundle/v1` containers.
//!
//! # The one interesting failure mode
//!
//! Every artifact in a bundle verifies standalone, so the container adds no
//! cryptography — and exactly one thing it must do is not obvious:
//! **check that the artifacts are about each other.** A pile of individually
//! valid receipts proves nothing about their relationship, and a notarization
//! that is never tied to the inclusion proof is two unrelated true facts
//! presented as one chain. Skipping the link checks yields a verifier that
//! reports success having established far less than a reader would assume,
//! which is the failure this crate exists to prevent.
//!
//! So the chain is checked as follows, and none of it is optional:
//!
//! 1. `links[0].checkpoint` must be **byte-identical** to
//! `inclusion_proof.checkpoint`. This is the join: it makes the first
//! notarization about *the log the leaf is in*, rather than about some other
//! log whose checkpoint also happens to verify.
//! 2. Each `links[i]`'s receipt must verify against `links[i].checkpoint` as its
//! proven leaf, under the anchor key the **caller** supplied for that link.
//! 3. Where a next link exists, `links[i+1].checkpoint` must be anchor `i`'s own
//! signed checkpoint, and its root and tree size must equal the root
//! receipt `i` reconstructs and the size it claims.
//!
//! Step 3 is the rung of the ladder. It forces two independently signed
//! statements to agree: the anchor's *receipt* signature over a reconstructed
//! root, and the anchor's *note* signature over its own published checkpoint.
//! It is also what makes the receipt format's documented `tree_size`
//! malleability harmless here — a relabelled size no longer matches a
//! note-signed one.
//!
//! # Trust comes from the caller, never from the bundle
//!
//! [`verify_bundle`] takes the log's verifier key and one anchor key per link.
//! The bundle supplies none of them. A container that named the keys to trust
//! would be asserting who is trustworthy, and packaging must never become a
//! trust statement.
//!
//! One anchor key serves both roles per link: its 32 key bytes verify that
//! anchor's *receipts*, and the same key verifies that anchor's *checkpoints*.
//! That is a binding worth enforcing rather than a convenience — an anchor
//! signing receipts with a key that never appears in its published checkpoints
//! could not be cross-checked against its own log by anyone.
//!
//! # Non-oracle
//!
//! Every failure collapses to
//! [`TrustError::BundleVerification`].
//! A malformed container, a broken inclusion proof, a receipt from the wrong
//! anchor and a chain whose links do not join are deliberately
//! indistinguishable: a bundle verifier is a network-exposed surface where a
//! distinguishable error becomes a parsing oracle. Callers diagnosing their own
//! bundle verify the embedded artifacts individually, where the errors are
//! actionable.
//!
//! [`TrustError::BundleVerification`]: crate::error::TrustError::BundleVerification
use Engine;
use STANDARD;
use crate;
use crate;
use crate;
use crateverify_receipt_bytes;
use crateverify_inclusion_artifact;
/// Non-oracle failure for every rejected bundle (see module docs).
/// What one verified rung of the chain establishes.
/// Evidence that a bundle verified: the leaf, the log checkpoint it was proven
/// against, and one [`Notarization`] per chain link, in order.
///
/// Fields are private and the only constructor is [`verify_bundle`], so
/// **holding a `VerifiedBundle` is itself the evidence that every check passed**
/// — following the `VerifiedRequest` precedent. A value of this type cannot be
/// assembled by a caller who skipped a step.
/// Verify a bundle end to end.
///
/// `log_verifier` is the verifier key for the log the leaf was logged in.
/// `anchors` supplies one key per chain link, in the same order as
/// `bundle.links` — each verifying both that link's receipt and (where a
/// further link follows) that anchor's own checkpoint.
///
/// # Errors
///
/// Returns [`TrustError::BundleVerification`] for every failure
/// (non-oracle; see the module docs).
///
/// Note that `anchors.len()` must equal `bundle.links.len()` exactly, and a
/// mismatch is a verification failure rather than an argument error. It is a
/// substantive refusal: a bundle claiming more notarization than the verifier is
/// prepared to check cannot be checked, and silently verifying a prefix would
/// report success for less than the bundle asserts.