@blamejs/pki 0.6.29 → 0.6.30
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 +17 -0
- package/lib/acme.js +6 -6
- 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/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 +8 -8
- package/lib/framework-error.js +2 -2
- package/lib/hpke.js +2 -2
- package/lib/http-transport.js +4 -4
- 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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.6.30 — 2026-09-04
|
|
8
|
+
|
|
9
|
+
Reference documentation is corrected across the toolkit: a mistyped signed-attribute field name, and options and return fields the code accepts or returns that the reference had omitted.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Options and return fields the code accepts or returns are now documented across pki.cms, pki.tsp, pki.pkcs12, pki.hpke, pki.path, pki.ocsp, pki.asn1, pki.est, pki.acme, and pki.webauthn. In particular, pki.webauthn.verifyAssertion is documented as checking the challenge and origin when expectedChallenge / expectedOrigin / expectedTopOrigin are supplied with clientDataJSON.
|
|
14
|
+
- Reference prose across the toolkit and the documentation site is reworded for consistency.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- pki.smime.sign documented additionalSignedAttributes as [{ oid, values }]; the code reads the type field, so a caller following the reference would pass an unrecognized field. The correct form is [{ type, values }].
|
|
19
|
+
- pki.ocsp.buildErrorResponse is documented as returning a Buffer; the signature previously showed Buffer | string, but the verb has no PEM option and every path returns DER.
|
|
20
|
+
- The pki.schema.all example output lists csrattrs, which the enumeration returns between cmp and ocsp-request.
|
|
21
|
+
- The pki.acme.client method list documents updateAccount and listOrders, which the client returns.
|
|
22
|
+
- pki.tsp.verify now includes policyName on a failure verdict; it already returned the resolved policy name on a valid verdict and the raw policy on both, so a refused token carries the same descriptive fields as a valid one.
|
|
23
|
+
|
|
7
24
|
## v0.6.29 — 2026-09-03
|
|
8
25
|
|
|
9
26
|
The EST, ACME, and CMP clients reach a CA through a forward HTTP proxy, with the same opts.proxy the SCEP client already accepts.
|
package/lib/acme.js
CHANGED
|
@@ -513,7 +513,7 @@ function validate(kind, obj) {
|
|
|
513
513
|
* @related pki.acme.validate
|
|
514
514
|
*
|
|
515
515
|
* Classify an ACME JSON object into exactly one kind by its discriminating
|
|
516
|
-
* member set
|
|
516
|
+
* member set: `"jws"`, `"problem"`, `"directory"`, `"order"`, `"authorization"`,
|
|
517
517
|
* `"challenge"`, `"account"`, `"renewalInfo"`, or `"unknown"`. The discriminators
|
|
518
518
|
* are proven mutually exclusive; a DER structure identifies as `"unknown"`.
|
|
519
519
|
*
|
|
@@ -1109,7 +1109,7 @@ async function finalize(o) {
|
|
|
1109
1109
|
* @related pki.acme.http01, pki.acme.dns01
|
|
1110
1110
|
*
|
|
1111
1111
|
* Build a challenge-response POST (RFC 8555 sec. 7.5.1): a kid-signed JWS whose
|
|
1112
|
-
* payload is the type-defined response object
|
|
1112
|
+
* payload is the type-defined response object: `{}` for the three registered
|
|
1113
1113
|
* challenge types (http-01 / dns-01 / tls-alpn-01), which is DISTINCT from a
|
|
1114
1114
|
* POST-as-GET empty payload. `opts` = `{ key, alg, nonce, url, kid, payload? }`
|
|
1115
1115
|
* (payload default `{}`; pass a custom object for a future challenge type).
|
|
@@ -1139,7 +1139,7 @@ function challengeResponse(o) {
|
|
|
1139
1139
|
* @related pki.acme.validate
|
|
1140
1140
|
*
|
|
1141
1141
|
* Build a deactivation POST (RFC 8555 sec. 7.3.6 account / sec. 7.5.2
|
|
1142
|
-
* authorization): a kid-signed JWS with the payload `{"status":"deactivated"}
|
|
1142
|
+
* authorization): a kid-signed JWS with the payload `{"status":"deactivated"}`,
|
|
1143
1143
|
* the only client-settable status. `opts` = `{ key, alg, nonce, url, kid }`.
|
|
1144
1144
|
*
|
|
1145
1145
|
* @example
|
|
@@ -1196,7 +1196,7 @@ function updateAccount(o) {
|
|
|
1196
1196
|
* Build a revokeCert request (RFC 8555 sec. 7.6): a JWS whose payload `certificate`
|
|
1197
1197
|
* is the base64url of the DER certificate and optional `reason` is an assigned
|
|
1198
1198
|
* RFC 5280 CRLReason (0-6, 8-10; 7 is unassigned). Signed EITHER by the account key (`kid` mode) OR by the
|
|
1199
|
-
* certificate key (`jwk` mode)
|
|
1199
|
+
* certificate key (`jwk` mode). Pass exactly one. `opts` = `{ key, alg, nonce,
|
|
1200
1200
|
* url, certificate (DER Buffer), reason?, kid? | jwk? }`.
|
|
1201
1201
|
*
|
|
1202
1202
|
* @example
|
|
@@ -1347,7 +1347,7 @@ function parseAriCertId(certId) {
|
|
|
1347
1347
|
*
|
|
1348
1348
|
* Validate an ARI RenewalInfo object (RFC 9773 sec. 4.2): a `suggestedWindow` with
|
|
1349
1349
|
* RFC 3339 `start` and `end`, `end` strictly after `start` (an inverted or
|
|
1350
|
-
* zero-width window throws `acme/bad-renewal-window
|
|
1350
|
+
* zero-width window throws `acme/bad-renewal-window`: the client treats it as no
|
|
1351
1351
|
* response, defusing a renewal stampede), and an optional `explanationURL`.
|
|
1352
1352
|
* Returns the object.
|
|
1353
1353
|
*
|
|
@@ -1758,7 +1758,7 @@ function _parseLinkNext(headers, base) { var m = _parseLinkRelation(headers, bas
|
|
|
1758
1758
|
* capped by a poll count and a total-wait budget. Returns a client
|
|
1759
1759
|
* object: `directory`, `newAccount`, `newOrder`, `newAuthz`, `getOrder` / `getAuthorization` / `getChallenge`,
|
|
1760
1760
|
* `respondToChallenge`, `finalize`, `pollOrder` / `pollAuthorization`, `downloadCertificate`,
|
|
1761
|
-
* `revokeCert`, `deactivateAccount` / `deactivateAuthorization`, `keyChange`, `renewalInfo`, `renewalWindow`, `scheduleRenewal`.
|
|
1761
|
+
* `revokeCert`, `deactivateAccount` / `deactivateAuthorization`, `updateAccount`, `listOrders`, `keyChange`, `renewalInfo`, `renewalWindow`, `scheduleRenewal`.
|
|
1762
1762
|
*
|
|
1763
1763
|
* @opts
|
|
1764
1764
|
* - `accountKey` / `accountJwk` / `alg` -- REQUIRED: the account private key, its public JWK, and the JWS alg.
|
package/lib/asn1-der.js
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* turn a node into a JS value (BigInt, dotted OID, Date, string); the
|
|
24
24
|
* `build.*` helpers construct canonical DER from JS values. Because DER
|
|
25
25
|
* is canonical, each value the `build.*` helpers emit has exactly one
|
|
26
|
-
* valid encoding
|
|
27
|
-
* output
|
|
26
|
+
* valid encoding, byte-identical to any other conformant DER encoder's
|
|
27
|
+
* output, and decoding it reproduces the value it was built from.
|
|
28
28
|
*
|
|
29
29
|
* @card
|
|
30
30
|
* Strict, fail-closed DER decode / encode with a navigable node tree
|
|
@@ -112,9 +112,10 @@ function _asBuffer(input, who) {
|
|
|
112
112
|
*
|
|
113
113
|
* Parse DER into a node tree. Each node is
|
|
114
114
|
* `{ tagClass, constructed, tagNumber, header, length, content, children,
|
|
115
|
-
* bytes }`, where `content` is the primitive value slice, `children` the
|
|
116
|
-
* decoded sub-nodes of a constructed node,
|
|
117
|
-
*
|
|
115
|
+
* bytes, contentStart, contentEnd }`, where `content` is the primitive value slice, `children` the
|
|
116
|
+
* decoded sub-nodes of a constructed node, `bytes` the full TLV slice, and
|
|
117
|
+
* `contentStart` / `contentEnd` the content slice's byte offsets in the input
|
|
118
|
+
* (all slices are zero-copy views over the input).
|
|
118
119
|
*
|
|
119
120
|
* Throws `Asn1Error` on any non-DER shape: indefinite length, a
|
|
120
121
|
* non-minimal length or a length that overruns the buffer, trailing bytes
|
|
@@ -124,13 +125,14 @@ function _asBuffer(input, who) {
|
|
|
124
125
|
* `ber: true` is a scoped relaxation for formats whose content regions are
|
|
125
126
|
* normatively BER (RFC 7292 PKCS#12): it accepts an indefinite length on a
|
|
126
127
|
* constructed value and a constructed OCTET STRING, whose segments are
|
|
127
|
-
* reassembled into one primitive `content`. Nothing else is relaxed
|
|
128
|
+
* reassembled into one primitive `content`. Nothing else is relaxed:
|
|
128
129
|
* definite lengths stay minimal, an indefinite length on a primitive value
|
|
129
130
|
* and a foreign-type segment still reject, and the size / depth caps hold.
|
|
130
131
|
*
|
|
131
132
|
* @opts
|
|
132
133
|
* maxBytes: number, // default: C.LIMITS.DER_MAX_BYTES (16 MiB)
|
|
133
134
|
* maxDepth: number, // default: C.LIMITS.DER_MAX_DEPTH (64)
|
|
135
|
+
* maxItems: number, // default: C.LIMITS.DER_MAX_ITEMS (total decoded node cap)
|
|
134
136
|
* allowTrailing: boolean, // default false; allow bytes after the top TLV
|
|
135
137
|
* ber: boolean, // default false; accept indefinite lengths +
|
|
136
138
|
* // constructed OCTET STRINGs (BER content regions)
|
|
@@ -680,7 +682,7 @@ function encodeIdentifier(classBits, constructed, tagNumber) {
|
|
|
680
682
|
* @spec X.690, ISO/IEC 8825-1
|
|
681
683
|
* @related pki.asn1.decode
|
|
682
684
|
*
|
|
683
|
-
* Low-level TLV encoder
|
|
685
|
+
* Low-level TLV encoder: prepend the identifier + DER length to a content
|
|
684
686
|
* buffer. Most callers use the higher-level `build.*` helpers; this is the
|
|
685
687
|
* escape hatch for context-tagged and implicitly-tagged constructions.
|
|
686
688
|
*
|
package/lib/attrcert-sign.js
CHANGED
|
@@ -583,17 +583,17 @@ function _unsupportedCritical(ac) {
|
|
|
583
583
|
* @defends attribute-certificate-forgery (CWE-347)
|
|
584
584
|
* @related pki.attrcert.sign, pki.path.validate, pki.schema.attrcert.parse
|
|
585
585
|
*
|
|
586
|
-
* Verify an RFC 5755 attribute certificate. An AC carries privilege attributes
|
|
587
|
-
* clearance
|
|
586
|
+
* Verify an RFC 5755 attribute certificate. An AC carries privilege attributes (a role, a group, a
|
|
587
|
+
* clearance), so a consumer that reads them without this grants privileges on unauthenticated
|
|
588
588
|
* input. `ac` is a DER `Buffer`, a PEM string, or a parsed attribute certificate; `issuer` is
|
|
589
589
|
* `{ name, publicKey }`, the AC issuer this verifier trusts as an AC issuer, which section 5 item 4
|
|
590
590
|
* makes the verifier's own configuration and this verb therefore refuses to infer from the AC.
|
|
591
|
-
* `issuer.name` takes every form `pki.attrcert.sign` accepts for the issuing AA
|
|
592
|
-
* array of RDNs, or raw `Name` DER
|
|
591
|
+
* `issuer.name` takes every form `pki.attrcert.sign` accepts for the issuing AA (a string, an
|
|
592
|
+
* array of RDNs, or raw `Name` DER) and is compared as a distinguished name (RFC 5280 section
|
|
593
593
|
* 7.1), so an AC issued under a multi-RDN authority name is verifiable under that same name.
|
|
594
594
|
*
|
|
595
595
|
* Both options are read at the call, before signature verification suspends it, so a caller that
|
|
596
|
-
* reuses or rewrites its options object
|
|
596
|
+
* reuses or rewrites its options object (including calling `setTime` on the `Date` it passed in)
|
|
597
597
|
* cannot change the verdict that call returns.
|
|
598
598
|
*
|
|
599
599
|
* The checks it performs are section 5's items 2, 4, 5, 6 and 7: the signature over the exact
|
|
@@ -613,7 +613,7 @@ function _unsupportedCritical(ac) {
|
|
|
613
613
|
* (RFC 5280 sec. 7.2), a mailbox matches its local-part exactly and its host-part case-insensitively
|
|
614
614
|
* (sec. 7.5), and a directoryName goes through the same distinguished-name comparison the rest of
|
|
615
615
|
* the toolkit uses (sec. 7.1). A URI compares as encoded, because sec. 7.4 makes URI equality a full
|
|
616
|
-
* RFC 3987 normalization
|
|
616
|
+
* RFC 3987 normalization (percent-encoding, path segments and scheme-based rules) and a partial
|
|
617
617
|
* one could match a URI it should not. A form with no comparison here (otherName, ediPartyName,
|
|
618
618
|
* x400Address) leaves `targetingChecked: false` and never a pass. Every one of those limits can
|
|
619
619
|
* refuse a target a fuller comparison would accept, and none can accept one it would refuse.
|
|
@@ -627,8 +627,8 @@ function _unsupportedCritical(ac) {
|
|
|
627
627
|
*
|
|
628
628
|
* Revocation is section 6, outside the seven. This verb implements that section's "never revoke"
|
|
629
629
|
* scheme, which AC users MUST support: it holds no revocation evidence and follows no pointer out
|
|
630
|
-
* of the certificate. The section states the consequence
|
|
631
|
-
* supported, then all ACs that do not contain a noRevAvail extension, MUST be rejected"
|
|
630
|
+
* of the certificate. The section states the consequence, "If only the 'never revoke' scheme is
|
|
631
|
+
* supported, then all ACs that do not contain a noRevAvail extension, MUST be rejected", because
|
|
632
632
|
* an issuer that omits noRevAvail is stating that revocation status checks are supported, and a
|
|
633
633
|
* verdict that skipped one would grant privileges the issuer expected to be able to withdraw. So an
|
|
634
634
|
* AC carrying `noRevAvail` verifies with `revocationChecked: true` and `noRevAvail: true`, and one
|
package/lib/byte-reader.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
6
|
-
* lib/byte-reader.js
|
|
6
|
+
* lib/byte-reader.js: a bounded big-endian byte cursor, the shared ENCODING-layer
|
|
7
7
|
* primitive under the toolkit's non-ASN.1 length-prefixed wire formats (TLS-vector
|
|
8
8
|
* SCTs in pki.ct, the packed big-endian TPM2B_* / authenticatorData structures in
|
|
9
9
|
* pki.webauthn). Every read is length-checked against a hard [pos, end) window
|
package/lib/byte-writer.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
6
|
-
* lib/byte-writer.js
|
|
6
|
+
* lib/byte-writer.js: a growable big-endian byte sink, the ENCODING-layer twin of
|
|
7
7
|
* lib/byte-reader.js, the shared producer under the toolkit's non-ASN.1 length-prefixed
|
|
8
8
|
* wire formats (the TLS-vector SCT list pki.ct emits; any future TLS-presentation
|
|
9
9
|
* producer). Every fixed-width integer is range-checked against its field width and
|
package/lib/cmc-verify.js
CHANGED
|
@@ -200,11 +200,11 @@ function _governingStatus(statuses, outcome) {
|
|
|
200
200
|
* Interpret a Full PKI Response into one terminal verdict. `response` is the DER or a PEM `CMS`
|
|
201
201
|
* block; an already-parsed message is interpreted too, but only under `allowUnverified: true`,
|
|
202
202
|
* because both carriers authenticate over BYTES and a parsed object the caller still owns pins
|
|
203
|
-
* none. `sent` is what the client retained from its request
|
|
203
|
+
* none. `sent` is what the client retained from its request:
|
|
204
204
|
* `transactionId`, `senderNonce`, `dataReturn`, and `bodyPartIDs` (every identifier the request
|
|
205
205
|
* carried). Each of those is checked only if it was sent, and once sent an absent or differing
|
|
206
206
|
* echo is a refusal: that asymmetry is the replay defense. `bodyPartIDs` is the same rule applied
|
|
207
|
-
* to what the response is ABOUT
|
|
207
|
+
* to what the response is ABOUT: a status naming a body part the request never sent is refused
|
|
208
208
|
* with `cmc/body-part-unknown`, which the transaction and nonce cannot catch, since a server can
|
|
209
209
|
* echo both correctly while reporting on something else.
|
|
210
210
|
*
|
|
@@ -229,7 +229,7 @@ function _governingStatus(statuses, outcome) {
|
|
|
229
229
|
* The carrier's signature MUST be verified (RFC 5272 sec. 3.2.1.3.4). A conforming SignedData
|
|
230
230
|
* carries its own signer certificate, so the ordinary case needs nothing from the caller: the
|
|
231
231
|
* signature is checked and the verdict reports `signatureVerified: true`. Where the signer cannot be
|
|
232
|
-
* found
|
|
232
|
+
* found (not embedded and not supplied), the posture is fail-closed with a NAMED opt-out rather
|
|
233
233
|
* than a silent one: pass `certs` with the responder's certificate, or `allowUnverified: true` and
|
|
234
234
|
* the verdict reports `signatureVerified: false`. Doing neither is refused, because that is the case
|
|
235
235
|
* where a caller would otherwise assume a check happened. The opt-out covers "could not check",
|
|
@@ -245,7 +245,7 @@ function _governingStatus(statuses, outcome) {
|
|
|
245
245
|
* who holds the key gets an authenticated verdict instead of the unauthenticated opt-out.
|
|
246
246
|
*
|
|
247
247
|
* Nothing is trusted here. `certificates` is the CMS certificate bag, where RFC 5272 sec. 4.2
|
|
248
|
-
* puts the issued certificates
|
|
248
|
+
* puts the issued certificates, surfaced raw for the caller to run through `pki.path.validate`,
|
|
249
249
|
* and a Publish Trust Anchors control is surfaced as `publishTrustAnchors` with `trusted: false`,
|
|
250
250
|
* never added to any store (RFC 5272 sec. 6.15 makes accepting one a four-part manual decision).
|
|
251
251
|
* The CMS signature is likewise the caller's to verify through `pki.cms.verify`.
|
package/lib/cmp-build.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @title Certificate management protocol messages
|
|
9
9
|
* @fullname CMP (Certificate Management Protocol, RFC 4210)
|
|
10
10
|
* @intro The RFC 9810 Certificate Management Protocol producing side. `pki.cmp.build` assembles a
|
|
11
|
-
* `PKIMessage
|
|
11
|
+
* `PKIMessage`: a `PKIHeader` (sender / recipient / transaction metadata), a `PKIBody` carrying one
|
|
12
12
|
* request or transaction (an `ir` / `cr` / `kur` certificate request via `pki.crmf.build`, a `p10cr`
|
|
13
13
|
* PKCS#10 via `pki.csr.sign`, a `certConf` / `pollReq` / `genm` / `rr`, or a `nested` wrapper of other
|
|
14
14
|
* PKIMessages an RA forwards), and an optional
|
|
@@ -682,7 +682,7 @@ function _collectExtraCerts(opts, protCertDer) {
|
|
|
682
682
|
* @spec RFC 9810, RFC 9481, RFC 9579
|
|
683
683
|
* @related pki.schema.cmp.parse
|
|
684
684
|
*
|
|
685
|
-
* Build an RFC 9810 CMP `PKIMessage
|
|
685
|
+
* Build an RFC 9810 CMP `PKIMessage`, the producing-side inverse of `pki.schema.cmp.parse`. `message` is
|
|
686
686
|
* `{ header, body }`: `header` carries the `sender` / `recipient` GeneralNames plus optional transaction
|
|
687
687
|
* metadata (`transactionID`, `senderNonce`, `messageTime`, `freeText`, `generalInfo`, ...); `body` is a
|
|
688
688
|
* single-key object naming the arm. Request-side: `{ ir }` / `{ cr }` / `{ kur }` / `{ krr }` (key recovery,
|
|
@@ -924,7 +924,7 @@ function wellKnownUrl(base, opts) {
|
|
|
924
924
|
*
|
|
925
925
|
* Build an RFC 9811 sec. 3.4 CMP request-URI under the `/.well-known/cmp` prefix (RFC 8615). The four forms:
|
|
926
926
|
* `<base>/.well-known/cmp`, `.../<operation>`, `.../p/<label>`, and `.../p/<label>/<operation>`. A pure string
|
|
927
|
-
* builder over an authority-rooted well-known path (RFC 8615)
|
|
927
|
+
* builder over an authority-rooted well-known path (RFC 8615): https vs http is left to the transport (both
|
|
928
928
|
* forms are accepted, RFC 9811 sec. 3.4), but the `base` MUST be an http(s) URL. It rejects (`cmp/bad-url`) an
|
|
929
929
|
* unparseable `base`, a non-http(s) scheme (`file:`/`data:`/a custom scheme has an opaque origin), a `base`
|
|
930
930
|
* carrying a path / query / fragment (a well-known URI is authority-rooted; a path would capture it), and a
|
package/lib/cmp-session.js
CHANGED
|
@@ -265,14 +265,14 @@ function _certConfHash(certDer) {
|
|
|
265
265
|
* requirements, whose template must omit `publicKey`, `serialNumber`, `signingAlg`, `issuerUID` and
|
|
266
266
|
* `subjectUID`, and whose `keySpec` states key requirements as `algId` (a non-RSA AlgorithmIdentifier) or
|
|
267
267
|
* `rsaKeyLen` (a positive integer); and `{ crlUpdate: { issuer, dpn?, thisUpdate? } }` for a CRL from a
|
|
268
|
-
* named source, held to that source
|
|
269
|
-
* against the CRL's own `issuingDistributionPoint` where it states one
|
|
268
|
+
* named source, held to that source (the issuer under the RFC 5280 sec. 7.1 rule, a distribution point
|
|
269
|
+
* against the CRL's own `issuingDistributionPoint` where it states one) and to the supplied `thisUpdate`.
|
|
270
270
|
* A request naming both sends the dpn (sec. 4.3.4: the dpn choice when a distribution point name is
|
|
271
271
|
* available) and holds the answer to the issuer as well. The issuer is REQUIRED, since a distribution
|
|
272
272
|
* point name alone leaves the answer unbound: a complete CRL states no scope to compare it against.
|
|
273
273
|
* Terminal `outcome`: `answered` (with `operation`, `present`, and the decoded `value`),
|
|
274
274
|
* `rejected`, or `poll-timeout`. An absent response value is `present: false` with a null `value`, which is
|
|
275
|
-
* how each of the four says "nothing available"
|
|
275
|
+
* how each of the four says "nothing available", never conflated with an empty result. Delayed delivery
|
|
276
276
|
* for both verbs arrives as an error message carrying the `waiting` status (sec. 4.4) and drives the same
|
|
277
277
|
* bounded poll loop, with the `pollReq` referring to the whole message.
|
|
278
278
|
*
|
package/lib/cms-verify.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @nav Verification
|
|
7
7
|
* @title CMS
|
|
8
8
|
* @fullname CMS (Cryptographic Message Syntax) signing and verification
|
|
9
|
-
* @intro Verify a CMS SignedData signature (RFC 5652 sec. 5)
|
|
9
|
+
* @intro Verify a CMS SignedData signature (RFC 5652 sec. 5), the structure S/MIME signed
|
|
10
10
|
* mail, RFC 3161 timestamp tokens, and code-signing all rest on. `verify(cms, opts)` parses
|
|
11
11
|
* the SignedData (over the strict `pki.schema.cms` codec), locates each SignerInfo's signer
|
|
12
12
|
* certificate by its issuerAndSerialNumber or subjectKeyIdentifier, and checks the signature
|
|
@@ -406,7 +406,7 @@ async function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
|
|
|
406
406
|
|
|
407
407
|
/**
|
|
408
408
|
* @primitive pki.cms.verify
|
|
409
|
-
* @signature pki.cms.verify(input, opts?) -> Promise<{ valid, trusted, eContentType, signers }>
|
|
409
|
+
* @signature pki.cms.verify(input, opts?) -> Promise<{ valid, trusted, eContentType, eContent, signers }>
|
|
410
410
|
* @since 0.2.14
|
|
411
411
|
* @status stable
|
|
412
412
|
* @spec RFC 5652
|
|
@@ -416,15 +416,16 @@ async function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
|
|
|
416
416
|
* @related pki.schema.cms.parse, pki.path.validate
|
|
417
417
|
*
|
|
418
418
|
* Verify a CMS SignedData signature (RFC 5652 sec. 5). `input` is a PEM string, a DER
|
|
419
|
-
* `Buffer`, or a parsed `pki.schema.cms` object. Returns `{ valid, trusted, eContentType, signers }`
|
|
420
|
-
*
|
|
421
|
-
*
|
|
419
|
+
* `Buffer`, or a parsed `pki.schema.cms` object. Returns `{ valid, trusted, eContentType, eContent, signers }`
|
|
420
|
+
* (`eContent` the encapsulated content when present, else null) where each `signers[i]` is
|
|
421
|
+
* `{ ok, sid, cert, trusted, signedAttributesPresent, signedAttributes, unsignedAttrs, digestAlgorithm, countersignatures }`
|
|
422
|
+
* (`cert` the matched signer certificate DER) or carries a `code` on a structural failure; `valid` is true when
|
|
422
423
|
* there is at least one signer and every signer verified.
|
|
423
424
|
*
|
|
424
425
|
* `eContentType` and `signedAttributesPresent` are there for a caller whose profile is stricter
|
|
425
|
-
* than RFC 5652's. Signing WITH attributes and signing the content directly are different claims
|
|
426
|
+
* than RFC 5652's. Signing WITH attributes and signing the content directly are different claims:
|
|
426
427
|
* attributes bind a content type and a signing time alongside the digest, content-only binds
|
|
427
|
-
* nothing but the bytes
|
|
428
|
+
* nothing but the bytes. One message may carry a signer of each. A profile that requires
|
|
428
429
|
* attributes (RFC 8551 S/MIME does) or a particular content type can enforce it from the verdict,
|
|
429
430
|
* with no need to parse the message a second time.
|
|
430
431
|
*
|
|
@@ -434,9 +435,8 @@ async function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
|
|
|
434
435
|
* certificate, sign with it, and embed it, so that says nothing about WHO signed. `trusted` says
|
|
435
436
|
* every signer chained to a root named in `opts.trustAnchors`, validated through the same RFC 5280
|
|
436
437
|
* path engine `pki.path.validate` uses. Without anchors there is nothing to chain to and `trusted`
|
|
437
|
-
* is `false
|
|
438
|
-
* configuration fault and throw.
|
|
439
|
-
* about the message for a check that never ran.
|
|
438
|
+
* is `false`, a definite answer, not a missing one. Anchors that cannot be read are a
|
|
439
|
+
* configuration fault and throw.
|
|
440
440
|
*
|
|
441
441
|
* Trust is decided from the certificate the SignerInfo selected, the one reported as
|
|
442
442
|
* `signers[i].cert`, never from another certificate that happens to share its key. A
|
|
@@ -706,6 +706,9 @@ function _addCert(out, der) {
|
|
|
706
706
|
* @opts eContentType The encapsulated content type (an OID name). Default `data`.
|
|
707
707
|
* @opts signedAttributes Include signed attributes (content-type, message-digest, signing-time);
|
|
708
708
|
* false signs the content directly. Default true.
|
|
709
|
+
* @opts additionalSignedAttributes Extra signed attributes to add to every SignerInfo, each
|
|
710
|
+
* `{ type, values }` (`type` an OID name or dotted string, `values` an
|
|
711
|
+
* array of DER value `Buffer`s). Covered by the signature.
|
|
709
712
|
* @opts signingTime A `Date` for the signing-time attribute, or false to omit it.
|
|
710
713
|
* @opts sid `"issuerAndSerial"` (default) or `"ski"` (subjectKeyIdentifier).
|
|
711
714
|
* @opts certificates Embed the signer certificates in the output. Default true.
|
|
@@ -739,7 +742,7 @@ var sign = cmsSign.sign;
|
|
|
739
742
|
* Add one or more countersignatures to an existing CMS SignedData (RFC 5652 sec. 11.4). A
|
|
740
743
|
* countersignature is itself a `SignerInfo` whose signature covers the CONTENTS of the countersigned
|
|
741
744
|
* SignerInfo's signature value (not the content), attached as the `id-countersignature` unsigned
|
|
742
|
-
* attribute
|
|
745
|
+
* attribute, the construction Authenticode timestamps and signature-attestation workflows rest on.
|
|
743
746
|
* `cms` is the SignedData (a DER `Buffer` or a PEM `CMS` string; the wire bytes are preserved, so
|
|
744
747
|
* the countersigned primary still verifies byte-for-byte). Each `signers[i]` is the same descriptor
|
|
745
748
|
* `pki.cms.sign` takes (`{ cert, key, digestAlgorithm?, pss? }`, any RSA / RSASSA-PSS / ECDSA /
|
|
@@ -784,7 +787,7 @@ var countersign = cmsSign.countersign;
|
|
|
784
787
|
* Encrypt content as a CMS EnvelopedData (CBC content), AuthEnvelopedData (AEAD content, the
|
|
785
788
|
* default), or EncryptedData. `recipients` is an ARRAY of recipient descriptors for the enveloped
|
|
786
789
|
* family, each wrapping the same fresh content-encryption key: `{ cert }` auto-dispatches off the
|
|
787
|
-
* recipient certificate's public-key algorithm
|
|
790
|
+
* recipient certificate's public-key algorithm: RSA yields a ktri with RSAES-OAEP-SHA256 (PKCS#1
|
|
788
791
|
* v1.5 is never emitted); EC P-256/384/521 a kari with ephemeral-static ECDH and the X9.63 KDF;
|
|
789
792
|
* X25519/X448 a kari per RFC 8418 (HKDF); ML-KEM-512/768/1024 an ori/KEMRecipientInfo per RFC
|
|
790
793
|
* 9629 + 9936; a composite ML-KEM (id-MLKEM768-* / id-MLKEM1024-*, draft-lamps-pq-composite-kem) the
|
|
@@ -827,7 +830,7 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
827
830
|
* @spec RFC 5652, RFC 2104, RFC 4231, RFC 3370
|
|
828
831
|
* @related pki.cms.decrypt, pki.schema.cms.parse
|
|
829
832
|
*
|
|
830
|
-
* Produce a CMS AuthenticatedData (RFC 5652 sec. 9): CLEARTEXT content plus a MAC (HMAC-SHA-2)
|
|
833
|
+
* Produce a CMS AuthenticatedData (RFC 5652 sec. 9): CLEARTEXT content plus a MAC (HMAC-SHA-2),
|
|
831
834
|
* authenticated but not encrypted, the authenticated-only sibling of AuthEnvelopedData. A single
|
|
832
835
|
* fresh MAC key is minted and wrapped for every recipient with the same RecipientInfo model
|
|
833
836
|
* `pki.cms.encrypt` uses for a content-encryption key, so `recipients` is the identical array of
|
|
@@ -835,8 +838,8 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
835
838
|
* `{ password }` (pwri), or `{ kek, kekId }` (kekri). By default the MAC covers the authenticated
|
|
836
839
|
* attributes (a content-type and a message-digest = digest of the content) re-tagged to the EXPLICIT
|
|
837
840
|
* SET OF (RFC 5652 sec. 9.2); with `authenticatedAttributes: false` (id-data content only) it covers
|
|
838
|
-
* the content octets directly. `pki.cms.decrypt` recovers the MAC key, recomputes the MAC, and
|
|
839
|
-
* with authenticated attributes
|
|
841
|
+
* the content octets directly. `pki.cms.decrypt` recovers the MAC key, recomputes the MAC, and
|
|
842
|
+
* (with authenticated attributes) independently confirms the message-digest before releasing the
|
|
840
843
|
* content, returning `authenticated: true`. Malformed input throws a typed `CmsError`.
|
|
841
844
|
*
|
|
842
845
|
* @opts macAlgorithm `"hmac-sha256"` (default) / `"hmac-sha384"` / `"hmac-sha512"`.
|
|
@@ -847,6 +850,8 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
847
850
|
* @opts authAttrs Extra authenticated attributes appended to the content-type + message-digest pair.
|
|
848
851
|
* @opts contentType The encapsulated content type (an OID name). Default `data`.
|
|
849
852
|
* @opts keyIdentifier `"issuerAndSerial"` (default) or `"subjectKeyIdentifier"`.
|
|
853
|
+
* @opts oaepHash The RSAES-OAEP hash for ktri recipients wrapping the MAC key: `"sha256"` (default) / `"sha384"` / `"sha512"`.
|
|
854
|
+
* @opts ukm User keying material (a Buffer) for kari / kemri recipients.
|
|
850
855
|
* @opts pem Return a PEM string (`-----BEGIN CMS-----`) instead of a DER Buffer.
|
|
851
856
|
* @example
|
|
852
857
|
* // authenticate a message to an RSA recipient, then verify the MAC via decrypt
|
|
@@ -890,7 +895,7 @@ var authenticate = cmsEncrypt.authenticate;
|
|
|
890
895
|
* recipient type: a `ktri` or ephemeral-static `kari` message is minted by anyone holding the recipient's
|
|
891
896
|
* PUBLIC key, and a `pwri` or `kekri` message by any co-recipient sharing the secret. Read
|
|
892
897
|
* `authenticated: true` as "these bytes were not altered after the key was chosen", and do not read it as
|
|
893
|
-
* a sender identity
|
|
898
|
+
* a sender identity. To bind one, verify a signature (`pki.cms.verify`) over the plaintext.
|
|
894
899
|
* `authenticatedBy` names what the integrity rests on (`"content-encryption-key"` for AEAD,
|
|
895
900
|
* `"message-authentication-key"` for AuthenticatedData, `null` for an unauthenticated cipher).
|
|
896
901
|
* `originatorInfo` is surfaced as the sender supplied it and is UNAUTHENTICATED: it sits outside the
|
|
@@ -958,7 +963,7 @@ var compress = cmsCompress.compress;
|
|
|
958
963
|
* decompression-bomb / resource-exhaustion defense (CWE-409): a cap breach throws
|
|
959
964
|
* `cms/decompress-too-large`, and every malformed / truncated / corrupt stream collapses to the uniform
|
|
960
965
|
* `cms/decompress-failed`. `opts.maxOutputBytes` tightens the cap DOWNWARD only. The verdict carries NO
|
|
961
|
-
* `authenticated` / `valid` field
|
|
966
|
+
* `authenticated` / `valid` field. CompressedData is not a security assertion (RFC 8551 sec. 2.4.5).
|
|
962
967
|
*
|
|
963
968
|
* @opts maxOutputBytes Lower the decompressed-output cap (a DoS bound; downward only).
|
|
964
969
|
* @example
|
package/lib/composite-kem.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* recovers the same secret from that ciphertext and the composite private key. Each component KEM
|
|
14
14
|
* runs independently, and the two secrets are mixed through the SHA3-256 combiner that also binds the
|
|
15
15
|
* traditional ciphertext, the traditional public key, and a per-algorithm label. All twelve registered
|
|
16
|
-
* algorithms
|
|
17
|
-
* P-256/P-384/P-521 and brainpool, X25519, and X448
|
|
16
|
+
* algorithms (ML-KEM-768 and ML-KEM-1024 paired with RSA-OAEP 2048/3072/4096, ECDH over
|
|
17
|
+
* P-256/P-384/P-521 and brainpool, X25519, and X448) are verified against the draft Appendix G
|
|
18
18
|
* known-answer vectors.
|
|
19
19
|
* @spec draft-ietf-lamps-pq-composite-kem
|
|
20
20
|
* @card Establish a shared secret with a hybrid post-quantum plus traditional KEM.
|
package/lib/crl-sign.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @fullname CRLs (certificate revocation lists): read, sign and shard
|
|
10
10
|
* @intro The X.509 CRL producing side. `pki.crl.sign` builds a `TBSCertList`, signs it, and emits a
|
|
11
11
|
* `CertificateList` (RFC 5280 sec. 5) that `pki.schema.crl.parse`, `pki.path.crlChecker`, and OpenSSL
|
|
12
|
-
* all accept
|
|
12
|
+
* all accept, over any signature algorithm the toolkit registry resolves: RSA (PKCS#1 v1.5 / PSS),
|
|
13
13
|
* ECDSA, EdDSA, ML-DSA, SLH-DSA, and the composite (hybrid) arms. `pki.crl.verify` checks a CRL
|
|
14
14
|
* signature through the one path-validation signature engine, and `pki.crl.isRevoked` looks a serial up
|
|
15
15
|
* in a parsed CRL. Parsing lives at `pki.schema.crl.parse`.
|
|
@@ -413,7 +413,7 @@ function _sign(spec, issuer, opts) {
|
|
|
413
413
|
* @defends crl-forgery (CWE-347)
|
|
414
414
|
* @related pki.schema.crl.parse, pki.crl.verify, pki.path.crlChecker
|
|
415
415
|
*
|
|
416
|
-
* Build, sign, and DER-encode an X.509 certificate revocation list. `spec` describes the CRL
|
|
416
|
+
* Build, sign, and DER-encode an X.509 certificate revocation list. `spec` describes the CRL:
|
|
417
417
|
* `thisUpdate` / `nextUpdate` (`Date`s), an optional `crlNumber`, a `revoked` array (each entry a
|
|
418
418
|
* `serialNumber` + `revocationDate` with an optional `reason` or `invalidityDate`),
|
|
419
419
|
* and an optional `extensions` object (`authorityKeyIdentifier`, `issuingDistributionPoint`,
|
|
@@ -491,8 +491,8 @@ function _issuerMaySign(parsed, cert) {
|
|
|
491
491
|
* Verify a CRL's signature over its exact parsed `tbsCertList` bytes under the issuer public key. `crl`
|
|
492
492
|
* is a DER `Buffer`, a PEM string, or a parsed CRL; `issuer` is `{ cert }` (DER/PEM/parsed), `{ publicKey }`
|
|
493
493
|
* (SPKI DER), or a raw SPKI `Buffer`. Verification composes the one path-validation signature engine
|
|
494
|
-
* `pki.path.crlChecker` uses
|
|
495
|
-
* EdDSA low-order-point gates
|
|
494
|
+
* `pki.path.crlChecker` uses, the same algorithm-confusion (RFC 9814 sec. 4 key-OID == sig-OID) and
|
|
495
|
+
* EdDSA low-order-point gates, so there is no second, weaker CRL verifier. The verdict's `valid` is
|
|
496
496
|
* `false` on any verification fault, and `signatureValid` reports the signature check on its own; malformed
|
|
497
497
|
* input throws a typed `CrlError`.
|
|
498
498
|
*
|
|
@@ -571,7 +571,7 @@ function _serialHexOf(serial) {
|
|
|
571
571
|
* refused rather than answered from (`crl/not-current`): one whose `thisUpdate` is later, one whose
|
|
572
572
|
* `nextUpdate` has passed, and one carrying no `nextUpdate` at all, which states no window and so cannot
|
|
573
573
|
* be told from a replayed copy. Without `opts.time` currency goes unasked and the verb is the structural
|
|
574
|
-
* lookup it has always been
|
|
574
|
+
* lookup it has always been; `null` then means "not listed on this CRL", which is weaker than "not
|
|
575
575
|
* revoked". `pki.path.crlChecker` decides currency against the material it fetched and is the verb to
|
|
576
576
|
* reach for when the answer has to mean the stronger thing.
|
|
577
577
|
*
|
package/lib/crmf-sign.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @title Certificate request messages
|
|
9
9
|
* @fullname CRMF (Certificate Request Message Format, RFC 4211)
|
|
10
10
|
* @intro The RFC 4211 certificate-request-message producing side. `pki.crmf.build` assembles a
|
|
11
|
-
* `CertReqMessages
|
|
11
|
+
* `CertReqMessages`: one or more `CertReqMsg`, each a `CertRequest` (a `CertTemplate` of the requested
|
|
12
12
|
* certificate fields plus optional controls) paired with a proof of possession. The common proof is a
|
|
13
13
|
* `POPOSigningKey` signature over the `CertRequest`, made with the private half of the key being
|
|
14
14
|
* certified (the requester proves possession, exactly as a PKCS#10 CSR does). The message drops into a
|
|
@@ -355,7 +355,7 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
355
355
|
* subjectKeyIdentifier, or pre-encoded Extension DER), and an optional `version` (2)), optional `controls`
|
|
356
356
|
* and `regInfo` (an object of regToken / authenticator / utf8Pairs / oldCertID / protocolEncrKey, or
|
|
357
357
|
* pre-encoded AttributeTypeAndValue DER), and an optional `pop` selector. `key` (or `{ key }`) is the
|
|
358
|
-
* REQUESTER's private key
|
|
358
|
+
* REQUESTER's private key, the private half of `certTemplate.publicKey`; the message carries a
|
|
359
359
|
* `POPOSigningKey` proof of possession signed with it (verified before the message is returned), exactly
|
|
360
360
|
* as a PKCS#10 CSR proves possession. The signature algorithm is resolved from the requested public key
|
|
361
361
|
* (RSA PKCS#1 v1.5 / PSS, ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). `key` is optional: omit it
|
|
@@ -373,7 +373,7 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
373
373
|
* content type is `id-ct-encKeyWithID`. RFC 9810 sec. 5.2.8.3.1 permits it only where archival is
|
|
374
374
|
* intended, so it takes `pop.archive: true`; `pop.privateKey` is the PKCS#8 DER, `pop.recipients` the CMS
|
|
375
375
|
* recipients to envelope it to, and `pop.identifier` (a string or GeneralName) is required, because
|
|
376
|
-
* sec. 4.2.1 makes it mandatory whenever the purpose is proving possession
|
|
376
|
+
* sec. 4.2.1 makes it mandatory whenever the purpose is proving possession: without it a decrypting
|
|
377
377
|
* agent cannot tell whose key it holds. `pop.contentEncryptionAlgorithm` defaults to `aes-256-cbc`; an
|
|
378
378
|
* AEAD algorithm is refused, since it would produce an `AuthEnvelopedData` this field cannot carry.
|
|
379
379
|
*
|
package/lib/csr-sign.js
CHANGED
|
@@ -179,8 +179,8 @@ function _coerceCsr(request) {
|
|
|
179
179
|
* requested extension, so none of them were altered after signing. It says nothing about who the
|
|
180
180
|
* producer is: the key is self-asserted, the name is self-asserted, and a requester free to choose
|
|
181
181
|
* both can prove possession of a key they generated a moment ago under any name they like. Binding
|
|
182
|
-
* that name to an identity is the enrollment protocol's job
|
|
183
|
-
* an out-of-band check
|
|
182
|
+
* that name to an identity is the enrollment protocol's job (`pki.est`, `pki.cmc`, `pki.cmp`, or
|
|
183
|
+
* an out-of-band check), and remains one after this returns `true`.
|
|
184
184
|
*
|
|
185
185
|
* Verification composes the one path-validation signature engine, with the same algorithm-confusion
|
|
186
186
|
* (RFC 9814 sec. 4 key-OID == sig-OID) and EdDSA low-order-point gates, rather than the self-check
|
package/lib/ct.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
*
|
|
35
35
|
* @card
|
|
36
36
|
* Parse RFC 6962 Certificate Transparency SCT lists from a certificate or OCSP
|
|
37
|
-
* extension
|
|
37
|
+
* extension: per-SCT logId / timestamp (BigInt) / algorithm / raw signature,
|
|
38
38
|
* the signed-preimage reconstruction surfaced for external verification,
|
|
39
39
|
* bounded TLS-struct decode, fail-closed.
|
|
40
40
|
*/
|
|
@@ -230,13 +230,13 @@ function _u24Bytes(n) {
|
|
|
230
230
|
* `entry` selects the log-entry arm:
|
|
231
231
|
* - `{ entryType: 0, leafCert: <DER Buffer> }`, an SCT delivered over TLS /
|
|
232
232
|
* OCSP, signed over `x509_entry(0)` with the leaf certificate.
|
|
233
|
-
* - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }
|
|
233
|
+
* - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }`,
|
|
234
234
|
* an SCT EMBEDDED in a certificate, signed over `precert_entry(1)` with the
|
|
235
235
|
* issuer key hash + the precertificate TBS (the TBS with only the SCT
|
|
236
236
|
* extension removed). `issuerKeyHash` is SHA-256 of the issuer's SPKI DER.
|
|
237
237
|
*
|
|
238
238
|
* The preimage reuses the parsed SCT's raw `extensions` byte-for-byte and
|
|
239
|
-
* re-emits the fixed-width scalars canonically. This never verifies anything
|
|
239
|
+
* re-emits the fixed-width scalars canonically. This never verifies anything;
|
|
240
240
|
* a verifier hashes the returned bytes and checks the signature with the log's
|
|
241
241
|
* public key (compose `webcrypto`). Throws `CtError` (`ct/bad-entry-type`,
|
|
242
242
|
* `ct/bad-issuer-key-hash`, `ct/bad-tbs-length`) on a malformed entry, and
|
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);
|
|
@@ -1006,7 +1006,7 @@ function _certsResult(op, res, opts, csrSpki) {
|
|
|
1006
1006
|
* surfaced as `issuedCertificates` (with `certificate` the first), distinct from `certificates`,
|
|
1007
1007
|
* which is the whole returned bag including any chain. Where the requested keys are distinct that
|
|
1008
1008
|
* list is in request order; where several requests deliberately SHARE one key it is not, and does
|
|
1009
|
-
* not claim to be
|
|
1009
|
+
* not claim to be: the public key is the only identification sec. 4.1 sanctions, so when it is
|
|
1010
1010
|
* 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
1011
|
* a degenerate SignedData with no signers by definition, so its security rests on the authenticated
|
|
1012
1012
|
* TLS channel, not on a signature.
|
|
@@ -1434,7 +1434,7 @@ function cacerts(baseUrl, opts) {
|
|
|
1434
1434
|
* @opts
|
|
1435
1435
|
* - `transport` / `tls` / `label` / `timeout` / `maxResponseBytes` / `maxRedirects` / `proxy` / `now`, as in pki.est.cacerts.
|
|
1436
1436
|
* - `strict`: reject an enroll response that carries more than the single issued certificate.
|
|
1437
|
-
* - `username` / `password` -- HTTP Basic
|
|
1437
|
+
* - `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
1438
|
* - `allowCrossOriginRedirect` -- opt in to following a cross-origin redirect on this POST.
|
|
1439
1439
|
* @example
|
|
1440
1440
|
* 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
|