Commit 4ee90c8
fix(provenance): domain-separated, length-prefixed hash over all fields (#88)
Closes #27, closes #28, closes #29, closes #30.
`ProvenanceEntry::compute_hash` was hashing
`previous_hash + entity_id + operation + timestamp` as raw byte
concatenation:
1. Three fields — `actor`, `before_snapshot`, `transformation` —
were *not* in the preimage. Tampering with any of them left
`verify()` returning `true`. The integration test
`test_provenance_chain_integrity_multi_step` *codified the hole*
(#30 / V-L2-C4): "Actor is not part of hash — tamper to actor
alone is invisible".
2. No domain separation. Hash bytes were concatenated without
length prefixes or a domain tag, so a future field reorder /
addition / removal could silently produce a colliding digest
for distinct inputs. No version tag either, so a migration to a
different hash algorithm had no way to mark old vs new entries.
3. Timestamp encoded as `to_rfc3339()`. Two valid RFC3339 strings
for the same instant (sub-second precision, `Z` vs `+00:00`,
etc.) produce two different hashes.
This commit lands all four V-L2-C* fixes together because they
necessarily ship as one change:
- **#27 / V-L2-C1** — new `compute_hash` signature accepts all
seven preimage fields and prepends `b"verisim-prov-v1\0"` as
the domain tag. Variable-length fields are length-prefixed with
`u64_le(len)` for canonical encoding.
- **#28 / V-L2-C2** — timestamp encoded as
`i64_le(secs) || u32_le(nanos)` via `chrono::DateTime::timestamp()`
+ `.timestamp_subsec_nanos()`. Different RFC3339 strings for the
same instant now produce the same hash. RFC3339 is kept for
display surface (JSON, status output) but is no longer part of
the hash preimage.
- **#29 / V-L2-C3** — five new tests in `abi::tests`:
`test_provenance_tamper_actor`,
`test_provenance_tamper_before_snapshot`,
`test_provenance_tamper_transformation`,
`test_provenance_timestamp_canonical_encoding`,
`test_provenance_mutation_matrix_breaks_verification`
(4-entry chain × 7 fields × every entry, asserts every single
mutation breaks `verify()`).
- **#30 / V-L2-C4** — the wontfix assertion in
`tests/integration_test.rs` is flipped to assert
`!tampered.verify()` with the updated comment.
API note: `compute_hash` signature changed (4 args → 7 args). The
function is `pub`, so this is a breaking change. It's a security
fix and the previous signature was wrong by construction; the
callers inside the crate are updated. No external callers known.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 0436bfe commit 4ee90c8
2 files changed
Lines changed: 170 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
164 | 170 | | |
165 | | - | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
166 | 175 | | |
167 | | - | |
168 | | - | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
169 | 194 | | |
170 | 195 | | |
171 | 196 | | |
172 | 197 | | |
173 | | - | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
174 | 202 | | |
175 | 203 | | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
180 | 213 | | |
181 | 214 | | |
182 | 215 | | |
183 | 216 | | |
184 | 217 | | |
185 | | - | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
186 | 222 | | |
187 | 223 | | |
188 | 224 | | |
189 | 225 | | |
190 | 226 | | |
191 | | - | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
192 | 231 | | |
193 | 232 | | |
194 | 233 | | |
195 | 234 | | |
196 | 235 | | |
197 | 236 | | |
198 | 237 | | |
199 | | - | |
| 238 | + | |
200 | 239 | | |
201 | 240 | | |
202 | 241 | | |
| |||
216 | 255 | | |
217 | 256 | | |
218 | 257 | | |
219 | | - | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
220 | 262 | | |
221 | 263 | | |
222 | 264 | | |
| |||
231 | 273 | | |
232 | 274 | | |
233 | 275 | | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
234 | 284 | | |
235 | 285 | | |
236 | 286 | | |
| |||
498 | 548 | | |
499 | 549 | | |
500 | 550 | | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
501 | 654 | | |
502 | 655 | | |
503 | 656 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
271 | 271 | | |
272 | 272 | | |
273 | 273 | | |
274 | | - | |
| 274 | + | |
| 275 | + | |
275 | 276 | | |
276 | 277 | | |
277 | 278 | | |
278 | | - | |
279 | | - | |
| 279 | + | |
| 280 | + | |
280 | 281 | | |
281 | | - | |
| 282 | + | |
282 | 283 | | |
283 | 284 | | |
284 | 285 | | |
| |||
0 commit comments