@blamejs/pki 0.8.55 → 0.8.57
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/CHANGELOG.md +94 -0
- package/README.md +1 -1
- package/lib/acme.js +2 -2
- package/lib/asn1-der.js +17 -5
- package/lib/attrcert-verify.js +1 -1
- package/lib/cmc-verify.js +1 -1
- package/lib/cmp-session.js +2 -2
- package/lib/cmp-verify.js +2 -2
- package/lib/cms-decrypt.js +4 -1
- package/lib/cms-encrypt.js +4 -1
- package/lib/cms-sign.js +26 -12
- package/lib/cms-verify.js +1 -1
- package/lib/composite-kem.js +30 -12
- package/lib/composite-sig.js +42 -23
- package/lib/constants.js +7 -2
- package/lib/crl-verify.js +1 -1
- package/lib/crmf-sign.js +72 -43
- package/lib/crmf-verify.js +1 -1
- package/lib/csr-verify.js +1 -1
- package/lib/ct.js +2 -2
- package/lib/edwards-point.js +1 -1
- package/lib/est.js +3 -3
- package/lib/guard-async.js +22 -1
- package/lib/guard-bytes.js +6 -3
- package/lib/guard-identifier.js +1 -1
- package/lib/guard-intrinsic.js +474 -0
- package/lib/guard-parsed.js +23 -7
- package/lib/http-digest.js +146 -62
- package/lib/http-transport.js +82 -17
- package/lib/identity-match.js +2 -2
- package/lib/inspect.js +1 -1
- package/lib/lint.js +51 -3
- package/lib/merkle.js +2 -2
- package/lib/ocsp-request-verify.js +1 -1
- package/lib/ocsp.js +53 -22
- package/lib/oid.js +7 -0
- package/lib/path-validate.js +169 -76
- package/lib/pbes2.js +14 -7
- package/lib/schema-attrcert.js +2 -2
- package/lib/schema-cmc.js +2 -2
- package/lib/schema-cmp.js +2 -2
- package/lib/schema-cms.js +115 -10
- package/lib/schema-crmf.js +2 -2
- package/lib/schema-csr.js +2 -2
- package/lib/schema-ocsp.js +41 -6
- package/lib/schema-pkix.js +3 -3
- package/lib/schema-x509.js +2 -2
- package/lib/sigstore.js +1 -1
- package/lib/tlog.js +7 -4
- package/lib/trust.js +187 -104
- package/lib/tsp-sign.js +1 -1
- package/lib/webauthn-mds.js +257 -121
- package/lib/webauthn.js +327 -164
- package/lib/webcrypto.js +10 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/trust.js
CHANGED
|
@@ -44,10 +44,25 @@ var intrinsic = require("./guard-intrinsic");
|
|
|
44
44
|
var _hasOwn = intrinsic.hasOwn;
|
|
45
45
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
46
46
|
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
47
|
+
/** @internal The operations a Trust Bits cell is read through. The token lookup GRANTS a purpose, so
|
|
48
|
+
* read off the live prototype these decide what an anchor is trusted for: measured, with the fold
|
|
49
|
+
* replaced for one token, a row stating only `Email` produced an anchor carrying `serverAuth`. */
|
|
50
|
+
var _strSplit = intrinsic.uncurry(String.prototype.split);
|
|
51
|
+
var _strTrim = intrinsic.uncurry(String.prototype.trim);
|
|
52
|
+
var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
53
|
+
var _arrIndexOf = intrinsic.uncurry(Array.prototype.indexOf);
|
|
54
|
+
var _arrJoin = intrinsic.uncurry(Array.prototype.join);
|
|
55
|
+
var _arrEvery = intrinsic.uncurry(Array.prototype.every);
|
|
47
56
|
var x509 = require("./schema-x509");
|
|
48
57
|
var pkix = require("./schema-pkix");
|
|
49
58
|
var oid = require("./oid");
|
|
50
59
|
var nodeFs = require("fs");
|
|
60
|
+
/** @internal The three file operations a CA bundle is read with, captured at load. The bytes they
|
|
61
|
+
* return become the host's trust anchors, so a replaced `readSync` substitutes the anchor set
|
|
62
|
+
* itself, which is a wider result than any single verdict. The exports are writable. */
|
|
63
|
+
var _fsOpenSync = nodeFs.openSync;
|
|
64
|
+
var _fsReadSync = nodeFs.readSync;
|
|
65
|
+
var _fsCloseSync = nodeFs.closeSync;
|
|
51
66
|
var nodeTls = require("tls");
|
|
52
67
|
var trustanchorSchema = require("./schema-trustanchor");
|
|
53
68
|
|
|
@@ -65,10 +80,10 @@ var PURPOSE_ATTRS = [
|
|
|
65
80
|
["CKA_TRUST_CODE_SIGNING", "codeSigning"],
|
|
66
81
|
];
|
|
67
82
|
|
|
68
|
-
var CKT_RECOGNIZED =
|
|
69
|
-
["CKT_NSS_TRUSTED_DELEGATOR", "CKT_NSS_TRUSTED", "CKT_NSS_MUST_VERIFY_TRUST",
|
|
70
|
-
"CKT_NSS_TRUST_UNKNOWN", "CKT_NSS_NOT_TRUSTED", "CKT_NSS_VALID_DELEGATOR"]
|
|
71
|
-
|
|
83
|
+
var CKT_RECOGNIZED = intrinsic.create(null);
|
|
84
|
+
intrinsic.forEach(["CKT_NSS_TRUSTED_DELEGATOR", "CKT_NSS_TRUSTED", "CKT_NSS_MUST_VERIFY_TRUST",
|
|
85
|
+
"CKT_NSS_TRUST_UNKNOWN", "CKT_NSS_NOT_TRUSTED", "CKT_NSS_VALID_DELEGATOR"],
|
|
86
|
+
function (t) { CKT_RECOGNIZED[t] = true; });
|
|
72
87
|
var CKT_DELEGATOR = "CKT_NSS_TRUSTED_DELEGATOR";
|
|
73
88
|
|
|
74
89
|
function _isNameChar(c) { return (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95; }
|
|
@@ -105,16 +120,21 @@ function _unquote(s) {
|
|
|
105
120
|
return _strSlice(s, 1, n - 1);
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
/** @internal Each piece is APPENDED with the operation that defines an own property. An index
|
|
124
|
+
* assignment writes through Set, which walks the prototype for a numeric setter, so an accessor
|
|
125
|
+
* installed on `Array.prototype` takes the piece and the index reads back as whatever its getter
|
|
126
|
+
* answers: substituting a line of a certdata trust object replaced its `CK_TRUST` token, and a root
|
|
127
|
+
* the file marks NOT_TRUSTED produced an anchor trusted as a delegator for server authentication. */
|
|
108
128
|
function _splitWhitespace(s) {
|
|
109
129
|
var out = [], i = 0, n = s.length, pieceStart = 0;
|
|
110
130
|
while (i < n) {
|
|
111
131
|
if (_isWsCode(_charCodeAt(s, i))) {
|
|
112
|
-
out
|
|
132
|
+
intrinsic.append(out, _strSlice(s, pieceStart, i));
|
|
113
133
|
while (i < n && _isWsCode(_charCodeAt(s, i))) i += 1;
|
|
114
134
|
pieceStart = i;
|
|
115
135
|
} else i += 1;
|
|
116
136
|
}
|
|
117
|
-
out
|
|
137
|
+
intrinsic.append(out, _strSlice(s, pieceStart));
|
|
118
138
|
return out;
|
|
119
139
|
}
|
|
120
140
|
|
|
@@ -129,15 +149,15 @@ function _splitLines(text) {
|
|
|
129
149
|
while (i < n) {
|
|
130
150
|
var c = _charCodeAt(text, i);
|
|
131
151
|
if (c === 13) {
|
|
132
|
-
out
|
|
152
|
+
intrinsic.append(out, _strSlice(text, start, i));
|
|
133
153
|
if (i + 1 < n && _charCodeAt(text, i + 1) === 10) i += 1;
|
|
134
154
|
i += 1; start = i;
|
|
135
155
|
} else if (c === 10) {
|
|
136
|
-
out
|
|
156
|
+
intrinsic.append(out, _strSlice(text, start, i));
|
|
137
157
|
i += 1; start = i;
|
|
138
158
|
} else i += 1;
|
|
139
159
|
}
|
|
140
|
-
out
|
|
160
|
+
intrinsic.append(out, _strSlice(text, start));
|
|
141
161
|
return out;
|
|
142
162
|
}
|
|
143
163
|
|
|
@@ -164,25 +184,28 @@ function _readOctalBody(lines, idx) {
|
|
|
164
184
|
var out = [];
|
|
165
185
|
var cap = LIMITS.TRUST_MAX_OCTAL_BYTES;
|
|
166
186
|
for (var i = idx; i < lines.length; i++) {
|
|
167
|
-
var t = lines[i]
|
|
168
|
-
|
|
187
|
+
var t = _strTrim(lines[i]);
|
|
188
|
+
/** @internal The bytes a MULTILINE_OCTAL body decodes to are constructed with the capture. This is
|
|
189
|
+
* the certificate DER itself, along with the CKA_ISSUER and CKA_SERIAL_NUMBER a pairing is decided
|
|
190
|
+
* on, so a replaced construction substitutes an anchor rather than steering one check. */
|
|
191
|
+
if (t === "END") return { bytes: intrinsic.bufferFrom(out), next: i + 1 };
|
|
169
192
|
if (t === "") throw E("trust/bad-octal", "MULTILINE_OCTAL body interrupted by a blank line before END");
|
|
170
193
|
var chunks = _splitWhitespace(t);
|
|
171
194
|
for (var c = 0; c < chunks.length; c++) {
|
|
172
195
|
var chunk = chunks[c];
|
|
173
196
|
for (var k = 0; k < chunk.length; k += 4) {
|
|
174
|
-
if (chunk
|
|
175
|
-
throw E("trust/bad-octal", "MULTILINE_OCTAL expects \\ooo escapes, got " +
|
|
197
|
+
if (_charCodeAt(chunk, k) !== 92 ) {
|
|
198
|
+
throw E("trust/bad-octal", "MULTILINE_OCTAL expects \\ooo escapes, got " + intrinsic.stringify(_strSlice(chunk, k, k + 4)));
|
|
176
199
|
}
|
|
177
|
-
var d = chunk
|
|
200
|
+
var d = _strSlice(chunk, k + 1, k + 4);
|
|
178
201
|
if (!_isOctal3(d)) {
|
|
179
|
-
throw E("trust/bad-octal", "a MULTILINE_OCTAL escape is a backslash + exactly three octal digits, got " +
|
|
202
|
+
throw E("trust/bad-octal", "a MULTILINE_OCTAL escape is a backslash + exactly three octal digits, got " + intrinsic.stringify("\\" + d));
|
|
180
203
|
}
|
|
181
|
-
if (d
|
|
204
|
+
if (_charCodeAt(d, 0) > 51 ) {
|
|
182
205
|
throw E("trust/bad-octal", "octal escape \\" + d + " exceeds \\377 (one byte)");
|
|
183
206
|
}
|
|
184
207
|
if (out.length >= cap) throw E("trust/bad-block", "MULTILINE_OCTAL blob exceeds LIMITS.TRUST_MAX_OCTAL_BYTES");
|
|
185
|
-
out.
|
|
208
|
+
guard.list.append(out, intrinsic.parseInt(d, 8));
|
|
186
209
|
}
|
|
187
210
|
}
|
|
188
211
|
}
|
|
@@ -194,27 +217,27 @@ function _lexObjects(text) {
|
|
|
194
217
|
var n = lines.length;
|
|
195
218
|
var i = 0, sawBegin = false;
|
|
196
219
|
for (; i < n; i++) {
|
|
197
|
-
if (lines[i]
|
|
220
|
+
if (_strTrim(lines[i]) === "BEGINDATA") { sawBegin = true; i++; break; }
|
|
198
221
|
}
|
|
199
222
|
if (!sawBegin) throw E("trust/bad-input", "not a certdata stream: no BEGINDATA line");
|
|
200
223
|
|
|
201
224
|
var objects = [];
|
|
202
225
|
var cur = null;
|
|
203
226
|
while (i < n) {
|
|
204
|
-
var t = lines[i]
|
|
227
|
+
var t = _strTrim(lines[i]);
|
|
205
228
|
if (t === "") {
|
|
206
|
-
if (cur) {
|
|
229
|
+
if (cur) { guard.list.append(objects, cur); cur = null; }
|
|
207
230
|
i++; continue;
|
|
208
231
|
}
|
|
209
|
-
if (t
|
|
232
|
+
if (_charAt(t, 0) === "#") { i++; continue; }
|
|
210
233
|
var m = _parseAttrLine(t);
|
|
211
|
-
if (!m) throw E("trust/bad-block", "unexpected line in the certdata object stream: " +
|
|
234
|
+
if (!m) throw E("trust/bad-block", "unexpected line in the certdata object stream: " + intrinsic.stringify(_strSlice(t, 0, 64)));
|
|
212
235
|
var name = m[0], type = m[1];
|
|
213
|
-
var rawVal = m[2] !== undefined ? m[2]
|
|
236
|
+
var rawVal = m[2] !== undefined ? _strTrim(m[2]) : undefined;
|
|
214
237
|
if (!type) throw E("trust/bad-block", "attribute line is missing its type token: " + name);
|
|
215
238
|
if (!cur) {
|
|
216
239
|
if (objects.length >= LIMITS.TRUST_MAX_OBJECTS) throw E("trust/bad-block", "certdata object count exceeds LIMITS.TRUST_MAX_OBJECTS");
|
|
217
|
-
cur = { attrs:
|
|
240
|
+
cur = { attrs: intrinsic.create(null) };
|
|
218
241
|
}
|
|
219
242
|
if (cur.attrs[name]) throw E("trust/bad-block", "duplicate attribute " + name + " within one object block");
|
|
220
243
|
|
|
@@ -235,18 +258,18 @@ function _lexObjects(text) {
|
|
|
235
258
|
entry = { type: type, value: q };
|
|
236
259
|
} else if (type === "CK_TRUST") {
|
|
237
260
|
if (rawVal === undefined || _containsWs(rawVal)) throw E("trust/bad-block", name + " CK_TRUST must carry a single token value");
|
|
238
|
-
if (!CKT_RECOGNIZED[rawVal]) throw E("trust/bad-trust-value", "unrecognized CK_TRUST value " +
|
|
261
|
+
if (!CKT_RECOGNIZED[rawVal]) throw E("trust/bad-trust-value", "unrecognized CK_TRUST value " + intrinsic.stringify(rawVal) + " on " + name);
|
|
239
262
|
entry = { type: type, value: rawVal };
|
|
240
263
|
} else if (type === "CK_OBJECT_CLASS" || type === "CK_CERTIFICATE_TYPE") {
|
|
241
264
|
if (rawVal === undefined || _containsWs(rawVal)) throw E("trust/bad-block", name + " " + type + " must carry a single token value");
|
|
242
265
|
entry = { type: type, value: rawVal };
|
|
243
266
|
} else {
|
|
244
|
-
throw E("trust/bad-block", "unrecognized attribute type " +
|
|
267
|
+
throw E("trust/bad-block", "unrecognized attribute type " + intrinsic.stringify(type) + " on " + name);
|
|
245
268
|
}
|
|
246
269
|
cur.attrs[name] = entry;
|
|
247
270
|
i++;
|
|
248
271
|
}
|
|
249
|
-
if (cur)
|
|
272
|
+
if (cur) guard.list.append(objects, cur);
|
|
250
273
|
return objects;
|
|
251
274
|
}
|
|
252
275
|
|
|
@@ -259,10 +282,13 @@ function _requireOctal(obj, name, what) {
|
|
|
259
282
|
return a.bytes;
|
|
260
283
|
}
|
|
261
284
|
|
|
285
|
+
/** @internal The TLV the time is decoded from is assembled with captured operations. A distrust date
|
|
286
|
+
* decides from when an anchor is no longer trusted for a purpose, so an assembly answering with a
|
|
287
|
+
* later date leaves a distrusted root in service. */
|
|
262
288
|
function _strictTime(payload, code, label) {
|
|
263
289
|
var tlv;
|
|
264
|
-
if (payload.length === 13) tlv =
|
|
265
|
-
else if (payload.length === 15) tlv =
|
|
290
|
+
if (payload.length === 13) tlv = intrinsic.bufferConcat([intrinsic.bufferFrom([0x17, 0x0d]), payload]);
|
|
291
|
+
else if (payload.length === 15) tlv = intrinsic.bufferConcat([intrinsic.bufferFrom([0x18, 0x0f]), payload]);
|
|
266
292
|
else throw E(code, label + " must be a 13-byte UTCTime or a 15-byte GeneralizedTime payload, got " + payload.length + " bytes");
|
|
267
293
|
try { return asn1.read.time(asn1.decode(tlv)); }
|
|
268
294
|
catch (e) { throw E(code, label + " does not decode as a strict time", e); }
|
|
@@ -288,14 +314,17 @@ function _certEntry(obj) {
|
|
|
288
314
|
var issuer = _requireOctal(obj, "CKA_ISSUER", "certificate");
|
|
289
315
|
var serial = _requireOctal(obj, "CKA_SERIAL_NUMBER", "certificate");
|
|
290
316
|
|
|
291
|
-
|
|
317
|
+
/** @internal Each of the three pairing refusals below compares through a captured operation. They
|
|
318
|
+
* decide that a certdata trust object describes THIS certificate, so one answering that two
|
|
319
|
+
* different values are equal attaches another object's purposes to it. */
|
|
320
|
+
if (!intrinsic.bufferEquals(issuer, cert.issuer.bytes)) {
|
|
292
321
|
throw E("trust/pairing-mismatch", "CKA_ISSUER disagrees with the certificate's issuer DER");
|
|
293
322
|
}
|
|
294
323
|
var serialContentHex;
|
|
295
324
|
try {
|
|
296
325
|
var sn = asn1.decode(serial);
|
|
297
326
|
asn1.read.integer(sn);
|
|
298
|
-
serialContentHex = sn.content
|
|
327
|
+
serialContentHex = intrinsic.bufToString(sn.content, "hex");
|
|
299
328
|
} catch (e) {
|
|
300
329
|
throw E("trust/pairing-mismatch", "CKA_SERIAL_NUMBER is not a DER INTEGER", e);
|
|
301
330
|
}
|
|
@@ -303,7 +332,7 @@ function _certEntry(obj) {
|
|
|
303
332
|
throw E("trust/pairing-mismatch", "CKA_SERIAL_NUMBER disagrees with the certificate's serial");
|
|
304
333
|
}
|
|
305
334
|
var subject = obj.attrs["CKA_SUBJECT"];
|
|
306
|
-
if (subject && (subject.type !== "MULTILINE_OCTAL" || !subject.bytes
|
|
335
|
+
if (subject && (subject.type !== "MULTILINE_OCTAL" || !intrinsic.bufferEquals(subject.bytes, cert.subject.bytes))) {
|
|
307
336
|
throw E("trust/pairing-mismatch", "CKA_SUBJECT disagrees with the certificate's subject DER");
|
|
308
337
|
}
|
|
309
338
|
|
|
@@ -312,14 +341,17 @@ function _certEntry(obj) {
|
|
|
312
341
|
var policy = obj.attrs["CKA_NSS_MOZILLA_CA_POLICY"];
|
|
313
342
|
if (policy && policy.type !== "CK_BBOOL") throw E("trust/bad-block", "CKA_NSS_MOZILLA_CA_POLICY must be CK_BBOOL");
|
|
314
343
|
|
|
315
|
-
var distrustAfter =
|
|
344
|
+
var distrustAfter = intrinsic.create(null);
|
|
316
345
|
var server = _distrustDate(obj, "CKA_NSS_SERVER_DISTRUST_AFTER");
|
|
317
346
|
if (server) distrustAfter.serverAuth = server;
|
|
318
347
|
var email = _distrustDate(obj, "CKA_NSS_EMAIL_DISTRUST_AFTER");
|
|
319
348
|
if (email) distrustAfter.emailProtection = email;
|
|
320
349
|
|
|
350
|
+
/** @internal The pairing key is built with the captured encoding. It is what a trust object and a
|
|
351
|
+
* certificate are matched on, so an encoding answering with another certificate's issuer or serial
|
|
352
|
+
* hands an otherwise orphaned object's delegator purposes to an unrelated certificate. */
|
|
321
353
|
return {
|
|
322
|
-
key:
|
|
354
|
+
key: intrinsic.bufToString(issuer, "hex") + "/" + intrinsic.bufToString(serial, "hex"),
|
|
323
355
|
der: der,
|
|
324
356
|
cert: cert,
|
|
325
357
|
label: label ? label.value : null,
|
|
@@ -333,13 +365,13 @@ function _trustEntry(obj) {
|
|
|
333
365
|
var issuer = _requireOctal(obj, "CKA_ISSUER", "trust");
|
|
334
366
|
var serial = _requireOctal(obj, "CKA_SERIAL_NUMBER", "trust");
|
|
335
367
|
var purposes = intrinsic.assign(intrinsic.create(null), { serverAuth: false, emailProtection: false, codeSigning: false });
|
|
336
|
-
|
|
368
|
+
intrinsic.forEach(PURPOSE_ATTRS, function (pair) {
|
|
337
369
|
var a = obj.attrs[pair[0]];
|
|
338
370
|
if (!a) return;
|
|
339
371
|
if (a.type !== "CK_TRUST") throw E("trust/bad-block", pair[0] + " must be CK_TRUST");
|
|
340
372
|
purposes[pair[1]] = a.value === CKT_DELEGATOR;
|
|
341
373
|
});
|
|
342
|
-
return { key:
|
|
374
|
+
return { key: intrinsic.bufToString(issuer, "hex") + "/" + intrinsic.bufToString(serial, "hex"), purposes: purposes };
|
|
343
375
|
}
|
|
344
376
|
|
|
345
377
|
|
|
@@ -366,9 +398,9 @@ function _mkAnchor(cert, meta) {
|
|
|
366
398
|
}
|
|
367
399
|
var derived = {
|
|
368
400
|
name: _copyName(cert.subject),
|
|
369
|
-
publicKey:
|
|
401
|
+
publicKey: intrinsic.bufferFrom(spki.bytes),
|
|
370
402
|
algorithm: spki.algorithm.oid,
|
|
371
|
-
parameters: spki.algorithm.parameters == null ? spki.algorithm.parameters :
|
|
403
|
+
parameters: spki.algorithm.parameters == null ? spki.algorithm.parameters : intrinsic.bufferFrom(spki.algorithm.parameters),
|
|
372
404
|
purposes: _purposesOrNull(meta.purposes),
|
|
373
405
|
distrustAfter: _copyDistrustAfter(meta.distrustAfter),
|
|
374
406
|
};
|
|
@@ -379,7 +411,7 @@ function _mkAnchor(cert, meta) {
|
|
|
379
411
|
dc.pathLenConstraint = certPathLen;
|
|
380
412
|
derived.constraints = dc;
|
|
381
413
|
}
|
|
382
|
-
|
|
414
|
+
intrinsic.weakSet(_DERIVED_FROM, entry, derived);
|
|
383
415
|
return entry;
|
|
384
416
|
}
|
|
385
417
|
var _DERIVED_FROM = new WeakMap();
|
|
@@ -408,34 +440,44 @@ function _copyDistrustAfter(src) {
|
|
|
408
440
|
if (!src || typeof src !== "object") return out;
|
|
409
441
|
var names = guard.identifier.optionNames(src);
|
|
410
442
|
for (var i = 0; i < names.length; i++) {
|
|
411
|
-
out[names[i]] = guard.time.isDate(src[names[i]]) ? new Date(guard.time.instantOf(src[names[i]])) : src[names[i]];
|
|
443
|
+
out[names[i]] = guard.time.isDate(src[names[i]]) ? new intrinsic.Date(guard.time.instantOf(src[names[i]])) : src[names[i]];
|
|
412
444
|
}
|
|
413
445
|
return out;
|
|
414
446
|
}
|
|
415
447
|
|
|
448
|
+
/** @internal The three classifications that decide whether a field is copied are captured. Each one
|
|
449
|
+
* answering `false` returns the caller's own object or buffer in place of the copy, which leaves the
|
|
450
|
+
* anchor's name mutable after it has been validated. */
|
|
416
451
|
function _copyName(name) {
|
|
417
|
-
if (!name || !
|
|
452
|
+
if (!name || !intrinsic.isArray(name.rdns)) return name;
|
|
418
453
|
var out = guard.identifier.ownOptions(name);
|
|
419
454
|
out.rdns = guard.list.copyMap(name.rdns, function (rdn) {
|
|
420
|
-
return
|
|
455
|
+
return intrinsic.isArray(rdn) ? guard.list.copyMap(rdn, function (atv) { return guard.identifier.ownOptions(atv); }) : rdn;
|
|
421
456
|
});
|
|
422
|
-
out.bytes =
|
|
457
|
+
out.bytes = intrinsic.isBuffer(name.bytes) ? intrinsic.bufferFrom(name.bytes) : name.bytes;
|
|
423
458
|
return out;
|
|
424
459
|
}
|
|
425
460
|
|
|
461
|
+
/** @internal The sort comes from the capture like the join and the test below it. Read off the live
|
|
462
|
+
* prototype it decides what the comparison SEES: a replacement answering with empty arrays makes the
|
|
463
|
+
* joins equal and the per-name test vacuous, so two rows for one root stop disagreeing without a
|
|
464
|
+
* single date being compared, and the pairing refusal is skipped while the later cutoff is kept. */
|
|
426
465
|
function _datesEqual(x, y) {
|
|
427
|
-
var kx = guard.identifier.optionNames(x)
|
|
428
|
-
if (kx
|
|
429
|
-
return kx
|
|
466
|
+
var kx = intrinsic.sort(guard.identifier.optionNames(x)), ky = intrinsic.sort(guard.identifier.optionNames(y));
|
|
467
|
+
if (_arrJoin(kx, ",") !== _arrJoin(ky, ",")) return false;
|
|
468
|
+
return _arrEvery(kx, function (k) { return guard.time.instantOf(x[k]) === guard.time.instantOf(y[k]); });
|
|
430
469
|
}
|
|
431
470
|
|
|
471
|
+
/** @internal The comparison that decides whether two rows for one root AGREE comes from the capture.
|
|
472
|
+
* A replacement answering `true` makes a conflicting pair compare equal, so the pairing refusal is
|
|
473
|
+
* skipped and the first row's grants are kept for both. */
|
|
432
474
|
function _purposesEqual(x, y) {
|
|
433
|
-
return PURPOSES
|
|
475
|
+
return _arrEvery(PURPOSES, function (p) { return x[p] === y[p]; });
|
|
434
476
|
}
|
|
435
477
|
|
|
436
478
|
function _paramsEqual(x, y) {
|
|
437
479
|
if (x === null || y === null) return x === y;
|
|
438
|
-
return
|
|
480
|
+
return intrinsic.isBuffer(x) && intrinsic.isBuffer(y) && intrinsic.bufferEquals(x, y);
|
|
439
481
|
}
|
|
440
482
|
|
|
441
483
|
function _anchorsAgree(x, y) {
|
|
@@ -447,13 +489,17 @@ function _anchorsAgree(x, y) {
|
|
|
447
489
|
_datesEqual(x.distrustAfter, y.distrustAfter);
|
|
448
490
|
}
|
|
449
491
|
|
|
492
|
+
/** @internal The dedup key identifies a root by its subject and its key, and the map that holds the
|
|
493
|
+
* keys is constructed here. Both come from captures: an encoding that answers with another root's
|
|
494
|
+
* bytes, or a map already holding this root's key, makes a second row for one root compare against
|
|
495
|
+
* metadata it never carried, which is how a disagreeing pair passes the refusal below. */
|
|
450
496
|
function _dedupAnchors(anchors) {
|
|
451
|
-
var seen =
|
|
497
|
+
var seen = intrinsic.create(null);
|
|
452
498
|
var out = [];
|
|
453
|
-
|
|
454
|
-
var key = a.subjectDer
|
|
499
|
+
intrinsic.forEach(anchors, function (a) {
|
|
500
|
+
var key = intrinsic.bufToString(a.subjectDer, "hex") + "/" + intrinsic.bufToString(a.publicKey, "hex");
|
|
455
501
|
var prev = seen[key];
|
|
456
|
-
if (!prev) { seen[key] = a;
|
|
502
|
+
if (!prev) { seen[key] = a; guard.list.append(out, a); return; }
|
|
457
503
|
if (!_anchorsAgree(prev, a)) {
|
|
458
504
|
throw E("trust/pairing-mismatch", "two entries for one root (same subject + key) disagree on trust metadata");
|
|
459
505
|
}
|
|
@@ -513,9 +559,12 @@ function parseCertdata(text) {
|
|
|
513
559
|
var objects = _lexObjects(text);
|
|
514
560
|
|
|
515
561
|
var certs = [];
|
|
516
|
-
|
|
562
|
+
/** @internal The pairing map is constructed from the capture. A map that already holds an entry
|
|
563
|
+
* under a key a trust object will present is the same bypass as a mis-encoded key: the lookup
|
|
564
|
+
* below finds it and the object's purposes reach a certificate it does not describe. */
|
|
565
|
+
var byKey = intrinsic.create(null);
|
|
517
566
|
var trusts = [];
|
|
518
|
-
|
|
567
|
+
intrinsic.forEach(objects, function (obj) {
|
|
519
568
|
var cls = obj.attrs["CKA_CLASS"];
|
|
520
569
|
if (!cls || cls.type !== "CK_OBJECT_CLASS") {
|
|
521
570
|
throw E("trust/bad-block", "object block is missing CKA_CLASS CK_OBJECT_CLASS");
|
|
@@ -524,7 +573,7 @@ function parseCertdata(text) {
|
|
|
524
573
|
var e = _certEntry(obj);
|
|
525
574
|
var prev = byKey[e.key];
|
|
526
575
|
if (prev) {
|
|
527
|
-
if (!prev.der
|
|
576
|
+
if (!intrinsic.bufferEquals(prev.der, e.der) || prev.label !== e.label ||
|
|
528
577
|
prev.mozillaCaPolicy !== e.mozillaCaPolicy ||
|
|
529
578
|
!_datesEqual(prev.distrustAfter, e.distrustAfter)) {
|
|
530
579
|
throw E("trust/pairing-mismatch", "two certificate objects share an (issuer, serial) but disagree");
|
|
@@ -532,13 +581,13 @@ function parseCertdata(text) {
|
|
|
532
581
|
return;
|
|
533
582
|
}
|
|
534
583
|
byKey[e.key] = e;
|
|
535
|
-
|
|
584
|
+
guard.list.append(certs, e);
|
|
536
585
|
} else if (cls.value === "CKO_NSS_TRUST") {
|
|
537
|
-
|
|
586
|
+
guard.list.append(trusts, _trustEntry(obj));
|
|
538
587
|
}
|
|
539
588
|
});
|
|
540
589
|
|
|
541
|
-
|
|
590
|
+
intrinsic.forEach(trusts, function (t) {
|
|
542
591
|
var c = byKey[t.key];
|
|
543
592
|
if (!c) return;
|
|
544
593
|
if (c.purposes) {
|
|
@@ -551,7 +600,7 @@ function parseCertdata(text) {
|
|
|
551
600
|
});
|
|
552
601
|
|
|
553
602
|
return {
|
|
554
|
-
anchors: _dedupAnchors(
|
|
603
|
+
anchors: _dedupAnchors(intrinsic.mapList(certs, function (c) {
|
|
555
604
|
return _mkAnchor(c.cert, {
|
|
556
605
|
distrustAfter: c.distrustAfter,
|
|
557
606
|
purposes: c.purposes || _copyPurposes(null),
|
|
@@ -580,19 +629,24 @@ function _csvRows(text) {
|
|
|
580
629
|
if (field.length >= fieldCap) throw E("trust/bad-csv", "a CSV field exceeds LIMITS.TRUST_MAX_CSV_FIELD_BYTES");
|
|
581
630
|
field += c;
|
|
582
631
|
}
|
|
583
|
-
function endField() {
|
|
632
|
+
function endField() { guard.list.append(row, field); field = ""; quoted = false; closed = false; }
|
|
584
633
|
function endRow() {
|
|
585
634
|
endField();
|
|
586
635
|
if (rows.length >= LIMITS.TRUST_MAX_CSV_ROWS) throw E("trust/bad-csv", "CSV row count exceeds LIMITS.TRUST_MAX_CSV_ROWS");
|
|
587
|
-
|
|
636
|
+
guard.list.append(rows, row); row = [];
|
|
588
637
|
}
|
|
589
638
|
|
|
639
|
+
/** @internal The lexer reads every character through the capture. It decides which characters
|
|
640
|
+
* reach the cell readers, so a replaced `charAt` changes the TOKEN those readers are handed even
|
|
641
|
+
* though they read it safely: measured, feeding the characters of `Email` at a `Bogus` token's
|
|
642
|
+
* offsets turned a refusal into an anchor carrying `emailProtection`. Converting the readers alone
|
|
643
|
+
* left this, which is why the module is converted rather than the sites that kept being found. */
|
|
590
644
|
var i = 0, n = text.length;
|
|
591
645
|
while (i < n) {
|
|
592
|
-
var ch = text
|
|
646
|
+
var ch = _charAt(text, i);
|
|
593
647
|
if (inQuotes) {
|
|
594
648
|
if (ch === "\"") {
|
|
595
|
-
if (text
|
|
649
|
+
if (_charAt(text, i + 1) === "\"") { appendChar("\""); i += 2; continue; }
|
|
596
650
|
inQuotes = false; closed = true; i++; continue;
|
|
597
651
|
}
|
|
598
652
|
appendChar(ch); i++; continue;
|
|
@@ -602,7 +656,7 @@ function _csvRows(text) {
|
|
|
602
656
|
inQuotes = true; quoted = true; i++; continue;
|
|
603
657
|
}
|
|
604
658
|
if (ch === ",") { endField(); i++; continue; }
|
|
605
|
-
if (ch === "\r" && text
|
|
659
|
+
if (ch === "\r" && _charAt(text, i + 1) === "\n") { endRow(); i += 2; continue; }
|
|
606
660
|
if (ch === "\n" || ch === "\r") { endRow(); i++; continue; }
|
|
607
661
|
if (closed) throw E("trust/bad-csv", "content after a closing quote (RFC 4180)");
|
|
608
662
|
appendChar(ch); i++;
|
|
@@ -621,12 +675,20 @@ var _TRUST_BIT_TOKENS = intrinsic.assign(intrinsic.create(null), {
|
|
|
621
675
|
});
|
|
622
676
|
function _trustBits(cell) {
|
|
623
677
|
var purposes = intrinsic.assign(intrinsic.create(null), { serverAuth: false, emailProtection: false, codeSigning: false });
|
|
624
|
-
|
|
625
|
-
|
|
678
|
+
/** @internal The string conversion is captured as well. A CSV field is already a string, so this
|
|
679
|
+
* guards the case where it is not; read off the live global it is one more operation that decides
|
|
680
|
+
* the TOKEN, and a `String` replacement mapping the cell `Email` to `Websites` reproduced the whole
|
|
681
|
+
* trust-widening bypass that the captured split, trim and fold had just closed. */
|
|
682
|
+
/** @internal The cell is scanned on the delimiter CHARACTER. A separator string carries a
|
|
683
|
+
* `Symbol.split` method the split operation is specified to look up and call, so capturing `split`
|
|
684
|
+
* leaves the separator's prototype chain in the operation; hard rule 11 bans `split` in lib/ for
|
|
685
|
+
* that reason. A hook answering `Websites` for a cell reading `Email` grants `serverAuth`. */
|
|
686
|
+
intrinsic.forEach(intrinsic.splitChar(intrinsic.String(cell), 59), function (tok) {
|
|
687
|
+
var t = _strTrim(tok);
|
|
626
688
|
if (t === "") return;
|
|
627
|
-
var l =
|
|
689
|
+
var l = intrinsic.toLowerCase(t);
|
|
628
690
|
if (!_hasOwn(_TRUST_BIT_TOKENS, l)) {
|
|
629
|
-
throw E("trust/bad-csv", "unrecognized Trust Bits token " +
|
|
691
|
+
throw E("trust/bad-csv", "unrecognized Trust Bits token " + intrinsic.stringify(t));
|
|
630
692
|
}
|
|
631
693
|
var p = _TRUST_BIT_TOKENS[l];
|
|
632
694
|
if (p) purposes[p] = true;
|
|
@@ -634,25 +696,30 @@ function _trustBits(cell) {
|
|
|
634
696
|
return purposes;
|
|
635
697
|
}
|
|
636
698
|
|
|
699
|
+
/** @internal The trim comes from the capture: an empty cell means NO distrust date, so a replacement
|
|
700
|
+
* answering with the empty string for a cell the parser would have refused removes a distrust date
|
|
701
|
+
* rather than failing. Measured, `not-a-date` trimmed to `""` turned `trust/bad-csv` into `null`. */
|
|
637
702
|
function _csvDate(cell, label) {
|
|
638
|
-
var s = String(cell)
|
|
703
|
+
var s = _strTrim(intrinsic.String(cell));
|
|
639
704
|
if (s === "") return null;
|
|
640
705
|
var m = _parseYmd(s);
|
|
641
|
-
if (!m) throw E("trust/bad-csv", label + " must be a Y-M-D date, got " +
|
|
706
|
+
if (!m) throw E("trust/bad-csv", label + " must be a Y-M-D date, got " + intrinsic.stringify(s));
|
|
642
707
|
var pad = function (x) { return x.length === 1 ? "0" + x : x; };
|
|
643
|
-
var payload =
|
|
708
|
+
var payload = intrinsic.bufferFrom(m[0] + pad(m[1]) + pad(m[2]) + "235959Z", "latin1");
|
|
644
709
|
try { return _strictTime(payload, "trust/bad-csv", label); }
|
|
645
710
|
catch (e) {
|
|
646
711
|
if (e && e.code === "trust/bad-csv") throw e;
|
|
647
|
-
throw E("trust/bad-csv", label + " is not a real calendar date: " +
|
|
712
|
+
throw E("trust/bad-csv", label + " is not a real calendar date: " + intrinsic.stringify(s), e);
|
|
648
713
|
}
|
|
649
714
|
}
|
|
650
715
|
|
|
716
|
+
/** @internal The cell this reads holds the CERTIFICATE an anchor is built from, so the operations
|
|
717
|
+
* that unquote it come from the captures like the rest of the CSV path. */
|
|
651
718
|
function _pemCell(cell) {
|
|
652
|
-
var s = String(cell)
|
|
653
|
-
if (s
|
|
654
|
-
if (
|
|
655
|
-
s = s
|
|
719
|
+
var s = _strTrim(intrinsic.String(cell));
|
|
720
|
+
if (_charCodeAt(s, 0) === 39) s = _strSlice(s, 1);
|
|
721
|
+
if (_charCodeAt(s, s.length - 1) === 39) s = _strSlice(s, 0, -1);
|
|
722
|
+
s = _strTrim(s);
|
|
656
723
|
if (s === "") throw E("trust/bad-csv", "PEM Info cell is empty");
|
|
657
724
|
try { return x509.parse(s); }
|
|
658
725
|
catch (e) { throw E("trust/not-a-certificate", "PEM Info does not parse as an X.509 certificate", e); }
|
|
@@ -702,17 +769,28 @@ function parseCcadbCsv(text) {
|
|
|
702
769
|
var rows = _csvRows(text);
|
|
703
770
|
if (rows.length === 0) throw E("trust/bad-csv", "CSV input carries no header row");
|
|
704
771
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
772
|
+
/** @internal The header names decide WHICH column each field is read from, so the trim comes from
|
|
773
|
+
* the capture. Read off the live prototype, a replacement that renames the real Trust Bits header
|
|
774
|
+
* and answers `Trust Bits` for another column makes the purposes come from that column: measured, a
|
|
775
|
+
* row stating `Email` produced an anchor carrying `serverAuth` taken from a decoy cell. */
|
|
776
|
+
var header = intrinsic.mapList(rows[0], function (h) { return _strTrim(h); });
|
|
777
|
+
/** @internal The name-to-index map is constructed from the capture as well. A map handed back
|
|
778
|
+
* already holding `Trust Bits` is the same result as a mis-trimmed header: the required-column
|
|
779
|
+
* check below finds the name present and every field is read from the column it names. */
|
|
780
|
+
var col = intrinsic.create(null);
|
|
781
|
+
/** @internal The iteration that maps a header NAME to its column index, and the membership test
|
|
782
|
+
* that refuses a duplicated required column, both come from the captures. A replaced `forEach`
|
|
783
|
+
* supplied the decoy column's index for `Trust Bits` and swapped the two distrust-date columns, and
|
|
784
|
+
* a replaced `indexOf` answering -1 let a duplicated required column through. */
|
|
785
|
+
intrinsic.forEach(header, function (h, idx) {
|
|
708
786
|
if (col[h] !== undefined) {
|
|
709
|
-
if (CSV_REQUIRED
|
|
787
|
+
if (_arrIndexOf(CSV_REQUIRED, h) !== -1) throw E("trust/bad-csv", "duplicate required column " + intrinsic.stringify(h));
|
|
710
788
|
return;
|
|
711
789
|
}
|
|
712
790
|
col[h] = idx;
|
|
713
791
|
});
|
|
714
|
-
|
|
715
|
-
if (col[r] === undefined) throw E("trust/bad-csv", "CSV is missing the required column " +
|
|
792
|
+
intrinsic.forEach(CSV_REQUIRED, function (r) {
|
|
793
|
+
if (col[r] === undefined) throw E("trust/bad-csv", "CSV is missing the required column " + intrinsic.stringify(r));
|
|
716
794
|
});
|
|
717
795
|
|
|
718
796
|
var anchors = [];
|
|
@@ -721,13 +799,13 @@ function parseCcadbCsv(text) {
|
|
|
721
799
|
if (row.length !== header.length) {
|
|
722
800
|
throw E("trust/bad-csv", "CSV row " + (i + 1) + " has " + row.length + " fields; the header has " + header.length);
|
|
723
801
|
}
|
|
724
|
-
var distrustAfter =
|
|
802
|
+
var distrustAfter = intrinsic.create(null);
|
|
725
803
|
var tls = _csvDate(row[col["Distrust for TLS After Date"]], "Distrust for TLS After Date");
|
|
726
804
|
if (tls) distrustAfter.serverAuth = tls;
|
|
727
805
|
var smime = _csvDate(row[col["Distrust for S/MIME After Date"]], "Distrust for S/MIME After Date");
|
|
728
806
|
if (smime) distrustAfter.emailProtection = smime;
|
|
729
|
-
var label = row[col["Common Name or Certificate Name"]]
|
|
730
|
-
|
|
807
|
+
var label = _strTrim(row[col["Common Name or Certificate Name"]]);
|
|
808
|
+
guard.list.append(anchors, _mkAnchor(_pemCell(row[col["PEM Info"]]), {
|
|
731
809
|
distrustAfter: distrustAfter,
|
|
732
810
|
purposes: _trustBits(row[col["Trust Bits"]]),
|
|
733
811
|
label: label === "" ? null : label,
|
|
@@ -948,15 +1026,18 @@ function anchor(entry, opts) {
|
|
|
948
1026
|
}
|
|
949
1027
|
opts = guard.identifier.optionsObject(opts, E, "trust/bad-input", "pki.trust.anchor options");
|
|
950
1028
|
guard.identifier.assertKnownKeys(opts, _ANCHOR_OPTS, E, "trust/bad-input", "unknown pki.trust.anchor option ");
|
|
951
|
-
|
|
1029
|
+
/** @internal The provenance lookup goes through the capture. A hit here is what says this entry is
|
|
1030
|
+
* one the store produced, so a replacement answering with metadata of its own both supplies the
|
|
1031
|
+
* purposes the gate below reads and waives the refusal that stops a rebuilt entry carrying them. */
|
|
1032
|
+
var derived = intrinsic.weakGet(_DERIVED_FROM, entry);
|
|
952
1033
|
var meta = derived || entry;
|
|
953
|
-
if (!derived && (!
|
|
954
|
-
!entry.name || !
|
|
1034
|
+
if (!derived && (!intrinsic.isBuffer(entry.publicKey) || typeof entry.algorithm !== "string" ||
|
|
1035
|
+
!entry.name || !intrinsic.isArray(entry.name.rdns))) {
|
|
955
1036
|
throw E("trust/bad-input", "anchor expects a trust-store entry ({ name, publicKey, algorithm, ... })");
|
|
956
1037
|
}
|
|
957
1038
|
if (opts.purpose !== undefined) {
|
|
958
|
-
if (PURPOSES
|
|
959
|
-
throw E("trust/bad-input", "anchor: opts.purpose must be one of " + PURPOSES
|
|
1039
|
+
if (_arrIndexOf(PURPOSES, opts.purpose) === -1) {
|
|
1040
|
+
throw E("trust/bad-input", "anchor: opts.purpose must be one of " + _arrJoin(PURPOSES, " | "));
|
|
960
1041
|
}
|
|
961
1042
|
if (!meta.purposes || meta.purposes[opts.purpose] !== true) {
|
|
962
1043
|
throw E("trust/purpose-not-trusted", "this root is not a trusted delegator for " + opts.purpose);
|
|
@@ -968,9 +1049,9 @@ function anchor(entry, opts) {
|
|
|
968
1049
|
var nameConstraints = _copyNameConstraints(opts.nameConstraints);
|
|
969
1050
|
var out = {
|
|
970
1051
|
name: _copyName(meta.name),
|
|
971
|
-
publicKey:
|
|
1052
|
+
publicKey: intrinsic.isBuffer(meta.publicKey) ? intrinsic.bufferFrom(meta.publicKey) : meta.publicKey,
|
|
972
1053
|
algorithm: meta.algorithm,
|
|
973
|
-
parameters:
|
|
1054
|
+
parameters: intrinsic.isBuffer(meta.parameters) ? intrinsic.bufferFrom(meta.parameters)
|
|
974
1055
|
: (meta.parameters !== undefined ? meta.parameters : null),
|
|
975
1056
|
distrustAfter: _copyDistrustAfter(meta.distrustAfter),
|
|
976
1057
|
purposes: _purposesOrNull(meta.purposes),
|
|
@@ -1001,7 +1082,7 @@ function _copyConstraints(c) {
|
|
|
1001
1082
|
excluded: _copySubtrees(c.nameConstraints.excluded, "excluded"),
|
|
1002
1083
|
};
|
|
1003
1084
|
}
|
|
1004
|
-
if (c.policySet) out.policySet = intrinsic.
|
|
1085
|
+
if (c.policySet) out.policySet = intrinsic.mapList(c.policySet, function (p) { return p; });
|
|
1005
1086
|
if (c.policyFlags) {
|
|
1006
1087
|
var f = intrinsic.create(null);
|
|
1007
1088
|
intrinsic.forEach(trustanchorSchema.policyFlagBits, function (k) { f[k] = c.policyFlags[k] === true; });
|
|
@@ -1031,13 +1112,13 @@ function _anchorConstraintsOf(taInfo) {
|
|
|
1031
1112
|
var any = false;
|
|
1032
1113
|
if (cp.nameConstr !== null) {
|
|
1033
1114
|
out.nameConstraints = {
|
|
1034
|
-
permitted: intrinsic.
|
|
1035
|
-
excluded: intrinsic.
|
|
1115
|
+
permitted: intrinsic.mapList(cp.nameConstr.permittedSubtrees || [], _subtreeOf),
|
|
1116
|
+
excluded: intrinsic.mapList(cp.nameConstr.excludedSubtrees || [], _subtreeOf),
|
|
1036
1117
|
};
|
|
1037
1118
|
any = true;
|
|
1038
1119
|
}
|
|
1039
1120
|
if (cp.policySet !== null) {
|
|
1040
|
-
out.policySet = intrinsic.
|
|
1121
|
+
out.policySet = intrinsic.mapList(cp.policySet, function (p) { return p.policyIdentifier; });
|
|
1041
1122
|
any = true;
|
|
1042
1123
|
}
|
|
1043
1124
|
if (cp.policyFlags !== null) { out.policyFlags = cp.policyFlags; any = true; }
|
|
@@ -1116,7 +1197,7 @@ function _entryFromAnchor(a, i) {
|
|
|
1116
1197
|
* obvious thing. Keeping the constraints out of reach of that call would hand them an
|
|
1117
1198
|
* UNCONSTRAINED anchor and silently drop every restriction the list stated. */
|
|
1118
1199
|
if (constraints) guard.verdict.set(entry, "constraints", constraints);
|
|
1119
|
-
|
|
1200
|
+
intrinsic.weakSet(_DERIVED_FROM, entry, {
|
|
1120
1201
|
name: _copyName(info.certPath.taName),
|
|
1121
1202
|
publicKey: intrinsic.bufferFrom(spki.bytes),
|
|
1122
1203
|
algorithm: spki.algorithm.oid,
|
|
@@ -1194,26 +1275,26 @@ function discoversBundles(platform) {
|
|
|
1194
1275
|
function _readBundleText(chosen, maxBytes) {
|
|
1195
1276
|
var fd = null, raw, read;
|
|
1196
1277
|
try {
|
|
1197
|
-
fd =
|
|
1278
|
+
fd = _fsOpenSync(chosen, "r");
|
|
1198
1279
|
var buf = intrinsic.bufferAlloc(maxBytes + 1);
|
|
1199
1280
|
/** @internal readSync may return fewer bytes than asked for, so the prefix of one call is not
|
|
1200
1281
|
* the file. Taking it as the whole bundle would drop the roots below the break with nothing
|
|
1201
1282
|
* reported, and would let a file above the cap pass the check underneath it. */
|
|
1202
1283
|
read = 0;
|
|
1203
1284
|
for (;;) {
|
|
1204
|
-
var got =
|
|
1285
|
+
var got = _fsReadSync(fd, buf, read, (maxBytes + 1) - read, read);
|
|
1205
1286
|
if (got === 0) break;
|
|
1206
1287
|
read += got;
|
|
1207
1288
|
if (read >= maxBytes + 1) break;
|
|
1208
1289
|
}
|
|
1209
|
-
raw =
|
|
1290
|
+
raw = intrinsic.byteSlice(buf, 0, read);
|
|
1210
1291
|
} catch (e) {
|
|
1211
1292
|
throw E("trust/no-readable-store", "no CA bundle could be read at " + guard.text.showValue(chosen) +
|
|
1212
1293
|
"; " + _exportHint() + ", or pass opts.allowNodeBundle to use the Mozilla set compiled into " +
|
|
1213
1294
|
"Node, which is NOT what this host trusts", e);
|
|
1214
1295
|
} finally {
|
|
1215
1296
|
if (fd !== null) {
|
|
1216
|
-
try {
|
|
1297
|
+
try { _fsCloseSync(fd); }
|
|
1217
1298
|
// allow:swallow-unverified the descriptor is released on a best-effort basis
|
|
1218
1299
|
catch (_e) { void 0; }
|
|
1219
1300
|
}
|
|
@@ -1260,7 +1341,7 @@ function _readBundleText(chosen, maxBytes) {
|
|
|
1260
1341
|
* console.log(store.skipped.length >= 0); // -> true (entries named, never dropped silently)
|
|
1261
1342
|
*/
|
|
1262
1343
|
function systemAnchors(opts) {
|
|
1263
|
-
opts = opts ||
|
|
1344
|
+
opts = opts || intrinsic.create(null);
|
|
1264
1345
|
guard.identifier.assertPlainRecord(opts, E, "trust/bad-input", "systemAnchors opts");
|
|
1265
1346
|
guard.identifier.assertKnownKeys(opts, _SYSTEM_ANCHOR_OPTS, E, "trust/bad-input",
|
|
1266
1347
|
"unknown systemAnchors option ");
|
|
@@ -1438,11 +1519,13 @@ function _systemResult(anchors, source, chosen, skipped) {
|
|
|
1438
1519
|
return out;
|
|
1439
1520
|
}
|
|
1440
1521
|
|
|
1441
|
-
|
|
1522
|
+
/** @internal Frozen, so a consumer that reads one of these off this object at the call cannot be
|
|
1523
|
+
* handed a replacement in place of it. */
|
|
1524
|
+
module.exports = intrinsic.freeze({
|
|
1442
1525
|
parseCertdata: parseCertdata,
|
|
1443
1526
|
parseCcadbCsv: parseCcadbCsv,
|
|
1444
1527
|
parseTrustAnchorList: parseTrustAnchorList,
|
|
1445
1528
|
systemAnchors: systemAnchors,
|
|
1446
1529
|
discoversBundles: discoversBundles,
|
|
1447
1530
|
anchor: anchor,
|
|
1448
|
-
};
|
|
1531
|
+
});
|