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
//! [`AnchorReceipt`] — the parsed form of the `lys/anchor-receipt/v1` tagged
//! `COSE_Sign1` artifact.
//!
//! # Invariants
//!
//! - **The only durable form is the COSE bytes.** The struct deliberately
//! implements no `serde` traits: nothing can persist a receipt except
//! [`AnchorReceipt::to_cose_bytes`], so no second wire shape can ever exist
//! alongside the frozen COSE artifact.
//! - **Round-trip identity:** `from_cose_bytes(x.to_cose_bytes()) == x` for
//! every `AnchorReceipt` value, and `to_cose_bytes` of a parsed receipt
//! reproduces the input bytes exactly.
//! - **The root is not a field.** The signed value is the anchor's Merkle
//! root, carried as a *detached* payload and therefore absent from the
//! artifact. Storing it on the struct would break round-trip identity
//! (`from_cose_bytes` could not populate it) and, worse, would invite
//! callers to read a root instead of recomputing one. It is derived by
//! [`AnchorReceipt::reconstructed_root`].
//! - **Canonical-encoding strictness:** [`AnchorReceipt::from_cose_bytes`]
//! rejects any input that is not byte-identical to the canonical re-encoding
//! of its parsed fields — including inputs whose signature is
//! cryptographically valid (indefinite lengths, oversized integer heads,
//! reordered maps, extra unprotected entries, tag stripping, duplicate keys,
//! trailing garbage).
//! - Parsing performs no signature verification and no proof check; pair
//! `from_cose_bytes` with [`super::sign::verify_receipt`] (or use
//! [`super::sign::verify_receipt_bytes`]) before trusting any field.
//! - Every parse failure collapses to
//! [`TrustError::ReceiptVerification`]
//! (non-oracle).
use crate;
use crate;
use crate;
/// An anchor's Ed25519-signed inclusion receipt, carried on the wire as a
/// tagged `COSE_Sign1` (RFC 9052) with an RFC 9942 verifiable data proof — the
/// `lys/anchor-receipt/v1` artifact.
///
/// The receipt asserts: *"the leaf that hashes to this path's base was
/// included at index `leaf_index` in my tree of size `tree_size`, whose root I
/// vouch for."*
///
/// Constructed by [`super::sign::sign_receipt`] or parsed from artifact bytes
/// by [`Self::from_cose_bytes`]; consumed by [`super::sign::verify_receipt`].
///
/// # Which fields are signature-covered, and why the rest are still safe
///
/// Only `anchor_public_key` rides inside the signed bytes, in the protected
/// `kid` header — inheriting the fix that took attestations from v1 to v2,
/// rather than rediscovering it. The proof fields (`tree_size`, `leaf_index`,
/// `inclusion_path`) sit in the *unprotected* header, which reads as
/// "unauthenticated" and is not: they are inputs to the root reconstruction
/// whose output the signature is checked against, so altering any of them
/// yields a root the anchor never signed. They are authenticated **by
/// consequence rather than by coverage**. See the [`super`] module docs.