@blamejs/pki 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to `@blamejs/pki` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
5
5
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## v0.1.4 — 2026-07-04
8
+
9
+ The ASN.1 codec's universal-type metadata moves to a single descriptor registry.
10
+
11
+ ### Changed
12
+
13
+ - The ASN.1/DER codec's universal-type metadata is now defined once in a descriptor registry (each entry carries the type's tag and its required DER encoding form). pki.asn1.TAGS, the primitive-only set (a type DER requires primitive, encoded constructed, is rejected) and the constructed-only set (a SEQUENCE/SET encoded primitive is rejected) are all derived from it, so registering a universal type is a single data entry. This is an internal refactor: the public surface and every decode/encode result are unchanged, and it lays the groundwork for schema-driven format parsers.
14
+
15
+ ## v0.1.3 — 2026-07-04
16
+
17
+ WebCrypto EC key import validates the curve against the key material.
18
+
19
+ ### Security
20
+
21
+ - pki.webcrypto.subtle.importKey now derives an imported EC key's named curve from the key material and enforces it across the spki, pkcs8 and jwk formats. Previously it trusted the caller-supplied namedCurve without checking it against the key, so a key on an unsupported curve (for example secp256k1) imported as an approved curve, and a key on one curve could be labelled as another — an algorithm-confusion vector in which the CryptoKey's algorithm disagreed with its key material. A curve the framework does not support is now rejected (NotSupportedError) and a namedCurve that does not match the key is rejected (DataError); generateKey already enforced this, and import now matches it. The raw-key format was already validated against its declared curve and is unchanged.
22
+
7
23
  ## v0.1.2 — 2026-07-04
8
24
 
9
25
  Fail-closed hardening across the DER codec, WebCrypto engine, and X.509 parser.
package/lib/asn1-der.js CHANGED
@@ -38,46 +38,56 @@ var OidError = frameworkError.OidError;
38
38
 
39
39
  // ---- Tag constants (universal class) --------------------------------
40
40
 
41
- var TAGS = {
42
- BOOLEAN: 0x01,
43
- INTEGER: 0x02,
44
- BIT_STRING: 0x03,
45
- OCTET_STRING: 0x04,
46
- NULL: 0x05,
47
- OBJECT_IDENTIFIER: 0x06,
48
- ENUMERATED: 0x0a,
49
- UTF8_STRING: 0x0c,
50
- SEQUENCE: 0x10,
51
- SET: 0x11,
52
- PRINTABLE_STRING: 0x13,
53
- TELETEX_STRING: 0x14,
54
- IA5_STRING: 0x16,
55
- UTC_TIME: 0x17,
56
- GENERALIZED_TIME: 0x18,
57
- VISIBLE_STRING: 0x1a,
58
- UNIVERSAL_STRING: 0x1c,
59
- BMP_STRING: 0x1e,
41
+ // L1 the ASN.1 universal-type descriptor registry: the single source of
42
+ // truth for the codec's type metadata. Each entry is DECLARATIVE DATA —
43
+ // { tag, form } where form is the DER encoding form ("primitive" or
44
+ // "constructed", X.690 §8/§10.2). TAGS and the two structural type-sets below
45
+ // are all DERIVED from it, so registering a universal type is one descriptor
46
+ // entry and the decoder's form checks (below) cover it automatically.
47
+ var UNIVERSAL_TYPES = {
48
+ BOOLEAN: { tag: 0x01, form: "primitive" },
49
+ INTEGER: { tag: 0x02, form: "primitive" },
50
+ BIT_STRING: { tag: 0x03, form: "primitive" },
51
+ OCTET_STRING: { tag: 0x04, form: "primitive" },
52
+ NULL: { tag: 0x05, form: "primitive" },
53
+ OBJECT_IDENTIFIER: { tag: 0x06, form: "primitive" },
54
+ ENUMERATED: { tag: 0x0a, form: "primitive" },
55
+ UTF8_STRING: { tag: 0x0c, form: "primitive" },
56
+ SEQUENCE: { tag: 0x10, form: "constructed" },
57
+ SET: { tag: 0x11, form: "constructed" },
58
+ PRINTABLE_STRING: { tag: 0x13, form: "primitive" },
59
+ TELETEX_STRING: { tag: 0x14, form: "primitive" },
60
+ IA5_STRING: { tag: 0x16, form: "primitive" },
61
+ UTC_TIME: { tag: 0x17, form: "primitive" },
62
+ GENERALIZED_TIME: { tag: 0x18, form: "primitive" },
63
+ VISIBLE_STRING: { tag: 0x1a, form: "primitive" },
64
+ UNIVERSAL_STRING: { tag: 0x1c, form: "primitive" },
65
+ BMP_STRING: { tag: 0x1e, form: "primitive" },
60
66
  };
61
67
 
68
+ // TAGS { name: tag } — derived from the registry; the public constant consumers
69
+ // read (pki.asn1.TAGS.INTEGER, ...).
70
+ var TAGS = {};
71
+ Object.keys(UNIVERSAL_TYPES).forEach(function (k) { TAGS[k] = UNIVERSAL_TYPES[k].tag; });
72
+
62
73
  var CLASS_UNIVERSAL = 0x00;
63
74
  var CLASS_APPLICATION = 0x40;
64
75
  var CLASS_CONTEXT = 0x80;
65
76
  var CLASS_PRIVATE = 0xc0;
66
77
  var CONSTRUCTED_BIT = 0x20;
67
78
 
68
- // X.690 §8.x / §10.2 (DER) every universal type the codec handles other than
69
- // SEQUENCE and SET is a primitive-only type: it MUST be encoded primitive, and
70
- // a constructed encoding (BOOLEAN, INTEGER, OID, the restricted strings,
71
- // UTC/GeneralizedTime, and DER-strict BIT STRING / OCTET STRING) is not
72
- // valid DER. Derived from TAGS so a newly-registered primitive type is covered
73
- // automatically. This is the mirror of the primitive-SEQUENCE/SET rejection.
74
- var PRIMITIVE_ONLY_UNIVERSAL_TAGS = (function () {
75
- var s = Object.create(null);
76
- Object.keys(TAGS).forEach(function (k) {
77
- if (k !== "SEQUENCE" && k !== "SET") s[TAGS[k]] = true;
78
- });
79
- return s;
80
- })();
79
+ // X.690 §8.9.1 / §8.11.1 / §10.2 (DER), the mirror rules a constructed-only
80
+ // universal type (SEQUENCE/SET) MUST be encoded constructed, and a
81
+ // primitive-only type (every other universal type the codec handles) MUST be
82
+ // encoded primitive. Both sets are DERIVED from the registry above and keyed by
83
+ // tag for an O(1) decode-time form check; a new descriptor is covered with no
84
+ // code change here.
85
+ var PRIMITIVE_ONLY_UNIVERSAL_TAGS = Object.create(null);
86
+ var CONSTRUCTED_ONLY_UNIVERSAL_TAGS = Object.create(null);
87
+ Object.keys(UNIVERSAL_TYPES).forEach(function (k) {
88
+ var d = UNIVERSAL_TYPES[k];
89
+ (d.form === "constructed" ? CONSTRUCTED_ONLY_UNIVERSAL_TAGS : PRIMITIVE_ONLY_UNIVERSAL_TAGS)[d.tag] = true;
90
+ });
81
91
 
82
92
  function _className(bits) {
83
93
  switch (bits) {
@@ -172,11 +182,12 @@ function _decodeTLV(buf, start, limit, depth, maxDepth) {
172
182
  }
173
183
  }
174
184
 
175
- // X.690 8.9.1 / 8.11.1 — a universal SEQUENCE or SET is always constructed;
176
- // a primitive-tagged 0x10 / 0x11 is not valid DER (and would decode to a
177
- // leaf that constructed-structure consumers dereference as a parent).
178
- if (tagClassBits === CLASS_UNIVERSAL && (tagNumber === TAGS.SEQUENCE || tagNumber === TAGS.SET) && !constructed) {
179
- throw new Asn1Error("asn1/bad-tlv", "SEQUENCE/SET must be constructed");
185
+ // X.690 8.9.1 / 8.11.1 — a universal constructed-only type (SEQUENCE / SET)
186
+ // is always constructed; a primitive-tagged one is not valid DER (and would
187
+ // decode to a leaf that constructed-structure consumers dereference as a
188
+ // parent). Driven by the registry-derived set, not a hardcoded tag list.
189
+ if (tagClassBits === CLASS_UNIVERSAL && CONSTRUCTED_ONLY_UNIVERSAL_TAGS[tagNumber] && !constructed) {
190
+ throw new Asn1Error("asn1/bad-tlv", "a universal constructed-only type (SEQUENCE/SET) must be constructed");
180
191
  }
181
192
 
182
193
  // X.690 §8.x / §10.2 — the mirror rule: a universal primitive-only type
package/lib/webcrypto.js CHANGED
@@ -490,7 +490,23 @@ function _importRawPublic(name, alg, raw, extractable, usages) {
490
490
  }
491
491
 
492
492
  function _algFromImport(name, alg, keyObject) {
493
- if (name === "ECDSA" || name === "ECDH") return { name: name, namedCurve: alg.namedCurve || _curveFromKey(keyObject) };
493
+ if (name === "ECDSA" || name === "ECDH") {
494
+ // W3C WebCrypto EC import — the curve is a property of the KEY, not the
495
+ // caller's claim. Derive it from the imported key material; reject an
496
+ // unsupported curve (NotSupportedError, matching generateKey) and reject a
497
+ // requested namedCurve that disagrees with the key (DataError). Trusting
498
+ // alg.namedCurve would mislabel the CryptoKey (algorithm confusion) and let
499
+ // a non-approved curve import as an approved one.
500
+ var actualCurve = _curveFromKey(keyObject);
501
+ if (!actualCurve) {
502
+ throw new WebCryptoError("webcrypto/not-supported", name + ": imported key uses an unsupported EC curve");
503
+ }
504
+ if (alg.namedCurve && alg.namedCurve !== actualCurve) {
505
+ throw new WebCryptoError("webcrypto/data", name + ": importKey namedCurve " + JSON.stringify(alg.namedCurve) +
506
+ " does not match the imported key's curve " + JSON.stringify(actualCurve));
507
+ }
508
+ return { name: name, namedCurve: actualCurve };
509
+ }
494
510
  if (name === "RSASSA-PKCS1-V1_5" || name === "RSA-PSS" || name === "RSA-OAEP") return { name: name, hash: _hashObj(alg.hash) };
495
511
  if (name === "ED25519") return { name: "Ed25519" };
496
512
  if (name === "ED448") return { name: "Ed448" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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:61f60234-7f86-4857-9542-58817209d84d",
5
+ "serialNumber": "urn:uuid:d25ccb6d-af2d-4b1f-b300-f756dc788762",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-07-04T19:11:06.976Z",
8
+ "timestamp": "2026-07-04T19:57:57.512Z",
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.2",
22
+ "bom-ref": "@blamejs/pki@0.1.4",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.1.2",
25
+ "version": "0.1.4",
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.2",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.1.4",
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.2",
57
+ "ref": "@blamejs/pki@0.1.4",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]