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
//! libFuzzer entry-point module for `chio-kernel-core` receipt-log replay.
//!
//! Gated behind the `fuzz` Cargo feature so it only compiles into the
//! standalone `chio-fuzz` workspace at `../../fuzz`. The production build of
//! `chio-kernel-core` never pulls in `arbitrary`, never exposes these symbols,
//! and never gets recompiled with libFuzzer instrumentation. The `fuzz`
//! feature also implies `std` because libFuzzer itself only runs in a hosted
//! environment, so the portable `no_std + alloc` host + wasm proof in
//! `scripts/check-portable-kernel.sh` never observes this surface.
//!
//! # Why this target exists
//!
//! Catches reordered / corrupted-chain bugs that the merkle_checkpoint target
//! cannot. The merkle root only commits to the set of leaf hashes; it cannot
//! detect a re-encoded receipt whose canonical-JSON bytes still hash to the
//! same leaf, nor a receipt log whose timestamps move backwards while every
//! individual signature still verifies. The replay-time verifier is the
//! canonical "decode-then-verify-then-check-chain-invariants" surface: tee
//! streams produce NDJSON receipt logs (one canonical-JSON `ChioReceipt` per
//! line) that the gate re-reads and validates, and any panic on that re-read
//! path would crash the gate every time the malformed bytes are seen.
//!
//! # Chosen entry point
//!
//! The receipt-log decode-then-verify pipeline is structured around
//! [`chio_core_types::receipt::body::ChioReceipt`]:
//!
//! 1. Split the input bytes on `\n` into NDJSON lines (matching the
//! `tests/replay/src/golden_writer.rs` newline-terminated framing).
//! 2. For each non-empty line, attempt `serde_json::from_slice::<ChioReceipt>`.
//! Errors are silently consumed (fail-closed: malformed lines are not part of the chain).
//! 3. For each successfully decoded receipt, call
//! [`ChioReceipt::verify_signature`]. The result is intentionally
//! discarded: the trust-boundary contract guarantees the only outcomes
//! are `Ok(true)` (signature verified), `Ok(false)` (signature mismatch),
//! `Err(_)` (decode error inside the canonical-JSON pipeline), or a panic
//! that libFuzzer surfaces as a crash.
//! 4. Cross-receipt chain invariants are then checked over the verified
//! sequence: timestamp monotonicity (non-decreasing) and `kernel_key`
//! consistency (every receipt in a single log shares one signer). These
//! catch the reorder and chain-tamper class.
//!
//! As a fall-back interpretation, the entire input is also fed once through
//! `serde_json::from_slice::<ChioReceipt>` so libFuzzer can reach the
//! single-receipt decode path with arbitrary bytes that do not contain a
//! newline. Every iteration drives both parsers from the same byte stream.
//!
//! No setup state is required: the verifier operates purely over its input
//! arguments (no clocks, no key registries, no stores).
use Vec;
use ChioReceipt;
use PublicKey;
/// Drive arbitrary bytes through the `chio-kernel-core` receipt-log replay
/// trust boundary.
///
/// Bytes are interpreted in two independent ways and each interpretation is
/// forwarded to the corresponding verifier:
///
/// 1. As an NDJSON receipt log (one canonical-JSON [`ChioReceipt`] per line,
/// `\n`-terminated, matching `tests/replay/src/golden_writer.rs`). Each
/// line is decoded; for each successfully decoded receipt
/// [`ChioReceipt::verify_signature`] is called and the result discarded.
/// Cross-receipt chain invariants (timestamp monotonicity,
/// `kernel_key` consistency) are then checked over the verified sequence.
/// 2. As a single canonical-JSON-encoded [`ChioReceipt`] (no NDJSON
/// framing). Decoded once per iteration so libFuzzer reaches the
/// single-receipt parse path with inputs that do not contain a newline.
///
/// Errors at every step are silently consumed: the trust-boundary contract
/// guarantees the only outcomes are `Err(_)`, `Ok(true)`, `Ok(false)`, or a
/// panic / abort (which libFuzzer surfaces as a crash). The goal is
/// exercising the parse plus signature-verify plus chain-invariant paths,
/// not asserting any post-condition on the success branch.
/// Interpret `data` as a `\n`-terminated NDJSON receipt log: decode each
/// line, verify every successfully decoded receipt, then check cross-receipt
/// chain invariants over the verified sequence.
/// Interpret `data` as a single canonical-JSON-encoded receipt: decode it,
/// then verify the signature. Reaches the single-receipt parse path even
/// when `data` contains no newline.
/// Check cross-receipt chain invariants over a sequence of decoded
/// receipts. Each invariant exercise is intentionally non-asserting: any
/// panic in the comparison primitives ([`PublicKey`] equality, `u64`
/// comparison) would surface through libFuzzer as a crash. The function's
/// purpose is to traverse the chain in order, not to enforce policy.
///
/// Invariants exercised:
///
/// - Timestamp monotonicity: successive `timestamp` values are read and
/// compared. Reordered or rewound receipts are visited but not rejected
/// (libFuzzer is the rejecter, via crashes). The comparison itself is the
/// load-bearing surface this target hardens.
/// - `kernel_key` consistency: successive `kernel_key` values are compared
/// against the first receipt's signer. Mixed-signer logs are visited but
/// not rejected here.