@amritk/nish-x86_64-linux 0.24.0 → 0.26.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.
- package/bin/nish +0 -0
- package/docs/AI.md +45 -4
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/host.d.ts +12 -0
- package/runtime/core/host.mjs +74 -2
- package/runtime/nish.d.ts +148 -0
- package/runtime/nish.h +59 -0
- package/runtime/nish.mjs +25 -0
- package/runtime/node.mjs +136 -6
- package/runtime/runtime-net.c +44 -3
- package/runtime/runtime-os.c +351 -18
- package/scripts/build.sh +13 -3
- package/std/README.md +27 -10
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +132 -20
- package/std/compress/deflate.ts +638 -248
- package/std/compress/gzip.ts +20 -6
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +7 -2
- package/std/net/http1.ts +9 -9
- package/std/net/postgres.ts +259 -26
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/std/crypto/x509.ts
CHANGED
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* // derToPem(der, "CERTIFICATE") is the file a TLS stack loads.
|
|
18
18
|
*
|
|
19
19
|
* Written for this module from ITU-T X.690 (DER), RFC 5280 (the certificate
|
|
20
|
-
* profile), RFC 7468 (PEM's textual encoding), RFC 5915 and
|
|
21
|
-
* SEC1 and PKCS#8 private-key structures), RFC 5480 (the
|
|
22
|
-
* identifiers)
|
|
20
|
+
* profile and its extensions), RFC 7468 (PEM's textual encoding), RFC 5915 and
|
|
21
|
+
* RFC 5208/5958 (the SEC1 and PKCS#8 private-key structures), RFC 5480 (the
|
|
22
|
+
* elliptic-curve key identifiers), RFC 5758 (ECDSA with SHA-2), RFC 4055 (RSA
|
|
23
|
+
* PKCS#1 v1.5 and RSASSA-PSS identifiers) and RFC 8410 (Ed25519).
|
|
23
24
|
*
|
|
24
25
|
* **DER is read strictly.** X.690 §10 leaves one encoding per value, and a
|
|
25
26
|
* reader that accepts a second spelling gives a certificate two identities
|
|
@@ -28,10 +29,15 @@
|
|
|
28
29
|
* zero length octet), an INTEGER must be minimal in two's complement, a tag
|
|
29
30
|
* must fit in one octet, every element must end inside its parent, and a
|
|
30
31
|
* certificate must end where its outer SEQUENCE does. Anything else answers
|
|
31
|
-
* `null`, never a panic. The reader
|
|
32
|
-
*
|
|
33
|
-
* public key and the signature
|
|
34
|
-
* (`[1]`, `[2]`, `[3]`)
|
|
32
|
+
* `null`, never a panic. The reader takes the version, serial, algorithms,
|
|
33
|
+
* the issuer and subject Names (their RDNs checked, their bytes kept whole),
|
|
34
|
+
* validity, the subject public key and the signature, checks the order of what
|
|
35
|
+
* follows the key (`[1]`, `[2]`, `[3]`), and decodes the extensions a path
|
|
36
|
+
* validator needs: subjectAltName, basicConstraints, keyUsage, extKeyUsage,
|
|
37
|
+
* nameConstraints and the two key identifiers. A duplicate or malformed one
|
|
38
|
+
* refuses the certificate, and any other critical one sets `unknownCritical`.
|
|
39
|
+
* It parses and does not validate: no module in `std/` does RFC 5280 §6 path
|
|
40
|
+
* validation yet, so applying these is the caller's. The mint writes two extensions of its
|
|
35
41
|
* own (below).
|
|
36
42
|
*
|
|
37
43
|
* **PEM is RFC 7468's strict grammar**, with standard base64 (`+` and `/`,
|
|
@@ -106,18 +112,83 @@ export const X509_MAX_DAYS: i32 = 14
|
|
|
106
112
|
/** Longest serial number, in content octets of its INTEGER (RFC 5280 §4.1.2.2). */
|
|
107
113
|
export const X509_MAX_SERIAL: i32 = 20
|
|
108
114
|
|
|
115
|
+
/** `X509Certificate.sigAlg`: a signature algorithm this module does not name, or one with parameters it does not accept. */
|
|
116
|
+
export const X509_SIG_UNKNOWN: i32 = 0
|
|
117
|
+
/** ecdsa-with-SHA256, parameters absent (RFC 5758 §3.2). */
|
|
118
|
+
export const X509_SIG_ECDSA_SHA256: i32 = 1
|
|
119
|
+
/** ecdsa-with-SHA384, parameters absent (RFC 5758 §3.2). */
|
|
120
|
+
export const X509_SIG_ECDSA_SHA384: i32 = 2
|
|
121
|
+
/** sha256WithRSAEncryption, parameters NULL (RFC 4055 §5). */
|
|
122
|
+
export const X509_SIG_RSA_PKCS1_SHA256: i32 = 3
|
|
123
|
+
/** sha384WithRSAEncryption, parameters NULL. */
|
|
124
|
+
export const X509_SIG_RSA_PKCS1_SHA384: i32 = 4
|
|
125
|
+
/** sha512WithRSAEncryption, parameters NULL. */
|
|
126
|
+
export const X509_SIG_RSA_PKCS1_SHA512: i32 = 5
|
|
127
|
+
/**
|
|
128
|
+
* RSASSA-PSS (RFC 4055 §3.1) with a SHA-2 hash, MGF1 over that same hash and
|
|
129
|
+
* the trailer 1; the hash is in `pssHash` and the salt length in `pssSaltLen`.
|
|
130
|
+
*/
|
|
131
|
+
export const X509_SIG_RSA_PSS: i32 = 6
|
|
132
|
+
/** Ed25519, parameters absent (RFC 8410 §3). */
|
|
133
|
+
export const X509_SIG_ED25519: i32 = 7
|
|
134
|
+
/** sha1WithRSAEncryption, parameters NULL: recognised so that a verifier can refuse it by name. */
|
|
135
|
+
export const X509_SIG_RSA_PKCS1_SHA1: i32 = 8
|
|
136
|
+
|
|
137
|
+
/** `X509Certificate.keyType`: a key algorithm this module does not name. */
|
|
138
|
+
export const X509_KEY_UNKNOWN: i32 = 0
|
|
139
|
+
/** id-ecPublicKey on secp256r1 (RFC 5480 §2.1.1): the same test as `p256`. */
|
|
140
|
+
export const X509_KEY_P256: i32 = 1
|
|
141
|
+
/** id-ecPublicKey on secp384r1 (RFC 5480 §2.1.1.1). */
|
|
142
|
+
export const X509_KEY_P384: i32 = 2
|
|
143
|
+
/** rsaEncryption with NULL parameters (RFC 3279 §2.3.1); `publicKey` is the RSAPublicKey DER. */
|
|
144
|
+
export const X509_KEY_RSA: i32 = 3
|
|
145
|
+
/** id-Ed25519 with parameters absent (RFC 8410 §3). */
|
|
146
|
+
export const X509_KEY_ED25519: i32 = 4
|
|
147
|
+
|
|
148
|
+
/** `X509Certificate.pssHash`: SHA-256. */
|
|
149
|
+
export const X509_HASH_SHA256: i32 = 1
|
|
150
|
+
/** SHA-384. */
|
|
151
|
+
export const X509_HASH_SHA384: i32 = 2
|
|
152
|
+
/** SHA-512. */
|
|
153
|
+
export const X509_HASH_SHA512: i32 = 3
|
|
154
|
+
|
|
155
|
+
/** `X509GeneralSubtree.kind`: a dNSName, in `dns`. */
|
|
156
|
+
export const X509_GN_DNS: i32 = 1
|
|
157
|
+
/** An iPAddress range, `ip` under `mask`. */
|
|
158
|
+
export const X509_GN_IP: i32 = 2
|
|
159
|
+
/** A directoryName, the Name's DER in `dirName`. */
|
|
160
|
+
export const X509_GN_DIR: i32 = 3
|
|
161
|
+
/** Any other GeneralName form: rfc822Name, URI, otherName and the rest, which this module keeps no value for. */
|
|
162
|
+
export const X509_GN_OTHER: i32 = 4
|
|
163
|
+
|
|
164
|
+
/** `X509Certificate.keyUsage` bits (RFC 5280 §4.2.1.3): bit 0 is digitalSignature. */
|
|
165
|
+
export const X509_KU_DIGITAL_SIGNATURE: i32 = 1
|
|
166
|
+
/** keyCertSign, bit 5. */
|
|
167
|
+
export const X509_KU_KEY_CERT_SIGN: i32 = 32
|
|
168
|
+
/** cRLSign, bit 6. */
|
|
169
|
+
export const X509_KU_CRL_SIGN: i32 = 64
|
|
170
|
+
|
|
109
171
|
/** The longest common name minted, in bytes (RFC 5280 Appendix A's `ub-common-name`). */
|
|
110
172
|
const X509_MAX_COMMON_NAME: i32 = 64
|
|
111
173
|
|
|
174
|
+
/**
|
|
175
|
+
* The most extensions a certificate may carry. A Web PKI certificate carries
|
|
176
|
+
* fewer than 20; the cap bounds the repeated-extnID search, which compares
|
|
177
|
+
* each extension with every one before it.
|
|
178
|
+
*/
|
|
179
|
+
const X509_MAX_EXTENSIONS: i32 = 64
|
|
180
|
+
|
|
112
181
|
/** What a time field reads as when it is not a valid RFC 5280 time: before any year the reader accepts. */
|
|
113
182
|
const X509_NO_TIME: i64 = -1000000000000
|
|
114
183
|
|
|
115
184
|
const X509_MS_PER_DAY: i64 = 86400000
|
|
116
185
|
|
|
117
186
|
// DER tags (X.690 §8, and the context tags RFC 5280 gives them).
|
|
187
|
+
const X509_TAG_BOOLEAN: i32 = 0x01
|
|
118
188
|
const X509_TAG_INTEGER: i32 = 0x02
|
|
119
189
|
const X509_TAG_BIT_STRING: i32 = 0x03
|
|
120
190
|
const X509_TAG_OCTET_STRING: i32 = 0x04
|
|
191
|
+
const X509_TAG_NULL: i32 = 0x05
|
|
121
192
|
const X509_TAG_OID: i32 = 0x06
|
|
122
193
|
const X509_TAG_UTF8_STRING: i32 = 0x0c
|
|
123
194
|
const X509_TAG_UTC_TIME: i32 = 0x17
|
|
@@ -125,12 +196,38 @@ const X509_TAG_GENERALIZED_TIME: i32 = 0x18
|
|
|
125
196
|
const X509_TAG_SEQUENCE: i32 = 0x30
|
|
126
197
|
const X509_TAG_SET: i32 = 0x31
|
|
127
198
|
|
|
199
|
+
/**
|
|
200
|
+
* One subtree of a nameConstraints extension (RFC 5280 §4.2.1.10): its base
|
|
201
|
+
* name's form in `kind`, and the value of the forms this module reads. A
|
|
202
|
+
* subtree whose `minimum` or `maximum` is present keeps its base here and sets
|
|
203
|
+
* the certificate's `ncUnsupported`.
|
|
204
|
+
*/
|
|
205
|
+
export class X509GeneralSubtree {
|
|
206
|
+
/** `X509_GN_DNS`, `X509_GN_IP`, `X509_GN_DIR` or `X509_GN_OTHER`. */
|
|
207
|
+
kind: i32 = 0
|
|
208
|
+
/** For a dNSName, its IA5 bytes exactly as encoded, with no case folded; `""` may be the whole tree. */
|
|
209
|
+
dns: string = ""
|
|
210
|
+
/** For an iPAddress, the address: 4 octets or 16. */
|
|
211
|
+
ip: u8[]
|
|
212
|
+
/** For an iPAddress, the mask, as long as `ip` and contiguous. */
|
|
213
|
+
mask: u8[]
|
|
214
|
+
/** For a directoryName, the Name's DER, whole. */
|
|
215
|
+
dirName: u8[]
|
|
216
|
+
|
|
217
|
+
constructor() {
|
|
218
|
+
this.ip = []
|
|
219
|
+
this.mask = []
|
|
220
|
+
this.dirName = []
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
128
224
|
/**
|
|
129
225
|
* One certificate, as `x509ParseCertificate` and `x509ParseChain` read it.
|
|
130
226
|
* The byte fields are copies, so the certificate outlives the text it was read
|
|
131
227
|
* from. Times are milliseconds since 1970-01-01 UTC, as `Date.now()` counts.
|
|
132
228
|
*/
|
|
133
229
|
export class X509Certificate {
|
|
230
|
+
// Widest first, which leaves no padding: pointers and i64s, then i32s, then booleans.
|
|
134
231
|
/** The whole certificate, as its SHA-256 is taken. */
|
|
135
232
|
der: u8[]
|
|
136
233
|
/** The DER of `tbsCertificate`, the bytes the signature covers. */
|
|
@@ -149,10 +246,61 @@ export class X509Certificate {
|
|
|
149
246
|
notBefore: i64 = 0
|
|
150
247
|
/** The last instant it is valid, inclusive. */
|
|
151
248
|
notAfter: i64 = 0
|
|
249
|
+
/** subjectAltName's dNSNames, the IA5 bytes exactly as encoded, with no case folded. */
|
|
250
|
+
sanDns: string[]
|
|
251
|
+
/** subjectAltName's iPAddresses, 4 octets or 16 each. */
|
|
252
|
+
sanIp: u8[][]
|
|
253
|
+
/** nameConstraints' permittedSubtrees, in order. */
|
|
254
|
+
ncPermitted: X509GeneralSubtree[]
|
|
255
|
+
/** nameConstraints' excludedSubtrees, in order. */
|
|
256
|
+
ncExcluded: X509GeneralSubtree[]
|
|
257
|
+
/** The subjectKeyIdentifier (RFC 5280 §4.2.1.2), or `null`. */
|
|
258
|
+
skid: u8[] | null = null
|
|
259
|
+
/** The authorityKeyIdentifier's keyIdentifier (RFC 5280 §4.2.1.1), or `null`. */
|
|
260
|
+
akid: u8[] | null = null
|
|
261
|
+
/** The version as encoded: 0 for v1, 1 for v2, 2 for v3. */
|
|
262
|
+
version: i32 = 0
|
|
263
|
+
/** The signature algorithm, an `X509_SIG_` constant. */
|
|
264
|
+
sigAlg: i32 = 0
|
|
265
|
+
/** For RSASSA-PSS, the hash of the message and of MGF1, an `X509_HASH_` constant; else 0. */
|
|
266
|
+
pssHash: i32 = 0
|
|
267
|
+
/** For RSASSA-PSS, the salt length in octets; else 0. */
|
|
268
|
+
pssSaltLen: i32 = 0
|
|
269
|
+
/** The subject key's algorithm, an `X509_KEY_` constant. */
|
|
270
|
+
keyType: i32 = 0
|
|
271
|
+
/**
|
|
272
|
+
* How many of subjectAltName's names are another form: rfc822Name, URI,
|
|
273
|
+
* directoryName and the rest. Each must still be well formed: an rfc822Name
|
|
274
|
+
* or URI is an IA5String like a dNSName, so one with an octet of 0x80 or
|
|
275
|
+
* more, or a NUL, refuses the certificate rather than being counted.
|
|
276
|
+
*/
|
|
277
|
+
sanOther: i32 = 0
|
|
278
|
+
/** basicConstraints' pathLenConstraint, or -1 when it is absent (no limit) or the extension is. */
|
|
279
|
+
bcPathLen: i32 = -1
|
|
280
|
+
/** The keyUsage bits (RFC 5280 §4.2.1.3), bit 0 digitalSignature to bit 8 decipherOnly, or -1 when absent. */
|
|
281
|
+
keyUsage: i32 = -1
|
|
152
282
|
/** Whether the key is id-ecPublicKey on prime256v1 (RFC 5480 §2.1.1), so `publicKey` is a P-256 point. */
|
|
153
283
|
p256: boolean = false
|
|
154
284
|
/** Whether the signature algorithm is ecdsa-with-SHA256 with its parameters absent (RFC 5758 §3.2). */
|
|
155
285
|
ecdsaSha256: boolean = false
|
|
286
|
+
/** Whether a subjectAltName extension is present (RFC 5280 §4.2.1.6). */
|
|
287
|
+
sanPresent: boolean = false
|
|
288
|
+
/** Whether a basicConstraints extension is present (RFC 5280 §4.2.1.9). */
|
|
289
|
+
bcPresent: boolean = false
|
|
290
|
+
/** basicConstraints' cA flag. */
|
|
291
|
+
bcCa: boolean = false
|
|
292
|
+
/** Whether an extKeyUsage extension is present (RFC 5280 §4.2.1.12). */
|
|
293
|
+
ekuPresent: boolean = false
|
|
294
|
+
/** Whether extKeyUsage names id-kp-serverAuth. */
|
|
295
|
+
ekuServerAuth: boolean = false
|
|
296
|
+
/** Whether extKeyUsage names anyExtendedKeyUsage. */
|
|
297
|
+
ekuAny: boolean = false
|
|
298
|
+
/** Whether a nameConstraints extension is present (RFC 5280 §4.2.1.10). */
|
|
299
|
+
ncPresent: boolean = false
|
|
300
|
+
/** Whether any subtree is `X509_GN_OTHER` or carries a `minimum` or `maximum`, which a validator must refuse or understand. */
|
|
301
|
+
ncUnsupported: boolean = false
|
|
302
|
+
/** Whether a critical extension is one this module does not decode: RFC 5280 §4.2 has a verifier refuse it. */
|
|
303
|
+
unknownCritical: boolean = false
|
|
156
304
|
|
|
157
305
|
constructor() {
|
|
158
306
|
this.der = []
|
|
@@ -162,6 +310,10 @@ export class X509Certificate {
|
|
|
162
310
|
this.subject = []
|
|
163
311
|
this.publicKey = []
|
|
164
312
|
this.signature = []
|
|
313
|
+
this.sanDns = []
|
|
314
|
+
this.sanIp = []
|
|
315
|
+
this.ncPermitted = []
|
|
316
|
+
this.ncExcluded = []
|
|
165
317
|
}
|
|
166
318
|
}
|
|
167
319
|
|
|
@@ -194,12 +346,24 @@ class X509DerElement {
|
|
|
194
346
|
* refused before they can overflow.
|
|
195
347
|
*/
|
|
196
348
|
const x509DerRead = (der: u8[], at: i32, limit: i32): X509DerElement | null => {
|
|
197
|
-
|
|
349
|
+
const e = new X509DerElement(0, 0, 0, 0)
|
|
350
|
+
if (!x509DerReadInto(der, at, limit, e)) {
|
|
198
351
|
return null
|
|
199
352
|
}
|
|
353
|
+
return e
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* `x509DerRead` into `into`, answering whether it read one: what a loop uses
|
|
358
|
+
* so a pass allocates nothing it leaves behind. `into` is unspecified on `false`.
|
|
359
|
+
*/
|
|
360
|
+
const x509DerReadInto = (der: u8[], at: i32, limit: i32, into: X509DerElement): boolean => {
|
|
361
|
+
if (at < 0 || limit > toI32(der.length) || at > limit - 2) {
|
|
362
|
+
return false
|
|
363
|
+
}
|
|
200
364
|
const tag: i32 = toI32(der[at])
|
|
201
365
|
if ((tag & 0x1f) === 0x1f) {
|
|
202
|
-
return
|
|
366
|
+
return false
|
|
203
367
|
}
|
|
204
368
|
let length: i32 = toI32(der[at + 1])
|
|
205
369
|
let start: i32 = at + 2
|
|
@@ -207,24 +371,28 @@ const x509DerRead = (der: u8[], at: i32, limit: i32): X509DerElement | null => {
|
|
|
207
371
|
const count: i32 = length & 0x7f
|
|
208
372
|
// 0x80 alone is BER's indefinite length, which DER forbids.
|
|
209
373
|
if (count < 1 || count > 4 || count > limit - start) {
|
|
210
|
-
return
|
|
374
|
+
return false
|
|
211
375
|
}
|
|
212
376
|
if (toI32(der[start]) === 0 || (count === 4 && toI32(der[start]) >= 0x80)) {
|
|
213
|
-
return
|
|
377
|
+
return false
|
|
214
378
|
}
|
|
215
379
|
length = 0
|
|
216
380
|
for (let k: i32 = 0; k < count; k++) {
|
|
217
381
|
length = (length << 8) | toI32(der[start + k])
|
|
218
382
|
}
|
|
219
383
|
if (length < 0x80) {
|
|
220
|
-
return
|
|
384
|
+
return false
|
|
221
385
|
}
|
|
222
386
|
start = start + count
|
|
223
387
|
}
|
|
224
388
|
if (length > limit - start) {
|
|
225
|
-
return
|
|
389
|
+
return false
|
|
226
390
|
}
|
|
227
|
-
|
|
391
|
+
into.tag = tag
|
|
392
|
+
into.at = at
|
|
393
|
+
into.start = start
|
|
394
|
+
into.end = start + length
|
|
395
|
+
return true
|
|
228
396
|
}
|
|
229
397
|
|
|
230
398
|
/** `x509DerRead`, answering `null` also when the element's tag is not `tag`. */
|
|
@@ -236,6 +404,10 @@ const x509DerExpect = (der: u8[], at: i32, limit: i32, tag: i32): X509DerElement
|
|
|
236
404
|
return e
|
|
237
405
|
}
|
|
238
406
|
|
|
407
|
+
/** `x509DerReadInto`, answering `false` also when the element's tag is not `tag`. */
|
|
408
|
+
const x509DerExpectInto = (der: u8[], at: i32, limit: i32, tag: i32, into: X509DerElement): boolean =>
|
|
409
|
+
x509DerReadInto(der, at, limit, into) && into.tag === tag
|
|
410
|
+
|
|
239
411
|
/**
|
|
240
412
|
* The identifier octet at `der[at]` when `at` is inside `der[.. limit)`, else
|
|
241
413
|
* `-1`: how an optional element is looked for.
|
|
@@ -460,6 +632,65 @@ const x509OidPrime256v1 = (): u8[] => x509DerOidContents([1, 2, 840, 10045, 3, 1
|
|
|
460
632
|
/** id-at-commonName, 2.5.4.3 (RFC 5280 Appendix A). */
|
|
461
633
|
const x509OidCommonName = (): u8[] => x509DerOidContents([2, 5, 4, 3])
|
|
462
634
|
|
|
635
|
+
/** ecdsa-with-SHA384, 1.2.840.10045.4.3.3 (RFC 5758 §3.2). */
|
|
636
|
+
const x509OidEcdsaSha384 = (): u8[] => x509DerOidContents([1, 2, 840, 10045, 4, 3, 3])
|
|
637
|
+
|
|
638
|
+
/** secp384r1, 1.3.132.0.34 (RFC 5480 §2.1.1.1). */
|
|
639
|
+
const x509OidSecp384r1 = (): u8[] => x509DerOidContents([1, 3, 132, 0, 34])
|
|
640
|
+
|
|
641
|
+
/** The PKCS #1 arc 1.2.840.113549.1.1.`n` (RFC 4055 §2.1 and §5): 1 rsaEncryption, 5, 11, 12 and 13 the PKCS#1 v1.5 signatures, 8 MGF1, 10 RSASSA-PSS. */
|
|
642
|
+
const x509OidPkcs1 = (n: i32): u8[] => x509DerOidContents([1, 2, 840, 113549, 1, 1, n])
|
|
643
|
+
|
|
644
|
+
/** id-Ed25519, 1.3.101.112 (RFC 8410 §3). */
|
|
645
|
+
const x509OidEd25519 = (): u8[] => x509DerOidContents([1, 3, 101, 112])
|
|
646
|
+
|
|
647
|
+
/** The NIST hash arc 2.16.840.1.101.3.4.2.`n` (RFC 4055 §2.1): 1 SHA-256, 2 SHA-384, 3 SHA-512. */
|
|
648
|
+
const x509OidNistHash = (n: i32): u8[] => x509DerOidContents([2, 16, 840, 1, 101, 3, 4, 2, n])
|
|
649
|
+
|
|
650
|
+
// The extensions decoded, by their arc under 2.5.29 (RFC 5280 §4.2.1).
|
|
651
|
+
const X509_CE_SUBJECT_KEY_ID: i32 = 14
|
|
652
|
+
const X509_CE_KEY_USAGE: i32 = 15
|
|
653
|
+
const X509_CE_SUBJECT_ALT_NAME: i32 = 17
|
|
654
|
+
const X509_CE_BASIC_CONSTRAINTS: i32 = 19
|
|
655
|
+
const X509_CE_NAME_CONSTRAINTS: i32 = 30
|
|
656
|
+
const X509_CE_AUTHORITY_KEY_ID: i32 = 35
|
|
657
|
+
const X509_CE_EXT_KEY_USAGE: i32 = 37
|
|
658
|
+
|
|
659
|
+
/** What `x509ReadExtension` answers: an extension it does not decode, one it decoded, one that is malformed. */
|
|
660
|
+
const X509_EXT_UNKNOWN: i32 = 0
|
|
661
|
+
const X509_EXT_SOUND: i32 = 1
|
|
662
|
+
const X509_EXT_MALFORMED: i32 = 2
|
|
663
|
+
|
|
664
|
+
/** id-kp-serverAuth, 1.3.6.1.5.5.7.3.1 (RFC 5280 §4.2.1.12). */
|
|
665
|
+
const x509OidServerAuth = (): u8[] => x509DerOidContents([1, 3, 6, 1, 5, 5, 7, 3, 1])
|
|
666
|
+
|
|
667
|
+
/** anyExtendedKeyUsage, 2.5.29.37.0 (RFC 5280 §4.2.1.12). */
|
|
668
|
+
const x509OidAnyEku = (): u8[] => x509DerOidContents([2, 5, 29, 37, 0])
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Whether an OID's contents are X.690 §8.19's: at least one octet, the last
|
|
672
|
+
* with its top bit clear, and no subidentifier starting with the padding
|
|
673
|
+
* octet `0x80` — so one OID has one spelling, and a duplicate extension cannot
|
|
674
|
+
* hide behind a second.
|
|
675
|
+
*/
|
|
676
|
+
const x509DerOidSound = (der: u8[], e: X509DerElement): boolean => {
|
|
677
|
+
if (e.tag !== X509_TAG_OID || e.end <= e.start || toI32(der[e.end - 1]) >= 0x80) {
|
|
678
|
+
return false
|
|
679
|
+
}
|
|
680
|
+
let first: boolean = true
|
|
681
|
+
for (let k: i32 = e.start; k < e.end; k++) {
|
|
682
|
+
// Always true for an element `x509DerRead` gave, and what lets the load drop its check.
|
|
683
|
+
if (k >= 0 && k < toI32(der.length)) {
|
|
684
|
+
const b: i32 = toI32(der[k])
|
|
685
|
+
if (first && b === 0x80) {
|
|
686
|
+
return false
|
|
687
|
+
}
|
|
688
|
+
first = b < 0x80
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return true
|
|
692
|
+
}
|
|
693
|
+
|
|
463
694
|
/**
|
|
464
695
|
* Whether `der[start .. end)` is exactly one OID element equal to `oid` and
|
|
465
696
|
* nothing after it.
|
|
@@ -488,11 +719,162 @@ const x509DerIsAlgorithm = (der: u8[], alg: X509DerElement, oid: u8[], params: u
|
|
|
488
719
|
}
|
|
489
720
|
|
|
490
721
|
/**
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* sign octet is set aside.
|
|
722
|
+
* Whether an AlgorithmIdentifier SEQUENCE is `{ oid, NULL }`, the form RFC
|
|
723
|
+
* 4055 §5 and RFC 3279 §2.3.1 give the PKCS#1 v1.5 signatures and the RSA key.
|
|
494
724
|
*/
|
|
495
|
-
const
|
|
725
|
+
const x509DerIsAlgorithmNull = (der: u8[], alg: X509DerElement, oid: u8[]): boolean => {
|
|
726
|
+
if (alg.tag !== X509_TAG_SEQUENCE) {
|
|
727
|
+
return false
|
|
728
|
+
}
|
|
729
|
+
const id: X509DerElement | null = x509DerExpect(der, alg.start, alg.end, X509_TAG_OID)
|
|
730
|
+
if (id === null || !x509DerContentsAre(der, id, oid)) {
|
|
731
|
+
return false
|
|
732
|
+
}
|
|
733
|
+
const params: X509DerElement | null = x509DerExpect(der, id.end, alg.end, X509_TAG_NULL)
|
|
734
|
+
return params !== null && params.start === params.end && params.end === alg.end
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* The `X509_HASH_` constant of a hash AlgorithmIdentifier in `der[start ..
|
|
739
|
+
* end)`, exactly one element: SHA-256, SHA-384 or SHA-512, with its parameters
|
|
740
|
+
* absent or NULL, both of which RFC 4055 §2.1 has a reader accept. 0 otherwise.
|
|
741
|
+
*/
|
|
742
|
+
const x509DerHashAlgorithm = (der: u8[], start: i32, end: i32): i32 => {
|
|
743
|
+
const alg: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
744
|
+
if (alg === null || alg.end !== end) {
|
|
745
|
+
return 0
|
|
746
|
+
}
|
|
747
|
+
for (let n: i32 = 1; n <= 3; n++) {
|
|
748
|
+
const oid: u8[] = x509OidNistHash(n)
|
|
749
|
+
if (x509DerIsAlgorithm(der, alg, oid, []) || x509DerIsAlgorithmNull(der, alg, oid)) {
|
|
750
|
+
return n
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return 0
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/** The signature algorithm `X509Certificate` reports, and for RSASSA-PSS its hash and salt length. */
|
|
757
|
+
class X509SigAlg {
|
|
758
|
+
alg: i32 = 0
|
|
759
|
+
hash: i32 = 0
|
|
760
|
+
salt: i32 = 0
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* RSASSA-PSS-params (RFC 4055 §3.1) in `der[start .. end)`, the contents of
|
|
765
|
+
* their SEQUENCE: `[0]` hashAlgorithm, `[1]` maskGenAlgorithm, `[2]`
|
|
766
|
+
* saltLength and `[3]` trailerField, each optional and in that order. Named
|
|
767
|
+
* only when the hash is SHA-2 (so `[0]` is present, its DEFAULT being SHA-1),
|
|
768
|
+
* the mask is MGF1 over that same hash, the salt length is 0 to 127 (20 when
|
|
769
|
+
* absent) and the trailer is 1; anything else is `X509_SIG_UNKNOWN`, which a
|
|
770
|
+
* verifier refuses. DER leaves a DEFAULT out (X.690 §11.5), so a salt length
|
|
771
|
+
* of 20 written out, or any trailerField at all (its one value, 1, being the
|
|
772
|
+
* DEFAULT), is unknown too.
|
|
773
|
+
*/
|
|
774
|
+
const x509PssParams = (der: u8[], start: i32, end: i32): X509SigAlg => {
|
|
775
|
+
const out = new X509SigAlg()
|
|
776
|
+
const hashWrap: X509DerElement | null = x509DerExpect(der, start, end, 0xa0)
|
|
777
|
+
if (hashWrap === null) {
|
|
778
|
+
return out
|
|
779
|
+
}
|
|
780
|
+
const hash: i32 = x509DerHashAlgorithm(der, hashWrap.start, hashWrap.end)
|
|
781
|
+
const mgfWrap: X509DerElement | null = x509DerExpect(der, hashWrap.end, end, 0xa1)
|
|
782
|
+
if (hash === 0 || mgfWrap === null) {
|
|
783
|
+
return out
|
|
784
|
+
}
|
|
785
|
+
// MaskGenAlgorithm is `SEQUENCE { id-mgf1, HashAlgorithm }`, and the hash
|
|
786
|
+
// must be the message's (RFC 4055 §3.1 recommends it; RFC 8446 §4.2.3 and
|
|
787
|
+
// the CA/Browser Forum require it).
|
|
788
|
+
const mgf: X509DerElement | null = x509DerExpect(der, mgfWrap.start, mgfWrap.end, X509_TAG_SEQUENCE)
|
|
789
|
+
if (mgf === null || mgf.end !== mgfWrap.end) {
|
|
790
|
+
return out
|
|
791
|
+
}
|
|
792
|
+
const mgfId: X509DerElement | null = x509DerExpect(der, mgf.start, mgf.end, X509_TAG_OID)
|
|
793
|
+
if (mgfId === null || !x509DerContentsAre(der, mgfId, x509OidPkcs1(8))) {
|
|
794
|
+
return out
|
|
795
|
+
}
|
|
796
|
+
if (x509DerHashAlgorithm(der, mgfId.end, mgf.end) !== hash) {
|
|
797
|
+
return out
|
|
798
|
+
}
|
|
799
|
+
let at: i32 = mgfWrap.end
|
|
800
|
+
let salt: i32 = 20
|
|
801
|
+
if (x509DerPeek(der, at, end) === 0xa2) {
|
|
802
|
+
const saltWrap: X509DerElement | null = x509DerRead(der, at, end)
|
|
803
|
+
if (saltWrap === null) {
|
|
804
|
+
return out
|
|
805
|
+
}
|
|
806
|
+
const value: X509DerElement | null = x509DerRead(der, saltWrap.start, saltWrap.end)
|
|
807
|
+
if (value === null || value.end !== saltWrap.end) {
|
|
808
|
+
return out
|
|
809
|
+
}
|
|
810
|
+
salt = x509DerSmallInteger(der, value)
|
|
811
|
+
if (salt < 0 || salt === 20) {
|
|
812
|
+
return out
|
|
813
|
+
}
|
|
814
|
+
at = saltWrap.end
|
|
815
|
+
}
|
|
816
|
+
if (at !== end) {
|
|
817
|
+
return out
|
|
818
|
+
}
|
|
819
|
+
out.alg = X509_SIG_RSA_PSS
|
|
820
|
+
out.hash = hash
|
|
821
|
+
out.salt = salt
|
|
822
|
+
return out
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/** The signature algorithm an AlgorithmIdentifier names, with the parameters RFC 5758, RFC 4055 and RFC 8410 give each. */
|
|
826
|
+
const x509SignatureAlgorithm = (der: u8[], alg: X509DerElement): X509SigAlg => {
|
|
827
|
+
const out = new X509SigAlg()
|
|
828
|
+
if (x509DerIsAlgorithm(der, alg, x509OidEcdsaSha256(), [])) {
|
|
829
|
+
out.alg = X509_SIG_ECDSA_SHA256
|
|
830
|
+
} else if (x509DerIsAlgorithm(der, alg, x509OidEcdsaSha384(), [])) {
|
|
831
|
+
out.alg = X509_SIG_ECDSA_SHA384
|
|
832
|
+
} else if (x509DerIsAlgorithmNull(der, alg, x509OidPkcs1(11))) {
|
|
833
|
+
out.alg = X509_SIG_RSA_PKCS1_SHA256
|
|
834
|
+
} else if (x509DerIsAlgorithmNull(der, alg, x509OidPkcs1(12))) {
|
|
835
|
+
out.alg = X509_SIG_RSA_PKCS1_SHA384
|
|
836
|
+
} else if (x509DerIsAlgorithmNull(der, alg, x509OidPkcs1(13))) {
|
|
837
|
+
out.alg = X509_SIG_RSA_PKCS1_SHA512
|
|
838
|
+
} else if (x509DerIsAlgorithmNull(der, alg, x509OidPkcs1(5))) {
|
|
839
|
+
out.alg = X509_SIG_RSA_PKCS1_SHA1
|
|
840
|
+
} else if (x509DerIsAlgorithm(der, alg, x509OidEd25519(), [])) {
|
|
841
|
+
out.alg = X509_SIG_ED25519
|
|
842
|
+
} else {
|
|
843
|
+
const id: X509DerElement | null = x509DerExpect(der, alg.start, alg.end, X509_TAG_OID)
|
|
844
|
+
if (id !== null && x509DerContentsAre(der, id, x509OidPkcs1(10))) {
|
|
845
|
+
// The parameters are required: RFC 4055 §3.1 gives them no absent form.
|
|
846
|
+
const params: X509DerElement | null = x509DerExpect(der, id.end, alg.end, X509_TAG_SEQUENCE)
|
|
847
|
+
if (params !== null && params.end === alg.end) {
|
|
848
|
+
return x509PssParams(der, params.start, params.end)
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return out
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/** The `X509_KEY_` constant a SubjectPublicKeyInfo's AlgorithmIdentifier names. */
|
|
856
|
+
const x509KeyType = (der: u8[], alg: X509DerElement): i32 => {
|
|
857
|
+
if (x509DerIsAlgorithm(der, alg, x509OidEcPublicKey(), x509OidPrime256v1())) {
|
|
858
|
+
return X509_KEY_P256
|
|
859
|
+
}
|
|
860
|
+
if (x509DerIsAlgorithm(der, alg, x509OidEcPublicKey(), x509OidSecp384r1())) {
|
|
861
|
+
return X509_KEY_P384
|
|
862
|
+
}
|
|
863
|
+
if (x509DerIsAlgorithmNull(der, alg, x509OidPkcs1(1))) {
|
|
864
|
+
return X509_KEY_RSA
|
|
865
|
+
}
|
|
866
|
+
if (x509DerIsAlgorithm(der, alg, x509OidEd25519(), [])) {
|
|
867
|
+
return X509_KEY_ED25519
|
|
868
|
+
}
|
|
869
|
+
return X509_KEY_UNKNOWN
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* A minimal, non-negative INTEGER right-aligned into `out[off .. off +
|
|
874
|
+
* width)`, or `false` when it is negative, not minimal or wider than `width`
|
|
875
|
+
* octets once a sign octet is set aside.
|
|
876
|
+
*/
|
|
877
|
+
const x509DerUnsignedInto = (der: u8[], e: X509DerElement, out: u8[], off: i32, width: i32): boolean => {
|
|
496
878
|
if (!x509DerIntegerMinimal(der, e) || toI32(der[e.start]) >= 0x80) {
|
|
497
879
|
return false
|
|
498
880
|
}
|
|
@@ -501,23 +883,27 @@ const x509DerUnsignedInto = (der: u8[], e: X509DerElement, out: u8[], off: i32):
|
|
|
501
883
|
start = start + 1
|
|
502
884
|
}
|
|
503
885
|
const n: i32 = e.end - start
|
|
504
|
-
if (n >
|
|
886
|
+
if (n > width) {
|
|
505
887
|
return false
|
|
506
888
|
}
|
|
507
889
|
for (let k: i32 = 0; k < n; k++) {
|
|
508
|
-
out[off +
|
|
890
|
+
out[off + width - n + k] = der[start + k]
|
|
509
891
|
}
|
|
510
892
|
return true
|
|
511
893
|
}
|
|
512
894
|
|
|
513
895
|
/**
|
|
514
|
-
* `r || s`,
|
|
515
|
-
* INTEGER s }` (RFC 5480 §2.2 by way of RFC 3279 §2.2.3), or `null` unless
|
|
516
|
-
* is exactly one with both integers minimal,
|
|
517
|
-
*
|
|
518
|
-
* `
|
|
896
|
+
* `r || s`, `width` octets each, out of an ECDSA-Sig-Value `SEQUENCE { INTEGER
|
|
897
|
+
* r, INTEGER s }` (RFC 5480 §2.2 by way of RFC 3279 §2.2.3), or `null` unless
|
|
898
|
+
* it is exactly one with both integers minimal, not negative and below
|
|
899
|
+
* 2^(8 * width), and `width` is 32 (P-256) or 48 (P-384). This is the form a
|
|
900
|
+
* TLS `CertificateVerify` or an X.509 signature carries; that each value is
|
|
901
|
+
* below the curve's order is the curve's check.
|
|
519
902
|
*/
|
|
520
|
-
export const
|
|
903
|
+
export const x509DerSignatureRSWidth = (sig: u8[], width: i32): u8[] | null => {
|
|
904
|
+
if (width !== 32 && width !== 48) {
|
|
905
|
+
return null
|
|
906
|
+
}
|
|
521
907
|
const total: i32 = toI32(sig.length)
|
|
522
908
|
const seq: X509DerElement | null = x509DerExpect(sig, 0, total, X509_TAG_SEQUENCE)
|
|
523
909
|
if (seq === null || seq.end !== total) {
|
|
@@ -531,13 +917,19 @@ export const x509DerSignatureRS = (sig: u8[]): u8[] | null => {
|
|
|
531
917
|
if (s === null || s.end !== seq.end) {
|
|
532
918
|
return null
|
|
533
919
|
}
|
|
534
|
-
const out: u8[] = new Array<u8>(
|
|
535
|
-
if (!x509DerUnsignedInto(sig, r, out, 0) || !x509DerUnsignedInto(sig, s, out,
|
|
920
|
+
const out: u8[] = new Array<u8>(2 * width)
|
|
921
|
+
if (!x509DerUnsignedInto(sig, r, out, 0, width) || !x509DerUnsignedInto(sig, s, out, width, width)) {
|
|
536
922
|
return null
|
|
537
923
|
}
|
|
538
924
|
return out
|
|
539
925
|
}
|
|
540
926
|
|
|
927
|
+
/**
|
|
928
|
+
* `x509DerSignatureRSWidth` for P-256: `r || s`, 32 octets each, the form
|
|
929
|
+
* `nish/crypto/p256` takes.
|
|
930
|
+
*/
|
|
931
|
+
export const x509DerSignatureRS = (sig: u8[]): u8[] | null => x509DerSignatureRSWidth(sig, 32)
|
|
932
|
+
|
|
541
933
|
// ---------------------------------------------------------------------------
|
|
542
934
|
// DER, writing
|
|
543
935
|
// ---------------------------------------------------------------------------
|
|
@@ -982,13 +1374,605 @@ export const x509ParseP256PrivateKey = (pem: string): Secret<u8[]> | null => {
|
|
|
982
1374
|
// ---------------------------------------------------------------------------
|
|
983
1375
|
|
|
984
1376
|
/**
|
|
985
|
-
* Whether a `[
|
|
986
|
-
*
|
|
987
|
-
*
|
|
1377
|
+
* Whether a Name in `der[e.start .. e.end)`, the contents of its SEQUENCE, is
|
|
1378
|
+
* RFC 5280 §4.1.2.4's `SEQUENCE OF RelativeDistinguishedName`: each RDN a
|
|
1379
|
+
* non-empty SET of `SEQUENCE { type OID, value }`, one element each, nothing
|
|
1380
|
+
* between or after. The values' string types are not read (the common name is
|
|
1381
|
+
* the mint's own), and DER's sorted SET OF is not checked: a validator compares
|
|
1382
|
+
* Names byte for byte, so an unsorted SET only fails to match.
|
|
1383
|
+
*/
|
|
1384
|
+
const x509DerNameSound = (der: u8[], e: X509DerElement): boolean => {
|
|
1385
|
+
let at: i32 = e.start
|
|
1386
|
+
while (at < e.end) {
|
|
1387
|
+
const rdn: X509DerElement | null = x509DerExpect(der, at, e.end, X509_TAG_SET)
|
|
1388
|
+
if (rdn === null || rdn.end === rdn.start) {
|
|
1389
|
+
return false
|
|
1390
|
+
}
|
|
1391
|
+
let inner: i32 = rdn.start
|
|
1392
|
+
while (inner < rdn.end) {
|
|
1393
|
+
const pair: X509DerElement | null = x509DerExpect(der, inner, rdn.end, X509_TAG_SEQUENCE)
|
|
1394
|
+
if (pair === null) {
|
|
1395
|
+
return false
|
|
1396
|
+
}
|
|
1397
|
+
const type: X509DerElement | null = x509DerExpect(der, pair.start, pair.end, X509_TAG_OID)
|
|
1398
|
+
if (type === null || !x509DerOidSound(der, type)) {
|
|
1399
|
+
return false
|
|
1400
|
+
}
|
|
1401
|
+
const value: X509DerElement | null = x509DerRead(der, type.end, pair.end)
|
|
1402
|
+
if (value === null || value.end !== pair.end) {
|
|
1403
|
+
return false
|
|
1404
|
+
}
|
|
1405
|
+
inner = pair.end
|
|
1406
|
+
}
|
|
1407
|
+
at = rdn.end
|
|
1408
|
+
}
|
|
1409
|
+
return true
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Whether two Names are the same: their DER, byte for byte. RFC 5280 §7.1's
|
|
1414
|
+
* comparison — case folded and white space collapsed after a stringprep of
|
|
1415
|
+
* each attribute — is not implemented (X509-12 in docs/security/crypto-x509.md),
|
|
1416
|
+
* so two Names spelled differently that it would match answer `false`. That
|
|
1417
|
+
* fails closed: a chain whose issuer and subject differ only so is not built.
|
|
1418
|
+
*/
|
|
1419
|
+
export const x509NameEqual = (a: u8[], b: u8[]): boolean => x509BytesEqual(a, b)
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Whether the Name `base` is a prefix of the Name `name`, both whole DER as
|
|
1423
|
+
* `X509Certificate.issuer` and `subject` hold them: each of `base`'s RDNs is
|
|
1424
|
+
* `name`'s RDN at the same place, byte for byte — how a directoryName
|
|
1425
|
+
* constraint (RFC 5280 §4.2.1.10) is matched. An empty `base` is a prefix of
|
|
1426
|
+
* every Name. `false` when either is not a Name.
|
|
1427
|
+
*/
|
|
1428
|
+
export const x509NameIsPrefix = (base: u8[], name: u8[]): boolean => {
|
|
1429
|
+
const b: X509DerElement | null = x509DerExpect(base, 0, toI32(base.length), X509_TAG_SEQUENCE)
|
|
1430
|
+
const n: X509DerElement | null = x509DerExpect(name, 0, toI32(name.length), X509_TAG_SEQUENCE)
|
|
1431
|
+
if (b === null || n === null || b.end !== toI32(base.length) || n.end !== toI32(name.length)) {
|
|
1432
|
+
return false
|
|
1433
|
+
}
|
|
1434
|
+
if (!x509DerNameSound(base, b) || !x509DerNameSound(name, n)) {
|
|
1435
|
+
return false
|
|
1436
|
+
}
|
|
1437
|
+
let atB: i32 = b.start
|
|
1438
|
+
let atN: i32 = n.start
|
|
1439
|
+
while (atB < b.end) {
|
|
1440
|
+
const rdnB: X509DerElement | null = x509DerRead(base, atB, b.end)
|
|
1441
|
+
const rdnN: X509DerElement | null = x509DerRead(name, atN, n.end)
|
|
1442
|
+
if (
|
|
1443
|
+
rdnB === null ||
|
|
1444
|
+
rdnN === null ||
|
|
1445
|
+
!x509BytesEqual(x509DerWhole(base, rdnB), x509DerWhole(name, rdnN))
|
|
1446
|
+
) {
|
|
1447
|
+
return false
|
|
1448
|
+
}
|
|
1449
|
+
atB = rdnB.end
|
|
1450
|
+
atN = rdnN.end
|
|
1451
|
+
}
|
|
1452
|
+
return true
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* Whether an IA5String's contents are what a name may hold: every octet below
|
|
1457
|
+
* 0x80 (X.680 §41's IA5 is seven-bit), and none NUL, the old
|
|
1458
|
+
* `localhost\0.example` confusion a C reader would cut short.
|
|
988
1459
|
*/
|
|
989
|
-
const
|
|
1460
|
+
const x509DerIa5Name = (der: u8[], e: X509DerElement): boolean => {
|
|
1461
|
+
for (let k: i32 = e.start; k < e.end; k++) {
|
|
1462
|
+
// Always true for an element `x509DerRead` gave, and what lets the load drop its check.
|
|
1463
|
+
if (k >= 0 && k < toI32(der.length)) {
|
|
1464
|
+
const b: i32 = toI32(der[k])
|
|
1465
|
+
if (b === 0 || b >= 0x80) {
|
|
1466
|
+
return false
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return true
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
/** `der[e.start .. e.end)` as a string, one character per octet, as `x509Bytes` reads one back. */
|
|
1474
|
+
const x509DerText = (der: u8[], e: X509DerElement): string => String.fromBytes(der, e.start, e.end)
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* The form of one GeneralName (RFC 5280 §4.2.1.6), its IMPLICIT tag checked
|
|
1478
|
+
* against its form — primitive for the strings, the address and the OID,
|
|
1479
|
+
* constructed for the rest — or -1 when it is malformed: a tag no form has, a
|
|
1480
|
+
* name string that is not `x509DerIa5Name`, or a directoryName that is not
|
|
1481
|
+
* exactly one sound Name. An iPAddress's length is the caller's to check,
|
|
1482
|
+
* since a subtree's is twice a name's.
|
|
1483
|
+
*/
|
|
1484
|
+
const x509GeneralNameKind = (der: u8[], e: X509DerElement): i32 => {
|
|
1485
|
+
const tag: i32 = e.tag
|
|
1486
|
+
if (tag === 0x82) {
|
|
1487
|
+
return x509DerIa5Name(der, e) ? X509_GN_DNS : -1
|
|
1488
|
+
}
|
|
1489
|
+
if (tag === 0x87) {
|
|
1490
|
+
return X509_GN_IP
|
|
1491
|
+
}
|
|
1492
|
+
if (tag === 0xa4) {
|
|
1493
|
+
const name: X509DerElement | null = x509DerExpect(der, e.start, e.end, X509_TAG_SEQUENCE)
|
|
1494
|
+
return name !== null && name.end === e.end && x509DerNameSound(der, name) ? X509_GN_DIR : -1
|
|
1495
|
+
}
|
|
1496
|
+
// rfc822Name [1] and uniformResourceIdentifier [6] are IA5 strings too.
|
|
1497
|
+
if (tag === 0x81 || tag === 0x86) {
|
|
1498
|
+
return x509DerIa5Name(der, e) ? X509_GN_OTHER : -1
|
|
1499
|
+
}
|
|
1500
|
+
// otherName [0] is `type-id OID, value [0] EXPLICIT ANY`, and registeredID
|
|
1501
|
+
// [8] an IMPLICIT OID.
|
|
1502
|
+
if (tag === 0xa0) {
|
|
1503
|
+
const typeId: X509DerElement | null = x509DerRead(der, e.start, e.end)
|
|
1504
|
+
const value: X509DerElement | null = typeId === null ? null : x509DerExpect(der, typeId.end, e.end, 0xa0)
|
|
1505
|
+
return typeId !== null && x509DerOidSound(der, typeId) && value !== null && value.end === e.end
|
|
1506
|
+
? X509_GN_OTHER
|
|
1507
|
+
: -1
|
|
1508
|
+
}
|
|
1509
|
+
if (tag === 0x88) {
|
|
1510
|
+
return x509DerOidSound(der, new X509DerElement(X509_TAG_OID, e.at, e.start, e.end)) ? X509_GN_OTHER : -1
|
|
1511
|
+
}
|
|
1512
|
+
// x400Address [3] and ediPartyName [5], whose contents are not read.
|
|
1513
|
+
if (tag === 0xa3 || tag === 0xa5) {
|
|
1514
|
+
return X509_GN_OTHER
|
|
1515
|
+
}
|
|
1516
|
+
return -1
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* subjectAltName (RFC 5280 §4.2.1.6), `SEQUENCE SIZE (1..MAX) OF
|
|
1521
|
+
* GeneralName`, into `out`: each dNSName and iPAddress (4 or 16 octets) kept,
|
|
1522
|
+
* the other forms counted. `false` when it is malformed, or holds an empty
|
|
1523
|
+
* dNSName, which §4.2.1.6 forbids (in a name constraint, empty is every name).
|
|
1524
|
+
*/
|
|
1525
|
+
const x509ReadSubjectAltName = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1526
|
+
const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
1527
|
+
if (seq === null || seq.end !== end || seq.end === seq.start) {
|
|
1528
|
+
return false
|
|
1529
|
+
}
|
|
1530
|
+
// Declared once and read into, so a pass leaves only what it keeps.
|
|
1531
|
+
const name = new X509DerElement(0, 0, 0, 0)
|
|
1532
|
+
let at: i32 = seq.start
|
|
1533
|
+
while (at < seq.end) {
|
|
1534
|
+
if (!x509DerReadInto(der, at, seq.end, name)) {
|
|
1535
|
+
return false
|
|
1536
|
+
}
|
|
1537
|
+
const kind: i32 = x509GeneralNameKind(der, name)
|
|
1538
|
+
const length: i32 = name.end - name.start
|
|
1539
|
+
if (kind === X509_GN_DNS && length > 0) {
|
|
1540
|
+
out.sanDns.push(x509DerText(der, name))
|
|
1541
|
+
} else if (kind === X509_GN_IP && (length === 4 || length === 16)) {
|
|
1542
|
+
out.sanIp.push(x509DerSlice(der, name.start, name.end))
|
|
1543
|
+
} else if (kind === X509_GN_DIR || kind === X509_GN_OTHER) {
|
|
1544
|
+
out.sanOther = out.sanOther + 1
|
|
1545
|
+
} else {
|
|
1546
|
+
return false
|
|
1547
|
+
}
|
|
1548
|
+
at = name.end
|
|
1549
|
+
}
|
|
1550
|
+
out.sanPresent = true
|
|
1551
|
+
return true
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* The value of a minimal, non-negative INTEGER below 2^31, or -1: a
|
|
1556
|
+
* pathLenConstraint, which RFC 5280 §4.2.1.9 makes `INTEGER (0..MAX)`.
|
|
1557
|
+
*/
|
|
1558
|
+
const x509DerNonNegative = (der: u8[], e: X509DerElement): i32 => {
|
|
1559
|
+
const n: i32 = e.end - e.start
|
|
1560
|
+
if (e.tag !== X509_TAG_INTEGER || !x509DerIntegerMinimal(der, e) || toI32(der[e.start]) >= 0x80 || n > 4) {
|
|
1561
|
+
return -1
|
|
1562
|
+
}
|
|
1563
|
+
let value: i32 = 0
|
|
1564
|
+
for (let k: i32 = e.start; k < e.end; k++) {
|
|
1565
|
+
// Always true for an element `x509DerRead` gave, and what lets the load drop its check.
|
|
1566
|
+
if (k >= 0 && k < toI32(der.length)) {
|
|
1567
|
+
value = (value << 8) | toI32(der[k])
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return value
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
/** Whether `e` is DER's TRUE, `01 01 ff`: a DEFAULT FALSE is left out (X.690 §11.5), so a present BOOLEAN is TRUE. */
|
|
1574
|
+
const x509DerIsTrue = (der: u8[], e: X509DerElement): boolean =>
|
|
1575
|
+
e.tag === X509_TAG_BOOLEAN && e.end - e.start === 1 && toI32(der[e.start]) === 0xff
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* basicConstraints (RFC 5280 §4.2.1.9), `SEQUENCE { cA BOOLEAN DEFAULT FALSE,
|
|
1579
|
+
* pathLenConstraint INTEGER (0..MAX) OPTIONAL }`, into `out`. `false` when it
|
|
1580
|
+
* is malformed: a cA that is not DER's TRUE, a negative or non-minimal path
|
|
1581
|
+
* length, or anything after it.
|
|
1582
|
+
*/
|
|
1583
|
+
const x509ReadBasicConstraints = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1584
|
+
const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
1585
|
+
if (seq === null || seq.end !== end) {
|
|
1586
|
+
return false
|
|
1587
|
+
}
|
|
1588
|
+
let at: i32 = seq.start
|
|
1589
|
+
if (x509DerPeek(der, at, seq.end) === X509_TAG_BOOLEAN) {
|
|
1590
|
+
const ca: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1591
|
+
if (ca === null || !x509DerIsTrue(der, ca)) {
|
|
1592
|
+
return false
|
|
1593
|
+
}
|
|
1594
|
+
out.bcCa = true
|
|
1595
|
+
at = ca.end
|
|
1596
|
+
}
|
|
1597
|
+
if (at < seq.end) {
|
|
1598
|
+
const pathLen: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1599
|
+
if (pathLen === null || pathLen.end !== seq.end) {
|
|
1600
|
+
return false
|
|
1601
|
+
}
|
|
1602
|
+
out.bcPathLen = x509DerNonNegative(der, pathLen)
|
|
1603
|
+
if (out.bcPathLen < 0) {
|
|
1604
|
+
return false
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
out.bcPresent = true
|
|
1608
|
+
return true
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* keyUsage (RFC 5280 §4.2.1.3), a BIT STRING of named bits 0 to 8, into
|
|
1613
|
+
* `out.keyUsage`. `false` when it is malformed: unused bits that are not
|
|
1614
|
+
* zero, no bit set (§4.2.1.3 requires one), a trailing zero bit DER would
|
|
1615
|
+
* have dropped (X.690 §11.2.2), or a bit past decipherOnly.
|
|
1616
|
+
*/
|
|
1617
|
+
const x509ReadKeyUsage = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1618
|
+
const bits: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_BIT_STRING)
|
|
1619
|
+
if (bits === null || bits.end !== end || !x509DerBitStringSound(der, bits)) {
|
|
1620
|
+
return false
|
|
1621
|
+
}
|
|
1622
|
+
// An empty BIT STRING (n = 0) needs no test of its own: its unused-bits
|
|
1623
|
+
// count is held to 0, so the bit check below finds no bit set.
|
|
1624
|
+
const n: i32 = bits.end - bits.start - 1
|
|
1625
|
+
if (n > 2) {
|
|
1626
|
+
return false
|
|
1627
|
+
}
|
|
1628
|
+
const unused: i32 = toI32(der[bits.start])
|
|
1629
|
+
if (((toI32(der[bits.end - 1]) >> unused) & 1) !== 1) {
|
|
1630
|
+
return false
|
|
1631
|
+
}
|
|
1632
|
+
let mask: i32 = 0
|
|
1633
|
+
for (let j: i32 = 0; j < n; j++) {
|
|
1634
|
+
const octet: i32 = toI32(der[bits.start + 1 + j])
|
|
1635
|
+
for (let b: i32 = 0; b < 8; b++) {
|
|
1636
|
+
if (((octet >> (7 - b)) & 1) === 1) {
|
|
1637
|
+
const bit: i32 = 8 * j + b
|
|
1638
|
+
mask = mask | (toI32(1) << bit)
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
// Past decipherOnly, bit 8.
|
|
1643
|
+
if (mask >= 512) {
|
|
1644
|
+
return false
|
|
1645
|
+
}
|
|
1646
|
+
out.keyUsage = mask
|
|
1647
|
+
return true
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* extKeyUsage (RFC 5280 §4.2.1.12), `SEQUENCE SIZE (1..MAX) OF KeyPurposeId`,
|
|
1652
|
+
* into `out`: whether it names serverAuth and anyExtendedKeyUsage. `false`
|
|
1653
|
+
* when it is empty or holds anything but sound OIDs.
|
|
1654
|
+
*/
|
|
1655
|
+
const x509ReadExtKeyUsage = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1656
|
+
const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
1657
|
+
if (seq === null || seq.end !== end || seq.end === seq.start) {
|
|
1658
|
+
return false
|
|
1659
|
+
}
|
|
1660
|
+
const serverAuth: u8[] = x509OidServerAuth()
|
|
1661
|
+
const anyEku: u8[] = x509OidAnyEku()
|
|
1662
|
+
let at: i32 = seq.start
|
|
1663
|
+
while (at < seq.end) {
|
|
1664
|
+
const purpose: X509DerElement | null = x509DerExpect(der, at, seq.end, X509_TAG_OID)
|
|
1665
|
+
if (purpose === null || !x509DerOidSound(der, purpose)) {
|
|
1666
|
+
return false
|
|
1667
|
+
}
|
|
1668
|
+
if (x509DerContentsAre(der, purpose, serverAuth)) {
|
|
1669
|
+
out.ekuServerAuth = true
|
|
1670
|
+
} else if (x509DerContentsAre(der, purpose, anyEku)) {
|
|
1671
|
+
out.ekuAny = true
|
|
1672
|
+
}
|
|
1673
|
+
at = purpose.end
|
|
1674
|
+
}
|
|
1675
|
+
out.ekuPresent = true
|
|
1676
|
+
return true
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/** Whether `mask` is ones and then zeros, as an iPAddress constraint's mask must be (RFC 5280 §4.2.1.10). */
|
|
1680
|
+
const x509MaskContiguous = (mask: u8[]): boolean => {
|
|
1681
|
+
let zeroSeen: boolean = false
|
|
1682
|
+
for (let k: i32 = 0; k < toI32(mask.length); k++) {
|
|
1683
|
+
const octet: i32 = toI32(mask[k])
|
|
1684
|
+
for (let b: i32 = 7; b >= 0; b--) {
|
|
1685
|
+
const one: boolean = ((octet >> b) & 1) === 1
|
|
1686
|
+
if (one && zeroSeen) {
|
|
1687
|
+
return false
|
|
1688
|
+
}
|
|
1689
|
+
zeroSeen = zeroSeen || !one
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return true
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
/** Whether a GeneralSubtree's IMPLICIT `BaseDistance` bound is a minimal, non-negative INTEGER. */
|
|
1696
|
+
const x509DerBoundSound = (der: u8[], e: X509DerElement): boolean =>
|
|
1697
|
+
x509DerIntegerMinimal(der, e) && toI32(der[e.start]) < 0x80
|
|
1698
|
+
|
|
1699
|
+
/**
|
|
1700
|
+
* The GeneralSubtrees filling an IMPLICIT `[0]` or `[1]` of nameConstraints
|
|
1701
|
+
* (RFC 5280 §4.2.1.10), `SIZE (1..MAX) OF GeneralSubtree`, pushed onto
|
|
1702
|
+
* `into`. Each is `SEQUENCE { base GeneralName, minimum [0] DEFAULT 0, maximum
|
|
1703
|
+
* [1] OPTIONAL }`: a subtree with a minimum other than 0, or a maximum, sets
|
|
1704
|
+
* `out.ncUnsupported`, since §4.2.1.10 has the minimum 0 and the maximum
|
|
1705
|
+
* absent. `false` when it is malformed: a minimum of 0 written out (DER leaves
|
|
1706
|
+
* a DEFAULT out), a bound that is not a minimal non-negative INTEGER, anything
|
|
1707
|
+
* else after the base, or an iPAddress that is not 8 or 32 octets or whose
|
|
1708
|
+
* mask is not contiguous.
|
|
1709
|
+
*/
|
|
1710
|
+
const x509ReadSubtrees = (
|
|
1711
|
+
der: u8[],
|
|
1712
|
+
e: X509DerElement,
|
|
1713
|
+
into: X509GeneralSubtree[],
|
|
1714
|
+
out: X509Certificate
|
|
1715
|
+
): boolean => {
|
|
1716
|
+
if (e.end === e.start) {
|
|
1717
|
+
return false
|
|
1718
|
+
}
|
|
1719
|
+
// Declared once and read into, so a pass leaves only the subtree it keeps.
|
|
1720
|
+
const tree = new X509DerElement(0, 0, 0, 0)
|
|
1721
|
+
const base = new X509DerElement(0, 0, 0, 0)
|
|
1722
|
+
const minimum = new X509DerElement(0, 0, 0, 0)
|
|
1723
|
+
const maximum = new X509DerElement(0, 0, 0, 0)
|
|
1724
|
+
let at: i32 = e.start
|
|
1725
|
+
while (at < e.end) {
|
|
1726
|
+
if (!x509DerExpectInto(der, at, e.end, X509_TAG_SEQUENCE, tree)) {
|
|
1727
|
+
return false
|
|
1728
|
+
}
|
|
1729
|
+
if (!x509DerReadInto(der, tree.start, tree.end, base)) {
|
|
1730
|
+
return false
|
|
1731
|
+
}
|
|
1732
|
+
const subtree = new X509GeneralSubtree()
|
|
1733
|
+
subtree.kind = x509GeneralNameKind(der, base)
|
|
1734
|
+
const length: i32 = base.end - base.start
|
|
1735
|
+
if (subtree.kind === X509_GN_DNS) {
|
|
1736
|
+
subtree.dns = x509DerText(der, base)
|
|
1737
|
+
} else if (subtree.kind === X509_GN_IP) {
|
|
1738
|
+
if (length !== 8 && length !== 32) {
|
|
1739
|
+
return false
|
|
1740
|
+
}
|
|
1741
|
+
subtree.ip = x509DerSlice(der, base.start, base.start + length / 2)
|
|
1742
|
+
subtree.mask = x509DerSlice(der, base.start + length / 2, base.end)
|
|
1743
|
+
if (!x509MaskContiguous(subtree.mask)) {
|
|
1744
|
+
return false
|
|
1745
|
+
}
|
|
1746
|
+
} else if (subtree.kind === X509_GN_DIR) {
|
|
1747
|
+
subtree.dirName = x509DerSlice(der, base.start, base.end)
|
|
1748
|
+
} else if (subtree.kind === X509_GN_OTHER) {
|
|
1749
|
+
out.ncUnsupported = true
|
|
1750
|
+
} else {
|
|
1751
|
+
return false
|
|
1752
|
+
}
|
|
1753
|
+
let bound: i32 = base.end
|
|
1754
|
+
if (x509DerPeek(der, bound, tree.end) === 0x80) {
|
|
1755
|
+
if (
|
|
1756
|
+
!x509DerReadInto(der, bound, tree.end, minimum) ||
|
|
1757
|
+
!x509DerBoundSound(der, minimum) ||
|
|
1758
|
+
(minimum.end - minimum.start === 1 && toI32(der[minimum.start]) === 0)
|
|
1759
|
+
) {
|
|
1760
|
+
return false
|
|
1761
|
+
}
|
|
1762
|
+
out.ncUnsupported = true
|
|
1763
|
+
bound = minimum.end
|
|
1764
|
+
}
|
|
1765
|
+
if (x509DerPeek(der, bound, tree.end) === 0x81) {
|
|
1766
|
+
if (!x509DerReadInto(der, bound, tree.end, maximum) || !x509DerBoundSound(der, maximum)) {
|
|
1767
|
+
return false
|
|
1768
|
+
}
|
|
1769
|
+
out.ncUnsupported = true
|
|
1770
|
+
bound = maximum.end
|
|
1771
|
+
}
|
|
1772
|
+
if (bound !== tree.end) {
|
|
1773
|
+
return false
|
|
1774
|
+
}
|
|
1775
|
+
into.push(subtree)
|
|
1776
|
+
at = tree.end
|
|
1777
|
+
}
|
|
1778
|
+
return true
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* nameConstraints (RFC 5280 §4.2.1.10), `SEQUENCE { permittedSubtrees [0]
|
|
1783
|
+
* OPTIONAL, excludedSubtrees [1] OPTIONAL }`, into `out`. `false` when it is
|
|
1784
|
+
* malformed, or empty, which §4.2.1.10 forbids.
|
|
1785
|
+
*/
|
|
1786
|
+
const x509ReadNameConstraints = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1787
|
+
const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
1788
|
+
if (seq === null || seq.end !== end || seq.end === seq.start) {
|
|
1789
|
+
return false
|
|
1790
|
+
}
|
|
1791
|
+
let at: i32 = seq.start
|
|
1792
|
+
if (x509DerPeek(der, at, seq.end) === 0xa0) {
|
|
1793
|
+
const permitted: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1794
|
+
if (permitted === null || !x509ReadSubtrees(der, permitted, out.ncPermitted, out)) {
|
|
1795
|
+
return false
|
|
1796
|
+
}
|
|
1797
|
+
at = permitted.end
|
|
1798
|
+
}
|
|
1799
|
+
if (x509DerPeek(der, at, seq.end) === 0xa1) {
|
|
1800
|
+
const excluded: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1801
|
+
if (excluded === null || !x509ReadSubtrees(der, excluded, out.ncExcluded, out)) {
|
|
1802
|
+
return false
|
|
1803
|
+
}
|
|
1804
|
+
at = excluded.end
|
|
1805
|
+
}
|
|
1806
|
+
out.ncPresent = true
|
|
1807
|
+
return at === seq.end
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/** subjectKeyIdentifier (RFC 5280 §4.2.1.2), one OCTET STRING, into `out.skid`; `false` when it is malformed or empty. */
|
|
1811
|
+
const x509ReadSubjectKeyId = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1812
|
+
const id: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_OCTET_STRING)
|
|
1813
|
+
if (id === null || id.end !== end || id.end === id.start) {
|
|
1814
|
+
return false
|
|
1815
|
+
}
|
|
1816
|
+
out.skid = x509DerSlice(der, id.start, id.end)
|
|
1817
|
+
return true
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/** Whether `e`'s contents are a non-empty run of well-formed GeneralNames. */
|
|
1821
|
+
const x509GeneralNamesSound = (der: u8[], e: X509DerElement): boolean => {
|
|
1822
|
+
if (e.end === e.start) {
|
|
1823
|
+
return false
|
|
1824
|
+
}
|
|
1825
|
+
let at: i32 = e.start
|
|
1826
|
+
while (at < e.end) {
|
|
1827
|
+
const name: X509DerElement | null = x509DerRead(der, at, e.end)
|
|
1828
|
+
if (name === null || x509GeneralNameKind(der, name) === -1) {
|
|
1829
|
+
return false
|
|
1830
|
+
}
|
|
1831
|
+
at = name.end
|
|
1832
|
+
}
|
|
1833
|
+
return true
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* authorityKeyIdentifier (RFC 5280 §4.2.1.1), `SEQUENCE { keyIdentifier [0]
|
|
1838
|
+
* OPTIONAL, authorityCertIssuer [1] OPTIONAL, authorityCertSerialNumber [2]
|
|
1839
|
+
* OPTIONAL }`, its keyIdentifier, which may not be empty, into `out.akid`.
|
|
1840
|
+
* The other two are held to their order, to being both present or both
|
|
1841
|
+
* absent, as §4.2.1.1 has them, to a non-empty GeneralNames and a minimal
|
|
1842
|
+
* INTEGER, and not kept. `false` when it is malformed.
|
|
1843
|
+
*/
|
|
1844
|
+
const x509ReadAuthorityKeyId = (der: u8[], start: i32, end: i32, out: X509Certificate): boolean => {
|
|
1845
|
+
const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
|
|
1846
|
+
if (seq === null || seq.end !== end) {
|
|
1847
|
+
return false
|
|
1848
|
+
}
|
|
1849
|
+
let at: i32 = seq.start
|
|
1850
|
+
if (x509DerPeek(der, at, seq.end) === 0x80) {
|
|
1851
|
+
const id: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1852
|
+
if (id === null || id.end === id.start) {
|
|
1853
|
+
return false
|
|
1854
|
+
}
|
|
1855
|
+
out.akid = x509DerSlice(der, id.start, id.end)
|
|
1856
|
+
at = id.end
|
|
1857
|
+
}
|
|
1858
|
+
if (x509DerPeek(der, at, seq.end) === 0xa1) {
|
|
1859
|
+
const issuer: X509DerElement | null = x509DerRead(der, at, seq.end)
|
|
1860
|
+
const serial: X509DerElement | null =
|
|
1861
|
+
issuer === null ? null : x509DerExpect(der, issuer.end, seq.end, 0x82)
|
|
1862
|
+
if (
|
|
1863
|
+
issuer === null ||
|
|
1864
|
+
serial === null ||
|
|
1865
|
+
!x509GeneralNamesSound(der, issuer) ||
|
|
1866
|
+
!x509DerIntegerMinimal(der, serial)
|
|
1867
|
+
) {
|
|
1868
|
+
return false
|
|
1869
|
+
}
|
|
1870
|
+
at = serial.end
|
|
1871
|
+
}
|
|
1872
|
+
return at === seq.end
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/**
|
|
1876
|
+
* One extension's value, the contents of its extnValue OCTET STRING, decoded
|
|
1877
|
+
* into `out` by the arc `ce` it has under 2.5.29: `X509_EXT_SOUND`, or
|
|
1878
|
+
* `X509_EXT_MALFORMED`. An extension this module does not decode answers
|
|
1879
|
+
* `X509_EXT_UNKNOWN` with `out` untouched, and `ce` is -1 for one outside the
|
|
1880
|
+
* arc.
|
|
1881
|
+
*/
|
|
1882
|
+
const x509ReadExtension = (der: u8[], ce: i32, start: i32, end: i32, out: X509Certificate): i32 => {
|
|
1883
|
+
let sound: boolean = false
|
|
1884
|
+
if (ce === X509_CE_SUBJECT_ALT_NAME) {
|
|
1885
|
+
sound = x509ReadSubjectAltName(der, start, end, out)
|
|
1886
|
+
} else if (ce === X509_CE_BASIC_CONSTRAINTS) {
|
|
1887
|
+
sound = x509ReadBasicConstraints(der, start, end, out)
|
|
1888
|
+
} else if (ce === X509_CE_KEY_USAGE) {
|
|
1889
|
+
sound = x509ReadKeyUsage(der, start, end, out)
|
|
1890
|
+
} else if (ce === X509_CE_EXT_KEY_USAGE) {
|
|
1891
|
+
sound = x509ReadExtKeyUsage(der, start, end, out)
|
|
1892
|
+
} else if (ce === X509_CE_NAME_CONSTRAINTS) {
|
|
1893
|
+
sound = x509ReadNameConstraints(der, start, end, out)
|
|
1894
|
+
} else if (ce === X509_CE_SUBJECT_KEY_ID) {
|
|
1895
|
+
sound = x509ReadSubjectKeyId(der, start, end, out)
|
|
1896
|
+
} else if (ce === X509_CE_AUTHORITY_KEY_ID) {
|
|
1897
|
+
sound = x509ReadAuthorityKeyId(der, start, end, out)
|
|
1898
|
+
} else {
|
|
1899
|
+
return X509_EXT_UNKNOWN
|
|
1900
|
+
}
|
|
1901
|
+
return sound ? X509_EXT_SOUND : X509_EXT_MALFORMED
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/** The arc under 2.5.29 an extnID names, 1 to 127 (one octet), or -1 for an OID outside it. */
|
|
1905
|
+
const x509ExtensionArc = (der: u8[], id: X509DerElement): i32 => {
|
|
1906
|
+
if (id.end - id.start !== 3 || toI32(der[id.start]) !== 0x55 || toI32(der[id.start + 1]) !== 0x1d) {
|
|
1907
|
+
return -1
|
|
1908
|
+
}
|
|
1909
|
+
const arc: i32 = toI32(der[id.start + 2])
|
|
1910
|
+
return arc < 0x80 ? arc : -1
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* The `[3]` extensions wrapper read into `out` (RFC 5280 §4.1 and §4.2): one
|
|
1915
|
+
* non-empty SEQUENCE of `SEQUENCE { extnID OID, critical BOOLEAN DEFAULT
|
|
1916
|
+
* FALSE, extnValue OCTET STRING }` and nothing after it. `false` when any is
|
|
1917
|
+
* malformed, a critical flag is not DER's TRUE, an OID appears twice (§4.2:
|
|
1918
|
+
* "MUST NOT include more than one instance"), there are more than
|
|
1919
|
+
* `X509_MAX_EXTENSIONS`, or a decoded extension is malformed. A critical
|
|
1920
|
+
* extension this module does not decode sets `out.unknownCritical`; a
|
|
1921
|
+
* non-critical one is skipped.
|
|
1922
|
+
*/
|
|
1923
|
+
const x509ReadExtensions = (der: u8[], e: X509DerElement, out: X509Certificate): boolean => {
|
|
990
1924
|
const seq: X509DerElement | null = x509DerExpect(der, e.start, e.end, X509_TAG_SEQUENCE)
|
|
991
|
-
|
|
1925
|
+
if (seq === null || seq.end !== e.end || seq.end === seq.start) {
|
|
1926
|
+
return false
|
|
1927
|
+
}
|
|
1928
|
+
// Every extnID so far, to find a second instance by its encoding, which
|
|
1929
|
+
// `x509DerOidSound` makes the only one.
|
|
1930
|
+
const seen: u8[][] = []
|
|
1931
|
+
// Declared once and read into, so a pass leaves only the extnID it keeps.
|
|
1932
|
+
const ext = new X509DerElement(0, 0, 0, 0)
|
|
1933
|
+
const id = new X509DerElement(0, 0, 0, 0)
|
|
1934
|
+
const flag = new X509DerElement(0, 0, 0, 0)
|
|
1935
|
+
const value = new X509DerElement(0, 0, 0, 0)
|
|
1936
|
+
let at: i32 = seq.start
|
|
1937
|
+
while (at < seq.end) {
|
|
1938
|
+
if (
|
|
1939
|
+
toI32(seen.length) >= X509_MAX_EXTENSIONS ||
|
|
1940
|
+
!x509DerExpectInto(der, at, seq.end, X509_TAG_SEQUENCE, ext)
|
|
1941
|
+
) {
|
|
1942
|
+
return false
|
|
1943
|
+
}
|
|
1944
|
+
if (!x509DerExpectInto(der, ext.start, ext.end, X509_TAG_OID, id) || !x509DerOidSound(der, id)) {
|
|
1945
|
+
return false
|
|
1946
|
+
}
|
|
1947
|
+
const oid: u8[] = x509DerSlice(der, id.start, id.end)
|
|
1948
|
+
for (const other of seen) {
|
|
1949
|
+
if (x509BytesEqual(other, oid)) {
|
|
1950
|
+
return false
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
seen.push(oid)
|
|
1954
|
+
let next: i32 = id.end
|
|
1955
|
+
let critical: boolean = false
|
|
1956
|
+
if (x509DerPeek(der, next, ext.end) === X509_TAG_BOOLEAN) {
|
|
1957
|
+
if (!x509DerReadInto(der, next, ext.end, flag) || !x509DerIsTrue(der, flag)) {
|
|
1958
|
+
return false
|
|
1959
|
+
}
|
|
1960
|
+
critical = true
|
|
1961
|
+
next = flag.end
|
|
1962
|
+
}
|
|
1963
|
+
if (!x509DerExpectInto(der, next, ext.end, X509_TAG_OCTET_STRING, value) || value.end !== ext.end) {
|
|
1964
|
+
return false
|
|
1965
|
+
}
|
|
1966
|
+
const read: i32 = x509ReadExtension(der, x509ExtensionArc(der, id), value.start, value.end, out)
|
|
1967
|
+
if (read === X509_EXT_MALFORMED) {
|
|
1968
|
+
return false
|
|
1969
|
+
}
|
|
1970
|
+
if (read === X509_EXT_UNKNOWN) {
|
|
1971
|
+
out.unknownCritical = out.unknownCritical || critical
|
|
1972
|
+
}
|
|
1973
|
+
at = ext.end
|
|
1974
|
+
}
|
|
1975
|
+
return true
|
|
992
1976
|
}
|
|
993
1977
|
|
|
994
1978
|
/**
|
|
@@ -999,16 +1983,22 @@ const x509DerExtensionsSound = (der: u8[], e: X509DerElement): boolean => {
|
|
|
999
1983
|
* to the outer one byte for byte (§4.1.1.2), issuer and subject Names, a
|
|
1000
1984
|
* Validity of two valid times, a SubjectPublicKeyInfo whose key is whole
|
|
1001
1985
|
* octets, then only the optional `[1]` and `[2]` unique identifiers (v2 and
|
|
1002
|
-
* v3) and `[3]` extensions (v3), in that order
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1986
|
+
* v3) and `[3]` extensions (v3), in that order. Issuer and subject must be
|
|
1987
|
+
* sound Names. The signature algorithm is named in `sigAlg` (with `pssHash`
|
|
1988
|
+
* and `pssSaltLen` for RSA-PSS) and the key in `keyType`; `p256` and
|
|
1989
|
+
* `ecdsaSha256` say whether they are the ones `x509VerifySignature` checks.
|
|
1990
|
+
*
|
|
1991
|
+
* Extensions are read: subjectAltName, basicConstraints, keyUsage,
|
|
1992
|
+
* extKeyUsage, nameConstraints and the two key identifiers are decoded into
|
|
1993
|
+
* their fields, and `null` is the answer when any of them is malformed or any
|
|
1994
|
+
* extension appears twice. A critical extension it does not decode sets
|
|
1995
|
+
* `unknownCritical`, which RFC 5280 §4.2 obliges a validator to refuse.
|
|
1005
1996
|
*
|
|
1006
1997
|
* **A parser, not a validator.** It checks that the bytes are one certificate
|
|
1007
|
-
* and
|
|
1008
|
-
* does), that `notBefore` is not after `notAfter` or
|
|
1009
|
-
* clock,
|
|
1010
|
-
*
|
|
1011
|
-
* honour are neither checked nor exposed.
|
|
1998
|
+
* and what its extensions say. It does not check the signature
|
|
1999
|
+
* (`x509VerifySignature` does), that `notBefore` is not after `notAfter` or
|
|
2000
|
+
* that either is near any clock, or what the extensions mean for a chain,
|
|
2001
|
+
* which is a path validator's to apply.
|
|
1012
2002
|
*/
|
|
1013
2003
|
export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
1014
2004
|
const total: i32 = toI32(der.length)
|
|
@@ -1059,7 +2049,7 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
|
1059
2049
|
return null
|
|
1060
2050
|
}
|
|
1061
2051
|
const issuer: X509DerElement | null = x509DerExpect(der, alg.end, tbs.end, X509_TAG_SEQUENCE)
|
|
1062
|
-
if (issuer === null) {
|
|
2052
|
+
if (issuer === null || !x509DerNameSound(der, issuer)) {
|
|
1063
2053
|
return null
|
|
1064
2054
|
}
|
|
1065
2055
|
const validity: X509DerElement | null = x509DerExpect(der, issuer.end, tbs.end, X509_TAG_SEQUENCE)
|
|
@@ -1075,7 +2065,7 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
|
1075
2065
|
return null
|
|
1076
2066
|
}
|
|
1077
2067
|
const subject: X509DerElement | null = x509DerExpect(der, validity.end, tbs.end, X509_TAG_SEQUENCE)
|
|
1078
|
-
if (subject === null) {
|
|
2068
|
+
if (subject === null || !x509DerNameSound(der, subject)) {
|
|
1079
2069
|
return null
|
|
1080
2070
|
}
|
|
1081
2071
|
const spki: X509DerElement | null = x509DerExpect(der, subject.end, tbs.end, X509_TAG_SEQUENCE)
|
|
@@ -1094,18 +2084,19 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
|
1094
2084
|
if (publicKey === null) {
|
|
1095
2085
|
return null
|
|
1096
2086
|
}
|
|
2087
|
+
const out = new X509Certificate()
|
|
1097
2088
|
at = spki.end
|
|
1098
2089
|
// [1] issuerUniqueID and [2] subjectUniqueID are IMPLICIT BIT STRINGs (so
|
|
1099
|
-
// primitive, 0x81 and 0x82) from v2
|
|
1100
|
-
//
|
|
2090
|
+
// primitive, 0x81 and 0x82) from v2, held to their shape and not read; [3]
|
|
2091
|
+
// extensions is explicit (0xa3), v3, and read into `out`.
|
|
1101
2092
|
const trailing: i32[] = [0x81, 0x82, 0xa3]
|
|
1102
|
-
|
|
2093
|
+
const e = new X509DerElement(0, 0, 0, 0)
|
|
2094
|
+
for (let i: i32 = 0; i < toI32(trailing.length); i++) {
|
|
1103
2095
|
if (x509DerPeek(der, at, tbs.end) === trailing[i]) {
|
|
1104
|
-
|
|
1105
|
-
if (e === null || version < (i === 2 ? 2 : 1)) {
|
|
2096
|
+
if (!x509DerReadInto(der, at, tbs.end, e) || version < (i === 2 ? 2 : 1)) {
|
|
1106
2097
|
return null
|
|
1107
2098
|
}
|
|
1108
|
-
if (i === 2 ? !
|
|
2099
|
+
if (i === 2 ? !x509ReadExtensions(der, e, out) : !x509DerBitStringSound(der, e)) {
|
|
1109
2100
|
return null
|
|
1110
2101
|
}
|
|
1111
2102
|
at = e.end
|
|
@@ -1120,18 +2111,23 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
|
1120
2111
|
if (before === X509_NO_TIME || after === X509_NO_TIME) {
|
|
1121
2112
|
return null
|
|
1122
2113
|
}
|
|
1123
|
-
const
|
|
2114
|
+
const sigAlg: X509SigAlg = x509SignatureAlgorithm(der, outerAlg)
|
|
2115
|
+
out.version = version
|
|
2116
|
+
out.sigAlg = sigAlg.alg
|
|
2117
|
+
out.pssHash = sigAlg.hash
|
|
2118
|
+
out.pssSaltLen = sigAlg.salt
|
|
2119
|
+
out.keyType = x509KeyType(der, keyAlg)
|
|
1124
2120
|
out.der = x509DerSlice(der, 0, total)
|
|
1125
2121
|
out.tbs = x509DerWhole(der, tbs)
|
|
1126
2122
|
out.serial = x509DerSlice(der, serial.start, serial.end)
|
|
1127
2123
|
out.issuer = x509DerWhole(der, issuer)
|
|
1128
2124
|
out.subject = x509DerWhole(der, subject)
|
|
1129
2125
|
out.publicKey = publicKey
|
|
1130
|
-
out.p256 =
|
|
2126
|
+
out.p256 = out.keyType === X509_KEY_P256
|
|
1131
2127
|
out.notBefore = before
|
|
1132
2128
|
out.notAfter = after
|
|
1133
2129
|
out.signature = signature
|
|
1134
|
-
out.ecdsaSha256 =
|
|
2130
|
+
out.ecdsaSha256 = out.sigAlg === X509_SIG_ECDSA_SHA256
|
|
1135
2131
|
return out
|
|
1136
2132
|
}
|
|
1137
2133
|
|
|
@@ -1144,9 +2140,10 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
|
|
|
1144
2140
|
* **It reads a chain; it does not validate one.** Nothing relates one
|
|
1145
2141
|
* certificate to the next: not the order, not that each is issued by the one
|
|
1146
2142
|
* after it (issuer against subject, or signature), not validity against a
|
|
1147
|
-
* clock, basicConstraints, keyUsage
|
|
1148
|
-
*
|
|
1149
|
-
* no evidence that anyone
|
|
2143
|
+
* clock, nor what each one's basicConstraints, keyUsage and name constraints
|
|
2144
|
+
* allow, nor a trust anchor. Nothing here does RFC 5280 §6's path
|
|
2145
|
+
* validation, and a chain this answers is no evidence that anyone
|
|
2146
|
+
* vouches for the leaf.
|
|
1150
2147
|
*/
|
|
1151
2148
|
export const x509ParseChain = (pem: string): X509Certificate[] | null => {
|
|
1152
2149
|
const blocks: u8[][] | null = pemToDer(pem, "CERTIFICATE")
|
|
@@ -1170,9 +2167,8 @@ export const x509ParseChain = (pem: string): X509Certificate[] | null => {
|
|
|
1170
2167
|
* as both. `false` for any other algorithm or key, and for a signature that is
|
|
1171
2168
|
* not a DER ECDSA-Sig-Value. Only the signature: that `cert.issuer` is
|
|
1172
2169
|
* `issuer.subject` and that the times hold are fields the caller compares, and
|
|
1173
|
-
* whether `issuer` may issue at all (
|
|
1174
|
-
*
|
|
1175
|
-
* validation.
|
|
2170
|
+
* whether `issuer` may issue at all (`bcCa`, `keyUsage`) is a path
|
|
2171
|
+
* validator's to decide, so a `true` here is not path validation.
|
|
1176
2172
|
*/
|
|
1177
2173
|
export const x509VerifySignature = (cert: X509Certificate, issuer: X509Certificate): boolean => {
|
|
1178
2174
|
if (!cert.ecdsaSha256 || !issuer.p256) {
|