@blamejs/pki 0.6.14 → 0.6.15
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 +14 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +8 -252
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-crl.js
CHANGED
|
@@ -29,8 +29,6 @@ var pkix = require("./schema-pkix");
|
|
|
29
29
|
var oid = require("./oid");
|
|
30
30
|
var frameworkError = require("./framework-error");
|
|
31
31
|
var intrinsic = require("./guard-intrinsic");
|
|
32
|
-
// Own-key membership through an operation taken at load. Written out, the question reads two
|
|
33
|
-
// replaceable properties to answer one thing, and here it decides which CRLReason values are defined.
|
|
34
32
|
var _hasOwn = intrinsic.hasOwn;
|
|
35
33
|
|
|
36
34
|
var CrlError = frameworkError.CrlError;
|
|
@@ -38,52 +36,32 @@ var PemError = frameworkError.PemError;
|
|
|
38
36
|
var TAGS = asn1.TAGS;
|
|
39
37
|
|
|
40
38
|
|
|
41
|
-
// CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1). Value 7 is unused/reserved.
|
|
42
|
-
// The legal set lives once in pkix.CRL_REASON_NAMES (shared with the OCSP
|
|
43
|
-
// RevokedInfo decoder, which surfaces the name; this decoder the numeric code).
|
|
44
39
|
var CRL_REASONS = pkix.CRL_REASON_NAMES;
|
|
45
40
|
|
|
46
|
-
// Extension-value decoding is keyed off the stable dotted OID (resolved once at
|
|
47
|
-
// load from the canonical name) and not the mutable display name, so a caller's
|
|
48
|
-
// pki.oid.register() display override cannot change parse behavior.
|
|
49
41
|
var OID_CRL_NUMBER = oid.byName("cRLNumber");
|
|
50
42
|
var OID_REASON_CODE = oid.byName("reasonCode");
|
|
51
43
|
var OID_INVALIDITY_DATE = oid.byName("invalidityDate");
|
|
52
44
|
|
|
53
|
-
// The crl error namespace the schema engine walks under. Shared PKIX sub-schemas
|
|
54
|
-
// are instantiated here under crl/* so a structural fault reports a crl code.
|
|
55
45
|
var NS = pkix.makeNS("crl", CrlError, oid);
|
|
56
46
|
|
|
57
47
|
var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
58
48
|
var NAME = pkix.name(NS);
|
|
59
49
|
var EXTENSIONS = pkix.extensions(NS);
|
|
60
|
-
// pkix.time enforces the RFC 5280 encoding cutover on decode (sec. 5.1.2.4 -
|
|
61
|
-
// 5.1.2.6: thisUpdate / nextUpdate / revocationDate through 2049 must be
|
|
62
|
-
// UTCTime). invalidityDate stays GeneralizedTime-only (sec. 5.3.2, decodeExt).
|
|
63
50
|
var TIME = pkix.time(NS);
|
|
64
51
|
|
|
65
|
-
// CRL Version ::= INTEGER { v1(0), v2(1) }, a bare INTEGER (not [0] EXPLICIT).
|
|
66
|
-
// A CRL is at most v2; reject an explicit v1 (DER forbids the default) and any
|
|
67
|
-
// value >= 2. Do not reuse the certificate readVersion (it maps 2 -> v3).
|
|
68
52
|
var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
|
|
69
53
|
|
|
70
|
-
// The three cheap, high-value CRL extension values are decoded from their raw
|
|
71
|
-
// extnValue octets (RFC 5280 sec. 5.2/sec. 5.3); GeneralNames-based extensions
|
|
72
|
-
// (issuingDistributionPoint, certificateIssuer, authorityKeyIdentifier, ...) stay
|
|
73
|
-
// raw with their bytes reachable. A malformed decoded value fails closed.
|
|
74
54
|
function decodeExt(ext) {
|
|
75
55
|
var value = ext.value;
|
|
76
56
|
try {
|
|
77
|
-
if (ext.oid === OID_CRL_NUMBER) {
|
|
57
|
+
if (ext.oid === OID_CRL_NUMBER) {
|
|
78
58
|
value = asn1.read.integer(asn1.decode(ext.value));
|
|
79
59
|
if (value < 0n) throw new Error("cRLNumber must be non-negative (INTEGER 0..MAX)");
|
|
80
|
-
} else if (ext.oid === OID_REASON_CODE) {
|
|
81
|
-
// read.enumerated asserts the ENUMERATED tag (a bare INTEGER is rejected) --
|
|
82
|
-
// the codec owns the tag check, so this reader need not repeat it.
|
|
60
|
+
} else if (ext.oid === OID_REASON_CODE) {
|
|
83
61
|
var reason = asn1.read.enumerated(asn1.decode(ext.value));
|
|
84
62
|
if (!_hasOwn(CRL_REASONS, reason.toString())) throw new Error("undefined CRLReason " + reason.toString());
|
|
85
63
|
value = Number(reason);
|
|
86
|
-
} else if (ext.oid === OID_INVALIDITY_DATE) {
|
|
64
|
+
} else if (ext.oid === OID_INVALIDITY_DATE) {
|
|
87
65
|
var n = asn1.decode(ext.value);
|
|
88
66
|
if (n.tagClass !== "universal" || n.tagNumber !== TAGS.GENERALIZED_TIME) {
|
|
89
67
|
throw new Error("invalidityDate must be a GeneralizedTime");
|
|
@@ -96,8 +74,6 @@ function decodeExt(ext) {
|
|
|
96
74
|
return { oid: ext.oid, name: ext.name, critical: ext.critical, value: value };
|
|
97
75
|
}
|
|
98
76
|
|
|
99
|
-
// RevokedCertificate ::= SEQUENCE { userCertificate CertificateSerialNumber,
|
|
100
|
-
// revocationDate Time, crlEntryExtensions Extensions OPTIONAL }
|
|
101
77
|
var REVOKED_ENTRY = schema.seq([
|
|
102
78
|
schema.field("userCertificate", schema.integerLeaf()),
|
|
103
79
|
schema.field("revocationDate", TIME),
|
|
@@ -107,9 +83,6 @@ var REVOKED_ENTRY = schema.seq([
|
|
|
107
83
|
build: function (m) {
|
|
108
84
|
return {
|
|
109
85
|
serialNumber: m.fields.userCertificate.value,
|
|
110
|
-
// Raw INTEGER content bytes (not a reserialized BigInt) so the hex matches
|
|
111
|
-
// the X.509 parser's serialNumberHex and preserves DER sign padding
|
|
112
|
-
// (a positive 0x80 serial encodes as content 00 80).
|
|
113
86
|
serialNumberHex: m.fields.userCertificate.node.content.toString("hex"),
|
|
114
87
|
revocationDate: m.fields.revocationDate.value,
|
|
115
88
|
crlEntryExtensions: m.fields.crlEntryExtensions.present ? m.fields.crlEntryExtensions.value.result.map(decodeExt) : [],
|
|
@@ -122,18 +95,6 @@ var REVOKED_LIST = schema.seqOf(REVOKED_ENTRY, {
|
|
|
122
95
|
build: function (m) { return m.items.map(function (it) { return it.value.result; }); },
|
|
123
96
|
});
|
|
124
97
|
|
|
125
|
-
// TBSCertList ::= SEQUENCE { version Version OPTIONAL, signature AlgorithmIdentifier,
|
|
126
|
-
// issuer Name, thisUpdate Time, nextUpdate Time OPTIONAL,
|
|
127
|
-
// revokedCertificates SEQUENCE OF SEQUENCE {...} OPTIONAL,
|
|
128
|
-
// crlExtensions [0] EXPLICIT Extensions OPTIONAL }.
|
|
129
|
-
// The three OPTIONAL universal-tagged fields (version=INTEGER, nextUpdate=Time,
|
|
130
|
-
// revokedCertificates=SEQUENCE) are disambiguated by their universal tag;
|
|
131
|
-
// crlExtensions is modeled as a trailing [0]..[0] so a stray non-[0] trailing
|
|
132
|
-
// context tag is REJECTED (crl/bad-tbs), not silently ignored.
|
|
133
|
-
// `signature` is consumed by the CERTIFICATE_LIST build (the sec. 5.1.1.2
|
|
134
|
-
// outer==inner agreement check reads tbsMatch.fields.signature.node.bytes);
|
|
135
|
-
// the operator reads the surfaced outer signatureAlgorithm, which that
|
|
136
|
-
// check proves byte-identical.
|
|
137
98
|
// allow:schema-build-drops-parsed-field
|
|
138
99
|
var TBS_CERTLIST = schema.seq([
|
|
139
100
|
schema.optional("version", CRL_VERSION, { whenUniversal: [TAGS.INTEGER] }),
|
|
@@ -149,7 +110,7 @@ var TBS_CERTLIST = schema.seq([
|
|
|
149
110
|
build: function (m) {
|
|
150
111
|
return {
|
|
151
112
|
version: m.fields.version.present ? m.fields.version.value : 1,
|
|
152
|
-
issuer: m.fields.issuer.value.result,
|
|
113
|
+
issuer: m.fields.issuer.value.result,
|
|
153
114
|
thisUpdate: m.fields.thisUpdate.value,
|
|
154
115
|
nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
|
|
155
116
|
revokedCertificates: m.fields.revokedCertificates.present ? m.fields.revokedCertificates.value.result : [],
|
|
@@ -159,23 +120,16 @@ var TBS_CERTLIST = schema.seq([
|
|
|
159
120
|
},
|
|
160
121
|
});
|
|
161
122
|
|
|
162
|
-
// CertificateList ::= SEQUENCE { tbsCertList, signatureAlgorithm, signatureValue }
|
|
163
|
-
// -- the shared SIGNED envelope. The CRL-specific invariants (outer==inner
|
|
164
|
-
// signatureAlgorithm agreement, non-empty issuer, v2-only extensions) live in the
|
|
165
|
-
// build; the envelope owns the SEQUENCE-of-3 shape and signature extraction.
|
|
166
123
|
var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
167
124
|
code: "crl/not-a-crl", what: "CertificateList",
|
|
168
125
|
build: function (e) {
|
|
169
126
|
var tbs = e.tbsMatch.result;
|
|
170
|
-
// RFC 5280 sec. 5.1.1.2 -- the outer signatureAlgorithm MUST equal tbsCertList.signature.
|
|
171
127
|
if (!e.outerSignatureAlgorithmBytes.equals(e.tbsMatch.fields.signature.node.bytes)) {
|
|
172
128
|
throw NS.E("crl/bad-signature-algorithm", "signatureAlgorithm must match tbsCertList.signature (RFC 5280 sec. 5.1.1.2)");
|
|
173
129
|
}
|
|
174
|
-
// RFC 5280 sec. 5.1.2.3 -- the issuer MUST be a non-empty distinguished name.
|
|
175
130
|
if (!tbs.issuer.rdns.length) {
|
|
176
131
|
throw NS.E("crl/bad-issuer", "issuer must be a non-empty distinguished name");
|
|
177
132
|
}
|
|
178
|
-
// RFC 5280 sec. 5.1.2.1 -- crlExtensions / crlEntryExtensions appear only in a v2 CRL.
|
|
179
133
|
var hasExtensions = tbs.crlExtensionsPresent ||
|
|
180
134
|
tbs.revokedCertificates.some(function (r) { return r.crlEntryExtensions.length > 0; });
|
|
181
135
|
if (hasExtensions && tbs.version !== 2) {
|
|
@@ -221,12 +175,6 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
|
221
175
|
* var crl = pki.schema.crl.parse(der);
|
|
222
176
|
* crl.revokedCertificates[0].serialNumberHex; // -> "0a3f"
|
|
223
177
|
*/
|
|
224
|
-
// Recording, for the same reason the certificate parser is: a CRL is one signature over one byte
|
|
225
|
-
// range, and a parsed CRL presents that range and the revocation list it encodes as separate
|
|
226
|
-
// properties. Keep a correctly signed CRL's `tbsBytes` and signature and empty `revokedCertificates`
|
|
227
|
-
// and the signature still verifies while the revocation answer comes from the edited list -- a
|
|
228
|
-
// revoked certificate reported as good. The scope fields (`crlExtensions`, an IDP, a delta
|
|
229
|
-
// indicator) fail the same way. The verdict verbs re-parse from what is recorded here.
|
|
230
178
|
var parse = pkix.makeRecordingParser({ pemLabel: "X509 CRL", PemError: PemError, ErrorClass: CrlError, prefix: "crl", what: "CRL", topSchema: CERTIFICATE_LIST, ns: NS }, "crl");
|
|
231
179
|
|
|
232
180
|
/**
|
|
@@ -275,24 +223,13 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "X509 CRL
|
|
|
275
223
|
*/
|
|
276
224
|
function pemEncode(der, label) { return pkix.pemEncode(der, label || "X509 CRL", PemError); }
|
|
277
225
|
|
|
278
|
-
// Certificate and CertificateList share the outer SEQUENCE-of-3 shape; a CRL is
|
|
279
|
-
// distinguished by its tbsCertList -- the first tbs element is a bare INTEGER
|
|
280
|
-
// (version) or an AlgorithmIdentifier (signature) SEQUENCE, and crucially the
|
|
281
|
-
// field at the certificate's Validity position is a bare Time (thisUpdate). The
|
|
282
|
-
// orchestrator uses `matches` to route; a cert's tbs leads with a [0] EXPLICIT
|
|
283
|
-
// version or an INTEGER serial FOLLOWED by an AlgorithmIdentifier and a Name,
|
|
284
|
-
// then a Validity SEQUENCE, never a bare Time at that depth.
|
|
285
226
|
function matches(root) {
|
|
286
227
|
var tbs = pkix.signedEnvelopeTbs(root);
|
|
287
228
|
if (!tbs) return false;
|
|
288
|
-
// A certificate's tbs leads with [0] EXPLICIT version -- a CRL never does.
|
|
289
229
|
if (tbs.children[0] && tbs.children[0].tagClass === "context") return false;
|
|
290
|
-
// Walk to the thisUpdate / validity position: skip an optional bare INTEGER
|
|
291
|
-
// version, then signature (SEQUENCE) + issuer (SEQUENCE); the next element is
|
|
292
|
-
// thisUpdate (Time) for a CRL, Validity (SEQUENCE) for a certificate.
|
|
293
230
|
var i = 0;
|
|
294
231
|
if (schema.isUniversal(tbs.children[i], TAGS.INTEGER)) i++;
|
|
295
|
-
i += 2;
|
|
232
|
+
i += 2;
|
|
296
233
|
var pos = tbs.children[i];
|
|
297
234
|
return schema.isUniversalOneOf(pos, [TAGS.UTC_TIME, TAGS.GENERALIZED_TIME]);
|
|
298
235
|
}
|
package/lib/schema-crmf.js
CHANGED
|
@@ -48,43 +48,27 @@ var NS = pkix.makeNS("crmf", CrmfError, oid);
|
|
|
48
48
|
|
|
49
49
|
var TAGS = asn1.TAGS;
|
|
50
50
|
|
|
51
|
-
// ---- dual-accept Name (issuer [3] / subject [5]) ---------------------
|
|
52
|
-
// RFC 4211 Appendix B is IMPLICIT TAGS, but Name is a CHOICE so X.680 sec. 31.2.7
|
|
53
|
-
// forces EXPLICIT tagging -- the standards-compliant wire is a context [tag]
|
|
54
|
-
// EXPLICIT wrapper around a universal RDNSequence. The dominant tooling
|
|
55
|
-
// (pyasn1-modules, BouncyCastle) instead encodes IMPLICIT (the [tag] REPLACES the
|
|
56
|
-
// RDNSequence 0x30, so the children ARE the RDN SETs). Both occur in real CMP /
|
|
57
|
-
// EST, so accept either, disambiguated by the first inner element's tag: a
|
|
58
|
-
// universal SET leads the IMPLICIT form; a single universal SEQUENCE is the
|
|
59
|
-
// EXPLICIT wrapper. Anything else fails closed.
|
|
60
51
|
function crmfName(tag) {
|
|
61
|
-
var NAME = pkix.name(NS);
|
|
62
|
-
var INAME = pkix.name(NS, { implicitTag: tag });
|
|
52
|
+
var NAME = pkix.name(NS);
|
|
53
|
+
var INAME = pkix.name(NS, { implicitTag: tag });
|
|
63
54
|
return schema.decode(function (n, ctx) {
|
|
64
55
|
if (n.tagClass !== "context" || n.tagNumber !== tag || !n.children) {
|
|
65
56
|
throw ctx.E("crmf/bad-name", "issuer/subject [" + tag + "] must be a Name (RFC 4211 sec. 5)");
|
|
66
57
|
}
|
|
67
|
-
if (n.children.length === 0) return schema.walk(INAME, n, ctx).result;
|
|
58
|
+
if (n.children.length === 0) return schema.walk(INAME, n, ctx).result;
|
|
68
59
|
var c0 = n.children[0];
|
|
69
60
|
if (c0.tagClass === "universal" && c0.tagNumber === TAGS.SET) {
|
|
70
|
-
return schema.walk(INAME, n, ctx).result;
|
|
61
|
+
return schema.walk(INAME, n, ctx).result;
|
|
71
62
|
}
|
|
72
63
|
if (n.children.length === 1 && c0.tagClass === "universal" && c0.tagNumber === TAGS.SEQUENCE) {
|
|
73
|
-
return schema.walk(NAME, c0, ctx).result;
|
|
64
|
+
return schema.walk(NAME, c0, ctx).result;
|
|
74
65
|
}
|
|
75
66
|
throw ctx.E("crmf/bad-name", "issuer/subject [" + tag + "] Name must be an IMPLICIT RDNSequence (SET-led) or an EXPLICIT-wrapped RDNSequence");
|
|
76
67
|
}, function (value) {
|
|
77
|
-
// encode the IMPLICIT form (the dominant wire encoding): the [tag] whose children
|
|
78
|
-
// ARE the RDN SETs, built from the decoded `rdns` via the shared Name schema.
|
|
79
68
|
return schema.encode(INAME, value.rdns, NS);
|
|
80
69
|
});
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
// ---- OptionalValidity (sec. 5) -------------------------------------------
|
|
84
|
-
// validity [4] IMPLICIT SEQUENCE { notBefore [0] Time OPTIONAL, notAfter [1] Time
|
|
85
|
-
// OPTIONAL }. Time is a CHOICE of UTCTime / GeneralizedTime, so [0]/[1] are
|
|
86
|
-
// EXPLICIT (an IMPLICIT tag would erase the UTCTime-vs-GeneralizedTime
|
|
87
|
-
// discriminator). RFC 4211 sec. 5 requires at least one of the two to be present.
|
|
88
72
|
var OPTIONAL_VALIDITY = schema.seq([
|
|
89
73
|
schema.trailing([
|
|
90
74
|
{ tag: 0, name: "notBefore", schema: schema.time(NS), explicit: true, emptyCode: "crmf/bad-validity" },
|
|
@@ -101,38 +85,19 @@ var OPTIONAL_VALIDITY = schema.seq([
|
|
|
101
85
|
},
|
|
102
86
|
});
|
|
103
87
|
|
|
104
|
-
// ---- ProofOfPossession (sec. 4) ------------------------------------------
|
|
105
|
-
// raVerified [0] NULL: the RA has already verified POP out of band.
|
|
106
88
|
var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
|
|
107
89
|
try { asn1.read.nullImplicit(n, 0); }
|
|
108
90
|
catch (e) { throw ctx.E("crmf/bad-popo", "raVerified [0] must be an IMPLICIT NULL", e); }
|
|
109
91
|
return { type: "raVerified" };
|
|
110
92
|
});
|
|
111
93
|
|
|
112
|
-
// When poposkInput is present the POP signature is over the DER of the
|
|
113
|
-
// POPOSigningKeyInput value (RFC 4211 sec. 4.1), which is a SEQUENCE. But on the wire
|
|
114
|
-
// poposkInput is an IMPLICIT [0] field, so its bytes lead with the context tag
|
|
115
|
-
// (0xA0) and not the SEQUENCE tag a verifier hashes. `asn1.sequenceTlv` recovers
|
|
116
|
-
// the SEQUENCE-tagged signed region from the field's content; `signedBytes` is that
|
|
117
|
-
// region and `bytes` keeps the raw wire [0] TLV, so a verifier can use either
|
|
118
|
-
// encoding its peer produced.
|
|
119
94
|
|
|
120
|
-
// POPOSigningKeyInput ::= SEQUENCE { authInfo CHOICE { sender [0] GeneralName,
|
|
121
|
-
// publicKeyMAC PKMACValue }, publicKey SubjectPublicKeyInfo } (sec. 4.1). Structurally
|
|
122
|
-
// validated (not deferred raw), so an empty or malformed poposkInput fails closed
|
|
123
|
-
// and never surfaces a signed region that was not a well-formed POP input.
|
|
124
|
-
// PKMACValue ::= SEQUENCE { algId AlgorithmIdentifier, value BIT STRING }. The
|
|
125
|
-
// MAC value is octet-string algorithm output handed to an external verifier;
|
|
126
|
-
// with no in-tree POP verify layer, octet alignment is enforced at parse.
|
|
127
95
|
var PKMAC_VALUE = schema.seq([
|
|
128
96
|
schema.field("algId", pkix.algorithmIdentifier(NS)),
|
|
129
97
|
schema.field("value", schema.bitString()),
|
|
130
98
|
], {
|
|
131
99
|
assert: "sequence", arity: { exact: 2 }, code: "crmf/bad-popo", what: "PKMACValue",
|
|
132
100
|
build: function (m, ctx) {
|
|
133
|
-
// The parsed BIT STRING structure { unusedBits, bytes }, a structural
|
|
134
|
-
// octet-alignment check on the unused-bit COUNT, not a comparison of the
|
|
135
|
-
// MAC secret (there is no in-tree POP verify), so no timing oracle exists.
|
|
136
101
|
var macBitString = m.fields.value.value;
|
|
137
102
|
if (macBitString.unusedBits !== 0) {
|
|
138
103
|
throw ctx.E("crmf/bad-popo", "a PKMACValue MAC BIT STRING must be octet-aligned (0 unused bits)");
|
|
@@ -141,8 +106,6 @@ var PKMAC_VALUE = schema.seq([
|
|
|
141
106
|
},
|
|
142
107
|
});
|
|
143
108
|
|
|
144
|
-
// authInfo CHOICE: sender [0] GeneralName is EXPLICIT (GeneralName is itself a
|
|
145
|
-
// CHOICE); publicKeyMAC is a bare PKMACValue SEQUENCE, disjoint by tag class.
|
|
146
109
|
var POPOSK_AUTH_INFO = schema.choice([
|
|
147
110
|
{ when: { tagClass: "context", tagNumber: 0 }, schema: schema.explicit(0, pkix.generalName(NS, { code: "crmf/bad-popo" }), { code: "crmf/bad-popo" }) },
|
|
148
111
|
{ when: { tagClass: "universal", tagNumber: TAGS.SEQUENCE }, schema: PKMAC_VALUE },
|
|
@@ -153,12 +116,8 @@ var POPOSK_INPUT = schema.seq([
|
|
|
153
116
|
schema.field("publicKey", pkix.spki(NS)),
|
|
154
117
|
], { assert: "implicit", implicitTag: 0, arity: { exact: 2 }, code: "crmf/bad-popo", what: "POPOSigningKeyInput" });
|
|
155
118
|
|
|
156
|
-
// POPOSigningKey ::= SEQUENCE { poposkInput [0] POPOSigningKeyInput OPTIONAL,
|
|
157
|
-
// algorithmIdentifier AlgorithmIdentifier, signature BIT STRING }, IMPLICIT [1].
|
|
158
119
|
var POPO_SIGNING_KEY = schema.seq([
|
|
159
120
|
schema.optional("poposkInput", schema.decode(function (n, ctx) {
|
|
160
|
-
// Validate the full POPOSigningKeyInput structure fail-closed, then surface the
|
|
161
|
-
// raw [0] node plus its decoded publicKey (for the sec. 4.1 template-match check).
|
|
162
121
|
var m = schema.walk(POPOSK_INPUT, n, ctx);
|
|
163
122
|
return { node: n, publicKey: m.fields.publicKey.value.result.bytes };
|
|
164
123
|
}), { tag: 0 }),
|
|
@@ -169,9 +128,6 @@ var POPO_SIGNING_KEY = schema.seq([
|
|
|
169
128
|
build: function (m, ctx) {
|
|
170
129
|
var pin = m.fields.poposkInput;
|
|
171
130
|
var sig = m.fields.signature.value;
|
|
172
|
-
// The POP signature is octet-string algorithm output an external verifier
|
|
173
|
-
// consumes; with no in-tree POP verify layer, octet alignment is enforced
|
|
174
|
-
// at parse (RFC 4211 sec. 4.1).
|
|
175
131
|
if (sig.unusedBits !== 0) {
|
|
176
132
|
throw ctx.E("crmf/bad-popo", "a POPOSigningKey signature BIT STRING must be octet-aligned (0 unused bits)");
|
|
177
133
|
}
|
|
@@ -184,19 +140,6 @@ var POPO_SIGNING_KEY = schema.seq([
|
|
|
184
140
|
},
|
|
185
141
|
});
|
|
186
142
|
|
|
187
|
-
// keyEncipherment [2] / keyAgreement [3] POPOPrivKey (sec. 4). POPOPrivKey is a
|
|
188
|
-
// 5-arm CHOICE { thisMessage [0], subsequentMessage [1], dhMAC [2], agreeMAC [3],
|
|
189
|
-
// encryptedKey [4] }, so X.680 sec. 31.2.7 forces the outer [2]/[3] tag to be EXPLICIT
|
|
190
|
-
// (a CHOICE has no single tag to replace). Validate that shell, a constructed
|
|
191
|
-
// wrapper around exactly one inner context [0]..[4] alternative, before surfacing
|
|
192
|
-
// the arm raw, so a primitive, empty or mis-tagged node is rejected and never
|
|
193
|
-
// reported to a verifier as a well-formed POP. The inner alternative's own value
|
|
194
|
-
// (incl. the encryptedKey [4] EnvelopedData) stays deferred to the verify layer.
|
|
195
|
-
// POPOPrivKey ::= CHOICE { thisMessage [0] BIT STRING (deprecated), subsequentMessage
|
|
196
|
-
// [1] SubsequentMessage, dhMAC [2] BIT STRING (deprecated), agreeMAC [3] PKMACValue,
|
|
197
|
-
// encryptedKey [4] EnvelopedData } (RFC 4211 sec. 4.2). The deprecated arms are still
|
|
198
|
-
// parsed. The inner VALUE stays deferred raw (the EnvelopedData / MAC decode is a
|
|
199
|
-
// verify-layer concern), but the alternative's shape is validated fail-closed.
|
|
200
143
|
var POPOPRIVKEY_METHODS = { 0: "thisMessage", 1: "subsequentMessage", 2: "dhMAC", 3: "agreeMAC", 4: "encryptedKey" };
|
|
201
144
|
function popoPrivKey(type) {
|
|
202
145
|
return schema.decode(function (n, ctx) {
|
|
@@ -207,23 +150,13 @@ function popoPrivKey(type) {
|
|
|
207
150
|
if (inner.tagClass !== "context" || inner.tagNumber < 0 || inner.tagNumber > 4) {
|
|
208
151
|
throw ctx.E("crmf/bad-popo", type + " POPOPrivKey alternative must be a context [0]..[4] CHOICE element (RFC 4211 sec. 4)");
|
|
209
152
|
}
|
|
210
|
-
// RFC 4211 sec. 4.2/sec. 4.3 -- the MAC alternatives dhMAC [2] / agreeMAC [3] are the
|
|
211
|
-
// key-agreement "fourth method"; key-encipherment POP uses only the
|
|
212
|
-
// thisMessage / subsequentMessage / encryptedKey methods, so a MAC arm under
|
|
213
|
-
// keyEncipherment is non-conforming.
|
|
214
153
|
if (type === "keyEncipherment" && (inner.tagNumber === 2 || inner.tagNumber === 3)) {
|
|
215
154
|
throw ctx.E("crmf/bad-popo", "keyEncipherment POP cannot use the MAC alternative " + POPOPRIVKEY_METHODS[inner.tagNumber] + " (RFC 4211 sec. 4.2)");
|
|
216
155
|
}
|
|
217
|
-
// FORM: thisMessage [0] / subsequentMessage [1] / dhMAC [2] are primitive (BIT
|
|
218
|
-
// STRING / INTEGER / BIT STRING); agreeMAC [3] PKMACValue and encryptedKey [4]
|
|
219
|
-
// EnvelopedData are constructed (SEQUENCE). A wrong-form node is malformed.
|
|
220
156
|
var mustBeConstructed = inner.tagNumber === 3 || inner.tagNumber === 4;
|
|
221
157
|
if (mustBeConstructed !== !!inner.children) {
|
|
222
158
|
throw ctx.E("crmf/bad-popo", type + " POPOPrivKey [" + inner.tagNumber + "] has the wrong primitive/constructed form (RFC 4211 sec. 4)");
|
|
223
159
|
}
|
|
224
|
-
// Each alternative's PAYLOAD is decoded fail-closed, not just its form:
|
|
225
|
-
// thisMessage [0] / dhMAC [2] are BIT STRINGs; subsequentMessage [1] an INTEGER
|
|
226
|
-
// in {0,1}; agreeMAC [3] a PKMACValue SEQUENCE; encryptedKey [4] an EnvelopedData.
|
|
227
160
|
if (inner.tagNumber === 0 || inner.tagNumber === 2) {
|
|
228
161
|
try { asn1.read.bitStringImplicit(inner, inner.tagNumber); }
|
|
229
162
|
catch (e) { throw ctx.E("crmf/bad-popo", POPOPRIVKEY_METHODS[inner.tagNumber] + " [" + inner.tagNumber + "] must be a BIT STRING (RFC 4211 sec. 4.2)", e); }
|
|
@@ -240,32 +173,19 @@ function popoPrivKey(type) {
|
|
|
240
173
|
try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), NS, { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
|
|
241
174
|
catch (e) { throw ctx.E("crmf/bad-popo", "agreeMAC [3] must be a PKMACValue SEQUENCE { algId, BIT STRING } (RFC 4211 sec. 4.2)", e); }
|
|
242
175
|
}
|
|
243
|
-
// encryptedKey [4] EnvelopedData: validate the CMS structure fail-closed by
|
|
244
|
-
// composing the shared EnvelopedData decoder (retag the IMPLICIT [4] to the
|
|
245
|
-
// universal SEQUENCE it decodes as). The EncKeyWithID identity check lives inside
|
|
246
|
-
// the ENCRYPTED content, so it stays a verify-layer concern.
|
|
247
176
|
if (inner.tagNumber === 4) {
|
|
248
177
|
var env;
|
|
249
178
|
try { env = cms.walkEnvelopedData(asn1.decode(asn1.sequenceTlv(inner))); }
|
|
250
179
|
catch (e) { throw ctx.E("crmf/bad-popo", "encryptedKey [4] must be a well-formed EnvelopedData (RFC 4211 sec. 4.2, RFC 5652 sec. 6.1)", e); }
|
|
251
|
-
// RFC 4211 sec. 4.2: the enveloped content type MUST be id-ct-encKeyWithID (the
|
|
252
|
-
// ContentType OID is in the clear even though the content itself is encrypted).
|
|
253
180
|
if (env.encryptedContentInfo.contentType !== oid.byName("encKeyWithID")) {
|
|
254
181
|
throw ctx.E("crmf/bad-popo", "encryptedKey [4] EnvelopedData content type MUST be id-ct-encKeyWithID (RFC 4211 sec. 4.2)");
|
|
255
182
|
}
|
|
256
|
-
// The encrypted key material MUST be present. CMS allows a detached
|
|
257
|
-
// EnvelopedData (encryptedContent OPTIONAL), but a POP with no key to verify
|
|
258
|
-
// or archive is meaningless. The shared CMS assert rejects a null OR
|
|
259
|
-
// zero-length attached ciphertext (the rule the EST / PKCS#12 siblings hold).
|
|
260
183
|
cms.assertAttachedCiphertext(env.encryptedContentInfo, ctx.E, "crmf/bad-popo", "encryptedKey [4] EnvelopedData");
|
|
261
184
|
}
|
|
262
185
|
return { type: type, method: POPOPRIVKEY_METHODS[inner.tagNumber], bytes: n.bytes };
|
|
263
186
|
});
|
|
264
187
|
}
|
|
265
188
|
|
|
266
|
-
// ProofOfPossession ::= CHOICE { raVerified [0] NULL, signature [1] POPOSigningKey,
|
|
267
|
-
// keyEncipherment [2] POPOPrivKey, keyAgreement [3] POPOPrivKey }. Each arm yields
|
|
268
|
-
// the final object so the CertReqMsg build reads one uniform popo shape.
|
|
269
189
|
var PROOF_OF_POSSESSION = schema.choice([
|
|
270
190
|
{ when: { tagClass: "context", tagNumber: 0 }, schema: POPO_RAVERIFIED },
|
|
271
191
|
{ when: { tagClass: "context", tagNumber: 1 }, schema: schema.decode(function (n, ctx) { return schema.walk(POPO_SIGNING_KEY, n, ctx).result; }) },
|
|
@@ -273,10 +193,6 @@ var PROOF_OF_POSSESSION = schema.choice([
|
|
|
273
193
|
{ when: { tagClass: "context", tagNumber: 3 }, schema: popoPrivKey("keyAgreement") },
|
|
274
194
|
], { code: "crmf/bad-popo" });
|
|
275
195
|
|
|
276
|
-
// ---- Controls / regInfo (sec. 5, sec. 6, sec. 7) ---------------------------------
|
|
277
|
-
// AttributeTypeAndValue ::= SEQUENCE { type OBJECT IDENTIFIER, value ANY DEFINED BY
|
|
278
|
-
// type }. The list is decoded; each value stays raw (the per-OID value semantics
|
|
279
|
-
// are deferred). Shared shape for both controls and regInfo.
|
|
280
196
|
var CONTROL = schema.seq([
|
|
281
197
|
schema.field("type", schema.oidLeaf()),
|
|
282
198
|
schema.field("value", schema.any()),
|
|
@@ -288,17 +204,9 @@ var CONTROL = schema.seq([
|
|
|
288
204
|
},
|
|
289
205
|
});
|
|
290
206
|
function mapControls(m) { return m.items.map(function (it) { return it.value.result; }); }
|
|
291
|
-
// Controls ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue (sec. 5).
|
|
292
207
|
var CONTROLS = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/bad-controls", what: "Controls", build: mapControls });
|
|
293
|
-
// regInfo ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue (sec. 7).
|
|
294
208
|
var REG_INFO = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/bad-reg-info", what: "regInfo", build: mapControls });
|
|
295
209
|
|
|
296
|
-
// ---- CertTemplate (sec. 5), the IMPLICIT-TAGS core ------------------------
|
|
297
|
-
// CertTemplate ::= SEQUENCE { version [0], serialNumber [1], signingAlg [2],
|
|
298
|
-
// issuer [3] Name, validity [4] OptionalValidity, subject [5] Name, publicKey [6]
|
|
299
|
-
// SubjectPublicKeyInfo, issuerUID [7], subjectUID [8], extensions [9] }, where every
|
|
300
|
-
// field IMPLICIT and OPTIONAL, so the whole body is one ascending [0..9] trailing
|
|
301
|
-
// run. issuer/subject Name and the [4] validity times are the CHOICE exceptions.
|
|
302
210
|
var CERT_TEMPLATE = schema.seq([
|
|
303
211
|
schema.trailing([
|
|
304
212
|
{ tag: 0, name: "version", schema: schema.implicitInteger(0) },
|
|
@@ -316,17 +224,6 @@ var CERT_TEMPLATE = schema.seq([
|
|
|
316
224
|
assert: "sequence", code: "crmf/bad-cert-template", what: "CertTemplate",
|
|
317
225
|
build: function (m, ctx) {
|
|
318
226
|
var f = m.fields;
|
|
319
|
-
// version MUST be 2 (v3) when supplied; SHOULD be omitted (RFC 4211 sec. 5). This
|
|
320
|
-
// is a structural CertTemplate rule (it holds wherever the template appears);
|
|
321
|
-
// the CA-assigned-field omission rule is REQUEST-context-specific and lives in
|
|
322
|
-
// CERT_REQUEST.build, because the same CertTemplate structure identifies an
|
|
323
|
-
// existing certificate -- serialNumber and issuer present -- inside a CMP
|
|
324
|
-
// RevDetails (RFC 9810 sec. 5.3.9). Every field is surfaced so both the request
|
|
325
|
-
// omission check and the revocation consumer read them off one result.
|
|
326
|
-
// The single profile override: RFC 9810 Appendix D.6 profiles the CMP
|
|
327
|
-
// cross-certification (ccr) template as "version v1 or v3", so the caller
|
|
328
|
-
// opts in to v1 (0) via ctx.allowV1Version, the same base-section-vs-
|
|
329
|
-
// profile-appendix override mechanism as the signingAlg relaxation below.
|
|
330
227
|
if (f.version.present && f.version.value !== 2n &&
|
|
331
228
|
!(ctx.allowV1Version && f.version.value === 0n)) {
|
|
332
229
|
throw ctx.E("crmf/bad-version", "CertTemplate version MUST be 2 (v3) if supplied (RFC 4211 sec. 5)");
|
|
@@ -346,10 +243,6 @@ var CERT_TEMPLATE = schema.seq([
|
|
|
346
243
|
},
|
|
347
244
|
});
|
|
348
245
|
|
|
349
|
-
// ---- CertRequest / CertReqMsg / CertReqMessages (sec. 3, sec. 5) --------------
|
|
350
|
-
// CertRequest ::= SEQUENCE { certReqId INTEGER, certTemplate CertTemplate,
|
|
351
|
-
// controls Controls OPTIONAL }. certReqId is a SIGNED INTEGER with no value
|
|
352
|
-
// constraint -- a negative value (the RFC 9483 -1 sentinel) is legal.
|
|
353
246
|
var CERT_REQUEST = schema.seq([
|
|
354
247
|
schema.field("certReqId", schema.integerLeaf()),
|
|
355
248
|
schema.field("certTemplate", CERT_TEMPLATE),
|
|
@@ -358,18 +251,6 @@ var CERT_REQUEST = schema.seq([
|
|
|
358
251
|
assert: "sequence", code: "crmf/bad-cert-request", what: "CertRequest",
|
|
359
252
|
build: function (m, ctx) {
|
|
360
253
|
var tpl = m.fields.certTemplate.value.result;
|
|
361
|
-
// RFC 4211 sec. 5 -- serialNumber, signingAlg, issuerUID, and subjectUID MUST be
|
|
362
|
-
// omitted from a REQUEST's CertTemplate: serialNumber and signingAlg are
|
|
363
|
-
// assigned by the CA, and the UID pair is deprecated. A requester must not
|
|
364
|
-
// dictate a CA-assigned value (a requester-chosen serialNumber is a real
|
|
365
|
-
// hazard), so a request template that sets any of them is rejected
|
|
366
|
-
// fail-closed. The check lives HERE, not in CERT_TEMPLATE, because the same
|
|
367
|
-
// structure legitimately carries serialNumber/issuer in a CMP RevDetails.
|
|
368
|
-
// A cross-certification request (ccr) is the one exception: RFC 9810 Appendix
|
|
369
|
-
// D.6 profiles its template with signingAlg PRESENT (the requesting CA states
|
|
370
|
-
// the algorithm it wants the cross-certificate signed with), so the caller
|
|
371
|
-
// relaxes that single field via ctx.allowSigningAlg; serialNumber and the UID
|
|
372
|
-
// pair stay forbidden.
|
|
373
254
|
var caAssigned = ctx.allowSigningAlg
|
|
374
255
|
? ["serialNumber", "issuerUID", "subjectUID"]
|
|
375
256
|
: ["serialNumber", "signingAlg", "issuerUID", "subjectUID"];
|
|
@@ -383,15 +264,11 @@ var CERT_REQUEST = schema.seq([
|
|
|
383
264
|
certReqIdHex: m.fields.certReqId.node.content.toString("hex"),
|
|
384
265
|
certTemplate: tpl,
|
|
385
266
|
controls: m.fields.controls.present ? m.fields.controls.value.result : null,
|
|
386
|
-
certReqBytes: m.node.bytes,
|
|
267
|
+
certReqBytes: m.node.bytes,
|
|
387
268
|
};
|
|
388
269
|
},
|
|
389
270
|
});
|
|
390
271
|
|
|
391
|
-
// CertReqMsg ::= SEQUENCE { certReq CertRequest, popo ProofOfPossession OPTIONAL,
|
|
392
|
-
// regInfo SEQUENCE OF AttributeTypeAndValue OPTIONAL }. popo is a CHOICE of context
|
|
393
|
-
// [0]..[3]; regInfo is a bare universal SEQUENCE, so the two OPTIONAL positions are
|
|
394
|
-
// disambiguated by tag class.
|
|
395
272
|
var CERT_REQ_MSG = schema.seq([
|
|
396
273
|
schema.field("certReq", CERT_REQUEST),
|
|
397
274
|
schema.optional("popo", PROOF_OF_POSSESSION, { tags: [0, 1, 2, 3] }),
|
|
@@ -401,14 +278,7 @@ var CERT_REQ_MSG = schema.seq([
|
|
|
401
278
|
build: function (m, ctx) {
|
|
402
279
|
var certReq = m.fields.certReq.value.result;
|
|
403
280
|
var popo = m.fields.popo.present ? m.fields.popo.value : null;
|
|
404
|
-
// A POP whose signature/MAC covers the certReq requires the CertTemplate to
|
|
405
|
-
// contain both subject and publicKey -- otherwise there is no complete request
|
|
406
|
-
// to sign / MAC over, and a verifier would be bound to the wrong identity/key.
|
|
407
281
|
var complete = certReq.certTemplate.subject !== null && certReq.certTemplate.publicKey !== null;
|
|
408
|
-
// RFC 4211 sec. 4.1 -- for a signature POP, poposkInput's presence is fixed by the
|
|
409
|
-
// CertTemplate: it MUST be omitted when the template carries both subject and
|
|
410
|
-
// publicKey (the signature is then over the DER of the CertRequest), and MUST be
|
|
411
|
-
// present otherwise.
|
|
412
282
|
if (popo && popo.type === "signature") {
|
|
413
283
|
if (complete && popo.poposkInput !== null) {
|
|
414
284
|
throw ctx.E("crmf/bad-popo", "poposkInput MUST be omitted when the CertTemplate contains both subject and publicKey (RFC 4211 sec. 4.1)");
|
|
@@ -416,16 +286,11 @@ var CERT_REQ_MSG = schema.seq([
|
|
|
416
286
|
if (!complete && popo.poposkInput === null) {
|
|
417
287
|
throw ctx.E("crmf/bad-popo", "poposkInput MUST be present when the CertTemplate lacks subject or publicKey (RFC 4211 sec. 4.1)");
|
|
418
288
|
}
|
|
419
|
-
// RFC 4211 sec. 4.1 -- the POPOSigningKeyInput publicKey MUST be exactly the template
|
|
420
|
-
// publicKey (both surfaced as canonical SubjectPublicKeyInfo DER), so a request
|
|
421
|
-
// cannot sign one key while asking for a certificate over a different key.
|
|
422
289
|
if (popo.poposkInput !== null && certReq.certTemplate.publicKey !== null &&
|
|
423
290
|
!popo.poposkInput.publicKey.equals(certReq.certTemplate.publicKey.bytes)) {
|
|
424
291
|
throw ctx.E("crmf/bad-popo", "POPOSigningKeyInput publicKey MUST equal the CertTemplate publicKey (RFC 4211 sec. 4.1)");
|
|
425
292
|
}
|
|
426
293
|
}
|
|
427
|
-
// RFC 4211 sec. 4.2/sec. 4.3 -- a MAC-based POPOPrivKey (dhMAC / agreeMAC) proves
|
|
428
|
-
// possession by MACing the certReq, which MUST include both subject and publicKey.
|
|
429
294
|
if (popo && (popo.type === "keyEncipherment" || popo.type === "keyAgreement") &&
|
|
430
295
|
(popo.method === "dhMAC" || popo.method === "agreeMAC") && !complete) {
|
|
431
296
|
throw ctx.E("crmf/bad-popo", "a MAC-based key-agreement/encipherment POP (dhMAC/agreeMAC) requires the CertTemplate to contain both subject and publicKey (RFC 4211 sec. 4.2/sec. 4.3)");
|
|
@@ -438,16 +303,11 @@ var CERT_REQ_MSG = schema.seq([
|
|
|
438
303
|
},
|
|
439
304
|
});
|
|
440
305
|
|
|
441
|
-
// CertReqMessages ::= SEQUENCE SIZE(1..MAX) OF CertReqMsg (sec. 3), where an empty sequence
|
|
442
|
-
// is malformed. The top-level reject code predates the
|
|
443
|
-
// `<prefix>/not-a-<structure>` convention and stays frozen on the public
|
|
444
|
-
// error-code surface.
|
|
445
306
|
var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
|
|
446
307
|
assert: "sequence", min: 1, code: "crmf/bad-cert-req-messages", what: "CertReqMessages",
|
|
447
308
|
build: function (m) { return { messages: m.items.map(function (it) { return it.value.result; }) }; },
|
|
448
309
|
});
|
|
449
310
|
|
|
450
|
-
// ---- parse -----------------------------------------------------------
|
|
451
311
|
|
|
452
312
|
/**
|
|
453
313
|
* @primitive pki.schema.crmf.parse
|
|
@@ -489,9 +349,6 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
|
|
|
489
349
|
* var m = pki.schema.crmf.parse(der);
|
|
490
350
|
* m.messages[0].certReq.certTemplate.subject.dn; // "CN=req.example"
|
|
491
351
|
*/
|
|
492
|
-
// Recording, because pki.crmf.verifyPop reaches a verdict from this result: the byte range a
|
|
493
|
-
// proof of possession covers, the key that possession is proven for, and the subject the CA will
|
|
494
|
-
// issue to are separate properties here, so the record is what binds them to one another.
|
|
495
352
|
var parse = pkix.makeRecordingParser({
|
|
496
353
|
pemLabel: null, PemError: PemError, ErrorClass: CrmfError, prefix: "crmf",
|
|
497
354
|
what: "certificate request message", topSchema: CERT_REQ_MESSAGES, ns: NS,
|
|
@@ -540,12 +397,6 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || null, Pem
|
|
|
540
397
|
*/
|
|
541
398
|
function pemEncode(der, label) { return pkix.pemEncode(der, label, PemError); }
|
|
542
399
|
|
|
543
|
-
// matches(root): a CertReqMessages is a universal SEQUENCE whose first CertReqMsg
|
|
544
|
-
// is a SEQUENCE whose first CertRequest is a SEQUENCE leading with a universal
|
|
545
|
-
// INTEGER (certReqId) then a universal SEQUENCE (certTemplate). This 3-level probe
|
|
546
|
-
// is disjoint from every registered root PROVIDED crmf is checked before
|
|
547
|
-
// ocsp-request: an OCSPRequest's tbsRequest never leads with the INTEGER-then-
|
|
548
|
-
// SEQUENCE pair (its children[0] is a context [0] version or a requestList).
|
|
549
400
|
function matches(root) {
|
|
550
401
|
var k = pkix.rootSequenceChildren(root, 1);
|
|
551
402
|
if (!k) return false;
|
|
@@ -558,15 +409,6 @@ function matches(root) {
|
|
|
558
409
|
schema.isUniversal(tpl, TAGS.SEQUENCE) && !!tpl.children;
|
|
559
410
|
}
|
|
560
411
|
|
|
561
|
-
// Validate a bare, already-decoded CertReqMessages / CertTemplate node -- for a
|
|
562
|
-
// composer that carries these structures inside its own envelope (an RFC 9810
|
|
563
|
-
// CMP PKIBody's ir/cr/kur/krr/ccr arms; a RevDetails.certDetails). NS-bound to
|
|
564
|
-
// crmf so an interior failure throws CrmfError with a crmf/* code, never the
|
|
565
|
-
// composer's class wearing a foreign code. Same contract as the CMS walkers.
|
|
566
|
-
// The opts carry the RFC 9810 Appendix D.6 ccr-profile overrides of RFC 4211 sec. 5,
|
|
567
|
-
// applied by the CMP composer to the cross-certification arm only:
|
|
568
|
-
// opts.allowSigningAlg relaxes the request-template signingAlg-omission rule and
|
|
569
|
-
// opts.allowV1Version accepts a CertTemplate version of v1 ("version v1 or v3").
|
|
570
412
|
function walkCertReqMessages(node, opts) {
|
|
571
413
|
var ctx = NS;
|
|
572
414
|
if (opts && (opts.allowSigningAlg || opts.allowV1Version)) {
|
|
@@ -585,8 +427,5 @@ module.exports = {
|
|
|
585
427
|
matches: matches,
|
|
586
428
|
walkCertReqMessages: walkCertReqMessages,
|
|
587
429
|
walkCertTemplate: walkCertTemplate,
|
|
588
|
-
// The top schema, exposed (not on the curated pki.schema.crmf surface) so a
|
|
589
|
-
// round-trip test can prove schema.encode(schema, v) -> decode -> parse recovers
|
|
590
|
-
// v -- the invariant that structurally validates the IMPLICIT/EXPLICIT tag handling.
|
|
591
430
|
certReqMessagesSchema: CERT_REQ_MESSAGES,
|
|
592
431
|
};
|