@blamejs/pki 0.8.10 → 0.8.11

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.
@@ -35,6 +35,7 @@ var schema = require("./schema-engine");
35
35
  var oid = require("./oid");
36
36
  var frameworkError = require("./framework-error");
37
37
  var pkix = require("./schema-pkix");
38
+ var intrinsic = require("./guard-intrinsic");
38
39
 
39
40
  var CertificateError = frameworkError.CertificateError;
40
41
  var PemError = frameworkError.PemError;
@@ -124,7 +125,7 @@ var CERTIFICATE = pkix.signedEnvelope(NS, CERTIFICATE_TBS, {
124
125
  build: function (e, ctx) {
125
126
  var tbs = e.tbsMatch;
126
127
 
127
- if (!e.outerSignatureAlgorithmBytes.equals(tbs.fields.signature.node.bytes)) {
128
+ if (!intrinsic.bufferEquals(e.outerSignatureAlgorithmBytes, tbs.fields.signature.node.bytes)) {
128
129
  throw ctx.E("x509/bad-signature-algorithm", "signatureAlgorithm must match tbsCertificate.signature (RFC 5280 sec. 4.1.1.2)");
129
130
  }
130
131
 
@@ -148,7 +149,7 @@ var CERTIFICATE = pkix.signedEnvelope(NS, CERTIFICATE_TBS, {
148
149
  return {
149
150
  version: version,
150
151
  serialNumber: tbs.fields.serialNumber.value,
151
- serialNumberHex: serialNode.content.toString("hex"),
152
+ serialNumberHex: intrinsic.bufToString(serialNode.content, "hex"),
152
153
  signatureAlgorithm: e.signatureAlgorithm,
153
154
  tbsSignatureAlgorithm: tbs.fields.signature.value.result,
154
155
  issuer: issuer,
@@ -213,14 +214,107 @@ function matches(root) {
213
214
  var validity = kids[i + 3];
214
215
  return schema.isUniversal(validity, TAGS.SEQUENCE) &&
215
216
  !!validity.children && validity.children.length === 2 &&
216
- validity.children.every(function (t) {
217
+ intrinsic.every(validity.children, function (t) {
217
218
  return schema.isUniversalOneOf(t, [TAGS.UTC_TIME, TAGS.GENERALIZED_TIME]);
218
219
  });
219
220
  }
220
221
 
222
+ /** @internal The certificate scope, as the shared door reads it: RFC 5280 sec. 4.2's extension
223
+ * table and the criticality that profile fixes. One scope, so the single-extension verb takes no
224
+ * `opts.scope`. */
225
+ var CERT_SCOPES = intrinsic.assign(intrinsic.create(null), {
226
+ certificate: {
227
+ decoders: function (ctx) { return pkix.certExtensionDecoders(ctx).byOid; },
228
+ criticality: pkix.certFixedCriticality(oid),
229
+ },
230
+ });
231
+ var DOOR = pkix.extensionDoor({
232
+ ns: NS, ErrorClass: CertificateError, key: "x509", kind: "certificate", what: "the certificate",
233
+ parse: parse, scopes: CERT_SCOPES, defaultScope: "certificate",
234
+ segments: function (cert) { return [{ list: cert.extensions || [], scope: "certificate" }]; },
235
+ });
236
+
237
+ /**
238
+ * @primitive pki.schema.x509.decodeExtensions
239
+ * @signature pki.schema.x509.decodeExtensions(certOrBytes, opts?) -> rows
240
+ * @since 0.8.11
241
+ * @status stable
242
+ * @spec RFC 5280 sec. 4.2
243
+ * @related pki.schema.x509.parse, pki.lint.certificate, pki.inspect.certificate
244
+ *
245
+ * Read a certificate's extensions as decoded records. `parse` hands back each extension's raw
246
+ * `extnValue` octets, because what an extension means is a question the reader asks rather than
247
+ * one the parse answers; this is the verb that asks it. The argument is the object `parse`
248
+ * returned or the bytes `parse` takes, and a parse result is read rather than re-derived.
249
+ *
250
+ * Each row is `{ oid, name, critical, value, scope, index, state, decoded, code, profile }`, with
251
+ * every field always present. `state` is the discriminator: `"decoded"` when the value read as the
252
+ * structure its identifier names, `"unrecognized"` when this toolkit registers no decoder for that
253
+ * OID, and `"undecodable"` when it does and the value did not read. Read `state` rather than
254
+ * testing `decoded`, because a valid `ocspNoCheck` decodes to `null`.
255
+ *
256
+ * `value` carries the raw octets in every arm, byte-identical to the certificate's, for a caller
257
+ * recomputing a hash or comparing an encoding. `code` is the typed error code in the undecodable
258
+ * arm and `null` elsewhere. `profile` reports the criticality the RFC fixes for that OID, as
259
+ * `{ criticalExpected, citation, deviates, violations }`, and is `null` for an extension whose
260
+ * criticality the issuer chooses.
261
+ *
262
+ * Nothing here is a verdict. An unrecognized critical extension is reported rather than refused,
263
+ * whatever `opts.strict` says, because RFC 5280 sec. 4.2's rule about one is the caller's to
264
+ * apply and refusing it would make a certificate carrying a private critical extension
265
+ * unreadable. The two facts that rule needs, `critical` and `state`, are on the row.
266
+ *
267
+ * @opts
268
+ * - `strict` (boolean) -- throw the decoder's own typed error instead of reporting an
269
+ * undecodable value. It does not turn an unrecognized OID into a throw.
270
+ * - `maxBytes` / `maxDepth` / `maxItems` (integer) -- decode caps for this call. Each may only
271
+ * tighten the built-in ceiling, and each bounds the decoding this call performs.
272
+ * @example
273
+ * var pair = await pki.key.generate("Ed25519");
274
+ * var certDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(pair.publicKey),
275
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2027-01-01T00:00:00Z"),
276
+ * extensions: { basicConstraints: { cA: false }, subjectAltName: [{ dNSName: "leaf.example" }] } },
277
+ * { key: await pki.key.export(pair.privateKey) });
278
+ * var rows = pki.schema.x509.decodeExtensions(certDer);
279
+ * rows.filter(function (r) { return r.critical && r.state !== "decoded"; }).length; // -> 0
280
+ * // The filter above is the RFC 5280 sec. 4.2 rule: a relying party rejects a certificate whose
281
+ * // critical extensions it cannot read.
282
+ */
283
+ var decodeExtensions = DOOR.decodeExtensions;
284
+
285
+ /**
286
+ * @primitive pki.schema.x509.decodeExtension
287
+ * @signature pki.schema.x509.decodeExtension(ext, opts?) -> row
288
+ * @since 0.8.11
289
+ * @status stable
290
+ * @spec RFC 5280 sec. 4.2
291
+ * @related pki.schema.x509.decodeExtensions
292
+ *
293
+ * Read one certificate extension record as a decoded row. The argument is one element of a
294
+ * `pki.schema.x509.parse` result's `extensions`, as `{ oid, name, critical, value }`, and the row
295
+ * is the one `decodeExtensions` would have produced for it, with `index` 0.
296
+ *
297
+ * @opts
298
+ * - `strict` (boolean) -- throw the decoder's own typed error instead of reporting an
299
+ * undecodable value.
300
+ * - `maxBytes` / `maxDepth` / `maxItems` (integer) -- decode caps for this call. Each may only
301
+ * tighten the built-in ceiling.
302
+ * @example
303
+ * var pair = await pki.key.generate("Ed25519");
304
+ * var certDer = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(pair.publicKey),
305
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2027-01-01T00:00:00Z"),
306
+ * extensions: { basicConstraints: { cA: false } } },
307
+ * { key: await pki.key.export(pair.privateKey) });
308
+ * var parsed = pki.schema.x509.parse(certDer);
309
+ * pki.schema.x509.decodeExtension(parsed.extensions[0]).state; // -> "decoded"
310
+ */
311
+ var decodeExtension = DOOR.decodeExtension;
312
+
221
313
  module.exports = {
222
314
  parse: parse,
223
315
  pemDecode: pemDecode,
224
316
  pemEncode: pemEncode,
225
317
  matches: matches,
318
+ decodeExtensions: decodeExtensions,
319
+ decodeExtension: decodeExtension,
226
320
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.8.10",
3
+ "version": "0.8.11",
4
4
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
package/sbom.cdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.5",
5
- "serialNumber": "urn:uuid:4cdebd00-c3c0-4ba6-9d9c-daacb902163c",
5
+ "serialNumber": "urn:uuid:41170205-6ef3-4f77-9577-7ba33e5632bc",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-09-23T19:58:00.767Z",
8
+ "timestamp": "2026-09-23T22:37:26.125Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/pki@0.8.10",
22
+ "bom-ref": "@blamejs/pki@0.8.11",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.8.10",
25
+ "version": "0.8.11",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
29
- "purl": "pkg:npm/%40blamejs/pki@0.8.10",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.8.11",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/pki@0.8.10",
57
+ "ref": "@blamejs/pki@0.8.11",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]