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
//! A WASI host shim for the leaf node.
//!
//! The leaf is written for firmware: it reads no clock, draws no entropy on
//! its own account, and owns no radio. Those are the host's, and on a chip the
//! host is the firmware around the crate. This example is the same crate with
//! a WebAssembly runtime as the host. The runtime hands in the time and the
//! frames, and takes back the frames the device owes, over WASI's standard
//! streams. Nothing in it is WASI-specific, so the same source also builds and
//! runs natively, which is how its output was recorded.
//!
//! # Build and run
//!
//! ```text
//! rustup target add wasm32-wasip1
//! cargo build -p offline-protocol-leaf --example wasi_host_shim --target wasm32-wasip1 --release
//! wasmtime run target/wasm32-wasip1/release/examples/wasi_host_shim.wasm \
//! com.example.lock off1qyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyr4s29s 1787314332 < frames.jsonl
//! ```
//!
//! Natively: `cargo run -p offline-protocol-leaf --example wasi_host_shim --
//! com.example.lock <peer address> <now_unix_secs> < frames.jsonl`.
//!
//! The arguments are the application id the device is provisioned under, the
//! address of the phone it is pairing with, and the current time in seconds
//! since the epoch. Standard input carries frames from that phone, one JSON
//! message per line, and may be empty.
//!
//! # What it does
//!
//! 1. Opens a device in a [`MemoryStore`], provisioning an identity on the
//! first run, and prints its address to standard error.
//! 2. Mints the key package frame for the peer at the supplied time and prints
//! it to standard output, one JSON frame per line. This is the frame a
//! device advertises to pair.
//! 3. Hands every frame on standard input to [`LeafDevice::handle`] at the
//! same time, prints the frames it owes in reply to standard output and the
//! events it raised to standard error. A frame the device refuses is
//! reported and skipped, as a device keeps running when a peer sends
//! something it does not accept. A line that is not a frame at all ends
//! the run: that is the host's stream being wrong, not the peer's.
//! 4. At the end of input, seals one message to the peer. When no Welcome from
//! the phone was among the frames, the crate refuses with `NoSession`, and
//! the shim prints that refusal. A sealed frame exists only for a session
//! that exists, and this example does not fake a peer to produce one.
//!
//! # What this deliberately does not show
//!
//! - **A pairing completing.** That takes a phone: the phone creates the group
//! from the key package and issues the Welcome, and this crate never commits
//! (ADR 0021). The two ends meet in the crate's `phone_interop` test and in
//! `tools/mls-interop`.
//! - **Durable storage.** [`MemoryStore`] forgets everything at exit. A host
//! implements [`offline_protocol_leaf::LeafStore`] over storage that is
//! atomic per entry, because a ratchet state rolled back by a crash reuses
//! an AEAD nonce, and the crate persists before it emits precisely so that
//! a durable store makes that impossible.
//! - **A clock.** WASI has one (`clock_time_get`), and the leaf never reads
//! it. The time is an argument here because it is the host's obligation on
//! every target: a device that lets the MLS library find a clock it does not
//! have stamps 1970 and has its key package refused as expired.
//! - **Authorization.** A session proves which address a peer is, never that
//! the owner meant them. Deciding when to accept a pairing and what a peer
//! may actuate is the host's, on a runtime as on a chip.
//!
//! # Entropy
//!
//! On WASI, `getrandom` reads the runtime's `random_get`. The device's
//! identity and every MLS key are exactly as strong as what that call returns,
//! and nothing in the crate can tell a strong source from a weak one. A
//! runtime configured to seed `random_get` deterministically, as some do for
//! reproducible runs, produces a predictable identity. The provisioning
//! specification's obligation that entropy is real applies to the runtime
//! here as it applies to a measurement stub on a bench: neither may reach a
//! deployment, and the runtime's source is what has to be audited first.
//!
//! # Why WASI, and not `wasm32-unknown-unknown`
//!
//! On the browser target the pinned MLS library enables `getrandom`'s `js`
//! feature, and `getrandom` 0.2 selects `js` ahead of `custom`. A host that
//! registered its own entropy backend there would supply a symbol nothing
//! calls, and the module would import browser glue instead. On WASI the
//! backend is chosen by the target, ahead of both features, so the host's
//! entropy is what the library reads. That is the claim CI pins, and the only
//! WebAssembly claim this crate makes.
//!
//! This example needs the crate's `std` feature for its standard streams.
//! The crate's `no_std` half is built for the same target by CI, separately,
//! because a host may prefer to drive it through its own exports rather than
//! through a process.
use ;
use ExitCode;
use Message;
use ;
const USAGE: &str = "usage: wasi_host_shim <app_id> <peer_address> <now_unix_secs> < frames.jsonl";
/// What the device says to the peer once a session exists.
const GREETING: &str = "hello from the leaf";
/// Writes one frame as one line of JSON.