@amritk/nish-x86_64-linux 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +24 -4
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/host.d.ts +8 -0
  6. package/runtime/core/host.mjs +50 -2
  7. package/runtime/nish.d.ts +100 -0
  8. package/runtime/nish.h +34 -0
  9. package/runtime/nish.mjs +21 -0
  10. package/runtime/node.mjs +57 -6
  11. package/runtime/runtime-net.c +44 -3
  12. package/runtime/runtime-os.c +115 -12
  13. package/scripts/build.sh +13 -3
  14. package/std/README.md +25 -8
  15. package/std/cli-args.ts +1216 -0
  16. package/std/cli-flags.ts +585 -0
  17. package/std/cli.ts +308 -19
  18. package/std/compress/checksum.ts +132 -20
  19. package/std/compress/deflate.ts +638 -248
  20. package/std/compress/gzip.ts +20 -6
  21. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  22. package/std/crypto/p256.ts +63 -1
  23. package/std/crypto/p384.ts +6044 -0
  24. package/std/crypto/pki-roots.ts +322 -0
  25. package/std/crypto/pki.ts +1207 -0
  26. package/std/crypto/rsa.ts +663 -0
  27. package/std/crypto/x509.ts +1054 -58
  28. package/std/net/dns-config.ts +734 -0
  29. package/std/net/dns-message.ts +517 -0
  30. package/std/net/dns-resolver.ts +1015 -0
  31. package/std/net/dns.ts +478 -0
  32. package/std/net/http1-client.ts +1227 -0
  33. package/std/net/http1-server.ts +7 -2
  34. package/std/net/http1.ts +9 -9
  35. package/std/net/tls/client.ts +1094 -0
  36. package/std/net/tls/codec.ts +585 -3
  37. package/std/net/tls/record-client.ts +1192 -0
  38. package/std/net/tls/verify.ts +221 -0
  39. package/std/net/tls-tcp.ts +642 -11
  40. package/std/net/tls.ts +2 -2
  41. package/std/net/url.ts +559 -4
  42. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,1207 @@
1
+ /**
2
+ * `nish/crypto/pki` — whether the certificate chain a TLS server sent leads to
3
+ * a root the program trusts and names the host it dialled: RFC 5280 §6 path
4
+ * validation and RFC 6125 §6 hostname matching, at the instant the caller
5
+ * gives.
6
+ *
7
+ * import { pkiLoadSystemRoots } from "nish/crypto/pki-roots";
8
+ * import { PKI_OK, PkiResult, PkiTrustStore, pkiReason, pkiTrustStore, pkiVerifyServer } from "nish/crypto/pki";
9
+ *
10
+ * const store: PkiTrustStore = pkiTrustStore(pkiLoadSystemRoots().roots);
11
+ * // chain: each certificate's DER as the server sent it, its own first
12
+ * const r: PkiResult = pkiVerifyServer(chain, "example.com", toI64(Date.now()), store);
13
+ * if (r.code !== PKI_OK) {
14
+ * console.log(pkiReason(r.code));
15
+ * }
16
+ * // r.path, when PKI_OK: the leaf first, the trusted root last
17
+ *
18
+ * The network chooses every byte of `chain` (docs/security/crypto-pki.md,
19
+ * "Threat model"), so every refusal has a reason code of its own and every
20
+ * cost has a named bound: the certificates and bytes of a chain, the names
21
+ * and subtrees of a certificate, the certificates of a path, the signatures
22
+ * checked and the name comparisons made by one call.
23
+ *
24
+ * The chain's first certificate is the leaf; the rest are a pool, in any
25
+ * order and possibly with extras, as servers send them. A path is searched
26
+ * for depth first from the leaf: an issuer is a root of the store, tried
27
+ * first, or a pool certificate, whose subject is the child's issuer Name byte
28
+ * for byte, and whose subjectKeyIdentifier, when both it and the child's
29
+ * authorityKeyIdentifier are present, is that identifier. A certificate is
30
+ * never twice in a path. A path ends at a root, whose own signature is not
31
+ * read, but whose validity, critical extensions, basicConstraints,
32
+ * extKeyUsage, name constraints and key identifiers are checked as an
33
+ * intermediate's are, as x509-limbo's cases ask. Every check of an issuer is
34
+ * made as it is added, against the certificates below it, so a refusal ends
35
+ * that branch at once; the leaf's profile (CA/Browser Forum Baseline
36
+ * Requirements §7.1) is checked once a path is found. When no path is
37
+ * accepted, the reason is that of the attempt that got nearest a root: one
38
+ * that reached a root beats any that did not, a longer one a shorter, and the
39
+ * first found a later one of the same length.
40
+ *
41
+ * Revocation (CRLs, OCSP, Certificate Transparency), certificate policies,
42
+ * RFC 5280 §7.1's Name folding and an extension's criticality are not
43
+ * checked; each is a finding of docs/security/crypto-pki.md. Nothing here is
44
+ * secret — certificates, names and public keys — so nothing is wiped.
45
+ *
46
+ * Written for this module from RFC 5280 (§4.2 and §6.1), RFC 6125 (§6.4),
47
+ * RFC 4291 §2.2 and RFC 3986 §3.2.2 (address literals), RFC 5758, RFC 8017
48
+ * and RFC 4055 (the signature algorithms), RFC 8032 (Ed25519) and the
49
+ * Baseline Requirements §7.1, and checked against x509-limbo
50
+ * (tests/link/crypto_pki_limbo).
51
+ *
52
+ * Private helpers share the importing program's flat symbol namespace
53
+ * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
54
+ */
55
+ import { ed25519Verify, ED25519_PUBLIC_KEY_SIZE } from "nish/crypto/ed25519"
56
+ import { p256Verify, P256_POINT_SIZE } from "nish/crypto/p256"
57
+ import { p384Verify, P384_POINT_SIZE } from "nish/crypto/p384"
58
+ import {
59
+ RSA_HASH_SHA256,
60
+ RSA_HASH_SHA384,
61
+ RSA_HASH_SHA512,
62
+ RsaPublicKey,
63
+ rsaParsePublicKey,
64
+ rsaVerifyPkcs1v15,
65
+ rsaVerifyPss,
66
+ } from "nish/crypto/rsa"
67
+ import { sha256 } from "nish/crypto/sha256"
68
+ import { sha384 } from "nish/crypto/sha512"
69
+ import {
70
+ X509Certificate,
71
+ X509GeneralSubtree,
72
+ X509_GN_DIR,
73
+ X509_GN_DNS,
74
+ X509_GN_IP,
75
+ X509_KEY_ED25519,
76
+ X509_KEY_P256,
77
+ X509_KEY_P384,
78
+ X509_KEY_RSA,
79
+ X509_KU_DIGITAL_SIGNATURE,
80
+ X509_KU_KEY_CERT_SIGN,
81
+ X509_MAX_SERIAL,
82
+ X509_SIG_ECDSA_SHA256,
83
+ X509_SIG_ECDSA_SHA384,
84
+ X509_SIG_ED25519,
85
+ X509_SIG_RSA_PKCS1_SHA256,
86
+ X509_SIG_RSA_PKCS1_SHA384,
87
+ X509_SIG_RSA_PKCS1_SHA512,
88
+ X509_SIG_RSA_PSS,
89
+ x509DerSignatureRSWidth,
90
+ x509NameEqual,
91
+ x509NameIsPrefix,
92
+ x509ParseCertificate,
93
+ } from "nish/crypto/x509"
94
+
95
+ /** The chain leads to a trusted root, every check passed and the leaf names the host. */
96
+ export const PKI_OK: i32 = 0
97
+ /**
98
+ * The chain is empty, a certificate in it is not one `x509ParseCertificate`
99
+ * reads, or a certificate on the path breaks the profile a server's chain
100
+ * keeps: a key identifier, serial number or dNSName.
101
+ */
102
+ export const PKI_ERR_MALFORMED: i32 = 1
103
+ /** A certificate's notAfter is before the instant given. */
104
+ export const PKI_ERR_EXPIRED: i32 = 2
105
+ /** A certificate's notBefore is after the instant given. */
106
+ export const PKI_ERR_NOT_YET_VALID: i32 = 3
107
+ /** No path leads to a root of the store. */
108
+ export const PKI_ERR_UNTRUSTED: i32 = 4
109
+ /** A signature does not verify under its issuer's key, or the key is not one. */
110
+ export const PKI_ERR_BAD_SIGNATURE: i32 = 5
111
+ /** An issuer is not a v3 certificate with basicConstraints cA, or the leaf is a CA. */
112
+ export const PKI_ERR_NOT_CA: i32 = 6
113
+ /** An issuer's pathLenConstraint is fewer than the intermediates below it. */
114
+ export const PKI_ERR_PATH_LENGTH: i32 = 7
115
+ /** An issuer's keyUsage lacks keyCertSign, or the leaf's lacks digitalSignature or has keyCertSign. */
116
+ export const PKI_ERR_KEY_USAGE: i32 = 8
117
+ /** An extKeyUsage lacks serverAuth, the leaf's allows any, or the root has one. */
118
+ export const PKI_ERR_EXT_KEY_USAGE: i32 = 9
119
+ /** A certificate carries a critical extension `nish/crypto/x509` does not decode. */
120
+ export const PKI_ERR_UNKNOWN_CRITICAL: i32 = 10
121
+ /** A name below an issuer is outside its nameConstraints, they are of a form not checked, or the leaf has some. */
122
+ export const PKI_ERR_NAME_CONSTRAINTS: i32 = 11
123
+ /** The leaf's subjectAltName does not name the host. */
124
+ export const PKI_ERR_HOSTNAME: i32 = 12
125
+ /** An RSA key is shorter than `PKI_MIN_RSA_BITS`, or its modulus is not a whole number of bytes. */
126
+ export const PKI_ERR_WEAK_KEY: i32 = 13
127
+ /** A signature algorithm is SHA-1, unknown, or not one the issuer's key makes, or the leaf's key is of no type offered. */
128
+ export const PKI_ERR_UNSUPPORTED_ALGORITHM: i32 = 14
129
+ /** The chain or a path is longer than its bound. */
130
+ export const PKI_ERR_CHAIN_TOO_LONG: i32 = 15
131
+ /** The chain asks for more work than one call does: signatures, names or subtrees. */
132
+ export const PKI_ERR_BUDGET: i32 = 16
133
+
134
+ /** The most certificates a chain may hold, the leaf among them. */
135
+ export const PKI_MAX_CHAIN_CERTS: i32 = 128
136
+ /** The most bytes a chain's certificates may total: 1 MiB. */
137
+ export const PKI_MAX_CHAIN_BYTES: i32 = 1048576
138
+ /** The most certificates in a path, the leaf and the root among them. */
139
+ export const PKI_MAX_PATH: i32 = 8
140
+ /** The most signatures one call checks. */
141
+ export const PKI_MAX_SIGNATURES: i32 = 64
142
+ /** The most subjectAltName dNSNames and iPAddresses together a certificate may carry. */
143
+ export const PKI_MAX_NAMES: i32 = 1024
144
+ /** The most permitted and excluded subtrees together a certificate may carry. */
145
+ export const PKI_MAX_SUBTREES: i32 = 256
146
+ /** The most name-against-subtree comparisons one call makes. */
147
+ export const PKI_MAX_NAME_CHECKS: i32 = 250000
148
+ /** The shortest RSA modulus accepted, in bits. */
149
+ export const PKI_MIN_RSA_BITS: i32 = 2048
150
+
151
+ /** What an attempt that reached a root adds to its length, so it outranks every one that did not. */
152
+ const PKI_AT_ROOT: i32 = 1000
153
+
154
+ /** The roots a program trusts, indexed by their subject Name. */
155
+ export class PkiTrustStore {
156
+ /** Every root, in the order given, no two with the same DER. */
157
+ roots: X509Certificate[]
158
+ /** The roots under each subject, keyed by the subject's DER in hex. */
159
+ bySubject: Map<string, X509Certificate[]>
160
+
161
+ constructor() {
162
+ this.roots = []
163
+ this.bySubject = new Map<string, X509Certificate[]>()
164
+ }
165
+ }
166
+
167
+ /** What `pkiVerifyServer` found. */
168
+ export class PkiResult {
169
+ /** `PKI_OK` or a `PKI_ERR_` constant. */
170
+ code: i32
171
+ /** When `PKI_OK`, the path: the leaf first, the root last; otherwise empty. */
172
+ path: X509Certificate[]
173
+
174
+ constructor(code: i32, path: X509Certificate[]) {
175
+ this.code = code
176
+ this.path = path
177
+ }
178
+ }
179
+
180
+ /** One call's search: the pool, the host, the path so far, and what has been spent. */
181
+ class PkiSearch {
182
+ store: PkiTrustStore
183
+ pool: X509Certificate[]
184
+ path: X509Certificate[]
185
+ /** The host as a DNS name, normalised, or `""` when it is an address. */
186
+ host: string
187
+ nowMs: i64 = 0
188
+ signatures: i32 = 0
189
+ nameChecks: i32 = 0
190
+ /** The reason of the attempt nearest a root, and how near it got. */
191
+ bestCode: i32 = 0
192
+ bestScore: i32 = -1
193
+ /** Whether a bound was spent, which ends the search. */
194
+ spent: boolean = false
195
+
196
+ constructor(store: PkiTrustStore, pool: X509Certificate[], host: string, nowMs: i64) {
197
+ this.store = store
198
+ this.pool = pool
199
+ this.path = []
200
+ this.host = host
201
+ this.nowMs = nowMs
202
+ this.bestCode = PKI_ERR_UNTRUSTED
203
+ }
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Bytes and ASCII
208
+ // ---------------------------------------------------------------------------
209
+
210
+ const pkiBytesEqual = (a: u8[], b: u8[]): boolean => {
211
+ const n: i32 = toI32(a.length)
212
+ if (n !== toI32(b.length)) {
213
+ return false
214
+ }
215
+ for (let i: i32 = 0; i < n && i < toI32(b.length); i++) {
216
+ if (a[i] !== b[i]) {
217
+ return false
218
+ }
219
+ }
220
+ return true
221
+ }
222
+
223
+ const pkiHexDigits: string = "0123456789abcdef"
224
+
225
+ /** `bytes` in lower-case hex, a `Map` key. */
226
+ const pkiHex = (bytes: u8[]): string => {
227
+ const out: u8[] = new Array<u8>(2 * toI32(bytes.length))
228
+ for (let i: i32 = 0; i < toI32(bytes.length) && 2 * i + 1 < toI32(out.length); i++) {
229
+ const b: i32 = toI32(bytes[i])
230
+ out[2 * i] = toU8(toI32(pkiHexDigits.charCodeAt(b >> 4)))
231
+ out[2 * i + 1] = toU8(toI32(pkiHexDigits.charCodeAt(b & 15)))
232
+ }
233
+ return String.fromBytes(out, 0, toI32(out.length))
234
+ }
235
+
236
+ const pkiLower = (c: i32): i32 => (c >= 65 && c <= 90 ? c + 32 : c)
237
+
238
+ /** Whether `a[aAt ..]` and `b[bAt ..]` agree for `n` bytes, ASCII case folded; both ranges must lie inside. */
239
+ const pkiFoldEqualAt = (a: string, aAt: i32, b: string, bAt: i32, n: i32): boolean => {
240
+ if (aAt < 0 || bAt < 0 || n < 0 || aAt + n > toI32(a.length) || bAt + n > toI32(b.length)) {
241
+ return false
242
+ }
243
+ for (let i: i32 = 0; i < n; i++) {
244
+ if (pkiLower(toI32(a.charCodeAt(aAt + i))) !== pkiLower(toI32(b.charCodeAt(bAt + i)))) {
245
+ return false
246
+ }
247
+ }
248
+ return true
249
+ }
250
+
251
+ /** `s` without one trailing dot. */
252
+ const pkiStripDot = (s: string): string =>
253
+ s.length > 0 && toI32(s.charCodeAt(toI32(s.length) - 1)) === 46 ? s.slice(0, toI32(s.length) - 1) : s
254
+
255
+ /**
256
+ * Whether `s` is a DNS name a hostname can be: one to 253 bytes of labels of
257
+ * one to 63 letters, digits, hyphens or underscores, joined by single dots,
258
+ * with no trailing dot (a caller strips it). No `*`, no space, no octet past
259
+ * ASCII.
260
+ */
261
+ const pkiValidDns = (s: string): boolean => {
262
+ const n: i32 = toI32(s.length)
263
+ if (n === 0 || n > 253) {
264
+ return false
265
+ }
266
+ let label: i32 = 0
267
+ for (let i: i32 = 0; i < n; i++) {
268
+ const c: i32 = toI32(s.charCodeAt(i))
269
+ if (c === 46) {
270
+ if (label === 0) {
271
+ return false
272
+ }
273
+ label = 0
274
+ } else if (
275
+ (c >= 97 && c <= 122) ||
276
+ (c >= 65 && c <= 90) ||
277
+ (c >= 48 && c <= 57) ||
278
+ c === 45 ||
279
+ c === 95
280
+ ) {
281
+ label++
282
+ if (label > 63) {
283
+ return false
284
+ }
285
+ } else {
286
+ return false
287
+ }
288
+ }
289
+ return label > 0
290
+ }
291
+
292
+ const pkiLabelCount = (s: string): i32 => {
293
+ let count: i32 = 1
294
+ for (let i: i32 = 0; i < toI32(s.length); i++) {
295
+ if (toI32(s.charCodeAt(i)) === 46) {
296
+ count++
297
+ }
298
+ }
299
+ return count
300
+ }
301
+
302
+ // ---------------------------------------------------------------------------
303
+ // Address literals (RFC 3986 §3.2.2, RFC 4291 §2.2)
304
+ // ---------------------------------------------------------------------------
305
+
306
+ const pkiDigit = (c: i32): i32 => (c >= 48 && c <= 57 ? c - 48 : -1)
307
+
308
+ const pkiHexDigit = (c: i32): i32 => {
309
+ if (c >= 48 && c <= 57) {
310
+ return c - 48
311
+ }
312
+ const l: i32 = pkiLower(c)
313
+ return l >= 97 && l <= 102 ? l - 87 : -1
314
+ }
315
+
316
+ /**
317
+ * The four octets of a dotted quad in `s[start .. end)`: four decimal numbers
318
+ * of 0 to 255, with no sign and no leading zero, or `null`.
319
+ */
320
+ const pkiParseIpv4 = (s: string, start: i32, end: i32): u8[] | null => {
321
+ if (start < 0 || end > toI32(s.length)) {
322
+ return null
323
+ }
324
+ const out: u8[] = new Array<u8>(4)
325
+ let part: i32 = 0
326
+ let value: i32 = 0
327
+ let digits: i32 = 0
328
+ for (let i: i32 = start; i <= end; i++) {
329
+ const c: i32 = i < end ? toI32(s.charCodeAt(i)) : 46
330
+ if (c === 46) {
331
+ if (digits === 0 || part > 3) {
332
+ return null
333
+ }
334
+ out[part] = toU8(value)
335
+ part++
336
+ value = 0
337
+ digits = 0
338
+ } else {
339
+ const d: i32 = pkiDigit(c)
340
+ if (d < 0 || (digits > 0 && value === 0)) {
341
+ return null
342
+ }
343
+ value = value * 10 + d
344
+ digits++
345
+ if (value > 255) {
346
+ return null
347
+ }
348
+ }
349
+ }
350
+ return part === 4 ? out : null
351
+ }
352
+
353
+ /**
354
+ * The sixteen octets of an IPv6 address in `s` (RFC 4291 §2.2): eight groups
355
+ * of one to four hex digits, at most one `::` standing for one or more zero
356
+ * groups, and a dotted quad as the last two groups. A zone (`%eth0`) is not an
357
+ * address. `null` for anything else.
358
+ */
359
+ const pkiParseIpv6 = (s: string): u8[] | null => {
360
+ const n: i32 = toI32(s.length)
361
+ const words: i32[] = []
362
+ let gap: i32 = -1
363
+ let value: i32 = 0
364
+ let digits: i32 = 0
365
+ let colons: i32 = 0
366
+ let group: i32 = 0
367
+ let quadAt: i32 = -1
368
+ for (let i: i32 = 0; i < n; i++) {
369
+ const c: i32 = toI32(s.charCodeAt(i))
370
+ if (c === 58) {
371
+ if (digits > 0) {
372
+ if (toI32(words.length) >= 8) {
373
+ return null
374
+ }
375
+ words.push(value)
376
+ value = 0
377
+ digits = 0
378
+ }
379
+ colons++
380
+ if (colons > 2 || (colons === 2 && gap >= 0)) {
381
+ return null
382
+ }
383
+ if (colons === 2) {
384
+ gap = toI32(words.length)
385
+ }
386
+ } else if (c === 46) {
387
+ quadAt = group
388
+ break
389
+ } else {
390
+ const d: i32 = pkiHexDigit(c)
391
+ if (d < 0 || digits >= 4 || (colons === 1 && toI32(words.length) === 0 && gap < 0)) {
392
+ return null
393
+ }
394
+ if (digits === 0) {
395
+ group = i
396
+ }
397
+ colons = 0
398
+ value = value * 16 + d
399
+ digits++
400
+ }
401
+ }
402
+ if (quadAt >= 0) {
403
+ const quad: u8[] | null = pkiParseIpv4(s, quadAt, n)
404
+ if (quad === null || toI32(words.length) > 6) {
405
+ return null
406
+ }
407
+ words.push((toI32(quad[0]) << 8) | toI32(quad[1]))
408
+ words.push((toI32(quad[2]) << 8) | toI32(quad[3]))
409
+ } else if (digits > 0) {
410
+ if (toI32(words.length) >= 8) {
411
+ return null
412
+ }
413
+ words.push(value)
414
+ } else if (colons !== 2) {
415
+ return null
416
+ }
417
+ const count: i32 = toI32(words.length)
418
+ if ((gap < 0 && count !== 8) || (gap >= 0 && count > 7)) {
419
+ return null
420
+ }
421
+ const out: u8[] = new Array<u8>(16)
422
+ for (let w: i32 = 0; w < count; w++) {
423
+ const at: i32 = gap >= 0 && w >= gap ? w + 8 - count : w
424
+ if (at >= 0 && 2 * at + 1 < 16) {
425
+ out[2 * at] = toU8(words[w] >> 8)
426
+ out[2 * at + 1] = toU8(words[w] & 255)
427
+ }
428
+ }
429
+ return out
430
+ }
431
+
432
+ /**
433
+ * The octets of `host` when it is an address: a dotted quad, or an IPv6
434
+ * address bare or in brackets. `null` when it is not one — then it is a DNS
435
+ * name or nothing, and a bracket or a colon is never part of a DNS name.
436
+ */
437
+ const pkiParseIp = (host: string): u8[] | null => {
438
+ const n: i32 = toI32(host.length)
439
+ if (n >= 2 && toI32(host.charCodeAt(0)) === 91 && toI32(host.charCodeAt(n - 1)) === 93) {
440
+ return pkiParseIpv6(host.slice(1, n - 1))
441
+ }
442
+ if (host.indexOf(":") >= 0) {
443
+ return pkiParseIpv6(host)
444
+ }
445
+ return pkiParseIpv4(host, 0, n)
446
+ }
447
+
448
+ // ---------------------------------------------------------------------------
449
+ // Hostname (RFC 6125 §6.4)
450
+ // ---------------------------------------------------------------------------
451
+
452
+ /**
453
+ * Whether the subjectAltName dNSName `san`, IA5 bytes as encoded, names
454
+ * `host`, a valid DNS name without its trailing dot. A wildcard is only the
455
+ * whole leftmost label, `*.` before two labels or more, and stands for
456
+ * exactly one label of `host`.
457
+ */
458
+ const pkiDnsMatch = (san: string, host: string): boolean => {
459
+ const name: string = pkiStripDot(san)
460
+ const n: i32 = toI32(name.length)
461
+ if (n >= 2 && toI32(name.charCodeAt(0)) === 42 && toI32(name.charCodeAt(1)) === 46) {
462
+ const rest: string = name.slice(2, n)
463
+ const dot: i32 = toI32(host.indexOf("."))
464
+ const restLength: i32 = n - 2
465
+ return (
466
+ pkiValidDns(rest) &&
467
+ pkiLabelCount(rest) >= 2 &&
468
+ dot > 0 &&
469
+ toI32(host.length) - dot - 1 === restLength &&
470
+ pkiFoldEqualAt(host, dot + 1, rest, 0, restLength)
471
+ )
472
+ }
473
+ return pkiValidDns(name) && n === toI32(host.length) && pkiFoldEqualAt(name, 0, host, 0, n)
474
+ }
475
+
476
+ /** `hostname` without one trailing dot when it is a valid DNS name, else `null`. */
477
+ const pkiHostDns = (hostname: string): string | null => {
478
+ const host: string = pkiStripDot(hostname)
479
+ return pkiValidDns(host) ? host : null
480
+ }
481
+
482
+ /**
483
+ * Whether `leaf` names `hostname` (RFC 6125 §6.4): an address against the
484
+ * subjectAltName iPAddresses, octet for octet; any other name, ASCII and
485
+ * compared without case after one trailing dot is stripped from each side,
486
+ * against the dNSNames, a wildcard only as a whole leftmost label. The
487
+ * subject's common name is never read, so a certificate without a
488
+ * subjectAltName names no host.
489
+ */
490
+ export const pkiMatchHostname = (leaf: X509Certificate, hostname: string): boolean => {
491
+ const ip: u8[] | null = pkiParseIp(pkiStripDot(hostname))
492
+ if (ip !== null) {
493
+ for (const a of leaf.sanIp) {
494
+ if (pkiBytesEqual(a, ip)) {
495
+ return true
496
+ }
497
+ }
498
+ return false
499
+ }
500
+ const host: string | null = pkiHostDns(hostname)
501
+ if (host === null) {
502
+ return false
503
+ }
504
+ for (const d of leaf.sanDns) {
505
+ if (pkiDnsMatch(d, host)) {
506
+ return true
507
+ }
508
+ }
509
+ return false
510
+ }
511
+
512
+ // ---------------------------------------------------------------------------
513
+ // Signatures
514
+ // ---------------------------------------------------------------------------
515
+
516
+ /**
517
+ * Whether an RSA key is fit to sign or serve, as a code: one
518
+ * `rsaParsePublicKey` refused is a bad signature, and one shorter than
519
+ * `PKI_MIN_RSA_BITS`, or whose modulus is not a whole number of bytes, a weak
520
+ * key.
521
+ */
522
+ const pkiRsaKeyCode = (key: RsaPublicKey | null): i32 => {
523
+ if (key === null) {
524
+ return PKI_ERR_BAD_SIGNATURE
525
+ }
526
+ return key.bits < PKI_MIN_RSA_BITS || key.bits % 8 !== 0 ? PKI_ERR_WEAK_KEY : PKI_OK
527
+ }
528
+
529
+ /**
530
+ * An RSA signature of `cert` under `issuer`'s key, PKCS#1 v1.5 or PSS as
531
+ * `cert.sigAlg` names. The key is checked by `pkiRsaKeyCode` first. `X509_HASH_` and
532
+ * `RSA_HASH_` number SHA-256, SHA-384 and SHA-512 alike, so `pssHash` passes
533
+ * straight through.
534
+ */
535
+ const pkiRsaCode = (cert: X509Certificate, issuer: X509Certificate): i32 => {
536
+ const key: RsaPublicKey | null = rsaParsePublicKey(issuer.publicKey)
537
+ const fit: i32 = pkiRsaKeyCode(key)
538
+ if (key === null || fit !== PKI_OK) {
539
+ return fit
540
+ }
541
+ const alg: i32 = cert.sigAlg
542
+ if (alg === X509_SIG_RSA_PSS) {
543
+ return rsaVerifyPss(key, cert.pssHash, cert.pssSaltLen, cert.tbs, cert.signature)
544
+ ? PKI_OK
545
+ : PKI_ERR_BAD_SIGNATURE
546
+ }
547
+ let hash: i32 = RSA_HASH_SHA512
548
+ if (alg === X509_SIG_RSA_PKCS1_SHA256) {
549
+ hash = RSA_HASH_SHA256
550
+ } else if (alg === X509_SIG_RSA_PKCS1_SHA384) {
551
+ hash = RSA_HASH_SHA384
552
+ }
553
+ return rsaVerifyPkcs1v15(key, hash, cert.tbs, cert.signature) ? PKI_OK : PKI_ERR_BAD_SIGNATURE
554
+ }
555
+
556
+ /** An ECDSA signature of `cert` under a P-256 or P-384 key, with SHA-256 or SHA-384 as `cert.sigAlg` names. */
557
+ const pkiEcdsaCode = (cert: X509Certificate, issuer: X509Certificate): i32 => {
558
+ const digest: u8[] = cert.sigAlg === X509_SIG_ECDSA_SHA256 ? sha256(cert.tbs) : sha384(cert.tbs)
559
+ if (issuer.keyType === X509_KEY_P256) {
560
+ const rs: u8[] | null = x509DerSignatureRSWidth(cert.signature, 32)
561
+ return toI32(issuer.publicKey.length) === P256_POINT_SIZE &&
562
+ rs !== null &&
563
+ p256Verify(issuer.publicKey, digest, rs)
564
+ ? PKI_OK
565
+ : PKI_ERR_BAD_SIGNATURE
566
+ }
567
+ const rs: u8[] | null = x509DerSignatureRSWidth(cert.signature, 48)
568
+ return toI32(issuer.publicKey.length) === P384_POINT_SIZE &&
569
+ rs !== null &&
570
+ p384Verify(issuer.publicKey, digest, rs)
571
+ ? PKI_OK
572
+ : PKI_ERR_BAD_SIGNATURE
573
+ }
574
+
575
+ /**
576
+ * Whether `cert`'s signature verifies under `issuer`'s key, as a code: `PKI_OK`,
577
+ * `PKI_ERR_BAD_SIGNATURE`, `PKI_ERR_WEAK_KEY`, or `PKI_ERR_UNSUPPORTED_ALGORITHM`
578
+ * for SHA-1, an unknown algorithm, or one the key does not make.
579
+ */
580
+ const pkiSignatureCode = (cert: X509Certificate, issuer: X509Certificate): i32 => {
581
+ const alg: i32 = cert.sigAlg
582
+ const key: i32 = issuer.keyType
583
+ if (alg === X509_SIG_ECDSA_SHA256 || alg === X509_SIG_ECDSA_SHA384) {
584
+ return key === X509_KEY_P256 || key === X509_KEY_P384
585
+ ? pkiEcdsaCode(cert, issuer)
586
+ : PKI_ERR_UNSUPPORTED_ALGORITHM
587
+ }
588
+ if (alg === X509_SIG_ED25519) {
589
+ if (key !== X509_KEY_ED25519) {
590
+ return PKI_ERR_UNSUPPORTED_ALGORITHM
591
+ }
592
+ return toI32(issuer.publicKey.length) === ED25519_PUBLIC_KEY_SIZE &&
593
+ ed25519Verify(issuer.publicKey, cert.tbs, cert.signature)
594
+ ? PKI_OK
595
+ : PKI_ERR_BAD_SIGNATURE
596
+ }
597
+ if (
598
+ alg === X509_SIG_RSA_PKCS1_SHA256 ||
599
+ alg === X509_SIG_RSA_PKCS1_SHA384 ||
600
+ alg === X509_SIG_RSA_PKCS1_SHA512 ||
601
+ alg === X509_SIG_RSA_PSS
602
+ ) {
603
+ return key === X509_KEY_RSA ? pkiRsaCode(cert, issuer) : PKI_ERR_UNSUPPORTED_ALGORITHM
604
+ }
605
+ return PKI_ERR_UNSUPPORTED_ALGORITHM
606
+ }
607
+
608
+ /**
609
+ * Whether `cert`'s signature verifies under `issuer`'s key: ECDSA with SHA-256
610
+ * or SHA-384 under P-256 or P-384, RSA PKCS#1 v1.5 with SHA-256, SHA-384 or
611
+ * SHA-512 and RSASSA-PSS under a key of `PKI_MIN_RSA_BITS` or more whose
612
+ * modulus is a whole number of bytes, and Ed25519. Nothing else about either certificate is checked.
613
+ */
614
+ export const pkiVerifySignature = (cert: X509Certificate, issuer: X509Certificate): boolean =>
615
+ pkiSignatureCode(cert, issuer) === PKI_OK
616
+
617
+ /** `pkiSignatureCode` within the call's budget of signatures. */
618
+ const pkiCheckSignature = (s: PkiSearch, cert: X509Certificate, issuer: X509Certificate): i32 => {
619
+ if (s.signatures >= PKI_MAX_SIGNATURES) {
620
+ s.spent = true
621
+ return PKI_ERR_BUDGET
622
+ }
623
+ s.signatures = s.signatures + 1
624
+ return pkiSignatureCode(cert, issuer)
625
+ }
626
+
627
+ // ---------------------------------------------------------------------------
628
+ // Name constraints (RFC 5280 §4.2.1.10, §6.1.3 (b) and (c))
629
+ // ---------------------------------------------------------------------------
630
+
631
+ /**
632
+ * Whether the DNS name `name` lies in the subtree `base`, without case: `""`
633
+ * is every name, `.example.com` every name below example.com, and
634
+ * `example.com` it and every name below it.
635
+ */
636
+ const pkiDnsWithin = (name: string, base: string): boolean => {
637
+ const b: string = pkiStripDot(base)
638
+ const nb: i32 = toI32(b.length)
639
+ const nn: i32 = toI32(name.length)
640
+ if (nb === 0) {
641
+ return true
642
+ }
643
+ if (toI32(b.charCodeAt(0)) === 46) {
644
+ return nn > nb && pkiFoldEqualAt(name, nn - nb, b, 0, nb)
645
+ }
646
+ if (nn === nb) {
647
+ return pkiFoldEqualAt(name, 0, b, 0, nb)
648
+ }
649
+ return nn > nb && toI32(name.charCodeAt(nn - nb - 1)) === 46 && pkiFoldEqualAt(name, nn - nb, b, 0, nb)
650
+ }
651
+
652
+ /** `base` as the name its subtree is rooted at: without its leading dot. */
653
+ const pkiSubtreeRoot = (base: string): string =>
654
+ base.length > 0 && toI32(base.charCodeAt(0)) === 46 ? base.slice(1, toI32(base.length)) : base
655
+
656
+ /**
657
+ * Whether `issuer`'s dNSName constraints allow the name `dns`: inside one
658
+ * permitted subtree when any is listed, and in no excluded one. A wildcard
659
+ * `*.X` stands for every name one label below X, so it is permitted when X
660
+ * lies in the subtree's root, and excluded when it could reach an excluded
661
+ * subtree — X lies in that root, or the root lies below X.
662
+ */
663
+ const pkiDnsAllowed = (issuer: X509Certificate, dns: string): boolean => {
664
+ const name: string = pkiStripDot(dns)
665
+ const wildcard: boolean = name.startsWith("*.")
666
+ const base: string = wildcard ? name.slice(2, toI32(name.length)) : name
667
+ let listed: boolean = false
668
+ let inside: boolean = false
669
+ for (const p of issuer.ncPermitted) {
670
+ if (p.kind === X509_GN_DNS) {
671
+ listed = true
672
+ if (wildcard ? pkiDnsWithin(base, pkiSubtreeRoot(p.dns)) : pkiDnsWithin(base, p.dns)) {
673
+ inside = true
674
+ }
675
+ }
676
+ }
677
+ if (listed && !inside) {
678
+ return false
679
+ }
680
+ for (const e of issuer.ncExcluded) {
681
+ if (e.kind === X509_GN_DNS) {
682
+ const root: string = pkiSubtreeRoot(e.dns)
683
+ if (wildcard ? pkiDnsWithin(base, root) || pkiDnsWithin(root, base) : pkiDnsWithin(base, e.dns)) {
684
+ return false
685
+ }
686
+ }
687
+ }
688
+ return true
689
+ }
690
+
691
+ /** Whether `ip` lies in the iPAddress subtree `t`: the same family, and equal under its mask. */
692
+ const pkiIpWithin = (ip: u8[], t: X509GeneralSubtree): boolean => {
693
+ const n: i32 = toI32(ip.length)
694
+ if (n !== toI32(t.ip.length) || n !== toI32(t.mask.length)) {
695
+ return false
696
+ }
697
+ for (let k: i32 = 0; k < n && k < toI32(t.ip.length) && k < toI32(t.mask.length); k++) {
698
+ if ((toI32(ip[k]) & toI32(t.mask[k])) !== (toI32(t.ip[k]) & toI32(t.mask[k]))) {
699
+ return false
700
+ }
701
+ }
702
+ return true
703
+ }
704
+
705
+ /** Whether `issuer`'s iPAddress constraints allow `ip`; a subtree of the other family never holds it. */
706
+ const pkiIpAllowed = (issuer: X509Certificate, ip: u8[]): boolean => {
707
+ let listed: boolean = false
708
+ let inside: boolean = false
709
+ for (const p of issuer.ncPermitted) {
710
+ if (p.kind === X509_GN_IP) {
711
+ listed = true
712
+ if (pkiIpWithin(ip, p)) {
713
+ inside = true
714
+ }
715
+ }
716
+ }
717
+ if (listed && !inside) {
718
+ return false
719
+ }
720
+ for (const e of issuer.ncExcluded) {
721
+ if (e.kind === X509_GN_IP && pkiIpWithin(ip, e)) {
722
+ return false
723
+ }
724
+ }
725
+ return true
726
+ }
727
+
728
+ /**
729
+ * Whether `issuer`'s permitted directoryName constraints allow the subject
730
+ * Name `subject`: one of them is a prefix of it, RDN by RDN. An empty subject
731
+ * names nothing and is allowed. Excluded directoryNames never reach here: a
732
+ * byte-exact prefix test would let a differently spelt Name past one, so a
733
+ * chain that carries any is refused whole.
734
+ */
735
+ const pkiDirAllowed = (issuer: X509Certificate, subject: u8[]): boolean => {
736
+ if (toI32(subject.length) <= 2) {
737
+ return true
738
+ }
739
+ let listed: boolean = false
740
+ for (const p of issuer.ncPermitted) {
741
+ if (p.kind === X509_GN_DIR) {
742
+ listed = true
743
+ if (x509NameIsPrefix(p.dirName, subject)) {
744
+ return true
745
+ }
746
+ }
747
+ }
748
+ return !listed
749
+ }
750
+
751
+ const pkiSelfIssued = (c: X509Certificate): boolean => x509NameEqual(c.subject, c.issuer)
752
+
753
+ /**
754
+ * Whether `issuer`'s nameConstraints allow every name below it in the path
755
+ * and the host, as a code. Each issuer's constraints are checked on their
756
+ * own, which makes permitted subtrees an intersection down the path and
757
+ * excluded ones a union.
758
+ */
759
+ const pkiConstraintsCode = (s: PkiSearch, issuer: X509Certificate): i32 => {
760
+ if (!issuer.ncPresent) {
761
+ return PKI_OK
762
+ }
763
+ if (issuer.ncUnsupported) {
764
+ return PKI_ERR_NAME_CONSTRAINTS
765
+ }
766
+ for (const e of issuer.ncExcluded) {
767
+ if (e.kind === X509_GN_DIR) {
768
+ return PKI_ERR_NAME_CONSTRAINTS
769
+ }
770
+ }
771
+ let names: i32 = 1
772
+ for (const c of s.path) {
773
+ names = names + toI32(c.sanDns.length) + toI32(c.sanIp.length) + 1
774
+ }
775
+ const cost: i32 = names * (toI32(issuer.ncPermitted.length) + toI32(issuer.ncExcluded.length))
776
+ if (cost > PKI_MAX_NAME_CHECKS - s.nameChecks) {
777
+ s.spent = true
778
+ return PKI_ERR_BUDGET
779
+ }
780
+ s.nameChecks = s.nameChecks + cost
781
+ if (s.host.length > 0 && !pkiDnsAllowed(issuer, s.host)) {
782
+ return PKI_ERR_NAME_CONSTRAINTS
783
+ }
784
+ for (let i: i32 = 0; i < toI32(s.path.length); i++) {
785
+ const c: X509Certificate = s.path[i]
786
+ for (const d of c.sanDns) {
787
+ if (!pkiDnsAllowed(issuer, d)) {
788
+ return PKI_ERR_NAME_CONSTRAINTS
789
+ }
790
+ }
791
+ for (const ip of c.sanIp) {
792
+ if (!pkiIpAllowed(issuer, ip)) {
793
+ return PKI_ERR_NAME_CONSTRAINTS
794
+ }
795
+ }
796
+ if ((i === 0 || !pkiSelfIssued(c)) && !pkiDirAllowed(issuer, c.subject)) {
797
+ return PKI_ERR_NAME_CONSTRAINTS
798
+ }
799
+ }
800
+ return PKI_OK
801
+ }
802
+
803
+ // ---------------------------------------------------------------------------
804
+ // The path (RFC 5280 §6.1)
805
+ // ---------------------------------------------------------------------------
806
+
807
+ /** Whether `c` is valid at `nowMs`, as a code. */
808
+ const pkiTimeCode = (c: X509Certificate, nowMs: i64): i32 => {
809
+ if (nowMs < c.notBefore) {
810
+ return PKI_ERR_NOT_YET_VALID
811
+ }
812
+ return nowMs > c.notAfter ? PKI_ERR_EXPIRED : PKI_OK
813
+ }
814
+
815
+ /**
816
+ * The key identifiers an issuer `cand` must carry (RFC 5280 §4.2.1.1 and
817
+ * §4.2.1.2), as a code: a subjectKeyIdentifier, which a root may lack, as
818
+ * one Mozilla ships does, and an authorityKeyIdentifier unless it is
819
+ * self-issued, even on a root. A self-issued root's authorityKeyIdentifier,
820
+ * when present, is its own subjectKeyIdentifier.
821
+ */
822
+ const pkiKeyIdsCode = (cand: X509Certificate, root: boolean): i32 => {
823
+ const skid: u8[] | null = cand.skid
824
+ const akid: u8[] | null = cand.akid
825
+ if (skid === null) {
826
+ return root ? PKI_OK : PKI_ERR_MALFORMED
827
+ }
828
+ if (!pkiSelfIssued(cand)) {
829
+ return akid === null ? PKI_ERR_MALFORMED : PKI_OK
830
+ }
831
+ return root && akid !== null && !pkiBytesEqual(akid, skid) ? PKI_ERR_MALFORMED : PKI_OK
832
+ }
833
+
834
+ /**
835
+ * The checks of the issuer `cand` added above the path, a root of the store
836
+ * when `root` holds and an intermediate otherwise: its validity, its critical
837
+ * extensions, that it is a CA, its extKeyUsage, its name constraints, its
838
+ * signature over `child` — the only check that costs — and, once that
839
+ * verifies, its key identifiers, so a wrong key reads as a signature. An
840
+ * intermediate also needs room for a root above it and a keyUsage that
841
+ * allows keyCertSign; every issuer, the root too, needs a pathLenConstraint
842
+ * that covers the intermediates below it. A root's keyUsage is not read, and
843
+ * it may carry no extKeyUsage at all.
844
+ */
845
+ const pkiIssuerCode = (s: PkiSearch, child: X509Certificate, cand: X509Certificate, root: boolean): i32 => {
846
+ // No intermediate is added past PKI_MAX_PATH - 1, so a root always fits.
847
+ if (!root && toI32(s.path.length) + 2 > PKI_MAX_PATH) {
848
+ return PKI_ERR_CHAIN_TOO_LONG
849
+ }
850
+ const time: i32 = pkiTimeCode(cand, s.nowMs)
851
+ if (time !== PKI_OK) {
852
+ return time
853
+ }
854
+ if (cand.unknownCritical) {
855
+ return PKI_ERR_UNKNOWN_CRITICAL
856
+ }
857
+ if (cand.version !== 2 || !cand.bcPresent || !cand.bcCa) {
858
+ return PKI_ERR_NOT_CA
859
+ }
860
+ if (!root && cand.keyUsage >= 0 && (cand.keyUsage & X509_KU_KEY_CERT_SIGN) === 0) {
861
+ return PKI_ERR_KEY_USAGE
862
+ }
863
+ if (cand.ekuPresent && (root || (!cand.ekuServerAuth && !cand.ekuAny))) {
864
+ return PKI_ERR_EXT_KEY_USAGE
865
+ }
866
+ if (cand.bcPathLen >= 0) {
867
+ let below: i32 = 0
868
+ for (let i: i32 = 1; i < toI32(s.path.length); i++) {
869
+ if (!pkiSelfIssued(s.path[i])) {
870
+ below++
871
+ }
872
+ }
873
+ if (below > cand.bcPathLen) {
874
+ return PKI_ERR_PATH_LENGTH
875
+ }
876
+ }
877
+ const names: i32 = pkiConstraintsCode(s, cand)
878
+ if (names !== PKI_OK) {
879
+ return names
880
+ }
881
+ const signature: i32 = pkiCheckSignature(s, child, cand)
882
+ return signature !== PKI_OK ? signature : pkiKeyIdsCode(cand, root)
883
+ }
884
+
885
+ /** Keeps `code` as the answer when its attempt, `score` long, got nearer a root than any before it. */
886
+ const pkiNote = (s: PkiSearch, code: i32, score: i32): void => {
887
+ if (score > s.bestScore) {
888
+ s.bestScore = score
889
+ s.bestCode = code
890
+ }
891
+ }
892
+
893
+ /**
894
+ * Whether `cand` may be tried as `child`'s issuer: its key identifier is not
895
+ * another than the one `child` names, and it is not already in the path.
896
+ */
897
+ const pkiCandidate = (s: PkiSearch, child: X509Certificate, cand: X509Certificate): boolean => {
898
+ const akid: u8[] | null = child.akid
899
+ const skid: u8[] | null = cand.skid
900
+ if (akid !== null && skid !== null && !pkiBytesEqual(akid, skid)) {
901
+ return false
902
+ }
903
+ for (const c of s.path) {
904
+ if (pkiBytesEqual(c.der, cand.der)) {
905
+ return false
906
+ }
907
+ }
908
+ return true
909
+ }
910
+
911
+ /**
912
+ * Extends the path above `child`, its last certificate, toward a root: the
913
+ * store's roots under `child`'s issuer first, then the pool's certificates in
914
+ * the order sent. Answers whether a path was accepted, then whole in `s.path`.
915
+ */
916
+ const pkiExtend = (s: PkiSearch, child: X509Certificate): boolean => {
917
+ const depth: i32 = toI32(s.path.length)
918
+ if (toI32(child.issuer.length) > 2) {
919
+ const roots: X509Certificate[] | undefined = s.store.bySubject.get(pkiHex(child.issuer))
920
+ if (roots !== undefined) {
921
+ for (const root of roots) {
922
+ if (pkiCandidate(s, child, root)) {
923
+ const code: i32 = pkiIssuerCode(s, child, root, true)
924
+ if (s.spent) {
925
+ return false
926
+ }
927
+ if (code === PKI_OK) {
928
+ s.path.push(root)
929
+ return true
930
+ }
931
+ pkiNote(s, code, PKI_AT_ROOT + depth + 1)
932
+ }
933
+ }
934
+ }
935
+ for (const cand of s.pool) {
936
+ if (x509NameEqual(cand.subject, child.issuer) && pkiCandidate(s, child, cand)) {
937
+ const code: i32 = pkiIssuerCode(s, child, cand, false)
938
+ if (s.spent) {
939
+ return false
940
+ }
941
+ if (code !== PKI_OK) {
942
+ pkiNote(s, code, depth + 1)
943
+ } else {
944
+ s.path.push(cand)
945
+ if (pkiExtend(s, cand)) {
946
+ return true
947
+ }
948
+ if (s.spent) {
949
+ return false
950
+ }
951
+ s.path.pop()
952
+ }
953
+ }
954
+ }
955
+ }
956
+ pkiNote(s, PKI_ERR_UNTRUSTED, depth)
957
+ return false
958
+ }
959
+
960
+ // ---------------------------------------------------------------------------
961
+ // The leaf's profile (CA/Browser Forum Baseline Requirements §7.1)
962
+ // ---------------------------------------------------------------------------
963
+
964
+ /** Whether `c`'s serial number is 1 to 20 octets and not zero (RFC 5280 §4.1.2.2). */
965
+ const pkiSerialSound = (c: X509Certificate): boolean => {
966
+ const n: i32 = toI32(c.serial.length)
967
+ if (n < 1 || n > X509_MAX_SERIAL) {
968
+ return false
969
+ }
970
+ for (let i: i32 = 0; i < n; i++) {
971
+ if (c.serial[i] !== 0) {
972
+ return true
973
+ }
974
+ }
975
+ return false
976
+ }
977
+
978
+ /**
979
+ * Whether the dNSName `san` is one a server's certificate may carry: a valid
980
+ * DNS name of letters, digits and hyphens, after one trailing dot, or `*.`
981
+ * before one.
982
+ */
983
+ const pkiSanDnsSound = (san: string): boolean => {
984
+ const name: string = pkiStripDot(san)
985
+ const n: i32 = toI32(name.length)
986
+ const wild: boolean = n >= 2 && toI32(name.charCodeAt(0)) === 42 && toI32(name.charCodeAt(1)) === 46
987
+ return name.indexOf("_") < 0 && pkiValidDns(wild ? name.slice(2, n) : name)
988
+ }
989
+
990
+ /**
991
+ * Whether the leaf's own key is one a server may hold, as a code: RSA of
992
+ * `PKI_MIN_RSA_BITS` or more in whole bytes, P-256, P-384 or Ed25519, the keys
993
+ * a signature is verified under here, so the server can prove it holds it.
994
+ */
995
+ const pkiSubscriberKeyCode = (leaf: X509Certificate): i32 => {
996
+ const type: i32 = leaf.keyType
997
+ if (type === X509_KEY_RSA) {
998
+ return pkiRsaKeyCode(rsaParsePublicKey(leaf.publicKey))
999
+ }
1000
+ return type === X509_KEY_P256 || type === X509_KEY_P384 || type === X509_KEY_ED25519
1001
+ ? PKI_OK
1002
+ : PKI_ERR_UNSUPPORTED_ALGORITHM
1003
+ }
1004
+
1005
+ /**
1006
+ * The checks of the leaf's profile, once a path above it is found: no keyCertSign
1007
+ * and no nameConstraints, no `anyExtendedKeyUsage`, a key a server may
1008
+ * hold, a sound serial number, an authorityKeyIdentifier unless it is
1009
+ * self-issued, and dNSNames of letters, digits and hyphens. The common name
1010
+ * is not read: limbo's own passing cases carry ones their subjectAltName
1011
+ * lacks.
1012
+ */
1013
+ const pkiLeafProfileCode = (leaf: X509Certificate): i32 => {
1014
+ if (leaf.keyUsage >= 0 && (leaf.keyUsage & X509_KU_KEY_CERT_SIGN) !== 0) {
1015
+ return PKI_ERR_KEY_USAGE
1016
+ }
1017
+ if (leaf.ncPresent) {
1018
+ return PKI_ERR_NAME_CONSTRAINTS
1019
+ }
1020
+ if (leaf.ekuAny) {
1021
+ return PKI_ERR_EXT_KEY_USAGE
1022
+ }
1023
+ const key: i32 = pkiSubscriberKeyCode(leaf)
1024
+ if (key !== PKI_OK) {
1025
+ return key
1026
+ }
1027
+ if (!pkiSerialSound(leaf) || (leaf.akid === null && !pkiSelfIssued(leaf))) {
1028
+ return PKI_ERR_MALFORMED
1029
+ }
1030
+ for (const d of leaf.sanDns) {
1031
+ if (!pkiSanDnsSound(d)) {
1032
+ return PKI_ERR_MALFORMED
1033
+ }
1034
+ }
1035
+ return PKI_OK
1036
+ }
1037
+
1038
+ /**
1039
+ * The checks of the leaf alone: its validity, its critical extensions, that
1040
+ * it is not a CA, that its key may sign a TLS handshake and is meant for a
1041
+ * server, and that it names the host. Its profile is judged after the path
1042
+ * search, so a path's reason comes first.
1043
+ */
1044
+ const pkiLeafCode = (leaf: X509Certificate, hostname: string, nowMs: i64): i32 => {
1045
+ const time: i32 = pkiTimeCode(leaf, nowMs)
1046
+ if (time !== PKI_OK) {
1047
+ return time
1048
+ }
1049
+ if (leaf.unknownCritical) {
1050
+ return PKI_ERR_UNKNOWN_CRITICAL
1051
+ }
1052
+ if (leaf.bcCa) {
1053
+ return PKI_ERR_NOT_CA
1054
+ }
1055
+ if (leaf.keyUsage >= 0 && (leaf.keyUsage & X509_KU_DIGITAL_SIGNATURE) === 0) {
1056
+ return PKI_ERR_KEY_USAGE
1057
+ }
1058
+ if (leaf.ekuPresent && !leaf.ekuServerAuth) {
1059
+ return PKI_ERR_EXT_KEY_USAGE
1060
+ }
1061
+ return pkiMatchHostname(leaf, hostname) ? PKI_OK : PKI_ERR_HOSTNAME
1062
+ }
1063
+
1064
+ /** A store of `roots`, indexed by subject; a root whose DER is one already given is kept once. */
1065
+ export const pkiTrustStore = (roots: X509Certificate[]): PkiTrustStore => {
1066
+ const store = new PkiTrustStore()
1067
+ for (const root of roots) {
1068
+ const key: string = pkiHex(root.subject)
1069
+ const list: X509Certificate[] | undefined = store.bySubject.get(key)
1070
+ if (list === undefined) {
1071
+ const fresh: X509Certificate[] = [root]
1072
+ store.bySubject.set(key, fresh)
1073
+ store.roots.push(root)
1074
+ } else {
1075
+ let seen: boolean = false
1076
+ for (const r of list) {
1077
+ if (pkiBytesEqual(r.der, root.der)) {
1078
+ seen = true
1079
+ }
1080
+ }
1081
+ if (!seen) {
1082
+ list.push(root)
1083
+ store.roots.push(root)
1084
+ }
1085
+ }
1086
+ }
1087
+ return store
1088
+ }
1089
+
1090
+ /**
1091
+ * Verifies a TLS server's chain: `chain` holds each certificate's DER as the
1092
+ * server sent it, the leaf first; `hostname` is the name or address dialled,
1093
+ * ASCII (an internationalised name in its A-label form); `nowMs` is the
1094
+ * instant to judge validity at, milliseconds since 1970-01-01 UTC; `store`
1095
+ * holds the roots trusted. `PKI_OK` with the path, or the reason it was
1096
+ * refused — see the module's header for which reason when several apply.
1097
+ */
1098
+ export const pkiVerifyServer = (
1099
+ chain: u8[][],
1100
+ hostname: string,
1101
+ nowMs: i64,
1102
+ store: PkiTrustStore
1103
+ ): PkiResult => {
1104
+ const none: X509Certificate[] = []
1105
+ const count: i32 = toI32(chain.length)
1106
+ if (count === 0) {
1107
+ return new PkiResult(PKI_ERR_MALFORMED, none)
1108
+ }
1109
+ if (count > PKI_MAX_CHAIN_CERTS) {
1110
+ return new PkiResult(PKI_ERR_CHAIN_TOO_LONG, none)
1111
+ }
1112
+ let bytes: i32 = 0
1113
+ for (const der of chain) {
1114
+ bytes = bytes + toI32(der.length)
1115
+ if (bytes > PKI_MAX_CHAIN_BYTES) {
1116
+ return new PkiResult(PKI_ERR_CHAIN_TOO_LONG, none)
1117
+ }
1118
+ }
1119
+ const certs: X509Certificate[] = []
1120
+ for (const der of chain) {
1121
+ const c: X509Certificate | null = x509ParseCertificate(der)
1122
+ if (c === null) {
1123
+ return new PkiResult(PKI_ERR_MALFORMED, none)
1124
+ }
1125
+ if (
1126
+ toI32(c.sanDns.length) + toI32(c.sanIp.length) > PKI_MAX_NAMES ||
1127
+ toI32(c.ncPermitted.length) + toI32(c.ncExcluded.length) > PKI_MAX_SUBTREES
1128
+ ) {
1129
+ return new PkiResult(PKI_ERR_BUDGET, none)
1130
+ }
1131
+ certs.push(c)
1132
+ }
1133
+ const leaf: X509Certificate = certs[0]
1134
+ const leafCode: i32 = pkiLeafCode(leaf, hostname, nowMs)
1135
+ if (leafCode !== PKI_OK) {
1136
+ return new PkiResult(leafCode, none)
1137
+ }
1138
+ const pool: X509Certificate[] = []
1139
+ for (let i: i32 = 1; i < toI32(certs.length); i++) {
1140
+ pool.push(certs[i])
1141
+ }
1142
+ const ip: u8[] | null = pkiParseIp(pkiStripDot(hostname))
1143
+ const dns: string | null = ip === null ? pkiHostDns(hostname) : null
1144
+ const s = new PkiSearch(store, pool, dns === null ? "" : dns, nowMs)
1145
+ s.path.push(leaf)
1146
+ if (pkiExtend(s, leaf)) {
1147
+ const profile: i32 = pkiLeafProfileCode(leaf)
1148
+ return profile === PKI_OK ? new PkiResult(PKI_OK, s.path) : new PkiResult(profile, none)
1149
+ }
1150
+ return new PkiResult(s.spent ? PKI_ERR_BUDGET : s.bestCode, none)
1151
+ }
1152
+
1153
+ /** A sentence for `code`, for a log or an error message. */
1154
+ export const pkiReason = (code: i32): string => {
1155
+ if (code === PKI_OK) {
1156
+ return "the chain is trusted and names the host"
1157
+ }
1158
+ if (code === PKI_ERR_MALFORMED) {
1159
+ return "the chain is empty, or holds a certificate that does not parse or breaks the server profile"
1160
+ }
1161
+ if (code === PKI_ERR_EXPIRED) {
1162
+ return "a certificate has expired"
1163
+ }
1164
+ if (code === PKI_ERR_NOT_YET_VALID) {
1165
+ return "a certificate is not yet valid"
1166
+ }
1167
+ if (code === PKI_ERR_UNTRUSTED) {
1168
+ return "no path leads to a trusted root"
1169
+ }
1170
+ if (code === PKI_ERR_BAD_SIGNATURE) {
1171
+ return "a signature does not verify under its issuer's key"
1172
+ }
1173
+ if (code === PKI_ERR_NOT_CA) {
1174
+ return "an issuer is not a CA certificate, or the leaf is one"
1175
+ }
1176
+ if (code === PKI_ERR_PATH_LENGTH) {
1177
+ return "an issuer's path length constraint is exceeded"
1178
+ }
1179
+ if (code === PKI_ERR_KEY_USAGE) {
1180
+ return "a key usage does not allow its use here"
1181
+ }
1182
+ if (code === PKI_ERR_EXT_KEY_USAGE) {
1183
+ return "an extended key usage does not allow TLS server authentication"
1184
+ }
1185
+ if (code === PKI_ERR_UNKNOWN_CRITICAL) {
1186
+ return "a certificate has a critical extension that is not understood"
1187
+ }
1188
+ if (code === PKI_ERR_NAME_CONSTRAINTS) {
1189
+ return "a name is outside an issuer's name constraints"
1190
+ }
1191
+ if (code === PKI_ERR_HOSTNAME) {
1192
+ return "the certificate does not name the host"
1193
+ }
1194
+ if (code === PKI_ERR_WEAK_KEY) {
1195
+ return "an RSA key is shorter than 2048 bits or not a whole number of bytes"
1196
+ }
1197
+ if (code === PKI_ERR_UNSUPPORTED_ALGORITHM) {
1198
+ return "a signature algorithm is not supported"
1199
+ }
1200
+ if (code === PKI_ERR_CHAIN_TOO_LONG) {
1201
+ return "the chain or its path is too long"
1202
+ }
1203
+ if (code === PKI_ERR_BUDGET) {
1204
+ return "the chain asks for more work than one verification does"
1205
+ }
1206
+ return "unknown reason"
1207
+ }