@blamejs/pki 0.8.3 → 0.8.5
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 +32 -0
- package/README.md +27 -0
- package/lib/cms-sign.js +16 -6
- package/lib/cms-verify.js +2 -1
- package/lib/csr-sign.js +2 -1
- package/lib/guard-parsed.js +2 -2
- package/lib/ocsp.js +18 -7
- package/lib/pki-build.js +1 -1
- package/lib/schema-attrcert.js +36 -26
- package/lib/schema-c509.js +72 -42
- package/lib/schema-cmc.js +25 -22
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +39 -28
- package/lib/schema-crl.js +11 -11
- package/lib/schema-crmf.js +11 -6
- package/lib/schema-csrattrs.js +8 -6
- package/lib/schema-engine.js +65 -2
- package/lib/schema-ocsp.js +17 -16
- package/lib/schema-pkcs12.js +15 -11
- package/lib/schema-pkcs8.js +9 -2
- package/lib/schema-pkix.js +44 -17
- package/lib/schema-tsp.js +16 -3
- package/lib/sign-scheme.js +117 -5
- package/lib/x509-sign.js +7 -2
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,38 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.8.5 — 2026-09-25
|
|
8
|
+
|
|
9
|
+
A key this process cannot export can sign, and every verb that signs proves its signature first.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- A signer `{ algorithm, publicKey, sign }` is accepted wherever a signing verb takes a private key: `pki.x509.sign`, `pki.crl.sign`, `pki.csr.sign`, `pki.cms.sign` and its countersignatures, `pki.attrcert.sign`, `pki.cmp.build`, `pki.crmf.build`, `pki.ocsp.buildRequest`, `pki.ocsp.sign` and `pki.tsp.sign`. `sign(bytes)` is handed the bytes WebCrypto would be handed and returns the bytes WebCrypto would return, or a promise of them; for ECDSA that is the fixed-width `r || s` of RFC 9053 sec. 2.1, and a DER `SEQUENCE` is refused by name rather than encoded a second time. The private half never reaches the toolkit.
|
|
14
|
+
- A signer's declared `algorithm` is held to the algorithm of the key the artifact certifies, and its declared `publicKey` to the key the signature is verified against, both before the callback is asked to sign. The caller's object is read once, in the synchronous prologue, so an accessor cannot answer one way to the check and another to the use. For an ECDSA key, whose curve is named but whose digest is chosen per signature, the digest the signer declares is checked on its own, before any remote signing request. The bytes the callback is handed are a copy in an allocation of its own, so a callback that rewrites them rewrites only its own copy and cannot reach the bytes the artifact carries. The bytes it returns are copied before use. Hold a client or any mutable state in the callback's closure: a signing verb snapshots its options, so the signer object the callback sees is a copy of the one you passed. A composite ML-DSA signature is refused by name, since it is produced from both component private keys and one callback cannot do that.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `pki.ocsp.sign`, `pki.ocsp.buildRequest` and `pki.cms.sign`'s countersignature path verify the signature they produced against the public key the artifact declares, which the other signing verbs already did. A signer holding a key that does not match the certificate it signs under produced an OCSP response, an OCSP request or a countersignature that no relying party could validate, and the emitting side reported success.
|
|
19
|
+
|
|
20
|
+
## v0.8.4 — 2026-09-25
|
|
21
|
+
|
|
22
|
+
A decode cap bounds every structure a parse reads, and a refusal caused by a cap is no longer reported as malformed input.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `pki.schema.engine.decodeNested(bytes, ctx, label, opts?)`, the decode to perform on a value already read out of a parse's input. It applies the caps carried on the walk context and records a resource refusal against the parse before re-throwing, which is what a format parser composed on the engine needs so its embedded payloads answer to the caller's caps.
|
|
27
|
+
- `pki.schema.engine.withResources(ns, source)` carries a parse's caps and budget onto another namespace, for a format whose structure is carried inside another one. The structure is still walked in its own namespace, so a fault in it keeps that format's error class and code. `pki.schema.engine.isResourceRefusal(err)` reports whether a codec error was a limit rather than a claim about the bytes.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- An input refused for exceeding a decode limit reports `<format>/too-large` in cases where it previously reported the format's own malformed-value code, such as `cms/bad-aead-params` or `cmp/bad-body`. A parser that decodes a value out of its input wraps a decode failure in a code of its own, and that code sent a reader to inspect bytes that were fine and a cap that was too small. A caller matching on one of those codes to detect malformed input will stop seeing over-budget inputs under it. The wrapped error is carried on the refusal as its `cause`.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- A decode cap bounds every structure a parse reads, not the input alone. An embedded `OCTET STRING` payload, an encapsulated protocol body, a `SafeContents` inside a PKCS#12 store and an attribute value are all decoded under the caps the caller named. The 0.8.3 notes recorded that a structure decoded from inside the input kept the built-in `pki.C.LIMITS` defaults; it no longer does.
|
|
36
|
+
- `pki.schema.cmc.parse` applies the caller's caps to the `PKIData` or `PKIResponse` body. It read the caps, bounded the CMS carrier with them, and then decoded the body that carries the request or response with the built-in defaults.
|
|
37
|
+
- A parse cannot return a result after a decode limit has refused something inside that parse, whatever the code between the two does with the failure. A parser that catches a resource refusal and re-throws its own typed error is the common shape, and that shape previously carried the parse past the latch the budget sets. The door now reads the latch on the failure path as well as on the success path.
|
|
38
|
+
|
|
7
39
|
## v0.8.3 — 2026-09-25
|
|
8
40
|
|
|
9
41
|
A parser takes decode caps from its caller, and a resource refusal says so instead of reading as malformed input.
|
package/README.md
CHANGED
|
@@ -190,6 +190,33 @@ var ok = await subtle.verify({ name: "ML-DSA-65" }, kp.publicKey, sig, data); /
|
|
|
190
190
|
// same call shape, and every key it exports is OpenSSL/NSS-interoperable.
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
+
### Sign with a key this process cannot export
|
|
194
|
+
|
|
195
|
+
Every signing verb takes a signer object wherever it takes a private key, for a
|
|
196
|
+
key held in a hardware security module, a cloud key management service, a PKCS#11
|
|
197
|
+
token, a PIV slot or a Trusted Platform Module. The private half never reaches
|
|
198
|
+
the toolkit.
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
var signer = {
|
|
202
|
+
algorithm: { name: "ECDSA", namedCurve: "P-256", hash: { name: "SHA-256" } },
|
|
203
|
+
publicKey: spkiDer, // the SPKI DER of the public half
|
|
204
|
+
sign: function (bytes) { return kms.sign(keyId, bytes); }, // bytes or a promise of them
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
var cert = await pki.x509.sign(spec, { key: signer, cert: caCert });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`sign(bytes)` is handed the bytes WebCrypto would be handed and returns the bytes
|
|
211
|
+
WebCrypto would return: for ECDSA the fixed-width `r || s` of RFC 9053 §2.1, not a
|
|
212
|
+
DER `SEQUENCE`. `algorithm` is held to the certificate's key algorithm, `publicKey`
|
|
213
|
+
is held to the key the signature is verified against, and the signature is
|
|
214
|
+
verified before the artifact is returned. The bytes handed to the callback are a
|
|
215
|
+
copy, so rewriting them rewrites nothing the artifact carries. Hold the client and
|
|
216
|
+
any mutable state in the callback's closure, as the example does: a signing verb
|
|
217
|
+
snapshots its options, so the signer object the callback sees is a copy of the one
|
|
218
|
+
you passed.
|
|
219
|
+
|
|
193
220
|
## What ships today
|
|
194
221
|
|
|
195
222
|
Everything below is callable now; nothing is a stub. The whole documented
|
package/lib/cms-sign.js
CHANGED
|
@@ -214,7 +214,10 @@ function _keyOnlyCertStandIn(spkiDer) {
|
|
|
214
214
|
if (e && typeof e.code === "string" && e.code.indexOf("cms/") === 0) throw e;
|
|
215
215
|
throw _err("cms/bad-input", "a key-only signer's spki is not a SubjectPublicKeyInfo", e);
|
|
216
216
|
}
|
|
217
|
-
|
|
217
|
+
/** @internal The SPKI bytes travel with the stand-in, because the key this signature is verified
|
|
218
|
+
* against is what a signer's declared publicKey is held to, and this form names that key by SPKI
|
|
219
|
+
* rather than by certificate. */
|
|
220
|
+
return { subjectPublicKeyInfo: { bytes: spkiDer, algorithm: alg } };
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
function _keyOnlyKeyId(so) {
|
|
@@ -432,11 +435,18 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
432
435
|
return _buildSignedAttrs(pairs.concat(extra));
|
|
433
436
|
});
|
|
434
437
|
}).then(function (attrs) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
return
|
|
438
|
+
var preimage = attrs ? attrs.setOf : targetSigOctets;
|
|
439
|
+
return signScheme.signOverTbs(scheme, soKey, preimage, _signE).then(function (sig) {
|
|
440
|
+
/** @internal The countersignature is held to the same proof the signature is: it verifies
|
|
441
|
+
* against the public key the countersigner's certificate declares. */
|
|
442
|
+
return guard.async.deferred(function () {
|
|
443
|
+
return _b.assertSignatureVerifies(preimage, sig, cert.subjectPublicKeyInfo.bytes, scheme);
|
|
444
|
+
}).then(function () {
|
|
445
|
+
var fields = [b.integer(BigInt(sidv.version)), sidv.sid, scheme.digestAlgId];
|
|
446
|
+
if (attrs) fields.push(attrs.wire);
|
|
447
|
+
fields.push(scheme.sigAlgId, b.octetString(sig));
|
|
448
|
+
return { value: b.sequence(fields), certDer: certDer };
|
|
449
|
+
});
|
|
440
450
|
});
|
|
441
451
|
});
|
|
442
452
|
}
|
package/lib/cms-verify.js
CHANGED
|
@@ -721,7 +721,8 @@ function _addCert(out, der) {
|
|
|
721
721
|
* S/MIME signed mail, RFC 3161 timestamp tokens, and code signing rest on, and exactly what
|
|
722
722
|
* `pki.cms.verify` consumes and OpenSSL `cms -verify` validates. Each `signers[i]` is
|
|
723
723
|
* `{ cert, key, digestAlgorithm?, pss? }`: `cert` the signer certificate (PEM or DER), `key`
|
|
724
|
-
* its private key (a WebCrypto `CryptoKey
|
|
724
|
+
* its private key (a WebCrypto `CryptoKey`, a PKCS#8 DER `Buffer` / PEM string, or a signer
|
|
725
|
+
* `{ algorithm, publicKey, sign }` for a key held outside this process; for a composite
|
|
725
726
|
* ML-DSA signer, the two component keys `{ mldsa, trad }`, each PKCS#8). The
|
|
726
727
|
* signature covers the RFC 5652 sec. 5.4 preimage: with signed attributes (the default) the
|
|
727
728
|
* message-digest attribute is bound to the content digest and the signature is over the
|
package/lib/csr-sign.js
CHANGED
|
@@ -77,7 +77,8 @@ function _challengePassword(pw) {
|
|
|
77
77
|
* `challengePassword`. An extension the issuing CA assigns (authorityKeyIdentifier, the
|
|
78
78
|
* certificate-transparency pair, the CA version and previous-certificate hash, ocspNoCheck) is not a
|
|
79
79
|
* request and is refused by name. `key` (or
|
|
80
|
-
* `{ key }`) is the subject's own PKCS#8 private key
|
|
80
|
+
* `{ key }`) is the subject's own PKCS#8 private key, WebCrypto CryptoKey, or signer
|
|
81
|
+
* `{ algorithm, publicKey, sign }`, so the request is self-signed
|
|
81
82
|
* to prove possession of the private half of `subjectPublicKey`, and that proof is verified before the
|
|
82
83
|
* request is returned. The signature algorithm is resolved from the subject key (RSA PKCS#1 v1.5 or PSS,
|
|
83
84
|
* ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). Returns DER, or a PEM `CERTIFICATE REQUEST` with
|
package/lib/guard-parsed.js
CHANGED
|
@@ -158,8 +158,8 @@ function recordingParser(kind, parse, ErrorClass, code, label) {
|
|
|
158
158
|
|
|
159
159
|
// @enforced-by guard-shape-reinlined -- shares recordingParser's shape below: the PROVENANCE.set
|
|
160
160
|
function recordingWalker(kind, walkNode, decodeBytes) {
|
|
161
|
-
return function (node) {
|
|
162
|
-
var out = walkNode(node);
|
|
161
|
+
return function (node, ctx) {
|
|
162
|
+
var out = walkNode(node, ctx);
|
|
163
163
|
if (out && typeof out === "object" && node && _isBytes(node.bytes)) {
|
|
164
164
|
_weakSet(PROVENANCE, out, {
|
|
165
165
|
kind: kind,
|
package/lib/ocsp.js
CHANGED
|
@@ -45,6 +45,9 @@ var b = asn1.build;
|
|
|
45
45
|
function O(name) { return oid.byName(name); }
|
|
46
46
|
function _err(code, message, cause) { return new OcspError(code, message, cause); }
|
|
47
47
|
function _signE(kind, message, cause) { return new OcspError("ocsp/" + kind, message, cause); }
|
|
48
|
+
/** @internal Only the signature self-check is taken from the shared builder here; the name and
|
|
49
|
+
* SPKI schemas it also serves belong to the certificate verbs, and this module reads neither. */
|
|
50
|
+
var _b = pkiBuild.makeBuilder({ ErrorClass: OcspError, prefix: "ocsp", O: O, NS: null, NAME_SCHEMA: null, SPKI_SCHEMA: null, EXT_DECODERS: {} });
|
|
48
51
|
|
|
49
52
|
var HASH_WC = Object.assign(Object.create(null), { sha1: "SHA-1", sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" });
|
|
50
53
|
var REASON_CODE = Object.create(null);
|
|
@@ -214,8 +217,12 @@ function _buildRequest(query, opts) {
|
|
|
214
217
|
if (signerCert === null) return _emitReq(b.sequence([tbsRequest]), opts);
|
|
215
218
|
var scheme = signScheme.resolveSignScheme(signerCert, { combinedRsaSig: true }, true, _signE);
|
|
216
219
|
return signScheme.signOverTbs(scheme, opts.signer.key, tbsRequest, _signE).then(function (sig) {
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
/** @internal The request is held to the same proof a certificate is: the signature verifies
|
|
221
|
+
* against the public key the certificate it carries declares. */
|
|
222
|
+
return Promise.resolve(_b.assertSignatureVerifies(tbsRequest, sig, signerCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
|
|
223
|
+
var optionalSignature = b.sequence([scheme.sigAlgId, b.bitString(sig, 0), b.explicit(0, b.sequence([b.raw(signerCertDer)]))]);
|
|
224
|
+
return _emitReq(b.sequence([tbsRequest, b.explicit(0, optionalSignature)]), opts);
|
|
225
|
+
});
|
|
219
226
|
});
|
|
220
227
|
});
|
|
221
228
|
}
|
|
@@ -349,11 +356,15 @@ function _signResponse(responseData, responder, respCert, respCertDer, responder
|
|
|
349
356
|
var responseDataDer = b.sequence(rdChildren);
|
|
350
357
|
var scheme = signScheme.resolveSignScheme(respCert, { combinedRsaSig: true }, true, _signE);
|
|
351
358
|
return signScheme.signOverTbs(scheme, responderKey, responseDataDer, _signE).then(function (sig) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
359
|
+
/** @internal The response is held to the same proof a certificate is: the signature verifies
|
|
360
|
+
* against the public key the responder certificate declares. */
|
|
361
|
+
return Promise.resolve(_b.assertSignatureVerifies(responseDataDer, sig, respCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
|
|
362
|
+
var basicChildren = [responseDataDer, scheme.sigAlgId, b.bitString(sig, 0)];
|
|
363
|
+
if (opts.embedCert !== false) basicChildren.push(b.explicit(0, b.sequence([b.raw(respCertDer)])));
|
|
364
|
+
var responseBytes = b.sequence([b.oid(OID_OCSP_BASIC), b.octetString(b.sequence(basicChildren))]);
|
|
365
|
+
var der = b.sequence([b.enumerated(0n), b.explicit(0, responseBytes)]);
|
|
366
|
+
return opts.pem ? ocspSchema.pemEncode(der, "OCSP RESPONSE") : der;
|
|
367
|
+
});
|
|
357
368
|
});
|
|
358
369
|
});
|
|
359
370
|
});
|
package/lib/pki-build.js
CHANGED
|
@@ -1290,7 +1290,7 @@ function makeBuilder(ctx) {
|
|
|
1290
1290
|
var keyOid;
|
|
1291
1291
|
try { keyOid = asn1.read.oid(alg.children[0]); }
|
|
1292
1292
|
catch (e) { throw E("bad-input", "the signing key SPKI algorithm is not an OID", e); }
|
|
1293
|
-
return { subjectPublicKeyInfo: { algorithm: { oid: keyOid, parameters: alg.children.length > 1 ? alg.children[1].bytes : undefined } } };
|
|
1293
|
+
return { subjectPublicKeyInfo: { bytes: spkiDer, algorithm: { oid: keyOid, parameters: alg.children.length > 1 ? alg.children[1].bytes : undefined } } };
|
|
1294
1294
|
}
|
|
1295
1295
|
|
|
1296
1296
|
function assertSignatureVerifies(preimage, sig, spki, scheme) {
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -147,7 +147,7 @@ var V2_FORM = schema.seq([
|
|
|
147
147
|
if (issuerName.names[0].tagNumber !== 4) {
|
|
148
148
|
throw ctx.E("attrcert/bad-issuer-name", "the issuerName GeneralName must be a directoryName [4] (RFC 5755 sec. 4.2.3)");
|
|
149
149
|
}
|
|
150
|
-
var dirName =
|
|
150
|
+
var dirName = pkix.decodeNested(issuerName.names[0].bytes, ctx, "the issuerName directoryName").children[0];
|
|
151
151
|
if (!dirName.children || dirName.children.length < 1) {
|
|
152
152
|
throw ctx.E("attrcert/bad-issuer-name", "the issuerName directoryName must be a non-empty distinguished name (RFC 5755 sec. 4.2.3)");
|
|
153
153
|
}
|
|
@@ -217,14 +217,14 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
|
|
|
217
217
|
var attributes = acinfo.fields.attributes.value.result;
|
|
218
218
|
attributes.forEach(function (a) {
|
|
219
219
|
var dec = _ATTR_VALUE_DECODERS[a.type];
|
|
220
|
-
a.decoded = a.values.map(function (v) { return dec ? dec(v) : { opaque: true, bytes: v }; });
|
|
220
|
+
a.decoded = a.values.map(function (v) { return dec ? dec(v, ctx) : { opaque: true, bytes: v }; });
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
var extField = acinfo.fields.extensions;
|
|
224
224
|
var extensions = (extField && extField.present) ? extField.value.result : [];
|
|
225
225
|
extensions.forEach(function (x) {
|
|
226
226
|
var dec = _AC_EXT_DECODERS[x.oid];
|
|
227
|
-
x.decoded = dec ? dec(x.value) : { opaque: true, bytes: x.value };
|
|
227
|
+
x.decoded = dec ? dec(x.value, ctx) : { opaque: true, bytes: x.value };
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
return {
|
|
@@ -398,12 +398,13 @@ function _oneGN(node, code) {
|
|
|
398
398
|
|
|
399
399
|
// @internal
|
|
400
400
|
function readGeneralName(der, code) {
|
|
401
|
+
// allow:bare-decode reached from the builder, not from a parse, so no parse context carries caps here.
|
|
401
402
|
return _oneGN(asn1.decode(der), code || "attrcert/bad-input");
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
function _svceAuthInfo(authInfoAllowed, code) {
|
|
405
|
-
return function (buf) {
|
|
406
|
-
var kids = _seqChildren(buf, code, "SvceAuthInfo");
|
|
406
|
+
return function (buf, ctx) {
|
|
407
|
+
var kids = _seqChildren(buf, code, "SvceAuthInfo", ctx);
|
|
407
408
|
if (kids.length < 2 || kids.length > 3) throw NS.E(code, "SvceAuthInfo must be a SEQUENCE { service, ident, authInfo? }");
|
|
408
409
|
var service = _oneGN(kids[0], code);
|
|
409
410
|
var ident = _oneGN(kids[1], code);
|
|
@@ -417,12 +418,12 @@ function _svceAuthInfo(authInfoAllowed, code) {
|
|
|
417
418
|
};
|
|
418
419
|
}
|
|
419
420
|
|
|
420
|
-
function _ietfAttrSyntax(buf) {
|
|
421
|
+
function _ietfAttrSyntax(buf, ctx) {
|
|
421
422
|
var C = "attrcert/bad-ietf-attr";
|
|
422
|
-
var kids = _seqChildren(buf, C, "IetfAttrSyntax");
|
|
423
|
+
var kids = _seqChildren(buf, C, "IetfAttrSyntax", ctx);
|
|
423
424
|
if (!kids.length || kids.length > 2) throw NS.E(C, "IetfAttrSyntax must be a SEQUENCE { policyAuthority [0]?, values }");
|
|
424
425
|
var policyAuthority = null, i = 0;
|
|
425
|
-
if (_isCtx(kids[0], 0)) { policyAuthority = schema.walk(_GNS_0, kids[0], NS).result; i = 1; }
|
|
426
|
+
if (_isCtx(kids[0], 0)) { policyAuthority = schema.walk(_GNS_0, kids[0], schema.withResources(NS, ctx)).result; i = 1; }
|
|
426
427
|
var valuesNode = kids[i];
|
|
427
428
|
if (!valuesNode || !_isU(valuesNode, TAGS.SEQUENCE) || !valuesNode.children || !valuesNode.children.length) {
|
|
428
429
|
throw NS.E(C, "IetfAttrSyntax values must be a non-empty SEQUENCE OF value");
|
|
@@ -438,12 +439,12 @@ function _ietfAttrSyntax(buf) {
|
|
|
438
439
|
return { policyAuthority: policyAuthority, values: values, homogeneous: Object.keys(kinds).length === 1 };
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
function _role(buf) {
|
|
442
|
+
function _role(buf, ctx) {
|
|
442
443
|
var C = "attrcert/bad-role";
|
|
443
|
-
var kids = _seqChildren(buf, C, "RoleSyntax");
|
|
444
|
+
var kids = _seqChildren(buf, C, "RoleSyntax", ctx);
|
|
444
445
|
if (!kids.length || kids.length > 2) throw NS.E(C, "RoleSyntax must be a SEQUENCE { roleAuthority [0]?, roleName [1] }");
|
|
445
446
|
var roleAuthority = null, i = 0;
|
|
446
|
-
if (_isCtx(kids[0], 0)) { roleAuthority = schema.walk(_GNS_0, kids[0], NS).result; i = 1; }
|
|
447
|
+
if (_isCtx(kids[0], 0)) { roleAuthority = schema.walk(_GNS_0, kids[0], schema.withResources(NS, ctx)).result; i = 1; }
|
|
447
448
|
var rn = kids[i];
|
|
448
449
|
if (!_isCtx(rn, 1) || !rn.children || rn.children.length !== 1) throw NS.E(C, "RoleSyntax roleName must be an EXPLICIT [1] wrapping exactly one GeneralName");
|
|
449
450
|
if (i + 1 !== kids.length) throw NS.E(C, "RoleSyntax has unexpected trailing fields");
|
|
@@ -473,9 +474,9 @@ function _securityCategory(node, C) {
|
|
|
473
474
|
if (!_isCtx(valueNode, 1) || !valueNode.children || valueNode.children.length !== 1) throw NS.E(C, "SecurityCategory value must be an EXPLICIT [1] wrapping one element");
|
|
474
475
|
return { type: type, valueBytes: Buffer.concat([valueNode.children[0].bytes]) };
|
|
475
476
|
}
|
|
476
|
-
function _clearance(buf) {
|
|
477
|
+
function _clearance(buf, ctx) {
|
|
477
478
|
var C = "attrcert/bad-clearance";
|
|
478
|
-
var kids = _seqChildren(buf, C, "Clearance");
|
|
479
|
+
var kids = _seqChildren(buf, C, "Clearance", ctx);
|
|
479
480
|
if (!kids.length || kids.length > 3) throw NS.E(C, "Clearance must be a SEQUENCE { policyId, classList?, securityCategories? }");
|
|
480
481
|
if (!_isU(kids[0], TAGS.OBJECT_IDENTIFIER)) throw NS.E(C, "Clearance policyId must be an OBJECT IDENTIFIER");
|
|
481
482
|
var policyId; try { policyId = asn1.read.oid(kids[0]); } catch (e) { throw NS.E(C, "Clearance policyId must be an OBJECT IDENTIFIER", e); }
|
|
@@ -508,16 +509,16 @@ _ATTR_VALUE_DECODERS[_Oav("chargingIdentity")] = _ietfAttrSyntax;
|
|
|
508
509
|
_ATTR_VALUE_DECODERS[_Oav("group")] = _ietfAttrSyntax;
|
|
509
510
|
|
|
510
511
|
|
|
511
|
-
function _acAuditIdentity(buf) {
|
|
512
|
+
function _acAuditIdentity(buf, ctx) {
|
|
512
513
|
var C = "attrcert/bad-audit-identity";
|
|
513
|
-
var n = _decodeTop(buf, C, "AuditIdentity");
|
|
514
|
+
var n = _decodeTop(buf, C, "AuditIdentity", ctx);
|
|
514
515
|
if (!_isU(n, TAGS.OCTET_STRING)) throw NS.E(C, "AuditIdentity must be an OCTET STRING");
|
|
515
516
|
return Buffer.concat([asn1.read.octetString(n)]);
|
|
516
517
|
}
|
|
517
518
|
|
|
518
|
-
function _noRevAvail(buf) {
|
|
519
|
+
function _noRevAvail(buf, ctx) {
|
|
519
520
|
var C = "attrcert/bad-no-rev-avail";
|
|
520
|
-
var n = _decodeTop(buf, C, "NoRevAvail");
|
|
521
|
+
var n = _decodeTop(buf, C, "NoRevAvail", ctx);
|
|
521
522
|
try { asn1.read.nullValue(n); } catch (e) { throw NS.E(C, "NoRevAvail must be a DER NULL (RFC 5755 sec. 4.3.6)", e); }
|
|
522
523
|
return { noRevAvail: true };
|
|
523
524
|
}
|
|
@@ -546,13 +547,13 @@ function _acDecodeTargets(node, C, allowTargetCert) {
|
|
|
546
547
|
if (!_isU(node, TAGS.SEQUENCE) || !node.children || !node.children.length) throw NS.E(C, "Targets must be a non-empty SEQUENCE OF Target");
|
|
547
548
|
return node.children.map(function (t) { return _acTarget(t, C, allowTargetCert); });
|
|
548
549
|
}
|
|
549
|
-
function _seqOfTargets(buf, C, allowTargetCert) {
|
|
550
|
-
var kids = _seqChildren(buf, C, "SEQUENCE OF Targets");
|
|
550
|
+
function _seqOfTargets(buf, C, allowTargetCert, ctx) {
|
|
551
|
+
var kids = _seqChildren(buf, C, "SEQUENCE OF Targets", ctx);
|
|
551
552
|
if (!kids.length) throw NS.E(C, "a SEQUENCE OF Targets must be non-empty");
|
|
552
553
|
return kids.map(function (targetsNode) { return _acDecodeTargets(targetsNode, C, allowTargetCert); });
|
|
553
554
|
}
|
|
554
|
-
function _targetInformation(buf) { return _seqOfTargets(buf, "attrcert/bad-targets", true); }
|
|
555
|
-
function _acProxying(buf) { return _seqOfTargets(buf, "attrcert/bad-proxy-info", false); }
|
|
555
|
+
function _targetInformation(buf, ctx) { return _seqOfTargets(buf, "attrcert/bad-targets", true, ctx); }
|
|
556
|
+
function _acProxying(buf, ctx) { return _seqOfTargets(buf, "attrcert/bad-proxy-info", false, ctx); }
|
|
556
557
|
|
|
557
558
|
function _acAttrSpec(node, C) {
|
|
558
559
|
if (!node.children) throw NS.E(C, "an AttrSpec must be a SEQUENCE OF OBJECT IDENTIFIER");
|
|
@@ -561,9 +562,9 @@ function _acAttrSpec(node, C) {
|
|
|
561
562
|
try { return asn1.read.oid(o); } catch (e) { throw NS.E(C, "an AttrSpec element must be an OBJECT IDENTIFIER", e); }
|
|
562
563
|
});
|
|
563
564
|
}
|
|
564
|
-
function _aaControls(buf) {
|
|
565
|
+
function _aaControls(buf, ctx) {
|
|
565
566
|
var C = "attrcert/bad-aa-controls";
|
|
566
|
-
var kids = _seqChildren(buf, C, "AAControls");
|
|
567
|
+
var kids = _seqChildren(buf, C, "AAControls", ctx);
|
|
567
568
|
if (kids.length > 4) throw NS.E(C, "AAControls has too many fields");
|
|
568
569
|
var out = { pathLenConstraint: null, permittedAttrs: null, excludedAttrs: null, permitUnSpecified: true }, i = 0;
|
|
569
570
|
if (kids[i] && _isU(kids[i], TAGS.INTEGER)) { out.pathLenConstraint = guard.range.uint31(_readInt(kids[i], C, "pathLenConstraint"), NS.E, C, "AAControls pathLenConstraint"); i++; }
|
|
@@ -588,9 +589,18 @@ _AC_EXT_DECODERS[_Oav("acProxying")] = _acProxying;
|
|
|
588
589
|
* through the certificate decoders, so a relying party running the sec. 6 "pointer in AC" scheme
|
|
589
590
|
* reads the distribution point and the OCSP location off the parse rather than off raw bytes. */
|
|
590
591
|
var _CERT_EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
592
|
+
/** @internal The certificate decoders close over the namespace they were built with, so a parse
|
|
593
|
+
* that named caps gets its own table and every decode below these three reads the caller's
|
|
594
|
+
* figure. A caller outside a parse passes no context and reuses the module's table. */
|
|
595
|
+
function _certExtDecoder(name) {
|
|
596
|
+
var key = _Oav(name);
|
|
597
|
+
return function (buf, ctx) {
|
|
598
|
+
return (ctx ? pkix.certExtensionDecoders(schema.withResources(NS, ctx)).byOid : _CERT_EXT_DECODERS)[key](buf);
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
_AC_EXT_DECODERS[_Oav("authorityKeyIdentifier")] = _certExtDecoder("authorityKeyIdentifier");
|
|
602
|
+
_AC_EXT_DECODERS[_Oav("authorityInfoAccess")] = _certExtDecoder("authorityInfoAccess");
|
|
603
|
+
_AC_EXT_DECODERS[_Oav("cRLDistributionPoints")] = _certExtDecoder("cRLDistributionPoints");
|
|
594
604
|
|
|
595
605
|
// @internal
|
|
596
606
|
function validateAttributeValue(type, valueDer) {
|