@blamejs/pki 0.1.9 → 0.1.11
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 +24 -0
- package/README.md +60 -1
- package/lib/asn1-der.js +30 -0
- package/lib/framework-error.js +10 -0
- package/lib/oid.js +31 -2
- package/lib/schema-all.js +38 -2
- package/lib/schema-cms.js +375 -0
- package/lib/schema-engine.js +12 -1
- package/lib/schema-ocsp.js +571 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ 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.1.11 — 2026-07-05
|
|
8
|
+
|
|
9
|
+
An OCSP request and response parser joins the pki.schema family.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- pki.schema.ocsp.parseRequest and pki.schema.ocsp.parseResponse — an OCSP request / response parser per RFC 6960 (§4.1 OCSPRequest, §4.2 OCSPResponse). parseRequest turns a DER Buffer or an 'OCSP REQUEST' PEM string into { tbsRequestBytes, version, requestorName, requestList, requestExtensions, optionalSignature }, each requestList entry carrying its CertID with the two issuer hashes raw. parseResponse turns a DER Buffer or an 'OCSP RESPONSE' PEM string into { responseStatus, responseBytes, basicResponse }; for a successful basic response, basicResponse carries { tbsResponseDataBytes, responderID, producedAt, responses, signatureAlgorithm, signature, certs } and each responses[i].certStatus is { type: 'good' | 'revoked' | 'unknown' } (a revoked entry adds its revocationTime and revocationReason). A malformed structure throws a typed OcspError (ocsp/*), an unsupported responseType throws ocsp/unsupported-response-type, and a leaf-level codec fault surfaces as asn1/*. pki.schema.ocsp.pemDecode handles the PEM envelope, and pki.schema.parse detect-and-routes both OCSP shapes.
|
|
14
|
+
- pki.asn1.read.nullImplicit and the pki.schema.engine.implicitNull(tag) leaf — read a context-tagged IMPLICIT NULL (the shape the OCSP CertStatus good [0] and unknown [2] arms take), the primitive-leaf sibling of implicitBitString and implicitOctetString. It rejects a constructed or non-empty context node fail-closed.
|
|
15
|
+
- An ocsp-parse coverage-guided fuzz harness joins the CI fuzzing matrix (jazzer.js + libFuzzer, per pull request and nightly), driving both OCSP entry points on mutated input.
|
|
16
|
+
|
|
17
|
+
## v0.1.10 — 2026-07-05
|
|
18
|
+
|
|
19
|
+
A CMS SignedData parser joins the pki.schema family; coverage-guided fuzzing now runs in CI.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- pki.schema.cms.parse — a CMS SignedData parser per RFC 5652 (§3 ContentInfo + §5 SignedData). It turns a DER Buffer or a 'CMS' PEM string into { version, digestAlgorithms, encapContentInfo, certificates, crls, signerInfos }. encapContentInfo.eContent is the raw content (or null when the signature is detached); each SignerInfo carries its raw signature and, when signed attributes are present, the on-wire signedAttrsBytes for external verification. The signer identifier is an issuerAndSerialNumber or a subjectKeyIdentifier, with the version-to-identifier rule (RFC 5652 §5.3) enforced, and a degenerate certificates-only SignedData (empty digest algorithms and signer infos) is accepted. A ContentInfo whose content type is not id-signedData throws cms/unsupported-content-type (a recognized PKCS#7 type) or cms/unknown-content-type; a malformed structure throws a typed CmsError (cms/*) and a leaf-level codec fault surfaces as asn1/*. pki.schema.cms.pemDecode / pemEncode handle the PEM envelope, and pki.schema.parse detect-and-routes a CMS message.
|
|
24
|
+
- pki.asn1.read.octetStringImplicit and the pki.schema.engine.implicitOctetString(tag) leaf — read a context-tagged IMPLICIT OCTET STRING (the shape the CMS SignerIdentifier subjectKeyIdentifier [0] takes), the primitive-leaf sibling of implicitBitString.
|
|
25
|
+
- Coverage-guided fuzzing in CI — a ClusterFuzzLite integration (.clusterfuzzlite/) plus pull-request and nightly-batch workflows run jazzer.js + libFuzzer against the pki.asn1.decode, pki.schema.x509.parse and pki.schema.cms.parse harnesses. A finding fails the run with the reproducer inline and uploads the crash input as an artifact. The integration is detectable by OpenSSF Scorecard's Fuzzing check.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- The OpenSSL interop runner counted a cross-check the oracle could not perform (for example, an OpenSSL predating ML-DSA) as a pass. Such a cross-check is now recorded as a skip and reported separately, so the interop pass count is not inflated by checks that never executed.
|
|
30
|
+
|
|
7
31
|
## v0.1.9 — 2026-07-05
|
|
8
32
|
|
|
9
33
|
A PKCS#8 private-key parser joins the pki.schema family.
|
package/README.md
CHANGED
|
@@ -195,9 +195,11 @@ is callable today; nothing below is a stub.
|
|
|
195
195
|
| `pki.schema.crl` | Parse DER / PEM X.509 CRLs per RFC 5280 §5 — revoked serials with real-`Date` revocation times, named + partly-decoded extensions, fail-closed — `parse`, `pemDecode` |
|
|
196
196
|
| `pki.schema.csr` | Parse DER / PEM PKCS#10 certification requests per RFC 2986 — subject DN, public key, requested attributes, signature, fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
197
197
|
| `pki.schema.pkcs8` | Parse DER / PEM PKCS#8 private keys per RFC 5208 / 5958 — algorithm, raw key bytes, attributes, optional public key, fail-closed; encrypted keys recognized (not decrypted) — `parse`, `parseEncrypted`, `pemDecode`, `pemEncode` |
|
|
198
|
+
| `pki.schema.cms` | Parse DER / PEM CMS SignedData per RFC 5652 — encapsulated content, signer infos, raw signed-attribute bytes for external verification, certificates / CRLs kept raw, fail-closed; non-SignedData content types recognized (not decoded) — `parse`, `pemDecode`, `pemEncode` |
|
|
199
|
+
| `pki.schema.ocsp` | Parse DER / PEM OCSP requests and responses per RFC 6960 — per-certificate status (good / revoked / unknown), responder identity, raw tbs bytes for external verification, certificates kept raw, fail-closed; non-basic response types recognized (not decoded) — `parseRequest`, `parseResponse`, `pemDecode` |
|
|
198
200
|
| `pki.schema.engine` | The declarative ASN.1 structure-schema engine every format parser composes — `walk` plus the schema combinators |
|
|
199
201
|
| `pki.C` / `pki.constants` | Version-stable constants — functional scale helpers (`C.TIME.*`, `C.BYTES.*`), codec `LIMITS`, `version` |
|
|
200
|
-
| `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError` / `Asn1Error` / `OidError` / `PemError` / `CertificateError` / `CrlError` / `CsrError` / `Pkcs8Error`, each carrying a stable `code` in `domain/reason` form |
|
|
202
|
+
| `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError` / `Asn1Error` / `OidError` / `PemError` / `CertificateError` / `CrlError` / `CsrError` / `Pkcs8Error` / `CmsError` / `OcspError`, each carrying a stable `code` in `domain/reason` form |
|
|
201
203
|
| `pki` CLI | `pki version`, `pki oid <dotted\|name>`, `pki parse <cert>` |
|
|
202
204
|
|
|
203
205
|
### CLI
|
|
@@ -219,6 +221,63 @@ composites) are on the roadmap and ride this same core. See
|
|
|
219
221
|
[ROADMAP.md](ROADMAP.md) for the full plan and current status of each area, and
|
|
220
222
|
[CHANGELOG.md](CHANGELOG.md) for what has landed.
|
|
221
223
|
|
|
224
|
+
## Architecture
|
|
225
|
+
|
|
226
|
+
Every PKI format is a thin, declarative schema over one shared engine. A parser
|
|
227
|
+
declares the ASN.1 structure as data and hands it to `walk`; it never advances a
|
|
228
|
+
child cursor, re-checks a tag, or re-rolls PEM handling by hand. So each
|
|
229
|
+
structural rule — bounds-checked positional reads, optional / context-tagged
|
|
230
|
+
field ordering, SET-OF ascending-order and uniqueness, arity, and fail-closed
|
|
231
|
+
typed errors — is written **once** in the engine, and no new format can
|
|
232
|
+
reintroduce the bug class it prevents. Adding a format is a schema declaration
|
|
233
|
+
plus a documentation comment block, not new parse logic.
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
Detect + route pki.schema.parse ── detects the format, routes to a sibling
|
|
237
|
+
│
|
|
238
|
+
Format parsers x509 crl csr pkcs8 cms ocsp (siblings)
|
|
239
|
+
(one schema each) └──────┴─────┴──────┴──────┴─────┘
|
|
240
|
+
│ every sibling COMPOSES ↓
|
|
241
|
+
Shared structure pki.schema.engine PKIX sub-schemas
|
|
242
|
+
walk + combinators · AlgorithmIdentifier · Name ·
|
|
243
|
+
(positional reads, Extension · version reader ·
|
|
244
|
+
tag ordering, SET-OF the one coerce → decode → walk
|
|
245
|
+
uniqueness, arity, parse-entry (PEM cap + DER
|
|
246
|
+
fail-closed errors) wrapping shared, never copied)
|
|
247
|
+
│ built on ↓
|
|
248
|
+
Foundation pki.asn1 pki.oid pki.errors pki.C
|
|
249
|
+
DER codec · OID registry · PkiError · constants
|
|
250
|
+
(strict, (two-way, taxonomy (LIMITS,
|
|
251
|
+
bounded) PQC-seeded) (domain/reason) scale)
|
|
252
|
+
|
|
253
|
+
Crypto pki.webcrypto ──▶ node:crypto
|
|
254
|
+
W3C SubtleCrypto over the platform: the classical set +
|
|
255
|
+
post-quantum ML-DSA / SLH-DSA signatures + ML-KEM keygen
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Foundation.** The strict, bounded DER codec (`pki.asn1`), the two-way OID
|
|
259
|
+
registry (`pki.oid`), the `PkiError` taxonomy (`pki.errors`), and the
|
|
260
|
+
version-stable constants (`pki.C`). These have no PKI knowledge; they are the
|
|
261
|
+
bytes-and-names layer everything else stands on.
|
|
262
|
+
|
|
263
|
+
**Shared structure.** The declarative schema engine (`pki.schema.engine`) and the
|
|
264
|
+
PKIX sub-schemas it is fed — `AlgorithmIdentifier`, `Name`, `Extension`, the
|
|
265
|
+
bounded version reader, and the single coerce → decode → walk parse-entry that
|
|
266
|
+
every format's `parse` is bound to. Because input coercion, the PEM size cap, and
|
|
267
|
+
the DER-decode wrapping live here once, a format cannot diverge on a guard.
|
|
268
|
+
|
|
269
|
+
**Format parsers.** `x509`, `crl`, `csr`, `pkcs8`, `cms`, and `ocsp` are siblings:
|
|
270
|
+
each is a schema declaration composed from the shared pieces, emitting its own
|
|
271
|
+
typed `domain/reason` error codes. `pki.schema.parse` inspects a decoded root and
|
|
272
|
+
detect-and-routes to the matching sibling; the detectors are mutually exclusive by
|
|
273
|
+
construction, so routing is unambiguous regardless of registration order.
|
|
274
|
+
|
|
275
|
+
**Crypto.** `pki.webcrypto` is a W3C `SubtleCrypto` engine over `node:crypto`,
|
|
276
|
+
carrying the classical suite plus post-quantum ML-DSA / SLH-DSA signatures and
|
|
277
|
+
ML-KEM key generation. Sign/verify resolves algorithms through the same OID
|
|
278
|
+
registry the parsers read, so the signing surface and the parsing surface share
|
|
279
|
+
one algorithm vocabulary.
|
|
280
|
+
|
|
222
281
|
## Security posture
|
|
223
282
|
|
|
224
283
|
- **Zero npm runtime dependencies, nothing vendored.** The cryptography runs on
|
package/lib/asn1-der.js
CHANGED
|
@@ -360,6 +360,20 @@ function readOctetString(node) {
|
|
|
360
360
|
return node.content;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
// Read a [tag] IMPLICIT OCTET STRING — a context-class PRIMITIVE node whose
|
|
364
|
+
// content is the octet-string body (the IMPLICIT tag replaces the universal one,
|
|
365
|
+
// so there is no inner universal node). Primitive-only, like its universal
|
|
366
|
+
// counterpart, so the BER constructed/streamed form is rejected. Used for the CMS
|
|
367
|
+
// SignerIdentifier subjectKeyIdentifier [0] (RFC 5652 §5.3).
|
|
368
|
+
function readOctetStringImplicit(node, tag) {
|
|
369
|
+
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
370
|
+
throw new Asn1Error("asn1/unexpected-tag", "readOctetStringImplicit: expected context tag [" + tag +
|
|
371
|
+
"], got " + node.tagClass + "/" + node.tagNumber);
|
|
372
|
+
}
|
|
373
|
+
_expectPrimitive(node, "readOctetStringImplicit");
|
|
374
|
+
return node.content;
|
|
375
|
+
}
|
|
376
|
+
|
|
363
377
|
function readNull(node) {
|
|
364
378
|
_expectUniversal(node, TAGS.NULL, "readNull");
|
|
365
379
|
_expectPrimitive(node, "readNull");
|
|
@@ -367,6 +381,20 @@ function readNull(node) {
|
|
|
367
381
|
return null;
|
|
368
382
|
}
|
|
369
383
|
|
|
384
|
+
// Read a [tag] IMPLICIT NULL — a context-class PRIMITIVE node with empty content.
|
|
385
|
+
// The IMPLICIT tag replaces the universal NULL tag, so there is no inner universal
|
|
386
|
+
// node. The empty-content and primitive-form rules of a universal NULL still hold.
|
|
387
|
+
// Used for the OCSP CertStatus good [0] / unknown [2] arms (RFC 6960 §4.2.1).
|
|
388
|
+
function readNullImplicit(node, tag) {
|
|
389
|
+
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
390
|
+
throw new Asn1Error("asn1/unexpected-tag", "readNullImplicit: expected context tag [" + tag +
|
|
391
|
+
"], got " + node.tagClass + "/" + node.tagNumber);
|
|
392
|
+
}
|
|
393
|
+
_expectPrimitive(node, "readNullImplicit");
|
|
394
|
+
if (node.content.length !== 0) throw new Asn1Error("asn1/bad-null", "IMPLICIT NULL must have empty content");
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
370
398
|
/**
|
|
371
399
|
* @primitive pki.asn1.readOid
|
|
372
400
|
* @signature pki.asn1.read.oid(node) -> "1.2.840.113549.1.1.11"
|
|
@@ -794,7 +822,9 @@ module.exports = {
|
|
|
794
822
|
bitString: readBitString,
|
|
795
823
|
bitStringImplicit: readBitStringImplicit,
|
|
796
824
|
octetString: readOctetString,
|
|
825
|
+
octetStringImplicit: readOctetStringImplicit,
|
|
797
826
|
nullValue: readNull,
|
|
827
|
+
nullImplicit: readNullImplicit,
|
|
798
828
|
oid: readOid,
|
|
799
829
|
string: readString,
|
|
800
830
|
time: readTime,
|
package/lib/framework-error.js
CHANGED
|
@@ -137,6 +137,14 @@ var CsrError = defineClass("CsrError", { withCause: true });
|
|
|
137
137
|
// OneAsymmetricKey or EncryptedPrivateKeyInfo (RFC 5208 §5, RFC 5958 §2/§3).
|
|
138
138
|
var Pkcs8Error = defineClass("Pkcs8Error", { withCause: true });
|
|
139
139
|
|
|
140
|
+
// CmsError — a byte sequence that is not a well-formed CMS ContentInfo /
|
|
141
|
+
// SignedData (RFC 5652). Carries the underlying leaf fault as `.cause`.
|
|
142
|
+
var CmsError = defineClass("CmsError", { withCause: true });
|
|
143
|
+
|
|
144
|
+
// OcspError — a byte sequence that is not a well-formed OCSP request or response
|
|
145
|
+
// (OCSPRequest / OCSPResponse — RFC 6960 §4). Carries the leaf fault as `.cause`.
|
|
146
|
+
var OcspError = defineClass("OcspError", { withCause: true });
|
|
147
|
+
|
|
140
148
|
module.exports = {
|
|
141
149
|
PkiError: PkiError,
|
|
142
150
|
defineClass: defineClass,
|
|
@@ -149,4 +157,6 @@ module.exports = {
|
|
|
149
157
|
SchemaError: SchemaError,
|
|
150
158
|
CsrError: CsrError,
|
|
151
159
|
Pkcs8Error: Pkcs8Error,
|
|
160
|
+
CmsError: CmsError,
|
|
161
|
+
OcspError: OcspError,
|
|
152
162
|
};
|
package/lib/oid.js
CHANGED
|
@@ -65,13 +65,38 @@ var FAMILIES = {
|
|
|
65
65
|
// PKIX private extensions (authorityInfoAccess et al).
|
|
66
66
|
pkixAccess: { base: [1, 3, 6, 1, 5, 5, 7, 1], of: { authorityInfoAccess: 1 } },
|
|
67
67
|
|
|
68
|
+
// PKIX Access Descriptor methods (id-ad, RFC 5280 §4.2.2.1/§4.2.2.2). id-ad-ocsp
|
|
69
|
+
// is the arc the OCSP responder OIDs hang under; id-ad-caIssuers names the AIA
|
|
70
|
+
// CA-issuers access method.
|
|
71
|
+
adAccess: { base: [1, 3, 6, 1, 5, 5, 7, 48], of: { ocsp: 1, caIssuers: 2 } },
|
|
72
|
+
|
|
73
|
+
// OCSP (RFC 6960) on the id-pkix-ocsp arc (= id-ad-ocsp). id-pkix-ocsp-basic is
|
|
74
|
+
// the ResponseBytes.responseType this build decodes; id-pkix-ocsp-nonce (§4.4.1)
|
|
75
|
+
// names the nonce extension; the remaining members name the other OCSP extensions
|
|
76
|
+
// (CRL references, acceptable-response-types, archive-cutoff, service-locator,
|
|
77
|
+
// preferred-signature-algorithms, extended-revoke — RFC 6960 §4.4, RFC 9654).
|
|
78
|
+
ocsp: { base: [1, 3, 6, 1, 5, 5, 7, 48, 1], of: {
|
|
79
|
+
ocspBasic: 1, ocspNonce: 2, ocspCrl: 3, ocspResponse: 4, ocspNoCheck: 5,
|
|
80
|
+
ocspArchiveCutoff: 6, ocspServiceLocator: 7, ocspPrefSigAlgs: 8, ocspExtendedRevoke: 9 } },
|
|
81
|
+
|
|
68
82
|
// PKCS#1 RSA public-key + RSASSA signature algorithms.
|
|
69
83
|
rsa: { base: [1, 2, 840, 113549, 1, 1], of: {
|
|
70
84
|
rsaEncryption: 1, rsassaPss: 10, sha256WithRSAEncryption: 11,
|
|
71
85
|
sha384WithRSAEncryption: 12, sha512WithRSAEncryption: 13 } },
|
|
72
86
|
|
|
73
|
-
// PKCS#
|
|
74
|
-
|
|
87
|
+
// PKCS#7 / CMS content types (RFC 5652 §4, RFC 2315). id-signedData is the one
|
|
88
|
+
// this toolkit structurally decodes; the rest are recognized-and-deferred.
|
|
89
|
+
pkcs7: { base: [1, 2, 840, 113549, 1, 7], of: {
|
|
90
|
+
data: 1, signedData: 2, envelopedData: 3, signedAndEnvelopedData: 4,
|
|
91
|
+
digestedData: 5, encryptedData: 6 } },
|
|
92
|
+
|
|
93
|
+
// PKCS#9 attribute types — incl. the CMS signed-attribute OIDs (RFC 5652 §11).
|
|
94
|
+
pkcs9: { base: [1, 2, 840, 113549, 1, 9], of: {
|
|
95
|
+
emailAddress: 1, contentType: 3, messageDigest: 4, signingTime: 5,
|
|
96
|
+
challengePassword: 7, extensionRequest: 14 } },
|
|
97
|
+
|
|
98
|
+
// S/MIME content types on the PKCS#9 smime arc (RFC 5652, RFC 3161): id-ct.
|
|
99
|
+
smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4 } },
|
|
75
100
|
|
|
76
101
|
// ANSI X9.62 EC public key, named curve, and ECDSA signatures.
|
|
77
102
|
ansiX962: { base: [1, 2, 840, 10045], of: {
|
|
@@ -84,6 +109,10 @@ var FAMILIES = {
|
|
|
84
109
|
// Edwards / Montgomery curves (RFC 8410).
|
|
85
110
|
edwards: { base: [1, 3, 101], of: { X25519: 110, X448: 111, Ed25519: 112, Ed448: 113 } },
|
|
86
111
|
|
|
112
|
+
// OIW Secsig SHA-1 (1.3.14.3.2.26) — the most common OCSP CertID hashAlgorithm;
|
|
113
|
+
// nistHash covers only SHA-256 and above.
|
|
114
|
+
oiwSecsig: { base: [1, 3, 14, 3, 2], of: { sha1: 26 } },
|
|
115
|
+
|
|
87
116
|
// NIST hash functions (SHA-2, SHA-3, SHAKE).
|
|
88
117
|
nistHash: { base: [2, 16, 840, 1, 101, 3, 4, 2], of: {
|
|
89
118
|
sha256: 1, sha384: 2, sha512: 3, "sha3-256": 8, "sha3-512": 10, shake256: 12 } },
|
package/lib/schema-all.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @intro
|
|
13
13
|
* The schema family: a declarative ASN.1 structure-schema engine and the
|
|
14
14
|
* per-format parsers built on it. Every format (X.509 certificates, CRLs,
|
|
15
|
-
* PKCS#10 certification requests,
|
|
15
|
+
* PKCS#10 certification requests, PKCS#8 private keys, and CMS SignedData) is a
|
|
16
16
|
* member that COMPOSES the
|
|
17
17
|
* shared engine and the shared PKIX sub-schemas (AlgorithmIdentifier, Name,
|
|
18
18
|
* Extension), so a structural rule — bounds-checked positional reads,
|
|
@@ -36,6 +36,8 @@ var x509 = require("./schema-x509");
|
|
|
36
36
|
var crl = require("./schema-crl");
|
|
37
37
|
var csr = require("./schema-csr");
|
|
38
38
|
var pkcs8 = require("./schema-pkcs8");
|
|
39
|
+
var cms = require("./schema-cms");
|
|
40
|
+
var ocsp = require("./schema-ocsp");
|
|
39
41
|
var frameworkError = require("./framework-error");
|
|
40
42
|
|
|
41
43
|
var SchemaError = frameworkError.SchemaError;
|
|
@@ -52,6 +54,38 @@ var ENTRY = { pemLabel: null, PemError: PemError, ErrorClass: SchemaError, prefi
|
|
|
52
54
|
// logic. Order matters: the most specific detector wins, so a new member is
|
|
53
55
|
// inserted ahead of any more-permissive one.
|
|
54
56
|
var FORMATS = [
|
|
57
|
+
{
|
|
58
|
+
// CMS ContentInfo (RFC 5652 §3) — the only registered root that leads with an
|
|
59
|
+
// OBJECT IDENTIFIER child (a SEQUENCE of exactly 2: contentType OID + a
|
|
60
|
+
// context [0] EXPLICIT content wrapper). Disjoint from the INTEGER-first pkcs8
|
|
61
|
+
// and the tbs-SEQUENCE-first signed-envelope trio, so it detects unambiguously.
|
|
62
|
+
// A non-SignedData content type routes here and gets a precise
|
|
63
|
+
// cms/unsupported-content-type rather than schema/unknown-format.
|
|
64
|
+
name: "cms",
|
|
65
|
+
module: cms,
|
|
66
|
+
detect: cms.matches,
|
|
67
|
+
parse: function (input) { return cms.parse(input); },
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
// OCSPRequest ::= SEQUENCE { tbsRequest SEQUENCE, optionalSignature [0] EXPLICIT
|
|
71
|
+
// OPTIONAL } — a SEQUENCE of 1-2 whose first child is the tbsRequest SEQUENCE.
|
|
72
|
+
// Leads with a SEQUENCE like the signed-envelope trio, but is excluded by arity
|
|
73
|
+
// (the trio is EXACTLY 3 children; an OCSPRequest is 1-2), so it detects
|
|
74
|
+
// unambiguously regardless of registry order (RFC 6960 §4.1.1).
|
|
75
|
+
name: "ocsp-request",
|
|
76
|
+
module: ocsp,
|
|
77
|
+
detect: ocsp.matchesRequest,
|
|
78
|
+
parse: function (input) { return ocsp.parseRequest(input); },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
// OCSPResponse ::= SEQUENCE { responseStatus ENUMERATED, responseBytes [0]
|
|
82
|
+
// EXPLICIT OPTIONAL } — the only registered root that leads with an ENUMERATED
|
|
83
|
+
// child, so it is disjoint from every other format (RFC 6960 §4.2.1).
|
|
84
|
+
name: "ocsp-response",
|
|
85
|
+
module: ocsp,
|
|
86
|
+
detect: ocsp.matchesResponse,
|
|
87
|
+
parse: function (input) { return ocsp.parseResponse(input); },
|
|
88
|
+
},
|
|
55
89
|
{
|
|
56
90
|
// PKCS#8 PrivateKeyInfo / OneAsymmetricKey — SEQUENCE whose first child is an
|
|
57
91
|
// INTEGER (version) and third an OCTET STRING (privateKey); disjoint from the
|
|
@@ -107,7 +141,7 @@ var FORMATS = [
|
|
|
107
141
|
* The names of every registered format, in detection order.
|
|
108
142
|
*
|
|
109
143
|
* @example
|
|
110
|
-
* pki.schema.all(); // → ["pkcs8", "csr", "crl", "x509"]
|
|
144
|
+
* pki.schema.all(); // → ["cms", "ocsp-request", "ocsp-response", "pkcs8", "csr", "crl", "x509"]
|
|
111
145
|
*/
|
|
112
146
|
function all() { return FORMATS.map(function (f) { return f.name; }); }
|
|
113
147
|
|
|
@@ -149,6 +183,8 @@ module.exports = {
|
|
|
149
183
|
crl: { parse: crl.parse, pemDecode: crl.pemDecode },
|
|
150
184
|
csr: { parse: csr.parse, pemDecode: csr.pemDecode, pemEncode: csr.pemEncode },
|
|
151
185
|
pkcs8: { parse: pkcs8.parse, parseEncrypted: pkcs8.parseEncrypted, pemDecode: pkcs8.pemDecode, pemEncode: pkcs8.pemEncode },
|
|
186
|
+
cms: { parse: cms.parse, pemDecode: cms.pemDecode, pemEncode: cms.pemEncode },
|
|
187
|
+
ocsp: { parseRequest: ocsp.parseRequest, parseResponse: ocsp.parseResponse, pemDecode: ocsp.pemDecode },
|
|
152
188
|
all: all,
|
|
153
189
|
parse: parse,
|
|
154
190
|
};
|