@blamejs/pki 0.2.17 → 0.2.19

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.
@@ -71,4 +71,51 @@ function rawToEcdsaDer(raw, coordLen) {
71
71
  return asn1.build.sequence([asn1.build.integer(r), asn1.build.integer(s)]);
72
72
  }
73
73
 
74
- module.exports = { ecdsaSigToRaw: ecdsaSigToRaw, rawToEcdsaDer: rawToEcdsaDer };
74
+ // The curve group orders n -- a valid ECDSA signature has r, s in [1, n-1].
75
+ var CURVE_FIELD_BYTES = { "P-256": 32, "P-384": 48, "P-521": 66 };
76
+ var CURVE_ORDER = {
77
+ "P-256": BigInt("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"),
78
+ "P-384": BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"),
79
+ "P-521": BigInt("0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"),
80
+ };
81
+
82
+ // ecdsaDerToP1363(der, curve, E, code) -> the DER ECDSA-Sig-Value converted to raw r||s (P1363),
83
+ // each coordinate left-padded to the curve field width, rejecting r or s outside [1, n-1] against
84
+ // the curve ORDER (CVE-2022-21449 "Psychic Signatures" -- the r/s = 0 case AND the >= n upper
85
+ // bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the ORDER-AWARE gate a
86
+ // verifier that knows the curve order MUST use; it is STRICTER than ecdsaSigToRaw above, which
87
+ // bounds r/s only by the field SIZE (>= 1, <= coordLen bytes) and does not know the order. A
88
+ // signature whose r or s is >= n is rejected here but passes ecdsaSigToRaw -- do not conflate them.
89
+ // @enforced-by behavioral -- the RED conformance vectors (r/s = 0, r/s >= n, non-minimal, over-size)
90
+ // and the composite-signature + path-validation KATs are the guard.
91
+ function ecdsaDerToP1363(der, curve, E, code) {
92
+ var width = CURVE_FIELD_BYTES[curve];
93
+ var order = CURVE_ORDER[curve];
94
+ if (!width || !order) throw new E(code, "unsupported ECDSA curve " + curve);
95
+ var n;
96
+ try { n = asn1.decode(der); }
97
+ catch (e) { throw new E(code, "ECDSA signature is not a DER SEQUENCE(r,s)", e); }
98
+ if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.SEQUENCE || !n.children || n.children.length !== 2) {
99
+ throw new E(code, "ECDSA signature must be a SEQUENCE of exactly two INTEGERs");
100
+ }
101
+ var r = asn1.read.integer(n.children[0]);
102
+ var s = asn1.read.integer(n.children[1]);
103
+ if (r < 1n || s < 1n || r >= order || s >= order) {
104
+ throw new E(code, "ECDSA signature component out of range [1, n-1] (CVE-2022-21449)");
105
+ }
106
+ function pad(v) {
107
+ var hex = v.toString(16);
108
+ if (hex.length % 2) hex = "0" + hex;
109
+ var buf = Buffer.from(hex, "hex");
110
+ // Coverage residual -- unreachable: a component needing more than `width` bytes is >=
111
+ // 2^(8*width) > n (the curve order n < 2^(8*width) for P-256/384/521), so it is already
112
+ // rejected by the r/s >= order check above; this width guard is a defense-in-depth backstop.
113
+ if (buf.length > width) throw new E(code, "ECDSA signature component wider than the curve field");
114
+ var out = Buffer.alloc(width);
115
+ buf.copy(out, width - buf.length);
116
+ return out;
117
+ }
118
+ return Buffer.concat([pad(r), pad(s)]);
119
+ }
120
+
121
+ module.exports = { ecdsaSigToRaw: ecdsaSigToRaw, rawToEcdsaDer: rawToEcdsaDer, ecdsaDerToP1363: ecdsaDerToP1363 };
package/lib/webcrypto.js CHANGED
@@ -627,7 +627,7 @@ SubtleCrypto.prototype.unwrapKey = async function unwrapKey(format, wrappedKey,
627
627
  // notice. The shared JSON guard parses strictly (bounded, fatal UTF-8, and a
628
628
  // smuggled duplicate member rejected rather than resolved last-wins), and its
629
629
  // failure surfaces as the module's typed webcrypto/data (W3C: DataError).
630
- keyData = guard.json.parse(bytes, WebCryptoError, {
630
+ keyData = guard.json.parse(bytes, _wcErr, {
631
631
  maxBytes: constants.LIMITS.JSON_MAX_BYTES, maxDepth: constants.LIMITS.JSON_MAX_DEPTH,
632
632
  badJson: "webcrypto/data", tooDeep: "webcrypto/data", duplicateMember: "webcrypto/data",
633
633
  tooLarge: "webcrypto/data", badInput: "webcrypto/data", label: "the unwrapped JWK",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
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:e4cc4e90-fbb7-4869-9baf-d6ce16b67d7f",
5
+ "serialNumber": "urn:uuid:d6e0649c-f4db-4241-8784-6f3c9d3d663f",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-07-14T04:28:56.898Z",
8
+ "timestamp": "2026-07-14T16:14:40.970Z",
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.2.17",
22
+ "bom-ref": "@blamejs/pki@0.2.19",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.2.17",
25
+ "version": "0.2.19",
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.2.17",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.2.19",
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.2.17",
57
+ "ref": "@blamejs/pki@0.2.19",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]