@blamejs/pki 0.1.22 → 0.1.23

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/lib/oid.js CHANGED
@@ -13,12 +13,12 @@
13
13
  * OID strings and their human names, plus arc conversion and DER
14
14
  * encode/decode convenience. Every algorithm, attribute type, and
15
15
  * extension in PKI is named by an OID, and resolving them through one
16
- * registry rather than scattering magic dotted strings across the
17
- * codebase is what lets a new algorithm be a data entry instead of a
16
+ * registry -- rather than scattering magic dotted strings across the
17
+ * codebase -- is what lets a new algorithm be a data entry instead of a
18
18
  * code change.
19
19
  *
20
20
  * The seed set is declared by FAMILY: an OID belongs to a class with a
21
- * shared base arc (the "starting variable" `2.5.4` for the RFC 5280
21
+ * shared base arc (the "starting variable" -- `2.5.4` for the RFC 5280
22
22
  * attribute types, `2.5.29` for the extensions, `2.16.840.1.101.3.4` for
23
23
  * the NIST algorithms), and each member names only its trailing arc. The
24
24
  * full OID is derived from base + leaf at load, so the arc hierarchy that
@@ -29,7 +29,7 @@
29
29
  * extend it with `register` (one OID) or `registerFamily` (a whole arc).
30
30
  *
31
31
  * @card
32
- * Two-way OID name registry with arc conversion, seeded by family from
32
+ * Two-way OID <-> name registry with arc conversion, seeded by family from
33
33
  * the RFC 5280 and NIST post-quantum object identifiers.
34
34
  */
35
35
 
@@ -38,72 +38,72 @@ var frameworkError = require("./framework-error");
38
38
 
39
39
  var OidError = frameworkError.OidError;
40
40
 
41
- // FAMILIES OIDs grouped by their shared base arc (the "similar starting
41
+ // FAMILIES -- OIDs grouped by their shared base arc (the "similar starting
42
42
  // variable" that defines a class). A member is `name: leaf`, where leaf is a
43
43
  // trailing arc (number) or a short arc array for a multi-level leaf; the full
44
44
  // arc is derived at load via base.concat(leaf). Declaring by family means no
45
45
  // dotted-decimal OID literal appears in this source at all, and adding a
46
- // member is one `name: leaf` line under its class no base to re-type.
46
+ // member is one `name: leaf` line under its class -- no base to re-type.
47
47
  var FAMILIES = {
48
48
  // RFC 5280 attribute types.
49
49
  attributeType: { base: [2, 5, 4], of: {
50
50
  commonName: 3, surname: 4, serialNumber: 5, countryName: 6,
51
51
  localityName: 7, stateOrProvinceName: 8, streetAddress: 9,
52
52
  organizationName: 10, organizationalUnitName: 11, title: 12, givenName: 42,
53
- // RFC 5755 §4.4 attribute-certificate attribute types.
53
+ // RFC 5755 sec. 4.4 attribute-certificate attribute types.
54
54
  clearance: 55, role: 72 } },
55
55
 
56
56
  // RFC 5280 certificate extensions.
57
57
  certExtension: { base: [2, 5, 29], of: {
58
58
  subjectKeyIdentifier: 14, keyUsage: 15, subjectAltName: 17, issuerAltName: 18,
59
59
  basicConstraints: 19,
60
- // CRL + CRL-entry extensions (RFC 5280 §5.2, §5.3)
60
+ // CRL + CRL-entry extensions (RFC 5280 sec. 5.2, sec. 5.3)
61
61
  cRLNumber: 20, reasonCode: 21, invalidityDate: 24, deltaCRLIndicator: 27,
62
62
  issuingDistributionPoint: 28, certificateIssuer: 29,
63
63
  nameConstraints: 30, cRLDistributionPoints: 31,
64
64
  certificatePolicies: 32, policyMappings: 33, authorityKeyIdentifier: 35,
65
65
  policyConstraints: 36, extKeyUsage: 37, freshestCRL: 46,
66
66
  inhibitAnyPolicy: 54,
67
- // The two special-OID leaves under extension arcs (RFC 5280 §4.2.1.4 /
68
- // §4.2.1.12): the certificate-policies wildcard and the EKU wildcard.
67
+ // The two special-OID leaves under extension arcs (RFC 5280 sec. 4.2.1.4 /
68
+ // sec. 4.2.1.12): the certificate-policies wildcard and the EKU wildcard.
69
69
  anyPolicy: [32, 0], anyExtendedKeyUsage: [37, 0],
70
- // RFC 5755 attribute-certificate extensions (§4.3.2, §4.3.6).
70
+ // RFC 5755 attribute-certificate extensions (sec. 4.3.2, sec. 4.3.6).
71
71
  targetInformation: 55, noRevAvail: 56 } },
72
72
 
73
73
  // PKIX private extensions on the id-pe arc (authorityInfoAccess et al, plus the
74
- // RFC 5755 attribute-certificate id-pe extensions: ac-auditIdentity §4.3.1,
75
- // aaControls §7.4, ac-proxying §7.2).
74
+ // RFC 5755 attribute-certificate id-pe extensions: ac-auditIdentity sec. 4.3.1,
75
+ // aaControls sec. 7.4, ac-proxying sec. 7.2).
76
76
  pkixAccess: { base: [1, 3, 6, 1, 5, 5, 7, 1], of: {
77
77
  authorityInfoAccess: 1, acAuditIdentity: 4, aaControls: 6, acProxying: 10 } },
78
78
 
79
- // id-aca RFC 5755 attribute-certificate attribute types on the id-pkix 10 arc:
80
- // authenticationInfo §4.4.1 .. group §4.4.4, plus encAttrs §7.1 (the encrypted-
79
+ // id-aca -- RFC 5755 attribute-certificate attribute types on the id-pkix 10 arc:
80
+ // authenticationInfo sec. 4.4.1 .. group sec. 4.4.4, plus encAttrs sec. 7.1 (the encrypted-
81
81
  // attribute wrapper, syntax ContentInfo). { id-aca 5 } is reserved.
82
82
  idAca: { base: [1, 3, 6, 1, 5, 5, 7, 10], of: {
83
83
  authenticationInfo: 1, accessIdentity: 2, chargingIdentity: 3, group: 4, encAttrs: 6 } },
84
84
 
85
85
  // The legacy RFC 3281 id-at-clearance on the X.501 selected-attribute-types arc.
86
- // RFC 5755 §4.4.6 says implementations MUST NOT OUTPUT this form but SHOULD ACCEPT
86
+ // RFC 5755 sec. 4.4.6 says implementations MUST NOT OUTPUT this form but SHOULD ACCEPT
87
87
  // it for decoding, so it is registered as an alias of "clearance". The canonical
88
88
  // name -> OID reverse stays the RFC 5755 attributeType-arc 2.5.4.55 (registered
89
89
  // first under attributeType, so it wins the reverse mapping).
90
90
  selectedAttrType: { base: [2, 5, 1, 5], of: { clearance: 55 } },
91
91
 
92
- // PKIX Access Descriptor methods (id-ad, RFC 5280 §4.2.2.14.2.2.2). id-ad-ocsp
92
+ // PKIX Access Descriptor methods (id-ad, RFC 5280 sec. 4.2.2.1/sec. 4.2.2.2). id-ad-ocsp
93
93
  // is the arc the OCSP responder OIDs hang under; id-ad-caIssuers names the AIA
94
94
  // CA-issuers access method.
95
95
  adAccess: { base: [1, 3, 6, 1, 5, 5, 7, 48], of: { ocsp: 1, caIssuers: 2 } },
96
96
 
97
97
  // OCSP (RFC 6960) on the id-pkix-ocsp arc (= id-ad-ocsp). id-pkix-ocsp-basic is
98
- // the ResponseBytes.responseType this build decodes; id-pkix-ocsp-nonce (§4.4.1)
98
+ // the ResponseBytes.responseType this build decodes; id-pkix-ocsp-nonce (sec. 4.4.1)
99
99
  // names the nonce extension; the remaining members name the other OCSP extensions
100
100
  // (CRL references, acceptable-response-types, archive-cutoff, service-locator,
101
- // preferred-signature-algorithms, extended-revoke RFC 6960 §4.4, RFC 9654).
101
+ // preferred-signature-algorithms, extended-revoke -- RFC 6960 sec. 4.4, RFC 9654).
102
102
  ocsp: { base: [1, 3, 6, 1, 5, 5, 7, 48, 1], of: {
103
103
  ocspBasic: 1, ocspNonce: 2, ocspCrl: 3, ocspResponse: 4, ocspNoCheck: 5,
104
104
  ocspArchiveCutoff: 6, ocspServiceLocator: 7, ocspPrefSigAlgs: 8, ocspExtendedRevoke: 9 } },
105
105
 
106
- // CRMF (RFC 4211) registration controls (§6) and registration info (§7) on the
106
+ // CRMF (RFC 4211) registration controls (sec. 6) and registration info (sec. 7) on the
107
107
  // id-pkip arc (id-pkix 5). id-regCtrl (id-pkip 1) names the control types a
108
108
  // CertRequest carries; id-regInfo (id-pkip 2) names the registration-info types.
109
109
  // The parser surfaces each control/info value RAW keyed by these names.
@@ -111,15 +111,15 @@ var FAMILIES = {
111
111
  regToken: 1, authenticator: 2, pkiPublicationInfo: 3, pkiArchiveOptions: 4, oldCertID: 5, protocolEncrKey: 6 } },
112
112
  regInfo: { base: [1, 3, 6, 1, 5, 5, 7, 5, 2], of: { utf8Pairs: 1, certReq: 2 } },
113
113
 
114
- // PKIX extended key purposes (id-kp, RFC 5280 §4.2.1.12). timeStamping is required
115
- // critical and sole on an RFC 3161 TSA signing certificate (§2.3).
114
+ // PKIX extended key purposes (id-kp, RFC 5280 sec. 4.2.1.12). timeStamping is required
115
+ // -- critical and sole -- on an RFC 3161 TSA signing certificate (sec. 2.3).
116
116
  pkixKp: { base: [1, 3, 6, 1, 5, 5, 7, 3], of: {
117
117
  serverAuth: 1, clientAuth: 2, codeSigning: 3, emailProtection: 4, timeStamping: 8, ocspSigning: 9 } },
118
118
 
119
119
  // Google Certificate Transparency (RFC 6962) on the 1.3.6.1.4.1.11129.2.4 arc:
120
- // the SCT-list X.509 extension (§3.3), the precertificate poison (§3.1), the
121
- // precert-signing EKU (§3.1, naming only), and the OCSP-delivered SCT list
122
- // (§3.3). The SCT payload itself is TLS presentation language, not DER it is
120
+ // the SCT-list X.509 extension (sec. 3.3), the precertificate poison (sec. 3.1), the
121
+ // precert-signing EKU (sec. 3.1, naming only), and the OCSP-delivered SCT list
122
+ // (sec. 3.3). The SCT payload itself is TLS presentation language, not DER -- it is
123
123
  // parsed by lib/ct.js (pki.ct), never routed through the DER schema engine.
124
124
  ct: { base: [1, 3, 6, 1, 4, 1, 11129, 2, 4], of: {
125
125
  signedCertificateTimestampList: 2, precertificatePoison: 3,
@@ -131,27 +131,27 @@ var FAMILIES = {
131
131
  sha256WithRSAEncryption: 11,
132
132
  sha384WithRSAEncryption: 12, sha512WithRSAEncryption: 13 } },
133
133
 
134
- // PKCS#7 / CMS content types (RFC 5652 §4, RFC 2315). id-signedData is the one
134
+ // PKCS#7 / CMS content types (RFC 5652 sec. 4, RFC 2315). id-signedData is the one
135
135
  // this toolkit structurally decodes; the rest are recognized-and-deferred.
136
136
  pkcs7: { base: [1, 2, 840, 113549, 1, 7], of: {
137
137
  data: 1, signedData: 2, envelopedData: 3, signedAndEnvelopedData: 4,
138
138
  digestedData: 5, encryptedData: 6 } },
139
139
 
140
- // PKCS#9 attribute types incl. the CMS signed-attribute OIDs (RFC 5652 §11)
141
- // and the PKCS#12 bag attributes friendlyName / localKeyId (RFC 7292 §4.2).
140
+ // PKCS#9 attribute types -- incl. the CMS signed-attribute OIDs (RFC 5652 sec. 11)
141
+ // and the PKCS#12 bag attributes friendlyName / localKeyId (RFC 7292 sec. 4.2).
142
142
  pkcs9: { base: [1, 2, 840, 113549, 1, 9], of: {
143
143
  emailAddress: 1, contentType: 3, messageDigest: 4, signingTime: 5,
144
- challengePassword: 7, extensionRequest: 14, smimeCapabilities: 15,
145
- friendlyName: 20, localKeyId: 21 } },
144
+ countersignature: 6, challengePassword: 7, extensionRequest: 14,
145
+ smimeCapabilities: 15, friendlyName: 20, localKeyId: 21 } },
146
146
 
147
- // PKCS#9 CertBag / CRLBag value discriminators (RFC 7292 §4.2.34.2.4).
147
+ // PKCS#9 CertBag / CRLBag value discriminators (RFC 7292 sec. 4.2.3-sec. 4.2.4).
148
148
  pkcs9CertTypes: { base: [1, 2, 840, 113549, 1, 9, 22], of: { x509Certificate: 1, sdsiCertificate: 2 } },
149
149
  pkcs9CrlTypes: { base: [1, 2, 840, 113549, 1, 9, 23], of: { x509CRL: 1 } },
150
150
 
151
151
  // PKCS#5 password-based schemes (RFC 8018) + the PBMAC1 MacData arm (RFC 9579).
152
152
  pkcs5: { base: [1, 2, 840, 113549, 1, 5], of: { pbkdf2: 12, pbes2: 13, pbmac1: 14 } },
153
153
 
154
- // CMP InfoTypeAndValue types id-it under id-pkix (RFC 9810 §5.3.19; the
154
+ // CMP InfoTypeAndValue types -- id-it under id-pkix (RFC 9810 sec. 5.3.19; the
155
155
  // PKIXCMP-2023 module assigns these leaves, 8/9 unassigned).
156
156
  idIt: { base: [1, 3, 6, 1, 5, 5, 7, 4], of: {
157
157
  caProtEncCert: 1, signKeyPairTypes: 2, encKeyPairTypes: 3, preferredSymmAlg: 4,
@@ -162,15 +162,15 @@ var FAMILIES = {
162
162
  crls: 23, kemCiphertextInfo: 24 } },
163
163
 
164
164
  // CMP message-protection MAC algorithms on the Entrust arc (RFC 9810
165
- // §5.1.3.1/.2/.4; RFC 9481 §6.1.1).
165
+ // sec. 5.1.3.1/.2/.4; RFC 9481 sec. 6.1.1).
166
166
  entrustAlg: { base: [1, 2, 840, 113533, 7, 66], of: {
167
167
  passwordBasedMac: 13, kemBasedMac: 16, dhBasedMac: 30 } },
168
168
 
169
- // PKCS#12 bag types (RFC 7292 §4.2, Appendix D).
169
+ // PKCS#12 bag types (RFC 7292 sec. 4.2, Appendix D).
170
170
  pkcs12BagTypes: { base: [1, 2, 840, 113549, 1, 12, 10, 1], of: {
171
171
  keyBag: 1, pkcs8ShroudedKeyBag: 2, certBag: 3, crlBag: 4, secretBag: 5, safeContentsBag: 6 } },
172
172
 
173
- // PKCS#12 password-based encryption schemes (RFC 7292 Appendix C) legacy
173
+ // PKCS#12 password-based encryption schemes (RFC 7292 Appendix C) -- legacy
174
174
  // PBE identifiers still emitted by deployed exporters; recognized so a
175
175
  // shrouded bag's algorithm resolves to a name, never decrypted here.
176
176
  pkcs12Pbe: { base: [1, 2, 840, 113549, 1, 12, 1], of: {
@@ -178,17 +178,37 @@ var FAMILIES = {
178
178
  "pbeWithSHAAnd3-KeyTripleDES-CBC": 3, "pbeWithSHAAnd2-KeyTripleDES-CBC": 4,
179
179
  "pbeWithSHAAnd128BitRC2-CBC": 5, "pbeWithSHAAnd40BitRC2-CBC": 6 } },
180
180
 
181
- // RSADSI digest / HMAC algorithms (RFC 8018 §B.1) the PBKDF2 / PBMAC1 PRFs.
181
+ // RSADSI digest / HMAC algorithms (RFC 8018 sec. B.1) -- the PBKDF2 / PBMAC1 PRFs.
182
182
  rsadsiDigest: { base: [1, 2, 840, 113549, 2], of: {
183
183
  hmacWithSHA1: 7, hmacWithSHA224: 8, hmacWithSHA256: 9,
184
184
  hmacWithSHA384: 10, hmacWithSHA512: 11 } },
185
185
 
186
186
  // S/MIME content types on the PKCS#9 smime arc (RFC 5652, RFC 3161): id-ct.
187
- smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4, encKeyWithID: 21 } },
187
+ // authData is RFC 5652 sec. 9 AuthenticatedData; authEnvelopedData is RFC 5083.
188
+ smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4, authEnvelopedData: 23, encKeyWithID: 21 } },
189
+
190
+ // S/MIME other-recipient-info types (id-ori, RFC 5652 sec. 6.2.5). id-ori-kem
191
+ // carries a KEMRecipientInfo (RFC 9629) inside the ori [4] RecipientInfo arm.
192
+ smimeOri: { base: [1, 2, 840, 113549, 1, 9, 16, 13], of: { kem: 3 } },
193
+
194
+ // S/MIME algorithm identifiers (id-alg). The HKDF KDFs (RFC 8619) and the
195
+ // CEK-HKDF content-encryption wrapper (RFC 9709) a KEMRecipientInfo names, plus
196
+ // the RSA-KEM SPKI algorithm (RFC 9690). Parameters are absent for the HKDFs.
197
+ smimeAlg: { base: [1, 2, 840, 113549, 1, 9, 16, 3], of: {
198
+ "id-rsa-kem": 14, hkdfWithSha256: 28, hkdfWithSha384: 29, hkdfWithSha512: 30, cekHkdfSha256: 31 } },
199
+
200
+ // RSA-KEM key-transport algorithm (RFC 9690, obsoletes RFC 5990) on the ISO
201
+ // 18033-2 arc -- the kem OID an RSA KEMRecipientInfo carries (distinct from the
202
+ // id-rsa-kem SPKI algorithm above).
203
+ iso18033: { base: [1, 0, 18033, 2, 2], of: { "id-kem-rsa": 4 } },
204
+
205
+ // RFC 3370 HMAC-SHA-1 MAC algorithm (the AuthenticatedData macAlgorithm on the
206
+ // PKIX arc, distinct from the RSADSI hmacWithSHA1).
207
+ pkixHmac: { base: [1, 3, 6, 1, 5, 5, 8, 1], of: { "hmac-SHA1": 2 } },
188
208
 
189
209
  // S/MIME authenticated attributes (id-aa, RFC 2634 / RFC 5035 / RFC 5816). The ESS
190
210
  // signing-certificate attributes bind a CMS / TSP SignerInfo to its signing cert;
191
- // signingCertificateV2 (ESSCertIDv2) carries a non-SHA-1 cert hash (RFC 5816 §2.2.1).
211
+ // signingCertificateV2 (ESSCertIDv2) carries a non-SHA-1 cert hash (RFC 5816 sec. 2.2.1).
192
212
  // The RFC 2634 originals (receiptRequest .. contentReference) are name-only: they
193
213
  // resolve to a name so an unsupported-attribute diagnostic is legible, not decoded.
194
214
  smimeAa: { base: [1, 2, 840, 113549, 1, 9, 16, 2], of: {
@@ -207,22 +227,23 @@ var FAMILIES = {
207
227
  // Edwards / Montgomery curves (RFC 8410).
208
228
  edwards: { base: [1, 3, 101], of: { X25519: 110, X448: 111, Ed25519: 112, Ed448: 113 } },
209
229
 
210
- // OIW Secsig SHA-1 (1.3.14.3.2.26) the most common OCSP CertID hashAlgorithm;
230
+ // OIW Secsig SHA-1 (1.3.14.3.2.26) -- the most common OCSP CertID hashAlgorithm;
211
231
  // nistHash covers only SHA-256 and above.
212
232
  oiwSecsig: { base: [1, 3, 14, 3, 2], of: { sha1: 26 } },
213
233
 
214
- // NIST AES content-encryption algorithms (arc 2.16.840.1.101.3.4.1) the
234
+ // NIST AES content-encryption algorithms (arc 2.16.840.1.101.3.4.1) -- the
215
235
  // modern PBES2 / CMS content ciphers a PKCS#12 or EnvelopedData names.
216
236
  nistAes: { base: [2, 16, 840, 1, 101, 3, 4, 1], of: {
217
- "aes128-CBC": 2, "aes128-GCM": 6, "aes192-CBC": 22, "aes192-GCM": 26,
218
- "aes256-CBC": 42, "aes256-GCM": 46 } },
237
+ "aes128-CBC": 2, "aes128-wrap": 5, "aes128-GCM": 6, "aes128-CCM": 7,
238
+ "aes192-CBC": 22, "aes192-wrap": 25, "aes192-GCM": 26, "aes192-CCM": 27,
239
+ "aes256-CBC": 42, "aes256-wrap": 45, "aes256-GCM": 46, "aes256-CCM": 47 } },
219
240
 
220
241
  // NIST hash functions (SHA-2, SHA-3, SHAKE).
221
242
  nistHash: { base: [2, 16, 840, 1, 101, 3, 4, 2], of: {
222
243
  sha256: 1, sha384: 2, sha512: 3, "sha3-256": 8, "sha3-512": 10, shake256: 12 } },
223
244
 
224
- // NIST signature algorithms FIPS 204 ML-DSA + FIPS 205 SLH-DSA share the
225
- // signature arc 2.16.840.1.101.3.4.3. RFC 9909 §3 assigns the 12 Pure SLH-DSA
245
+ // NIST signature algorithms -- FIPS 204 ML-DSA + FIPS 205 SLH-DSA share the
246
+ // signature arc 2.16.840.1.101.3.4.3. RFC 9909 sec. 3 assigns the 12 Pure SLH-DSA
226
247
  // sets .20-.31 (SHA-2 .20-.25, SHAKE .26-.31) and the 12 pre-hash HashSLH-DSA
227
248
  // sets .35-.46, each pairing a parameter set with its message-digest hash. The
228
249
  // parameters MUST be absent for every one of them (enforced via paramsMustBeAbsent).
@@ -241,7 +262,7 @@ var FAMILIES = {
241
262
  "id-hash-slh-dsa-shake-192s-with-shake256": 43, "id-hash-slh-dsa-shake-192f-with-shake256": 44,
242
263
  "id-hash-slh-dsa-shake-256s-with-shake256": 45, "id-hash-slh-dsa-shake-256f-with-shake256": 46 } },
243
264
 
244
- // NIST KEM FIPS 203 ML-KEM (arc 2.16.840.1.101.3.4.4).
265
+ // NIST KEM -- FIPS 203 ML-KEM (arc 2.16.840.1.101.3.4.4).
245
266
  nistKem: { base: [2, 16, 840, 1, 101, 3, 4, 4], of: {
246
267
  "id-ml-kem-512": 1, "id-ml-kem-768": 2, "id-ml-kem-1024": 3 } },
247
268
 
@@ -266,7 +287,7 @@ function _isArc(a) {
266
287
  return typeof a === "number" && Number.isSafeInteger(a) && a >= 0;
267
288
  }
268
289
 
269
- // Seed the registry family by family the built-in set is registered through
290
+ // Seed the registry family by family -- the built-in set is registered through
270
291
  // the exact same primitive an operator uses (registerFamily), so there is one
271
292
  // path from a declared family to indexed OIDs and no dotted literals anywhere.
272
293
  Object.keys(FAMILIES).forEach(function (fam) {
@@ -274,11 +295,27 @@ Object.keys(FAMILIES).forEach(function (fam) {
274
295
  });
275
296
 
276
297
  function _assertDotted(dotted, who) {
277
- if (typeof dotted !== "string" || !/^\d+(\.\d+)+$/.test(dotted)) {
278
- throw new OidError("oid/bad-input", who + ": expected a dotted-decimal OID string");
298
+ // No leading-zero components: "01.2.840" is a key no decoded OID can match,
299
+ // and the arc converters round-trip it to a DIFFERENT OID ("1.2.840").
300
+ if (typeof dotted !== "string" || !/^(0|[1-9]\d*)(\.(0|[1-9]\d*))+$/.test(dotted)) {
301
+ throw new OidError("oid/bad-input", who + ": expected a dotted-decimal OID string (no leading-zero components)");
279
302
  }
280
303
  }
281
304
 
305
+ // X.660 encodability: the root arc is 0..2 and, under roots 0 and 1, the
306
+ // second arc is 0..39 (the first two arcs pack into a single octet as
307
+ // 40*X+Y). An OID outside these bounds can never be DER-encoded, so a
308
+ // registration carrying one fails at config time rather than at first use.
309
+ function _assertEncodableArcs(arcs, who) {
310
+ if (arcs.length < 2) {
311
+ throw new OidError("oid/bad-input", who + ": an OID must have at least 2 arcs");
312
+ }
313
+ var root = typeof arcs[0] === "bigint" ? arcs[0] : BigInt(arcs[0]);
314
+ var second = typeof arcs[1] === "bigint" ? arcs[1] : BigInt(arcs[1]);
315
+ if (root > 2n) throw new OidError("oid/bad-arc", who + ": the root arc must be 0, 1, or 2 (X.660)");
316
+ if (root < 2n && second > 39n) throw new OidError("oid/bad-arc", who + ": the second arc must be 0..39 under roots 0 and 1 (X.660)");
317
+ }
318
+
282
319
  /**
283
320
  * @primitive pki.oid.name
284
321
  * @signature pki.oid.name(dotted) -> string | undefined
@@ -317,10 +354,10 @@ function has(dotted) {
317
354
  * @spec X.660, RFC 5280
318
355
  * @related pki.oid.registerFamily, pki.oid.name
319
356
  *
320
- * Add (or override) an OID name mapping so an operator's private or
357
+ * Add (or override) an OID -> name mapping so an operator's private or
321
358
  * newly-standardized arc resolves through the same registry as the seed
322
359
  * set. A later registration of the same OID replaces the forward name;
323
- * the reverse (name OID) keeps the first registration as canonical.
360
+ * the reverse (name -> OID) keeps the first registration as canonical.
324
361
  *
325
362
  * @example
326
363
  * pki.oid.register("1.3.6.1.4.1.99999.1", "acmeWidgetPolicy");
@@ -328,6 +365,7 @@ function has(dotted) {
328
365
  function register(dotted, n) {
329
366
  _assertDotted(dotted, "register");
330
367
  if (typeof n !== "string" || n.length === 0) throw new OidError("oid/bad-input", "register: name must be a non-empty string");
368
+ _assertEncodableArcs(dotted.split(".").map(BigInt), "register");
331
369
  _index(dotted, n);
332
370
  }
333
371
 
@@ -341,7 +379,7 @@ function register(dotted, n) {
341
379
  *
342
380
  * Register a whole OID family in one call. `base` is the shared arc prefix
343
381
  * (the starting variable a class of OIDs has in common) and `members` maps
344
- * each name to its trailing arc a number, or a short arc array for a
382
+ * each name to its trailing arc -- a number, or a short arc array for a
345
383
  * multi-level leaf. Each full OID is derived as `base` followed by the leaf,
346
384
  * so a family is declared as its hierarchy rather than as re-spelled full
347
385
  * paths. This is the primitive the built-in seed set itself is built from.
@@ -372,6 +410,7 @@ function registerFamily(base, members) {
372
410
  if (!arcs.every(_isArc)) {
373
411
  throw new OidError("oid/bad-arc", "registerFamily: member " + JSON.stringify(nm) + " has a non-arc leaf");
374
412
  }
413
+ _assertEncodableArcs(arcs, "registerFamily");
375
414
  _index(arcs.join("."), nm);
376
415
  });
377
416
  }
@@ -382,7 +421,7 @@ function all() {
382
421
  return out;
383
422
  }
384
423
 
385
- // toArcs / fromArcs dotted <-> numeric arc array. Arcs come back as
424
+ // toArcs / fromArcs -- dotted <-> numeric arc array. Arcs come back as
386
425
  // Number where safe and BigInt where an arc exceeds 2^53 (rare, but a
387
426
  // UUID-based OID arc can), so the round-trip never loses precision.
388
427
  function toArcs(dotted) {
@@ -405,7 +444,7 @@ function fromArcs(arcs) {
405
444
  }).join(".");
406
445
  }
407
446
 
408
- // DER convenience thin pass-throughs to the codec so callers reach for
447
+ // DER convenience -- thin pass-throughs to the codec so callers reach for
409
448
  // one namespace when they have a dotted string in hand.
410
449
  function toDER(dotted) { _assertDotted(dotted, "toDER"); return asn1.build.oid(dotted); }
411
450
  function fromDER(input) {
@@ -414,11 +453,11 @@ function fromDER(input) {
414
453
  return asn1.read.oid(node);
415
454
  }
416
455
 
417
- // Algorithm identifiers whose `parameters` field MUST be absent no explicit
456
+ // Algorithm identifiers whose `parameters` field MUST be absent -- no explicit
418
457
  // NULL, no bytes. The FIPS 204 ML-DSA and FIPS 205 SLH-DSA signature families
419
- // and the RFC 8410 Edwards / Montgomery curves each carry this MUST: RFC 9909 §3
420
- // (SLH-DSA in X.509), RFC 9814 §4 (SLH-DSA in CMS), RFC 9881 §2 (ML-DSA in X.509),
421
- // RFC 8410 §3 (Ed25519 / Ed448 / X25519 / X448). Built by NAME through the same
458
+ // and the RFC 8410 Edwards / Montgomery curves each carry this MUST: RFC 9909 sec. 3
459
+ // (SLH-DSA in X.509), RFC 9814 sec. 4 (SLH-DSA in CMS), RFC 9881 sec. 2 (ML-DSA in X.509),
460
+ // RFC 8410 sec. 3 (Ed25519 / Ed448 / X25519 / X448). Built by NAME through the same
422
461
  // registry every caller uses, so a typo fails closed at load rather than silently
423
462
  // dropping a member. One set drives the single shared AlgorithmIdentifier reject
424
463
  // (schema-pkix), so every format consumer inherits the rule with no per-format code.
@@ -429,7 +468,7 @@ var _PARAMS_ABSENT = new Set();
429
468
  "id-slh-dsa-sha2-192f", "id-slh-dsa-sha2-256s", "id-slh-dsa-sha2-256f",
430
469
  "id-slh-dsa-shake-128s", "id-slh-dsa-shake-128f", "id-slh-dsa-shake-192s",
431
470
  "id-slh-dsa-shake-192f", "id-slh-dsa-shake-256s", "id-slh-dsa-shake-256f",
432
- // Pre-hash HashSLH-DSA sets (RFC 9909 §3) parameters MUST be absent too.
471
+ // Pre-hash HashSLH-DSA sets (RFC 9909 sec. 3) -- parameters MUST be absent too.
433
472
  "id-hash-slh-dsa-sha2-128s-with-sha256", "id-hash-slh-dsa-sha2-128f-with-sha256",
434
473
  "id-hash-slh-dsa-sha2-192s-with-sha512", "id-hash-slh-dsa-sha2-192f-with-sha512",
435
474
  "id-hash-slh-dsa-sha2-256s-with-sha512", "id-hash-slh-dsa-sha2-256f-with-sha512",
@@ -437,7 +476,19 @@ var _PARAMS_ABSENT = new Set();
437
476
  "id-hash-slh-dsa-shake-192s-with-shake256", "id-hash-slh-dsa-shake-192f-with-shake256",
438
477
  "id-hash-slh-dsa-shake-256s-with-shake256", "id-hash-slh-dsa-shake-256f-with-shake256",
439
478
  "Ed25519", "Ed448", "X25519", "X448",
440
- ].forEach(function (nm) { _PARAMS_ABSENT.add(byName(nm)); });
479
+ // FIPS 203 ML-KEM (RFC 9936 sec. 3: PARAMS ARE absent).
480
+ "id-ml-kem-512", "id-ml-kem-768", "id-ml-kem-1024",
481
+ // HKDF key-derivation identifiers (RFC 8619 sec. 2: when any of these appear
482
+ // within AlgorithmIdentifier, the parameters component SHALL be absent).
483
+ "hkdfWithSha256", "hkdfWithSha384", "hkdfWithSha512",
484
+ ].forEach(function (nm) {
485
+ var d = byName(nm);
486
+ // A seed-list typo must fail at module load -- admitting undefined would
487
+ // make paramsMustBeAbsent(byName(sameTypo)) return TRUE for every other
488
+ // unregistered name (Set.has(undefined)), a fail-open guard.
489
+ if (typeof d !== "string") throw new OidError("oid/unknown-name", "paramsMustBeAbsent seed: " + JSON.stringify(nm) + " is not a registered name");
490
+ _PARAMS_ABSENT.add(d);
491
+ });
441
492
 
442
493
  /**
443
494
  * @primitive pki.oid.paramsMustBeAbsent