@blamejs/pki 0.6.29 → 0.6.31
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 +29 -0
- package/README.md +1 -1
- package/lib/acme.js +8 -7
- package/lib/asn1-der.js +9 -7
- package/lib/attrcert-sign.js +8 -8
- package/lib/byte-reader.js +1 -1
- package/lib/byte-writer.js +1 -1
- package/lib/cmc-verify.js +4 -4
- package/lib/cmp-build.js +3 -3
- package/lib/cmp-session.js +3 -3
- package/lib/cmp-verify.js +2 -1
- package/lib/cms-verify.js +22 -17
- package/lib/composite-kem.js +2 -2
- package/lib/crl-sign.js +5 -5
- package/lib/crmf-sign.js +3 -3
- package/lib/csr-sign.js +2 -2
- package/lib/ct.js +3 -3
- package/lib/est.js +40 -8
- package/lib/framework-error.js +2 -2
- package/lib/hpke.js +2 -2
- package/lib/http-transport.js +61 -14
- package/lib/inspect.js +1 -1
- package/lib/jose.js +1 -1
- package/lib/key.js +7 -7
- package/lib/lint.js +1 -1
- package/lib/merkle.js +2 -6
- package/lib/mime.js +2 -2
- package/lib/ocsp.js +6 -6
- package/lib/oid.js +1 -1
- package/lib/path-validate.js +13 -13
- package/lib/pkcs12-build.js +4 -4
- package/lib/rfc3339.js +2 -2
- package/lib/scep.js +2 -2
- package/lib/schema-all.js +1 -1
- package/lib/schema-attrcert.js +1 -1
- package/lib/schema-cmc.js +2 -2
- package/lib/schema-cmp.js +8 -9
- package/lib/schema-cms.js +3 -3
- package/lib/schema-crmf.js +3 -3
- package/lib/schema-csr.js +1 -1
- package/lib/schema-csrattrs.js +1 -1
- package/lib/schema-engine.js +4 -4
- package/lib/schema-ocsp.js +1 -1
- package/lib/schema-pkcs12.js +2 -2
- package/lib/schema-pkcs8.js +2 -2
- package/lib/schema-smime.js +1 -1
- package/lib/schema-tsp.js +6 -6
- package/lib/schema-x509.js +2 -2
- package/lib/shbs.js +3 -4
- package/lib/sign-scheme.js +1 -1
- package/lib/sigstore.js +2 -2
- package/lib/smime.js +16 -16
- package/lib/tls-cert-compress.js +7 -7
- package/lib/trust.js +5 -5
- package/lib/tsp-sign.js +8 -6
- package/lib/webauthn.js +23 -19
- package/lib/webcrypto.js +3 -3
- package/lib/x509-sign.js +5 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/est.js
CHANGED
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @intro
|
|
13
13
|
* Enrollment over Secure Transport (RFC 7030, updated by RFC 8951 and RFC 9908).
|
|
14
|
-
* The network verbs
|
|
14
|
+
* The network verbs (`cacerts`, `simpleenroll`, `simplereenroll`) run the thin
|
|
15
15
|
* RFC 7030 client: they compose the codecs below over the shared `pki.transport`
|
|
16
16
|
* (a caller MAY inject `opts.transport`; the default is a fail-closed
|
|
17
17
|
* `pki.transport.https`). This module opens no socket itself: the sole socket
|
|
18
18
|
* choke point is `pki.transport`, so the verbs stay a thin, fail-closed shell:
|
|
19
19
|
* https-only (`est/insecure-url`), an explicit trust anchor required
|
|
20
20
|
* (`est/no-trust-anchors`), same-origin redirects followed but a downgrade / loop
|
|
21
|
-
* refused, a 202 Retry-After SURFACED and never slept, HTTP Basic answered
|
|
22
|
-
* after the transport authenticated the server, and the issued certificate chosen
|
|
21
|
+
* refused, a 202 Retry-After SURFACED and never slept, HTTP Basic or Digest answered
|
|
22
|
+
* only after the transport authenticated the server, and the issued certificate chosen
|
|
23
23
|
* by public-key match. Under them sit the transport-agnostic codecs, validators, and
|
|
24
24
|
* request builders over the shipped CMS / CSR / PKCS#8 / X.509 parsers:
|
|
25
25
|
* `transferDecode` / `transferEncode` are the RFC 8951
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
* `parseServerKeygenResponse` dispatches the two-part key + certificate
|
|
31
31
|
* response with recipient-arm coherence; `classifyResponse` is the HTTP status
|
|
32
32
|
* / content-type / Retry-After state machine (202 accepted-not-ready surfaces
|
|
33
|
-
* `retryAfterSeconds
|
|
33
|
+
* `retryAfterSeconds`, never an internal sleep; 204/404 on /csrattrs is a
|
|
34
34
|
* "none available" verdict, not an error). The builders assemble the CSR
|
|
35
|
-
* attributes EST adds
|
|
35
|
+
* attributes EST adds: a channel-binding challengePassword, the
|
|
36
36
|
* out-of-band-key identifiers, SMIMECapabilities, and the RFC 9908
|
|
37
37
|
* template-priority enroll plan.
|
|
38
38
|
*
|
|
@@ -536,7 +536,7 @@ var NOT_IMPLEMENTED_OPS = { fullcmc: 1 };
|
|
|
536
536
|
*
|
|
537
537
|
* Classify an EST HTTP response into a verdict or a typed fault. A 200 requires
|
|
538
538
|
* the operation's exact content-type (`est/bad-content-type`); a 202 requires a
|
|
539
|
-
* Retry-After (absent -> `est/missing-retry-after`)
|
|
539
|
+
* Retry-After (absent -> `est/missing-retry-after`): a delay-seconds value is
|
|
540
540
|
* surfaced as bounded `retryAfterSeconds`, an HTTP-date as absolute
|
|
541
541
|
* `retryAfterDate` (epoch ms; `retryAfterSeconds` too when `opts.now` is given),
|
|
542
542
|
* and any other value is `est/bad-retry-after` (never slept on either way);
|
|
@@ -643,6 +643,38 @@ function paths(baseUrl, opts) {
|
|
|
643
643
|
|
|
644
644
|
function _attr(typeOid, valueNodes) { return asn1.build.sequence([asn1.build.oid(typeOid), asn1.build.set(valueNodes)]); }
|
|
645
645
|
|
|
646
|
+
/**
|
|
647
|
+
* @primitive pki.est.challengePasswordFromTlsUnique
|
|
648
|
+
* @signature pki.est.challengePasswordFromTlsUnique(channelBinding) -> Buffer
|
|
649
|
+
* @since 0.6.31
|
|
650
|
+
* @originated 0.1.24
|
|
651
|
+
* @status stable
|
|
652
|
+
* @spec RFC 7030, RFC 5929, RFC 2985
|
|
653
|
+
* @related pki.csr.sign, pki.transport.https, pki.est.simpleenroll
|
|
654
|
+
*
|
|
655
|
+
* Encode RFC 5929 tls-unique channel-binding bytes as the RFC 7030 sec. 3.5
|
|
656
|
+
* challengePassword, binding an enrollment request to the TLS session it is sent over.
|
|
657
|
+
* The returned Buffer is a DER `Attribute` (`SEQUENCE { type OID, SET { PrintableString } }`)
|
|
658
|
+
* carrying the base64 of the binding, for a caller assembling certification-request
|
|
659
|
+
* attributes directly. Empty or non-byte input is `est/bad-input`; a base64 value longer
|
|
660
|
+
* than 255 octets is `est/tls-unique-too-long`.
|
|
661
|
+
*
|
|
662
|
+
* To sign a channel-bound request with `pki.csr.sign`, pass the base64 string as
|
|
663
|
+
* `spec.challengePassword` instead: that signer builds this same attribute itself and
|
|
664
|
+
* REFUSES a pre-encoded challengePassword in `spec.attributes` (`csr/bad-input`), so the
|
|
665
|
+
* two forms are alternatives, never combined. `pki.transport.https` supplies the binding
|
|
666
|
+
* as `tls.tlsUnique`, and a request body given as a function receives it before the body
|
|
667
|
+
* is written, which is the point at which the request can still be signed over it.
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* // the binding comes from the connection; pki.transport surfaces it as tls.tlsUnique
|
|
671
|
+
* var binding = Buffer.from("9f86d081884c7d65", "hex");
|
|
672
|
+
* var attr = pki.est.challengePasswordFromTlsUnique(binding);
|
|
673
|
+
* attr[0] === 0x30; // a DER Attribute SEQUENCE
|
|
674
|
+
* // to SIGN a channel-bound request, hand pki.csr.sign the same value as a string
|
|
675
|
+
* // via spec.challengePassword; it builds this identical attribute itself.
|
|
676
|
+
* var challengePassword = binding.toString("base64");
|
|
677
|
+
*/
|
|
646
678
|
function challengePasswordFromTlsUnique(channelBinding) {
|
|
647
679
|
if (!guard.bytes.isByteSource(channelBinding)) throw E("est/bad-input", "challengePasswordFromTlsUnique requires the channel-binding bytes");
|
|
648
680
|
var _cb = guard.bytes.source(channelBinding, EstError, "est/bad-input", "the channel-binding bytes");
|
|
@@ -1006,7 +1038,7 @@ function _certsResult(op, res, opts, csrSpki) {
|
|
|
1006
1038
|
* surfaced as `issuedCertificates` (with `certificate` the first), distinct from `certificates`,
|
|
1007
1039
|
* which is the whole returned bag including any chain. Where the requested keys are distinct that
|
|
1008
1040
|
* list is in request order; where several requests deliberately SHARE one key it is not, and does
|
|
1009
|
-
* not claim to be
|
|
1041
|
+
* not claim to be: the public key is the only identification sec. 4.1 sanctions, so when it is
|
|
1010
1042
|
* shared nothing in the response says which of those requests a given certificate answers. That arm reports `signatureVerified: false`: a certs-only body is
|
|
1011
1043
|
* a degenerate SignedData with no signers by definition, so its security rests on the authenticated
|
|
1012
1044
|
* TLS channel, not on a signature.
|
|
@@ -1434,7 +1466,7 @@ function cacerts(baseUrl, opts) {
|
|
|
1434
1466
|
* @opts
|
|
1435
1467
|
* - `transport` / `tls` / `label` / `timeout` / `maxResponseBytes` / `maxRedirects` / `proxy` / `now`, as in pki.est.cacerts.
|
|
1436
1468
|
* - `strict`: reject an enroll response that carries more than the single issued certificate.
|
|
1437
|
-
* - `username` / `password` -- HTTP Basic
|
|
1469
|
+
* - `username` / `password` / `auth` -- HTTP credentials: top-level `username` / `password` mean Basic; `auth` (`{ scheme: "basic" | "digest", username, password, ... }`, as in pki.est.cacerts) selects Basic or Digest. Answered only after server authorization (empty username allowed).
|
|
1438
1470
|
* - `allowCrossOriginRedirect` -- opt in to following a cross-origin redirect on this POST.
|
|
1439
1471
|
* @example
|
|
1440
1472
|
* var req = await pki.csr.sign({ subject: "device.example", subjectPublicKey: signerSpki }, { key: signerKeyPkcs8 });
|
package/lib/framework-error.js
CHANGED
|
@@ -90,8 +90,8 @@ class PkiError extends Error {
|
|
|
90
90
|
* @status stable
|
|
91
91
|
* @spec internal (design: error-class factory)
|
|
92
92
|
*
|
|
93
|
-
* Factory that produces a `PkiError` subclass with the standard shape
|
|
94
|
-
*
|
|
93
|
+
* Factory that produces a `PkiError` subclass with the standard shape. The
|
|
94
|
+
* returned constructor takes
|
|
95
95
|
* `(code, message)`, stamps `name`, sets an `is<Name>` flag, and exposes
|
|
96
96
|
* a `.factory` static for the common `var _err = XxxError.factory` shape.
|
|
97
97
|
* The `code` must be a `domain/reason` string (the base-class contract);
|
package/lib/hpke.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @nav Protocols
|
|
7
7
|
* @title HPKE
|
|
8
8
|
* @fullname HPKE (Hybrid Public Key Encryption, RFC 9180)
|
|
9
|
-
* @intro Hybrid Public Key Encryption (RFC 9180)
|
|
9
|
+
* @intro Hybrid Public Key Encryption (RFC 9180): the standard construction
|
|
10
10
|
* behind TLS Encrypted Client Hello, MLS, and OHTTP that turns a recipient KEM
|
|
11
11
|
* public key into an encapsulated key plus an AEAD-encrypting context. This is
|
|
12
12
|
* the RFC 9180 base construction: the DHKEM suites P-256, P-521, X25519, and
|
|
@@ -358,7 +358,7 @@ var suites = {
|
|
|
358
358
|
|
|
359
359
|
/**
|
|
360
360
|
* @primitive pki.hpke.setupS
|
|
361
|
-
* @signature pki.hpke.setupS(suiteIds, recipientPublicKey, opts?) -> { enc, context }
|
|
361
|
+
* @signature pki.hpke.setupS(suiteIds, recipientPublicKey, opts?) -> { enc, context, sharedSecret }
|
|
362
362
|
* @since 0.2.2
|
|
363
363
|
* @status stable
|
|
364
364
|
* @spec RFC 9180
|
package/lib/http-transport.js
CHANGED
|
@@ -16,15 +16,28 @@
|
|
|
16
16
|
* composes it (or an injected substitute) through one contract:
|
|
17
17
|
* `transport(request) -> Promise<{ status, headers, body, tls }>`. The first three are
|
|
18
18
|
* exactly what a message layer's classifier consumes, so no protocol semantics leak
|
|
19
|
-
* into the socket layer
|
|
19
|
+
* into the socket layer: the transport owns socket lifecycle, the TLS trust policy,
|
|
20
20
|
* the streaming size cap, and the timeout budget; the caller owns HTTP status,
|
|
21
21
|
* content-type, redirect, and authentication decisions. `tls` reports the negotiated
|
|
22
|
-
* channel
|
|
23
|
-
*
|
|
22
|
+
* channel, `{ protocol, cipher, peerCertificate, tlsUnique }`, facts a caller cannot
|
|
23
|
+
* recover from the response bytes. `tlsUnique` is the RFC 5929 channel binding (the first
|
|
24
|
+
* TLS Finished) on a TLS 1.2 connection, which `pki.est.challengePasswordFromTlsUnique`
|
|
25
|
+
* base64-encodes into an EST challengePassword (RFC 7030 sec. 3.5); it is `null` on TLS 1.3,
|
|
26
|
+
* where RFC 5929 defines no tls-unique. An INJECTED substitute should return these too:
|
|
24
27
|
* `pki.est.serverkeygen` asserts the negotiated cipher can protect the private key it
|
|
25
28
|
* is about to accept, and a transport that reports no cipher is trusted instead of
|
|
26
29
|
* refused (so a loopback test channel works), which means omitting the field silently
|
|
27
|
-
* skips that assertion.
|
|
30
|
+
* skips that assertion. Because `tlsUnique` on the response arrives after the request
|
|
31
|
+
* body was already sent, a request `body` MAY instead be a function `(tls) -> bytes`.
|
|
32
|
+
* The transport invokes it once the handshake completes and before it writes the body,
|
|
33
|
+
* passing the same `{ protocol, cipher, peerCertificate, tlsUnique }` object, so the
|
|
34
|
+
* caller builds the request body (a channel-bound CSR) from `tlsUnique` on the very
|
|
35
|
+
* connection the request is sent over. The callback returns bytes or a string, or a
|
|
36
|
+
* promise of either, so the CSR can be signed with the toolkit's own asynchronous
|
|
37
|
+
* signer while the connection is held open. A throw, a rejection, a missing return,
|
|
38
|
+
* `null`, or any other value fails the request closed, so a callback that forgets to
|
|
39
|
+
* return cannot post an empty enrollment to a CA. An explicit empty string is an
|
|
40
|
+
* intentional empty body, and the request budget still bounds the whole exchange.
|
|
28
41
|
*
|
|
29
42
|
* `pki.transport.https(defaults?)` binds TLS + budget defaults and returns a
|
|
30
43
|
* transport. Trust is EXPLICIT and fail-closed: a request is refused unless it
|
|
@@ -40,7 +53,7 @@
|
|
|
40
53
|
* @card
|
|
41
54
|
* The shared fail-closed node:https transport (est / acme / cmp): explicit trust
|
|
42
55
|
* anchors, rejectUnauthorized always on, a TLS floor, a streaming response-size cap,
|
|
43
|
-
* and a timeout
|
|
56
|
+
* and a timeout, behind one `transport(request) -> {status, headers, body, tls}` seam.
|
|
44
57
|
*/
|
|
45
58
|
|
|
46
59
|
var nodeHttps = require("node:https");
|
|
@@ -80,6 +93,17 @@ function _stripIpv6Brackets(s) {
|
|
|
80
93
|
return (s.length >= 2 && s.charAt(0) === "[" && s.charAt(s.length - 1) === "]") ? s.slice(1, s.length - 1) : s;
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
function _tlsInfo(socket) {
|
|
97
|
+
var proto = socket && socket.getProtocol ? socket.getProtocol() : null;
|
|
98
|
+
var cipher = socket && socket.getCipher ? socket.getCipher() : null;
|
|
99
|
+
var peer = socket && socket.getPeerCertificate ? socket.getPeerCertificate() : null;
|
|
100
|
+
var reused = socket && socket.isSessionReused ? socket.isSessionReused() : false;
|
|
101
|
+
var tlsUnique = (socket && socket.getFinished && socket.getPeerFinished && proto === "TLSv1.2")
|
|
102
|
+
? (reused ? socket.getPeerFinished() : socket.getFinished()) : null;
|
|
103
|
+
if (tlsUnique != null && tlsUnique.length === 0) tlsUnique = null;
|
|
104
|
+
return { protocol: proto, cipher: cipher, peerCertificate: peer && peer.raw ? peer.raw : null, tlsUnique: tlsUnique };
|
|
105
|
+
}
|
|
106
|
+
|
|
83
107
|
function _pemifyAnchor(a) {
|
|
84
108
|
if (typeof a === "string") return a;
|
|
85
109
|
if (Buffer.isBuffer(a)) {
|
|
@@ -351,7 +375,7 @@ function _proxyErrorClass(e, C) {
|
|
|
351
375
|
* (`transport/server-auth-failed`) all fail closed; `rejectUnauthorized` is always on.
|
|
352
376
|
* A protocol client passes its own error factory (`defaults.E`) + `defaults.errPrefix`
|
|
353
377
|
* to surface domain codes (`est/...`). The transport owns no HTTP/redirect/auth
|
|
354
|
-
* semantics
|
|
378
|
+
* semantics; those live in the message layer that consumes the response triple.
|
|
355
379
|
*
|
|
356
380
|
* @opts
|
|
357
381
|
* - `tls.anchors` -- Explicit trust anchor(s): a DER/PEM Buffer, an array, or PEM string(s) (node `ca`).
|
|
@@ -393,11 +417,14 @@ function httpsTransport(defaults) {
|
|
|
393
417
|
if (minVersion !== "TLSv1.2" && minVersion !== "TLSv1.3") throw E(C("bad-input"), "tls.minVersion must be 'TLSv1.2' or 'TLSv1.3' (never below the floor), got " + minVersion);
|
|
394
418
|
|
|
395
419
|
var body = request.body;
|
|
396
|
-
|
|
420
|
+
var bodyIsFn = typeof body === "function";
|
|
421
|
+
if (!bodyIsFn && guard.bytes.isByteSource(body)) body = guard.bytes.source(body, E, C("bad-input"), "the request body");
|
|
397
422
|
|
|
398
423
|
var reqHeaders = Object.assign({}, request.headers || {});
|
|
399
424
|
Object.keys(reqHeaders).forEach(function (k) { var lk = k.toLowerCase(); if (lk === "proxy-authorization" || lk === "proxy-connection") delete reqHeaders[k]; });
|
|
400
|
-
if (
|
|
425
|
+
if (bodyIsFn) {
|
|
426
|
+
Object.keys(reqHeaders).forEach(function (k) { var lk = k.toLowerCase(); if (lk === "content-length" || lk === "transfer-encoding" || lk === "expect") delete reqHeaders[k]; });
|
|
427
|
+
} else if (body != null && body !== "") {
|
|
401
428
|
Object.keys(reqHeaders).forEach(function (k) { var lk = k.toLowerCase(); if (lk === "content-length" || lk === "transfer-encoding") delete reqHeaders[k]; });
|
|
402
429
|
reqHeaders["Content-Length"] = Buffer.byteLength(body);
|
|
403
430
|
}
|
|
@@ -484,9 +511,7 @@ function httpsTransport(defaults) {
|
|
|
484
511
|
function issue(options) {
|
|
485
512
|
try {
|
|
486
513
|
req = nodeHttps.request(options, function (res) {
|
|
487
|
-
var
|
|
488
|
-
var cipher = res.socket && res.socket.getCipher ? res.socket.getCipher() : null;
|
|
489
|
-
var peer = res.socket && res.socket.getPeerCertificate ? res.socket.getPeerCertificate() : null;
|
|
514
|
+
var tlsInfo = _tlsInfo(res.socket);
|
|
490
515
|
var declared = parseInt((res.headers || {})["content-length"], 10);
|
|
491
516
|
if (Number.isFinite(declared) && declared > prep.maxBytes) { fail(C("response-too-large"), "the declared content-length " + declared + " exceeds the " + prep.maxBytes + "-byte cap (RFC 7030 sec. 6)"); return; }
|
|
492
517
|
var buf = Buffer.allocUnsafe(0);
|
|
@@ -514,14 +539,36 @@ function httpsTransport(defaults) {
|
|
|
514
539
|
status: res.statusCode,
|
|
515
540
|
headers: lower,
|
|
516
541
|
body: Buffer.from(buf.subarray(0, len)),
|
|
517
|
-
tls:
|
|
542
|
+
tls: tlsInfo,
|
|
518
543
|
});
|
|
519
544
|
});
|
|
520
545
|
res.on("error", function (e) { fail(C("transport-error"), "the response stream failed", e); });
|
|
521
546
|
});
|
|
522
547
|
req.on("error", function (e) { fail(_classifyError(e, C), "the request failed: " + ((e && e.message) || String(e)), e); });
|
|
523
|
-
if (
|
|
524
|
-
|
|
548
|
+
if (typeof prep.body === "function") {
|
|
549
|
+
req.once("socket", function (socket) {
|
|
550
|
+
function _writeBoundBody() {
|
|
551
|
+
if (settled) return;
|
|
552
|
+
var info = _tlsInfo(socket);
|
|
553
|
+
Promise.resolve().then(function () { return prep.body(info); }).then(function (b) {
|
|
554
|
+
if (settled) return;
|
|
555
|
+
try {
|
|
556
|
+
if (guard.bytes.isByteSource(b)) b = guard.bytes.source(b, E, C("bad-input"), "the channel-binding body");
|
|
557
|
+
else if (typeof b !== "string") { fail(C("bad-input"), "the request body callback must return bytes or a string; a missing return, null, or any other value is refused"); return; }
|
|
558
|
+
} catch (ve) { _settleReject(ve); return; }
|
|
559
|
+
try {
|
|
560
|
+
if (b !== "") { req.setHeader("Content-Length", Buffer.byteLength(b)); req.write(b); }
|
|
561
|
+
req.end();
|
|
562
|
+
} catch (e2) { fail(C("transport-error"), "the request body could not be written: " + ((e2 && e2.message) || String(e2)), e2); }
|
|
563
|
+
}, function (e) { fail(C("transport-error"), "the channel-binding body callback failed", e); });
|
|
564
|
+
}
|
|
565
|
+
if (socket.getFinished && socket.getFinished()) { _writeBoundBody(); return; }
|
|
566
|
+
socket.once("secureConnect", _writeBoundBody);
|
|
567
|
+
});
|
|
568
|
+
} else {
|
|
569
|
+
if (prep.body != null && prep.body !== "") req.write(prep.body);
|
|
570
|
+
req.end();
|
|
571
|
+
}
|
|
525
572
|
} catch (e) { fail(C("transport-error"), "the request could not be initiated: " + ((e && e.message) || String(e)), e); }
|
|
526
573
|
}
|
|
527
574
|
|
package/lib/inspect.js
CHANGED
|
@@ -795,7 +795,7 @@ var _INSPECT_BY_FORMAT = { x509: certificate, crl: crlReport, csr: csrReport, cm
|
|
|
795
795
|
* @related pki.schema.detectFormat, pki.inspect.certificate
|
|
796
796
|
*
|
|
797
797
|
* Detect which PKI format `input` (a PEM string or DER Buffer) encodes and render
|
|
798
|
-
* it with the matching report
|
|
798
|
+
* it with the matching report, the inspect analogue of `pki.schema.parse`. Routes
|
|
799
799
|
* a certificate / CRL / CSR / CMS to `certificate` / `crl` / `csr` / `cms`; a
|
|
800
800
|
* detected but out-of-scope format (OCSP, TSP, PKCS#8/#12, CRMF, CMP, ...) throws
|
|
801
801
|
* `inspect/unsupported-format` naming it, and an unrecognized input
|
package/lib/jose.js
CHANGED
|
@@ -294,7 +294,7 @@ function assertPublicJwk(jwk) {
|
|
|
294
294
|
* or the message is refused with `jose/key-mismatch`. They are compared as RFC 7638
|
|
295
295
|
* thumbprints, so member order and members outside the key itself cannot make equal
|
|
296
296
|
* keys look different. Without `opts.key` the embedded `jwk` is used where the
|
|
297
|
-
* profile permits one
|
|
297
|
+
* profile permits one, which verifies that the message is internally consistent,
|
|
298
298
|
* not that any particular signer produced it. `keySource` reports which of the two
|
|
299
299
|
* answered, so a signature checked against a caller-named key is distinguishable
|
|
300
300
|
* from one checked against the key the message brought with it.
|
package/lib/key.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `publicFromPrivate` over every algorithm the WebCrypto engine drives: RSA, EC, Ed25519/Ed448,
|
|
13
13
|
* X25519/X448, and the FIPS post-quantum ML-DSA / ML-KEM. Unencrypted export / import DELEGATES to the
|
|
14
14
|
* WebCrypto `exportKey` / `importKey` PKCS#8 / SPKI encoders (which already emit each algorithm's
|
|
15
|
-
* `AlgorithmIdentifier.parameters` correctly
|
|
15
|
+
* `AlgorithmIdentifier.parameters` correctly: RSA NULL, EC namedCurve, Ed/X ABSENT), so the wrapper
|
|
16
16
|
* never re-encodes an AlgorithmIdentifier. PBES2 encrypt / decrypt composes the one shared `lib/pbes2.js`
|
|
17
17
|
* home (the same PBKDF2 + AES-CBC primitives `pki.cms` uses). Parsing lives at `pki.schema.pkcs8.parse`.
|
|
18
18
|
* @spec RFC 5958, RFC 8018, RFC 5280
|
|
@@ -73,8 +73,8 @@ async function _toPrivateKeyDer(input) {
|
|
|
73
73
|
* (never encrypt opaque bytes), and the produced `EncryptedPrivateKeyInfo` is re-parsed before return.
|
|
74
74
|
*
|
|
75
75
|
* The PBKDF2 `prf` equal to the default (`hmacWithSHA1`) is omitted from the parameters (X.690 sec. 11.5),
|
|
76
|
-
* `keyLength` is omitted (the AES cipher OID fixes the key size), and the salt / iteration count are bounded
|
|
77
|
-
*
|
|
76
|
+
* `keyLength` is omitted (the AES cipher OID fixes the key size), and the salt / iteration count are bounded,
|
|
77
|
+
* so the output is byte-exact with OpenSSL's `pkcs8 -topk8 -v2`. A bad input throws a typed `KeyError`.
|
|
78
78
|
*
|
|
79
79
|
* @opts
|
|
80
80
|
* - `cipher` (string) -- `aes-256-cbc` (default), `aes-192-cbc`, or `aes-128-cbc`.
|
|
@@ -187,13 +187,13 @@ function _decryptPbes2(encAlg, ciphertext, password, opts) {
|
|
|
187
187
|
*
|
|
188
188
|
* Export an extractable `CryptoKey` to DER (or PEM): a private key as PKCS#8 `OneAsymmetricKey`, a public
|
|
189
189
|
* key as SubjectPublicKeyInfo. The encoding is delegated to the WebCrypto `exportKey` PKCS#8 / SPKI encoder,
|
|
190
|
-
* so the algorithm-specific `AlgorithmIdentifier.parameters` are byte-correct
|
|
190
|
+
* so the algorithm-specific `AlgorithmIdentifier.parameters` are byte-correct: RSA carries an explicit
|
|
191
191
|
* NULL, EC a namedCurve OID, and Ed25519 / Ed448 / X25519 / X448 omit parameters (RFC 8410 sec. 3); the
|
|
192
192
|
* wrapper never re-encodes the AlgorithmIdentifier.
|
|
193
193
|
*
|
|
194
194
|
* The key need not have been created by this toolkit's own WebCrypto: one from the platform's, or from a
|
|
195
|
-
* separately-installed copy of this toolkit, is exported through whichever of them holds its material
|
|
196
|
-
*
|
|
195
|
+
* separately-installed copy of this toolkit, is exported through whichever of them holds its material.
|
|
196
|
+
* A key created non-extractable is refused with that as the reason,
|
|
197
197
|
* as is one whose implementation keeps its material out of this process's reach entirely.
|
|
198
198
|
*
|
|
199
199
|
* @opts
|
|
@@ -231,7 +231,7 @@ async function export_(key, opts) {
|
|
|
231
231
|
* @related pki.key.export, pki.key.decrypt
|
|
232
232
|
*
|
|
233
233
|
* Import a DER / PEM PKCS#8 private key, SPKI public key, or (with `opts.password`) an `ENCRYPTED PRIVATE
|
|
234
|
-
* KEY
|
|
234
|
+
* KEY`, auto-detecting the structure, into a `CryptoKey`. The WebCrypto algorithm is inferred from the
|
|
235
235
|
* key's OID for the algorithms that name exactly one (Ed25519 / Ed448 / X25519 / X448 / ML-DSA / ML-KEM /
|
|
236
236
|
* SLH-DSA); RSA and EC are ambiguous between signing and key agreement, so `opts.algorithm` must be supplied
|
|
237
237
|
* for them (without it the import fails closed). Default key usages follow the algorithm and key type.
|
package/lib/lint.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @nav Tooling
|
|
7
7
|
* @title Lint
|
|
8
8
|
* @fullname Certificate linting against CA/Browser Forum requirements
|
|
9
|
-
* @intro The certificate LINTING engine
|
|
9
|
+
* @intro The certificate LINTING engine, the zlint / pkilint of JavaScript. It walks an
|
|
10
10
|
* ALREADY-PARSED certificate (from `pki.schema.x509.parse`, whose extension values it
|
|
11
11
|
* decodes with the shared RFC 5280 decoders) and emits graded, advisory FINDINGS: each
|
|
12
12
|
* with a stable id, a severity (`fatal` > `error` > `warn` > `notice` > `pass`), a source,
|
package/lib/merkle.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @intro
|
|
13
13
|
* RFC 6962 (Certificate Transparency) / RFC 9162 (CT 2.0) Merkle-tree hash
|
|
14
|
-
* and proof-verification core
|
|
14
|
+
* and proof-verification core: the load-bearing primitive a static-CT
|
|
15
15
|
* client, a Merkle-Tree-Certificates relying party, and a sigstore / Rekor
|
|
16
16
|
* inclusion check all compose. Every verb is strict verification over
|
|
17
17
|
* SHA-256: zero new crypto.
|
|
@@ -234,11 +234,7 @@ function verifyInclusion(opts) {
|
|
|
234
234
|
* `merkle/no-consistency-claim`. RFC 6962 sec. 2.1.2 defines the proof for
|
|
235
235
|
* `0 < oldSize < newSize`: the empty tree is a prefix of every tree by
|
|
236
236
|
* definition, so there is no proof to check and nothing at all binds `newRoot`.
|
|
237
|
-
*
|
|
238
|
-
* been proven, and a monitor written `if (!verifyConsistency(...)) alarm()` would
|
|
239
|
-
* take that silence for evidence. A caller with no prior tree has an inclusion
|
|
240
|
-
* question about the new one, or should start from a checkpoint it already
|
|
241
|
-
* trusts. Two empty trees are still answered:
|
|
237
|
+
* Two empty trees are still answered:
|
|
242
238
|
* that is the degenerate identity case, and both roots must be `emptyRootHash()`.
|
|
243
239
|
*
|
|
244
240
|
* A malformed input throws a typed `merkle/*` error: `oldSize > newSize`, a
|
package/lib/mime.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
6
|
-
* lib/mime.js
|
|
6
|
+
* lib/mime.js: a minimal MIME entity framer + canonicalizer, the message-layer engine primitive
|
|
7
7
|
* under pki.smime (RFC 8551 S/MIME) and any future MIME-carrying feature. It parses an entity into
|
|
8
8
|
* its headers + body, canonicalizes a text entity to the RFC 8551 sec. 3.1.1 form (CRLF line endings),
|
|
9
9
|
* splits a multipart body on its boundary, and builds an entity/multipart back. There is NO crypto
|
|
10
|
-
* here
|
|
10
|
+
* here. The CMS layer signs/verifies the canonical bytes this module produces.
|
|
11
11
|
*
|
|
12
12
|
* The load-bearing rule is canonicalization: the detached signature over a multipart/signed first part
|
|
13
13
|
* is computed over that part's canonical MIME form, so the signer and verifier MUST share one
|
package/lib/ocsp.js
CHANGED
|
@@ -352,14 +352,14 @@ function _certStatusNode(status, opts) {
|
|
|
352
352
|
|
|
353
353
|
/**
|
|
354
354
|
* @primitive pki.ocsp.buildErrorResponse
|
|
355
|
-
* @signature pki.ocsp.buildErrorResponse(status) -> Buffer
|
|
355
|
+
* @signature pki.ocsp.buildErrorResponse(status) -> Buffer
|
|
356
356
|
* @since 0.2.22
|
|
357
357
|
* @status stable
|
|
358
358
|
* @spec RFC 6960
|
|
359
359
|
* @related pki.ocsp.sign
|
|
360
360
|
*
|
|
361
|
-
* Build an UNSIGNED error OCSPResponse
|
|
362
|
-
* `sigRequired` / `unauthorized
|
|
361
|
+
* Build an UNSIGNED error OCSPResponse, one of `malformedRequest` / `internalError` / `tryLater` /
|
|
362
|
+
* `sigRequired` / `unauthorized`, carrying only the responseStatus and no responseBytes (RFC 6960
|
|
363
363
|
* sec. 2.3: an error message conveys no certificate status and is not signed).
|
|
364
364
|
*
|
|
365
365
|
* @example
|
|
@@ -374,7 +374,7 @@ function buildErrorResponse(status) {
|
|
|
374
374
|
|
|
375
375
|
/**
|
|
376
376
|
* @primitive pki.ocsp.verify
|
|
377
|
-
* @signature pki.ocsp.verify(response, opts) -> Promise<{ status, responderAuthorized, signatureValid, thisUpdate, nextUpdate, revocationReason?, revocationTime?, nonceMatched?, reason }>
|
|
377
|
+
* @signature pki.ocsp.verify(response, opts) -> Promise<{ status, responderAuthorized, signatureValid, matched, thisUpdate, nextUpdate, revocationReason?, revocationTime?, nonceMatched?, reason }>
|
|
378
378
|
* @since 0.2.22
|
|
379
379
|
* @status stable
|
|
380
380
|
* @spec RFC 6960, RFC 9654, RFC 5019
|
|
@@ -524,9 +524,9 @@ function _signerVerdict(parsed, found, failReason) {
|
|
|
524
524
|
* request carried (or opts.certs supplied), including intermediates that do not sign and so are absent
|
|
525
525
|
* from `signerCerts`: the responder passes it as the `opts.candidates` pool to `pki.path.build`, which
|
|
526
526
|
* discovers and validates the ordered path from a `signerCerts` entry (leaf) up to a trust anchor.
|
|
527
|
-
* The responder then confirms that certificate is authorized to sign the request
|
|
527
|
+
* The responder then confirms that certificate is authorized to sign the request: its `keyUsage`,
|
|
528
528
|
* where present, must assert `digitalSignature` (RFC 5280 sec. 4.2.1.3, since the certificate signed
|
|
529
|
-
* the request)
|
|
529
|
+
* the request). It also compares the certificate's subject to the `requestorName` it expects. The decoded
|
|
530
530
|
* `requestList`, `requestExtensions` and `version` are
|
|
531
531
|
* returned so the CertIDs being asked about need no re-parse.
|
|
532
532
|
*
|
package/lib/oid.js
CHANGED
|
@@ -446,7 +446,7 @@ function register(dotted, n) {
|
|
|
446
446
|
*
|
|
447
447
|
* Register a whole OID family in one call. `base` is the shared arc prefix
|
|
448
448
|
* (the starting variable a class of OIDs has in common) and `members` maps
|
|
449
|
-
* each name to its trailing arc
|
|
449
|
+
* each name to its trailing arc: a number, or a short arc array for a
|
|
450
450
|
* multi-level leaf. Each full OID is derived as `base` followed by the leaf,
|
|
451
451
|
* so a family is declared as its hierarchy and no full path is re-spelled.
|
|
452
452
|
* This is the primitive the built-in seed set itself is built from.
|
package/lib/path-validate.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* @intro
|
|
11
11
|
* RFC 5280 6 certification-path validation as a pure, re-entrant algorithm
|
|
12
12
|
* over already-parsed certificates. `pki.path.validate(path, opts)` runs the
|
|
13
|
-
* 6.1 state machine
|
|
13
|
+
* 6.1 state machine (signature chaining, validity windows, name chaining,
|
|
14
14
|
* basic constraints and path length, key usage, name constraints, and the
|
|
15
|
-
* certificate-policy tree
|
|
15
|
+
* certificate-policy tree) and returns a structured verdict with a per-check
|
|
16
16
|
* reason code for every step. Validity-window enforcement is always on, with
|
|
17
17
|
* the check date an explicit input; the trust anchor is an input, never one of
|
|
18
18
|
* the validated certificates, and no input object is mutated.
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
* Revocation is a pluggable hook: `pki.path.crlChecker(crls)` ships a CRL
|
|
21
21
|
* consultation built on `pki.schema.crl.parse`; an OCSP checker satisfies the
|
|
22
22
|
* same interface. Signature verification derives its algorithm from the
|
|
23
|
-
* certificate and the issuer key
|
|
23
|
+
* certificate and the issuer key, never from a value the message controls,
|
|
24
24
|
* and fails closed on an unknown critical extension, an undetermined
|
|
25
25
|
* revocation status, or any structural fault.
|
|
26
26
|
*
|
|
27
27
|
* @card
|
|
28
|
-
* RFC 5280 6 certification-path validation
|
|
28
|
+
* RFC 5280 6 certification-path validation: run the 6.1 state machine over
|
|
29
29
|
* an ordered path and a trust anchor for a structured, fail-closed verdict
|
|
30
30
|
* with per-check reason codes. Pure and re-entrant.
|
|
31
31
|
*/
|
|
@@ -836,7 +836,7 @@ function validateCriticalExtensionStructure(cert) {
|
|
|
836
836
|
* inputs (`initialExplicitPolicy`,
|
|
837
837
|
* `initialAnyPolicyInhibit`, `initialPolicyMappingInhibit`,
|
|
838
838
|
* `userInitialPolicySet`, and `initialPermittedSubtrees` /
|
|
839
|
-
* `initialExcludedSubtrees
|
|
839
|
+
* `initialExcludedSubtrees`: arrays of `{ tag, base }` where `tag` is the
|
|
840
840
|
* GeneralName tag number and `base` that form's constraint value), an
|
|
841
841
|
* optional `requiredEku` (key purposes the target's extendedKeyUsage must
|
|
842
842
|
* assert, given as registered OID names or dotted OID strings; an absent
|
|
@@ -846,9 +846,9 @@ function validateCriticalExtensionStructure(cert) {
|
|
|
846
846
|
* are validated at the entry point. A mis-shaped value throws `path/bad-input`,
|
|
847
847
|
* so it cannot silently go unapplied. Returns `{ valid, revocationChecked, anchorConstraints,
|
|
848
848
|
* path, results, workingPublicKey, workingPublicKeyAlgorithm,
|
|
849
|
-
* workingPublicKeyParameters, validPolicyTree }` where `results[i].checks`
|
|
849
|
+
* workingPublicKeyParameters, validPolicyTree, userConstrainedPolicySet }` where `results[i].checks`
|
|
850
850
|
* carries a per-check reason code (`path/*`) for every step. Pure and
|
|
851
|
-
* re-entrant
|
|
851
|
+
* re-entrant: no input object is mutated. An empty path or a missing anchor
|
|
852
852
|
* throws a typed `PathError`.
|
|
853
853
|
*
|
|
854
854
|
* `valid` alone cannot say whether revocation was ever established, so
|
|
@@ -1365,7 +1365,7 @@ function selectDelta(candidates) {
|
|
|
1365
1365
|
* (`thisUpdate`/`nextUpdate`), and reports `{ status: "good"|"revoked"|
|
|
1366
1366
|
* "unknown" }`. A partitioned/sharded CRL (a critical IDP naming a
|
|
1367
1367
|
* distribution point) establishes "good" when it corresponds to one of the
|
|
1368
|
-
* certificate's own cRLDistributionPoints
|
|
1368
|
+
* certificate's own cRLDistributionPoints: at least one identically-encoded
|
|
1369
1369
|
* name in common (RFC 5280 sec. 6.3.3). Reason-sharded CRLs ACCUMULATE: each
|
|
1370
1370
|
* corresponding CRL contributes its interim reason mask (sec. 6.3.3(d)) and the
|
|
1371
1371
|
* certificate is "good" once the shards together cover all eight revocation
|
|
@@ -1713,21 +1713,21 @@ function ocspChecker(responses) {
|
|
|
1713
1713
|
* directly, or a CA-issued delegate bearing both id-kp-OCSPSigning and
|
|
1714
1714
|
* id-pkix-ocsp-nocheck and passing the full out-of-path certificate gates),
|
|
1715
1715
|
* verifies the response signature over `tbsResponseDataBytes`, and checks
|
|
1716
|
-
* currency (`thisUpdate`/`nextUpdate`)
|
|
1716
|
+
* currency (`thisUpdate`/`nextUpdate`). There is no weaker second OCSP verify
|
|
1717
1717
|
* path. It is fail-closed and never throws on an unauthorized, stale, or
|
|
1718
1718
|
* unverifiable response: those yield `{ status: "unknown" }` with the granular
|
|
1719
1719
|
* `responderAuthorized`/`signatureValid`/`matched` flags and a `reason`; a
|
|
1720
1720
|
* `revoked` status surfaces its `revocationReason`. Setting `opts.historicalMode`
|
|
1721
1721
|
* treats a revocation whose `revocationTime` is strictly after `time` as not-yet-
|
|
1722
|
-
* revoked (`good`)
|
|
1722
|
+
* revoked (`good`): for validating a signature as of a past `time`, before the
|
|
1723
1723
|
* certificate was later revoked; the responder certificate is still validated at
|
|
1724
1724
|
* `time` either way. `time` must be a valid `Date`. A malformed response's parse
|
|
1725
1725
|
* fault surfaces as the parser's typed `ocsp/*` / `asn1/*` error.
|
|
1726
1726
|
*
|
|
1727
1727
|
* The response is its DER bytes, a PEM string, or an unmodified
|
|
1728
1728
|
* `pki.schema.ocsp.parseResponse` result. A REBUILT parsed response is refused. A
|
|
1729
|
-
* signature check has three parts
|
|
1730
|
-
* and the bytes it covers
|
|
1729
|
+
* signature check has three parts (the signature, the algorithm that verifies it,
|
|
1730
|
+
* and the bytes it covers) and on a parsed object all three are separate properties:
|
|
1731
1731
|
* a genuine CA signature over a certificate that CA issued, relabeled, verifies as a
|
|
1732
1732
|
* ResponseData signature for a response that never existed. The parser marks what it
|
|
1733
1733
|
* returns, so those three are known to have been derived together from one byte
|
|
@@ -2285,7 +2285,7 @@ async function _fetchAiaIssuers(current, aia) {
|
|
|
2285
2285
|
* from a certificate's Authority Information Access `caIssuers` URL (RFC 5280 sec. 4.2.2.1) over
|
|
2286
2286
|
* `pki.transport`: the fetch triggers only on a pool miss, every fetched certificate is UNTRUSTED pool material
|
|
2287
2287
|
* that still flows through `validate` when validation is on (never a trust anchor), and the whole surface is
|
|
2288
|
-
* SSRF/amplification bounded
|
|
2288
|
+
* SSRF/amplification bounded: https-only, a total fetch budget (a SILENT cap, never a throw that denies a
|
|
2289
2289
|
* buildable path), a per-cert URL cap, a build-wide URL dedupe, a response size + certificate-count cap, and no
|
|
2290
2290
|
* redirect following; every fetch fault is a silent skip. `aiaFetches` reports how many network GETs the build
|
|
2291
2291
|
* performed (`0` when `fetchAia` is off). NOTE: with `opts.validate:false` (pure-builder mode) a fetched cert is
|
package/lib/pkcs12-build.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @title PKCS#12
|
|
8
8
|
* @fullname PKCS#12 keystores (.p12 and .pfx): build and open
|
|
9
9
|
* @intro The PKCS#12 (.p12 / .pfx) producing side (RFC 7292, RFC 9579). `pki.pkcs12.build` assembles a
|
|
10
|
-
* password-integrity PFX
|
|
10
|
+
* password-integrity PFX: key, certificate, CRL, and secret bags (shrouded keys and cert safes encrypted
|
|
11
11
|
* under RFC 8018 PBES2) wrapped in an AuthenticatedSafe, protected by either a classic Appendix B HMAC or
|
|
12
12
|
* an RFC 9579 PBMAC1 (PBKDF2 + HMAC) MAC, over AES-128/192/256-CBC and SHA-256/384/512. Every password is
|
|
13
13
|
* encoded the PKCS#12 way (BMPString + NULL, Appendix B.1), so a file it emits opens in OpenSSL and NSS.
|
|
@@ -427,7 +427,7 @@ function _normalizeSpec(spec, opts, form) {
|
|
|
427
427
|
*
|
|
428
428
|
* @opts
|
|
429
429
|
* - `password` -- the shared privacy + integrity password (string / Buffer / Uint8Array).
|
|
430
|
-
* - `mac` -- `false` for a MAC-less store, or `{ algorithm: 'hmac'(default)|'pbmac1', hash: 'sha256'(default)|'sha1'|'sha384'|'sha512', salt?, iterations? }`. `keyLength` is a `pbmac1` field: the classic Appendix B derivation produces a key at the hash's own output length, and naming one under `hmac` is refused.
|
|
430
|
+
* - `mac` -- `false` for a MAC-less store, or `{ algorithm: 'hmac'(default)|'pbmac1', hash: 'sha256'(default)|'sha1'|'sha384'|'sha512', salt?, iterations?, password? }`. `password` overrides the shared integrity password for the MAC. `keyLength` is a `pbmac1` field: the classic Appendix B derivation produces a key at the hash's own output length, and naming one under `hmac` is refused.
|
|
431
431
|
* - `integrity` -- `{ mode: 'public-key', signer: { cert, key, digestAlgorithm?, pss? } | signers: [ ... ], sid?, signingTime?, certificates? }` for public-key integrity (a CMS SignedData authSafe over any `pki.cms.sign` signer algorithm, no MacData). Combining it with a truthy `mac` is rejected.
|
|
432
432
|
* - `recipientCerts` -- (public-key privacy convenience) `[certDer|PEM, ...]`; the convenience-form cert + key are placed in one recipient-enveloped safe (a plaintext keyBag). For the full form, set per-`safeContents` `recipients: [{ cert }, ...]` -- CERTIFICATE recipients only (RSA-OAEP / ECDH / X25519 / X448 / ML-KEM, dispatched off the cert key); a password or KEK recipient is not public-key privacy and is rejected -- with an optional `contentEncryptionAlgorithm` (`aes-128|192|256-cbc`, default 256; GCM/AEAD rejected). `encrypt` (password) and `recipients` (public-key) on the same safe is rejected; privacy is independent of integrity.
|
|
433
433
|
* - `pem` (boolean) -- return a PEM `PKCS12` string instead of DER.
|
|
@@ -534,7 +534,7 @@ async function _build(spec, opts) {
|
|
|
534
534
|
* a byte range the object carries, and the parser's mark is what says that range and the store it describes
|
|
535
535
|
* came from one place. `Object.assign`, spread and a JSON round-trip drop the mark. The password is BMPString+NULL encoded (RFC 7292 App. B.1), the MAC is
|
|
536
536
|
* recomputed over the store's exact AuthenticatedSafe byte range (`macedBytes`) using the store's own MAC
|
|
537
|
-
* parameters
|
|
537
|
+
* parameters, the classic Appendix B (ID=3) HMAC or the RFC 9579 PBMAC1, and constant-time-compared to
|
|
538
538
|
* the stored MAC value. The verdict's `valid` is the password match, and `macAlgorithm` (`hmac` or `pbmac1`),
|
|
539
539
|
* `macAlgorithmName` (the digest / scheme, e.g. `sha1`, `sha256`), and `iterationCount` name the integrity
|
|
540
540
|
* algorithm the store used, so a caller can reject a legacy SHA-1 MAC by inspecting them rather than
|
|
@@ -617,7 +617,7 @@ function _capWork(iterations, salt, opts, keyLength, hardCap) {
|
|
|
617
617
|
* SignedData signature, per RFC 7292 sec. 4 / 5.1, so no bag from a store whose integrity check fails is trusted),
|
|
618
618
|
* then decrypt every PBES2 privacy safe and pkcs8ShroudedKeyBag with the password, and every id-envelopedData
|
|
619
619
|
* (public-key privacy) safe with `opts.recipientKey` (RFC 7292 sec. 3.1, via `pki.cms.decrypt`), returning a
|
|
620
|
-
* structured bundle `{ integrityMode, macVerified, signers, keys, certs, crls, secrets }` with each private key as PKCS#8
|
|
620
|
+
* structured bundle `{ valid, integrityMode, macVerified, signers, keys, certs, crls, secrets }` with each private key as PKCS#8
|
|
621
621
|
* `PrivateKeyInfo` DER (re-validated), each certificate / CRL / secret as raw DER, all carrying their
|
|
622
622
|
* `friendlyName` / `localKeyId` for pairing. `pfx` is the store's DER `Buffer`, a PEM string, or an
|
|
623
623
|
* unmodified `pki.schema.pkcs12.parse` result; a rebuilt parsed store is refused, since the bytes whose
|
package/lib/rfc3339.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
6
|
-
* lib/rfc3339.js
|
|
6
|
+
* lib/rfc3339.js: RFC 3339 date-time parsing + validation, a fail-closed engine primitive shared by
|
|
7
7
|
* pki.acme (a boolean field-validator over server-supplied JSON) and pki.ct (parse a CT log-list
|
|
8
8
|
* timestamp / temporal-interval bound to a comparable Date). Beyond the grammar it enforces CALENDAR
|
|
9
|
-
* validity
|
|
9
|
+
* validity: a syntactically well-formed but impossible instant (month 13, February 30, hour 25, a
|
|
10
10
|
* :60 leap second, a +25:00 offset) is rejected, so a downstream expiry / window comparison never runs
|
|
11
11
|
* on a value JS `Date` would silently roll over or `Date.parse` would NaN. Like the byte-reader / guard
|
|
12
12
|
* family, `parse` takes the caller's `(code, message) -> error` factory so every consumer keeps its own
|
package/lib/scep.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* issued certificate) in two nested CMS layers: an inner EnvelopedData
|
|
15
15
|
* (`pkcsPKIEnvelope`) that encrypts the messageData to the recipient, and an
|
|
16
16
|
* outer SignedData that signs it under a set of authenticated transaction
|
|
17
|
-
* attributes
|
|
17
|
+
* attributes: messageType, transactionID, pkiStatus, failInfo, and the
|
|
18
18
|
* sender/recipient nonces (RFC 8894 sec. 3). `pki.scep.build` assembles a
|
|
19
19
|
* request pkiMessage (PKCSReq, RenewalReq, CertPoll, GetCert, or GetCRL) or a
|
|
20
20
|
* CA-side CertRep response; `pki.scep.parse` verifies the
|
|
@@ -520,7 +520,7 @@ async function parse(bytes, opts) {
|
|
|
520
520
|
var messageType = MESSAGE_TYPE_BY_CODE[messageTypeCode];
|
|
521
521
|
if (messageType == null) throw _err("scep/bad-message-type", "unknown messageType " + JSON.stringify(messageTypeCode) + " (RFC 8894 sec. 3.2.1.2)");
|
|
522
522
|
if (_PARSE_SUPPORTED[messageType] !== 1) {
|
|
523
|
-
throw _err("scep/unsupported-message-type", "pki.scep.parse reads
|
|
523
|
+
throw _err("scep/unsupported-message-type", "pki.scep.parse reads " + Object.keys(_PARSE_SUPPORTED).join(", ") + "; " + messageType + " is not read in this release");
|
|
524
524
|
}
|
|
525
525
|
var out = {
|
|
526
526
|
signatureValid: true, signerAuthenticated: signerAuthenticated, signerCert: signer.cert,
|
package/lib/schema-all.js
CHANGED
|
@@ -153,7 +153,7 @@ var FORMATS = [
|
|
|
153
153
|
* The names of every registered format, in detection order.
|
|
154
154
|
*
|
|
155
155
|
* @example
|
|
156
|
-
* pki.schema.all(); // -> ["cms", "tsp", "crmf", "cmp", "ocsp-request", "ocsp-response", "pkcs12", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
|
|
156
|
+
* pki.schema.all(); // -> ["cms", "tsp", "crmf", "cmp", "csrattrs", "ocsp-request", "ocsp-response", "pkcs12", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
|
|
157
157
|
*/
|
|
158
158
|
function all() { return FORMATS.map(function (f) { return f.name; }); }
|
|
159
159
|
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @intro
|
|
12
12
|
* X.509 Attribute Certificate handling per RFC 5755. An attribute certificate
|
|
13
13
|
* binds a holder to a set of privilege attributes (role, group, clearance)
|
|
14
|
-
* without carrying a public key
|
|
14
|
+
* without carrying a public key, the authorization counterpart to an identity
|
|
15
15
|
* certificate. `parse` decodes a v2 `AttributeCertificate` into its holder,
|
|
16
16
|
* issuer, validity window (real `Date`s), attributes, and extensions, reusing
|
|
17
17
|
* the shared signed-envelope so the raw `tbsBytes` (the exact signed region) and
|