@blamejs/pki 0.6.13 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +1 -1
  3. package/index.js +1 -1
  4. package/lib/acme.js +59 -815
  5. package/lib/asn1-der.js +7 -245
  6. package/lib/attrcert-sign.js +12 -260
  7. package/lib/attrcert-verify.js +1 -7
  8. package/lib/byte-reader.js +0 -8
  9. package/lib/byte-writer.js +0 -11
  10. package/lib/cbor-det.js +0 -54
  11. package/lib/cmc-build.js +15 -357
  12. package/lib/cmc-verify.js +6 -239
  13. package/lib/cmp-build.js +41 -317
  14. package/lib/cmp-session.js +73 -760
  15. package/lib/cmp-verify.js +32 -488
  16. package/lib/cms-compress.js +3 -32
  17. package/lib/cms-decrypt.js +29 -294
  18. package/lib/cms-encrypt.js +14 -234
  19. package/lib/cms-sign.js +29 -324
  20. package/lib/cms-verify.js +9 -500
  21. package/lib/composite-kem.js +2 -102
  22. package/lib/composite-sig.js +1 -77
  23. package/lib/constants.js +0 -249
  24. package/lib/crl-sign.js +9 -237
  25. package/lib/crl-verify.js +1 -10
  26. package/lib/crmf-sign.js +19 -198
  27. package/lib/crmf-verify.js +1 -12
  28. package/lib/csr-sign.js +3 -34
  29. package/lib/csr-verify.js +1 -12
  30. package/lib/ct.js +28 -253
  31. package/lib/edwards-point.js +10 -44
  32. package/lib/est.js +28 -764
  33. package/lib/framework-error.js +4 -182
  34. package/lib/guard-all.js +1 -58
  35. package/lib/guard-async.js +1 -36
  36. package/lib/guard-bytes.js +6 -650
  37. package/lib/guard-compress.js +5 -102
  38. package/lib/guard-crypto.js +1 -40
  39. package/lib/guard-der.js +2 -93
  40. package/lib/guard-encoding.js +2 -60
  41. package/lib/guard-header.js +1 -36
  42. package/lib/guard-identifier.js +7 -531
  43. package/lib/guard-intrinsic.js +1 -150
  44. package/lib/guard-json.js +10 -73
  45. package/lib/guard-limits.js +1 -77
  46. package/lib/guard-list.js +1 -49
  47. package/lib/guard-name.js +3 -209
  48. package/lib/guard-parsed.js +7 -335
  49. package/lib/guard-range.js +1 -71
  50. package/lib/guard-secret.js +1 -102
  51. package/lib/guard-text.js +1 -64
  52. package/lib/guard-time.js +1 -97
  53. package/lib/hpke.js +3 -105
  54. package/lib/http-digest.js +14 -142
  55. package/lib/http-retry-after.js +1 -47
  56. package/lib/http-transport.js +21 -131
  57. package/lib/inspect.js +30 -284
  58. package/lib/ip-utils.js +6 -47
  59. package/lib/jose.js +7 -142
  60. package/lib/key.js +2 -67
  61. package/lib/lint.js +9 -194
  62. package/lib/merkle.js +1 -27
  63. package/lib/mime.js +9 -82
  64. package/lib/ocsp-request-verify.js +1 -14
  65. package/lib/ocsp-verify.js +1 -82
  66. package/lib/ocsp.js +11 -169
  67. package/lib/oid.js +5 -337
  68. package/lib/path-validate.js +103 -1492
  69. package/lib/pbes2.js +2 -89
  70. package/lib/pkcs12-build.js +31 -379
  71. package/lib/pki-build.js +64 -290
  72. package/lib/rc2.js +2 -18
  73. package/lib/rfc3339.js +4 -27
  74. package/lib/scep.js +59 -233
  75. package/lib/schema-all.js +0 -122
  76. package/lib/schema-attrcert.js +4 -146
  77. package/lib/schema-c509.js +153 -802
  78. package/lib/schema-cmc.js +5 -220
  79. package/lib/schema-cmp.js +2 -223
  80. package/lib/schema-cms.js +13 -427
  81. package/lib/schema-crl.js +5 -68
  82. package/lib/schema-crmf.js +6 -167
  83. package/lib/schema-csr.js +1 -54
  84. package/lib/schema-csrattrs.js +2 -87
  85. package/lib/schema-engine.js +0 -152
  86. package/lib/schema-ocsp.js +0 -117
  87. package/lib/schema-pkcs12.js +2 -93
  88. package/lib/schema-pkcs8.js +3 -35
  89. package/lib/schema-pkix.js +24 -655
  90. package/lib/schema-smime.js +0 -54
  91. package/lib/schema-tsp.js +3 -99
  92. package/lib/schema-x509.js +4 -63
  93. package/lib/shbs.js +11 -95
  94. package/lib/sign-scheme.js +2 -97
  95. package/lib/sigstore.js +9 -169
  96. package/lib/sleep.js +2 -8
  97. package/lib/smime.js +15 -408
  98. package/lib/tls-cert-compress.js +0 -62
  99. package/lib/trust.js +19 -185
  100. package/lib/tsp-sign.js +16 -270
  101. package/lib/validator-all.js +1 -30
  102. package/lib/validator-attcert.js +2 -46
  103. package/lib/validator-cose.js +2 -153
  104. package/lib/validator-keydesc.js +1 -37
  105. package/lib/validator-sig.js +1 -46
  106. package/lib/validator-tls.js +1 -88
  107. package/lib/validator-tpm.js +19 -122
  108. package/lib/verify-seam.js +1 -24
  109. package/lib/webauthn-mds.js +3 -400
  110. package/lib/webauthn.js +12 -833
  111. package/lib/webcrypto.js +5 -485
  112. package/lib/x509-sign.js +4 -108
  113. package/package.json +1 -1
  114. 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) { // cRLNumber ::= INTEGER (0..MAX)
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) { // reasonCode ::= ENUMERATED (CRLReason)
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) { // invalidityDate ::= GeneralizedTime
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, // Name is a seqOf -> field.value is the match; .result is the {rdns, dn} build
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; // signature + issuer
232
+ i += 2;
296
233
  var pos = tbs.children[i];
297
234
  return schema.isUniversalOneOf(pos, [TAGS.UTC_TIME, TAGS.GENERALIZED_TIME]);
298
235
  }
@@ -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); // EXPLICIT arm walks a universal RDNSequence
62
- var INAME = pkix.name(NS, { implicitTag: tag }); // IMPLICIT arm: [tag] children ARE the RDN SETs
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; // IMPLICIT empty RDNSequence
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; // IMPLICIT: children ARE RDN SETs
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; // EXPLICIT: [tag] wraps RDNSequence
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, // the exact CertRequest TLV the POP signature covers (sec. 4.1)
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
  };