@blamejs/pki 0.8.56 → 0.8.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/lib/guard-parsed.js +23 -7
- package/lib/inspect.js +1 -1
- package/lib/lint.js +51 -3
- package/lib/ocsp.js +7 -0
- package/lib/oid.js +7 -0
- package/lib/schema-cms.js +113 -8
- package/lib/schema-ocsp.js +41 -6
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ 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.8.57 — 2026-10-07
|
|
8
|
+
|
|
9
|
+
Read the OCSP response a CMS message carries in its crls field, instead of accepting it unread.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- A `crls` entry in the `other [1]` arm is read as RFC 5652 sec. 10.2.1 `OtherRevocationInfoFormat` and carries `otherRevInfoFormat` (dotted), `otherRevInfoFormatName` (or `null` for a format no registry names) and `otherRevInfoBytes`, the raw ANY TLV exactly as it arrived. A CertificateList entry is unchanged. An operator can now tell an RFC 5940 OCSP entry from an SCVP one, and either from an unregistered format, which nothing in this build could do before.
|
|
14
|
+
- For `id-ri-ocsp-response`, the embedded OCSP response is surfaced in `ocspResponse` as a parsed record, with the byte ranges a verifier hashes left raw. `pki.path.ocspChecker`, `pki.path.verifyOcspResponse`, `pki.ocsp.verify` and `pki.lint.ocsp` all accept it as it stands: harvest the entries with `crls.map(e => e.ocspResponse).filter(Boolean)` and hand them over. The same verbs take the record and the caller re-encodes nothing; a rebuilt copy of it is refused exactly as a rebuilt parse result is.
|
|
15
|
+
- A carried response whose `responseType` names a body this build does not decode keeps its envelope and leaves the body alone: `ocspResponse.basicResponse` is `null` and `ocspResponse.responseBytes.response` holds the bytes as they arrived. RFC 6960 sec. 4.2.1 dispatches the response body on that OID and RFC 5940 sec. 3 asks for a successful status rather than a body a reader can decode, so the message parses and a caller who only needs its signature verified is not stopped by a response type nothing in the path consults. The verbs above are handed a record with no response body and determine nothing from it, so a certificate's status stays undetermined rather than reading as good. `pki.schema.ocsp.parseResponse` still refuses the same bytes with `ocsp/unsupported-response-type`: a client that asked a responder for an answer needs to be told it cannot be read.
|
|
16
|
+
- The same reading applies to `originatorInfo.crls`, so EnvelopedData (RFC 5652 sec. 6.1), AuthenticatedData (sec. 9.1) and AuthEnvelopedData (RFC 5083 sec. 2.1) carry it too, not SignedData alone. RFC 5940 sec. 1 names all four.
|
|
17
|
+
- `pki.oid` resolves the two id-ri formats a reader can act on: `id-ri-ocsp-response` (1.3.6.1.5.5.7.16.2) and `id-ri-scvp` (1.3.6.1.5.5.7.16.4).
|
|
18
|
+
- `pki.lint.cms` reports a notice when a `crls` entry carries revocation information this build does not read, naming the format, the response type where there is one, and where the entry sat. An `id-ri-ocsp-response` entry whose response was read draws nothing; one whose `responseType` named a body this build does not decode draws the notice, because the revocation information it carries went unconsulted either way.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `pki.schema.cms.parse` reads these entries by default. An entry that was accepted unread before may now be refused if it is malformed under RFC 5652 sec. 10.2.1 or RFC 5940 sec. 3; an unrecognized format is still accepted and surfaced opaquely, because sec. 10.2.1's ANY DEFINED BY is an extension point and refusing an unknown format would refuse a future registered one. The reading is not optional, because an opt-in would leave the entry unread for every caller who does not know to ask for it.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- An `other [1]` crls entry is held to its stated shape. `OtherRevocationInfoFormat` is exactly an OBJECT IDENTIFIER and an ANY, so an empty arm, a one-element arm carrying a format and no revocation information, and an arm with a third element are each refused as `cms/bad-other-rev-info`. The one-element form is what OpenSSL both accepts and can emit, because its template marks the value optional.
|
|
27
|
+
- An OCSP response carried in that arm must state a status. A `tryLater` or `malformed` response is a well-formed OCSPResponse that conveys no revocation information, so it is refused here as `cms/bad-ocsp-rev-info` (RFC 5940 sec. 3). `pki.schema.ocsp.parseResponse` still accepts one, because a client reading a live responder's answer needs to see it.
|
|
28
|
+
- A BasicOCSPResponse where an OCSPResponse belongs is refused. Both are SEQUENCEs, so the confusion RFC 5126 sec. 6.3.4 invites by carrying `SEQUENCE OF BasicOCSPResponse` in a signed attribute passes any tag check and fails only on the first field, where a status is read and a ResponseData arrives.
|
|
29
|
+
- An OCSP response placed in the CertificateList position is refused as `cms/misplaced-ocsp-response`. RFC 5940 sec. 2 states that such a response is carried in the `other` arm, and a universal SEQUENCE in `crls` was previously surfaced raw whatever it held.
|
|
30
|
+
- A caller's decode caps reach the embedded response. The walk runs under the parse's own budget rather than starting a fresh one, so a tightened cap refuses the message with the caller's typed `cms/too-large` instead of letting the nested work run at defaults.
|
|
31
|
+
- `pki.cms.parseCertsOnly`'s `maxCerts` bound no longer decides which messages it accepts. The bound caps the certificates and CRLs parsed and returned, but it was applied by shortening the lists before they were checked, so a cap leaving no room for the CRLs dropped a tagged entry silently: a capped reader accepted a message an uncapped reader refuses and handed back an empty CRL list where revocation information had been. The structural check now runs over every entry, and only the certificate and CRL parsing stays bounded, which is the work the bound exists for.
|
|
32
|
+
- `pki.ocsp.verify` takes the record a `crls` entry surfaces, which it did not. Every verb that accepts a parse result re-reads it from the bytes it was parsed from, so that an edited copy cannot be verified in place of what was parsed; a record produced by walking a structure a message carries was not attributed to those bytes, so the re-reading produced an object that the next verb in the chain read as rebuilt and refused with `path/bad-input`. A record is now attributed the same way whether it was parsed from its own bytes or walked inside a message that carried it, and the refusal of a genuinely rebuilt record is unchanged.
|
|
33
|
+
- The three readers that require a plain CertificateList still refuse a tagged entry now that a well-formed one parses: `pki.cms.parseCertsOnly` (`cms/bad-crl`), `pki.est.parseCertsOnly` (`est/bad-crl`) and a SCEP SUCCESS CertRep (`scep/bad-crl`). The SCEP refusal had no test before this change.
|
|
34
|
+
|
|
7
35
|
## v0.8.56 — 2026-10-06
|
|
8
36
|
|
|
9
37
|
Decide a name, an address, a revocation and a trust bit through operations and values no caller can replace.
|
package/README.md
CHANGED
|
@@ -249,7 +249,7 @@ comment blocks, is at [pkijs.com](https://pkijs.com).
|
|
|
249
249
|
| `pki.schema.csr` | PKCS#10 certification requests (RFC 2986): subject DN, public key, requested attributes, signature. `decodeExtensions` is what a CA reads to see what a request asked for, returning the rows of its one `extensionRequest` attribute; a request carrying more than one is refused, and `groups: true` reads each separately Fail-closed — `parse`, `pemDecode`, `pemEncode`, `decodeExtensions`, `decodeExtension` |
|
|
250
250
|
| `pki.schema.trustanchor` | RFC 5914 trust anchor lists: a trust anchor as a structure rather than as a certificate, carrying the constraints a root program otherwise states out of band. `parse` reads a `TrustAnchorList` into `{ anchors }`, each naming which of the three `TrustAnchorChoice` arms it came from: a `certificate`, a bare `tbsCert` (an anchor has no signature to check), or a `taInfo`. A `taInfo` carries the public key, the key identifier, an optional title, and a `certPath` stating the name the anchor is known by plus the policies it may assert, the namespace it is trusted for, and how far below it a path may run. Every rule RFC 5914 states is enforced: an empty `taName`, a policy set carrying qualifiers, a negative path length, and a `requireExplicitPolicy` with no policy set are each refused, and where the anchor carries a certificate its subject, public key and `subjectKeyIdentifier` must match the anchor's own. The four extension types the RFC excludes from `exts` are reported and left out rather than refused, as it says to ignore them. `parseInfo` reads a bare `TrustAnchorInfo`. `pki.trust.parseTrustAnchorList` turns a list into anchors `pki.path.validate` consumes, and `pki.trustanchor.build` writes one Fail-closed — `parse`, `parseInfo`, `pemDecode`, `pemEncode` |
|
|
251
251
|
| `pki.schema.pkcs8` | PKCS#8 private keys (RFC 5208 / 5958): algorithm, raw key bytes, attributes, optional public key. Encrypted keys are recognized but not decrypted. Fail-closed — `parse`, `parseEncrypted`, `pemDecode`, `pemEncode` |
|
|
252
|
-
| `pki.schema.cms` | CMS (RFC 5652 / 5083 / 9629): SignedData (§5, signer infos plus raw signed-attribute bytes for external verification), EnvelopedData (§6, all five RecipientInfo kinds including RFC 5753 key agreement and RFC 9629 KEM recipients with ML-KEM validation), EncryptedData (§8), AuthenticatedData (§9, MAC surface plus raw `authAttrsBytes`), and AuthEnvelopedData (RFC 5083, with RFC 5084 AES-GCM/CCM parameter validation). §11 attribute placement is enforced, countersignatures validate recursively, certificates and CRLs are validated against the closed CHOICE sets and kept raw, and every result is tagged `contentTypeName` Fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
252
|
+
| `pki.schema.cms` | CMS (RFC 5652 / 5083 / 9629): SignedData (§5, signer infos plus raw signed-attribute bytes for external verification), EnvelopedData (§6, all five RecipientInfo kinds including RFC 5753 key agreement and RFC 9629 KEM recipients with ML-KEM validation), EncryptedData (§8), AuthenticatedData (§9, MAC surface plus raw `authAttrsBytes`), and AuthEnvelopedData (RFC 5083, with RFC 5084 AES-GCM/CCM parameter validation). §11 attribute placement is enforced, countersignatures validate recursively, certificates and CRLs are validated against the closed CHOICE sets and kept raw, a `crls` entry in the `other [1]` arm is read as RFC 5652 §10.2.1 `OtherRevocationInfoFormat` with an RFC 5940 OCSP response surfaced as a record the revocation verbs take directly, and every result is tagged `contentTypeName` Fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
253
253
|
| `pki.schema.ocsp` | OCSP requests and responses (RFC 6960): per-certificate status (good, revoked, unknown), responder identity, raw tbs bytes for external verification, certificates kept raw. A response's nonce, and a single response's archive cutoff (§4.4.4) and CRL reference (§4.4.2), are decoded onto their extension entries by name; a malformed one is refused. Non-basic response types are recognized but not decoded. Fail-closed — `parseRequest`, `parseResponse`, `pemDecode`, `pemEncode` |
|
|
254
254
|
| `pki.schema.tsp` | RFC 3161 timestamp requests, responses, and tokens: the TimeStampReq a client sends (imprint, requested policy, nonce, certReq), the TSTInfo payload (imprint, genTime with sub-second precision, serial, nonce, accuracy), the status-to-token coupling, and the token wrapper composed over CMS with the single-signer rule. Fail-closed — `parse`, `parseRequest`, `parseResponse`, `parseTstInfo`, `parseToken`, `pemDecode`, `pemEncode` |
|
|
255
255
|
| `pki.schema.attrcert` | Attribute certificates (RFC 5755): holder and issuer identities as validated GeneralNames, the validity window as real `Date`s, and the privilege attributes and extensions decoded to structured values (role, clearance, service and access identity, group, charging identity; audit identity, target and proxy information, no-rev-avail, AA controls), with the raw signed region for a verifier. Unknown types stay opaque, and the obsolete v1 form is recognized and deferred. `decodeExtensions` returns the same row shape the other formats return, reporting the criticality RFC 5755 fixes for each extension it profiles Fail-closed — `parse`, `pemDecode`, `pemEncode`, `decodeExtensions`, `decodeExtension` |
|
package/lib/guard-parsed.js
CHANGED
|
@@ -157,18 +157,28 @@ function recordingParser(kind, parse, ErrorClass, code, label) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// @enforced-by guard-shape-reinlined -- shares recordingParser's shape below: the PROVENANCE.set
|
|
160
|
+
/** @internal `derive` records what it produces, so a re-derivation is the same kind of record the
|
|
161
|
+
* first walk was. Verbs stack: a door answers a record by re-deriving it from the bytes it
|
|
162
|
+
* recorded, and hands that to the next door, which is then the one deciding whether it is a parse
|
|
163
|
+
* result. Deriving through the bare walk left the second door with an object carrying the kind's
|
|
164
|
+
* fields and no provenance, which is the shape it refuses as rebuilt. */
|
|
160
165
|
function recordingWalker(kind, walkNode, decodeBytes) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (out && typeof out === "object" && node && _isBytes(node.bytes)) {
|
|
166
|
+
function remember(out, source) {
|
|
167
|
+
if (out && typeof out === "object" && _isBytes(source)) {
|
|
164
168
|
_weakSet(PROVENANCE, out, {
|
|
165
169
|
kind: kind,
|
|
166
|
-
source: _bufferFrom(
|
|
170
|
+
source: _bufferFrom(source),
|
|
167
171
|
shape: _shapeOf(out),
|
|
168
|
-
derive:
|
|
172
|
+
derive: derive,
|
|
169
173
|
});
|
|
170
174
|
}
|
|
171
175
|
return out;
|
|
176
|
+
}
|
|
177
|
+
function derive(src) { return remember(walkNode(decodeBytes(src)), src); }
|
|
178
|
+
return function (node, ctx) {
|
|
179
|
+
var out = walkNode(node, ctx);
|
|
180
|
+
if (!out || typeof out !== "object" || !node) return out;
|
|
181
|
+
return remember(out, node.bytes);
|
|
172
182
|
};
|
|
173
183
|
}
|
|
174
184
|
|
|
@@ -208,9 +218,15 @@ function isRecordedAsProduced(obj) {
|
|
|
208
218
|
*/
|
|
209
219
|
function sourceOf(obj) {
|
|
210
220
|
if (!isRecordedAsProduced(obj)) return null;
|
|
211
|
-
return
|
|
221
|
+
return _sourceCopy(_weakGet(PROVENANCE, obj).source);
|
|
212
222
|
}
|
|
213
223
|
|
|
224
|
+
/** @internal The recorded bytes are the one account of what was parsed, so what leaves this module
|
|
225
|
+
* is a copy: a parse that keeps its argument would otherwise be holding them, and a write through
|
|
226
|
+
* that reference changes what the NEXT re-derivation of the same record reads. A PEM source is a
|
|
227
|
+
* string and cannot be written through. */
|
|
228
|
+
function _sourceCopy(source) { return _isBytes(source) ? _bufferFrom(source) : source; }
|
|
229
|
+
|
|
214
230
|
function _shapeOf(obj) {
|
|
215
231
|
var shape = _create(null);
|
|
216
232
|
var keys = _allNames(obj);
|
|
@@ -238,7 +254,7 @@ function _allNames(obj) {
|
|
|
238
254
|
function fromTrustedSource(input, kind, claimFields, parse, E, code, why) {
|
|
239
255
|
if (input && typeof input === "object" && !_isBytes(input)) {
|
|
240
256
|
var rec = _recordOf(input, kind);
|
|
241
|
-
if (rec !== undefined) return (rec.derive || parse)(rec.source);
|
|
257
|
+
if (rec !== undefined) return (rec.derive || parse)(_sourceCopy(rec.source));
|
|
242
258
|
for (var i = 0; i < claimFields.length; i++) {
|
|
243
259
|
var claims;
|
|
244
260
|
try { claims = input[claimFields[i]] !== undefined; }
|
package/lib/inspect.js
CHANGED
|
@@ -1063,7 +1063,7 @@ function _looksParsedOcspResp(o) {
|
|
|
1063
1063
|
var st = o.responseStatus;
|
|
1064
1064
|
if (!st || typeof st !== "object" || typeof st.code !== "number" || typeof st.name !== "string") return false;
|
|
1065
1065
|
var br = o.basicResponse;
|
|
1066
|
-
/** @internal A response with no basic response is
|
|
1066
|
+
/** @internal A response with no basic response is an envelope-only answer the report handles, but one that
|
|
1067
1067
|
* HAS a basic response has to carry the answers the report walks: a responderID and a responses list whose
|
|
1068
1068
|
* entries name a CertID and a status. */
|
|
1069
1069
|
if (br === null || br === undefined) return true;
|
package/lib/lint.js
CHANGED
|
@@ -5087,6 +5087,51 @@ var RFC5652_CMS_RULES = [
|
|
|
5087
5087
|
return (encap && encap.eContent) ? null : true;
|
|
5088
5088
|
},
|
|
5089
5089
|
},
|
|
5090
|
+
{
|
|
5091
|
+
id: "lint/rfc5652/other-revocation-format-not-read", severity: "notice", source: "rfc5652",
|
|
5092
|
+
citation: "RFC 5940 4",
|
|
5093
|
+
/** @internal The carriage is sec. 10.2.1's `other` arm, which is why the row sits in this
|
|
5094
|
+
* profile; the document that registers the formats is the citation. RFC 5940 sec. 2 registers
|
|
5095
|
+
* `id-ri-ocsp-response` for an OCSP response carried in the `crls` field and sec. 4 registers
|
|
5096
|
+
* `id-ri-scvp` for an SCVP request-response pair. The parse reads the first and leaves the second
|
|
5097
|
+
* as raw bytes, because acting on it needs an SCVP schema this build does not carry. An
|
|
5098
|
+
* unregistered format is opaque for the same reason. Neither is a parse fault: sec. 10.2.1's ANY
|
|
5099
|
+
* DEFINED BY is an extension point, so refusing an unknown format would refuse a future
|
|
5100
|
+
* registered one. The finding tells an operator that revocation information present in the
|
|
5101
|
+
* message was not read, which a reader counting on it needs to know. A notice: the message is
|
|
5102
|
+
* conformant and the entry may be irrelevant to the certificates it travels with.
|
|
5103
|
+
* An id-ri-ocsp-response entry whose OCSPResponse states a responseType this build does not
|
|
5104
|
+
* decode (RFC 6960 sec. 4.2.1) is reported for the same reason: the format was recognized, the
|
|
5105
|
+
* revocation information still was not read, and `responseType` in the context names why. */
|
|
5106
|
+
message: "a crls entry carries revocation information this build does not read, so it was not consulted",
|
|
5107
|
+
/** @internal Every content type that can carry a RevocationInfoChoice: SignedData's own `crls`
|
|
5108
|
+
* and the `originatorInfo.crls` that EnvelopedData, AuthenticatedData and AuthEnvelopedData
|
|
5109
|
+
* share. The buckets are read by presence rather than by content type, so one predicate admits
|
|
5110
|
+
* each carrier and a type that carries neither draws nothing. */
|
|
5111
|
+
appliesTo: function (cms) {
|
|
5112
|
+
return Array.isArray(cms.crls) || !!(cms.originatorInfo && Array.isArray(cms.originatorInfo.crls));
|
|
5113
|
+
},
|
|
5114
|
+
check: function (cms) {
|
|
5115
|
+
var out = [];
|
|
5116
|
+
var buckets = [{ where: "crls", list: cms.crls },
|
|
5117
|
+
{ where: "originatorInfo.crls", list: cms.originatorInfo && cms.originatorInfo.crls }];
|
|
5118
|
+
buckets.forEach(function (bucket) {
|
|
5119
|
+
if (!Array.isArray(bucket.list)) return;
|
|
5120
|
+
bucket.list.forEach(function (entry, index) {
|
|
5121
|
+
if (!entry || entry.tagClass !== "context") return;
|
|
5122
|
+
var resp = entry.ocspResponse;
|
|
5123
|
+
if (resp && resp.basicResponse) return;
|
|
5124
|
+
var rb = resp && resp.responseBytes;
|
|
5125
|
+
out.push({ context: { placement: bucket.where, index: index,
|
|
5126
|
+
otherRevInfoFormat: entry.otherRevInfoFormat,
|
|
5127
|
+
otherRevInfoFormatName: entry.otherRevInfoFormatName,
|
|
5128
|
+
responseType: (rb && rb.responseType) || null,
|
|
5129
|
+
responseTypeName: (rb && rb.responseTypeName) || null } });
|
|
5130
|
+
});
|
|
5131
|
+
});
|
|
5132
|
+
return out.length ? out : null;
|
|
5133
|
+
},
|
|
5134
|
+
},
|
|
5090
5135
|
];
|
|
5091
5136
|
|
|
5092
5137
|
/** @internal RFC 8933 updates RFC 5652 rather than restating it, so its rows are their own profile:
|
|
@@ -7141,7 +7186,9 @@ function _ingestOcsp(input) {
|
|
|
7141
7186
|
* already-parsed `pki.schema.ocsp.parseResponse` object. It returns the same `LintReport` shape
|
|
7142
7187
|
* `pki.lint.certificate` returns, and it never throws on the data path: bytes that are not a
|
|
7143
7188
|
* well-formed response produce a single `fatal` finding `lint/unparseable` carrying the strict
|
|
7144
|
-
* parser's own code. A
|
|
7189
|
+
* parser's own code. A response with no BasicOCSPResponse to grade draws no row: a non-successful
|
|
7190
|
+
* status carries none, and neither does a response carried in a CMS `crls` entry whose
|
|
7191
|
+
* `responseType` this build does not decode.
|
|
7145
7192
|
*
|
|
7146
7193
|
* The `rfc6960` rows cover each SingleResponse's update times and their relation to producedAt,
|
|
7147
7194
|
* an empty `certs` field, the extended revoked definition (criticality, value and placement) and
|
|
@@ -7203,8 +7250,9 @@ function ocsp(input, opts) {
|
|
|
7203
7250
|
report = { findings: [ingested.fatal], ran: [],
|
|
7204
7251
|
counts: Object.assign(Object.create(null), { fatal: 1, error: 0, warn: 0, notice: 0, pass: 0, na: 0, ne: 0 }) };
|
|
7205
7252
|
} else if (!ingested.response.basicResponse) {
|
|
7206
|
-
/** @internal
|
|
7207
|
-
*
|
|
7253
|
+
/** @internal Every row is not applicable to a response with no BasicOCSPResponse: the profile
|
|
7254
|
+
* grades a response body, and neither an error status nor a responseType this build does not
|
|
7255
|
+
* decode produces one. */
|
|
7208
7256
|
report = { findings: [], ran: [],
|
|
7209
7257
|
counts: Object.assign(Object.create(null), { fatal: 0, error: 0, warn: 0, notice: 0, pass: 0, na: rules_.length, ne: 0 }) };
|
|
7210
7258
|
} else {
|
package/lib/ocsp.js
CHANGED
|
@@ -659,6 +659,13 @@ function buildErrorResponse(status) {
|
|
|
659
659
|
* replayed would hand a soft-failing caller the very certificate the responder refused. A replayed
|
|
660
660
|
* `revoked` is therefore reported as `revoked` with `nonceMatched: false`.
|
|
661
661
|
*
|
|
662
|
+
* `response` is DER `Buffer` / `Uint8Array`, a PEM `OCSP RESPONSE` string, or an unmodified
|
|
663
|
+
* `pki.schema.ocsp.parseResponse` result, which includes the record a CMS `crls` entry surfaces in
|
|
664
|
+
* `ocspResponse`. From bytes, a `responseType` this build does not decode is the parse fault
|
|
665
|
+
* `ocsp/unsupported-response-type`. A record that carries such a response, which RFC 5940 sec. 3
|
|
666
|
+
* permits a message to carry, is taken and determines nothing: the verdict is `"unknown"` with no
|
|
667
|
+
* `signatureValid`, because the body stating the certificate's status was never read.
|
|
668
|
+
*
|
|
662
669
|
* @opts
|
|
663
670
|
* cert the target certificate (parsed, DER, or PEM) -- REQUIRED.
|
|
664
671
|
* issuer its issuer certificate (parsed, DER, or PEM) -- REQUIRED.
|
package/lib/oid.js
CHANGED
|
@@ -432,6 +432,13 @@ var FAMILIES = Object.assign(Object.create(null), {
|
|
|
432
432
|
scepMessageType: 2, scepPkiStatus: 3, scepFailInfo: 4,
|
|
433
433
|
scepSenderNonce: 5, scepRecipientNonce: 6, scepTransactionId: 7 } },
|
|
434
434
|
scep: { base: [1, 3, 6, 1, 5, 5, 7, 24], of: { scepFailInfoText: 1 } },
|
|
435
|
+
/** @internal id-ri, the RFC 5940 revocation-information arc. Only the two leaves a reader can act
|
|
436
|
+
* on are registered: 2 names an OCSPResponse carried in a CMS `crls` field (RFC 5940 sec. 2) and
|
|
437
|
+
* 4 names an SCVP request-response pair (sec. 4). Leaves 1 and 3 are the SCVP sub-arcs RFC 5940
|
|
438
|
+
* does not define a CMS carriage for. The hyphenated member names are the RFC's own; `ocspResponse`
|
|
439
|
+
* is already bound to the OCSP nonce arc's `1.3.6.1.5.5.7.48.1.4` and is read by name from the
|
|
440
|
+
* OCSP extension-placement table, so reusing it here would redirect that table as well. */
|
|
441
|
+
idRi: { base: [1, 3, 6, 1, 5, 5, 7, 16], of: { "id-ri-ocsp-response": 2, "id-ri-scvp": 4 } },
|
|
435
442
|
});
|
|
436
443
|
|
|
437
444
|
var _byOid = new Map();
|
package/lib/schema-cms.js
CHANGED
|
@@ -34,8 +34,13 @@
|
|
|
34
34
|
* (sec. 5.4); `authAttrsBytes` plays the same role for the sec. 9.2 MAC input and the
|
|
35
35
|
* RFC 5083 sec. 2.2 AAD. Embedded certificates and CRLs are surfaced as raw DER +
|
|
36
36
|
* their outer tag, validated against the closed CertificateChoices /
|
|
37
|
-
* RevocationInfoChoice tag sets, so an
|
|
38
|
-
*
|
|
37
|
+
* RevocationInfoChoice tag sets, so an out-of-set element fails the parse. A
|
|
38
|
+
* certificate alternative is surfaced raw whatever it holds; a `crls` entry in the
|
|
39
|
+
* `other [1]` arm is read as RFC 5652 sec. 10.2.1 `OtherRevocationInfoFormat`,
|
|
40
|
+
* which names its format and, for RFC 5940 `id-ri-ocsp-response`, carries the
|
|
41
|
+
* embedded OCSP response as a record revocation checking takes directly. An
|
|
42
|
+
* unrecognized format stays opaque; a malformed one fails the parse. DER-only,
|
|
43
|
+
* fail-closed.
|
|
39
44
|
*
|
|
40
45
|
* @card
|
|
41
46
|
* Parse DER / PEM CMS (RFC 5652 / 5083 / 9629) into structured, validated fields:
|
|
@@ -49,6 +54,9 @@ var schema = require("./schema-engine");
|
|
|
49
54
|
var pkix = require("./schema-pkix");
|
|
50
55
|
var guard = require("./guard-all");
|
|
51
56
|
var oid = require("./oid");
|
|
57
|
+
/** @internal The OCSP response schema, for the RFC 5940 `crls` arm. `schema-ocsp` requires neither
|
|
58
|
+
* this module nor anything that reaches it, so this is an ordinary top-of-file require. */
|
|
59
|
+
var ocspSchema = require("./schema-ocsp");
|
|
52
60
|
var frameworkError = require("./framework-error");
|
|
53
61
|
var schemaX509 = require("./schema-x509");
|
|
54
62
|
var schemaCrl = require("./schema-crl");
|
|
@@ -383,6 +391,78 @@ var CRL_CHOICE_TAGS = Object.assign(Object.create(null), {
|
|
|
383
391
|
universal: Object.create(null),
|
|
384
392
|
context: Object.assign(Object.create(null), { 1: true }),
|
|
385
393
|
});
|
|
394
|
+
|
|
395
|
+
/** @internal RFC 5652 sec. 10.2.1's `other` arm:
|
|
396
|
+
*
|
|
397
|
+
* OtherRevocationInfoFormat ::= SEQUENCE { otherRevInfoFormat OBJECT IDENTIFIER, otherRevInfo ANY }
|
|
398
|
+
*
|
|
399
|
+
* Exactly two components. OpenSSL's `crypto/cms/cms_asn1.c` makes `otherRevInfo` optional, so it both
|
|
400
|
+
* accepts and can emit an entry carrying no revocation information at all; the ASN.1 above has no
|
|
401
|
+
* OPTIONAL and the one-element form is refused here. */
|
|
402
|
+
var OTHER_REV_INFO = schema.seq([
|
|
403
|
+
schema.field("otherRevInfoFormat", schema.oidLeaf()),
|
|
404
|
+
schema.field("otherRevInfo", schema.any()),
|
|
405
|
+
], {
|
|
406
|
+
assert: "implicit", implicitTag: 1, arity: { exact: 2 },
|
|
407
|
+
code: "cms/bad-other-rev-info", what: "OtherRevocationInfoFormat",
|
|
408
|
+
build: function (m) {
|
|
409
|
+
return {
|
|
410
|
+
otherRevInfoFormat: m.fields.otherRevInfoFormat.value,
|
|
411
|
+
otherRevInfoNode: m.fields.otherRevInfo.value,
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
var OID_ID_RI_OCSP_RESPONSE = oid.byName("id-ri-ocsp-response");
|
|
416
|
+
|
|
417
|
+
/** @internal The crls element mapper, which unlike `rawChoiceElement` needs the walk's own ctx: the
|
|
418
|
+
* `other` arm's interior is decoded under the caller's caps and budget rather than at defaults.
|
|
419
|
+
*
|
|
420
|
+
* The universal arm is surfaced raw as before, with one new refusal: RFC 5940 sec. 2 states that an
|
|
421
|
+
* OCSP response is carried in the `other` arm, so a response placed where a CertificateList belongs
|
|
422
|
+
* is a smuggle. It is detected POSITIVELY, by the OCSP module's own response detector, because the
|
|
423
|
+
* CRL detector answers false for shapes that are not full CertificateLists and "not a CRL" would
|
|
424
|
+
* therefore refuse more than sec. 2 forbids. */
|
|
425
|
+
/** @internal The mapper bound to one walk's ctx, so it fits the one-argument shape a list projection
|
|
426
|
+
* passes. The ctx is the walk's own, not a fresh one: that is what carries the caller's caps and the
|
|
427
|
+
* budget already being spent into the embedded response. */
|
|
428
|
+
function _mapCrlsWith(ctx, code, what) {
|
|
429
|
+
var mapper = crlChoiceElement(code, what);
|
|
430
|
+
return function (item) { return mapper(item, ctx); };
|
|
431
|
+
}
|
|
432
|
+
function crlChoiceElement(code, what) {
|
|
433
|
+
return function (item, ctx) {
|
|
434
|
+
var n = item.node;
|
|
435
|
+
var byClass = CRL_CHOICE_TAGS[n.tagClass];
|
|
436
|
+
if (!n.constructed || !byClass || byClass[n.tagNumber] !== true) {
|
|
437
|
+
throw NS.E(code, what + " element tag is outside the closed CHOICE set (RFC 5652 sec. 10.2.1-sec. 10.2.2)");
|
|
438
|
+
}
|
|
439
|
+
var out = rawElement(item);
|
|
440
|
+
if (n.tagClass !== "context") {
|
|
441
|
+
if (ocspSchema.matchesResponse(n)) {
|
|
442
|
+
throw NS.E("cms/misplaced-ocsp-response", "an OCSP response is carried in the other [1] " +
|
|
443
|
+
"RevocationInfoChoice arm under id-ri-ocsp-response, not in the CertificateList arm (RFC 5940 sec. 2)");
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
var other = schema.walk(OTHER_REV_INFO, n, ctx).result;
|
|
448
|
+
out.otherRevInfoFormat = other.otherRevInfoFormat;
|
|
449
|
+
out.otherRevInfoFormatName = ctx.oid.name(other.otherRevInfoFormat) || null;
|
|
450
|
+
out.otherRevInfoBytes = other.otherRevInfoNode.bytes;
|
|
451
|
+
out.ocspResponse = null;
|
|
452
|
+
if (other.otherRevInfoFormat === OID_ID_RI_OCSP_RESPONSE) {
|
|
453
|
+
var resp = ocspSchema.walkOcspResponse(other.otherRevInfoNode, ctx);
|
|
454
|
+
/** @internal RFC 5940 sec. 3: the carried response states a status. A `tryLater` or `malformed`
|
|
455
|
+
* OCSPResponse is a VALID OCSPResponse that conveys no revocation information, so it is refused
|
|
456
|
+
* HERE rather than in the OCSP module, whose standalone door must keep accepting it. */
|
|
457
|
+
if (resp.responseStatus.code !== 0) {
|
|
458
|
+
throw NS.E("cms/bad-ocsp-rev-info", "an id-ri-ocsp-response revocation entry carries a " +
|
|
459
|
+
resp.responseStatus.name + " OCSP response, which states no revocation status (RFC 5940 sec. 3)");
|
|
460
|
+
}
|
|
461
|
+
out.ocspResponse = resp;
|
|
462
|
+
}
|
|
463
|
+
return out;
|
|
464
|
+
};
|
|
465
|
+
}
|
|
386
466
|
CERT_CHOICE_TAGS.universal[asn1.TAGS.SEQUENCE] = true;
|
|
387
467
|
CRL_CHOICE_TAGS.universal[asn1.TAGS.SEQUENCE] = true;
|
|
388
468
|
function rawChoiceElement(allowed, code, what) {
|
|
@@ -648,7 +728,8 @@ var SIGNED_DATA = schema.seq([
|
|
|
648
728
|
var version = m.fields.version.value;
|
|
649
729
|
var encapContentInfo = m.fields.encapContentInfo.value.result;
|
|
650
730
|
var certificates = m.fields.certificates.present ? m.fields.certificates.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-certificates", "certificates")) : [];
|
|
651
|
-
var crls = m.fields.crls.present
|
|
731
|
+
var crls = m.fields.crls.present
|
|
732
|
+
? guard.list.copyMap(m.fields.crls.value.items, _mapCrlsWith(ctx, "cms/bad-crls", "crls")) : [];
|
|
652
733
|
var signerInfos = m.fields.signerInfos.value.items.map(function (it) { return it.value.result; });
|
|
653
734
|
|
|
654
735
|
if (encapContentInfo.eContentType !== OID_DATA) {
|
|
@@ -909,10 +990,16 @@ var ORIGINATOR_INFO = schema.seq([
|
|
|
909
990
|
schema.optional("crls", schema.implicitSetOf(1, schema.any(), { min: 1, code: "cms/bad-originator-crls", what: "crls" }), { tag: 1 }),
|
|
910
991
|
], {
|
|
911
992
|
assert: "constructed", code: "cms/bad-originator-info", what: "OriginatorInfo",
|
|
912
|
-
build
|
|
993
|
+
/** @internal The build takes `ctx` because the crls mapper decodes the RFC 5940 arm under the
|
|
994
|
+
* caller's own caps and budget. This one record is shared by EnvelopedData (RFC 5652 sec. 6.1),
|
|
995
|
+
* AuthenticatedData (sec. 9.1) and AuthEnvelopedData (RFC 5083 sec. 2.1), so decoding only in
|
|
996
|
+
* SignedData would leave three of the four content types RFC 5940 sec. 1 names undecoded. */
|
|
997
|
+
build: function (m, ctx) {
|
|
913
998
|
return {
|
|
914
999
|
certs: m.fields.certs.present ? m.fields.certs.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-originator-certs", "OriginatorInfo certs")) : [],
|
|
915
|
-
crls: m.fields.crls.present
|
|
1000
|
+
crls: m.fields.crls.present
|
|
1001
|
+
? guard.list.copyMap(m.fields.crls.value.items,
|
|
1002
|
+
_mapCrlsWith(ctx, "cms/bad-originator-crls", "OriginatorInfo crls")) : [],
|
|
916
1003
|
};
|
|
917
1004
|
},
|
|
918
1005
|
});
|
|
@@ -1184,7 +1271,14 @@ var CONTENT_INFO = schema.seq([
|
|
|
1184
1271
|
* unauthAttrs }` (`aead` holds the validated AES-GCM/CCM nonce + ICV length, or
|
|
1185
1272
|
* `null` for an unrecognized algorithm). A KEM recipient (RFC 9629) surfaces as
|
|
1186
1273
|
* `{ type: "ori", oriType, oriValue, kemri }` with the parsed KEMRecipientInfo in
|
|
1187
|
-
* `kemri`.
|
|
1274
|
+
* `kemri`. A `crls` entry is `{ bytes, tagClass, tagNumber }` for a CertificateList;
|
|
1275
|
+
* an entry in the `other [1]` arm additionally carries `otherRevInfoFormat` (dotted),
|
|
1276
|
+
* `otherRevInfoFormatName` (or `null`), `otherRevInfoBytes` (the raw ANY TLV) and,
|
|
1277
|
+
* for RFC 5940 `id-ri-ocsp-response`, `ocspResponse` holding the embedded response
|
|
1278
|
+
* as a record `pki.path.ocspChecker` and `pki.path.verifyOcspResponse` take
|
|
1279
|
+
* directly. A response whose `responseType` is one this build does not decode keeps
|
|
1280
|
+
* its envelope in `ocspResponse` with `basicResponse` null and the body in
|
|
1281
|
+
* `responseBytes.response` as it arrived. `originatorInfo.crls` carries the same shapes. Every result additionally carries `contentType` (the dotted OID) and
|
|
1188
1282
|
* `contentTypeName` (its registry name) naming which of the five shapes was
|
|
1189
1283
|
* dispatched. The raw byte ranges an external verifier hashes (`eContent`,
|
|
1190
1284
|
* `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac`) are surfaced
|
|
@@ -1337,16 +1431,27 @@ function parseCertsOnly(der, E, prefix, maxCerts, requireCert) {
|
|
|
1337
1431
|
} else if (allCerts.length === 0 && allCrls.length === 0) {
|
|
1338
1432
|
throw E(prefix + "/no-certificates", "a certs-only message must contain at least one certificate or CRL (RFC 8551 sec. 3.8)");
|
|
1339
1433
|
}
|
|
1434
|
+
/** @internal `maxCerts` caps the certificates and CRLs PARSED and RETURNED, which is a resource
|
|
1435
|
+
* bound. It may not decide which messages are accepted. The tag-class test reads a field off an
|
|
1436
|
+
* entry the parse already produced and costs nothing, so it runs over EVERY entry before the cap
|
|
1437
|
+
* is applied: taken after it, a cap leaving no room for the crls dropped a tagged entry silently,
|
|
1438
|
+
* so a capped reader accepted a message an uncapped reader refuses and handed back an empty crls
|
|
1439
|
+
* list where revocation information had been. Only the X.509 and CRL parsing below stays bounded,
|
|
1440
|
+
* which is the work the bound exists for. */
|
|
1441
|
+
for (var ci = 0; ci < allCerts.length; ci++) {
|
|
1442
|
+
if (allCerts[ci].tagClass !== "universal") throw E(prefix + "/bad-certificate-choice", "a certs-only response exchanges plain X.509 certificates; a tagged CertificateChoices alternative is not permitted (RFC 5272)");
|
|
1443
|
+
}
|
|
1444
|
+
for (var cj = 0; cj < allCrls.length; cj++) {
|
|
1445
|
+
if (allCrls[cj].tagClass !== "universal") throw E(prefix + "/bad-crl", "a certs-only response CRL must be a plain X.509 CertificateList, not a tagged otherRevInfo alternative (RFC 5652 sec. 10.2.1)");
|
|
1446
|
+
}
|
|
1340
1447
|
var certs = (maxCerts != null && allCerts.length > maxCerts) ? allCerts.slice(0, maxCerts) : allCerts;
|
|
1341
1448
|
for (var i = 0; i < certs.length; i++) {
|
|
1342
|
-
if (certs[i].tagClass !== "universal") throw E(prefix + "/bad-certificate-choice", "a certs-only response exchanges plain X.509 certificates; a tagged CertificateChoices alternative is not permitted (RFC 5272)");
|
|
1343
1449
|
try { schemaX509.parse(certs[i].bytes); }
|
|
1344
1450
|
catch (e) { throw E(prefix + "/bad-certificate", "a certs-only response carried a non-certificate in its certificates field (RFC 5272 sec. 4.1)", e); }
|
|
1345
1451
|
}
|
|
1346
1452
|
var crlCap = maxCerts != null ? (maxCerts - certs.length) : null;
|
|
1347
1453
|
var crls = (crlCap != null && allCrls.length > crlCap) ? allCrls.slice(0, crlCap) : allCrls;
|
|
1348
1454
|
for (var j = 0; j < crls.length; j++) {
|
|
1349
|
-
if (crls[j].tagClass !== "universal") throw E(prefix + "/bad-crl", "a certs-only response CRL must be a plain X.509 CertificateList, not a tagged otherRevInfo alternative (RFC 5652 sec. 10.2.1)");
|
|
1350
1455
|
try { schemaCrl.parse(crls[j].bytes); }
|
|
1351
1456
|
catch (e) { throw E(prefix + "/bad-crl", "a certs-only response carried a non-CRL in its crls field", e); }
|
|
1352
1457
|
}
|
package/lib/schema-ocsp.js
CHANGED
|
@@ -40,6 +40,7 @@ var oid = require("./oid");
|
|
|
40
40
|
var constants = require("./constants");
|
|
41
41
|
var frameworkError = require("./framework-error");
|
|
42
42
|
var intrinsic = require("./guard-intrinsic");
|
|
43
|
+
var guard = require("./guard-all");
|
|
43
44
|
var _hasOwn = intrinsic.hasOwn;
|
|
44
45
|
var _String = intrinsic.String;
|
|
45
46
|
|
|
@@ -319,18 +320,24 @@ var RESPONSE_BYTES = schema.seq([
|
|
|
319
320
|
schema.field("response", schema.octetString()),
|
|
320
321
|
], {
|
|
321
322
|
assert: "sequence", arity: { exact: 2 }, code: "ocsp/bad-response-bytes", what: "ResponseBytes",
|
|
323
|
+
/** @internal RFC 6960 sec. 4.2.1 dispatches the body on `responseType`, so a type this build does
|
|
324
|
+
* not read leaves `basicResponse` null and the body in `response` exactly as it arrived. The
|
|
325
|
+
* envelope is the part every reader shares, and which bodies this build decodes is its own support
|
|
326
|
+
* limit rather than a shape rule: `parseResponse` refuses an unread body at its door, where the
|
|
327
|
+
* caller asked for a response to read, and a response carried inside another structure stays
|
|
328
|
+
* readable as far as its envelope goes. A body that claims id-pkix-ocsp-basic is still decoded,
|
|
329
|
+
* and still refused when it is not one. */
|
|
322
330
|
build: function (m, ctx) {
|
|
323
331
|
var responseType = m.fields.responseType.value;
|
|
324
332
|
var raw = m.fields.response.value;
|
|
325
|
-
if (responseType !== OID_OCSP_BASIC) {
|
|
326
|
-
throw NS.E("ocsp/unsupported-response-type", (ctx.oid.name(responseType) || responseType) + " is not id-pkix-ocsp-basic (RFC 6960 sec. 4.2.1)");
|
|
327
|
-
}
|
|
328
333
|
return {
|
|
329
334
|
responseType: responseType,
|
|
330
335
|
responseTypeName: ctx.oid.name(responseType) || null,
|
|
331
336
|
response: raw,
|
|
332
|
-
basicResponse:
|
|
333
|
-
|
|
337
|
+
basicResponse: responseType === OID_OCSP_BASIC
|
|
338
|
+
? schema.embeddedDer(BASIC_OCSP_RESPONSE, raw, ctx,
|
|
339
|
+
{ code: "ocsp/bad-der", what: "BasicOCSPResponse" }).result
|
|
340
|
+
: null,
|
|
334
341
|
};
|
|
335
342
|
},
|
|
336
343
|
});
|
|
@@ -506,7 +513,19 @@ var parseRequest = pkix.makeParser({ pemLabel: "OCSP REQUEST", PemError: PemErro
|
|
|
506
513
|
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
507
514
|
* refused with the `/too-large` code of this format.
|
|
508
515
|
*/
|
|
509
|
-
var
|
|
516
|
+
var _parseResponseRecorded = pkix.makeRecordingParser({ pemLabel: "OCSP RESPONSE", PemError: PemError, ErrorClass: OcspError, prefix: "ocsp", what: "OCSP response", topSchema: OCSP_RESPONSE, ns: NS }, "ocspResponse");
|
|
517
|
+
/** @internal The decision is made on `responseType`, the field RFC 6960 sec. 4.2.1 dispatches on,
|
|
518
|
+
* rather than on the absent `basicResponse` that follows from it. A non-successful response carries
|
|
519
|
+
* no responseBytes at all and keeps reaching the caller, which is the status a live responder
|
|
520
|
+
* returns. */
|
|
521
|
+
function parseResponse(input, caps) {
|
|
522
|
+
var out = _parseResponseRecorded(input, caps);
|
|
523
|
+
var rb = out.responseBytes;
|
|
524
|
+
if (rb && rb.responseType !== OID_OCSP_BASIC) {
|
|
525
|
+
throw NS.E("ocsp/unsupported-response-type", (rb.responseTypeName || rb.responseType) + " is not id-pkix-ocsp-basic (RFC 6960 sec. 4.2.1)");
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
510
529
|
|
|
511
530
|
/**
|
|
512
531
|
* @primitive pki.schema.ocsp.pemDecode
|
|
@@ -580,9 +599,25 @@ function matchesRequest(root) {
|
|
|
580
599
|
return true;
|
|
581
600
|
}
|
|
582
601
|
|
|
602
|
+
/** @internal Walk an ALREADY-DECODED OCSPResponse node under the caller's own parse resources, for a
|
|
603
|
+
* carrier that embeds one: RFC 5940 sec. 2 puts an OCSPResponse in a CMS `crls` field, and the CMS
|
|
604
|
+
* parse reaches it with a node it has already decoded and a budget it is already spending.
|
|
605
|
+
* `parseResponse(bytes)` cannot serve that: it re-decodes from scratch and starts a fresh budget, so
|
|
606
|
+
* a caller's tightened caps would not reach the embedded walk. `withResources` carries the caps and
|
|
607
|
+
* the budget while keeping this module's own error class, so a malformed embedded response still
|
|
608
|
+
* reports an `ocsp/*` code. The record is a recording one, so `guard.parsed.fromTrustedSource`
|
|
609
|
+
* accepts it wherever a parsed response is taken and a rebuilt copy is still refused. The re-decode
|
|
610
|
+
* this walker performs on raw DER is STRICT, unlike the CMS one: an OCSPResponse has no
|
|
611
|
+
* BER-tolerant door. */
|
|
612
|
+
var walkOcspResponse = guard.parsed.recordingWalker("ocspResponse", function (node, ctx) {
|
|
613
|
+
return schema.walk(OCSP_RESPONSE, node, schema.withResources(NS, ctx)).result;
|
|
614
|
+
// allow:bare-decode the re-decode a recording walker performs on raw DER a caller hands it, outside any parse.
|
|
615
|
+
}, function (der) { return asn1.decode(der); });
|
|
616
|
+
|
|
583
617
|
module.exports = {
|
|
584
618
|
parseRequest: parseRequest,
|
|
585
619
|
parseResponse: parseResponse,
|
|
620
|
+
walkOcspResponse: walkOcspResponse,
|
|
586
621
|
pemDecode: pemDecode,
|
|
587
622
|
pemEncode: pemEncode,
|
|
588
623
|
matchesRequest: matchesRequest,
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:114b9f0f-7681-4cab-8c6a-fcdbf8fd64af",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-07T02:26:14.514Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.8.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.8.57",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.57",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.8.57",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.8.
|
|
57
|
+
"ref": "@blamejs/pki@0.8.57",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|