@blamejs/pki 0.1.21 → 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,26 +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, friendlyName: 20, localKeyId: 21 } },
144
+ countersignature: 6, challengePassword: 7, extensionRequest: 14,
145
+ smimeCapabilities: 15, friendlyName: 20, localKeyId: 21 } },
145
146
 
146
- // 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).
147
148
  pkcs9CertTypes: { base: [1, 2, 840, 113549, 1, 9, 22], of: { x509Certificate: 1, sdsiCertificate: 2 } },
148
149
  pkcs9CrlTypes: { base: [1, 2, 840, 113549, 1, 9, 23], of: { x509CRL: 1 } },
149
150
 
150
151
  // PKCS#5 password-based schemes (RFC 8018) + the PBMAC1 MacData arm (RFC 9579).
151
152
  pkcs5: { base: [1, 2, 840, 113549, 1, 5], of: { pbkdf2: 12, pbes2: 13, pbmac1: 14 } },
152
153
 
153
- // 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
154
155
  // PKIXCMP-2023 module assigns these leaves, 8/9 unassigned).
155
156
  idIt: { base: [1, 3, 6, 1, 5, 5, 7, 4], of: {
156
157
  caProtEncCert: 1, signKeyPairTypes: 2, encKeyPairTypes: 3, preferredSymmAlg: 4,
@@ -161,15 +162,15 @@ var FAMILIES = {
161
162
  crls: 23, kemCiphertextInfo: 24 } },
162
163
 
163
164
  // CMP message-protection MAC algorithms on the Entrust arc (RFC 9810
164
- // §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).
165
166
  entrustAlg: { base: [1, 2, 840, 113533, 7, 66], of: {
166
167
  passwordBasedMac: 13, kemBasedMac: 16, dhBasedMac: 30 } },
167
168
 
168
- // PKCS#12 bag types (RFC 7292 §4.2, Appendix D).
169
+ // PKCS#12 bag types (RFC 7292 sec. 4.2, Appendix D).
169
170
  pkcs12BagTypes: { base: [1, 2, 840, 113549, 1, 12, 10, 1], of: {
170
171
  keyBag: 1, pkcs8ShroudedKeyBag: 2, certBag: 3, crlBag: 4, secretBag: 5, safeContentsBag: 6 } },
171
172
 
172
- // PKCS#12 password-based encryption schemes (RFC 7292 Appendix C) legacy
173
+ // PKCS#12 password-based encryption schemes (RFC 7292 Appendix C) -- legacy
173
174
  // PBE identifiers still emitted by deployed exporters; recognized so a
174
175
  // shrouded bag's algorithm resolves to a name, never decrypted here.
175
176
  pkcs12Pbe: { base: [1, 2, 840, 113549, 1, 12, 1], of: {
@@ -177,18 +178,42 @@ var FAMILIES = {
177
178
  "pbeWithSHAAnd3-KeyTripleDES-CBC": 3, "pbeWithSHAAnd2-KeyTripleDES-CBC": 4,
178
179
  "pbeWithSHAAnd128BitRC2-CBC": 5, "pbeWithSHAAnd40BitRC2-CBC": 6 } },
179
180
 
180
- // 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.
181
182
  rsadsiDigest: { base: [1, 2, 840, 113549, 2], of: {
182
183
  hmacWithSHA1: 7, hmacWithSHA224: 8, hmacWithSHA256: 9,
183
184
  hmacWithSHA384: 10, hmacWithSHA512: 11 } },
184
185
 
185
186
  // S/MIME content types on the PKCS#9 smime arc (RFC 5652, RFC 3161): id-ct.
186
- 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 } },
187
208
 
188
209
  // S/MIME authenticated attributes (id-aa, RFC 2634 / RFC 5035 / RFC 5816). The ESS
189
210
  // signing-certificate attributes bind a CMS / TSP SignerInfo to its signing cert;
190
- // 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).
212
+ // The RFC 2634 originals (receiptRequest .. contentReference) are name-only: they
213
+ // resolve to a name so an unsupported-attribute diagnostic is legible, not decoded.
191
214
  smimeAa: { base: [1, 2, 840, 113549, 1, 9, 16, 2], of: {
215
+ receiptRequest: 1, eSSSecurityLabel: 2, mlExpansionHistory: 3, contentHints: 4,
216
+ msgSigDigest: 5, contentIdentifier: 7, equivalentLabels: 9, contentReference: 10,
192
217
  signingCertificate: 12, timeStampToken: 14, signingCertificateV2: 47 } },
193
218
 
194
219
  // ANSI X9.62 EC public key, named curve, and ECDSA signatures.
@@ -202,22 +227,23 @@ var FAMILIES = {
202
227
  // Edwards / Montgomery curves (RFC 8410).
203
228
  edwards: { base: [1, 3, 101], of: { X25519: 110, X448: 111, Ed25519: 112, Ed448: 113 } },
204
229
 
205
- // 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;
206
231
  // nistHash covers only SHA-256 and above.
207
232
  oiwSecsig: { base: [1, 3, 14, 3, 2], of: { sha1: 26 } },
208
233
 
209
- // 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
210
235
  // modern PBES2 / CMS content ciphers a PKCS#12 or EnvelopedData names.
211
236
  nistAes: { base: [2, 16, 840, 1, 101, 3, 4, 1], of: {
212
- "aes128-CBC": 2, "aes128-GCM": 6, "aes192-CBC": 22, "aes192-GCM": 26,
213
- "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 } },
214
240
 
215
241
  // NIST hash functions (SHA-2, SHA-3, SHAKE).
216
242
  nistHash: { base: [2, 16, 840, 1, 101, 3, 4, 2], of: {
217
243
  sha256: 1, sha384: 2, sha512: 3, "sha3-256": 8, "sha3-512": 10, shake256: 12 } },
218
244
 
219
- // NIST signature algorithms FIPS 204 ML-DSA + FIPS 205 SLH-DSA share the
220
- // 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
221
247
  // sets .20-.31 (SHA-2 .20-.25, SHAKE .26-.31) and the 12 pre-hash HashSLH-DSA
222
248
  // sets .35-.46, each pairing a parameter set with its message-digest hash. The
223
249
  // parameters MUST be absent for every one of them (enforced via paramsMustBeAbsent).
@@ -236,7 +262,7 @@ var FAMILIES = {
236
262
  "id-hash-slh-dsa-shake-192s-with-shake256": 43, "id-hash-slh-dsa-shake-192f-with-shake256": 44,
237
263
  "id-hash-slh-dsa-shake-256s-with-shake256": 45, "id-hash-slh-dsa-shake-256f-with-shake256": 46 } },
238
264
 
239
- // 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).
240
266
  nistKem: { base: [2, 16, 840, 1, 101, 3, 4, 4], of: {
241
267
  "id-ml-kem-512": 1, "id-ml-kem-768": 2, "id-ml-kem-1024": 3 } },
242
268
 
@@ -261,7 +287,7 @@ function _isArc(a) {
261
287
  return typeof a === "number" && Number.isSafeInteger(a) && a >= 0;
262
288
  }
263
289
 
264
- // 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
265
291
  // the exact same primitive an operator uses (registerFamily), so there is one
266
292
  // path from a declared family to indexed OIDs and no dotted literals anywhere.
267
293
  Object.keys(FAMILIES).forEach(function (fam) {
@@ -269,11 +295,27 @@ Object.keys(FAMILIES).forEach(function (fam) {
269
295
  });
270
296
 
271
297
  function _assertDotted(dotted, who) {
272
- if (typeof dotted !== "string" || !/^\d+(\.\d+)+$/.test(dotted)) {
273
- 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)");
274
302
  }
275
303
  }
276
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
+
277
319
  /**
278
320
  * @primitive pki.oid.name
279
321
  * @signature pki.oid.name(dotted) -> string | undefined
@@ -312,10 +354,10 @@ function has(dotted) {
312
354
  * @spec X.660, RFC 5280
313
355
  * @related pki.oid.registerFamily, pki.oid.name
314
356
  *
315
- * 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
316
358
  * newly-standardized arc resolves through the same registry as the seed
317
359
  * set. A later registration of the same OID replaces the forward name;
318
- * the reverse (name OID) keeps the first registration as canonical.
360
+ * the reverse (name -> OID) keeps the first registration as canonical.
319
361
  *
320
362
  * @example
321
363
  * pki.oid.register("1.3.6.1.4.1.99999.1", "acmeWidgetPolicy");
@@ -323,6 +365,7 @@ function has(dotted) {
323
365
  function register(dotted, n) {
324
366
  _assertDotted(dotted, "register");
325
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");
326
369
  _index(dotted, n);
327
370
  }
328
371
 
@@ -336,7 +379,7 @@ function register(dotted, n) {
336
379
  *
337
380
  * Register a whole OID family in one call. `base` is the shared arc prefix
338
381
  * (the starting variable a class of OIDs has in common) and `members` maps
339
- * 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
340
383
  * multi-level leaf. Each full OID is derived as `base` followed by the leaf,
341
384
  * so a family is declared as its hierarchy rather than as re-spelled full
342
385
  * paths. This is the primitive the built-in seed set itself is built from.
@@ -367,6 +410,7 @@ function registerFamily(base, members) {
367
410
  if (!arcs.every(_isArc)) {
368
411
  throw new OidError("oid/bad-arc", "registerFamily: member " + JSON.stringify(nm) + " has a non-arc leaf");
369
412
  }
413
+ _assertEncodableArcs(arcs, "registerFamily");
370
414
  _index(arcs.join("."), nm);
371
415
  });
372
416
  }
@@ -377,7 +421,7 @@ function all() {
377
421
  return out;
378
422
  }
379
423
 
380
- // toArcs / fromArcs dotted <-> numeric arc array. Arcs come back as
424
+ // toArcs / fromArcs -- dotted <-> numeric arc array. Arcs come back as
381
425
  // Number where safe and BigInt where an arc exceeds 2^53 (rare, but a
382
426
  // UUID-based OID arc can), so the round-trip never loses precision.
383
427
  function toArcs(dotted) {
@@ -400,7 +444,7 @@ function fromArcs(arcs) {
400
444
  }).join(".");
401
445
  }
402
446
 
403
- // 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
404
448
  // one namespace when they have a dotted string in hand.
405
449
  function toDER(dotted) { _assertDotted(dotted, "toDER"); return asn1.build.oid(dotted); }
406
450
  function fromDER(input) {
@@ -409,11 +453,11 @@ function fromDER(input) {
409
453
  return asn1.read.oid(node);
410
454
  }
411
455
 
412
- // Algorithm identifiers whose `parameters` field MUST be absent no explicit
456
+ // Algorithm identifiers whose `parameters` field MUST be absent -- no explicit
413
457
  // NULL, no bytes. The FIPS 204 ML-DSA and FIPS 205 SLH-DSA signature families
414
- // and the RFC 8410 Edwards / Montgomery curves each carry this MUST: RFC 9909 §3
415
- // (SLH-DSA in X.509), RFC 9814 §4 (SLH-DSA in CMS), RFC 9881 §2 (ML-DSA in X.509),
416
- // 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
417
461
  // registry every caller uses, so a typo fails closed at load rather than silently
418
462
  // dropping a member. One set drives the single shared AlgorithmIdentifier reject
419
463
  // (schema-pkix), so every format consumer inherits the rule with no per-format code.
@@ -424,7 +468,7 @@ var _PARAMS_ABSENT = new Set();
424
468
  "id-slh-dsa-sha2-192f", "id-slh-dsa-sha2-256s", "id-slh-dsa-sha2-256f",
425
469
  "id-slh-dsa-shake-128s", "id-slh-dsa-shake-128f", "id-slh-dsa-shake-192s",
426
470
  "id-slh-dsa-shake-192f", "id-slh-dsa-shake-256s", "id-slh-dsa-shake-256f",
427
- // 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.
428
472
  "id-hash-slh-dsa-sha2-128s-with-sha256", "id-hash-slh-dsa-sha2-128f-with-sha256",
429
473
  "id-hash-slh-dsa-sha2-192s-with-sha512", "id-hash-slh-dsa-sha2-192f-with-sha512",
430
474
  "id-hash-slh-dsa-sha2-256s-with-sha512", "id-hash-slh-dsa-sha2-256f-with-sha512",
@@ -432,7 +476,19 @@ var _PARAMS_ABSENT = new Set();
432
476
  "id-hash-slh-dsa-shake-192s-with-shake256", "id-hash-slh-dsa-shake-192f-with-shake256",
433
477
  "id-hash-slh-dsa-shake-256s-with-shake256", "id-hash-slh-dsa-shake-256f-with-shake256",
434
478
  "Ed25519", "Ed448", "X25519", "X448",
435
- ].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
+ });
436
492
 
437
493
  /**
438
494
  * @primitive pki.oid.paramsMustBeAbsent