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
//! **A new box's material** (yog's `docs/REMOTE.md` §8.4) — the six fields the
//! `enroll` act answers, the rendezvous pair beside them when the engine holds
//! one, and the one envelope a camera carries them in.
//!
//! # The reply this seat must not keep
//!
//! Every other kind in this vocabulary is something the window draws and may
//! redraw. This one carries a **private key that is not this box's** — minted
//! on the engine's CA for a device that does not exist yet — and the seat's
//! whole job with it is to put it on a screen and forget it. Nothing here
//! writes, caches or logs; [`Enrolled`] is a value the window holds while a
//! symbol is on screen and drops with the pane. See DESIGN §3.
//!
//! # The envelope is a re-saying, not a second field list
//!
//! [`Enrolled::envelope`] is here rather than beside the QR encoder for one
//! reason: it names the same six fields the reader above it names, and two
//! lists of one field set drift. REMOTE §8.4 is the authority for its shape and
//! this module implements it:
//!
//! - **compact JSON**, no whitespace;
//! - the six fields **verbatim**, PEM as minted — DER-plus-base64 was measured,
//! buys about a tenth of the bytes and costs the property worth keeping,
//! which is a field an operator can paste into `openssl x509 -text`;
//! - under `"yog-enroll": 1`, the marker a scanner recognises it by and the
//! version it will be told about if the fields ever move;
//! - the **rendezvous pair** (`rendezvous_pub`, `pairing_salt`; edition 20,
//! yog bl-9043) when the reply carried it, and neither key when it did not
//! — the phone reads its roving material (REMOTE §13.3) from exactly this
//! envelope, so a seat that re-said only the six drew a symbol whose device
//! could never find a moved engine (bl-5378);
//! - and **without `ok` and `kind`**, which say what a *wire answer* is. A
//! photograph is not one.
//!
//! Key order is `serde_json`'s own, which is sorted, and that is the same order
//! the engine's encoder writes — one less thing for two ends to disagree about.
//! It is not semantic either way: a scanner parses the object.
use ;
use fields;
/// The reply kind this module reads.
pub const KIND: &str = "enrolled";
/// The marker a scanner recognises the envelope by, and the version of the
/// field set under it.
const MARKER: &str = "yog-enroll";
const VERSION: u64 = 1;
/// The six fields, spelled once. The reader and the envelope both read this
/// list, which is what makes it one list.
const GRADE: &str = "grade";
const NAME: &str = "name";
const ADDRESS: &str = "address";
const CA: &str = "ca";
const CERT: &str = "cert";
const KEY: &str = "key";
/// The rendezvous pair's two keys — the files' own names (`rendezvous.pub`,
/// `pairing.salt`) with the dot a JSON key would not want.
const PUBLIC: &str = "rendezvous_pub";
const SALT: &str = "pairing_salt";
/// **What a new box needs to dial this engine, and nothing more.**
///
/// `address` is the engine's own wire address *as clients dial it*, not the
/// port a `:0` request became — REMOTE §8.4 makes the engine refuse the second,
/// because a symbol carrying a runtime port would be stale before it was
/// scanned. So a seat has nothing to check here: an address that arrived is an
/// address the engine already stood behind.
/// **The engine's rendezvous public key and the pairing salt**, 32 bytes of
/// lowercase hex each, exactly as the files `rendezvous.pub` and
/// `pairing.salt` hold them. One type because they travel together: half a
/// pairing derives nothing a device could use.
/// Read the material. **Rung 1 throughout**: every field is required and every
/// refusal names the field, because a half-read enrollment is a symbol that
/// scans into a box that cannot dial. The rendezvous pair is optional as a
/// PAIR — both or neither — and one without the other refuses naming both.
pub
/// The pair, both or neither.