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
//! **A certificate leaf name** (REMOTE §2, bl-8bbc): the client identity read
//! off the certificate the peer presented, and nothing else.
//!
//! §2 is exact about what a client is — *"One certificate = one client identity
//! (its leaf name)"* — so the identity is the leaf's subject **common name**,
//! which is what `make wire-certs` mints (`/CN=yog-client`) and what an
//! operator types when they seat a registration by hand. A fingerprint would
//! have been cheaper to compute and wrong on both counts: it is unreadable in a
//! `clients/` listing, and it changes on renewal, silently de-scoping every
//! registration the operator wrote.
//!
//! **yog links no certificate library, so this is a DER walk** (AGENTS.md rule
//! 6: zero new dependencies). It is ~60 lines of 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 — a scan for the CN object identifier would
//! return the operator CA's name for every client 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.
/// 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. Spelled as bytes for [`COMMON_NAME`]'s
/// reason.
const ORG_UNIT: = ;
/// How many constructed fields separate `serialNumber` from `subject`:
/// signature, issuer, validity, subject.
const SERIAL_TO_SUBJECT: usize = 4;
/// 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 `CN` is the leaf's own; a certificate minted by `make wire-certs` has
/// exactly one and the question does not arise.
/// **The grade the same subject carries** (REMOTE §4.2, bl-1dd3): an
/// `OU=foot` anywhere in it is a foot, and everything else — a subject with
/// other organizational units, a subject with none, bytes that are not a
/// certificate at all — is operator grade.
///
/// **Default-operator, not default-foot**, which is why this answers a
/// [`Grade`] rather than an `Option`: a leaf minted before the grade existed
/// must keep working exactly as it did. A silent demotion would be an outage
/// with no sentence attached; a silent promotion cannot happen, because the
/// word is written by the operator's own CA or not at all.
///
/// It is the organizational unit rather than a custom extension or an EKU OID
/// because those need an `openssl` config stanza in the mint *and* an extension
/// parser yog does not have, where the OU is one more attribute in a subject
/// this walk opens anyway.
/// **The grade a leaf ON DISK carries** (bl-bd48) — [`grade`] against a stored
/// PEM, or the refusal naming the file it could not read.
///
/// The one caller is the enrollment that ADOPTS a leaf `wire-certs` already
/// minted (`boundary::dispatch::enroll`): the grade is a fact the operator's own
/// CA wrote into a subject, so an adoption that took the asked-for grade on
/// trust would grant a foot's authority to an operator leaf, or the reverse, on
/// the strength of a word typed at a seat.
///
/// The PEM framing is rustls' — the crate already links it for the channel
/// (`wire::tls`), and base64 between two labels is not a certificate library.
/// The certificate itself is still read by [`grade`]'s own DER walk.
/// 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.