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
//! Newer-root derivation from a consistency path — RFC 6962 §2.1.4.2.
//!
//! [`verify_consistency`](super::verify_consistency) answers "are these two
//! roots I already hold consistent?". This module answers the question a
//! consistency-receipt verifier must ask instead: **"given the root I held
//! before, what root does this proof say the log has grown into?"**
//!
//! The difference is not cosmetic. A verifier that must supply both roots learns
//! nothing it did not already know — it can only confirm a pair it holds in
//! full. Deriving the newer root is what lets a receipt *tell* a verifier where
//! the log got to, which is the case that matters and the reason RFC 9942
//! carries the newer root as a **detached** payload. Supplying both roots would
//! make that detachment pointless.
//!
//! # What authenticates what
//!
//! Nothing here is trusted. The derived root is an input to a signature check,
//! never a verdict — identical in shape to
//! [`root_from_inclusion_path`](super::root_from_inclusion_path). Two things
//! carry the weight:
//!
//! - **The old root is the caller's.** It is a required argument and no entry
//! point omits it. RFC 9942 is silent on where a verifier obtains it, and if
//! the verifier took it from the artifact then the anchor would be choosing
//! *both* endpoints: "consistent with an earlier version of my log" would
//! become "consistent with whichever earlier version I nominate today", which
//! is the equivocation a consistency proof exists to detect, walking back in
//! through the front door.
//! - **The old root is checked against the path, not assumed.** RFC 6962's
//! algorithm reconstructs the older root as `fr` while it reconstructs the
//! newer one as `sr`, from the same nodes. This function requires `fr` to
//! equal the supplied old root. A path that does not descend from the
//! caller's own earlier view is refused here rather than producing a newer
//! root that a signature would then appear to endorse.
//!
//! # Equal sizes are refused, and that reverses an earlier ruling
//!
//! An equal-size consistency proof has an empty path and states something true:
//! *these two views are the same log.* The wire draft first ruled that issuance
//! would refuse it while verification kept accepting it, by analogy with the
//! one-leaf inclusion path (`[+ bstr]` cannot express an empty path either).
//!
//! **The analogy is wrong, and writing this function is what showed it.** For a
//! one-leaf tree the empty path still leaves a derivation standing: the root is
//! computed from the *leaf hash*, which the verifier supplies and which the
//! receipt's statement is about, so tampering still changes the result. For
//! equal sizes there is no derivation left at all — the "derived" newer root is
//! the caller's `old_root` argument, unchanged and untouched.
//!
//! That degenerates the signature check into *"has this anchor ever signed this
//! 32-byte value?"*, with the value entirely attacker-chosen. Every other input
//! shape is authenticated by consequence: alter anything and the derived root
//! stops matching what was signed. Here there is no consequence to alter, so
//! the only thing standing between an unrelated 32-byte signature and an
//! accepted consistency receipt is the content type in the protected header.
//! Domain separation should be the second line of that defence, not the only
//! one.
//!
//! Nothing is lost by refusing: a verifier that already holds `old_root` learns
//! precisely nothing from being told the log is still that size. **"Accept
//! anything true" is the right rule while the true statement carries
//! information; a vacuous statement whose acceptance degenerates a signature
//! check is not the case it was written for.**
use ;
use crate;
/// RFC 6962 domain-separation prefix for interior nodes.
const NODE_PREFIX: u8 = 0x01;
/// Length of a SHA-256 digest, and so of every node in a consistency path.
const DIGEST_LEN: usize = 32;
/// Derives the newer Merkle Tree Hash a consistency path implies.
///
/// Runs RFC 6962 §2.1.4.2's verification algorithm over `path`, reconstructing
/// both the older root (`fr`) and the newer one (`sr`). The older
/// reconstruction is required to equal `old_root`; the newer one is returned.
///
/// `old_root` is the root the caller *itself* previously held for the log at
/// `size_1` — never a value read out of the artifact being checked. See the
/// module docs.
///
/// The returned root is **not** a verification result. It is a candidate value
/// to be checked against something independently trustworthy, which for a
/// consistency receipt means the anchor's signature over it.
///
/// # Errors
///
/// Returns [`TrustError::MerkleTree`] if `size_1` is zero, if `size_1` is not
/// strictly below `size_2` (equal sizes are refused — see the module docs), if
/// `path` is not a whole number of 32-byte digests, if `path`'s length
/// disagrees with the one `(size_1, size_2)` requires in either direction, or
/// if the older root the path reconstructs is not `old_root`.
/// Hashes an interior node as RFC 6962's `SHA-256(0x01 ‖ left ‖ right)`.