@blamejs/pki 0.1.12 → 0.1.14
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 +20 -0
- package/README.md +32 -22
- package/lib/asn1-der.js +41 -4
- package/lib/framework-error.js +12 -0
- package/lib/oid.js +36 -5
- package/lib/schema-all.js +43 -3
- package/lib/schema-attrcert.js +383 -0
- package/lib/schema-engine.js +16 -2
- package/lib/schema-ocsp.js +4 -53
- package/lib/schema-pkix.js +78 -0
- package/lib/schema-tsp.js +411 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ 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.14 — 2026-07-06
|
|
8
|
+
|
|
9
|
+
An RFC 5755 attribute-certificate parser joins the pki.schema family.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- pki.schema.attrcert — an RFC 5755 attribute-certificate parser. pki.schema.attrcert.parse turns a DER Buffer or PEM into a structured v2 attribute certificate ({ version, holder, issuer, signatureAlgorithm, serialNumber, serialNumberHex, validity, attributes, issuerUniqueID, extensions, tbsBytes, signatureValue }). The holder (baseCertificateID / entityName / objectDigestInfo) and issuer (v1Form / v2Form) identities come back as validated GeneralNames; the validity window is real Dates; the privilege attributes (id-at-role, id-aca-group, id-at-clearance, and any others) resolve by name where the registry knows them. The outer-equals-inner signatureAlgorithm agreement (RFC 5755 4.2.4), the positive-and-at-most-20-octet serialNumber (4.2.5), the GeneralizedTime-only validity (4.2.6), the non-empty unique-typed attribute list (4.2.7), and the digestedObjectType enumeration are all enforced fail-closed. pki.schema.parse detect-and-routes an attribute certificate; the obsolete v1 form is recognized and deferred with a precise attrcert/legacy-v1-not-supported.
|
|
14
|
+
- pki.schema.pkix gains a shared GeneralNames validator that the attribute-certificate parser composes for its four GeneralNames-bearing fields — validating every element as a well-formed GeneralName (rejecting a bad tag, a wrong primitive/constructed form, a non-IA5 string, or a mis-sized iPAddress) rather than surfacing the sequence as opaque bytes. It handles both a bare universal SEQUENCE OF GeneralName and a context-tagged IMPLICIT GeneralNames.
|
|
15
|
+
- The OID registry gains the RFC 5755 attribute-certificate object identifiers: the id-aca attribute-type family (authenticationInfo / accessIdentity / chargingIdentity / group), id-at-role and id-at-clearance, the id-ce-targetInformation and id-ce-noRevAvail extensions, and the id-pe-ac-auditIdentity / id-pe-aaControls / id-pe-ac-proxying private extensions — so a parsed attribute certificate's attributes and extensions resolve by name.
|
|
16
|
+
|
|
17
|
+
## v0.1.13 — 2026-07-05
|
|
18
|
+
|
|
19
|
+
An RFC 3161 timestamp parser joins the pki.schema family.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- pki.schema.tsp — an RFC 3161 timestamp parser. pki.schema.tsp.parse turns a DER Buffer or PEM into a TimeStampResp ({ status, statusString, failInfo, timeStampToken }) with the granted-carries-token / rejected-carries-none coupling enforced (tsp/missing-token, tsp/unexpected-token) and the PKIFailureInfo named bits decoded. pki.schema.tsp.parseToken parses a TimeStampToken by composing pki.schema.cms.parse and asserting the id-ct-TSTInfo content type (tsp/wrong-econtent-type), attached content (tsp/detached-token), and the single (TSA) signer (tsp/multi-signer), returning the decoded TSTInfo plus the signer material. pki.schema.tsp.parseTstInfo decodes a bare TSTInfo. The TSTInfo mandatory version-1, the GeneralizedTime-only genTime, the accuracy 1..999 range, the ordering DEFAULT FALSE omission, and the PKIStatus 0..5 range are all enforced fail-closed. pki.schema.parse detect-and-routes a TimeStampResp.
|
|
24
|
+
- The codec and schema engine gain three composable primitives that TSP required: pki.asn1.read.integerImplicit / pki.schema.engine.implicitInteger(tag) (a context-tagged IMPLICIT INTEGER, for the Accuracy millis / micros fields); pki.schema.engine.implicitSeqOf(tag, item) (an order-preserving IMPLICIT SEQUENCE OF, the sibling of implicitSetOf without the SET ordering rule, for the extensions field); and RFC 3161 / X.690 §11.7 fractional-seconds GeneralizedTime support in pki.asn1.read.time (a '.'-separated, trailing-zero-free, Z-terminated fraction, surfaced at millisecond precision).
|
|
25
|
+
- The OID registry gains the id-kp extended-key-purpose family (including id-kp-timeStamping) and the id-aa S/MIME authenticated-attribute family (signingCertificate / signingCertificateV2 / timeStampToken), so a parsed TSA certificate's key purpose and a signer's ESS binding attribute resolve by name.
|
|
26
|
+
|
|
7
27
|
## v0.1.12 — 2026-07-05
|
|
8
28
|
|
|
9
29
|
SLH-DSA object identifiers corrected and completed to all twelve FIPS 205 parameter sets.
|
package/README.md
CHANGED
|
@@ -197,9 +197,11 @@ is callable today; nothing below is a stub.
|
|
|
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
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
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` |
|
|
200
|
+
| `pki.schema.tsp` | Parse DER / PEM RFC 3161 timestamp responses and tokens — 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`, `parseTstInfo`, `parseToken`, `pemDecode` |
|
|
201
|
+
| `pki.schema.attrcert` | Parse DER / PEM RFC 5755 attribute certificates — the holder and issuer identities (validated GeneralNames), the validity window (real `Date`s), the privilege attributes (role / group / clearance) and extensions, with the raw signed region for a verifier; the obsolete v1 form is recognized and deferred, fail-closed — `parse`, `pemDecode` |
|
|
200
202
|
| `pki.schema.engine` | The declarative ASN.1 structure-schema engine every format parser composes — `walk` plus the schema combinators |
|
|
201
203
|
| `pki.C` / `pki.constants` | Version-stable constants — functional scale helpers (`C.TIME.*`, `C.BYTES.*`), codec `LIMITS`, `version` |
|
|
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 |
|
|
204
|
+
| `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError` / `Asn1Error` / `OidError` / `PemError` / `CertificateError` / `CrlError` / `CsrError` / `Pkcs8Error` / `CmsError` / `OcspError` / `TspError`, each carrying a stable `code` in `domain/reason` form |
|
|
203
205
|
| `pki` CLI | `pki version`, `pki oid <dotted\|name>`, `pki parse <cert>` |
|
|
204
206
|
|
|
205
207
|
### CLI
|
|
@@ -233,26 +235,34 @@ reintroduce the bug class it prevents. Adding a format is a schema declaration
|
|
|
233
235
|
plus a documentation comment block, not new parse logic.
|
|
234
236
|
|
|
235
237
|
```
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
238
|
+
┌─ Detect + route ────────────────────────────────────────────────────────┐
|
|
239
|
+
│ pki.schema.parse — inspect the DER root, route to the matching sibling │
|
|
240
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
241
|
+
│
|
|
242
|
+
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ Format
|
|
243
|
+
│ x509 │ │ crl │ │ csr │ │ pkcs8 │ parsers
|
|
244
|
+
└────────┘ └────────┘ └────────┘ └────────┘ (siblings)
|
|
245
|
+
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
|
|
246
|
+
│ cms │ │ ocsp │ │ tsp │ │attrcert│
|
|
247
|
+
└────────┘ └────────┘ └────────┘ └────────┘
|
|
248
|
+
│ every sibling composes ↓
|
|
249
|
+
┌─ Shared structure ──────────────────────────────────────────────────────┐
|
|
250
|
+
│ pki.schema.engine — walk + combinators (positional reads, tag order, │
|
|
251
|
+
│ SET-OF uniqueness, arity, typed errors) · the PKIX sub-schemas — │
|
|
252
|
+
│ AlgorithmIdentifier · Name · Extension · a bounded version reader — │
|
|
253
|
+
│ and the one coerce → decode → walk parse-entry (PEM cap + DER wrap). │
|
|
254
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
255
|
+
│ built on ↓
|
|
256
|
+
┌─ Foundation ────────────────────────────────────────────────────────────┐
|
|
257
|
+
│ pki.asn1 — strict, bounded DER codec · pki.oid — two-way, PQC-seeded │
|
|
258
|
+
│ OID registry · pki.errors — the PkiError domain/reason taxonomy · │
|
|
259
|
+
│ pki.C — version-stable LIMITS + scale constants. │
|
|
260
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
261
|
+
|
|
262
|
+
┌─ Crypto ────────────────────────────────────────────────────────────────┐
|
|
263
|
+
│ pki.webcrypto ──▶ node:crypto — a W3C SubtleCrypto engine: the │
|
|
264
|
+
│ classical set + post-quantum ML-DSA / SLH-DSA + ML-KEM key generation. │
|
|
265
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
256
266
|
```
|
|
257
267
|
|
|
258
268
|
**Foundation.** The strict, bounded DER codec (`pki.asn1`), the two-way OID
|
|
@@ -266,7 +276,7 @@ bounded version reader, and the single coerce → decode → walk parse-entry th
|
|
|
266
276
|
every format's `parse` is bound to. Because input coercion, the PEM size cap, and
|
|
267
277
|
the DER-decode wrapping live here once, a format cannot diverge on a guard.
|
|
268
278
|
|
|
269
|
-
**Format parsers.** `x509`, `crl`, `csr`, `pkcs8`, `cms`, and `
|
|
279
|
+
**Format parsers.** `x509`, `crl`, `csr`, `pkcs8`, `cms`, `ocsp`, `tsp`, and `attrcert` are siblings:
|
|
270
280
|
each is a schema declaration composed from the shared pieces, emitting its own
|
|
271
281
|
typed `domain/reason` error codes. `pki.schema.parse` inspects a decoded root and
|
|
272
282
|
detect-and-routes to the matching sibling; the detectors are mutually exclusive by
|
package/lib/asn1-der.js
CHANGED
|
@@ -318,6 +318,18 @@ function readEnumerated(node) {
|
|
|
318
318
|
return _readIntegerLikeContent(node, "ENUMERATED", "readEnumerated");
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// Read a [tag] IMPLICIT INTEGER — a context-class PRIMITIVE node whose content is an
|
|
322
|
+
// integer body (the IMPLICIT tag replaces the universal INTEGER tag). Same minimal
|
|
323
|
+
// two's-complement content rules as a universal INTEGER. The integer counterpart of
|
|
324
|
+
// readBitStringImplicit, for the RFC 3161 Accuracy millis [0] / micros [1] fields.
|
|
325
|
+
function readIntegerImplicit(node, tag) {
|
|
326
|
+
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
327
|
+
throw new Asn1Error("asn1/unexpected-tag", "readIntegerImplicit: expected context tag [" + tag +
|
|
328
|
+
"], got " + node.tagClass + "/" + node.tagNumber);
|
|
329
|
+
}
|
|
330
|
+
return _readIntegerLikeContent(node, "INTEGER", "readIntegerImplicit");
|
|
331
|
+
}
|
|
332
|
+
|
|
321
333
|
// The BIT STRING content decoder (the leading unused-bit octet + body, with the
|
|
322
334
|
// DER zero-pad rule). Shared by the universal reader and the IMPLICIT
|
|
323
335
|
// context-tagged reader — the caller asserts the tag first.
|
|
@@ -533,8 +545,17 @@ function _decodeUtf32be(buf) {
|
|
|
533
545
|
|
|
534
546
|
var UTC_RE = /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/;
|
|
535
547
|
var GEN_RE = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/;
|
|
536
|
-
|
|
537
|
-
|
|
548
|
+
// The X.690 §11.7 DER fractional-seconds GeneralizedTime profile: seconds present,
|
|
549
|
+
// a `.` decimal separator (never `,`), a non-empty fraction, Z-terminated. The
|
|
550
|
+
// trailing-zeros rule (§11.7.3) is enforced separately. Used by RFC 3161 genTime.
|
|
551
|
+
var GEN_FRAC_RE = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d+)Z$/;
|
|
552
|
+
|
|
553
|
+
// readTime(node, opts?) — opts.allowFractional enables the RFC 3161 / X.690 §11.7
|
|
554
|
+
// fractional-seconds GeneralizedTime profile. Fractional is OFF by default because
|
|
555
|
+
// most profiles (RFC 5280 certificate/CRL Validity, §4.1.2.5.2) require the integer-
|
|
556
|
+
// second YYYYMMDDHHMMSSZ form and MUST NOT include a fraction; only callers that
|
|
557
|
+
// permit sub-second precision (TSP genTime) pass allowFractional.
|
|
558
|
+
function readTime(node, opts) {
|
|
538
559
|
if (node.tagClass !== "universal") throw new Asn1Error("asn1/expected-time", "readTime: not a universal time type");
|
|
539
560
|
_expectPrimitive(node, "readTime");
|
|
540
561
|
var s = node.content.toString("latin1");
|
|
@@ -547,7 +568,19 @@ function readTime(node) {
|
|
|
547
568
|
year += (year < 50) ? 2000 : 1900;
|
|
548
569
|
} else if (node.tagNumber === TAGS.GENERALIZED_TIME) {
|
|
549
570
|
m = GEN_RE.exec(s);
|
|
550
|
-
if (!m)
|
|
571
|
+
if (!m) {
|
|
572
|
+
if (!(opts && opts.allowFractional)) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime must be YYYYMMDDHHMMSSZ, got " + JSON.stringify(s));
|
|
573
|
+
m = GEN_FRAC_RE.exec(s);
|
|
574
|
+
if (!m) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime must be YYYYMMDDHHMMSS[.fraction]Z, got " + JSON.stringify(s));
|
|
575
|
+
// X.690 §11.7.3 — the fractional part MUST NOT end in a zero (and is non-empty
|
|
576
|
+
// by the regex). ".5Z" is valid; ".50Z" and ".500Z" are non-canonical. X.690
|
|
577
|
+
// §11.7 places NO cap on the fraction length (RFC 3161 §2.4.2 gives the 5-digit
|
|
578
|
+
// example 19990609001326.34352Z), so a fraction of any length is accepted; the
|
|
579
|
+
// returned Date is millisecond-precision, and a caller needing the exact fraction
|
|
580
|
+
// reads it from node.content (the raw bytes, surfaced losslessly like the OID /
|
|
581
|
+
// serial paths).
|
|
582
|
+
if (m[7].charAt(m[7].length - 1) === "0") throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime fraction must not have trailing zeros, got " + JSON.stringify(s));
|
|
583
|
+
}
|
|
551
584
|
year = parseInt(m[1], 10);
|
|
552
585
|
} else {
|
|
553
586
|
throw new Asn1Error("asn1/expected-time", "readTime: tag " + node.tagNumber + " is not a time type");
|
|
@@ -562,9 +595,12 @@ function readTime(node) {
|
|
|
562
595
|
// GeneralizedTime year below 100 (0099 -> 1999). setUTCFullYear(year, ...)
|
|
563
596
|
// takes the year literally and uses that year's own calendar, so years
|
|
564
597
|
// below 100 and proleptic leap years are handled correctly.
|
|
598
|
+
// A fractional GeneralizedTime carries sub-second precision; surface it on the
|
|
599
|
+
// Date at millisecond resolution (the first three fraction digits, zero-padded).
|
|
600
|
+
var ms = (node.tagNumber === TAGS.GENERALIZED_TIME && m[7]) ? parseInt((m[7] + "000").slice(0, 3), 10) : 0;
|
|
565
601
|
var d = new Date(0);
|
|
566
602
|
d.setUTCFullYear(year, month - 1, day);
|
|
567
|
-
d.setUTCHours(hour, min, sec,
|
|
603
|
+
d.setUTCHours(hour, min, sec, ms);
|
|
568
604
|
if (isNaN(d.getTime())) throw new Asn1Error("asn1/bad-time", "unparseable time " + JSON.stringify(s));
|
|
569
605
|
// Date/setUTC* silently roll over out-of-range fields (Feb 30 -> Mar 2,
|
|
570
606
|
// month 13 -> next Jan, hour 25 -> +1 day). Reject unless every component
|
|
@@ -818,6 +854,7 @@ module.exports = {
|
|
|
818
854
|
read: {
|
|
819
855
|
boolean: readBoolean,
|
|
820
856
|
integer: readInteger,
|
|
857
|
+
integerImplicit: readIntegerImplicit,
|
|
821
858
|
enumerated: readEnumerated,
|
|
822
859
|
bitString: readBitString,
|
|
823
860
|
bitStringImplicit: readBitStringImplicit,
|
package/lib/framework-error.js
CHANGED
|
@@ -145,6 +145,16 @@ var CmsError = defineClass("CmsError", { withCause: true });
|
|
|
145
145
|
// (OCSPRequest / OCSPResponse — RFC 6960 §4). Carries the leaf fault as `.cause`.
|
|
146
146
|
var OcspError = defineClass("OcspError", { withCause: true });
|
|
147
147
|
|
|
148
|
+
// TspError — a byte sequence that is not a well-formed RFC 3161 timestamp token,
|
|
149
|
+
// TSTInfo, or TimeStampResp. Carries the underlying leaf fault as `.cause`.
|
|
150
|
+
var TspError = defineClass("TspError", { withCause: true });
|
|
151
|
+
|
|
152
|
+
// AttrCertError — a byte sequence that is not a well-formed X.509 Attribute
|
|
153
|
+
// Certificate (AttributeCertificate / AttributeCertificateInfo — RFC 5755 §4),
|
|
154
|
+
// or a recognized-and-deferred legacy AttributeCertificateV1. Carries the
|
|
155
|
+
// underlying leaf fault as `.cause`.
|
|
156
|
+
var AttrCertError = defineClass("AttrCertError", { withCause: true });
|
|
157
|
+
|
|
148
158
|
module.exports = {
|
|
149
159
|
PkiError: PkiError,
|
|
150
160
|
defineClass: defineClass,
|
|
@@ -159,4 +169,6 @@ module.exports = {
|
|
|
159
169
|
Pkcs8Error: Pkcs8Error,
|
|
160
170
|
CmsError: CmsError,
|
|
161
171
|
OcspError: OcspError,
|
|
172
|
+
TspError: TspError,
|
|
173
|
+
AttrCertError: AttrCertError,
|
|
162
174
|
};
|
package/lib/oid.js
CHANGED
|
@@ -49,7 +49,9 @@ var FAMILIES = {
|
|
|
49
49
|
attributeType: { base: [2, 5, 4], of: {
|
|
50
50
|
commonName: 3, surname: 4, serialNumber: 5, countryName: 6,
|
|
51
51
|
localityName: 7, stateOrProvinceName: 8, streetAddress: 9,
|
|
52
|
-
organizationName: 10, organizationalUnitName: 11, title: 12, givenName: 42
|
|
52
|
+
organizationName: 10, organizationalUnitName: 11, title: 12, givenName: 42,
|
|
53
|
+
// RFC 5755 §4.4 attribute-certificate attribute types.
|
|
54
|
+
clearance: 55, role: 72 } },
|
|
53
55
|
|
|
54
56
|
// RFC 5280 certificate extensions.
|
|
55
57
|
certExtension: { base: [2, 5, 29], of: {
|
|
@@ -60,10 +62,28 @@ var FAMILIES = {
|
|
|
60
62
|
issuingDistributionPoint: 28, certificateIssuer: 29,
|
|
61
63
|
nameConstraints: 30, cRLDistributionPoints: 31,
|
|
62
64
|
certificatePolicies: 32, authorityKeyIdentifier: 35, extKeyUsage: 37,
|
|
63
|
-
freshestCRL: 46
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
freshestCRL: 46,
|
|
66
|
+
// RFC 5755 attribute-certificate extensions (§4.3.2, §4.3.6).
|
|
67
|
+
targetInformation: 55, noRevAvail: 56 } },
|
|
68
|
+
|
|
69
|
+
// PKIX private extensions on the id-pe arc (authorityInfoAccess et al, plus the
|
|
70
|
+
// RFC 5755 attribute-certificate id-pe extensions: ac-auditIdentity §4.3.1,
|
|
71
|
+
// aaControls §7.4, ac-proxying §7.2).
|
|
72
|
+
pkixAccess: { base: [1, 3, 6, 1, 5, 5, 7, 1], of: {
|
|
73
|
+
authorityInfoAccess: 1, acAuditIdentity: 4, aaControls: 6, acProxying: 10 } },
|
|
74
|
+
|
|
75
|
+
// id-aca — RFC 5755 attribute-certificate attribute types on the id-pkix 10 arc:
|
|
76
|
+
// authenticationInfo §4.4.1 .. group §4.4.4, plus encAttrs §7.1 (the encrypted-
|
|
77
|
+
// attribute wrapper, syntax ContentInfo). { id-aca 5 } is reserved.
|
|
78
|
+
idAca: { base: [1, 3, 6, 1, 5, 5, 7, 10], of: {
|
|
79
|
+
authenticationInfo: 1, accessIdentity: 2, chargingIdentity: 3, group: 4, encAttrs: 6 } },
|
|
80
|
+
|
|
81
|
+
// The legacy RFC 3281 id-at-clearance on the X.501 selected-attribute-types arc.
|
|
82
|
+
// RFC 5755 §4.4.6 says implementations MUST NOT OUTPUT this form but SHOULD ACCEPT
|
|
83
|
+
// it for decoding, so it is registered as an alias of "clearance". The canonical
|
|
84
|
+
// name -> OID reverse stays the RFC 5755 attributeType-arc 2.5.4.55 (registered
|
|
85
|
+
// first under attributeType, so it wins the reverse mapping).
|
|
86
|
+
selectedAttrType: { base: [2, 5, 1, 5], of: { clearance: 55 } },
|
|
67
87
|
|
|
68
88
|
// PKIX Access Descriptor methods (id-ad, RFC 5280 §4.2.2.1/§4.2.2.2). id-ad-ocsp
|
|
69
89
|
// is the arc the OCSP responder OIDs hang under; id-ad-caIssuers names the AIA
|
|
@@ -79,6 +99,11 @@ var FAMILIES = {
|
|
|
79
99
|
ocspBasic: 1, ocspNonce: 2, ocspCrl: 3, ocspResponse: 4, ocspNoCheck: 5,
|
|
80
100
|
ocspArchiveCutoff: 6, ocspServiceLocator: 7, ocspPrefSigAlgs: 8, ocspExtendedRevoke: 9 } },
|
|
81
101
|
|
|
102
|
+
// PKIX extended key purposes (id-kp, RFC 5280 §4.2.1.12). timeStamping is required
|
|
103
|
+
// — critical and sole — on an RFC 3161 TSA signing certificate (§2.3).
|
|
104
|
+
pkixKp: { base: [1, 3, 6, 1, 5, 5, 7, 3], of: {
|
|
105
|
+
serverAuth: 1, clientAuth: 2, codeSigning: 3, emailProtection: 4, timeStamping: 8, ocspSigning: 9 } },
|
|
106
|
+
|
|
82
107
|
// PKCS#1 RSA public-key + RSASSA signature algorithms.
|
|
83
108
|
rsa: { base: [1, 2, 840, 113549, 1, 1], of: {
|
|
84
109
|
rsaEncryption: 1, rsassaPss: 10, sha256WithRSAEncryption: 11,
|
|
@@ -98,6 +123,12 @@ var FAMILIES = {
|
|
|
98
123
|
// S/MIME content types on the PKCS#9 smime arc (RFC 5652, RFC 3161): id-ct.
|
|
99
124
|
smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4 } },
|
|
100
125
|
|
|
126
|
+
// S/MIME authenticated attributes (id-aa, RFC 2634 / RFC 5035 / RFC 5816). The ESS
|
|
127
|
+
// signing-certificate attributes bind a CMS / TSP SignerInfo to its signing cert;
|
|
128
|
+
// signingCertificateV2 (ESSCertIDv2) carries a non-SHA-1 cert hash (RFC 5816 §2.2.1).
|
|
129
|
+
smimeAa: { base: [1, 2, 840, 113549, 1, 9, 16, 2], of: {
|
|
130
|
+
signingCertificate: 12, timeStampToken: 14, signingCertificateV2: 47 } },
|
|
131
|
+
|
|
101
132
|
// ANSI X9.62 EC public key, named curve, and ECDSA signatures.
|
|
102
133
|
ansiX962: { base: [1, 2, 840, 10045], of: {
|
|
103
134
|
ecPublicKey: [2, 1], prime256v1: [3, 1, 7],
|
package/lib/schema-all.js
CHANGED
|
@@ -38,6 +38,8 @@ var csr = require("./schema-csr");
|
|
|
38
38
|
var pkcs8 = require("./schema-pkcs8");
|
|
39
39
|
var cms = require("./schema-cms");
|
|
40
40
|
var ocsp = require("./schema-ocsp");
|
|
41
|
+
var tsp = require("./schema-tsp");
|
|
42
|
+
var attrcert = require("./schema-attrcert");
|
|
41
43
|
var frameworkError = require("./framework-error");
|
|
42
44
|
|
|
43
45
|
var SchemaError = frameworkError.SchemaError;
|
|
@@ -66,12 +68,27 @@ var FORMATS = [
|
|
|
66
68
|
detect: cms.matches,
|
|
67
69
|
parse: function (input) { return cms.parse(input); },
|
|
68
70
|
},
|
|
71
|
+
{
|
|
72
|
+
// TimeStampResp ::= SEQUENCE { status PKIStatusInfo, timeStampToken OPTIONAL } —
|
|
73
|
+
// a SEQUENCE of 1-2 whose first child is a PKIStatusInfo SEQUENCE whose own first
|
|
74
|
+
// child is an INTEGER status. This detector is a strict REFINEMENT of the
|
|
75
|
+
// ocsp-request one (both are SEQUENCE-of-1-2 with a SEQUENCE first child), so it
|
|
76
|
+
// MUST be checked first: a tokenless TimeStampResp (a bare PKIStatusInfo) would
|
|
77
|
+
// otherwise be shadowed by ocsp-request, whereas an OCSPRequest's tbsRequest never
|
|
78
|
+
// leads with a universal INTEGER and so never matches here (RFC 3161 §2.4.2). A
|
|
79
|
+
// bare timestamp token is a CMS ContentInfo and routes to cms; a bare TSTInfo is
|
|
80
|
+
// an internal payload (reached via pki.schema.tsp.parseTstInfo).
|
|
81
|
+
name: "tsp",
|
|
82
|
+
module: tsp,
|
|
83
|
+
detect: tsp.matches,
|
|
84
|
+
parse: function (input) { return tsp.parse(input); },
|
|
85
|
+
},
|
|
69
86
|
{
|
|
70
87
|
// OCSPRequest ::= SEQUENCE { tbsRequest SEQUENCE, optionalSignature [0] EXPLICIT
|
|
71
88
|
// OPTIONAL } — a SEQUENCE of 1-2 whose first child is the tbsRequest SEQUENCE.
|
|
72
89
|
// 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)
|
|
74
|
-
//
|
|
90
|
+
// (the trio is EXACTLY 3 children; an OCSPRequest is 1-2). Checked AFTER tsp,
|
|
91
|
+
// whose detector is a strict refinement (RFC 6960 §4.1.1).
|
|
75
92
|
name: "ocsp-request",
|
|
76
93
|
module: ocsp,
|
|
77
94
|
detect: ocsp.matchesRequest,
|
|
@@ -110,6 +127,27 @@ var FORMATS = [
|
|
|
110
127
|
detect: csr.matches,
|
|
111
128
|
parse: function (input) { return csr.parse(input); },
|
|
112
129
|
},
|
|
130
|
+
{
|
|
131
|
+
// AttributeCertificate ::= SEQUENCE { acinfo, signatureAlgorithm, signatureValue }
|
|
132
|
+
// — the signed-envelope trio, distinguished from a certificate / CRL by an acinfo
|
|
133
|
+
// that LEADS WITH a bare INTEGER version and carries a 2-GeneralizedTime validity
|
|
134
|
+
// at children[5]. Registered ahead of crl / x509 (most-specific-first): a legacy v1
|
|
135
|
+
// AC would otherwise satisfy x509.matches's Validity probe (RFC 5755 §4.1).
|
|
136
|
+
name: "attrcert",
|
|
137
|
+
module: attrcert,
|
|
138
|
+
detect: attrcert.matches,
|
|
139
|
+
parse: function (input) { return attrcert.parse(input); },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
// AttributeCertificateV1 (X.509-1997, RFC 5652 §10.2) — the obsolete predecessor
|
|
143
|
+
// whose acInfo LEADS WITH the subject CHOICE (context [0]/[1]). Recognized and
|
|
144
|
+
// deferred with a precise attrcert/legacy-v1-not-supported rather than routed to a
|
|
145
|
+
// wrong format. children[1] == SEQUENCE keeps it disjoint from a v3 certificate.
|
|
146
|
+
name: "attrcert-v1",
|
|
147
|
+
module: attrcert,
|
|
148
|
+
detect: attrcert.matchesV1,
|
|
149
|
+
parse: function (input) { return attrcert.parseV1(input); },
|
|
150
|
+
},
|
|
113
151
|
{
|
|
114
152
|
// CertificateList ::= SEQUENCE { tbsCertList, signatureAlgorithm,
|
|
115
153
|
// signatureValue } — the same outer shape as a certificate, distinguished
|
|
@@ -141,7 +179,7 @@ var FORMATS = [
|
|
|
141
179
|
* The names of every registered format, in detection order.
|
|
142
180
|
*
|
|
143
181
|
* @example
|
|
144
|
-
* pki.schema.all(); // → ["cms", "ocsp-request", "ocsp-response", "pkcs8", "csr", "crl", "x509"]
|
|
182
|
+
* pki.schema.all(); // → ["cms", "tsp", "ocsp-request", "ocsp-response", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
|
|
145
183
|
*/
|
|
146
184
|
function all() { return FORMATS.map(function (f) { return f.name; }); }
|
|
147
185
|
|
|
@@ -185,6 +223,8 @@ module.exports = {
|
|
|
185
223
|
pkcs8: { parse: pkcs8.parse, parseEncrypted: pkcs8.parseEncrypted, pemDecode: pkcs8.pemDecode, pemEncode: pkcs8.pemEncode },
|
|
186
224
|
cms: { parse: cms.parse, pemDecode: cms.pemDecode, pemEncode: cms.pemEncode },
|
|
187
225
|
ocsp: { parseRequest: ocsp.parseRequest, parseResponse: ocsp.parseResponse, pemDecode: ocsp.pemDecode },
|
|
226
|
+
tsp: { parse: tsp.parse, parseTstInfo: tsp.parseTstInfo, parseToken: tsp.parseToken, pemDecode: tsp.pemDecode },
|
|
227
|
+
attrcert: { parse: attrcert.parse, pemDecode: attrcert.pemDecode },
|
|
188
228
|
all: all,
|
|
189
229
|
parse: parse,
|
|
190
230
|
};
|