@blamejs/pki 0.9.0 → 0.9.3
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 +36 -0
- package/MIGRATING.md +103 -0
- package/README.md +5 -5
- package/lib/alt-sig.js +3 -3
- package/lib/attrcert-sign.js +5 -5
- package/lib/cmc-verify.js +13 -9
- package/lib/cmp-build.js +2 -2
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-sign.js +21 -0
- package/lib/cms-verify.js +5 -3
- package/lib/constants.js +3 -0
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/csr-sign.js +4 -2
- package/lib/est.js +93 -21
- package/lib/guard-identifier.js +110 -0
- package/lib/guard-name.js +91 -4
- package/lib/inspect.js +4 -4
- package/lib/lint.js +2 -2
- package/lib/mime.js +124 -17
- package/lib/ocsp-verify.js +1 -1
- package/lib/pki-build.js +14 -0
- package/lib/possession.js +20 -14
- package/lib/related-cert.js +72 -11
- package/lib/schema-attrcert.js +5 -5
- package/lib/schema-c509.js +42 -6
- package/lib/schema-cmp.js +3 -3
- package/lib/schema-pkix.js +62 -3
- package/lib/schema-trustanchor.js +4 -4
- package/lib/sigstore.js +1 -1
- package/lib/smime.js +18 -7
- package/lib/trust.js +1 -1
- package/lib/trustanchor-build.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cms-sign.js
CHANGED
|
@@ -205,6 +205,19 @@ function _assertAlgorithmProtectionPairs(pairs, digestAlgId, armAlgId, armTag) {
|
|
|
205
205
|
function _resolveSignerContext(signer, opts) {
|
|
206
206
|
var so = signer || intrinsic.create(null);
|
|
207
207
|
var keyOnly = so.cert == null && so.spki != null;
|
|
208
|
+
/** @internal A descriptor names the digest and the padding the signature is made under, so a field
|
|
209
|
+
* this verb does not read is a request that was never carried out: measured, a `digestAlgoritm`
|
|
210
|
+
* typo signed under sha256 while the caller believed they had asked for sha384, and the same
|
|
211
|
+
* misspelling one argument along in `opts` was already a refusal. The two key sets below have
|
|
212
|
+
* described this shape since they were written, and `pkcs12-build` already holds the same
|
|
213
|
+
* descriptors to them; this is the module that owns them doing the same. */
|
|
214
|
+
guard.identifier.assertKnownDescriptorKeys(so, keyOnly ? KNOWN_SIGNER_KEY_ONLY_KEYS : KNOWN_SIGNER_CERT_KEYS,
|
|
215
|
+
_err, "cms/bad-input", function (k) {
|
|
216
|
+
return "unknown signer field " + guard.text.showValue(k) + " for the " + (keyOnly
|
|
217
|
+
? "key-only form; it takes { key, spki, keyIdentifier } plus the signature parameters"
|
|
218
|
+
: "certificate form; it takes { key, cert } plus the signature parameters") +
|
|
219
|
+
" (pss, digestAlgorithm, combinedRsaSig)";
|
|
220
|
+
});
|
|
208
221
|
var soKey = so.key;
|
|
209
222
|
var soSpki = keyOnly ? guard.bytes.snapshotSource(so.spki, CmsError, "cms/bad-input", "a key-only signer's spki") : null;
|
|
210
223
|
var certDer = keyOnly ? null : _normCertDer(so.cert);
|
|
@@ -534,6 +547,14 @@ function _resolveSignerIndices(spec, n) {
|
|
|
534
547
|
|
|
535
548
|
function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
536
549
|
var so = countersigner || intrinsic.create(null);
|
|
550
|
+
/** @internal A countersignature is a SignerInfo (RFC 5652 sec. 11.4) and its descriptor names the
|
|
551
|
+
* same signature parameters, so a field this reader does not read is the same dropped request it
|
|
552
|
+
* is at `sign`. This reader is its own, so the gate at the other one did not cover it. A
|
|
553
|
+
* countersignature is always the certificate form, which is the `cert` read below. */
|
|
554
|
+
guard.identifier.assertKnownDescriptorKeys(so, KNOWN_SIGNER_CERT_KEYS, _err, "cms/bad-input", function (k) {
|
|
555
|
+
return "unknown countersigner field " + guard.text.showValue(k) +
|
|
556
|
+
"; it takes { key, cert } plus the signature parameters (pss, digestAlgorithm, combinedRsaSig)";
|
|
557
|
+
});
|
|
537
558
|
var certDer = _normCertDer(so.cert);
|
|
538
559
|
var cert = x509.parse(certDer);
|
|
539
560
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
package/lib/cms-verify.js
CHANGED
|
@@ -1170,10 +1170,12 @@ function _snapshotTimestampTrust(opts, allowWeakDigests) {
|
|
|
1170
1170
|
};
|
|
1171
1171
|
}
|
|
1172
1172
|
|
|
1173
|
-
/** @internal
|
|
1174
|
-
*
|
|
1173
|
+
/** @internal The decoder emits the RFC 5280 sec. 4.2.1.3 bit name `nonRepudiation`, never the X.509
|
|
1174
|
+
* spelling `contentCommitment`, so naming the latter here asked for a name nothing defines.
|
|
1175
|
+
* A signature over content is authorized by digitalSignature or nonRepudiation alike; the shared
|
|
1176
|
+
* helper carries the rest of the rule. */
|
|
1175
1177
|
var _permitsSigning = pkix.keyUsagePermits(pkix.makeNS("cms", CmsError, oid),
|
|
1176
|
-
["digitalSignature", "
|
|
1178
|
+
["digitalSignature", "nonRepudiation"]);
|
|
1177
1179
|
|
|
1178
1180
|
function _keyUsagePermitsSigning(parsedCerts, der) {
|
|
1179
1181
|
var entry = guard.list.firstMatch(parsedCerts, function (c) { return intrinsic.bufferEquals(c.der, der); });
|
package/lib/constants.js
CHANGED
|
@@ -189,6 +189,9 @@ var LIMITS = Object.assign(Object.create(null), {
|
|
|
189
189
|
PBKDF2_MAX_SALT: 1024,
|
|
190
190
|
MIME_MAX_BYTES: BYTES.mib(16),
|
|
191
191
|
HEADER_LINE_MAX_OCTETS: 998,
|
|
192
|
+
MIME_MAX_PARTS: 1024,
|
|
193
|
+
MIME_MAX_HEADERS: 1024,
|
|
194
|
+
MIME_MAX_PARAMS: 256,
|
|
192
195
|
COMPRESS_MAX_BYTES: BYTES.mib(16),
|
|
193
196
|
TLS_CERT_MSG_MAX_BYTES: BYTES.mib(16) - 1,
|
|
194
197
|
TLS_CERT_MAX_ENTRIES: 100,
|
package/lib/crl-sign.js
CHANGED
|
@@ -682,7 +682,7 @@ function _verify(crl, issuer) {
|
|
|
682
682
|
var parsed = _coerceCrl(crl);
|
|
683
683
|
var resolved = _resolveIssuer(issuer);
|
|
684
684
|
var issuerMaySign = resolved.cert ? _issuerMaySign(parsed, resolved.cert) : true;
|
|
685
|
-
return crlVerify.verifyCrlSignature(parsed, resolved.spki)
|
|
685
|
+
return intrinsic.chain(crlVerify.verifyCrlSignature(parsed, resolved.spki), function (sigOk) {
|
|
686
686
|
var signatureValid = sigOk === true;
|
|
687
687
|
var valid = issuerMaySign && signatureValid;
|
|
688
688
|
return guard.verdict.of(
|
package/lib/crmf-sign.js
CHANGED
|
@@ -1255,7 +1255,7 @@ function _build(spec, key, opts) {
|
|
|
1255
1255
|
* `Buffer`; a malformed template throws a typed `CrmfError`.
|
|
1256
1256
|
*
|
|
1257
1257
|
* @example
|
|
1258
|
-
* var tpl = pki.crmf.buildCertTemplate({ serialNumber: 42n, issuer: "
|
|
1258
|
+
* var tpl = pki.crmf.buildCertTemplate({ serialNumber: 42n, issuer: "CA" });
|
|
1259
1259
|
* pki.asn1.decode(tpl).tagNumber === pki.asn1.TAGS.SEQUENCE; // the CertTemplate SEQUENCE
|
|
1260
1260
|
*/
|
|
1261
1261
|
function buildCertTemplate(template) { return _encodeCertTemplate(template, { revocation: true }).der; }
|
package/lib/csr-sign.js
CHANGED
|
@@ -338,8 +338,10 @@ function _coerceCsr(request) {
|
|
|
338
338
|
* var req = await pki.csr.sign({ subject: "device-42", subjectPublicKey: spki }, { key: pkcs8 });
|
|
339
339
|
* var r = await pki.csr.verify(req);
|
|
340
340
|
* // Issue from r.subject / r.subjectPublicKeyInfo / r.attributes, which are the verified fields.
|
|
341
|
+
* // `r.subject.bytes` is the request's own Name DER, so the issued subject is the requested one
|
|
342
|
+
* // byte for byte. `r.subject.dn` is its text form, which a builder reads as a common name.
|
|
341
343
|
* var issued = r.verified
|
|
342
|
-
* ? await pki.x509.sign({ subject: r.subject.
|
|
344
|
+
* ? await pki.x509.sign({ subject: r.subject.bytes, subjectPublicKey: r.subjectPublicKeyInfo.bytes,
|
|
343
345
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2027-01-01T00:00:00Z") },
|
|
344
346
|
* { key: pkcs8 })
|
|
345
347
|
* : null;
|
|
@@ -347,7 +349,7 @@ function _coerceCsr(request) {
|
|
|
347
349
|
function verify(request) { return guard.async.deferred(function () { return _verify(request); }); }
|
|
348
350
|
function _verify(request) {
|
|
349
351
|
var parsed = _coerceCsr(request);
|
|
350
|
-
return _promiseResolve(_Promise, csrVerify.verifyCsrSignature(parsed))
|
|
352
|
+
return intrinsic.chain(_promiseResolve(_Promise, csrVerify.verifyCsrSignature(parsed)), function (ok) {
|
|
351
353
|
return guard.verdict.of({
|
|
352
354
|
valid: ok === true,
|
|
353
355
|
verified: ok === true,
|
package/lib/est.js
CHANGED
|
@@ -281,8 +281,14 @@ function _matchDelimAt(text, ls, boundary) {
|
|
|
281
281
|
if (_charCodeAt(text, q) === 0x0d && q + 1 < n && _charCodeAt(text, q + 1) === 0x0a) return { end: q + 2, close: close };
|
|
282
282
|
return null;
|
|
283
283
|
}
|
|
284
|
+
/** @internal The delimiter count is bounded before the list is built, since one mark is appended per
|
|
285
|
+
* delimiter found anywhere in the body and the scan does not stop at the terminal one. The bound is
|
|
286
|
+
* the part cap plus that terminal delimiter, and it is the same `C.LIMITS` row the MIME reader
|
|
287
|
+
* counts parts against, so the two doors hold a multipart body to one number. */
|
|
284
288
|
function _findMultipartMarks(text, boundary) {
|
|
285
289
|
var marks = [], n = text.length, searchStart = 0, p;
|
|
290
|
+
var counted = guard.limits.counter(constants.LIMITS.MIME_MAX_PARTS, E, "est/bad-multipart",
|
|
291
|
+
"the multipart body part");
|
|
286
292
|
while (searchStart <= n) {
|
|
287
293
|
var found = null, foundAt = -1;
|
|
288
294
|
for (p = searchStart; p <= n; p++) {
|
|
@@ -293,7 +299,15 @@ function _findMultipartMarks(text, boundary) {
|
|
|
293
299
|
if (lsN !== -1) { var r1 = _matchDelimAt(text, lsN, boundary); if (r1) { found = r1; foundAt = p; break; } }
|
|
294
300
|
}
|
|
295
301
|
if (!found) break;
|
|
302
|
+
/** @internal The closing delimiter opens no part, so it is not charged and the cap counts the
|
|
303
|
+
* parts an operator is told it counts. */
|
|
304
|
+
if (!found.close) counted.tick();
|
|
296
305
|
guard.list.append(marks, { at: foundAt, bodyStart: found.end, close: found.close });
|
|
306
|
+
/** @internal RFC 2046 sec. 5.1.1 discards everything after the closing delimiter, and the caller
|
|
307
|
+
* reads no mark past the first one that closes, so the scan stops there. Running on charged the
|
|
308
|
+
* epilogue against the part budget: a body at the cap was refused for text it never reads, while
|
|
309
|
+
* the same body without that epilogue was accepted. */
|
|
310
|
+
if (found.close) break;
|
|
297
311
|
searchStart = found.end === foundAt ? found.end + 1 : found.end;
|
|
298
312
|
}
|
|
299
313
|
return marks;
|
|
@@ -314,18 +328,29 @@ function _stripOneTrailingEol(seg, end) {
|
|
|
314
328
|
}
|
|
315
329
|
return end;
|
|
316
330
|
}
|
|
317
|
-
|
|
318
|
-
|
|
331
|
+
/** @internal Every line break in the part's header area is charged as the scan passes it, before the
|
|
332
|
+
* unfolded text is split, so the refusal costs the cap rather than the input. Counting the split
|
|
333
|
+
* lines afterwards instead read 2,000,000 one-line headers in 425 ms and 172 MiB of resident memory
|
|
334
|
+
* before refusing, and never reached two on 1,500,000 continuation lines, which fold into one line. */
|
|
335
|
+
function _unfoldHeaders(raw, counted) {
|
|
336
|
+
var out = "", last = 0, i = 0, n = raw.length, afterLastBreak = 0;
|
|
319
337
|
while (i < n) {
|
|
320
338
|
var nlLen = 0;
|
|
321
339
|
if (_charCodeAt(raw, i) === 0x0d && i + 1 < n && _charCodeAt(raw, i + 1) === 0x0a) nlLen = 2;
|
|
322
340
|
else if (_charCodeAt(raw, i) === 0x0a) nlLen = 1;
|
|
323
|
-
if (nlLen
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
341
|
+
if (nlLen === 0) { i += 1; continue; }
|
|
342
|
+
/** @internal The scan steps PAST the whole break rather than one byte into it, so a CRLF is one
|
|
343
|
+
* line and not two: stepping one byte left the LF to be read as a second break on the next pass,
|
|
344
|
+
* which charged every CRLF twice and halved the cap a part was actually held to. */
|
|
345
|
+
counted.tick();
|
|
346
|
+
var after = i + nlLen;
|
|
347
|
+
afterLastBreak = after;
|
|
348
|
+
if (after < n && (_charCodeAt(raw, after) === 0x20 || _charCodeAt(raw, after) === 0x09)) { out += _strSlice(raw, last, i); last = after; i = after; continue; }
|
|
349
|
+
i = after;
|
|
328
350
|
}
|
|
351
|
+
/** @internal The run after the last break is a line as well, and a header area that ends at a break
|
|
352
|
+
* has no run after it, so the charge is the LINE count the cap is stated in and not one less. */
|
|
353
|
+
if (afterLastBreak < n) counted.tick();
|
|
329
354
|
return out + _strSlice(raw, last, n);
|
|
330
355
|
}
|
|
331
356
|
function _splitCrlf(s) {
|
|
@@ -359,7 +384,9 @@ function splitMultipartMixed(body, contentType) {
|
|
|
359
384
|
var rawHeaders = _strSlice(seg, 0, sep);
|
|
360
385
|
var partBody = _strSlice(seg, _stripTwoLeadingEol(seg, sep), _stripOneTrailingEol(seg, seg.length));
|
|
361
386
|
var headers = intrinsic.create(null);
|
|
362
|
-
var
|
|
387
|
+
var countedHeaders = guard.limits.counter(constants.LIMITS.MIME_MAX_HEADERS, E, "est/bad-multipart",
|
|
388
|
+
"the multipart part header line");
|
|
389
|
+
var lines = _splitCrlf(_unfoldHeaders(rawHeaders, countedHeaders));
|
|
363
390
|
for (var li = 0; li < lines.length; li++) {
|
|
364
391
|
var line = lines[li];
|
|
365
392
|
/** @internal A part's header name and value are cut, trimmed and folded through the captured
|
|
@@ -367,7 +394,20 @@ function splitMultipartMixed(body, contentType) {
|
|
|
367
394
|
* its body goes to: measured, a part stating `application/pkcs8` was read as
|
|
368
395
|
* `application/pkcs7-mime` with a `smime-type` of `certs-only`. */
|
|
369
396
|
var col = _estIndexOf(line, ":");
|
|
370
|
-
if (col
|
|
397
|
+
if (col <= 0) continue;
|
|
398
|
+
var fname = _toLowerCase(_estTrim(_strSlice(line, 0, col)));
|
|
399
|
+
/** @internal A repeat is refused rather than overwritten. Storing each value in turn left the
|
|
400
|
+
* LAST line deciding the field, and the field this decides is the part's own content type,
|
|
401
|
+
* which selects the reader its body goes to: measured, the same two Content-Type lines swapped
|
|
402
|
+
* reported `application/pkcs7-mime; smime-type=certs-only` one way and `application/pkcs8` the
|
|
403
|
+
* other, which is which part is the private key and which is the certificate. RFC 5322 sec. 3.6
|
|
404
|
+
* gives a singleton field one occurrence, and taking the first is as conforming as taking the
|
|
405
|
+
* last, so there is no value here to resolve to. */
|
|
406
|
+
if (intrinsic.hasOwn(headers, fname)) {
|
|
407
|
+
throw E("est/bad-multipart", "a multipart part declares the " + guard.text.showValue(fname) +
|
|
408
|
+
" header field more than once, so the value it resolves to is ambiguous (RFC 5322 sec. 3.6)");
|
|
409
|
+
}
|
|
410
|
+
headers[fname] = _estTrim(_strSlice(line, col + 1));
|
|
371
411
|
}
|
|
372
412
|
var partCt = headers["content-type"] || "";
|
|
373
413
|
if (_ciStartsWith(partCt, "MULTIPART/")) throw E("est/bad-multipart", "a nested multipart part is not permitted");
|
|
@@ -471,36 +511,58 @@ function _recipientMatches(recipientInfos, opts) {
|
|
|
471
511
|
* replacement decides which segments a parameter is then looked for in, and one of those parameters
|
|
472
512
|
* is the multipart boundary a response body is divided on: measured, a `boundary=wire` header
|
|
473
513
|
* yielded `forged`. */
|
|
514
|
+
/** @internal The first segment is the media type rather than a parameter, so it is not charged, and
|
|
515
|
+
* the charge comes BEFORE the segment is stored, so a refusal costs the cap and not the input: with
|
|
516
|
+
* no cap here a 3.8 MiB Content-Type carrying 1,000,000 parameters was accepted in 194 ms and 100 MiB
|
|
517
|
+
* of resident memory, and a part's media type splits the same value a second time. The bound is the
|
|
518
|
+
* `C.LIMITS` row the MIME reader counts parameters against, so both doors hold one media type to one
|
|
519
|
+
* number. */
|
|
520
|
+
function _pushCtSegment(segs, seg, counted) {
|
|
521
|
+
if (segs.length > 0) counted.tick();
|
|
522
|
+
intrinsic.append(segs, seg);
|
|
523
|
+
}
|
|
524
|
+
|
|
474
525
|
function _splitContentTypeParams(s) {
|
|
475
526
|
var segs = [], cur = "", inQuote = false;
|
|
527
|
+
var counted = guard.limits.counter(constants.LIMITS.MIME_MAX_PARAMS, E, "est/bad-multipart",
|
|
528
|
+
"the Content-Type parameter");
|
|
476
529
|
for (var i = 0; i < s.length; i++) {
|
|
477
530
|
var ch = _estCharAt(s, i);
|
|
478
531
|
if (ch === '"') { inQuote = !inQuote; cur += ch; }
|
|
479
|
-
else if (ch === ";" && !inQuote) {
|
|
532
|
+
else if (ch === ";" && !inQuote) { _pushCtSegment(segs, cur, counted); cur = ""; }
|
|
480
533
|
else cur += ch;
|
|
481
534
|
}
|
|
482
|
-
|
|
535
|
+
_pushCtSegment(segs, cur, counted);
|
|
483
536
|
return segs;
|
|
484
537
|
}
|
|
485
538
|
|
|
486
539
|
function _ctParam(contentType, name) {
|
|
487
540
|
var segs = _splitContentTypeParams(_stringOf(contentType || ""));
|
|
488
|
-
var value = null,
|
|
541
|
+
var value = null, plain = 0, starred = 0;
|
|
489
542
|
/** @internal Each parameter is read through the captured operations. The name decides which
|
|
490
543
|
* parameter this is, and the value it returns is the one a caller acts on: for the RFC 7030
|
|
491
544
|
* multipart boundary, it is where a response body is split. */
|
|
492
545
|
for (var i = 1; i < segs.length; i++) {
|
|
493
546
|
var seg = segs[i];
|
|
494
547
|
var eq = _estIndexOf(seg, "=");
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
548
|
+
/** @internal An occurrence counts whether or not it carries a value, which is why a bare
|
|
549
|
+
* attribute is counted: skipping it let `boundary; boundary="b"` through while
|
|
550
|
+
* `boundary="a"; boundary="b"` was refused. The plain and the starred RFC 2231 spellings are
|
|
551
|
+
* counted apart, because what has to be refused is two readings carrying DIFFERENT values.
|
|
552
|
+
* `boundary*=us-ascii''DECOY; boundary=REAL` is read here as REAL and by an RFC 2231 reader as
|
|
553
|
+
* DECOY, while continuation segments alone leave this reader no boundary at all, which the
|
|
554
|
+
* caller already refuses (RFC 2231 sec. 3, sec. 4, sec. 7). */
|
|
555
|
+
var attr = _toLowerCase(_estTrim(eq === -1 ? seg : _strSlice(seg, 0, eq)));
|
|
556
|
+
var star = _estIndexOf(attr, "*");
|
|
557
|
+
if ((star > 0 ? _strSlice(attr, 0, star) : attr) !== name) continue;
|
|
558
|
+
if (star > 0) { starred++; continue; }
|
|
559
|
+
plain++;
|
|
560
|
+
if (eq === -1 || plain > 1) continue;
|
|
499
561
|
var val = _estTrim(_strSlice(seg, eq + 1));
|
|
500
562
|
if (val.length >= 2 && _estCharAt(val, 0) === '"' && _estCharAt(val, val.length - 1) === '"') val = _strSlice(val, 1, -1);
|
|
501
563
|
value = val;
|
|
502
564
|
}
|
|
503
|
-
return { value: value, duplicated:
|
|
565
|
+
return { value: value, duplicated: plain > 1 || (plain > 0 && starred > 0) };
|
|
504
566
|
}
|
|
505
567
|
|
|
506
568
|
var _MT_FIRST = pkix.charTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
|
@@ -1451,7 +1513,7 @@ function _fullcmcResult(res, opts, wanted, sent) {
|
|
|
1451
1513
|
bound: { transactionId: false, senderNonce: false, dataReturn: false, bodyPartIDs: false },
|
|
1452
1514
|
boundToRequest: false });
|
|
1453
1515
|
}
|
|
1454
|
-
return cmcVerify.verify(der, sent)
|
|
1516
|
+
return intrinsic.chain(cmcVerify.verify(der, sent), function (verdict) {
|
|
1455
1517
|
if (verdict.outcome !== "issued") return verdict;
|
|
1456
1518
|
if (!wanted.length) { verdict.issuedCertificates = []; return verdict; }
|
|
1457
1519
|
var issuedCerts = _correlateIssued(verdict.certificates, wanted, "CMC-response");
|
|
@@ -1462,12 +1524,12 @@ function _fullcmcResult(res, opts, wanted, sent) {
|
|
|
1462
1524
|
}
|
|
1463
1525
|
|
|
1464
1526
|
function _tryDecodeCmcFault(res, sent) {
|
|
1465
|
-
return _promiseResolve(_Promise, undefined)
|
|
1527
|
+
return intrinsic.chain(intrinsic.chain(_promiseResolve(_Promise, undefined), function () {
|
|
1466
1528
|
var pt = _partMediaType(_ciHeader(res.headers, "content-type"));
|
|
1467
1529
|
if (pt.media !== "application/pkcs7-mime") return null;
|
|
1468
1530
|
if (pt.ambiguous) return null;
|
|
1469
1531
|
if (String(pt.smimeType || "").toLowerCase() !== "cmc-response") return null;
|
|
1470
|
-
return cmcVerify.verify(transferDecode(res.body), sent)
|
|
1532
|
+
return intrinsic.chain(cmcVerify.verify(transferDecode(res.body), sent), function (verdict) {
|
|
1471
1533
|
if (verdict.outcome !== "rejected") return null;
|
|
1472
1534
|
var e = E("est/cmc-failed",
|
|
1473
1535
|
"the EST server rejected the Full PKI Request: " + verdict.outcome +
|
|
@@ -1476,7 +1538,7 @@ function _tryDecodeCmcFault(res, sent) {
|
|
|
1476
1538
|
e.httpStatus = res.status;
|
|
1477
1539
|
return e;
|
|
1478
1540
|
});
|
|
1479
|
-
})
|
|
1541
|
+
}), null, function () { return null; });
|
|
1480
1542
|
}
|
|
1481
1543
|
|
|
1482
1544
|
function _isTypedError(e) {
|
|
@@ -1777,6 +1839,16 @@ function _ciHeader(headers, name) {
|
|
|
1777
1839
|
"the response carries more than one " + lname + " header field, so what the body is cannot be told " +
|
|
1778
1840
|
"from it (RFC 9110 sec. 5.1: field names are case-insensitive)");
|
|
1779
1841
|
}
|
|
1842
|
+
/** @internal One key can still hold SEVERAL values, which is how `node:http` reports a field that
|
|
1843
|
+
* arrived more than once, and these three fields each carry one value. Counting keys alone left a
|
|
1844
|
+
* two-element array to be read as one string: measured, the two orders of the same pair gave
|
|
1845
|
+
* `est/bad-content-type` one way and an accepted classification the other, because the joined text
|
|
1846
|
+
* parsed as a media type in one order and not in the other. */
|
|
1847
|
+
if (intrinsic.isArray(found) && found.length > 1) {
|
|
1848
|
+
throw E("est/bad-content-type",
|
|
1849
|
+
"the response carries " + found.length + " values for " + lname + ", so what the body is cannot be " +
|
|
1850
|
+
"told from it (RFC 9110 sec. 5.1)");
|
|
1851
|
+
}
|
|
1780
1852
|
return n === 0 ? null : found;
|
|
1781
1853
|
}
|
|
1782
1854
|
|
package/lib/guard-identifier.js
CHANGED
|
@@ -31,6 +31,7 @@ var _mapSet = intrinsic.mapSet;
|
|
|
31
31
|
var _mapHas = intrinsic.mapHas;
|
|
32
32
|
var _Set = intrinsic.Set;
|
|
33
33
|
var _Map = intrinsic.Map;
|
|
34
|
+
var _ObjectProto = intrinsic.ObjectProto;
|
|
34
35
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
35
36
|
var _strSplit = intrinsic.uncurry(String.prototype.split);
|
|
36
37
|
var _fnToString = intrinsic.uncurry(Function.prototype.toString);
|
|
@@ -92,6 +93,54 @@ function assertKnownKeys(obj, known, E, code, message) {
|
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @internal
|
|
98
|
+
* Whether `k` is `v`'s function machinery, owned or reached from its prototype chain. A check that
|
|
99
|
+
* walks the chain asks this; a reader that copies one LEVEL at a time asks `isOwnMachineryName`.
|
|
100
|
+
*
|
|
101
|
+
* Which of the two a given name is depends on the function and the engine. A STRICT function, which
|
|
102
|
+
* is every function in an ES module or a `"use strict"` file, owns neither `arguments` nor `caller`,
|
|
103
|
+
* and `Function.prototype` carries both as the restricted accessors, so a chain walk finds them
|
|
104
|
+
* there. On the Node 24.21.0 floor a non-strict function owns both outright. Measured before this
|
|
105
|
+
* covered the inherited side: a plain strict callable descriptor was refused for `arguments` by
|
|
106
|
+
* `pki.cms.sign` and `pki.cms.countersign`, while the minted floor shape passed, which is the shape
|
|
107
|
+
* the vectors were written on.
|
|
108
|
+
*/
|
|
109
|
+
// @enforced-by behavioral -- the vectors that drive a PLAIN callable and a floor-shaped one through
|
|
110
|
+
// @enforced-by behavioral -- each door are the guard; the name set has no rename-proof code shape
|
|
111
|
+
function isMachineryName(v, k) {
|
|
112
|
+
if (v === null || v === undefined) return false;
|
|
113
|
+
if (isOwnMachineryName(v, k)) return true;
|
|
114
|
+
if (typeof v !== "function" || !_CALLABLE_MACHINERY.has(k)) return false;
|
|
115
|
+
/** @internal Not owned, so `readableNames` reached it through the prototype chain. A caller's own
|
|
116
|
+
* property of that name is theirs and stays refused by name, which `isOwnMachineryName` above
|
|
117
|
+
* decides; making one takes `defineProperty`, since assigning `fn.arguments` reaches the
|
|
118
|
+
* restricted setter and throws. */
|
|
119
|
+
return _getOwnPropertyDescriptor(v, k) === undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @internal
|
|
124
|
+
* `assertKnownKeys` for a bag a verb documents as acceptably CALLABLE, where the names a function
|
|
125
|
+
* carries by construction are not fields the caller named. The exemption belongs at the door that
|
|
126
|
+
* refuses, not only at a reader that copies: `pki.smime.sign` snapshots a descriptor before handing
|
|
127
|
+
* it on, so excluding the machinery there left a caller going straight to `pki.cms.sign` with the
|
|
128
|
+
* same callable refused for `length`.
|
|
129
|
+
*
|
|
130
|
+
* It is not `assertKnownKeys`'s own rule, because a bare function handed in where an OPTIONS BAG
|
|
131
|
+
* belongs is refused for exactly those names, which is a contract of its own.
|
|
132
|
+
*/
|
|
133
|
+
// @enforced-by behavioral -- the vectors that drive a callable descriptor through each door are the
|
|
134
|
+
// @enforced-by behavioral -- guard; the exemption has no rename-proof code shape
|
|
135
|
+
function assertKnownDescriptorKeys(obj, known, E, code, message) {
|
|
136
|
+
_refuseUnenumerable(obj, E, code, "the options");
|
|
137
|
+
var describe = typeof message === "function" ? message : function (k) { return message + _stringify(k); };
|
|
138
|
+
_forEach(_readableNames(obj), function (k) {
|
|
139
|
+
if (_hasOwn(known, k) || isMachineryName(obj, k)) return;
|
|
140
|
+
throw E(code, describe(typeof k === "symbol" ? _String(k) : k));
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
95
144
|
// @enforced-by behavioral -- reading a property twice has no rename-proof code shape; the RED
|
|
96
145
|
// @enforced-by behavioral -- vectors that drive an accessor-backed option are the guard
|
|
97
146
|
function snapshotOptions(obj, known) {
|
|
@@ -320,6 +369,64 @@ function _kindMembers(v) {
|
|
|
320
369
|
return [];
|
|
321
370
|
}
|
|
322
371
|
|
|
372
|
+
/** @internal The names a callable carries before a caller assigns a field, asked of the engine
|
|
373
|
+
* rather than listed here: a function's own names, plus `Function.prototype`'s, which is where the
|
|
374
|
+
* restricted `arguments` and `caller` accessors live. Which of them a given function makes OWN
|
|
375
|
+
* depends on the engine. Measured: on Node 24.21.0, the version `engines.node` floors at, every
|
|
376
|
+
* non-strict function carries own `arguments` and `caller`; on 26.9.0 none does, for a declaration
|
|
377
|
+
* and for `new Function` alike. A list written against one of those refuses a legitimate callable
|
|
378
|
+
* descriptor on the other, and a caller's non-strict CommonJS module is where the shape comes
|
|
379
|
+
* from. */
|
|
380
|
+
var _CALLABLE_MACHINERY = (function () {
|
|
381
|
+
var names = _create(null), symbols = new _Set();
|
|
382
|
+
/** @internal One pristine probe per callable KIND, because the kinds do not share a prototype: an
|
|
383
|
+
* async function, a generator and an async generator each sit on their own intrinsic prototype
|
|
384
|
+
* carrying `Symbol.toStringTag`, which an ordinary function's chain never reaches. Measured
|
|
385
|
+
* before the probes were per-kind: `pki.cms.sign` refused all three for
|
|
386
|
+
* `Symbol(Symbol.toStringTag)` while an ordinary function passed. Symbol keys are collected too,
|
|
387
|
+
* since that is the name that was missing. */
|
|
388
|
+
/** @internal Every kind, not the subset that happens to suffice: the four intrinsic prototypes
|
|
389
|
+
* carry the SAME well-known `Symbol.toStringTag` under different values, so dropping the async
|
|
390
|
+
* generator still passes today and would not if a kind ever carried a key of its own. */
|
|
391
|
+
var probes = [function () {}, function* () {}, async function () {}, async function* () {}, class {}];
|
|
392
|
+
_forEach(probes, function (p) {
|
|
393
|
+
for (var o = p; o !== null && o !== _ObjectProto; o = _getPrototypeOf(o)) {
|
|
394
|
+
_forEach(_ownKeys(o), function (k) {
|
|
395
|
+
if (typeof k === "symbol") _setAdd(symbols, k); else names[k] = true;
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return {
|
|
400
|
+
has: function (k) { return typeof k === "symbol" ? _setHas(symbols, k) : names[k] === true; },
|
|
401
|
+
};
|
|
402
|
+
})();
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @internal
|
|
406
|
+
* Whether `k` is part of `v`'s own machinery rather than a field a caller named: a RegExp's
|
|
407
|
+
* `lastIndex`, the non-thenable mask's `then`, and a callable's own function machinery, which is
|
|
408
|
+
* non-enumerable where a field a caller assigned is enumerable.
|
|
409
|
+
*
|
|
410
|
+
* A reader that COPIES a descriptor's own keys asks this, so it copies what the caller wrote and
|
|
411
|
+
* nothing else. The name checks do not: a bare function handed in where an options bag belongs is
|
|
412
|
+
* refused for the names it carries, which is a contract of its own, so the callable rule lives here
|
|
413
|
+
* rather than in `_isOwnStructuralName`.
|
|
414
|
+
*/
|
|
415
|
+
// @enforced-by behavioral -- a per-kind name set has no rename-proof code shape; the vectors
|
|
416
|
+
// @enforced-by behavioral -- that drive a callable descriptor through a copying door are the guard
|
|
417
|
+
function isOwnMachineryName(v, k) {
|
|
418
|
+
if (v === null || v === undefined) return false;
|
|
419
|
+
var d = _getOwnPropertyDescriptor(v, k);
|
|
420
|
+
/** @internal A DATA property, because that is what the machinery is: measured on both Node
|
|
421
|
+
* 24.21.0 and 26.9.0, every one of these names a function owns is a non-enumerable data
|
|
422
|
+
* property. An accessor a caller installed under one of those names is therefore theirs, and it
|
|
423
|
+
* reaches the unknown-field refusal by its name rather than being exempted and ignored. Asking
|
|
424
|
+
* the descriptor rather than reading the property leaves the getter uninvoked either way. */
|
|
425
|
+
if (typeof v === "function" && !!d && d.enumerable === false && _hasOwn(d, "value") &&
|
|
426
|
+
_CALLABLE_MACHINERY.has(k)) return true;
|
|
427
|
+
return _isOwnStructuralName(v, k, d, false);
|
|
428
|
+
}
|
|
429
|
+
|
|
323
430
|
function _isOwnStructuralName(v, k, d, inherited) {
|
|
324
431
|
if (util.types.isRegExp(v) && k === "lastIndex") return true;
|
|
325
432
|
/** @internal The non-thenable mask `guard.verdict.shield` leaves on a value the toolkit hands back:
|
|
@@ -680,6 +787,9 @@ module.exports = intrinsic.freeze({
|
|
|
680
787
|
snapshotOptions: snapshotOptions,
|
|
681
788
|
snapshotPresentOptions: snapshotPresentOptions,
|
|
682
789
|
optionNames: optionNames,
|
|
790
|
+
isOwnMachineryName: isOwnMachineryName,
|
|
791
|
+
isMachineryName: isMachineryName,
|
|
792
|
+
assertKnownDescriptorKeys: assertKnownDescriptorKeys,
|
|
683
793
|
ownOptions: ownOptions,
|
|
684
794
|
pickSettings: pickSettings,
|
|
685
795
|
readableNames: readableNames,
|
package/lib/guard-name.js
CHANGED
|
@@ -65,7 +65,51 @@ function assertPrintableIa5(buf, E, code, label) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function _isSpace(c) { return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d; }
|
|
68
|
-
|
|
68
|
+
/** @internal The attribute types whose EQUALITY rule is not case-ignoring, resolved once to their
|
|
69
|
+
* object identifiers. An equality rule belongs to the attribute, and folding case on one whose rule
|
|
70
|
+
* is case-exact makes two distinct names compare as one name: measured through `pki.x509.parseDn`,
|
|
71
|
+
* `challengePassword=Secret` and `challengePassword=secret` encode differently and compared EQUAL,
|
|
72
|
+
* as did `localKeyId=AB` against `ab` and a `role` URI against its lowercase form.
|
|
73
|
+
*
|
|
74
|
+
* Only the exceptions are listed. caseIgnoreMatch under the RFC 4518 preparation is the correct
|
|
75
|
+
* rule for every attribute RFC 5280 sec. 4.1.2.4 requires support for, so a conforming PKIX name
|
|
76
|
+
* compares as it did and an attribute the registry does not name keeps that default rather than
|
|
77
|
+
* being given a rule nothing states.
|
|
78
|
+
*
|
|
79
|
+
* The identifiers are resolved rather than written, since a dotted-decimal literal is never the
|
|
80
|
+
* identity a module carries. */
|
|
81
|
+
/** @internal RFC 2985 sec. 5.4.1 gives challengePassword caseExactMatch, sec. 5.5.2 gives localKeyId
|
|
82
|
+
* octetStringMatch, and RFC 5755 sec. 4.4.5 makes a role a URI. */
|
|
83
|
+
var _CASE_EXACT_NAMES = ["challengePassword", "localKeyId", "role"];
|
|
84
|
+
var _caseExactOids = null;
|
|
85
|
+
function _isCaseExactType(type) {
|
|
86
|
+
if (typeof type !== "string") return false;
|
|
87
|
+
if (_caseExactOids === null) {
|
|
88
|
+
var oid = require("./oid"); // allow:inline-require -- circular load: oid -> guard-all -> this module
|
|
89
|
+
_caseExactOids = _create(null);
|
|
90
|
+
for (var i = 0; i < _CASE_EXACT_NAMES.length; i++) {
|
|
91
|
+
var o = oid.byName(_CASE_EXACT_NAMES[i]);
|
|
92
|
+
if (typeof o === "string") _caseExactOids[o] = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return _caseExactOids[type] === true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @internal The registry's own reading of a dotted-decimal object identifier, taken from there
|
|
99
|
+
* rather than re-derived: `numericoid` is RFC 4512's production and one definition of it decides
|
|
100
|
+
* both what the registry resolves and what opens a distinguished name. */
|
|
101
|
+
var _isDottedOid = null;
|
|
102
|
+
function _opensAsNumericOid(run) {
|
|
103
|
+
if (_isDottedOid === null) {
|
|
104
|
+
var oid = require("./oid"); // allow:inline-require -- circular load: oid -> guard-all -> this module
|
|
105
|
+
_isDottedOid = oid.isDottedDecimal;
|
|
106
|
+
}
|
|
107
|
+
return _isDottedOid(run) === true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @internal The RFC 4518 insignificant-space preparation, which every equality rule here applies.
|
|
111
|
+
* The case fold is the caller's to add, because that step is the one the attribute's rule decides. */
|
|
112
|
+
function _prepAttrValue(v, E, code, label) {
|
|
69
113
|
if (typeof v !== "string") return v;
|
|
70
114
|
assertNoControlBytes(v, E, code, label);
|
|
71
115
|
var out = "", lastWasSpace = true;
|
|
@@ -75,8 +119,9 @@ function _canonAttrValue(v, E, code, label) {
|
|
|
75
119
|
lastWasSpace = false;
|
|
76
120
|
out += _charAt(v, i);
|
|
77
121
|
}
|
|
78
|
-
return
|
|
122
|
+
return out;
|
|
79
123
|
}
|
|
124
|
+
|
|
80
125
|
/** @internal Both lengths are NARROWED where they are taken, and every test and walk below asks the
|
|
81
126
|
* local. `Array.isArray` is true for a Proxy whose target is an array and a `get` trap answers `length`
|
|
82
127
|
* afresh on each read, so the equality test and the walk that follows it could see two different counts:
|
|
@@ -114,14 +159,25 @@ function rdnEqual(a, b, E, code, label) {
|
|
|
114
159
|
* comparison's one unacceptable answer, since it is what chains a certificate to its issuer. */
|
|
115
160
|
var ea = a[i];
|
|
116
161
|
var typeA = ea == null ? undefined : ea.type;
|
|
117
|
-
|
|
162
|
+
/** @internal Prepared once without the fold, and folded once, so the attribute's own rule picks
|
|
163
|
+
* between them without reading the element again. */
|
|
164
|
+
var prepA = _prepAttrValue(ea == null ? undefined : ea.value, E, code, label);
|
|
165
|
+
var foldedA = typeof prepA === "string" ? _toLowerCase(prepA) : prepA;
|
|
166
|
+
var exactA = _isCaseExactType(typeA);
|
|
118
167
|
var found = false;
|
|
119
168
|
for (var j = 0; j < n.b; j++) {
|
|
120
169
|
if (used[j]) continue;
|
|
121
170
|
var eb = b[j];
|
|
122
171
|
var typeB = eb == null ? undefined : eb.type;
|
|
123
172
|
if (typeA !== typeB) continue;
|
|
124
|
-
|
|
173
|
+
var prepB = _prepAttrValue(eb == null ? undefined : eb.value, E, code, label);
|
|
174
|
+
/** @internal Either side naming a case-exact attribute decides it. The types already matched,
|
|
175
|
+
* so the two can disagree only through an object answering differently per read, and the
|
|
176
|
+
* exact comparison is the one that cannot make two distinct names equal. */
|
|
177
|
+
var same = exactA || _isCaseExactType(typeB)
|
|
178
|
+
? prepA === prepB
|
|
179
|
+
: foldedA === (typeof prepB === "string" ? _toLowerCase(prepB) : prepB);
|
|
180
|
+
if (same) {
|
|
125
181
|
used[j] = true; found = true; break;
|
|
126
182
|
}
|
|
127
183
|
}
|
|
@@ -213,6 +269,36 @@ function isAttributeDescriptor(name) {
|
|
|
213
269
|
return true;
|
|
214
270
|
}
|
|
215
271
|
|
|
272
|
+
/**
|
|
273
|
+
* @internal
|
|
274
|
+
* Whether a string opens with the prefix RFC 4514 sec. 3 requires of a distinguished name: an
|
|
275
|
+
* attribute type, a descriptor or a dotted-decimal OID, immediately followed by `=`. It answers one
|
|
276
|
+
* question, whether a caller who passed a bare string meant a distinguished name rather than the
|
|
277
|
+
* single value the shorthand reads it as, and it is deliberately narrower than parsing: a value that
|
|
278
|
+
* CONTAINS an `=` further along, `Example CA (v=2)`, opens with no attribute type and is not
|
|
279
|
+
* a distinguished name.
|
|
280
|
+
*
|
|
281
|
+
* `attributeType` is `descr / numericoid`, and a `numericoid` carries at least one dot, so a numeric
|
|
282
|
+
* run without one is not an attribute type: `1=1` is a value no reader reads as a distinguished
|
|
283
|
+
* name, and refusing it would name a remedy that does not apply. The leading whitespace sec. 3
|
|
284
|
+
* admits nowhere is the same case from the other side: ` CN=Example` opens with no attribute type
|
|
285
|
+
* and is the value it says it is.
|
|
286
|
+
*/
|
|
287
|
+
// @enforced-by behavioral -- an opening-prefix test has no rename-proof shape distinct from
|
|
288
|
+
function opensAsDnString(str) {
|
|
289
|
+
if (typeof str !== "string" || str.length === 0) return false;
|
|
290
|
+
var i = 0, n = str.length, first = _charCodeAt(str, 0);
|
|
291
|
+
if (_isDigit(first)) {
|
|
292
|
+
while (i < n && (_isDigit(_charCodeAt(str, i)) || _charCodeAt(str, i) === 0x2e)) i++;
|
|
293
|
+
if (!_opensAsNumericOid(_strSlice(str, 0, i))) return false;
|
|
294
|
+
} else if (_isAttrTypeStart(first)) {
|
|
295
|
+
while (i < n && _isAttrTypeChar(_charCodeAt(str, i))) i++;
|
|
296
|
+
} else {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
return i > 0 && i < n && _charCodeAt(str, i) === 0x3d;
|
|
300
|
+
}
|
|
301
|
+
|
|
216
302
|
// @enforced-by behavioral -- the inverse of escapeDnValue has no rename-proof shape distinct
|
|
217
303
|
function parseDnString(str, E, code, label) {
|
|
218
304
|
if (typeof str !== "string") throw E(code, label + " must be a string");
|
|
@@ -1058,6 +1144,7 @@ module.exports = intrinsic.freeze({
|
|
|
1058
1144
|
assertNoControlBytes: assertNoControlBytes, assertPrintableIa5: assertPrintableIa5,
|
|
1059
1145
|
dnEqual: dnEqual, rdnEqual: rdnEqual, emailEqual: emailEqual, dpnCorresponds: dpnCorresponds,
|
|
1060
1146
|
escapeControlBytes: escapeControlBytes, escapeDnValue: escapeDnValue, parseDnString: parseDnString,
|
|
1147
|
+
opensAsDnString: opensAsDnString,
|
|
1061
1148
|
isAttributeDescriptor: isAttributeDescriptor,
|
|
1062
1149
|
lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
|
|
1063
1150
|
stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
|
package/lib/inspect.js
CHANGED
|
@@ -1531,8 +1531,8 @@ function crmfReport(input) {
|
|
|
1531
1531
|
* var key = await pki.key.export(pair.privateKey);
|
|
1532
1532
|
* var cert = await pki.x509.sign({ subject: "cmp.example", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
1533
1533
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
1534
|
-
* var der = await pki.cmp.build({ header: { sender: { directoryName: "
|
|
1535
|
-
* recipient: { directoryName: "
|
|
1534
|
+
* var der = await pki.cmp.build({ header: { sender: { directoryName: "cmp.example" },
|
|
1535
|
+
* recipient: { directoryName: "A CA" } },
|
|
1536
1536
|
* body: { ir: { certTemplate: { subject: [{ commonName: "cmp.example" }], publicKey: await pki.key.export(pair.publicKey) } } } },
|
|
1537
1537
|
* { cert: cert, key: key });
|
|
1538
1538
|
* pki.inspect.cmp(der).split("\n")[0]; // "CMP Message:"
|
|
@@ -1937,10 +1937,10 @@ function tspReport(input) {
|
|
|
1937
1937
|
*
|
|
1938
1938
|
* @example
|
|
1939
1939
|
* var pair = await pki.key.generate("Ed25519");
|
|
1940
|
-
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "
|
|
1940
|
+
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "Alice" } },
|
|
1941
1941
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
1942
1942
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
1943
|
-
* { name: "
|
|
1943
|
+
* { name: "An AA", publicKey: await pki.key.export(pair.publicKey), key: await pki.key.export(pair.privateKey) });
|
|
1944
1944
|
* pki.inspect.attrcert(der).split("\n")[0]; // "Attribute Certificate:"
|
|
1945
1945
|
*/
|
|
1946
1946
|
function attrCertReport(input) {
|
package/lib/lint.js
CHANGED
|
@@ -7556,10 +7556,10 @@ function _ingestAttrCert(input) {
|
|
|
7556
7556
|
* @example
|
|
7557
7557
|
* var pair = await pki.key.generate("Ed25519");
|
|
7558
7558
|
* var key = await pki.key.export(pair.privateKey);
|
|
7559
|
-
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "
|
|
7559
|
+
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "Alice" } },
|
|
7560
7560
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
7561
7561
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
7562
|
-
* { name: "
|
|
7562
|
+
* { name: "Example AA", publicKey: await pki.key.export(pair.publicKey), key: key });
|
|
7563
7563
|
* pki.lint.attrcert(der).findings.length; // -> 0 for a conforming attribute certificate
|
|
7564
7564
|
*/
|
|
7565
7565
|
function attrcert(input, opts) {
|