@blamejs/pki 0.4.15 → 0.5.1
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 +51 -1
- package/MIGRATING.md +2 -2
- package/README.md +142 -137
- package/index.js +4 -0
- package/lib/acme.js +73 -1
- package/lib/asn1-der.js +2 -0
- package/lib/attrcert-sign.js +4 -0
- package/lib/cbor-det.js +32 -16
- package/lib/cmc-build.js +880 -0
- package/lib/cmc-verify.js +657 -0
- package/lib/cmp-build.js +8 -7
- package/lib/cmp-verify.js +11 -1
- package/lib/cms-sign.js +170 -8
- package/lib/cms-verify.js +80 -14
- package/lib/crl-sign.js +22 -0
- package/lib/crmf-sign.js +5 -2
- package/lib/csr-sign.js +3 -0
- package/lib/ct.js +72 -0
- package/lib/est.js +828 -32
- package/lib/framework-error.js +13 -0
- package/lib/guard-bytes.js +37 -1
- package/lib/guard-range.js +23 -1
- package/lib/http-transport.js +9 -3
- package/lib/inspect.js +28 -5
- package/lib/jose.js +64 -6
- package/lib/lint.js +4 -0
- package/lib/merkle.js +5 -5
- package/lib/ocsp.js +139 -11
- package/lib/oid.js +69 -1
- package/lib/path-validate.js +27 -4
- package/lib/pkcs12-build.js +12 -0
- package/lib/schema-all.js +19 -1
- package/lib/schema-attrcert.js +27 -0
- package/lib/schema-c509.js +6 -0
- package/lib/schema-cmc.js +791 -0
- package/lib/schema-cmp.js +25 -0
- package/lib/schema-cms.js +17 -1
- package/lib/schema-crl.js +23 -1
- package/lib/schema-crmf.js +13 -0
- package/lib/schema-csr.js +11 -0
- package/lib/schema-csrattrs.js +6 -0
- package/lib/schema-engine.js +6 -2
- package/lib/schema-ocsp.js +41 -0
- package/lib/schema-pkcs12.js +16 -0
- package/lib/schema-pkcs8.js +8 -0
- package/lib/schema-smime.js +4 -4
- package/lib/schema-tsp.js +32 -1
- package/lib/schema-x509.js +14 -1
- package/lib/shbs.js +12 -4
- package/lib/sigstore.js +62 -6
- package/lib/smime.js +28 -7
- package/lib/tls-cert-compress.js +15 -3
- package/lib/trust.js +27 -4
- package/lib/tsp-sign.js +41 -6
- package/lib/vendor/README.md +19 -19
- package/lib/webauthn.js +895 -26
- package/lib/webcrypto.js +35 -2
- package/lib/x509-sign.js +3 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/ocsp.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* ML-DSA / SLH-DSA per the responder key. Verification composes the SAME hardened responder-
|
|
16
16
|
* authorization + signature + currency gates `pki.path.ocspChecker` runs -- there is no weaker
|
|
17
17
|
* second verify path. Fail-closed: `verify` returns a `"unknown"` verdict (never a silent accept)
|
|
18
|
-
* for any unmet gate
|
|
18
|
+
* for any unmet gate, with ONE scoped exception -- a request-nonce mismatch downgrades only a
|
|
19
|
+
* `good` to `"unknown"`, leaving a signed, current, authorized `revoked` reported as `revoked`
|
|
20
|
+
* with `nonceMatched: false` (see `verify`). Malformed input throws a typed `OcspError`.
|
|
19
21
|
* @spec RFC 6960, RFC 9654, RFC 5019
|
|
20
22
|
* @card Build, sign, and verify RFC 6960 OCSP requests + responses (a responder + a relying party).
|
|
21
23
|
*/
|
|
@@ -103,7 +105,7 @@ function _buildCertID(cert, issuer, hashName) {
|
|
|
103
105
|
* @primitive pki.ocsp.buildRequest
|
|
104
106
|
* @signature pki.ocsp.buildRequest(query, opts?) -> Buffer | string
|
|
105
107
|
* @since 0.2.22
|
|
106
|
-
* @status
|
|
108
|
+
* @status stable
|
|
107
109
|
* @spec RFC 6960, RFC 9654, RFC 5019
|
|
108
110
|
* @related pki.ocsp.verify, pki.schema.ocsp.parseRequest
|
|
109
111
|
*
|
|
@@ -121,6 +123,15 @@ function _buildCertID(cert, issuer, hashName) {
|
|
|
121
123
|
* profile `"lightweight"` -- one Request, SHA-1 CertID, nonce-only extensions (RFC 5019).
|
|
122
124
|
* pem emit a PEM `OCSP REQUEST` string instead of DER.
|
|
123
125
|
* @example
|
|
126
|
+
* var ca = await pki.key.generate("Ed25519");
|
|
127
|
+
* var caKey = await pki.key.export(ca.privateKey);
|
|
128
|
+
* var caDer = await pki.x509.sign({ subject: "Example CA", subjectPublicKey: await pki.key.export(ca.publicKey),
|
|
129
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
130
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectKeyIdentifier: true } }, { key: caKey });
|
|
131
|
+
* var leaf = await pki.key.generate("Ed25519");
|
|
132
|
+
* var leafDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(leaf.publicKey),
|
|
133
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
134
|
+
* { cert: caDer, key: caKey });
|
|
124
135
|
* var der = await pki.ocsp.buildRequest({ cert: leafDer, issuer: caDer }, { nonce: true });
|
|
125
136
|
*/
|
|
126
137
|
function buildRequest(query, opts) {
|
|
@@ -195,7 +206,7 @@ function _normCertDer(cert, what) {
|
|
|
195
206
|
* @primitive pki.ocsp.sign
|
|
196
207
|
* @signature pki.ocsp.sign(responseData, responder, opts?) -> Promise<Buffer | string>
|
|
197
208
|
* @since 0.2.22
|
|
198
|
-
* @status
|
|
209
|
+
* @status stable
|
|
199
210
|
* @spec RFC 6960, RFC 9654
|
|
200
211
|
* @related pki.ocsp.verify, pki.ocsp.buildErrorResponse
|
|
201
212
|
*
|
|
@@ -212,6 +223,18 @@ function _normCertDer(cert, what) {
|
|
|
212
223
|
* embedCert `false` to omit certs [0] (a direct-CA response the client already trusts).
|
|
213
224
|
* pem emit a PEM `OCSP RESPONSE` string instead of DER.
|
|
214
225
|
* @example
|
|
226
|
+
* var ca = await pki.key.generate("Ed25519");
|
|
227
|
+
* var responderPkcs8 = await pki.key.export(ca.privateKey);
|
|
228
|
+
* // the issuing CA responds directly here, so its own certificate is the responder's
|
|
229
|
+
* var caDer = await pki.x509.sign({ subject: "Example CA", subjectPublicKey: await pki.key.export(ca.publicKey),
|
|
230
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
231
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectKeyIdentifier: true } },
|
|
232
|
+
* { key: responderPkcs8 });
|
|
233
|
+
* var responderCertDer = caDer;
|
|
234
|
+
* var leaf = await pki.key.generate("Ed25519");
|
|
235
|
+
* var leafDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(leaf.publicKey),
|
|
236
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
237
|
+
* { cert: caDer, key: responderPkcs8 });
|
|
215
238
|
* var resp = await pki.ocsp.sign(
|
|
216
239
|
* { responderID: "byName", responses: [{ cert: leafDer, issuer: caDer, status: "good" }] },
|
|
217
240
|
* { cert: responderCertDer, key: responderPkcs8 });
|
|
@@ -222,6 +245,43 @@ function sign(responseData, responder, opts) {
|
|
|
222
245
|
if (!responder || responder.cert == null || responder.key == null) throw _err("ocsp/bad-input", "a responder must be { cert, key }");
|
|
223
246
|
var respCertDer = _normCertDer(responder.cert, "the responder certificate");
|
|
224
247
|
var respCert = _certOf(respCertDer, "the responder certificate");
|
|
248
|
+
// Read with the certificate, not after the awaits below. The ResponderID and the
|
|
249
|
+
// embedded certificate are both fixed from `respCert` here; the signature is made
|
|
250
|
+
// several promise turns later, so a `responder` whose key was replaced in between
|
|
251
|
+
// would produce a response naming one responder and signed by another key.
|
|
252
|
+
//
|
|
253
|
+
// Capturing the REFERENCE only closes half of that: it stops `responder.key = other`,
|
|
254
|
+
// but PKCS#8 arrives as caller-owned BYTES and a composite key as a caller-owned
|
|
255
|
+
// { mldsa, trad } object, either of which can be rewritten in place across the same
|
|
256
|
+
// gap with the same result -- a response carrying this responder's ID and certificate
|
|
257
|
+
// over a signature made by a different key, which no relying party can verify. So the
|
|
258
|
+
// material itself is snapshotted here, not just the binding to it. A CryptoKey is
|
|
259
|
+
// opaque and a PEM string is immutable, so both are already safe by reference.
|
|
260
|
+
var ownedKeyBytes = [];
|
|
261
|
+
var responderKey = _snapshotSignerKey(responder.key, ownedKeyBytes);
|
|
262
|
+
// The copy is cleared on the failing path as well as the succeeding one -- a wrong or
|
|
263
|
+
// malformed key is the case an attacker can force, so a success-only wipe would keep the
|
|
264
|
+
// secret exactly when it matters. The wipe covers the WHOLE window the copy exists, not
|
|
265
|
+
// just the signing call: the copy is taken before the response list, the responder ID, the
|
|
266
|
+
// SingleResponses, the dates, the nonce and the signature scheme are validated, and every
|
|
267
|
+
// one of those can fail. Attaching cleanup to signing alone leaves the secret in the heap
|
|
268
|
+
// on each of those earlier exits, which are the easiest ones for a caller to reach.
|
|
269
|
+
function _wipeOwnedKey() {
|
|
270
|
+
if (ownedKeyBytes.length) guard.secret.zeroizeAll(ownedKeyBytes, OcspError, "ocsp/bad-input", "the responder key copy");
|
|
271
|
+
ownedKeyBytes.length = 0;
|
|
272
|
+
}
|
|
273
|
+
var pending;
|
|
274
|
+
try {
|
|
275
|
+
pending = _signResponse(responseData, responder, respCert, respCertDer, responderKey, opts);
|
|
276
|
+
} catch (e) { _wipeOwnedKey(); throw e; }
|
|
277
|
+
return pending.then(function (out) { _wipeOwnedKey(); return out; },
|
|
278
|
+
function (e) { _wipeOwnedKey(); throw e; });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Everything from the key snapshot onward, so a single cleanup in the caller covers every
|
|
282
|
+
// exit -- sync throw and async rejection alike -- rather than each fallible step needing to
|
|
283
|
+
// remember. `responderKey` is the private copy; it is never the caller's object.
|
|
284
|
+
function _signResponse(responseData, responder, respCert, respCertDer, responderKey, opts) {
|
|
225
285
|
var responses = responseData.responses || [];
|
|
226
286
|
if (!responses.length) throw _err("ocsp/bad-input", "a response MUST include at least one SingleResponse (RFC 6960 sec. 4.2.1)");
|
|
227
287
|
|
|
@@ -234,7 +294,7 @@ function sign(responseData, responder, opts) {
|
|
|
234
294
|
if (respExts.length) rdChildren.push(b.explicit(1, b.sequence(respExts)));
|
|
235
295
|
var responseDataDer = b.sequence(rdChildren);
|
|
236
296
|
var scheme = signScheme.resolveSignScheme(respCert, { combinedRsaSig: true }, true, _signE);
|
|
237
|
-
return signScheme.signOverTbs(scheme,
|
|
297
|
+
return signScheme.signOverTbs(scheme, responderKey, responseDataDer, _signE).then(function (sig) {
|
|
238
298
|
var basicChildren = [responseDataDer, scheme.sigAlgId, b.bitString(sig, 0)];
|
|
239
299
|
if (opts.embedCert !== false) basicChildren.push(b.explicit(0, b.sequence([b.raw(respCertDer)])));
|
|
240
300
|
var responseBytes = b.sequence([b.oid(OID_OCSP_BASIC), b.octetString(b.sequence(basicChildren))]);
|
|
@@ -253,6 +313,36 @@ function _asDate(d) {
|
|
|
253
313
|
if (isNaN(dt.getTime())) throw _err("ocsp/bad-input", "an invalid date value " + JSON.stringify(d));
|
|
254
314
|
return dt;
|
|
255
315
|
}
|
|
316
|
+
// A private copy of whatever signer-key material the caller owns, taken at the entry
|
|
317
|
+
// point because signing happens several promise turns later (see `sign`). Bytes are
|
|
318
|
+
// copied; a composite { mldsa, trad } is rebuilt with each component copied, since
|
|
319
|
+
// rewriting a component is the same attack one level down. Anything else -- a CryptoKey,
|
|
320
|
+
// a PEM string -- is not caller-mutable in a way that changes the key, so it rides as-is
|
|
321
|
+
// rather than being coerced into a shape this function does not understand.
|
|
322
|
+
// `owned` collects every buffer THIS function allocated, so the caller can clear them once
|
|
323
|
+
// signing is done. A copy of a private key is a second copy of a secret, which is precisely
|
|
324
|
+
// what this project's secret-lifetime discipline exists to avoid -- so the copy that closes
|
|
325
|
+
// the aliasing window is wiped rather than left to the garbage collector. Only our own
|
|
326
|
+
// allocations are listed: the caller's key is never written to.
|
|
327
|
+
function _snapshotSignerKey(key, owned) {
|
|
328
|
+
if (Buffer.isBuffer(key) || key instanceof Uint8Array) {
|
|
329
|
+
var copy = guard.bytes.snapshot(key, OcspError, "ocsp/bad-input", "the responder key");
|
|
330
|
+
owned.push(copy);
|
|
331
|
+
return copy;
|
|
332
|
+
}
|
|
333
|
+
// The test is "is this a composite DESCRIPTOR", not "does it currently hold bytes". A
|
|
334
|
+
// composite whose components are both PEM strings carries nothing mutable inside it, but
|
|
335
|
+
// the OBJECT is still the caller's: reassigning `key.mldsa` after the call reaches the
|
|
336
|
+
// deferred signing operation just as rewriting a buffer would, and yields a response whose
|
|
337
|
+
// responder ID and embedded certificate describe one responder over a signature made by
|
|
338
|
+
// another key. So the container is always rebuilt, and each component is snapshotted by
|
|
339
|
+
// its own type -- bytes copied, an immutable PEM string passed through.
|
|
340
|
+
if (key && typeof key === "object" && (key.mldsa != null || key.trad != null)) {
|
|
341
|
+
return Object.assign({}, key, { mldsa: _snapshotSignerKey(key.mldsa, owned), trad: _snapshotSignerKey(key.trad, owned) });
|
|
342
|
+
}
|
|
343
|
+
return key;
|
|
344
|
+
}
|
|
345
|
+
|
|
256
346
|
function _responderID(rid, respCert) {
|
|
257
347
|
if (rid == null || rid === "byName") return Promise.resolve(b.explicit(1, b.raw(respCert.subject.bytes))); // byName [1] EXPLICIT Name
|
|
258
348
|
if (rid === "byKey") return _digest("SHA-1", _keyValue(respCert.subjectPublicKeyInfo.bytes)).then(function (kh) { return b.explicit(2, b.octetString(kh)); });
|
|
@@ -261,7 +351,10 @@ function _responderID(rid, respCert) {
|
|
|
261
351
|
function _buildSingleResponse(r, opts) {
|
|
262
352
|
r = r || {};
|
|
263
353
|
var certIdP;
|
|
264
|
-
|
|
354
|
+
// A pre-encoded CertID is spliced in RAW, so its type is checked rather than coerced:
|
|
355
|
+
// Buffer.from(20) would allocate twenty zero octets and emit a structurally broken
|
|
356
|
+
// CertID inside a response this responder then SIGNS.
|
|
357
|
+
if (r.certID != null) certIdP = Promise.resolve(b.raw(guard.bytes.view(r.certID, OcspError, "ocsp/bad-input", "a response entry's certID")));
|
|
265
358
|
else if (r.cert != null && r.issuer != null) certIdP = _buildCertID(_certOf(r.cert, "a response certificate"), _certOf(r.issuer, "a response issuer"), r.hashAlgorithm || "sha1");
|
|
266
359
|
else return Promise.reject(_err("ocsp/bad-input", "each response entry needs { certID } or { cert, issuer }"));
|
|
267
360
|
return certIdP.then(function (certID) {
|
|
@@ -292,7 +385,7 @@ function _certStatusNode(status, opts) {
|
|
|
292
385
|
* @primitive pki.ocsp.buildErrorResponse
|
|
293
386
|
* @signature pki.ocsp.buildErrorResponse(status) -> Buffer | string
|
|
294
387
|
* @since 0.2.22
|
|
295
|
-
* @status
|
|
388
|
+
* @status stable
|
|
296
389
|
* @spec RFC 6960
|
|
297
390
|
* @related pki.ocsp.sign
|
|
298
391
|
*
|
|
@@ -314,7 +407,7 @@ function buildErrorResponse(status) {
|
|
|
314
407
|
* @primitive pki.ocsp.verify
|
|
315
408
|
* @signature pki.ocsp.verify(response, opts) -> Promise<{ status, responderAuthorized, signatureValid, thisUpdate, nextUpdate, revocationReason?, nonceMatched?, reason }>
|
|
316
409
|
* @since 0.2.22
|
|
317
|
-
* @status
|
|
410
|
+
* @status stable
|
|
318
411
|
* @spec RFC 6960, RFC 9654, RFC 5019
|
|
319
412
|
* @related pki.path.ocspChecker, pki.ocsp.buildRequest
|
|
320
413
|
*
|
|
@@ -324,8 +417,16 @@ function buildErrorResponse(status) {
|
|
|
324
417
|
* to the target certificate under the CertID's own hashAlgorithm, checks currency
|
|
325
418
|
* (`thisUpdate`/`nextUpdate`), and -- when `opts.requestNonce` is supplied -- confirms the response
|
|
326
419
|
* nonce echoes it. This runs the SAME hardened gates `pki.path.ocspChecker` does. Fail-closed: an
|
|
327
|
-
* unauthorized, stale,
|
|
328
|
-
*
|
|
420
|
+
* unauthorized, stale, or CertID-mismatched response is a `"unknown"` verdict (never a silent
|
|
421
|
+
* accept); a malformed response's parse fault surfaces as the parser's `ocsp/*` / `asn1/*`.
|
|
422
|
+
*
|
|
423
|
+
* The request-nonce check is reported, and downgrades `good` ONLY. Every verdict carries
|
|
424
|
+
* `nonceMatched` (true / false / null when the client sent no nonce). An unmatched nonce turns a
|
|
425
|
+
* `good` into `"unknown"`, because a response that is not an answer to this request cannot be relied
|
|
426
|
+
* on to say the certificate is still fine. It does NOT touch `revoked`: revocation does not go stale
|
|
427
|
+
* the way non-revocation does, so discarding a signed, current, authorized `revoked` because it was
|
|
428
|
+
* replayed would hand a soft-failing caller the very certificate the responder refused. A replayed
|
|
429
|
+
* `revoked` is therefore reported as `revoked` with `nonceMatched: false`.
|
|
329
430
|
*
|
|
330
431
|
* @opts
|
|
331
432
|
* cert the target certificate (parsed, DER, or PEM) -- REQUIRED.
|
|
@@ -334,6 +435,19 @@ function buildErrorResponse(status) {
|
|
|
334
435
|
* requestNonce the nonce the client sent; when given, the response MUST echo it (constant-time).
|
|
335
436
|
* historicalMode defer a strictly-future revocation (report good) instead of revoking on skew.
|
|
336
437
|
* @example
|
|
438
|
+
* var ca = await pki.key.generate("Ed25519");
|
|
439
|
+
* var caKey = await pki.key.export(ca.privateKey);
|
|
440
|
+
* var caDer = await pki.x509.sign({ subject: "Example CA", subjectPublicKey: await pki.key.export(ca.publicKey),
|
|
441
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
442
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectKeyIdentifier: true } },
|
|
443
|
+
* { key: caKey });
|
|
444
|
+
* var leaf = await pki.key.generate("Ed25519");
|
|
445
|
+
* var leafDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(leaf.publicKey),
|
|
446
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
447
|
+
* { cert: caDer, key: caKey });
|
|
448
|
+
* var responseDer = await pki.ocsp.sign(
|
|
449
|
+
* { responderID: "byName", responses: [{ cert: leafDer, issuer: caDer, status: "good" }] },
|
|
450
|
+
* { cert: caDer, key: caKey });
|
|
337
451
|
* var res = await pki.ocsp.verify(responseDer, { cert: leafDer, issuer: caDer });
|
|
338
452
|
* res.status; // "good" | "revoked" | "unknown"
|
|
339
453
|
*/
|
|
@@ -350,14 +464,28 @@ function verify(response, opts) {
|
|
|
350
464
|
time = opts.time == null ? new Date() : _asDate(opts.time);
|
|
351
465
|
} catch (e) { return Promise.reject(e); }
|
|
352
466
|
return pathValidate.verifyOcspResponse(parsed, cert, issuerCert, time, { historicalMode: opts.historicalMode === true }).then(function (verdict) {
|
|
353
|
-
|
|
467
|
+
// A client that sent no nonce still gets the field, as null. Leaving it absent would make
|
|
468
|
+
// "not requested" indistinguishable from "the field is not there yet" for a consumer reading
|
|
469
|
+
// res.nonceMatched, and the three-state contract is the whole point: true bound, false not
|
|
470
|
+
// bound, null never asked. The verdict is copied rather than mutated, since it is the lower
|
|
471
|
+
// primitive's object and this layer does not own it.
|
|
472
|
+
if (opts.requestNonce == null) return Object.assign({}, verdict, { nonceMatched: null });
|
|
354
473
|
// A client that sent a nonce binds it (RFC 9654 / RFC 5019 sec. 4): a missing or mismatched
|
|
355
474
|
// response nonce fails the verdict closed, even if the status/signature were otherwise good.
|
|
356
475
|
var respNonce = _responseNonce(parsed);
|
|
357
476
|
var reqNonce = Buffer.isBuffer(opts.requestNonce) ? opts.requestNonce : (opts.requestNonce instanceof Uint8Array ? Buffer.from(opts.requestNonce) : null);
|
|
358
477
|
var matched = respNonce != null && reqNonce != null && guard.crypto.constantTimeEqual(respNonce, reqNonce);
|
|
359
478
|
var out = Object.assign({}, verdict, { nonceMatched: matched });
|
|
360
|
-
|
|
479
|
+
// The downgrade applies to `good` ONLY. `unknown` is the closed direction for a
|
|
480
|
+
// response claiming the certificate is fine, because an unmatched nonce means
|
|
481
|
+
// this is not an answer to this request and the "fine" may be stale. It is NOT
|
|
482
|
+
// the closed direction for `revoked`: revocation does not expire the way
|
|
483
|
+
// non-revocation does, so discarding a signed, current, authorized revoked
|
|
484
|
+
// verdict because it was replayed would hand a soft-fail caller the certificate
|
|
485
|
+
// the responder just refused -- turning the anti-replay defence into the thing
|
|
486
|
+
// that accepts a revoked certificate. `nonceMatched: false` still reports that
|
|
487
|
+
// this response was not bound to this request.
|
|
488
|
+
if (!matched && verdict.status === "good") {
|
|
361
489
|
return Object.assign(out, { status: "unknown", reason: "the OCSP response nonce does not echo the request nonce (RFC 9654)" });
|
|
362
490
|
}
|
|
363
491
|
return out;
|
package/lib/oid.js
CHANGED
|
@@ -132,7 +132,65 @@ var FAMILIES = {
|
|
|
132
132
|
// name->OID reverse stays unambiguous next to the id-kp "timeStamping" purpose (a distinct arc).
|
|
133
133
|
adAccess: { base: [1, 3, 6, 1, 5, 5, 7, 48], of: { ocsp: 1, caIssuers: 2,
|
|
134
134
|
"id-ad-timeStamping": 3, "id-ad-caRepository": 5, "id-ad-rpkiManifest": 10,
|
|
135
|
-
"id-ad-signedObject": 11, "id-ad-rpkiNotify": 13 } },
|
|
135
|
+
"id-ad-signedObject": 11, "id-ad-rpkiNotify": 13, "id-ad-cmc": 12 } },
|
|
136
|
+
|
|
137
|
+
// CMC control attributes -- id-cmc ::= {id-pkix 7} (RFC 5272 sec. 6, extended by RFC 6402
|
|
138
|
+
// sec. 2.2/2.8). The whole registry is declared, not just the controls used today: a control
|
|
139
|
+
// this side does not act on must still be RECOGNIZED to be reported by name, and a decoder that
|
|
140
|
+
// meets an unknown OID cannot tell "control this build ignores" from "attacker-supplied filler".
|
|
141
|
+
//
|
|
142
|
+
// The gaps are the specification's own: 12/13/14 (identityProof/popLinkRandom/popLinkWitness's
|
|
143
|
+
// superseded siblings are retained below where RFC 6402 kept them) and 20 are unassigned in
|
|
144
|
+
// RFC 5272 Appendix A. V2 forms exist because RFC 6402 replaced the originals to fix a hash
|
|
145
|
+
// agility gap -- BOTH are registered, since a v1 message is still legal to receive.
|
|
146
|
+
idCmc: { base: [1, 3, 6, 1, 5, 5, 7, 7], of: {
|
|
147
|
+
"id-cmc-statusInfo": 1, "id-cmc-identification": 2, "id-cmc-identityProof": 3,
|
|
148
|
+
"id-cmc-dataReturn": 4, "id-cmc-transactionId": 5, "id-cmc-senderNonce": 6,
|
|
149
|
+
"id-cmc-recipientNonce": 7, "id-cmc-addExtensions": 8, "id-cmc-encryptedPOP": 9,
|
|
150
|
+
"id-cmc-decryptedPOP": 10, "id-cmc-lraPOPWitness": 11,
|
|
151
|
+
"id-cmc-getCert": 15, "id-cmc-getCRL": 16, "id-cmc-revokeRequest": 17,
|
|
152
|
+
"id-cmc-regInfo": 18, "id-cmc-responseInfo": 19,
|
|
153
|
+
"id-cmc-queryPending": 21, "id-cmc-popLinkRandom": 22, "id-cmc-popLinkWitness": 23,
|
|
154
|
+
"id-cmc-confirmCertAcceptance": 24, "id-cmc-statusInfoV2": 25, "id-cmc-trustedAnchors": 26,
|
|
155
|
+
"id-cmc-authData": 27, "id-cmc-batchRequests": 28, "id-cmc-batchResponses": 29,
|
|
156
|
+
"id-cmc-publishCert": 30, "id-cmc-modCertTemplate": 31, "id-cmc-controlProcessed": 32,
|
|
157
|
+
// ---------------------------------------------------------------------
|
|
158
|
+
// 33 / 34 -- the specification assigns these two OIDs BOTH ways, and no
|
|
159
|
+
// erratum resolves it. Counted across every place either document states an
|
|
160
|
+
// assignment, it is not an even split:
|
|
161
|
+
//
|
|
162
|
+
// identityProofV2 = 34, popLinkWitnessV2 = 33
|
|
163
|
+
// rfc5272.txt:1494 / :1504 the sec. 6 control summary table
|
|
164
|
+
// rfc5272.txt:1846 sec. 6.2.1 body
|
|
165
|
+
// rfc5272.txt:2037 sec. 6.3.1.1 body
|
|
166
|
+
// rfc5272.txt:4041 / :4049 RFC 5272's own ASN.1 module
|
|
167
|
+
// rfc6402.txt:1228 / :1245 RFC 6402 Appendix A.1 (1988 module)
|
|
168
|
+
//
|
|
169
|
+
// identityProofV2 = 33, popLinkWitnessV2 = 34
|
|
170
|
+
// rfc6402.txt:1938 / :1949 RFC 6402 Appendix A.2 (2008 module) ONLY
|
|
171
|
+
//
|
|
172
|
+
// The rows below therefore follow the base specification's normative body
|
|
173
|
+
// text and its module, which RFC 6402's own 1988 module agrees with. The
|
|
174
|
+
// 2008 module is the lone outlier and is treated as its typo.
|
|
175
|
+
//
|
|
176
|
+
// This binds what the toolkit EMITS, and it is load-bearing because the two
|
|
177
|
+
// controls cannot be told apart by SHAPE: both are SEQUENCE {
|
|
178
|
+
// AlgorithmIdentifier, AlgorithmIdentifier, OCTET STRING } (rfc6402.txt:1238,
|
|
179
|
+
// :1246), so the OID is the only discriminator. Emitting the A.2 reading
|
|
180
|
+
// would hand a peer built on RFC 5272 an Identity Proof it reads as a POP
|
|
181
|
+
// Link Witness. A CONSUMER must still not infer meaning from these names
|
|
182
|
+
// alone -- a peer built against A.2 means the other one, and nothing on the
|
|
183
|
+
// wire says which module it read.
|
|
184
|
+
"id-cmc-identityProofV2": 34, "id-cmc-popLinkWitnessV2": 33,
|
|
185
|
+
"id-cmc-raIdentityWitness": 35, "id-cmc-changeSubjectName": 36, "id-cmc-responseBody": 37,
|
|
186
|
+
} },
|
|
187
|
+
|
|
188
|
+
// CMC content types -- id-cct ::= {id-pkix 12} (RFC 5272 Appendix A). The eContentType a CMC
|
|
189
|
+
// message rides under inside its CMS SignedData wrapper. RFC 5272 Appendix B repeatedly writes
|
|
190
|
+
// "id-ct-PKIData"; that is erratum 4775 (Verified) and the correct prefix is id-cct-.
|
|
191
|
+
idCct: { base: [1, 3, 6, 1, 5, 5, 7, 12], of: {
|
|
192
|
+
"id-cct-PKIData": 2, "id-cct-PKIResponse": 3,
|
|
193
|
+
} },
|
|
136
194
|
|
|
137
195
|
// id-on -- RFC 5280 sec. 4.2.1.6 otherName type-ids the C509 sec. 8.13 registry gives their own
|
|
138
196
|
// negative ints: id-on-hardwareModuleName (RFC 4108), id-on-SmtpUTF8Mailbox (RFC 9598),
|
|
@@ -312,6 +370,11 @@ var FAMILIES = {
|
|
|
312
370
|
// identifiers (RFC 9802 sec. 4). HSS/LMS additionally has the SMIME
|
|
313
371
|
// id-alg-hss-lms-hashsig OID above (RFC 9708 / RFC 9802 share it).
|
|
314
372
|
pkixAlg: { base: [1, 3, 6, 1, 5, 5, 7, 6], of: {
|
|
373
|
+
// id-alg-noSignature (RFC 6402 sec. 2.4, {id-pkix id-alg(6) 2}): the CMC
|
|
374
|
+
// "signature" algorithm for a request whose signer has no key yet -- the
|
|
375
|
+
// SignerInfo carries a MAC-based proof instead. Registered so a decoder can
|
|
376
|
+
// NAME it and refuse it deliberately, rather than meeting an unknown OID.
|
|
377
|
+
"id-alg-noSignature": 2,
|
|
315
378
|
"id-alg-xmss-hashsig": 34, "id-alg-xmssmt-hashsig": 35,
|
|
316
379
|
// Composite ML-DSA signature algorithms (draft-ietf-lamps-pq-composite-sigs
|
|
317
380
|
// sec. 6): a PQ ML-DSA paired with a traditional RSA / ECDSA / EdDSA so the
|
|
@@ -348,6 +411,11 @@ var FAMILIES = {
|
|
|
348
411
|
receiptRequest: 1, eSSSecurityLabel: 2, mlExpansionHistory: 3, contentHints: 4,
|
|
349
412
|
msgSigDigest: 5, contentIdentifier: 7, equivalentLabels: 9, contentReference: 10,
|
|
350
413
|
signingCertificate: 12, timeStampToken: 14, decryptKeyID: 37, signingCertificateV2: 47,
|
|
414
|
+
// id-aa-cmc-unsignedData (RFC 6402 sec. 2.7, {id-aa 34}): the CMC unsigned
|
|
415
|
+
// attribute that carries body parts too large to sit inside the signed
|
|
416
|
+
// PKIData -- unsigned by design, so a consumer must treat its contents as
|
|
417
|
+
// untrusted data rather than as part of the authenticated message.
|
|
418
|
+
cmcUnsignedData: 34,
|
|
351
419
|
asymmDecryptKeyID: 54, certificationRequestInfoTemplate: 61, extensionReqTemplate: 62 } },
|
|
352
420
|
|
|
353
421
|
// ANSI X9.62 EC public key, named curve, and ECDSA signatures.
|
package/lib/path-validate.js
CHANGED
|
@@ -1107,7 +1107,7 @@ function validateCriticalExtensionStructure(cert) {
|
|
|
1107
1107
|
* @primitive pki.path.validate
|
|
1108
1108
|
* @signature pki.path.validate(path, opts) -> Promise<result>
|
|
1109
1109
|
* @since 0.1.16
|
|
1110
|
-
* @status
|
|
1110
|
+
* @status stable
|
|
1111
1111
|
* @spec RFC 5280
|
|
1112
1112
|
* @related pki.schema.x509.parse, pki.path.crlChecker
|
|
1113
1113
|
*
|
|
@@ -1134,6 +1134,10 @@ function validateCriticalExtensionStructure(cert) {
|
|
|
1134
1134
|
* throws a typed `PathError`.
|
|
1135
1135
|
*
|
|
1136
1136
|
* @example
|
|
1137
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
1138
|
+
* var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
1139
|
+
* notBefore: new Date("2019-01-01T00:00:00Z"), notAfter: new Date("2029-01-01T00:00:00Z") },
|
|
1140
|
+
* { key: await pki.key.export(pair.privateKey) });
|
|
1137
1141
|
* var cert = pki.schema.x509.parse(der);
|
|
1138
1142
|
* var res = await pki.path.validate([cert], {
|
|
1139
1143
|
* time: new Date("2020-01-01T00:00:00Z"),
|
|
@@ -1769,7 +1773,7 @@ function selectDelta(candidates) {
|
|
|
1769
1773
|
* @primitive pki.path.crlChecker
|
|
1770
1774
|
* @signature pki.path.crlChecker(crls, opts?) -> RevocationChecker
|
|
1771
1775
|
* @since 0.1.16
|
|
1772
|
-
* @status
|
|
1776
|
+
* @status stable
|
|
1773
1777
|
* @spec RFC 5280
|
|
1774
1778
|
* @related pki.path.validate, pki.schema.crl.parse
|
|
1775
1779
|
*
|
|
@@ -2212,7 +2216,7 @@ cmpSession.setEngine({ build: build, validate: validate, toAnchor: toAnchor, coe
|
|
|
2212
2216
|
* @primitive pki.path.ocspChecker
|
|
2213
2217
|
* @signature pki.path.ocspChecker(responses) -> RevocationChecker
|
|
2214
2218
|
* @since 0.1.32
|
|
2215
|
-
* @status
|
|
2219
|
+
* @status stable
|
|
2216
2220
|
* @spec RFC 6960
|
|
2217
2221
|
* @related pki.path.validate, pki.schema.ocsp.parseResponse, pki.path.crlChecker
|
|
2218
2222
|
*
|
|
@@ -2291,7 +2295,7 @@ function ocspChecker(responses) {
|
|
|
2291
2295
|
* @primitive pki.path.verifyOcspResponse
|
|
2292
2296
|
* @signature pki.path.verifyOcspResponse(parsedResponse, cert, issuerCert, time, opts?) -> Promise<{ status, responderAuthorized, signatureValid, matched, thisUpdate, nextUpdate, revocationReason?, reason }>
|
|
2293
2297
|
* @since 0.2.22
|
|
2294
|
-
* @status
|
|
2298
|
+
* @status stable
|
|
2295
2299
|
* @spec RFC 6960
|
|
2296
2300
|
* @related pki.ocsp.verify, pki.path.ocspChecker
|
|
2297
2301
|
*
|
|
@@ -2318,6 +2322,20 @@ function ocspChecker(responses) {
|
|
|
2318
2322
|
* fault surfaces as the parser's typed `ocsp/*` / `asn1/*` error.
|
|
2319
2323
|
*
|
|
2320
2324
|
* @example
|
|
2325
|
+
* var ca = await pki.key.generate("Ed25519");
|
|
2326
|
+
* var caKey = await pki.key.export(ca.privateKey);
|
|
2327
|
+
* var caDer = await pki.x509.sign({ subject: "Example CA", subjectPublicKey: await pki.key.export(ca.publicKey),
|
|
2328
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
2329
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectKeyIdentifier: true } },
|
|
2330
|
+
* { key: caKey });
|
|
2331
|
+
* var leaf = await pki.key.generate("Ed25519");
|
|
2332
|
+
* var leafDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(leaf.publicKey),
|
|
2333
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
2334
|
+
* { cert: caDer, key: caKey });
|
|
2335
|
+
* var der = await pki.ocsp.sign(
|
|
2336
|
+
* { responderID: "byName", responses: [{ cert: leafDer, issuer: caDer, status: "good" }] },
|
|
2337
|
+
* { cert: caDer, key: caKey });
|
|
2338
|
+
* var cert = pki.schema.x509.parse(leafDer), issuerCert = pki.schema.x509.parse(caDer);
|
|
2321
2339
|
* var resp = pki.schema.ocsp.parseResponse(der);
|
|
2322
2340
|
* var v = await pki.path.verifyOcspResponse(resp, cert, issuerCert, new Date());
|
|
2323
2341
|
* v.status; // "good" | "revoked" | "unknown"
|
|
@@ -2674,6 +2692,11 @@ async function _fetchAiaIssuers(current, aia) {
|
|
|
2674
2692
|
* @opts maxResponseBytes Per-fetch response size cap, forwarded to the transport (tightenable downward only).
|
|
2675
2693
|
* @opts (validate options) Every `pki.path.validate` option (`requiredEku`, `revocationChecker`, `checkPurpose`, the initial policy inputs, ...) is forwarded unchanged.
|
|
2676
2694
|
* @example
|
|
2695
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
2696
|
+
* var pemString = await pki.x509.sign({ subject: "Example Root", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
2697
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
2698
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectKeyIdentifier: true } },
|
|
2699
|
+
* { key: await pki.key.export(pair.privateKey) }, { pem: true });
|
|
2677
2700
|
* var result = await pki.path.build(pemString, {
|
|
2678
2701
|
* candidates: [], // untrusted intermediates (the openssl -untrusted set)
|
|
2679
2702
|
* trustAnchors: [pemString], // a self-signed root, or a { name, publicKey, algorithm } tuple
|
package/lib/pkcs12-build.js
CHANGED
|
@@ -469,6 +469,10 @@ function _normalizeSpec(spec, opts) {
|
|
|
469
469
|
* - `recipientCerts` -- (public-key privacy convenience) `[certDer|PEM, ...]`; the convenience-form cert + key are placed in one recipient-enveloped safe (a plaintext keyBag). For the full form, set per-`safeContents` `recipients: [{ cert }, ...]` -- CERTIFICATE recipients only (RSA-OAEP / ECDH / X25519 / X448 / ML-KEM, dispatched off the cert key); a password or KEK recipient is not public-key privacy and is rejected -- with an optional `contentEncryptionAlgorithm` (`aes-128|192|256-cbc`, default 256; GCM/AEAD rejected). `encrypt` (password) and `recipients` (public-key) on the same safe is rejected; privacy is independent of integrity.
|
|
470
470
|
* - `pem` (boolean) -- return a PEM `PKCS12` string instead of DER.
|
|
471
471
|
* @example
|
|
472
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
473
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
474
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
475
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
472
476
|
* var p12 = await pki.pkcs12.build({ safeContents: [{ bags: [
|
|
473
477
|
* { type: 'cert', cert: signerCertDer },
|
|
474
478
|
* { type: 'shroudedKey', key: signerKeyPkcs8, encrypt: { password: 'changeit' } } ] }] },
|
|
@@ -526,6 +530,10 @@ async function build(spec, opts) {
|
|
|
526
530
|
* or public-key-integrity store, or an unsupported MAC algorithm (never a falsy verdict standing in for an error).
|
|
527
531
|
*
|
|
528
532
|
* @example
|
|
533
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
534
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
535
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
536
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
529
537
|
* var p12 = await pki.pkcs12.build({ key: signerKeyPkcs8, cert: signerCertDer }, { password: 'changeit' });
|
|
530
538
|
* var ok = await pki.pkcs12.verifyMac(p12, 'changeit');
|
|
531
539
|
*/
|
|
@@ -614,6 +622,10 @@ function _capWork(iterations, salt, opts, keyLength, hardCap) {
|
|
|
614
622
|
* - `keys` (string) -- `der` (default) or `crypto` (also `pki.key.import` each private key to a CryptoKey).
|
|
615
623
|
* - `importAlgorithm` -- forwarded to `pki.key.import` for the ambiguous RSA / EC arms when `keys: crypto`.
|
|
616
624
|
* @example
|
|
625
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
626
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
627
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
628
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
617
629
|
* var p12 = await pki.pkcs12.build({ key: signerKeyPkcs8, cert: signerCertDer }, { password: 'changeit' });
|
|
618
630
|
* var store = await pki.pkcs12.open(p12, 'changeit');
|
|
619
631
|
* var keyDer = store.keys[0].pkcs8, certDer = store.certs[0].cert;
|
package/lib/schema-all.js
CHANGED
|
@@ -50,6 +50,15 @@ var attrcert = require("./schema-attrcert");
|
|
|
50
50
|
// SEQUENCE) so it is deliberately absent from FORMATS / the detect-and-route
|
|
51
51
|
// `parse`; it is reached only by explicit OID dispatch on a CMS attribute.
|
|
52
52
|
var smime = require("./schema-smime");
|
|
53
|
+
// cmc is likewise a COMPANION decoder, for the same reason arrived at from the
|
|
54
|
+
// other direction: a CMC message's outer DER root IS a CMS ContentInfo, which
|
|
55
|
+
// `cms.matches` already claims below. A `cmc` FORMATS row would either shadow CMS
|
|
56
|
+
// or be shadowed by it, and there is no structurally disjoint root to detect --
|
|
57
|
+
// the discriminator is the encapsulated content type, one layer in. So it is
|
|
58
|
+
// reached by explicit call, or by dispatching on
|
|
59
|
+
// `pki.schema.cms.parse(der).encapContentInfo.eContentType`, and exports no
|
|
60
|
+
// `matches` for nobody to call.
|
|
61
|
+
var cmc = require("./schema-cmc");
|
|
53
62
|
var frameworkError = require("./framework-error");
|
|
54
63
|
|
|
55
64
|
var SchemaError = frameworkError.SchemaError;
|
|
@@ -276,6 +285,10 @@ function all() { return FORMATS.map(function (f) { return f.name; }); }
|
|
|
276
285
|
* errors of the matched format propagate unchanged.
|
|
277
286
|
*
|
|
278
287
|
* @example
|
|
288
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
289
|
+
* var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
290
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
291
|
+
* { key: await pki.key.export(pair.privateKey) });
|
|
279
292
|
* var parsed = pki.schema.parse(der); // cert -> the pki.schema.x509 shape
|
|
280
293
|
*/
|
|
281
294
|
function parse(input) {
|
|
@@ -294,7 +307,7 @@ function parse(input) {
|
|
|
294
307
|
* @primitive pki.schema.detectFormat
|
|
295
308
|
* @signature pki.schema.detectFormat(input) -> string | null
|
|
296
309
|
* @since 0.3.8
|
|
297
|
-
* @status
|
|
310
|
+
* @status stable
|
|
298
311
|
* @spec RFC 5280
|
|
299
312
|
* @related pki.schema.parse, pki.schema.all
|
|
300
313
|
*
|
|
@@ -307,6 +320,10 @@ function parse(input) {
|
|
|
307
320
|
* same coercion / decode error `parse` throws.
|
|
308
321
|
*
|
|
309
322
|
* @example
|
|
323
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
324
|
+
* var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
325
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
|
|
326
|
+
* { key: await pki.key.export(pair.privateKey) });
|
|
310
327
|
* pki.schema.detectFormat(der); // "x509" | "crl" | "csr" | "cms" | ... | null
|
|
311
328
|
*/
|
|
312
329
|
function detectFormat(input) {
|
|
@@ -339,6 +356,7 @@ module.exports = {
|
|
|
339
356
|
csrattrs: { parse: csrattrs.parse },
|
|
340
357
|
attrcert: { parse: attrcert.parse, pemDecode: attrcert.pemDecode, pemEncode: attrcert.pemEncode },
|
|
341
358
|
smime: { parseSigningCertificate: smime.parseSigningCertificate, parseSigningCertificateV2: smime.parseSigningCertificateV2, parseSmimeCapabilities: smime.parseSmimeCapabilities, decodeAttribute: smime.decodeAttribute },
|
|
359
|
+
cmc: { parse: cmc.parse, parsePkiData: cmc.parsePkiData, parsePkiResponse: cmc.parsePkiResponse },
|
|
342
360
|
all: all,
|
|
343
361
|
parse: parse,
|
|
344
362
|
detectFormat: detectFormat,
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -340,6 +340,15 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
|
|
|
340
340
|
* and `Asn1Error` when the underlying DER is malformed.
|
|
341
341
|
*
|
|
342
342
|
* @example
|
|
343
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
344
|
+
* var key = await pki.key.export(pair.privateKey);
|
|
345
|
+
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
346
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
347
|
+
* var der = await pki.attrcert.sign(
|
|
348
|
+
* { holder: { entityName: { directoryName: "CN=Alice" } },
|
|
349
|
+
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
350
|
+
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
351
|
+
* { cert: aaCert, key: key });
|
|
343
352
|
* var ac = pki.schema.attrcert.parse(der);
|
|
344
353
|
* ac.attributes[0].name; // "role"
|
|
345
354
|
* ac.validity.notAfterTime;// Date
|
|
@@ -383,6 +392,15 @@ function parseV1(input) {
|
|
|
383
392
|
* `PemError` on a missing / mismatched envelope or a non-base64 body.
|
|
384
393
|
*
|
|
385
394
|
* @example
|
|
395
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
396
|
+
* var key = await pki.key.export(pair.privateKey);
|
|
397
|
+
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
398
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
399
|
+
* var pemText = await pki.attrcert.sign(
|
|
400
|
+
* { holder: { entityName: { directoryName: "CN=Alice" } },
|
|
401
|
+
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
402
|
+
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
403
|
+
* { cert: aaCert, key: key }, { pem: true });
|
|
386
404
|
* var der = pki.schema.attrcert.pemDecode(pemText);
|
|
387
405
|
*/
|
|
388
406
|
function pemDecode(text, label) { return pkix.pemDecode(text, label === null ? null : (label || "ATTRIBUTE CERTIFICATE"), PemError); }
|
|
@@ -399,6 +417,15 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label === null ? n
|
|
|
399
417
|
* same default `pemDecode` reads). Throws `PemError` on a malformed label.
|
|
400
418
|
*
|
|
401
419
|
* @example
|
|
420
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
421
|
+
* var key = await pki.key.export(pair.privateKey);
|
|
422
|
+
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
423
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
424
|
+
* var der = await pki.attrcert.sign(
|
|
425
|
+
* { holder: { entityName: { directoryName: "CN=Alice" } },
|
|
426
|
+
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
427
|
+
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
428
|
+
* { cert: aaCert, key: key });
|
|
402
429
|
* var pem = pki.schema.attrcert.pemEncode(der);
|
|
403
430
|
*/
|
|
404
431
|
function pemEncode(der, label) { return pkix.pemEncode(der, label || "ATTRIBUTE CERTIFICATE", PemError); }
|
package/lib/schema-c509.js
CHANGED
|
@@ -2327,6 +2327,12 @@ function _encodeC509Array(r) {
|
|
|
2327
2327
|
* only for the DER -> type-3 path; ignored when re-emitting a parse result.
|
|
2328
2328
|
*
|
|
2329
2329
|
* @example
|
|
2330
|
+
* // the type-3 (natively signed) encoding covers ECDSA-signed X.509 v3 certificates
|
|
2331
|
+
* var pair = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
2332
|
+
* var signerCertDer = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
2333
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
2334
|
+
* extensions: { keyUsage: ["digitalSignature"] } },
|
|
2335
|
+
* { key: await pki.key.export(pair.privateKey) });
|
|
2330
2336
|
* var cbor = pki.schema.c509.encode(signerCertDer); // a DER cert -> a compact type-3 C509
|
|
2331
2337
|
* pki.schema.c509.parse(cbor).certificateType; // 3
|
|
2332
2338
|
*/
|