@blamejs/pki 0.1.16 → 0.1.18

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.
@@ -77,8 +77,17 @@ function _isPemArmor(buf) {
77
77
 
78
78
  // Decode the DER root, wrapping a codec fault in the caller's <prefix>/bad-der.
79
79
  function decodeRoot(der, opts) {
80
- try { return asn1.decode(der); }
81
- catch (e) { throw new opts.ErrorClass(opts.prefix + "/bad-der", (opts.what || "input") + " DER did not decode: " + ((e && e.message) || String(e)), e); }
80
+ // A format whose wire encoding is normatively BER (RFC 7292 PKCS#12) opts
81
+ // in to the codec's ber mode for the WHOLE decode. BER-vs-DER divergence is
82
+ // not always a decode-time throw — a definite-length constructed IMPLICIT
83
+ // OCTET STRING decodes strictly and only diverges at the typed reader — so
84
+ // a strict-first-retry-on-throw boundary would miss legal BER shapes. A
85
+ // strict-DER input decodes identically under the ber mode, every other
86
+ // strictness verdict included.
87
+ try { return asn1.decode(der, opts.ber ? { ber: true } : undefined); }
88
+ catch (e) {
89
+ throw new opts.ErrorClass(opts.prefix + "/bad-der", (opts.what || "input") + " DER did not decode: " + ((e && e.message) || String(e)), e);
90
+ }
82
91
  }
83
92
 
84
93
  // The shared parse entry: coerce -> decode -> walk the top schema. A format's
@@ -166,6 +175,12 @@ function attrValueToString(ns) {
166
175
  }
167
176
  return "#" + node.bytes.toString("hex");
168
177
  }
178
+ }, function (value) {
179
+ // encode: a "#hex" escape (RFC 4514 §2.4) round-trips its raw DER verbatim; any
180
+ // other string encodes as UTF8String (the decode does not preserve the original
181
+ // string type, so this is the canonical re-encoding, not a byte-exact one).
182
+ if (typeof value === "string" && value.charAt(0) === "#") return Buffer.from(value.slice(1), "hex");
183
+ return asn1.build.utf8(value);
169
184
  });
170
185
  }
171
186
 
@@ -193,25 +208,34 @@ function relativeDistinguishedName(ns) {
193
208
  // RelativeDistinguishedName ::= SET SIZE (1..MAX) — an empty SET {} is malformed.
194
209
  return schema.setOf(attributeTypeAndValue(ns), { assert: "set", min: 1, code: ns.prefix + "/bad-rdn", what: "RelativeDistinguishedName" });
195
210
  }
196
- function name(ns) {
197
- return schema.seqOf(relativeDistinguishedName(ns), {
198
- assert: "sequence", code: ns.prefix + "/bad-name", what: "Name",
199
- build: function (m) {
200
- var rdns = [], parts = [];
201
- m.items.forEach(function (rdnItem) {
202
- var atvs = [], atvParts = [];
203
- rdnItem.value.items.forEach(function (atvItem) {
204
- var a = atvItem.value.result; // atvItem.value = the atv seq-match; .result = its build result
205
- atvs.push(a);
206
- var label = (a.name && DN_SHORT[a.name]) || a.name || a.type;
207
- atvParts.push(label + "=" + _escapeDnValue(a.value));
208
- });
209
- rdns.push(atvs);
210
- parts.push(atvParts.join("+"));
211
+ // opts.implicitTag (optional): read the Name as a [tag] IMPLICIT RDNSequence — the
212
+ // context tag REPLACES the universal SEQUENCE tag, so the node is a context-class
213
+ // constructed [tag] whose children ARE the RDN SETs (the CRMF CertTemplate issuer
214
+ // [3] / subject [5] IMPLICIT arm, RFC 4211 §5). With no opts the shape is a bare
215
+ // universal SEQUENCE OF, byte-identical to every existing caller.
216
+ function name(ns, opts) {
217
+ opts = opts || {};
218
+ function nameBuild(m) {
219
+ var rdns = [], parts = [];
220
+ m.items.forEach(function (rdnItem) {
221
+ var atvs = [], atvParts = [];
222
+ rdnItem.value.items.forEach(function (atvItem) {
223
+ var a = atvItem.value.result; // atvItem.value = the atv seq-match; .result = its build result
224
+ atvs.push(a);
225
+ var label = (a.name && DN_SHORT[a.name]) || a.name || a.type;
226
+ atvParts.push(label + "=" + _escapeDnValue(a.value));
211
227
  });
212
- return { rdns: rdns, dn: parts.join(", ") };
213
- },
214
- });
228
+ rdns.push(atvs);
229
+ parts.push(atvParts.join("+"));
230
+ });
231
+ return { rdns: rdns, dn: parts.join(", ") };
232
+ }
233
+ if (opts.implicitTag != null) {
234
+ return schema.implicitSeqOf(opts.implicitTag, relativeDistinguishedName(ns), {
235
+ code: ns.prefix + "/bad-name", what: "Name", build: nameBuild });
236
+ }
237
+ return schema.seqOf(relativeDistinguishedName(ns), {
238
+ assert: "sequence", code: ns.prefix + "/bad-name", what: "Name", build: nameBuild });
215
239
  }
216
240
 
217
241
  // GeneralName ::= CHOICE (RFC 5280 §4.2.1.6). A validate-and-surface-raw leaf for a
@@ -623,12 +647,20 @@ function extension(ns) {
623
647
  return { oid: extnID, name: ns.oid.name(extnID) || null, critical: critical, value: asn1.read.octetString(valueNode) };
624
648
  });
625
649
  }
626
- function extensions(ns) {
627
- return schema.seqOf(extension(ns), {
628
- assert: "sequence", min: 1, code: ns.prefix + "/bad-extensions", what: "Extensions",
629
- unique: function (item) { return item.value.oid; }, dupCode: ns.prefix + "/duplicate-extension",
650
+ // opts.implicitTag (optional): read Extensions as a [tag] IMPLICIT SEQUENCE OF
651
+ // Extension — the context tag REPLACES the universal SEQUENCE tag, for the CRMF
652
+ // CertTemplate extensions [9] (RFC 4211 §5). With no opts the shape is a bare
653
+ // universal SEQUENCE OF, byte-identical to every existing caller.
654
+ function extensions(ns, opts) {
655
+ opts = opts || {};
656
+ var EXT = extension(ns);
657
+ var extOpts = {
658
+ min: 1, code: ns.prefix + "/bad-extensions", what: "Extensions",
659
+ unique: function (it) { return it.value.oid; }, dupCode: ns.prefix + "/duplicate-extension",
630
660
  build: function (m) { return m.items.map(function (it) { return it.value; }); },
631
- });
661
+ };
662
+ if (opts.implicitTag != null) return schema.implicitSeqOf(opts.implicitTag, EXT, extOpts);
663
+ return schema.seqOf(EXT, Object.assign({ assert: "sequence" }, extOpts));
632
664
  }
633
665
 
634
666
  // SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
@@ -636,17 +668,26 @@ function extensions(ns) {
636
668
  // universal SEQUENCE — a context-tagged or SET-tagged constructed node carrying
637
669
  // two well-formed children is NOT a SubjectPublicKeyInfo. Shared by the
638
670
  // certificate and CSR parsers.
639
- function spki(ns) {
671
+ // opts.implicitTag (optional): read the SubjectPublicKeyInfo as a [tag] IMPLICIT
672
+ // SEQUENCE (a context-class constructed node whose children are algorithm +
673
+ // subjectPublicKey), for the CRMF CertTemplate publicKey [6] (RFC 4211 §5). With no
674
+ // opts the shape is a universal SEQUENCE, byte-identical to every existing caller.
675
+ function spki(ns, opts) {
676
+ opts = opts || {};
640
677
  return schema.seq([
641
678
  schema.field("algorithm", algorithmIdentifier(ns)),
642
679
  schema.field("subjectPublicKey", schema.bitString()),
643
680
  ], {
644
- assert: "sequence", arity: { exact: 2 }, code: ns.prefix + "/bad-spki", what: "SubjectPublicKeyInfo",
681
+ assert: opts.implicitTag != null ? "implicit" : "sequence", implicitTag: opts.implicitTag,
682
+ arity: { exact: 2 }, code: ns.prefix + "/bad-spki", what: "SubjectPublicKeyInfo",
645
683
  build: function (m) {
646
684
  return {
647
685
  algorithm: m.fields.algorithm.value.result,
648
686
  publicKey: { unusedBits: m.fields.subjectPublicKey.value.unusedBits, bytes: m.fields.subjectPublicKey.value.bytes },
649
- bytes: m.node.bytes,
687
+ // `bytes` is the importable SubjectPublicKeyInfo DER. In IMPLICIT [tag]
688
+ // mode the wire node leads with the context tag, so recover the SEQUENCE
689
+ // encoding a consumer imports / hashes rather than the [tag] wire form.
690
+ bytes: opts.implicitTag != null ? asn1.sequenceTlv(m.node) : m.node.bytes,
650
691
  };
651
692
  },
652
693
  });
@@ -660,7 +701,8 @@ function spki(ns) {
660
701
  function attribute(ns) {
661
702
  return schema.seq([
662
703
  schema.field("type", schema.oidLeaf()),
663
- schema.field("values", schema.setOf(schema.any(), { assert: "set", min: 1, code: ns.prefix + "/bad-attribute-values", what: "attribute values" })),
704
+ schema.field("values", schema.setOf(schema.any(), { assert: "set", min: 1, max: constants.LIMITS.ATTRIBUTE_MAX_VALUES,
705
+ code: ns.prefix + "/bad-attribute-values", what: "attribute values" })),
664
706
  ], {
665
707
  assert: "sequence", arity: { exact: 2 }, code: ns.prefix + "/bad-attribute", what: "Attribute",
666
708
  build: function (m, ctx) {
package/lib/schema-tsp.js CHANGED
@@ -359,7 +359,7 @@ function parseToken(input) {
359
359
  throw new TspError("tsp/multi-signer", "a TimeStampToken must contain exactly one (TSA) signerInfo (RFC 3161 §2.4.2)");
360
360
  }
361
361
  var tstInfo;
362
- try { tstInfo = schema.walk(TST_INFO, asn1.decode(encap.eContent), NS); }
362
+ try { tstInfo = schema.embeddedDer(TST_INFO, encap.eContent, NS, { code: "tsp/bad-der", what: "the encapsulated TSTInfo" }); }
363
363
  catch (e) {
364
364
  if (e instanceof TspError) throw e;
365
365
  throw new TspError("tsp/bad-der", "the encapsulated TSTInfo did not decode: " + ((e && e.message) || String(e)), e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
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:69692b51-d096-4124-9a0c-886b9a5ad78e",
5
+ "serialNumber": "urn:uuid:a14a33cf-8c68-40d3-92b0-6f17ef343543",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-07-06T19:48:47.311Z",
8
+ "timestamp": "2026-07-09T03:55:56.855Z",
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.1.16",
22
+ "bom-ref": "@blamejs/pki@0.1.18",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.1.16",
25
+ "version": "0.1.18",
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.1.16",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.1.18",
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.1.16",
57
+ "ref": "@blamejs/pki@0.1.18",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]