hd-wallet-wasm 2.1.1 → 2.1.2

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.
@@ -45,6 +45,73 @@ export const VALIDATION_ERROR = Object.freeze({
45
45
  INVALID_SIGNATURE: 402,
46
46
  });
47
47
 
48
+ // The length a typed array really has, read from its internal slot through
49
+ // %TypedArray%.prototype's getter: a subclass getter or an own `length`
50
+ // property cannot change it. Captured at load.
51
+ const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
52
+ const TYPED_ARRAY_LENGTH = Object.getOwnPropertyDescriptor(TYPED_ARRAY_PROTOTYPE, 'length').get;
53
+ const TYPED_ARRAY_BUFFER = Object.getOwnPropertyDescriptor(TYPED_ARRAY_PROTOTYPE, 'buffer').get;
54
+ const BUFFER_RESIZABLE = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'resizable')?.get;
55
+ const BUFFER_DETACHED = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'detached')?.get;
56
+ const SHARED_BUFFER_GROWABLE = typeof SharedArrayBuffer === 'function'
57
+ ? Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'growable')?.get
58
+ : undefined;
59
+
60
+ /** The internal length of a typed array, or undefined for anything else. */
61
+ function intrinsicLength(value) {
62
+ try {
63
+ return TYPED_ARRAY_LENGTH.call(value);
64
+ } catch {
65
+ return undefined;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Whether reading `value.length` reaches %TypedArray%.prototype's getter: no
71
+ * own `length` property and none on a subclass prototype, so no later read
72
+ * can answer differently from the check.
73
+ */
74
+ function hasIntrinsicLengthProperty(value) {
75
+ for (let object = value; object !== null && object !== TYPED_ARRAY_PROTOTYPE;
76
+ object = Object.getPrototypeOf(object)) {
77
+ if (Object.prototype.hasOwnProperty.call(object, 'length')) return false;
78
+ }
79
+ return true;
80
+ }
81
+
82
+ /** Whether a typed array's buffer is detached (transferred). */
83
+ function isDetached(value) {
84
+ const buffer = TYPED_ARRAY_BUFFER.call(value);
85
+ if (BUFFER_DETACHED) {
86
+ try {
87
+ return BUFFER_DETACHED.call(buffer);
88
+ } catch {
89
+ return false; // A SharedArrayBuffer cannot be detached.
90
+ }
91
+ }
92
+ if (TYPED_ARRAY_LENGTH.call(value) !== 0) return false;
93
+ try {
94
+ new Uint8Array(value);
95
+ return false;
96
+ } catch {
97
+ return true;
98
+ }
99
+ }
100
+
101
+ /** Whether a typed array's buffer can never change size (not resizable or growable). */
102
+ function hasFixedSize(value) {
103
+ const buffer = TYPED_ARRAY_BUFFER.call(value);
104
+ for (const getter of [BUFFER_RESIZABLE, SHARED_BUFFER_GROWABLE]) {
105
+ if (!getter) continue;
106
+ try {
107
+ if (getter.call(buffer)) return false;
108
+ } catch {
109
+ // Not that kind of buffer.
110
+ }
111
+ }
112
+ return true;
113
+ }
114
+
48
115
  const CURVE_NAMES = Object.freeze(['secp256k1', 'ed25519', 'P-256', 'P-384', 'X25519']);
49
116
  const PRIVATE_KEY_LENGTH = Object.freeze([32, 32, 32, 48, 32]);
50
117
  const PUBLIC_KEY_LENGTHS = Object.freeze([
@@ -78,14 +145,39 @@ export function createInputValidation(makeError) {
78
145
  return typeof lengths === 'number' ? `${lengths}` : lengths.join(' or ');
79
146
  }
80
147
 
81
- /** True when `value` is a Uint8Array (Node Buffers included). */
148
+ /**
149
+ * True when `value` is a Uint8Array (Node Buffers included) whose `length`
150
+ * is its own (no subclass getter or own property can answer, then or later)
151
+ * over a buffer that is attached and cannot be resized. A DataView, an
152
+ * ArrayBuffer or another typed array is not binary data here.
153
+ */
82
154
  function isBytes(value) {
83
- return value instanceof Uint8Array;
155
+ if (intrinsicLength(value) === undefined || !(value instanceof Uint8Array)) return false;
156
+ return hasIntrinsicLengthProperty(value) && !isDetached(value) && hasFixedSize(value);
157
+ }
158
+
159
+ /**
160
+ * Binary data checked as by bytes(), returned as a copy made once from the
161
+ * array's own bytes: later reads of the caller's array cannot change it.
162
+ */
163
+ function snapshot(value, name = 'data', code = INVALID_ARGUMENT) {
164
+ bytes(value, name, code);
165
+ return new Uint8Array(value);
166
+ }
167
+
168
+ /**
169
+ * The byte length of binary data from the array itself, never from its
170
+ * `length` property. Throws for anything isBytes refuses.
171
+ */
172
+ function byteLength(value, name = 'data', code = INVALID_ARGUMENT) {
173
+ bytes(value, name, code);
174
+ return intrinsicLength(value);
84
175
  }
85
176
 
86
177
  /** True when the length of `bytes` is `lengths` (a number) or one of `lengths`. */
87
178
  function hasLength(bytes, lengths) {
88
- return typeof lengths === 'number' ? bytes.length === lengths : lengths.includes(bytes.length);
179
+ const length = intrinsicLength(bytes);
180
+ return typeof lengths === 'number' ? length === lengths : lengths.includes(length);
89
181
  }
90
182
 
91
183
  /** Require binary data (a Uint8Array) of any length. */
@@ -104,7 +196,8 @@ export function createInputValidation(makeError) {
104
196
  /** Require a Uint8Array of min..max bytes. */
105
197
  function bytesInRange(value, min, max, name = 'data', code = INVALID_ARGUMENT) {
106
198
  bytes(value, name, code);
107
- if (value.length < min || value.length > max) {
199
+ const length = intrinsicLength(value);
200
+ if (length < min || length > max) {
108
201
  fail(code, `${name} must be ${min}-${max} bytes`);
109
202
  }
110
203
  return value;
@@ -200,13 +293,17 @@ export function createInputValidation(makeError) {
200
293
  if (typeof check !== 'function') {
201
294
  fail(NOT_SUPPORTED, `Function ${exportName} is not available in this build`);
202
295
  }
203
- const pointer = wasm._hd_alloc(Math.max(1, value.length));
296
+ const copy = new Uint8Array(value);
297
+ const pointer = wasm._hd_alloc(Math.max(1, copy.length));
204
298
  if (!pointer) fail(OUT_OF_MEMORY, 'Out of memory');
205
299
  try {
206
- wasm.HEAPU8.set(value, pointer);
207
- return check(pointer, value.length, curveId) === 0;
300
+ wasm.HEAPU8.set(copy, pointer);
301
+ return check(pointer, copy.length, curveId) === 0;
208
302
  } finally {
209
- if (secret) wasm._hd_secure_wipe(pointer, Math.max(1, value.length));
303
+ if (secret) {
304
+ wasm._hd_secure_wipe(pointer, Math.max(1, copy.length));
305
+ copy.fill(0);
306
+ }
210
307
  wasm._hd_dealloc(pointer);
211
308
  }
212
309
  }
@@ -248,6 +345,8 @@ export function createInputValidation(makeError) {
248
345
 
249
346
  return Object.freeze({
250
347
  isBytes,
348
+ byteLength,
349
+ snapshot,
251
350
  hasLength,
252
351
  bytes,
253
352
  bytesOfLength,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-wallet-wasm",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Comprehensive HD Wallet implementation in WebAssembly - BIP-32/39/44, multi-curve, multi-chain support",
5
5
  "type": "module",
6
6
  "main": "./dist/runtime/index.mjs",
@@ -92,5 +92,5 @@
92
92
  "devDependencies": {
93
93
  "@types/node": "^20.0.0"
94
94
  },
95
- "gitHead": "dc864f30a8d277735d73028a38cb4f30e54709a6"
95
+ "gitHead": "e862d42fcf505cdbc7e928d676bc1a180a167989"
96
96
  }