@blamejs/pki 0.8.30 → 0.8.48
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 +411 -0
- package/README.md +67 -16
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +28 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +939 -48
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +448 -2
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +267 -4
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/revocation-fetch.js
CHANGED
|
@@ -352,8 +352,8 @@ async function _tryOcsp(state, cert, issuer, ctx, candidate) {
|
|
|
352
352
|
if (!state.nonce && v.nextUpdate && v.thisUpdate) {
|
|
353
353
|
state.cache.put(key, hit, v.thisUpdate, v.nextUpdate, hit.length);
|
|
354
354
|
}
|
|
355
|
-
return { status: v.status, reason: "an OCSP response from " + candidate.url,
|
|
356
|
-
revocationReason: v.revocationReason, revocationTime: v.revocationTime };
|
|
355
|
+
return guard.verdict.of({ status: v.status, reason: "an OCSP response from " + candidate.url,
|
|
356
|
+
revocationReason: v.revocationReason, revocationTime: v.revocationTime });
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
/** @internal The bytes the nonce extension carries, which is what the response echoes. The
|
|
@@ -490,14 +490,14 @@ async function _tryCrl(state, cert, issuer, ctx, candidates, certDeltas) {
|
|
|
490
490
|
var interim = await state.crlChecker(gathered, { useDeltas: state.useDeltas }).check(cert, issuer, ctx);
|
|
491
491
|
if (interim && (interim.status === "good" || interim.status === "revoked")) {
|
|
492
492
|
if (authentic) _keep(state, url, parsed, fetchedBytes, cert.issuer.bytes, issuer.workingPublicKey);
|
|
493
|
-
return { status: interim.status, reason: "a CRL from " + url,
|
|
494
|
-
revocationReason: _reasonCodeOf(interim), revocationTime: interim.revocationTime };
|
|
493
|
+
return guard.verdict.of({ status: interim.status, reason: "a CRL from " + url,
|
|
494
|
+
revocationReason: _reasonCodeOf(interim), revocationTime: interim.revocationTime });
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
497
|
if (gathered.length === 0) throw E("path/revocation-fetch-failed", "no CRL was fetched");
|
|
498
498
|
var last = await state.crlChecker(gathered, { useDeltas: state.useDeltas }).check(cert, issuer, ctx);
|
|
499
|
-
return { status: (last && last.status) || "unknown", reason: (last && last.reason) || "the fetched CRLs did not settle the status",
|
|
500
|
-
revocationReason: _reasonCodeOf(last), revocationTime: last && last.revocationTime };
|
|
499
|
+
return guard.verdict.of({ status: (last && last.status) || "unknown", reason: (last && last.reason) || "the fetched CRLs did not settle the status",
|
|
500
|
+
revocationReason: _reasonCodeOf(last), revocationTime: last && last.revocationTime });
|
|
501
501
|
}
|
|
502
502
|
|
|
503
503
|
/** @internal `crlChecker` names the CRLReason integer `reasonCode` and `verifyOcspResponse` names it
|
package/lib/scep.js
CHANGED
|
@@ -473,7 +473,7 @@ function _buildIssuerAndSerial(spec) {
|
|
|
473
473
|
*
|
|
474
474
|
* @opts
|
|
475
475
|
* - `recipientKey` -- `{ cert, key }` for the recipient, to decrypt the pkcsPKIEnvelope into `messageData`.
|
|
476
|
-
* - `signerCert` -- the expected signer certificate DER (the CA certificate for a CertRep); the message signer's public key must match it, or the message is refused.
|
|
476
|
+
* - `signerCert` -- the expected signer certificate DER (the CA certificate for a CertRep); the message signer's public key must match it, or the message is refused. It also decides whether an envelope using RSAES-PKCS1-v1_5 key transport over a content with no integrity tag is read: that combination is refused for a signer this option has not authenticated.
|
|
477
477
|
* - `expectedSenderNonce` -- a 16-byte Buffer; a message whose `recipientNonce` does not echo it is refused.
|
|
478
478
|
* @example
|
|
479
479
|
* // requires: pkiMessage -- a SCEP pkiMessage DER (from pki.scep.build, or a CA's response)
|
|
@@ -571,7 +571,17 @@ async function parse(bytes, opts) {
|
|
|
571
571
|
}
|
|
572
572
|
if (recipientKey != null && carriesEnvelope && v.eContent != null) {
|
|
573
573
|
var dec;
|
|
574
|
-
|
|
574
|
+
/** @internal The opt-in is passed only once the signer has been authenticated, which is what the
|
|
575
|
+
* premise rests on. `pki.cms.decrypt` refuses RSAES-PKCS1-v1_5 key transport over a content with no
|
|
576
|
+
* integrity tag because acceptance tells an attacker submitting chosen ciphertexts that theirs
|
|
577
|
+
* decoded. With `opts.signerCert` matched, the envelope arrived inside a signature an authorized
|
|
578
|
+
* peer made, and an attacker who cannot sign as that peer cannot submit one. Without it the
|
|
579
|
+
* signature is any signer's, this verb reports `signerAuthenticated: false`, and the caller
|
|
580
|
+
* authenticates the surfaced certificate after the decrypt has already produced its outcome, so the
|
|
581
|
+
* refusal stands. RFC 8894 sec. 3.1 puts the envelope there, and v1.5 over a CBC content is what
|
|
582
|
+
* deployed SCEP peers send, so an authenticated exchange still reads them. */
|
|
583
|
+
var decOpts = signerAuthenticated ? { allowUnauthenticatedRsa15: true } : {};
|
|
584
|
+
try { dec = await cmsDecrypt.decrypt(v.eContent, recipientKey, decOpts); }
|
|
575
585
|
catch (e) {
|
|
576
586
|
if (e && e.isPkiError) throw _err("scep/decrypt-failed", "the pkcsPKIEnvelope could not be decrypted with the recipient key: " + e.message, e);
|
|
577
587
|
throw e;
|
|
@@ -606,7 +616,7 @@ async function parse(bytes, opts) {
|
|
|
606
616
|
if (isnCh[1].content.length > 20) throw _err("scep/bad-request-payload", "a " + messageType + " messageData serialNumber must not exceed 20 octets (RFC 5280 sec. 4.1.2.2)");
|
|
607
617
|
}
|
|
608
618
|
}
|
|
609
|
-
return out;
|
|
619
|
+
return guard.verdict.shield(out);
|
|
610
620
|
}
|
|
611
621
|
var _PARSE_KEYS = Object.assign(Object.create(null), { recipientKey: 1, expectedSenderNonce: 1, signerCert: 1 });
|
|
612
622
|
|
|
@@ -1006,7 +1016,7 @@ function _dispatchCertRep(rep, transactionId, spki) {
|
|
|
1006
1016
|
if (findIssuedCert(repCerts.filter(function (c) { return c !== issued; }), spki.bytes) != null) {
|
|
1007
1017
|
throw _err("scep/ambiguous-issued-cert", "the SUCCESS CertRep carried more than one certificate matching the request public key; the issued certificate is ambiguous (RFC 8894 sec. 3.3)");
|
|
1008
1018
|
}
|
|
1009
|
-
return { pending: false, result: { status: "SUCCESS", certificate: issued, certificates: repCerts, transactionId: rep.transactionId } };
|
|
1019
|
+
return guard.verdict.of({ pending: false, result: guard.verdict.of({ status: "SUCCESS", certificate: issued, certificates: repCerts, transactionId: rep.transactionId }) });
|
|
1010
1020
|
}
|
|
1011
1021
|
|
|
1012
1022
|
function _pollExhausted(which, transactionId, senderNonce) {
|
|
@@ -1089,7 +1099,7 @@ async function _pkiOperation(messageType, baseUrl, opts, verbName) {
|
|
|
1089
1099
|
var rep = await parse(body, { signerCert: signerCert, expectedSenderNonce: senderNonce, recipientKey: recipientKey });
|
|
1090
1100
|
var d = _dispatchCertRep(rep, transactionId, spki);
|
|
1091
1101
|
if (!d.pending) return d.result;
|
|
1092
|
-
if (pollCount <= 0) return { status: "PENDING", transactionId: rep.transactionId, senderNonce: senderNonce };
|
|
1102
|
+
if (pollCount <= 0) return guard.verdict.of({ status: "PENDING", transactionId: rep.transactionId, senderNonce: senderNonce });
|
|
1093
1103
|
return _pollForIssuance({
|
|
1094
1104
|
firstResponse: res, firstSenderNonce: senderNonce, postUrl: postUrl, httpMethod: httpMethod, clientOpts: clientOpts,
|
|
1095
1105
|
caCert: caCert, csrSubject: csrSubject, issuerCert: issuerCert, signer: signer, signerCert: signerCert, recipientKey: recipientKey,
|
package/lib/schema-c509.js
CHANGED
|
@@ -1282,7 +1282,7 @@ function _requireStrictDerTlv(content, code, label) {
|
|
|
1282
1282
|
|
|
1283
1283
|
function _derSetInDeclaredOrder(vals) {
|
|
1284
1284
|
for (var i = 1; i < vals.length; i++) {
|
|
1285
|
-
if (
|
|
1285
|
+
if (guard.bytes.compare(vals[i - 1], vals[i]) > 0) throw _err("c509/bad-extensions", "a subjectDirectoryAttributes attributeValue list must be in DER ascending order (X.690 sec. 11.6)");
|
|
1286
1286
|
}
|
|
1287
1287
|
return b.set(vals);
|
|
1288
1288
|
}
|
|
@@ -2033,7 +2033,10 @@ function encode(input, opts) {
|
|
|
2033
2033
|
var origDer = pkix.coerceToDer(input, { pemLabel: "CERTIFICATE", PemError: frameworkError.PemError, ErrorClass: C509Error, prefix: "c509" });
|
|
2034
2034
|
var encoded = _encodeC509Array(_derToType3(origDer, opts));
|
|
2035
2035
|
var recon = parse(encoded).reconstructedDer;
|
|
2036
|
-
|
|
2036
|
+
/** @internal Through the captured comparator: this IS the byte-for-byte claim a type-3 C509 makes
|
|
2037
|
+
* about the certificate it re-encodes, so a replacement answering zero asserts it over bytes that
|
|
2038
|
+
* do not match. */
|
|
2039
|
+
if (!recon || guard.bytes.compare(recon, origDer) !== 0) throw _err("c509/non-invertible", "the type-3 C509 does not reconstruct the source certificate byte-for-byte");
|
|
2037
2040
|
return encoded;
|
|
2038
2041
|
}
|
|
2039
2042
|
|
package/lib/schema-cmc.js
CHANGED
|
@@ -245,7 +245,7 @@ function parseStatusInfo(valueDer, version, bodyPartID, ctx) {
|
|
|
245
245
|
extendedFailInfo: other ? other.extendedFailInfo : null,
|
|
246
246
|
};
|
|
247
247
|
_assertStatusCoherent(out);
|
|
248
|
-
return out;
|
|
248
|
+
return guard.verdict.shield(out);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
function _assertStatusCoherent(s) {
|
package/lib/schema-cmp.js
CHANGED
|
@@ -231,11 +231,11 @@ var PKI_STATUS_INFO = schema.seq([
|
|
|
231
231
|
], {
|
|
232
232
|
assert: "sequence", code: "cmp/bad-status-info", what: "PKIStatusInfo",
|
|
233
233
|
build: function (m) {
|
|
234
|
-
return {
|
|
234
|
+
return guard.verdict.of({
|
|
235
235
|
status: m.fields.status.value,
|
|
236
236
|
statusString: m.fields.statusString.present ? m.fields.statusString.value.result : null,
|
|
237
237
|
failInfo: m.fields.failInfo.present ? m.fields.failInfo.value : null,
|
|
238
|
-
};
|
|
238
|
+
});
|
|
239
239
|
},
|
|
240
240
|
});
|
|
241
241
|
|
|
@@ -329,11 +329,11 @@ var REV_REP_CONTENT = schema.seq([
|
|
|
329
329
|
], {
|
|
330
330
|
assert: "sequence", code: "cmp/bad-rev-rep", what: "RevRepContent",
|
|
331
331
|
build: function (m) {
|
|
332
|
-
return {
|
|
332
|
+
return guard.verdict.of({
|
|
333
333
|
status: m.fields.status.value.result,
|
|
334
334
|
revCerts: m.fields.revCerts.present ? m.fields.revCerts.value.result : null,
|
|
335
335
|
crls: m.fields.crls.present ? m.fields.crls.value.result : null,
|
|
336
|
-
};
|
|
336
|
+
});
|
|
337
337
|
},
|
|
338
338
|
});
|
|
339
339
|
|
|
@@ -411,12 +411,12 @@ var CERT_RESPONSE = schema.seq([
|
|
|
411
411
|
throw ctx.E("cmp/bad-cert-response",
|
|
412
412
|
"a CertResponse certifiedKeyPair is allowed only under a granting status (accepted or grantedWithMods) (RFC 9810 sec. 5.3.4)");
|
|
413
413
|
}
|
|
414
|
-
return {
|
|
414
|
+
return guard.verdict.of({
|
|
415
415
|
certReqId: m.fields.certReqId.value,
|
|
416
416
|
status: status,
|
|
417
417
|
certifiedKeyPair: certifiedKeyPair,
|
|
418
418
|
rspInfo: m.fields.rspInfo.present ? m.fields.rspInfo.value : null,
|
|
419
|
-
};
|
|
419
|
+
});
|
|
420
420
|
},
|
|
421
421
|
});
|
|
422
422
|
|
|
@@ -455,12 +455,12 @@ var KEY_REC_REP_CONTENT = schema.seq([
|
|
|
455
455
|
], {
|
|
456
456
|
assert: "sequence", code: "cmp/bad-key-rec-rep", what: "KeyRecRepContent",
|
|
457
457
|
build: function (m) {
|
|
458
|
-
return {
|
|
458
|
+
return guard.verdict.of({
|
|
459
459
|
status: m.fields.status.value.result,
|
|
460
460
|
newSigCert: m.fields.newSigCert.present ? m.fields.newSigCert.value : null,
|
|
461
461
|
caCerts: m.fields.caCerts.present ? m.fields.caCerts.value.result : null,
|
|
462
462
|
keyPairHist: m.fields.keyPairHist.present ? m.fields.keyPairHist.value.result : null,
|
|
463
|
-
};
|
|
463
|
+
});
|
|
464
464
|
},
|
|
465
465
|
});
|
|
466
466
|
|
package/lib/schema-cms.js
CHANGED
|
@@ -60,7 +60,6 @@ var NS = pkix.makeNS("cms", CmsError, oid);
|
|
|
60
60
|
|
|
61
61
|
var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
62
62
|
var ATTRIBUTE = pkix.attribute(NS);
|
|
63
|
-
var NAME = pkix.name(NS);
|
|
64
63
|
|
|
65
64
|
var SIGNED_DATA_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3, "4": 4, "5": 5 });
|
|
66
65
|
var SIGNER_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3 });
|
|
@@ -179,7 +178,7 @@ function _readsAsTime(vals) {
|
|
|
179
178
|
|
|
180
179
|
function _ascendingDer(nodes) {
|
|
181
180
|
for (var i = 1; i < nodes.length; i++) {
|
|
182
|
-
if (
|
|
181
|
+
if (guard.bytes.compare(nodes[i - 1].bytes, nodes[i].bytes) > 0) return false;
|
|
183
182
|
}
|
|
184
183
|
return true;
|
|
185
184
|
}
|
|
@@ -330,19 +329,7 @@ var ENCAP_CONTENT_INFO = schema.seq([
|
|
|
330
329
|
},
|
|
331
330
|
});
|
|
332
331
|
|
|
333
|
-
var ISSUER_AND_SERIAL =
|
|
334
|
-
schema.field("issuer", NAME),
|
|
335
|
-
schema.field("serialNumber", schema.integerLeaf()),
|
|
336
|
-
], {
|
|
337
|
-
assert: "sequence", arity: { exact: 2 }, code: "cms/bad-issuer-and-serial", what: "IssuerAndSerialNumber",
|
|
338
|
-
build: function (m) {
|
|
339
|
-
return {
|
|
340
|
-
issuer: m.fields.issuer.value.result,
|
|
341
|
-
serialNumber: m.fields.serialNumber.value,
|
|
342
|
-
serialNumberHex: m.fields.serialNumber.node.content.toString("hex"),
|
|
343
|
-
};
|
|
344
|
-
},
|
|
345
|
-
});
|
|
332
|
+
var ISSUER_AND_SERIAL = pkix.issuerAndSerialNumber(NS, { code: "cms/bad-issuer-and-serial" });
|
|
346
333
|
|
|
347
334
|
var SIGNER_IDENTIFIER = schema.choice([
|
|
348
335
|
{ when: { tagClass: "universal", tagNumber: asn1.TAGS.SEQUENCE }, schema: ISSUER_AND_SERIAL },
|
package/lib/schema-csr.js
CHANGED
|
@@ -35,6 +35,7 @@ var pkix = require("./schema-pkix");
|
|
|
35
35
|
var oid = require("./oid");
|
|
36
36
|
var frameworkError = require("./framework-error");
|
|
37
37
|
var intrinsic = require("./guard-intrinsic");
|
|
38
|
+
var constants = require("./constants");
|
|
38
39
|
|
|
39
40
|
var CsrError = frameworkError.CsrError;
|
|
40
41
|
var PemError = frameworkError.PemError;
|
|
@@ -73,6 +74,74 @@ RECOGNIZED_ATTRIBUTE_VALUE[oid.byName("challengePassword")] = function (node, ct
|
|
|
73
74
|
}
|
|
74
75
|
};
|
|
75
76
|
|
|
77
|
+
/** @internal RequesterCertificate ::= SEQUENCE { certID IssuerAndSerialNumber, requestTime BinaryTime,
|
|
78
|
+
* locationInfo UniformResourceIdentifiers, signature BIT STRING } (RFC 9763 sec. 3.1), where
|
|
79
|
+
* UniformResourceIdentifiers ::= SEQUENCE SIZE (1..MAX) OF URI and URI ::= IA5String. locationInfo is
|
|
80
|
+
* a SEQUENCE OF rather than one URI, and it is the one field the signature does not cover. */
|
|
81
|
+
var REQUESTER_CERTIFICATE = schema.seq([
|
|
82
|
+
schema.field("certID", pkix.issuerAndSerialNumber(NS, { code: "csr/bad-attribute-value" })),
|
|
83
|
+
schema.field("requestTime", schema.integerLeaf()),
|
|
84
|
+
/** @internal A SEQUENCE OF, so the DER SET-OF ordering rule does not apply and the URIs keep the
|
|
85
|
+
* order the requester wrote them in. */
|
|
86
|
+
schema.field("locationInfo", schema.seqOf(schema.decode(function (n, ctx) {
|
|
87
|
+
if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.IA5_STRING) {
|
|
88
|
+
throw ctx.E("csr/bad-attribute-value", "a RequesterCertificate locationInfo entry must be an IA5String URI (RFC 9763 sec. 3.1)");
|
|
89
|
+
}
|
|
90
|
+
return asn1.read.string(n);
|
|
91
|
+
}), { assert: "sequence", min: 1, max: constants.LIMITS.ATTRIBUTE_MAX_VALUES,
|
|
92
|
+
code: "csr/bad-attribute-value", what: "RequesterCertificate locationInfo" })),
|
|
93
|
+
schema.field("signature", schema.bitString()),
|
|
94
|
+
], {
|
|
95
|
+
assert: "sequence", arity: { exact: 4 }, code: "csr/bad-attribute-value", what: "RequesterCertificate",
|
|
96
|
+
build: function (m, ctx) {
|
|
97
|
+
var t = m.fields.requestTime.value;
|
|
98
|
+
if (t < 0n) throw ctx.E("csr/bad-attribute-value", "a RequesterCertificate requestTime must not be negative (BinaryTime is INTEGER (0..MAX), RFC 6019 sec. 2)");
|
|
99
|
+
var bits = m.fields.signature.value;
|
|
100
|
+
if (bits.unusedBits !== 0) throw ctx.E("csr/bad-attribute-value", "a RequesterCertificate signature BIT STRING must be octet-aligned");
|
|
101
|
+
if (bits.bytes.length === 0) throw ctx.E("csr/bad-attribute-value", "a RequesterCertificate signature must not be empty");
|
|
102
|
+
return {
|
|
103
|
+
certID: m.fields.certID.value.result,
|
|
104
|
+
requestTime: t,
|
|
105
|
+
locationInfo: intrinsic.map(m.fields.locationInfo.value.items, function (it) { return it.value; }),
|
|
106
|
+
signature: { unusedBits: bits.unusedBits, bytes: bits.bytes },
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
/** @internal PrivateKeyPossessionStatement ::= SEQUENCE { signer IssuerAndSerialNumber,
|
|
111
|
+
* cert Certificate OPTIONAL } (RFC 9883 sec. 3). The signer is the shared IssuerAndSerialNumber; the
|
|
112
|
+
* certificate is surfaced as raw DER, because it is a whole Certificate a caller parses and verifies
|
|
113
|
+
* rather than a field read here. */
|
|
114
|
+
var POSSESSION_STATEMENT = schema.seq([
|
|
115
|
+
schema.field("signer", pkix.issuerAndSerialNumber(NS, { code: "csr/bad-attribute-value" })),
|
|
116
|
+
schema.optional("certificate", schema.any(), { whenUniversal: [asn1.TAGS.SEQUENCE] }),
|
|
117
|
+
], {
|
|
118
|
+
assert: "sequence", arity: { min: 1, max: 2 },
|
|
119
|
+
code: "csr/bad-attribute-value", what: "PrivateKeyPossessionStatement",
|
|
120
|
+
build: function (m) {
|
|
121
|
+
return {
|
|
122
|
+
signer: m.fields.signer.value.result,
|
|
123
|
+
certificate: m.fields.certificate.present ? m.fields.certificate.node.bytes : null,
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
RECOGNIZED_ATTRIBUTE_VALUE[oid.byName("statementOfPossession")] = function (node, ctx) {
|
|
128
|
+
try { return { privateKeyPossessionStatement: schema.walk(POSSESSION_STATEMENT, node, ctx).result }; }
|
|
129
|
+
catch (e) {
|
|
130
|
+
if (e instanceof CsrError) throw e;
|
|
131
|
+
throw ctx.E("csr/bad-attribute-value",
|
|
132
|
+
"statementOfPossession value must be a well-formed PrivateKeyPossessionStatement SEQUENCE (RFC 9883 sec. 3): " + ((e && e.message) || intrinsic.String(e)), e);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
RECOGNIZED_ATTRIBUTE_VALUE[oid.byName("relatedCertRequest")] = function (node, ctx) {
|
|
137
|
+
try { return { relatedCertRequest: schema.walk(REQUESTER_CERTIFICATE, node, ctx).result }; }
|
|
138
|
+
catch (e) {
|
|
139
|
+
if (e instanceof CsrError) throw e;
|
|
140
|
+
throw ctx.E("csr/bad-attribute-value",
|
|
141
|
+
"relatedCertRequest value must be a well-formed RequesterCertificate SEQUENCE (RFC 9763 sec. 3.1): " + ((e && e.message) || intrinsic.String(e)), e);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
76
145
|
var CERTIFICATION_REQUEST_INFO = schema.seq([
|
|
77
146
|
schema.field("version", CSR_VERSION),
|
|
78
147
|
schema.field("subject", NAME),
|
package/lib/schema-engine.js
CHANGED
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
|
|
29
29
|
var asn1 = require("./asn1-der.js");
|
|
30
30
|
var intrinsic = require("./guard-intrinsic");
|
|
31
|
+
/** @internal Just the list guard, not the whole family: this module sits below the format layer and
|
|
32
|
+
* takes the one primitive it needs. The copy-and-sort it provides reads `length` once and orders
|
|
33
|
+
* through operations captured at load, which is what a DER SET ordering rests on. */
|
|
34
|
+
var guardList = require("./guard-list");
|
|
31
35
|
|
|
32
36
|
var TAGS = asn1.TAGS;
|
|
33
37
|
|
|
@@ -310,7 +314,7 @@ function _walkRepeat(schema, node, ctx) {
|
|
|
310
314
|
}
|
|
311
315
|
if (schema.derSetOrder) {
|
|
312
316
|
for (var s = 1; s < kids.length; s++) {
|
|
313
|
-
if (
|
|
317
|
+
if (intrinsic.compare(kids[s - 1].bytes, kids[s].bytes) > 0) {
|
|
314
318
|
_fail(ctx, schema.code, (schema.what || "value") + " components must be in ascending DER order (X.690 sec. 11.6)");
|
|
315
319
|
}
|
|
316
320
|
}
|
|
@@ -474,7 +478,7 @@ function _encodeSeq(schema, value, ctx) {
|
|
|
474
478
|
if (!isDefault) parts.push(fld.explicit ? asn1.build.explicit(fld.tag, enc) : enc);
|
|
475
479
|
}
|
|
476
480
|
} else if (fld.fkind === "trailing") {
|
|
477
|
-
var members = fld.members
|
|
481
|
+
var members = guardList.sortedCopy(fld.members, function (a, b) { return a.tag - b.tag; });
|
|
478
482
|
for (var i = 0; i < members.length; i++) {
|
|
479
483
|
var mm = members[i];
|
|
480
484
|
if (_present(value[mm.name])) {
|
|
@@ -509,7 +513,10 @@ function _encodeRepeat(schema, items, ctx) {
|
|
|
509
513
|
seen.add(key);
|
|
510
514
|
});
|
|
511
515
|
}
|
|
512
|
-
|
|
516
|
+
/** @internal The comparator goes through the captured operation, as the copy and the sort do. This
|
|
517
|
+
* is the engine's own SET OF emission, so whatever decides "before" decides the bytes every schema
|
|
518
|
+
* built on it emits. */
|
|
519
|
+
if (schema.derSetOrder) parts = guardList.sortedCopy(parts, intrinsic.compare);
|
|
513
520
|
if (schema.implicitTag != null) return asn1.build.contextConstructed(schema.implicitTag, Buffer.concat(parts));
|
|
514
521
|
return schema.assert === "set" ? asn1.build.set(parts) : asn1.build.sequence(parts);
|
|
515
522
|
}
|