@blamejs/pki 0.1.28 → 0.1.29
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 +10 -1
- package/lib/asn1-der.js +12 -2
- package/lib/cbor-det.js +14 -2
- package/lib/ct.js +13 -6
- package/lib/framework-error.js +6 -4
- package/lib/webcrypto.js +23 -4
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,16 @@ 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.
|
|
7
|
+
## v0.1.29 — 2026-07-11
|
|
8
|
+
|
|
9
|
+
A detached-backed BufferSource now fails closed with a typed error at every byte-input boundary.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- pki.webcrypto digest / sign / verify no longer silently process a detached-backed Buffer as EMPTY input (a fail-open where a transferred backing ArrayBuffer left the view zero-length); a detached BufferSource is now rejected with a typed webcrypto/data error, as is getRandomValues.
|
|
14
|
+
- pki.asn1.decode, pki.cbor.decode, and pki.ct.parseSctList reject a detached-backed Buffer or view with a typed error (asn1/not-buffer, cbor/not-buffer, ct/bad-input) instead of a raw TypeError or a misleading truncated-input verdict. The underlying byte-view failure is threaded as the error cause.
|
|
15
|
+
|
|
16
|
+
## v0.1.28 — 2026-07-10
|
|
8
17
|
|
|
9
18
|
Merkle transparency proof verification joins the toolkit as pki.merkle.
|
|
10
19
|
|
package/lib/asn1-der.js
CHANGED
|
@@ -105,8 +105,18 @@ function _className(bits) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function _asBuffer(input, who) {
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
// A Buffer is a Uint8Array, so it routes through the same guarded re-view --
|
|
109
|
+
// NOT a `Buffer.isBuffer` fast-path that returns it as-is. A detached backing
|
|
110
|
+
// ArrayBuffer (a transferred / structuredClone'd view) reads as zero-length;
|
|
111
|
+
// re-viewing through Buffer.from surfaces it as a typed error here rather than
|
|
112
|
+
// a misleading downstream truncated-DER verdict on an empty buffer.
|
|
113
|
+
if (Buffer.isBuffer(input) || input instanceof Uint8Array) {
|
|
114
|
+
try {
|
|
115
|
+
return Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
throw new Asn1Error("asn1/not-buffer", who + ": input is not a usable byte view (detached backing buffer?)", e);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
110
120
|
throw new Asn1Error("asn1/not-buffer", who + ": expected a Buffer / Uint8Array");
|
|
111
121
|
}
|
|
112
122
|
|
package/lib/cbor-det.js
CHANGED
|
@@ -57,8 +57,20 @@ var _utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
|
57
57
|
var _MAX_EPOCH_SECONDS = 8640000000000n;
|
|
58
58
|
|
|
59
59
|
function _asBuffer(input, who) {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
// A Buffer is itself a Uint8Array, so it goes through the same guarded re-view
|
|
61
|
+
// -- NOT a `Buffer.isBuffer` fast-path that would return it as-is. A detached
|
|
62
|
+
// backing ArrayBuffer (a transferred / structuredClone'd view) has had its
|
|
63
|
+
// bytes removed and reads as zero-length; the fast-path would hand the byte
|
|
64
|
+
// walk an empty buffer. Always re-view through Buffer.from so a detached input
|
|
65
|
+
// fails closed here as a typed error rather than a raw TypeError deeper in the
|
|
66
|
+
// walk (or a misleading truncated-input verdict).
|
|
67
|
+
if (Buffer.isBuffer(input) || input instanceof Uint8Array) {
|
|
68
|
+
try {
|
|
69
|
+
return Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
throw new CborError("cbor/not-buffer", who + ": input is not a usable byte view (detached backing buffer?)", e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
62
74
|
throw new CborError("cbor/not-buffer", who + ": expected a Buffer / Uint8Array");
|
|
63
75
|
}
|
|
64
76
|
|
package/lib/ct.js
CHANGED
|
@@ -111,8 +111,18 @@ function _peelInner(extValue) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function _toBuffer(v, field) {
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
// A Buffer is a Uint8Array, so it routes through the same guarded re-view --
|
|
115
|
+
// NOT a `Buffer.isBuffer` fast-path that returns it as-is. A detached backing
|
|
116
|
+
// ArrayBuffer (a transferred / structuredClone'd view) reads as zero-length;
|
|
117
|
+
// re-viewing through Buffer.from surfaces it as a typed error here rather than
|
|
118
|
+
// a misleading downstream bad-DER verdict on an empty buffer.
|
|
119
|
+
if (Buffer.isBuffer(v) || v instanceof Uint8Array) {
|
|
120
|
+
try {
|
|
121
|
+
return Buffer.from(v.buffer, v.byteOffset, v.byteLength);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
throw new CtError("ct/bad-input", field + " is not a usable byte view (detached backing buffer?)", e);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
116
126
|
throw new CtError("ct/bad-input", field + " must be a Buffer or Uint8Array");
|
|
117
127
|
}
|
|
118
128
|
|
|
@@ -199,10 +209,7 @@ function _parseSct(r, sctLen) {
|
|
|
199
209
|
* }
|
|
200
210
|
*/
|
|
201
211
|
function parseSctList(extValue) {
|
|
202
|
-
|
|
203
|
-
throw new CtError("ct/bad-input", "parseSctList expects the SCT-list extension value as a Buffer or Uint8Array");
|
|
204
|
-
}
|
|
205
|
-
var blob = _peelInner(Buffer.isBuffer(extValue) ? extValue : Buffer.from(extValue));
|
|
212
|
+
var blob = _peelInner(_toBuffer(extValue, "the SCT-list extension value"));
|
|
206
213
|
if (blob.length > C.LIMITS.SCT_MAX_BYTES) {
|
|
207
214
|
throw new CtError("ct/too-large", "SCT list " + blob.length + " bytes exceeds the cap " + C.LIMITS.SCT_MAX_BYTES);
|
|
208
215
|
}
|
package/lib/framework-error.js
CHANGED
|
@@ -127,8 +127,9 @@ var ConstantsError = defineClass("ConstantsError");
|
|
|
127
127
|
// Asn1Error -- malformed / non-canonical DER: truncated TLV, indefinite
|
|
128
128
|
// length, non-minimal length or integer, leftover trailing bytes, depth
|
|
129
129
|
// or size cap exceeded. DER is a canonical encoding, so anything the
|
|
130
|
-
// decoder rejects is permanently invalid.
|
|
131
|
-
|
|
130
|
+
// decoder rejects is permanently invalid. withCause threads a raw byte-view
|
|
131
|
+
// failure (a detached backing ArrayBuffer) as the cause rather than discarding it.
|
|
132
|
+
var Asn1Error = defineClass("Asn1Error", { withCause: true });
|
|
132
133
|
|
|
133
134
|
// CborError -- malformed / non-deterministic CBOR: a reserved or indefinite
|
|
134
135
|
// additional-info value, a stray break, a non-minimal ("preferred") argument,
|
|
@@ -136,8 +137,9 @@ var Asn1Error = defineClass("Asn1Error");
|
|
|
136
137
|
// key, a non-minimal / oversized bignum, bad UTF-8, a wrong-typed tag body,
|
|
137
138
|
// leftover trailing bytes, or a depth / size cap exceeded. RFC 8949 core-
|
|
138
139
|
// deterministic encoding (sec. 4.2.1) is canonical, so anything the decoder
|
|
139
|
-
// rejects is permanently invalid.
|
|
140
|
-
|
|
140
|
+
// rejects is permanently invalid. withCause threads a raw byte-view failure
|
|
141
|
+
// (a detached backing ArrayBuffer) as the cause rather than discarding it.
|
|
142
|
+
var CborError = defineClass("CborError", { withCause: true });
|
|
141
143
|
|
|
142
144
|
// OidError -- a malformed object-identifier: fewer than two arcs, a first
|
|
143
145
|
// arc outside 0..2, a second arc >= 40 under arcs 0/1, or a non-minimal
|
package/lib/webcrypto.js
CHANGED
|
@@ -47,9 +47,20 @@ var MAX_RANDOM_BYTES = 65536;
|
|
|
47
47
|
// ---- value helpers ---------------------------------------------------
|
|
48
48
|
|
|
49
49
|
function _toBuf(data, who) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
var isAb = data instanceof ArrayBuffer;
|
|
51
|
+
// A Buffer is itself an ArrayBuffer view, so it is handled by the view arm --
|
|
52
|
+
// NOT a `Buffer.isBuffer` fast-path that would return it as-is. A detached
|
|
53
|
+
// backing ArrayBuffer (a transferred / structuredClone'd Buffer, view, or
|
|
54
|
+
// ArrayBuffer) has had its bytes removed and reads as zero-length; the
|
|
55
|
+
// fast-path would silently hash / sign EMPTY (a fail-OPEN). Always re-view
|
|
56
|
+
// through Buffer.from so a detached input fails closed here as a typed error.
|
|
57
|
+
if (isAb || ArrayBuffer.isView(data)) {
|
|
58
|
+
try {
|
|
59
|
+
return isAb ? Buffer.from(data) : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
throw new WebCryptoError("webcrypto/data", (who || "input") + ": input is not a usable byte source (detached backing buffer?)", e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
53
64
|
throw new WebCryptoError("webcrypto/data", (who || "input") + ": expected BufferSource (ArrayBuffer / TypedArray / Buffer)");
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -718,7 +729,15 @@ Crypto.prototype.getRandomValues = function getRandomValues(typedArray) {
|
|
|
718
729
|
if (typedArray.byteLength > MAX_RANDOM_BYTES) {
|
|
719
730
|
throw new WebCryptoError("webcrypto/data", "getRandomValues: byteLength exceeds " + MAX_RANDOM_BYTES);
|
|
720
731
|
}
|
|
721
|
-
|
|
732
|
+
var view;
|
|
733
|
+
try {
|
|
734
|
+
// A detached backing ArrayBuffer surfaces as a typed error rather than a
|
|
735
|
+
// raw TypeError from Buffer.from over the transferred/cloned view.
|
|
736
|
+
view = Buffer.from(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);
|
|
737
|
+
} catch (e) {
|
|
738
|
+
throw new WebCryptoError("webcrypto/data", "getRandomValues: byte view is not usable (detached backing buffer?)", e);
|
|
739
|
+
}
|
|
740
|
+
nodeCrypto.randomFillSync(view);
|
|
722
741
|
return typedArray;
|
|
723
742
|
};
|
|
724
743
|
|
package/package.json
CHANGED
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:
|
|
5
|
+
"serialNumber": "urn:uuid:26032baa-c793-470f-863c-569dc97c186e",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-07-
|
|
8
|
+
"timestamp": "2026-07-11T05:58:59.165Z",
|
|
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.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.1.29",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.1.
|
|
25
|
+
"version": "0.1.29",
|
|
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.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.1.29",
|
|
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.
|
|
57
|
+
"ref": "@blamejs/pki@0.1.29",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|