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
202
203
204
//! **The foot grade, read off this box's own certificate** (REMOTE §4.2,
//! DESIGN §3.2).
//!
//! REMOTE §4.2 gives the grade one home — *"the subject's organizational unit,
//! read by the walk that already reads the common name: `CN=<client>, OU=foot`
//! is a foot"* — and one issuer: the operator's own CA, out of channel, on the
//! same act §1.4 already requires. **Enforcement is the server's**, because the
//! server is the party that can be trusted to enforce it. thrall's obligation
//! is the half it can keep: carry a leaf of that grade, and refuse to be
//! configured with anything else.
//!
//! **So this end fails closed where the engine fails open, and the asymmetry is
//! the design.** yog reads a subject with no `OU=foot` as operator grade —
//! default-operator, so a certificate minted before the grade existed keeps
//! working. thrall refuses that same certificate: an operator-grade leaf on a
//! foot is a machine holding the whole boundary in order to run commands, which
//! is the thing being a foot exists to give up. Bytes that are not a
//! certificate at all refuse for the same reason. Neither end is defaulting;
//! they are answering different questions.
//!
//! **thrall links no certificate library** (`Cargo.toml`'s approved set), so
//! this is a DER walk — structural ASN.1 rather than a byte search, and the
//! structure is the point: the **issuer** carries a common name too, and it
//! comes FIRST, so a scan for the common-name object identifier would answer
//! the operator CA's name for every leaf on the box.
//!
//! What it reads, per RFC 5280:
//!
//! ```text
//! Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signature }
//! TBSCertificate ::= SEQUENCE { [0] version OPTIONAL, serialNumber INTEGER,
//! signature, issuer, validity, subject, … }
//! Name ::= SEQUENCE OF SET OF SEQUENCE { type OID, value ANY }
//! ```
//!
//! The optional `[0] version` is why `subject` is located **relative to the
//! serial number** rather than at a fixed index: the serial is the first field
//! certainly present, and `subject` is four constructed values past it. A
//! version-1 certificate and a version-3 one then take one path, not two.
use Path;
use CertificateDer;
use PemObject;
/// DER tags this walk names.
const INTEGER: u8 = 0x02;
const OID: u8 = 0x06;
/// `id-at-commonName` — ASN.1 `{joint-iso-itu-t(2) ds(5) attributeType(4)
/// commonName(3)}`, in its DER encoding. Spelled as bytes rather than as the
/// dotted arc string because the dotted form of four small arcs is
/// indistinguishable from an IPv4 address, to a reader and to `make leak-scan`.
const COMMON_NAME: = ;
/// `id-at-organizationalUnitName` — the same arc one attribute over, and the
/// home REMOTE §4.2 gives the grade.
const ORG_UNIT: = ;
/// The organizational unit that says foot. One word, written by the operator's
/// CA or not at all.
const FOOT: &str = "foot";
/// How many constructed fields separate `serialNumber` from `subject`:
/// signature, issuer, validity, subject.
const SERIAL_TO_SUBJECT: usize = 4;
/// **This box's identity on this channel, if it is entitled to be a foot.**
///
/// Answers the leaf's subject common name — which *is* the client identity the
/// engine reads back off the presented certificate (REMOTE §2), so a foot that
/// knows its own name learned it from the same bytes the engine will. Refuses
/// anything else, naming what it read.
/// The first certificate in a PEM chain, as DER. The leaf comes first by
/// convention and by every mint's own order; what follows it is the chain to
/// the anchor, and none of it is this box's identity.
///
/// **The refusal says what the bytes should have been** (bl-52ba). This is what
/// a mis-copied file looks like, and it is the one refusal in this file that
/// used to say only what the PEM reader said — *"no items found"* — next to a
/// sibling one line away that names the grade, the section that fixes it and
/// the act that mints one. A file that locates a problem and stops there leaves
/// the operator exactly where they started.
/// The subject common name of a DER-encoded certificate, or `None` when the
/// bytes are not a certificate or carry no readable one.
///
/// The **last** common name wins. A distinguished name is written most-general
/// first in DER and most-specific last (RFC 4514 renders it reversed), so the
/// final one is the leaf's own.
/// Whether the same subject says foot.
/// The `Name` bytes of the certificate's **subject** — located relative to the
/// serial number, for the reason the module doc gives.
/// Every value of attribute `oid` in a `Name`, in DER order, decoded as UTF-8.
/// Every string type these attributes are minted in — `UTF8String`,
/// `PrintableString`, `IA5String` — is UTF-8 or a subset of it, and one that is
/// not (`BMPString` is UTF-16) fails the decode and is skipped rather than
/// mis-read.
/// One DER type-length-value off the front of `bytes`: its tag, its contents,
/// and what follows it. `None` for a truncated header, a truncated value, or a
/// length DER does not permit — the indefinite form (`0x80`), which BER allows
/// and DER forbids, and a length wider than this walk will serve.
/// Every element of a constructed value, in order. A trailing byte run that is
/// not a whole TLV ends the walk — a malformed tail yields the elements read
/// before it, which is what makes every read above total.