hd-wallet-wasm 2.1.0 → 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.
- package/README.md +1 -1
- package/dist/hd-wallet-wasi.wasm +0 -0
- package/dist/hd-wallet.js +1 -1
- package/dist/runtime/aligned.mjs +198 -79
- package/dist/runtime/epm-attestation.mjs +19 -4
- package/dist/runtime/index.d.ts +31 -13
- package/dist/runtime/index.mjs +1037 -517
- package/dist/runtime/input-validation.mjs +368 -0
- package/package.json +2 -2
package/dist/runtime/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Transaction building and signing
|
|
10
10
|
*
|
|
11
11
|
* @module hd-wallet-wasm
|
|
12
|
-
* @version 2.1.
|
|
12
|
+
* @version 2.1.2
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Import aligned API for batch operations
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from './sdn-plugin.mjs';
|
|
21
21
|
import { HD_WALLET_SDN_PLUGIN_MANIFEST } from './sdn-plugin-manifest-source.mjs';
|
|
22
22
|
import { createSdnTypedCapabilities } from './sdn-typed.mjs';
|
|
23
|
+
import { createInputValidation } from './input-validation.mjs';
|
|
23
24
|
|
|
24
25
|
const walletOriginCapabilitiesByModule = new WeakMap();
|
|
25
26
|
|
|
@@ -323,16 +324,77 @@ class HDWalletError extends Error {
|
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
/**
|
|
326
|
-
* Check
|
|
327
|
+
* Check a status-returning export: 0 is success, any other value is an error.
|
|
327
328
|
* @param {number} result - Result code from WASM function
|
|
328
329
|
* @throws {HDWalletError} If result is non-zero
|
|
329
330
|
*/
|
|
330
331
|
function checkResult(result) {
|
|
331
332
|
if (result !== 0) {
|
|
332
|
-
throw new HDWalletError(result);
|
|
333
|
+
throw new HDWalletError(nativeErrorCode(result));
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
// Native return convention: an export whose success value is 0 reports any
|
|
338
|
+
// other value as an error; an export whose success is a length, a count or 1/0
|
|
339
|
+
// reports failure as a negative -ErrorCode. The checks below accept exactly the
|
|
340
|
+
// documented success value, so no error code (of either sign) is ever read as a
|
|
341
|
+
// length and no output buffer is read after a failed call.
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Map a failed native result to a library error code.
|
|
345
|
+
* @param {number} result - Native return value that was not success
|
|
346
|
+
* @returns {number} Known error code, or INTERNAL for anything else
|
|
347
|
+
*/
|
|
348
|
+
function nativeErrorCode(result) {
|
|
349
|
+
const code = result < 0 ? -result : result;
|
|
350
|
+
return code !== ErrorCode.OK && ERROR_MESSAGES[code] !== undefined ? code : ErrorCode.INTERNAL;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Check a length-returning export: exactly `expected` bytes were written.
|
|
355
|
+
* @param {number} result - Native return value
|
|
356
|
+
* @param {number} expected - Byte length a successful call writes
|
|
357
|
+
* @returns {number} The length
|
|
358
|
+
* @throws {HDWalletError} On any other value, positive or negative
|
|
359
|
+
*/
|
|
360
|
+
function checkLength(result, expected) {
|
|
361
|
+
if (result !== expected) {
|
|
362
|
+
throw new HDWalletError(nativeErrorCode(result));
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Check a variable-length export: a length within [min, max].
|
|
369
|
+
* @param {number} result - Native return value
|
|
370
|
+
* @param {number} min - Smallest valid length
|
|
371
|
+
* @param {number} max - Largest valid length (the output capacity)
|
|
372
|
+
* @returns {number} The length
|
|
373
|
+
* @throws {HDWalletError} On any value outside the range
|
|
374
|
+
*/
|
|
375
|
+
function checkLengthWithin(result, min, max) {
|
|
376
|
+
if (!Number.isInteger(result) || result < min || result > max) {
|
|
377
|
+
throw new HDWalletError(nativeErrorCode(result));
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Input validation for every wrapper (input-validation.mjs): type, exact
|
|
384
|
+
* length, and for keys a native validity check, before anything reaches WASM.
|
|
385
|
+
* Verification returns false for invalid bytes and throws only for programmer
|
|
386
|
+
* errors (wrong argument types, unknown curves or coins).
|
|
387
|
+
*/
|
|
388
|
+
const V = createInputValidation((code, message) => new HDWalletError(code, message));
|
|
389
|
+
|
|
390
|
+
/** Decode a hex string to bytes, or null when it is not valid hex. */
|
|
391
|
+
function hexOrNull(value) {
|
|
392
|
+
if (typeof value !== 'string') return null;
|
|
393
|
+
const hex = value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;
|
|
394
|
+
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) return null;
|
|
395
|
+
return decodeHexFallback(hex);
|
|
396
|
+
}
|
|
397
|
+
|
|
336
398
|
/**
|
|
337
399
|
* Return a WASM function if exported by this build, otherwise null.
|
|
338
400
|
* Some features are optional and can be omitted from lean builds.
|
|
@@ -495,6 +557,59 @@ function decodeBech32Fallback(str) {
|
|
|
495
557
|
return { hrp, data: out };
|
|
496
558
|
}
|
|
497
559
|
|
|
560
|
+
/** %TypedArray%.prototype.fill, captured at load (uses the internal length). */
|
|
561
|
+
const TYPED_ARRAY_FILL = Object.getPrototypeOf(Uint8Array.prototype).fill;
|
|
562
|
+
|
|
563
|
+
// Internal-slot getters, captured at load: they work on objects of any realm
|
|
564
|
+
// and cannot be overridden by the object.
|
|
565
|
+
const slotGetter = (prototype, name) => Object.getOwnPropertyDescriptor(prototype, name)?.get;
|
|
566
|
+
const TYPED_ARRAY_SLOTS = ['buffer', 'byteOffset', 'byteLength']
|
|
567
|
+
.map((name) => slotGetter(Object.getPrototypeOf(Uint8Array.prototype), name));
|
|
568
|
+
const DATA_VIEW_SLOTS = ['buffer', 'byteOffset', 'byteLength'].map((name) => slotGetter(DataView.prototype, name));
|
|
569
|
+
const ARRAY_BUFFER_BYTE_LENGTH = slotGetter(ArrayBuffer.prototype, 'byteLength');
|
|
570
|
+
const ARRAY_BUFFER_DETACHED = slotGetter(ArrayBuffer.prototype, 'detached');
|
|
571
|
+
const SHARED_BUFFER_BYTE_LENGTH = typeof SharedArrayBuffer === 'function'
|
|
572
|
+
? slotGetter(SharedArrayBuffer.prototype, 'byteLength')
|
|
573
|
+
: undefined;
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* The bytes under binary data of any kind and realm: { buffer, offset,
|
|
577
|
+
* length, detached }, or null for anything that is not binary data.
|
|
578
|
+
*/
|
|
579
|
+
function binaryRegion(value) {
|
|
580
|
+
const read = (getters) => {
|
|
581
|
+
try {
|
|
582
|
+
const [buffer, offset, length] = getters.map((get) => get.call(value));
|
|
583
|
+
return { buffer, offset, length };
|
|
584
|
+
} catch {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
let region = read(TYPED_ARRAY_SLOTS) ?? read(DATA_VIEW_SLOTS);
|
|
589
|
+
if (region === null) {
|
|
590
|
+
for (const byteLength of [ARRAY_BUFFER_BYTE_LENGTH, SHARED_BUFFER_BYTE_LENGTH]) {
|
|
591
|
+
if (!byteLength) continue;
|
|
592
|
+
try {
|
|
593
|
+
region = { buffer: value, offset: 0, length: byteLength.call(value) };
|
|
594
|
+
break;
|
|
595
|
+
} catch {
|
|
596
|
+
// Not that kind of buffer.
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (region === null) return null;
|
|
601
|
+
let detached = false;
|
|
602
|
+
try {
|
|
603
|
+
detached = ARRAY_BUFFER_DETACHED ? ARRAY_BUFFER_DETACHED.call(region.buffer) : false;
|
|
604
|
+
} catch {
|
|
605
|
+
detached = false; // A SharedArrayBuffer cannot be detached.
|
|
606
|
+
}
|
|
607
|
+
return { ...region, detached };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** The largest Ethereum chain ID (EIP-2294). */
|
|
611
|
+
const ETH_MAX_CHAIN_ID = 9223372036854775771n;
|
|
612
|
+
|
|
498
613
|
// =============================================================================
|
|
499
614
|
// Memory Helpers
|
|
500
615
|
// =============================================================================
|
|
@@ -503,17 +618,23 @@ function decodeBech32Fallback(str) {
|
|
|
503
618
|
* Allocate memory and copy data
|
|
504
619
|
* @param {WebAssembly.Module} wasm - WASM module
|
|
505
620
|
* @param {Uint8Array} data - Data to copy
|
|
506
|
-
* @returns {number}
|
|
621
|
+
* @returns {{ptr: number, len: number}} Heap pointer and the copied length
|
|
507
622
|
*/
|
|
508
623
|
function allocAndCopy(wasm, data) {
|
|
624
|
+
// Only binary data reaches the heap (a string or array would be coerced).
|
|
625
|
+
V.bytes(data, 'data');
|
|
626
|
+
// One copy from the array's own bytes and length: the caller's `length`
|
|
627
|
+
// (a subclass getter, an own property) is never read again. Callers pass
|
|
628
|
+
// the returned `len` to native code.
|
|
629
|
+
const copy = new Uint8Array(data);
|
|
630
|
+
const len = copy.length;
|
|
509
631
|
// Many C++ implementations assume pointers are non-null even when `len == 0`.
|
|
510
632
|
// Allocate a 1-byte placeholder in that case to avoid passing NULL into WASM.
|
|
511
|
-
const
|
|
512
|
-
const allocLen = len === 0 ? 1 : len;
|
|
513
|
-
const ptr = wasm._hd_alloc(allocLen);
|
|
633
|
+
const ptr = wasm._hd_alloc(len === 0 ? 1 : len);
|
|
514
634
|
if (!ptr) throw new HDWalletError(ErrorCode.OUT_OF_MEMORY);
|
|
515
|
-
|
|
516
|
-
|
|
635
|
+
wasm.HEAPU8.set(copy, ptr);
|
|
636
|
+
copy.fill(0);
|
|
637
|
+
return { ptr, len };
|
|
517
638
|
}
|
|
518
639
|
|
|
519
640
|
/**
|
|
@@ -523,6 +644,7 @@ function allocAndCopy(wasm, data) {
|
|
|
523
644
|
* @returns {number} Pointer to allocated memory
|
|
524
645
|
*/
|
|
525
646
|
function allocString(wasm, str) {
|
|
647
|
+
V.cString(str, 'string argument');
|
|
526
648
|
const len = wasm.lengthBytesUTF8(str) + 1;
|
|
527
649
|
const ptr = wasm._hd_alloc(len);
|
|
528
650
|
if (!ptr) throw new HDWalletError(ErrorCode.OUT_OF_MEMORY);
|
|
@@ -649,17 +771,10 @@ function callDynamicStringResult(wasm, invoke, options = {}) {
|
|
|
649
771
|
}
|
|
650
772
|
}
|
|
651
773
|
|
|
774
|
+
// Binary data is a Uint8Array in every family: a DataView, an ArrayBuffer or
|
|
775
|
+
// another typed array is refused, as everywhere else.
|
|
652
776
|
function toUint8Array(data, name = 'data') {
|
|
653
|
-
|
|
654
|
-
return data;
|
|
655
|
-
}
|
|
656
|
-
if (ArrayBuffer.isView(data)) {
|
|
657
|
-
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice();
|
|
658
|
-
}
|
|
659
|
-
if (data instanceof ArrayBuffer) {
|
|
660
|
-
return new Uint8Array(data.slice(0));
|
|
661
|
-
}
|
|
662
|
-
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, `${name} must be binary data`);
|
|
777
|
+
return V.bytes(data, name);
|
|
663
778
|
}
|
|
664
779
|
|
|
665
780
|
function curveToName(curve) {
|
|
@@ -916,7 +1031,7 @@ class HDKey {
|
|
|
916
1031
|
publicKeyUncompressed() {
|
|
917
1032
|
this._checkDestroyed();
|
|
918
1033
|
const compressed = this.publicKey();
|
|
919
|
-
const inPtr = allocAndCopy(this._wasm, compressed);
|
|
1034
|
+
const { ptr: inPtr, len: inLen } = allocAndCopy(this._wasm, compressed);
|
|
920
1035
|
const outPtr = this._wasm._hd_alloc(65);
|
|
921
1036
|
try {
|
|
922
1037
|
const result = this._wasm._hd_curve_decompress_pubkey(inPtr, Curve.SECP256K1, outPtr, 65);
|
|
@@ -960,6 +1075,7 @@ class HDKey {
|
|
|
960
1075
|
*/
|
|
961
1076
|
deriveChild(index) {
|
|
962
1077
|
this._checkDestroyed();
|
|
1078
|
+
V.uint32(index, 'index');
|
|
963
1079
|
const childHandle = this._wasm._hd_key_derive_child(this._handle, index);
|
|
964
1080
|
if (!childHandle) {
|
|
965
1081
|
throw new HDWalletError(ErrorCode.KEY_DERIVATION_FAILED);
|
|
@@ -975,6 +1091,7 @@ class HDKey {
|
|
|
975
1091
|
*/
|
|
976
1092
|
deriveHardened(index) {
|
|
977
1093
|
this._checkDestroyed();
|
|
1094
|
+
V.integer(index, 'index', 0, 0x7fffffff);
|
|
978
1095
|
const childHandle = this._wasm._hd_key_derive_hardened(this._handle, index);
|
|
979
1096
|
if (!childHandle) {
|
|
980
1097
|
throw new HDWalletError(ErrorCode.KEY_DERIVATION_FAILED);
|
|
@@ -1066,11 +1183,11 @@ class HDKey {
|
|
|
1066
1183
|
peerId() {
|
|
1067
1184
|
this._checkDestroyed();
|
|
1068
1185
|
const pubKey = this.publicKey();
|
|
1069
|
-
const pubPtr = allocAndCopy(this._wasm, pubKey);
|
|
1186
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
|
|
1070
1187
|
const outPtr = this._wasm._hd_alloc(64);
|
|
1071
1188
|
try {
|
|
1072
|
-
const result =
|
|
1073
|
-
|
|
1189
|
+
const result = checkLengthWithin(
|
|
1190
|
+
this._wasm._hd_libp2p_peer_id(pubPtr, pubLen, this.curve, outPtr, 64), 1, 64);
|
|
1074
1191
|
return readBytes(this._wasm, outPtr, result);
|
|
1075
1192
|
} finally {
|
|
1076
1193
|
this._wasm._hd_dealloc(pubPtr);
|
|
@@ -1085,10 +1202,10 @@ class HDKey {
|
|
|
1085
1202
|
peerIdString() {
|
|
1086
1203
|
this._checkDestroyed();
|
|
1087
1204
|
const pubKey = this.publicKey();
|
|
1088
|
-
const pubPtr = allocAndCopy(this._wasm, pubKey);
|
|
1205
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
|
|
1089
1206
|
const outPtr = this._wasm._hd_alloc(128);
|
|
1090
1207
|
try {
|
|
1091
|
-
const result = this._wasm._hd_libp2p_peer_id_string(pubPtr,
|
|
1208
|
+
const result = this._wasm._hd_libp2p_peer_id_string(pubPtr, pubLen, this.curve, outPtr, 128);
|
|
1092
1209
|
checkResult(result);
|
|
1093
1210
|
return readString(this._wasm, outPtr);
|
|
1094
1211
|
} finally {
|
|
@@ -1104,10 +1221,10 @@ class HDKey {
|
|
|
1104
1221
|
ipnsHash() {
|
|
1105
1222
|
this._checkDestroyed();
|
|
1106
1223
|
const pubKey = this.publicKey();
|
|
1107
|
-
const pubPtr = allocAndCopy(this._wasm, pubKey);
|
|
1224
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
|
|
1108
1225
|
const outPtr = this._wasm._hd_alloc(128);
|
|
1109
1226
|
try {
|
|
1110
|
-
const result = this._wasm._hd_libp2p_ipns_hash(pubPtr,
|
|
1227
|
+
const result = this._wasm._hd_libp2p_ipns_hash(pubPtr, pubLen, this.curve, outPtr, 128);
|
|
1111
1228
|
checkResult(result);
|
|
1112
1229
|
return readString(this._wasm, outPtr);
|
|
1113
1230
|
} finally {
|
|
@@ -1272,6 +1389,8 @@ function createModule(wasm) {
|
|
|
1272
1389
|
* @throws {HDWalletError} If entropy not available
|
|
1273
1390
|
*/
|
|
1274
1391
|
generate(wordCount = 24, language = Language.ENGLISH) {
|
|
1392
|
+
V.integer(wordCount, 'wordCount', 0, 0x7fffffff);
|
|
1393
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1275
1394
|
const ptr = wasm._hd_alloc(1024);
|
|
1276
1395
|
try {
|
|
1277
1396
|
const result = wasm._hd_mnemonic_generate(ptr, 1024, wordCount, language);
|
|
@@ -1289,6 +1408,8 @@ function createModule(wasm) {
|
|
|
1289
1408
|
* @returns {boolean} True if valid
|
|
1290
1409
|
*/
|
|
1291
1410
|
validate(mnemonicStr, language = Language.ENGLISH) {
|
|
1411
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1412
|
+
if (!V.isCString(V.string(mnemonicStr, 'mnemonic'))) return false;
|
|
1292
1413
|
const ptr = allocString(wasm, mnemonicStr);
|
|
1293
1414
|
try {
|
|
1294
1415
|
const result = wasm._hd_mnemonic_validate(ptr, language);
|
|
@@ -1305,6 +1426,8 @@ function createModule(wasm) {
|
|
|
1305
1426
|
* @returns {Uint8Array} 64-byte seed
|
|
1306
1427
|
*/
|
|
1307
1428
|
toSeed(mnemonicStr, passphrase = '') {
|
|
1429
|
+
V.string(mnemonicStr, 'mnemonic');
|
|
1430
|
+
V.string(passphrase, 'passphrase');
|
|
1308
1431
|
const mnemonicPtr = allocString(wasm, mnemonicStr);
|
|
1309
1432
|
const passphrasePtr = allocString(wasm, passphrase);
|
|
1310
1433
|
const seedPtr = wasm._hd_alloc(64);
|
|
@@ -1330,6 +1453,7 @@ function createModule(wasm) {
|
|
|
1330
1453
|
* @returns {Uint8Array} Entropy bytes
|
|
1331
1454
|
*/
|
|
1332
1455
|
toEntropy(mnemonicStr, language = Language.ENGLISH) {
|
|
1456
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1333
1457
|
const mnemonicPtr = allocString(wasm, mnemonicStr);
|
|
1334
1458
|
const mnemonicLen = wasm.lengthBytesUTF8(mnemonicStr) + 1;
|
|
1335
1459
|
const entropyPtr = wasm._hd_alloc(33);
|
|
@@ -1338,7 +1462,7 @@ function createModule(wasm) {
|
|
|
1338
1462
|
wasm.setValue(sizePtr, 33, 'i32');
|
|
1339
1463
|
const result = wasm._hd_mnemonic_to_entropy(mnemonicPtr, language, entropyPtr, sizePtr);
|
|
1340
1464
|
checkResult(result);
|
|
1341
|
-
const size = wasm.getValue(sizePtr, 'i32');
|
|
1465
|
+
const size = checkLengthWithin(wasm.getValue(sizePtr, 'i32'), 16, 32);
|
|
1342
1466
|
return readBytes(wasm, entropyPtr, size);
|
|
1343
1467
|
} finally {
|
|
1344
1468
|
// SECURITY FIX [HIGH-04]: Wipe mnemonic from memory before deallocation
|
|
@@ -1358,15 +1482,17 @@ function createModule(wasm) {
|
|
|
1358
1482
|
* @returns {string} Mnemonic phrase
|
|
1359
1483
|
*/
|
|
1360
1484
|
fromEntropy(entropy, language = Language.ENGLISH) {
|
|
1361
|
-
|
|
1485
|
+
V.bytes(entropy, 'entropy', ErrorCode.INVALID_ENTROPY_LENGTH);
|
|
1486
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1487
|
+
const { ptr: entropyPtr, len: entropyLen } = allocAndCopy(wasm, entropy);
|
|
1362
1488
|
const outputPtr = wasm._hd_alloc(1024);
|
|
1363
1489
|
try {
|
|
1364
|
-
const result = wasm._hd_entropy_to_mnemonic(entropyPtr,
|
|
1490
|
+
const result = wasm._hd_entropy_to_mnemonic(entropyPtr, entropyLen, language, outputPtr, 1024);
|
|
1365
1491
|
checkResult(result);
|
|
1366
1492
|
return readString(wasm, outputPtr);
|
|
1367
1493
|
} finally {
|
|
1368
1494
|
// SECURITY FIX [HIGH-04]: Wipe sensitive data before deallocation
|
|
1369
|
-
wasm._hd_secure_wipe(entropyPtr,
|
|
1495
|
+
wasm._hd_secure_wipe(entropyPtr, entropyLen);
|
|
1370
1496
|
wasm._hd_dealloc(entropyPtr);
|
|
1371
1497
|
// Wipe mnemonic output buffer
|
|
1372
1498
|
wasm._hd_secure_wipe(outputPtr, 1024);
|
|
@@ -1380,6 +1506,7 @@ function createModule(wasm) {
|
|
|
1380
1506
|
* @returns {string[]} Array of 2048 words
|
|
1381
1507
|
*/
|
|
1382
1508
|
getWordlist(language = Language.ENGLISH) {
|
|
1509
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1383
1510
|
const ptr = wasm._hd_mnemonic_get_wordlist(language);
|
|
1384
1511
|
if (!ptr) {
|
|
1385
1512
|
throw new HDWalletError(ErrorCode.NOT_SUPPORTED, 'Mnemonic wordlist not available for selected language');
|
|
@@ -1396,6 +1523,8 @@ function createModule(wasm) {
|
|
|
1396
1523
|
* @returns {string[]} Suggested words
|
|
1397
1524
|
*/
|
|
1398
1525
|
suggestWords(prefix, language = Language.ENGLISH, maxSuggestions = 5) {
|
|
1526
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1527
|
+
V.integer(maxSuggestions, 'maxSuggestions', 0, 2048);
|
|
1399
1528
|
const prefixPtr = allocString(wasm, prefix);
|
|
1400
1529
|
const outputPtr = wasm._hd_alloc(1024);
|
|
1401
1530
|
try {
|
|
@@ -1417,10 +1546,15 @@ function createModule(wasm) {
|
|
|
1417
1546
|
* @returns {boolean} True if word is in wordlist
|
|
1418
1547
|
*/
|
|
1419
1548
|
checkWord(word, language = Language.ENGLISH) {
|
|
1549
|
+
V.integer(language, 'language', 0, 0x7fffffff);
|
|
1420
1550
|
const wordPtr = allocString(wasm, word);
|
|
1421
1551
|
try {
|
|
1422
1552
|
const result = wasm._hd_mnemonic_check_word(wordPtr, language);
|
|
1423
|
-
|
|
1553
|
+
// Word index 0-2047, or -1 when the word is not in the list; any
|
|
1554
|
+
// other value is an error (invalid argument, unsupported language).
|
|
1555
|
+
if (result >= 0 && result <= 2047) return true;
|
|
1556
|
+
if (result === -1) return false;
|
|
1557
|
+
throw new HDWalletError(nativeErrorCode(result));
|
|
1424
1558
|
} finally {
|
|
1425
1559
|
wasm._hd_dealloc(wordPtr);
|
|
1426
1560
|
}
|
|
@@ -1444,18 +1578,17 @@ function createModule(wasm) {
|
|
|
1444
1578
|
*/
|
|
1445
1579
|
fromSeed(seed, curve = Curve.SECP256K1) {
|
|
1446
1580
|
// BIP-32 allows 128-512 bits (16-64 bytes)
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
const seedPtr = allocAndCopy(wasm, seed);
|
|
1581
|
+
V.bytesInRange(seed, 16, 64, 'Seed', ErrorCode.INVALID_SEED);
|
|
1582
|
+
V.curve(curve);
|
|
1583
|
+
const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
|
|
1451
1584
|
try {
|
|
1452
|
-
const handle = wasm._hd_key_from_seed(seedPtr,
|
|
1585
|
+
const handle = wasm._hd_key_from_seed(seedPtr, seedLen, curve);
|
|
1453
1586
|
if (!handle) {
|
|
1454
1587
|
throw new HDWalletError(ErrorCode.INVALID_SEED);
|
|
1455
1588
|
}
|
|
1456
1589
|
return new HDKey(wasm, handle, 'm');
|
|
1457
1590
|
} finally {
|
|
1458
|
-
wasm._hd_secure_wipe(seedPtr,
|
|
1591
|
+
wasm._hd_secure_wipe(seedPtr, seedLen);
|
|
1459
1592
|
wasm._hd_dealloc(seedPtr);
|
|
1460
1593
|
}
|
|
1461
1594
|
},
|
|
@@ -1506,6 +1639,11 @@ function createModule(wasm) {
|
|
|
1506
1639
|
* @returns {string} Derivation path
|
|
1507
1640
|
*/
|
|
1508
1641
|
buildPath(purpose, coinType, account = 0, change = 0, index = 0) {
|
|
1642
|
+
V.uint32(purpose, 'purpose');
|
|
1643
|
+
V.uint32(coinType, 'coinType');
|
|
1644
|
+
V.uint32(account, 'account');
|
|
1645
|
+
V.uint32(change, 'change');
|
|
1646
|
+
V.uint32(index, 'index');
|
|
1509
1647
|
const ptr = wasm._hd_alloc(128);
|
|
1510
1648
|
try {
|
|
1511
1649
|
const result = wasm._hd_path_build(ptr, 128, purpose, coinType, account, change, index);
|
|
@@ -1570,61 +1708,39 @@ function createModule(wasm) {
|
|
|
1570
1708
|
*/
|
|
1571
1709
|
publicKeyFromPrivate(privateKey, curve) {
|
|
1572
1710
|
const pubFromPrivFn = requireWasmFunction(wasm, '_hd_curve_pubkey_from_privkey');
|
|
1573
|
-
|
|
1711
|
+
V.privateKey(wasm, privateKey, curve);
|
|
1712
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
1574
1713
|
const pubPtr = wasm._hd_alloc(65);
|
|
1575
1714
|
try {
|
|
1576
|
-
|
|
1577
|
-
if (result < 0) throw new HDWalletError(result);
|
|
1715
|
+
// Returns the key length; a private key outside 0 < d < n fails.
|
|
1578
1716
|
const expectedLen = curve === Curve.P384 ? 49 : 33;
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
}
|
|
1582
|
-
throw new HDWalletError(result);
|
|
1717
|
+
checkLength(pubFromPrivFn(privPtr, curve, pubPtr, 65), expectedLen);
|
|
1718
|
+
return readBytes(wasm, pubPtr, expectedLen);
|
|
1583
1719
|
} finally {
|
|
1584
|
-
wasm._hd_secure_wipe(privPtr,
|
|
1720
|
+
wasm._hd_secure_wipe(privPtr, privLen);
|
|
1585
1721
|
wasm._hd_dealloc(privPtr);
|
|
1586
1722
|
wasm._hd_dealloc(pubPtr);
|
|
1587
1723
|
}
|
|
1588
1724
|
},
|
|
1589
1725
|
|
|
1590
1726
|
/**
|
|
1591
|
-
* Compress public key
|
|
1592
|
-
*
|
|
1593
|
-
*
|
|
1594
|
-
* @
|
|
1727
|
+
* Compress public key. The key must be a valid uncompressed point of the
|
|
1728
|
+
* curve (checked by the module); the compressed form is its parity byte
|
|
1729
|
+
* and x-coordinate.
|
|
1730
|
+
* @param {Uint8Array} publicKey - Uncompressed public key (65 bytes; 97 for P-384)
|
|
1731
|
+
* @param {number} curve - Curve type (secp256k1, P-256 or P-384)
|
|
1732
|
+
* @returns {Uint8Array} Compressed public key (33 bytes; 49 for P-384)
|
|
1595
1733
|
*/
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
const coordLen = (publicKey.length - 1) / 2;
|
|
1607
|
-
const out = new Uint8Array(coordLen + 1);
|
|
1608
|
-
out[0] = (publicKey[publicKey.length - 1] & 1) ? 0x03 : 0x02;
|
|
1609
|
-
out.set(publicKey.slice(1, coordLen + 1), 1);
|
|
1610
|
-
return out;
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
|
-
const inPtr = allocAndCopy(wasm, publicKey);
|
|
1614
|
-
const outPtr = wasm._hd_alloc(65);
|
|
1615
|
-
try {
|
|
1616
|
-
const result = compressFn(inPtr, curve, outPtr, 65);
|
|
1617
|
-
if (result < 0) throw new HDWalletError(result);
|
|
1618
|
-
const expectedLen = curve === Curve.P384 ? 49 : 33;
|
|
1619
|
-
if (result === 0 || result === expectedLen) {
|
|
1620
|
-
return readBytes(wasm, outPtr, expectedLen);
|
|
1621
|
-
}
|
|
1622
|
-
throw new HDWalletError(result);
|
|
1623
|
-
} finally {
|
|
1624
|
-
wasm._hd_dealloc(inPtr);
|
|
1625
|
-
wasm._hd_dealloc(outPtr);
|
|
1626
|
-
}
|
|
1627
|
-
},
|
|
1734
|
+
compressPublicKey(publicKey, curve) {
|
|
1735
|
+
V.curve(curve, [Curve.SECP256K1, Curve.P256, Curve.P384]);
|
|
1736
|
+
const uncompressedLength = curve === Curve.P384 ? 97 : 65;
|
|
1737
|
+
V.publicKey(wasm, publicKey, curve, 'publicKey', [uncompressedLength]);
|
|
1738
|
+
const coordLen = (uncompressedLength - 1) / 2;
|
|
1739
|
+
const out = new Uint8Array(coordLen + 1);
|
|
1740
|
+
out[0] = (publicKey[uncompressedLength - 1] & 1) ? 0x03 : 0x02;
|
|
1741
|
+
out.set(publicKey.subarray(1, coordLen + 1), 1);
|
|
1742
|
+
return out;
|
|
1743
|
+
},
|
|
1628
1744
|
|
|
1629
1745
|
/**
|
|
1630
1746
|
* Decompress public key
|
|
@@ -1634,16 +1750,15 @@ function createModule(wasm) {
|
|
|
1634
1750
|
*/
|
|
1635
1751
|
decompressPublicKey(publicKey, curve) {
|
|
1636
1752
|
const decompressFn = requireWasmFunction(wasm, '_hd_curve_decompress_pubkey');
|
|
1637
|
-
|
|
1753
|
+
// The export reads exactly 33 bytes: a valid compressed key only.
|
|
1754
|
+
V.publicKey(wasm, publicKey, curve, 'publicKey', [curve === Curve.P384 ? 49 : 33]);
|
|
1755
|
+
const { ptr: inPtr, len: inLen } = allocAndCopy(wasm, publicKey);
|
|
1638
1756
|
const outPtr = wasm._hd_alloc(97);
|
|
1639
1757
|
try {
|
|
1640
|
-
|
|
1641
|
-
|
|
1758
|
+
// Status export: 0 on success (secp256k1 only), -Error otherwise.
|
|
1759
|
+
checkResult(decompressFn(inPtr, curve, outPtr, 97));
|
|
1642
1760
|
const expectedLen = curve === Curve.P384 ? 97 : 65;
|
|
1643
|
-
|
|
1644
|
-
return readBytes(wasm, outPtr, expectedLen);
|
|
1645
|
-
}
|
|
1646
|
-
throw new HDWalletError(result);
|
|
1761
|
+
return readBytes(wasm, outPtr, expectedLen);
|
|
1647
1762
|
} finally {
|
|
1648
1763
|
wasm._hd_dealloc(inPtr);
|
|
1649
1764
|
wasm._hd_dealloc(outPtr);
|
|
@@ -1664,18 +1779,20 @@ function createModule(wasm) {
|
|
|
1664
1779
|
*/
|
|
1665
1780
|
ecdh(curve, privateKey, publicKey) {
|
|
1666
1781
|
const ecdhFn = requireWasmFunction(wasm, '_hd_ecdh');
|
|
1667
|
-
|
|
1668
|
-
|
|
1782
|
+
V.curve(curve, [Curve.SECP256K1, Curve.P256, Curve.P384, Curve.X25519]);
|
|
1783
|
+
V.privateKey(wasm, privateKey, curve);
|
|
1784
|
+
V.publicKey(wasm, publicKey, curve);
|
|
1785
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
1786
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1669
1787
|
const secretSize = curve === Curve.P384 ? 48 : 32;
|
|
1670
1788
|
const secretPtr = wasm._hd_alloc(secretSize);
|
|
1671
1789
|
try {
|
|
1672
|
-
const result = ecdhFn(curve, privPtr,
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
return readBytes(wasm, secretPtr, actualLen);
|
|
1790
|
+
const result = ecdhFn(curve, privPtr, privLen, pubPtr, pubLen, secretPtr, secretSize);
|
|
1791
|
+
// Returns the secret length (the curve's field size); -1 on failure.
|
|
1792
|
+
if (result !== secretSize) throw new HDWalletError(ErrorCode.INTERNAL, 'ECDH key exchange failed');
|
|
1793
|
+
return readBytes(wasm, secretPtr, secretSize);
|
|
1677
1794
|
} finally {
|
|
1678
|
-
wasm._hd_secure_wipe(privPtr,
|
|
1795
|
+
wasm._hd_secure_wipe(privPtr, privLen);
|
|
1679
1796
|
wasm._hd_secure_wipe(secretPtr, secretSize);
|
|
1680
1797
|
wasm._hd_dealloc(privPtr);
|
|
1681
1798
|
wasm._hd_dealloc(pubPtr);
|
|
@@ -1697,13 +1814,15 @@ function createModule(wasm) {
|
|
|
1697
1814
|
* @returns {Uint8Array} 64-byte compact signature r||s
|
|
1698
1815
|
*/
|
|
1699
1816
|
sign(message, privateKey) {
|
|
1700
|
-
|
|
1701
|
-
|
|
1817
|
+
V.bytes(message, 'message');
|
|
1818
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
1819
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
1820
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
1702
1821
|
const sigPtr = wasm._hd_alloc(72);
|
|
1703
1822
|
try {
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
return readBytes(wasm, sigPtr,
|
|
1823
|
+
// Returns 64 (compact r||s); a private key outside 0 < d < n fails.
|
|
1824
|
+
checkLength(wasm._hd_secp256k1_sign(msgPtr, msgLen, keyPtr, sigPtr, 72), 64);
|
|
1825
|
+
return readBytes(wasm, sigPtr, 64);
|
|
1707
1826
|
} finally {
|
|
1708
1827
|
wasm._hd_secure_wipe(keyPtr, 32);
|
|
1709
1828
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1722,17 +1841,19 @@ function createModule(wasm) {
|
|
|
1722
1841
|
*/
|
|
1723
1842
|
signRecoverable(message, privateKey) {
|
|
1724
1843
|
const signRecoverableFn = requireWasmFunction(wasm, '_hd_secp256k1_sign_recoverable');
|
|
1725
|
-
|
|
1726
|
-
|
|
1844
|
+
V.bytesOfLength(message, 32, 'message hash');
|
|
1845
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
1846
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
1847
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
1727
1848
|
const sigPtr = wasm._hd_alloc(65);
|
|
1728
1849
|
try {
|
|
1729
1850
|
// C signature: int32_t hd_secp256k1_sign_recoverable(hash, hash_len, privkey, out_sig65)
|
|
1730
|
-
// Returns 65 on success (bytes written),
|
|
1851
|
+
// Returns 65 on success (bytes written), -Error on failure.
|
|
1731
1852
|
// out_sig65 = r[32] + s[32] + v[1], recovery ID is at byte[64].
|
|
1732
|
-
|
|
1733
|
-
if (result < 0) throw new HDWalletError(result);
|
|
1734
|
-
const signature = readBytes(wasm, sigPtr, 64);
|
|
1853
|
+
checkLength(signRecoverableFn(msgPtr, msgLen, keyPtr, sigPtr), 65);
|
|
1735
1854
|
const recoveryId = wasm.HEAPU8[sigPtr + 64];
|
|
1855
|
+
if (recoveryId > 3) throw new HDWalletError(ErrorCode.INVALID_SIGNATURE);
|
|
1856
|
+
const signature = readBytes(wasm, sigPtr, 64);
|
|
1736
1857
|
return { signature, recoveryId };
|
|
1737
1858
|
} finally {
|
|
1738
1859
|
wasm._hd_secure_wipe(keyPtr, 32);
|
|
@@ -1751,11 +1872,18 @@ function createModule(wasm) {
|
|
|
1751
1872
|
* @returns {boolean} True if valid
|
|
1752
1873
|
*/
|
|
1753
1874
|
verify(message, signature, publicKey) {
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1875
|
+
V.bytes(message, 'message');
|
|
1876
|
+
V.bytes(signature, 'signature');
|
|
1877
|
+
V.bytes(publicKey, 'publicKey');
|
|
1878
|
+
// Compact r||s (64 bytes) or DER (8-72 bytes); a key of either SEC1 size.
|
|
1879
|
+
if (V.byteLength(signature) < 8 || V.byteLength(signature) > 72 || !V.hasLength(publicKey, [33, 65])) {
|
|
1880
|
+
return false;
|
|
1881
|
+
}
|
|
1882
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
1883
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
1884
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1757
1885
|
try {
|
|
1758
|
-
const result = wasm._hd_secp256k1_verify(msgPtr,
|
|
1886
|
+
const result = wasm._hd_secp256k1_verify(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen);
|
|
1759
1887
|
return result === 1;
|
|
1760
1888
|
} finally {
|
|
1761
1889
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1774,17 +1902,23 @@ function createModule(wasm) {
|
|
|
1774
1902
|
*/
|
|
1775
1903
|
recover(message, signature, recoveryId) {
|
|
1776
1904
|
const recoverFn = requireWasmFunction(wasm, '_hd_secp256k1_recover');
|
|
1777
|
-
|
|
1905
|
+
V.bytesOfLength(message, 32, 'message hash');
|
|
1906
|
+
V.bytesOfLength(signature, 64, 'signature', ErrorCode.INVALID_SIGNATURE);
|
|
1907
|
+
if (!Number.isInteger(recoveryId) || recoveryId < 0 || recoveryId > 3) {
|
|
1908
|
+
throw new HDWalletError(ErrorCode.INVALID_SIGNATURE, 'recoveryId must be 0, 1, 2 or 3');
|
|
1909
|
+
}
|
|
1910
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
1778
1911
|
// Build 65-byte sig buffer: r[32] + s[32] + v[1]
|
|
1779
1912
|
// C signature: int32_t hd_secp256k1_recover(hash, hash_len, sig65, out_pubkey33)
|
|
1780
1913
|
const sig65 = new Uint8Array(65);
|
|
1781
1914
|
sig65.set(signature.subarray(0, 64));
|
|
1782
1915
|
sig65[64] = recoveryId;
|
|
1783
|
-
const sigPtr = allocAndCopy(wasm, sig65);
|
|
1916
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sig65);
|
|
1784
1917
|
const pubPtr = wasm._hd_alloc(33);
|
|
1785
1918
|
try {
|
|
1786
|
-
|
|
1787
|
-
|
|
1919
|
+
// Returns 33 on success; a signature no key recovers from (ids 2
|
|
1920
|
+
// and 3 when r + n >= p included) fails, so this throws.
|
|
1921
|
+
checkLength(recoverFn(msgPtr, msgLen, sigPtr, pubPtr), 33);
|
|
1788
1922
|
return readBytes(wasm, pubPtr, 33);
|
|
1789
1923
|
} finally {
|
|
1790
1924
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1800,12 +1934,13 @@ function createModule(wasm) {
|
|
|
1800
1934
|
* @returns {Uint8Array} Shared secret
|
|
1801
1935
|
*/
|
|
1802
1936
|
ecdh(privateKey, publicKey) {
|
|
1803
|
-
|
|
1804
|
-
|
|
1937
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
1938
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1);
|
|
1939
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
1940
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1805
1941
|
const secretPtr = wasm._hd_alloc(32);
|
|
1806
1942
|
try {
|
|
1807
|
-
|
|
1808
|
-
if (result < 0) throw new HDWalletError(result);
|
|
1943
|
+
checkLength(wasm._hd_ecdh_secp256k1(privPtr, pubPtr, pubLen, secretPtr, 32), 32);
|
|
1809
1944
|
return readBytes(wasm, secretPtr, 32);
|
|
1810
1945
|
} finally {
|
|
1811
1946
|
wasm._hd_secure_wipe(privPtr, 32);
|
|
@@ -1827,11 +1962,11 @@ function createModule(wasm) {
|
|
|
1827
1962
|
* @returns {Uint8Array} 32-byte public key
|
|
1828
1963
|
*/
|
|
1829
1964
|
publicKeyFromSeed(seed) {
|
|
1830
|
-
|
|
1965
|
+
V.privateKey(wasm, seed, Curve.ED25519, 'seed');
|
|
1966
|
+
const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
|
|
1831
1967
|
const pubPtr = wasm._hd_alloc(32);
|
|
1832
1968
|
try {
|
|
1833
|
-
|
|
1834
|
-
if (result < 0) throw new HDWalletError(result);
|
|
1969
|
+
checkResult(wasm._hd_ed25519_pubkey_from_seed(seedPtr, pubPtr, 32));
|
|
1835
1970
|
return readBytes(wasm, pubPtr, 32);
|
|
1836
1971
|
} finally {
|
|
1837
1972
|
wasm._hd_secure_wipe(seedPtr, 32);
|
|
@@ -1847,12 +1982,13 @@ function createModule(wasm) {
|
|
|
1847
1982
|
* @returns {Uint8Array} 64-byte signature
|
|
1848
1983
|
*/
|
|
1849
1984
|
sign(message, privateKey) {
|
|
1850
|
-
|
|
1851
|
-
|
|
1985
|
+
V.bytes(message, 'message');
|
|
1986
|
+
V.privateKey(wasm, privateKey, Curve.ED25519);
|
|
1987
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
1988
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
1852
1989
|
const sigPtr = wasm._hd_alloc(64);
|
|
1853
1990
|
try {
|
|
1854
|
-
|
|
1855
|
-
if (len < 0) throw new HDWalletError(len);
|
|
1991
|
+
checkLength(wasm._hd_ed25519_sign(msgPtr, msgLen, keyPtr, sigPtr, 64), 64);
|
|
1856
1992
|
return readBytes(wasm, sigPtr, 64);
|
|
1857
1993
|
} finally {
|
|
1858
1994
|
wasm._hd_secure_wipe(keyPtr, 32);
|
|
@@ -1870,11 +2006,16 @@ function createModule(wasm) {
|
|
|
1870
2006
|
* @returns {boolean} True if valid
|
|
1871
2007
|
*/
|
|
1872
2008
|
verify(message, signature, publicKey) {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2009
|
+
V.bytes(message, 'message');
|
|
2010
|
+
V.bytes(signature, 'signature');
|
|
2011
|
+
V.bytes(publicKey, 'publicKey');
|
|
2012
|
+
if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
|
|
2013
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2014
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
2015
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1876
2016
|
try {
|
|
1877
|
-
|
|
2017
|
+
// RFC 8032 strict: S < L, A and R canonical and not of small order.
|
|
2018
|
+
const result = wasm._hd_ed25519_verify(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen);
|
|
1878
2019
|
return result === 1;
|
|
1879
2020
|
} finally {
|
|
1880
2021
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1896,13 +2037,14 @@ function createModule(wasm) {
|
|
|
1896
2037
|
* @returns {Uint8Array} 64-byte compact signature r||s
|
|
1897
2038
|
*/
|
|
1898
2039
|
sign(message, privateKey) {
|
|
1899
|
-
|
|
1900
|
-
|
|
2040
|
+
V.bytes(message, 'message');
|
|
2041
|
+
V.privateKey(wasm, privateKey, Curve.P256);
|
|
2042
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2043
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
1901
2044
|
const sigPtr = wasm._hd_alloc(72);
|
|
1902
2045
|
try {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
return readBytes(wasm, sigPtr, len);
|
|
2046
|
+
checkLength(wasm._hd_p256_sign(msgPtr, msgLen, keyPtr, sigPtr, 72), 64);
|
|
2047
|
+
return readBytes(wasm, sigPtr, 64);
|
|
1906
2048
|
} finally {
|
|
1907
2049
|
wasm._hd_secure_wipe(keyPtr, 32);
|
|
1908
2050
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1920,12 +2062,16 @@ function createModule(wasm) {
|
|
|
1920
2062
|
*/
|
|
1921
2063
|
verify(message, signature, publicKey) {
|
|
1922
2064
|
const verifyFn = requireWasmFunction(wasm, '_hd_p256_verify');
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
2065
|
+
V.bytes(message, 'message');
|
|
2066
|
+
V.bytes(signature, 'signature');
|
|
2067
|
+
// The export reads exactly 64 signature bytes and takes no length.
|
|
2068
|
+
if (V.byteLength(signature) !== 64 || !V.isPublicKey(wasm, publicKey, Curve.P256)) return false;
|
|
2069
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2070
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
2071
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1926
2072
|
try {
|
|
1927
2073
|
// C: hd_p256_verify(message, message_len, public_key, public_key_len, signature)
|
|
1928
|
-
return verifyFn(msgPtr,
|
|
2074
|
+
return verifyFn(msgPtr, msgLen, pubPtr, pubLen, sigPtr) === 0;
|
|
1929
2075
|
} finally {
|
|
1930
2076
|
wasm._hd_dealloc(msgPtr);
|
|
1931
2077
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -1934,12 +2080,13 @@ function createModule(wasm) {
|
|
|
1934
2080
|
},
|
|
1935
2081
|
|
|
1936
2082
|
ecdh(privateKey, publicKey) {
|
|
1937
|
-
|
|
1938
|
-
|
|
2083
|
+
V.privateKey(wasm, privateKey, Curve.P256);
|
|
2084
|
+
V.publicKey(wasm, publicKey, Curve.P256);
|
|
2085
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
2086
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1939
2087
|
const secretPtr = wasm._hd_alloc(32);
|
|
1940
2088
|
try {
|
|
1941
|
-
|
|
1942
|
-
if (result < 0) throw new HDWalletError(result);
|
|
2089
|
+
checkLength(wasm._hd_ecdh_p256(privPtr, pubPtr, pubLen, secretPtr, 32), 32);
|
|
1943
2090
|
return readBytes(wasm, secretPtr, 32);
|
|
1944
2091
|
} finally {
|
|
1945
2092
|
wasm._hd_secure_wipe(privPtr, 32);
|
|
@@ -1963,13 +2110,14 @@ function createModule(wasm) {
|
|
|
1963
2110
|
* @returns {Uint8Array} 96-byte compact signature r||s
|
|
1964
2111
|
*/
|
|
1965
2112
|
sign(message, privateKey) {
|
|
1966
|
-
|
|
1967
|
-
|
|
2113
|
+
V.bytes(message, 'message');
|
|
2114
|
+
V.privateKey(wasm, privateKey, Curve.P384);
|
|
2115
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2116
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
1968
2117
|
const sigPtr = wasm._hd_alloc(104);
|
|
1969
2118
|
try {
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
return readBytes(wasm, sigPtr, len);
|
|
2119
|
+
checkLength(wasm._hd_p384_sign(msgPtr, msgLen, keyPtr, sigPtr, 104), 96);
|
|
2120
|
+
return readBytes(wasm, sigPtr, 96);
|
|
1973
2121
|
} finally {
|
|
1974
2122
|
wasm._hd_secure_wipe(keyPtr, 48);
|
|
1975
2123
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -1987,12 +2135,16 @@ function createModule(wasm) {
|
|
|
1987
2135
|
*/
|
|
1988
2136
|
verify(message, signature, publicKey) {
|
|
1989
2137
|
const verifyFn = requireWasmFunction(wasm, '_hd_p384_verify');
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2138
|
+
V.bytes(message, 'message');
|
|
2139
|
+
V.bytes(signature, 'signature');
|
|
2140
|
+
// The export reads exactly 96 signature bytes and takes no length.
|
|
2141
|
+
if (V.byteLength(signature) !== 96 || !V.isPublicKey(wasm, publicKey, Curve.P384)) return false;
|
|
2142
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2143
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
2144
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
1993
2145
|
try {
|
|
1994
2146
|
// C: hd_p384_verify(message, message_len, public_key, public_key_len, signature)
|
|
1995
|
-
return verifyFn(msgPtr,
|
|
2147
|
+
return verifyFn(msgPtr, msgLen, pubPtr, pubLen, sigPtr) === 0;
|
|
1996
2148
|
} finally {
|
|
1997
2149
|
wasm._hd_dealloc(msgPtr);
|
|
1998
2150
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -2001,12 +2153,13 @@ function createModule(wasm) {
|
|
|
2001
2153
|
},
|
|
2002
2154
|
|
|
2003
2155
|
ecdh(privateKey, publicKey) {
|
|
2004
|
-
|
|
2005
|
-
|
|
2156
|
+
V.privateKey(wasm, privateKey, Curve.P384);
|
|
2157
|
+
V.publicKey(wasm, publicKey, Curve.P384);
|
|
2158
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
2159
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2006
2160
|
const secretPtr = wasm._hd_alloc(48);
|
|
2007
2161
|
try {
|
|
2008
|
-
|
|
2009
|
-
if (result < 0) throw new HDWalletError(result);
|
|
2162
|
+
checkLength(wasm._hd_ecdh_p384(privPtr, pubPtr, pubLen, secretPtr, 48), 48);
|
|
2010
2163
|
return readBytes(wasm, secretPtr, 48);
|
|
2011
2164
|
} finally {
|
|
2012
2165
|
wasm._hd_secure_wipe(privPtr, 48);
|
|
@@ -2028,11 +2181,11 @@ function createModule(wasm) {
|
|
|
2028
2181
|
* @returns {Uint8Array} 32-byte public key
|
|
2029
2182
|
*/
|
|
2030
2183
|
publicKey(privateKey) {
|
|
2031
|
-
|
|
2184
|
+
V.privateKey(wasm, privateKey, Curve.X25519);
|
|
2185
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
2032
2186
|
const pubPtr = wasm._hd_alloc(32);
|
|
2033
2187
|
try {
|
|
2034
|
-
|
|
2035
|
-
if (result < 0) throw new HDWalletError(result);
|
|
2188
|
+
checkResult(wasm._hd_x25519_pubkey(privPtr, pubPtr, 32));
|
|
2036
2189
|
return readBytes(wasm, pubPtr, 32);
|
|
2037
2190
|
} finally {
|
|
2038
2191
|
wasm._hd_secure_wipe(privPtr, 32);
|
|
@@ -2048,12 +2201,13 @@ function createModule(wasm) {
|
|
|
2048
2201
|
* @returns {Uint8Array} 32-byte shared secret
|
|
2049
2202
|
*/
|
|
2050
2203
|
ecdh(privateKey, publicKey) {
|
|
2051
|
-
|
|
2052
|
-
|
|
2204
|
+
V.privateKey(wasm, privateKey, Curve.X25519);
|
|
2205
|
+
V.publicKey(wasm, publicKey, Curve.X25519);
|
|
2206
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
|
|
2207
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2053
2208
|
const secretPtr = wasm._hd_alloc(32);
|
|
2054
2209
|
try {
|
|
2055
|
-
|
|
2056
|
-
if (result < 0) throw new HDWalletError(result);
|
|
2210
|
+
checkResult(wasm._hd_ecdh_x25519(privPtr, pubPtr, secretPtr, 32));
|
|
2057
2211
|
return readBytes(wasm, secretPtr, 32);
|
|
2058
2212
|
} finally {
|
|
2059
2213
|
wasm._hd_secure_wipe(privPtr, 32);
|
|
@@ -2079,6 +2233,44 @@ function createModule(wasm) {
|
|
|
2079
2233
|
*
|
|
2080
2234
|
* @type {Object}
|
|
2081
2235
|
*/
|
|
2236
|
+
// Coins of the generic coin API and the curve of their keys.
|
|
2237
|
+
const COIN_CURVES = Object.freeze({
|
|
2238
|
+
[CoinType.BITCOIN]: Curve.SECP256K1,
|
|
2239
|
+
[CoinType.BITCOIN_TESTNET]: Curve.SECP256K1,
|
|
2240
|
+
[CoinType.ETHEREUM]: Curve.SECP256K1,
|
|
2241
|
+
[CoinType.COSMOS]: Curve.SECP256K1,
|
|
2242
|
+
[CoinType.SOLANA]: Curve.ED25519,
|
|
2243
|
+
[CoinType.POLKADOT]: Curve.ED25519,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
function coinCurve(coinType) {
|
|
2247
|
+
V.integer(coinType, 'coinType', -0x80000000, 0x7fffffff);
|
|
2248
|
+
const curve = COIN_CURVES[coinType];
|
|
2249
|
+
if (curve === undefined) {
|
|
2250
|
+
throw new HDWalletError(ErrorCode.NOT_SUPPORTED, `Unsupported coin type: ${coinType}`);
|
|
2251
|
+
}
|
|
2252
|
+
return curve;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
// The generic export takes 33 bytes for every coin: a compressed secp256k1
|
|
2256
|
+
// key, or 0x00 || an Ed25519 key.
|
|
2257
|
+
function coinPublicKey(coinType, publicKey) {
|
|
2258
|
+
const curve = coinCurve(coinType);
|
|
2259
|
+
if (curve === Curve.SECP256K1) {
|
|
2260
|
+
return V.publicKey(wasm, publicKey, Curve.SECP256K1, 'publicKey', [33]);
|
|
2261
|
+
}
|
|
2262
|
+
V.bytesOfLength(publicKey, [32, 33], 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
|
|
2263
|
+
if (publicKey.length === 32) {
|
|
2264
|
+
const padded = new Uint8Array(33);
|
|
2265
|
+
padded.set(publicKey, 1);
|
|
2266
|
+
return padded;
|
|
2267
|
+
}
|
|
2268
|
+
if (publicKey[0] !== 0x00) {
|
|
2269
|
+
throw new HDWalletError(ErrorCode.INVALID_PUBLIC_KEY, 'publicKey must be 32 bytes, or 33 with a 0x00 prefix');
|
|
2270
|
+
}
|
|
2271
|
+
return publicKey;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2082
2274
|
const coin = {
|
|
2083
2275
|
/**
|
|
2084
2276
|
* Generate address from public key for any supported coin
|
|
@@ -2089,10 +2281,12 @@ function createModule(wasm) {
|
|
|
2089
2281
|
*/
|
|
2090
2282
|
addressFromPubkey(coinType, publicKey, network = Network.MAINNET) {
|
|
2091
2283
|
const fn = requireWasmFunction(wasm, '_hd_coin_address_from_pubkey');
|
|
2092
|
-
|
|
2284
|
+
publicKey = coinPublicKey(coinType, publicKey);
|
|
2285
|
+
V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
|
|
2286
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2093
2287
|
const outPtr = wasm._hd_alloc(128);
|
|
2094
2288
|
try {
|
|
2095
|
-
const result = fn(coinType, network, pubPtr,
|
|
2289
|
+
const result = fn(coinType, network, pubPtr, pubLen, outPtr, 128);
|
|
2096
2290
|
checkResult(result);
|
|
2097
2291
|
return readString(wasm, outPtr);
|
|
2098
2292
|
} finally {
|
|
@@ -2110,6 +2304,9 @@ function createModule(wasm) {
|
|
|
2110
2304
|
*/
|
|
2111
2305
|
validateAddress(coinType, address, network = Network.MAINNET) {
|
|
2112
2306
|
const fn = requireWasmFunction(wasm, '_hd_coin_validate_address');
|
|
2307
|
+
V.integer(coinType, 'coinType', -0x80000000, 0x7fffffff);
|
|
2308
|
+
V.integer(network, 'network', -0x80000000, 0x7fffffff);
|
|
2309
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
2113
2310
|
const addrPtr = allocString(wasm, address);
|
|
2114
2311
|
try {
|
|
2115
2312
|
return fn(coinType, network, addrPtr) === 0;
|
|
@@ -2127,20 +2324,22 @@ function createModule(wasm) {
|
|
|
2127
2324
|
*/
|
|
2128
2325
|
signMessage(coinType, message, privateKey) {
|
|
2129
2326
|
const fn = requireWasmFunction(wasm, '_hd_coin_sign_message');
|
|
2130
|
-
|
|
2131
|
-
|
|
2327
|
+
V.bytes(message, 'message');
|
|
2328
|
+
V.privateKey(wasm, privateKey, coinCurve(coinType));
|
|
2329
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2330
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2132
2331
|
const sigLenPtr = wasm._hd_alloc(4);
|
|
2133
2332
|
// Pre-allocate enough for the largest possible signature (65 bytes for ECDSA recoverable)
|
|
2134
2333
|
const maxSigLen = 128;
|
|
2135
2334
|
wasm.setValue(sigLenPtr, maxSigLen, 'i32');
|
|
2136
2335
|
const sigPtr = wasm._hd_alloc(maxSigLen);
|
|
2137
2336
|
try {
|
|
2138
|
-
const result = fn(coinType, msgPtr,
|
|
2337
|
+
const result = fn(coinType, msgPtr, msgLen, keyPtr, sigPtr, sigLenPtr);
|
|
2139
2338
|
checkResult(result);
|
|
2140
|
-
const actualLen = wasm.getValue(sigLenPtr, 'i32');
|
|
2339
|
+
const actualLen = checkLengthWithin(wasm.getValue(sigLenPtr, 'i32'), 1, maxSigLen);
|
|
2141
2340
|
return readBytes(wasm, sigPtr, actualLen);
|
|
2142
2341
|
} finally {
|
|
2143
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
2342
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
2144
2343
|
wasm._hd_dealloc(msgPtr);
|
|
2145
2344
|
wasm._hd_dealloc(keyPtr);
|
|
2146
2345
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -2158,13 +2357,17 @@ function createModule(wasm) {
|
|
|
2158
2357
|
*/
|
|
2159
2358
|
verifyMessage(coinType, message, signature, publicKey) {
|
|
2160
2359
|
const fn = requireWasmFunction(wasm, '_hd_coin_verify_message');
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2360
|
+
coinCurve(coinType);
|
|
2361
|
+
V.bytes(message, 'message');
|
|
2362
|
+
V.bytes(signature, 'signature');
|
|
2363
|
+
V.bytes(publicKey, 'publicKey');
|
|
2364
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
2365
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
2366
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2164
2367
|
try {
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
return
|
|
2368
|
+
// The export takes every length: any failure (a malformed signature
|
|
2369
|
+
// or key included) is "not verified".
|
|
2370
|
+
return fn(coinType, msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
|
|
2168
2371
|
} finally {
|
|
2169
2372
|
wasm._hd_dealloc(msgPtr);
|
|
2170
2373
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -2190,7 +2393,34 @@ function createModule(wasm) {
|
|
|
2190
2393
|
* @returns {string} Bitcoin address
|
|
2191
2394
|
*/
|
|
2192
2395
|
getAddress(publicKey, type, network = Network.MAINNET) {
|
|
2193
|
-
|
|
2396
|
+
V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
|
|
2397
|
+
switch (type) {
|
|
2398
|
+
case BitcoinAddressType.P2PKH:
|
|
2399
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1);
|
|
2400
|
+
break;
|
|
2401
|
+
case BitcoinAddressType.P2SH:
|
|
2402
|
+
case BitcoinAddressType.P2WPKH:
|
|
2403
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1, 'publicKey', [33]);
|
|
2404
|
+
break;
|
|
2405
|
+
case BitcoinAddressType.P2TR:
|
|
2406
|
+
if (V.bytesOfLength(publicKey, [32, 33], 'publicKey', ErrorCode.INVALID_PUBLIC_KEY).length === 32) {
|
|
2407
|
+
// An x-only key: valid when 02 || x is a point.
|
|
2408
|
+
const even = new Uint8Array(33);
|
|
2409
|
+
even[0] = 0x02;
|
|
2410
|
+
even.set(publicKey, 1);
|
|
2411
|
+
V.publicKey(wasm, even, Curve.SECP256K1, 'publicKey', [33]);
|
|
2412
|
+
} else {
|
|
2413
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1, 'publicKey', [33]);
|
|
2414
|
+
}
|
|
2415
|
+
break;
|
|
2416
|
+
case BitcoinAddressType.P2WSH:
|
|
2417
|
+
// A witness script, not a key.
|
|
2418
|
+
V.bytesInRange(publicKey, 1, 10000, 'witnessScript');
|
|
2419
|
+
break;
|
|
2420
|
+
default:
|
|
2421
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Invalid address type');
|
|
2422
|
+
}
|
|
2423
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2194
2424
|
const outPtr = wasm._hd_alloc(128);
|
|
2195
2425
|
try {
|
|
2196
2426
|
let fnName;
|
|
@@ -2214,7 +2444,7 @@ function createModule(wasm) {
|
|
|
2214
2444
|
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Invalid address type');
|
|
2215
2445
|
}
|
|
2216
2446
|
const addressFn = requireWasmFunction(wasm, fnName);
|
|
2217
|
-
const result = addressFn(pubPtr,
|
|
2447
|
+
const result = addressFn(pubPtr, pubLen, network, outPtr, 128);
|
|
2218
2448
|
checkResult(result);
|
|
2219
2449
|
return readString(wasm, outPtr);
|
|
2220
2450
|
} finally {
|
|
@@ -2230,6 +2460,8 @@ function createModule(wasm) {
|
|
|
2230
2460
|
*/
|
|
2231
2461
|
validateAddress(address, network = Network.MAINNET) {
|
|
2232
2462
|
const validateFn = requireWasmFunction(wasm, '_hd_btc_validate_address');
|
|
2463
|
+
V.integer(network, 'network', -0x80000000, 0x7fffffff);
|
|
2464
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
2233
2465
|
const addrPtr = allocString(wasm, address);
|
|
2234
2466
|
try {
|
|
2235
2467
|
const result = validateFn(addrPtr, network);
|
|
@@ -2250,7 +2482,7 @@ function createModule(wasm) {
|
|
|
2250
2482
|
wasm.setValue(hashLenPtr, 32, 'i32');
|
|
2251
2483
|
const result = decodeFn(addrPtr, typePtr, hashPtr, hashLenPtr, networkPtr);
|
|
2252
2484
|
checkResult(result);
|
|
2253
|
-
const hashLen = wasm.getValue(hashLenPtr, 'i32');
|
|
2485
|
+
const hashLen = checkLengthWithin(wasm.getValue(hashLenPtr, 'i32'), 1, 32);
|
|
2254
2486
|
return {
|
|
2255
2487
|
type: wasm.getValue(typePtr, 'i32'),
|
|
2256
2488
|
hash: readBytes(wasm, hashPtr, hashLen),
|
|
@@ -2267,6 +2499,7 @@ function createModule(wasm) {
|
|
|
2267
2499
|
|
|
2268
2500
|
detectAddressType(address, network = Network.MAINNET) {
|
|
2269
2501
|
const detectFn = requireWasmFunction(wasm, '_hd_btc_detect_address_type');
|
|
2502
|
+
V.integer(network, 'network', -0x80000000, 0x7fffffff);
|
|
2270
2503
|
const addrPtr = allocString(wasm, address);
|
|
2271
2504
|
try {
|
|
2272
2505
|
return detectFn(addrPtr, network);
|
|
@@ -2277,7 +2510,9 @@ function createModule(wasm) {
|
|
|
2277
2510
|
|
|
2278
2511
|
toWIF(privateKey, compressed = true, network = Network.MAINNET) {
|
|
2279
2512
|
const toWifFn = requireWasmFunction(wasm, '_hd_btc_to_wif');
|
|
2280
|
-
|
|
2513
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2514
|
+
V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
|
|
2515
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2281
2516
|
const outPtr = wasm._hd_alloc(64);
|
|
2282
2517
|
try {
|
|
2283
2518
|
const result = toWifFn(keyPtr, compressed ? 1 : 0, network, outPtr, 64);
|
|
@@ -2312,8 +2547,10 @@ function createModule(wasm) {
|
|
|
2312
2547
|
|
|
2313
2548
|
signMessage(message, privateKey, compressed = true) {
|
|
2314
2549
|
const signFn = requireWasmFunction(wasm, '_hd_btc_sign_message');
|
|
2550
|
+
V.string(message, 'message');
|
|
2551
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2315
2552
|
const msgPtr = allocString(wasm, message);
|
|
2316
|
-
const keyPtr = allocAndCopy(wasm, privateKey);
|
|
2553
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2317
2554
|
const sigPtr = wasm._hd_alloc(65);
|
|
2318
2555
|
try {
|
|
2319
2556
|
const result = signFn(msgPtr, keyPtr, compressed ? 1 : 0, sigPtr, 65);
|
|
@@ -2330,12 +2567,23 @@ function createModule(wasm) {
|
|
|
2330
2567
|
|
|
2331
2568
|
verifyMessage(message, signature, address, network = Network.MAINNET) {
|
|
2332
2569
|
const verifyFn = requireWasmFunction(wasm, '_hd_btc_verify_message');
|
|
2333
|
-
|
|
2570
|
+
V.string(message, 'message');
|
|
2571
|
+
V.string(signature, 'signature');
|
|
2572
|
+
V.string(address, 'address');
|
|
2573
|
+
V.integer(network, 'network', -0x80000000, 0x7fffffff);
|
|
2574
|
+
// A signature that is not base64 of 65 bytes is "not verified".
|
|
2575
|
+
let sigBytes;
|
|
2576
|
+
try {
|
|
2577
|
+
sigBytes = decodeBase64Fallback(signature);
|
|
2578
|
+
} catch {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
if (V.byteLength(sigBytes) !== 65) return false;
|
|
2334
2582
|
const msgPtr = allocString(wasm, message);
|
|
2335
|
-
const sigPtr = allocAndCopy(wasm, sigBytes);
|
|
2583
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sigBytes);
|
|
2336
2584
|
const addrPtr = allocString(wasm, address);
|
|
2337
2585
|
try {
|
|
2338
|
-
const result = verifyFn(msgPtr, sigPtr,
|
|
2586
|
+
const result = verifyFn(msgPtr, sigPtr, sigLen, addrPtr, network);
|
|
2339
2587
|
return result === 1;
|
|
2340
2588
|
} finally {
|
|
2341
2589
|
wasm._hd_dealloc(msgPtr);
|
|
@@ -2355,30 +2603,49 @@ function createModule(wasm) {
|
|
|
2355
2603
|
|
|
2356
2604
|
addInput(input) {
|
|
2357
2605
|
const addInputFn = requireWasmFunction(wasm, '_hd_btc_tx_add_input');
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
const txidBytes = new Uint8Array(32);
|
|
2361
|
-
for (let i = 0; i < 32; i++) {
|
|
2362
|
-
txidBytes[i] = parseInt(txidHex.substr(i * 2, 2), 16);
|
|
2606
|
+
if (input === null || typeof input !== 'object') {
|
|
2607
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'input must be an object');
|
|
2363
2608
|
}
|
|
2364
|
-
|
|
2365
|
-
const
|
|
2609
|
+
// Each field is read once; only these values are used below.
|
|
2610
|
+
const { txid, vout, amount: amountArg, scriptType: scriptTypeArg, sequence: sequenceArg, pubkey } = input;
|
|
2611
|
+
// The txid: 32 bytes as 64 hex digits, in the given byte order.
|
|
2612
|
+
const txidBytes = typeof txid === 'string' && txid.length === 64
|
|
2613
|
+
? hexOrNull(txid)
|
|
2614
|
+
: null;
|
|
2615
|
+
if (txidBytes === null) {
|
|
2616
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'txid must be 64 hex digits');
|
|
2617
|
+
}
|
|
2618
|
+
V.uint32(vout, 'vout');
|
|
2619
|
+
const amount = V.unsignedBigInt(amountArg, 'amount', 64);
|
|
2620
|
+
const scriptType = scriptTypeArg || 0;
|
|
2621
|
+
V.integer(scriptType, 'scriptType', 0, BitcoinScriptType.MULTISIG);
|
|
2622
|
+
const sequence = sequenceArg || 0xffffffff;
|
|
2623
|
+
V.uint32(sequence, 'sequence');
|
|
2624
|
+
// The export reads a 33-byte compressed key.
|
|
2625
|
+
V.publicKey(wasm, pubkey, Curve.SECP256K1, 'pubkey', [33]);
|
|
2626
|
+
const { ptr: txidPtr } = allocAndCopy(wasm, txidBytes);
|
|
2627
|
+
const { ptr: pubPtr } = allocAndCopy(wasm, pubkey);
|
|
2366
2628
|
try {
|
|
2367
2629
|
const result = addInputFn(
|
|
2368
|
-
this._handle, txidPtr,
|
|
2369
|
-
|
|
2370
|
-
pubPtr,
|
|
2630
|
+
this._handle, txidPtr, vout,
|
|
2631
|
+
amount, scriptType,
|
|
2632
|
+
pubPtr, sequence
|
|
2371
2633
|
);
|
|
2372
2634
|
checkResult(result);
|
|
2373
2635
|
this._inputCount++;
|
|
2374
2636
|
} finally {
|
|
2375
2637
|
wasm._hd_dealloc(txidPtr);
|
|
2376
|
-
|
|
2638
|
+
wasm._hd_dealloc(pubPtr);
|
|
2377
2639
|
}
|
|
2378
2640
|
return this;
|
|
2379
2641
|
},
|
|
2380
2642
|
|
|
2381
2643
|
addOutput(address, amount, network = Network.MAINNET) {
|
|
2644
|
+
V.string(address, 'address');
|
|
2645
|
+
amount = V.unsignedBigInt(amount, 'amount', 64);
|
|
2646
|
+
// The address must be of this network: a mainnet transaction never
|
|
2647
|
+
// pays a testnet address, or the reverse. Regtest is not supported.
|
|
2648
|
+
V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
|
|
2382
2649
|
// Use address-based output helper: decode address to script internally
|
|
2383
2650
|
const addP2wpkhFn = getWasmFunction(wasm, '_hd_btc_tx_add_p2wpkh_output');
|
|
2384
2651
|
const addP2pkhFn = getWasmFunction(wasm, '_hd_btc_tx_add_p2pkh_output');
|
|
@@ -2395,44 +2662,53 @@ function createModule(wasm) {
|
|
|
2395
2662
|
try {
|
|
2396
2663
|
wasm.setValue(hashLenPtr, 32, 'i32');
|
|
2397
2664
|
const decResult = decodeFn(addrPtr, typePtr, hashPtr, hashLenPtr, networkPtr);
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
script = new Uint8Array([0xa9, 0x14, ...hash, 0x87]);
|
|
2420
|
-
} else if (addrType === 3) { // P2WSH: OP_0 <32-byte-hash>
|
|
2421
|
-
script = new Uint8Array([0x00, 0x20, ...hash]);
|
|
2422
|
-
} else if (addrType === 4) { // P2TR: OP_1 <32-byte-key>
|
|
2423
|
-
script = new Uint8Array([0x51, 0x20, ...hash]);
|
|
2424
|
-
} else {
|
|
2425
|
-
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Unsupported address type');
|
|
2426
|
-
}
|
|
2427
|
-
const scriptPtr = allocAndCopy(wasm, script);
|
|
2428
|
-
try {
|
|
2429
|
-
const result = addOutputFn(this._handle, BigInt(amount), scriptPtr, script.length);
|
|
2430
|
-
checkResult(result);
|
|
2431
|
-
} finally {
|
|
2432
|
-
wasm._hd_dealloc(scriptPtr);
|
|
2433
|
-
}
|
|
2665
|
+
checkResult(decResult);
|
|
2666
|
+
const addrType = wasm.getValue(typePtr, 'i32');
|
|
2667
|
+
const hashLen = checkLengthWithin(wasm.getValue(hashLenPtr, 'i32'), 1, 32);
|
|
2668
|
+
if (wasm.getValue(networkPtr, 'i32') !== network) {
|
|
2669
|
+
throw new HDWalletError(ErrorCode.INVALID_ADDRESS, 'address is not of this transaction\'s network');
|
|
2670
|
+
}
|
|
2671
|
+
// The native decoder types an address by witness version and
|
|
2672
|
+
// program length together; each type has one payload length
|
|
2673
|
+
// (the P2WPKH/P2PKH helpers read a 20-byte key hash).
|
|
2674
|
+
const payloadLength = [20, 20, 20, 32, 32][addrType];
|
|
2675
|
+
if (payloadLength === undefined || hashLen !== payloadLength) {
|
|
2676
|
+
throw new HDWalletError(ErrorCode.INVALID_ADDRESS);
|
|
2677
|
+
}
|
|
2678
|
+
// P2WPKH = 2, P2PKH = 0
|
|
2679
|
+
if (addrType === 2 && addP2wpkhFn) {
|
|
2680
|
+
const result = addP2wpkhFn(this._handle, BigInt(amount), hashPtr);
|
|
2681
|
+
checkResult(result);
|
|
2682
|
+
return this;
|
|
2683
|
+
} else if (addrType === 0 && addP2pkhFn) {
|
|
2684
|
+
const result = addP2pkhFn(this._handle, BigInt(amount), hashPtr);
|
|
2685
|
+
checkResult(result);
|
|
2434
2686
|
return this;
|
|
2435
2687
|
}
|
|
2688
|
+
// For other types, build script manually
|
|
2689
|
+
const hash = readBytes(wasm, hashPtr, hashLen);
|
|
2690
|
+
let script;
|
|
2691
|
+
if (addrType === 2) { // P2WPKH: OP_0 <20-byte-hash>
|
|
2692
|
+
script = new Uint8Array([0x00, 0x14, ...hash]);
|
|
2693
|
+
} else if (addrType === 0) { // P2PKH: OP_DUP OP_HASH160 <20> <hash> OP_EQUALVERIFY OP_CHECKSIG
|
|
2694
|
+
script = new Uint8Array([0x76, 0xa9, 0x14, ...hash, 0x88, 0xac]);
|
|
2695
|
+
} else if (addrType === 1) { // P2SH: OP_HASH160 <20> <hash> OP_EQUAL
|
|
2696
|
+
script = new Uint8Array([0xa9, 0x14, ...hash, 0x87]);
|
|
2697
|
+
} else if (addrType === 3) { // P2WSH: OP_0 <32-byte-hash>
|
|
2698
|
+
script = new Uint8Array([0x00, 0x20, ...hash]);
|
|
2699
|
+
} else if (addrType === 4) { // P2TR: OP_1 <32-byte-key>
|
|
2700
|
+
script = new Uint8Array([0x51, 0x20, ...hash]);
|
|
2701
|
+
} else {
|
|
2702
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Unsupported address type');
|
|
2703
|
+
}
|
|
2704
|
+
const { ptr: scriptPtr, len: scriptLen } = allocAndCopy(wasm, script);
|
|
2705
|
+
try {
|
|
2706
|
+
const result = addOutputFn(this._handle, BigInt(amount), scriptPtr, scriptLen);
|
|
2707
|
+
checkResult(result);
|
|
2708
|
+
} finally {
|
|
2709
|
+
wasm._hd_dealloc(scriptPtr);
|
|
2710
|
+
}
|
|
2711
|
+
return this;
|
|
2436
2712
|
} finally {
|
|
2437
2713
|
wasm._hd_dealloc(addrPtr);
|
|
2438
2714
|
wasm._hd_dealloc(typePtr);
|
|
@@ -2446,7 +2722,8 @@ function createModule(wasm) {
|
|
|
2446
2722
|
|
|
2447
2723
|
sign(privateKey) {
|
|
2448
2724
|
const signFn = requireWasmFunction(wasm, '_hd_btc_tx_sign');
|
|
2449
|
-
|
|
2725
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2726
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2450
2727
|
try {
|
|
2451
2728
|
// Sign all inputs
|
|
2452
2729
|
for (let i = 0; i < this._inputCount; i++) {
|
|
@@ -2468,7 +2745,7 @@ function createModule(wasm) {
|
|
|
2468
2745
|
wasm.setValue(sizePtr, 65536, 'i32');
|
|
2469
2746
|
const result = serializeFn(this._handle, outPtr, 65536, sizePtr);
|
|
2470
2747
|
checkResult(result);
|
|
2471
|
-
const size = wasm.getValue(sizePtr, 'i32');
|
|
2748
|
+
const size = checkLengthWithin(wasm.getValue(sizePtr, 'i32'), 1, 65536);
|
|
2472
2749
|
return readBytes(wasm, outPtr, size);
|
|
2473
2750
|
} finally {
|
|
2474
2751
|
wasm._hd_dealloc(outPtr);
|
|
@@ -2533,9 +2810,10 @@ function createModule(wasm) {
|
|
|
2533
2810
|
|
|
2534
2811
|
parse(data) {
|
|
2535
2812
|
const parseFn = requireWasmFunction(wasm, '_hd_btc_tx_parse');
|
|
2536
|
-
|
|
2813
|
+
V.bytes(data, 'data');
|
|
2814
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
2537
2815
|
try {
|
|
2538
|
-
const handle = parseFn(dataPtr,
|
|
2816
|
+
const handle = parseFn(dataPtr, dataLen);
|
|
2539
2817
|
if (!handle) throw new HDWalletError(ErrorCode.INVALID_TRANSACTION);
|
|
2540
2818
|
return this._makeTxObj(handle);
|
|
2541
2819
|
} finally {
|
|
@@ -2553,6 +2831,12 @@ function createModule(wasm) {
|
|
|
2553
2831
|
* Ethereum API
|
|
2554
2832
|
* @type {Object}
|
|
2555
2833
|
*/
|
|
2834
|
+
// An Ethereum message: a string (UTF-8) or bytes.
|
|
2835
|
+
function ethereumMessageBytes(message) {
|
|
2836
|
+
if (typeof message === 'string') return UTF8_ENCODER.encode(message);
|
|
2837
|
+
return V.bytes(message, 'message');
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2556
2840
|
const ethereum = {
|
|
2557
2841
|
/**
|
|
2558
2842
|
* Get Ethereum address from public key
|
|
@@ -2561,10 +2845,20 @@ function createModule(wasm) {
|
|
|
2561
2845
|
*/
|
|
2562
2846
|
getAddress(publicKey) {
|
|
2563
2847
|
const getAddressFn = requireWasmFunction(wasm, '_hd_eth_address');
|
|
2564
|
-
|
|
2848
|
+
V.bytesOfLength(publicKey, [33, 64, 65], 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
|
|
2849
|
+
if (V.byteLength(publicKey) === 64) {
|
|
2850
|
+
// Raw x || y: valid when 04 || x || y is a point.
|
|
2851
|
+
const sec1 = new Uint8Array(65);
|
|
2852
|
+
sec1[0] = 0x04;
|
|
2853
|
+
sec1.set(publicKey, 1);
|
|
2854
|
+
V.publicKey(wasm, sec1, Curve.SECP256K1);
|
|
2855
|
+
} else {
|
|
2856
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1);
|
|
2857
|
+
}
|
|
2858
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2565
2859
|
const outPtr = wasm._hd_alloc(64);
|
|
2566
2860
|
try {
|
|
2567
|
-
const result = getAddressFn(pubPtr,
|
|
2861
|
+
const result = getAddressFn(pubPtr, pubLen, outPtr, 64);
|
|
2568
2862
|
checkResult(result);
|
|
2569
2863
|
return readString(wasm, outPtr);
|
|
2570
2864
|
} finally {
|
|
@@ -2599,6 +2893,7 @@ function createModule(wasm) {
|
|
|
2599
2893
|
*/
|
|
2600
2894
|
validateAddress(address) {
|
|
2601
2895
|
const validateFn = requireWasmFunction(wasm, '_hd_eth_validate_address');
|
|
2896
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
2602
2897
|
const addrPtr = allocString(wasm, address);
|
|
2603
2898
|
try {
|
|
2604
2899
|
const result = validateFn(addrPtr);
|
|
@@ -2616,13 +2911,13 @@ function createModule(wasm) {
|
|
|
2616
2911
|
*/
|
|
2617
2912
|
signMessage(message, privateKey) {
|
|
2618
2913
|
const signFn = requireWasmFunction(wasm, '_hd_eth_sign_message');
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2621
|
-
const msgPtr = allocAndCopy(wasm, msgBytes);
|
|
2622
|
-
const keyPtr = allocAndCopy(wasm, privateKey);
|
|
2914
|
+
const msgBytes = ethereumMessageBytes(message);
|
|
2915
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2916
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
|
|
2917
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2623
2918
|
const sigPtr = wasm._hd_alloc(65);
|
|
2624
2919
|
try {
|
|
2625
|
-
const result = signFn(msgPtr,
|
|
2920
|
+
const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 65);
|
|
2626
2921
|
checkResult(result);
|
|
2627
2922
|
const sigBytes = readBytes(wasm, sigPtr, 65);
|
|
2628
2923
|
return '0x' + encodeHexFallback(sigBytes);
|
|
@@ -2642,16 +2937,22 @@ function createModule(wasm) {
|
|
|
2642
2937
|
*/
|
|
2643
2938
|
signTypedData(typedData, privateKey) {
|
|
2644
2939
|
const signTypedDataFn = requireWasmFunction(wasm, '_hd_eth_sign_typed_data');
|
|
2940
|
+
if (typedData === null || typeof typedData !== 'object' ||
|
|
2941
|
+
typedData.types === null || typeof typedData.types !== 'object' ||
|
|
2942
|
+
typeof typedData.primaryType !== 'string' ||
|
|
2943
|
+
typedData.domain === null || typeof typedData.domain !== 'object') {
|
|
2944
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'typedData must have types, primaryType, domain and message');
|
|
2945
|
+
}
|
|
2946
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2645
2947
|
|
|
2646
2948
|
// --- EIP-712 struct hash computation (data encoding, not crypto) ---
|
|
2647
2949
|
// keccak256 via WASM
|
|
2648
2950
|
function k256(data) {
|
|
2649
2951
|
const d = data instanceof Uint8Array ? data : new TextEncoder().encode(data);
|
|
2650
|
-
const dPtr = allocAndCopy(wasm, d);
|
|
2952
|
+
const { ptr: dPtr, len: dLen } = allocAndCopy(wasm, d);
|
|
2651
2953
|
const hPtr = wasm._hd_alloc(32);
|
|
2652
2954
|
try {
|
|
2653
|
-
|
|
2654
|
-
if (r < 0) throw new HDWalletError(r);
|
|
2955
|
+
checkLength(wasm._hd_hash_keccak256(dPtr, dLen, hPtr, 32), 32);
|
|
2655
2956
|
return readBytes(wasm, hPtr, 32);
|
|
2656
2957
|
} finally {
|
|
2657
2958
|
wasm._hd_dealloc(dPtr);
|
|
@@ -2743,8 +3044,8 @@ function createModule(wasm) {
|
|
|
2743
3044
|
const namePtr = allocString(wasm, domain.name || '');
|
|
2744
3045
|
const versionPtr = allocString(wasm, domain.version || '');
|
|
2745
3046
|
const contractPtr = allocString(wasm, domain.verifyingContract || '');
|
|
2746
|
-
const structHashPtr = allocAndCopy(wasm, structHash);
|
|
2747
|
-
const keyPtr = allocAndCopy(wasm, privateKey);
|
|
3047
|
+
const { ptr: structHashPtr, len: structHashLen } = allocAndCopy(wasm, structHash);
|
|
3048
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2748
3049
|
const sigPtr = wasm._hd_alloc(65);
|
|
2749
3050
|
try {
|
|
2750
3051
|
const result = signTypedDataFn(namePtr, versionPtr, BigInt(domain.chainId || 0), contractPtr, structHashPtr, keyPtr, sigPtr, 65);
|
|
@@ -2762,17 +3063,29 @@ function createModule(wasm) {
|
|
|
2762
3063
|
}
|
|
2763
3064
|
},
|
|
2764
3065
|
|
|
3066
|
+
/**
|
|
3067
|
+
* Recover the signer's address of an EIP-191 message signature. A recover,
|
|
3068
|
+
* not a boolean verify: compare the result with the expected address. A
|
|
3069
|
+
* signature that recovers no key (malformed hex, not 65 bytes, v outside
|
|
3070
|
+
* 0, 1, 27, 28, r or s out of range) throws INVALID_SIGNATURE; it never
|
|
3071
|
+
* yields an address.
|
|
3072
|
+
* @param {string|Uint8Array} message
|
|
3073
|
+
* @param {string} signature - 0x-prefixed hex r || s || v
|
|
3074
|
+
* @returns {string} Recovered address
|
|
3075
|
+
*/
|
|
2765
3076
|
verifyMessage(message, signature) {
|
|
2766
3077
|
const recoverFn = requireWasmFunction(wasm, '_hd_eth_recover_address');
|
|
2767
|
-
const
|
|
2768
|
-
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
3078
|
+
const msgBytes = ethereumMessageBytes(message);
|
|
3079
|
+
V.string(signature, 'signature');
|
|
3080
|
+
const sigBytes = hexOrNull(signature);
|
|
3081
|
+
if (sigBytes === null || V.byteLength(sigBytes) !== 65 || ![0, 1, 27, 28].includes(sigBytes[64])) {
|
|
3082
|
+
throw new HDWalletError(ErrorCode.INVALID_SIGNATURE, 'signature must be 65 bytes r || s || v with v 0, 1, 27 or 28');
|
|
3083
|
+
}
|
|
3084
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
|
|
3085
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sigBytes);
|
|
2773
3086
|
const addrPtr = wasm._hd_alloc(64);
|
|
2774
3087
|
try {
|
|
2775
|
-
const result = recoverFn(msgPtr,
|
|
3088
|
+
const result = recoverFn(msgPtr, msgLen, sigPtr, sigLen, addrPtr, 64);
|
|
2776
3089
|
checkResult(result);
|
|
2777
3090
|
return readString(wasm, addrPtr);
|
|
2778
3091
|
} finally {
|
|
@@ -2794,12 +3107,11 @@ function createModule(wasm) {
|
|
|
2794
3107
|
|
|
2795
3108
|
hashMessage(message) {
|
|
2796
3109
|
const hashFn = requireWasmFunction(wasm, '_hd_eth_hash_message');
|
|
2797
|
-
const
|
|
2798
|
-
const
|
|
2799
|
-
const msgPtr = allocAndCopy(wasm, msgBytes);
|
|
3110
|
+
const msgBytes = ethereumMessageBytes(message);
|
|
3111
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
|
|
2800
3112
|
const hashPtr = wasm._hd_alloc(32);
|
|
2801
3113
|
try {
|
|
2802
|
-
const result = hashFn(msgPtr,
|
|
3114
|
+
const result = hashFn(msgPtr, msgLen, hashPtr);
|
|
2803
3115
|
checkResult(result);
|
|
2804
3116
|
return readBytes(wasm, hashPtr, 32);
|
|
2805
3117
|
} finally {
|
|
@@ -2815,7 +3127,8 @@ function createModule(wasm) {
|
|
|
2815
3127
|
|
|
2816
3128
|
sign(privateKey) {
|
|
2817
3129
|
const signFn = requireWasmFunction(wasm, '_hd_eth_tx_sign');
|
|
2818
|
-
|
|
3130
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
3131
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2819
3132
|
try {
|
|
2820
3133
|
const result = signFn(this._handle, keyPtr);
|
|
2821
3134
|
checkResult(result);
|
|
@@ -2834,7 +3147,7 @@ function createModule(wasm) {
|
|
|
2834
3147
|
wasm.setValue(sizePtr, 65536, 'i32');
|
|
2835
3148
|
const result = serializeFn(this._handle, outPtr, 65536, sizePtr);
|
|
2836
3149
|
checkResult(result);
|
|
2837
|
-
const size = wasm.getValue(sizePtr, 'i32');
|
|
3150
|
+
const size = checkLengthWithin(wasm.getValue(sizePtr, 'i32'), 1, 65536);
|
|
2838
3151
|
return readBytes(wasm, outPtr, size);
|
|
2839
3152
|
} finally {
|
|
2840
3153
|
wasm._hd_dealloc(outPtr);
|
|
@@ -2864,62 +3177,137 @@ function createModule(wasm) {
|
|
|
2864
3177
|
};
|
|
2865
3178
|
},
|
|
2866
3179
|
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
if (
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
const
|
|
2878
|
-
|
|
3180
|
+
// Read each field of `params` once (a getter or a Uint8Array subclass
|
|
3181
|
+
// could answer differently on a second read: a longer `length` put heap
|
|
3182
|
+
// bytes into the signed data) and check it. The nonce and the chain ID
|
|
3183
|
+
// have no defaults: a missing nonce signed nonce 0, a missing chain ID
|
|
3184
|
+
// chain 1. The native setters take uint64 (a 256-bit value as 32 bytes);
|
|
3185
|
+
// a larger or negative number would wrap modulo 2^64, so it is refused.
|
|
3186
|
+
_readParams(params) {
|
|
3187
|
+
if (params === null || typeof params !== 'object') {
|
|
3188
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'params must be an object');
|
|
3189
|
+
}
|
|
3190
|
+
const {
|
|
3191
|
+
nonce, chainId, gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas, to, value, data,
|
|
3192
|
+
} = params;
|
|
3193
|
+
if (nonce === undefined || nonce === null) {
|
|
3194
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'nonce is required');
|
|
3195
|
+
}
|
|
3196
|
+
if (chainId === undefined || chainId === null) {
|
|
3197
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'chainId is required');
|
|
3198
|
+
}
|
|
3199
|
+
const fields = {
|
|
3200
|
+
nonce: V.unsignedBigInt(nonce, 'nonce', 64),
|
|
3201
|
+
chainId: V.unsignedBigInt(chainId, 'chainId', 64),
|
|
3202
|
+
gasLimit: gasLimit === undefined ? undefined : V.unsignedBigInt(gasLimit, 'gasLimit', 64),
|
|
3203
|
+
gasPrice: gasPrice === undefined ? undefined : V.unsignedBigInt(gasPrice, 'gasPrice', 64),
|
|
3204
|
+
maxFeePerGas,
|
|
3205
|
+
maxPriorityFeePerGas,
|
|
3206
|
+
to: to === undefined || to === null || to === '' ? undefined : V.string(to, 'to'),
|
|
3207
|
+
value: value === undefined ? undefined : V.unsignedBigInt(value, 'value', 256),
|
|
3208
|
+
// A copy made from the array's own bytes and length.
|
|
3209
|
+
data: data === undefined || data === null ? undefined : new Uint8Array(V.bytes(data, 'data')),
|
|
3210
|
+
};
|
|
3211
|
+
// EIP-2294: 1 <= chainId <= floor(MAX_UINT64 / 2) - 36, so a legacy
|
|
3212
|
+
// v = 2 * chainId + 35 + recovery id fits 64 bits.
|
|
3213
|
+
if (fields.chainId === 0n || fields.chainId > ETH_MAX_CHAIN_ID) {
|
|
3214
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, `chainId must be from 1 to ${ETH_MAX_CHAIN_ID}`);
|
|
3215
|
+
}
|
|
3216
|
+
return fields;
|
|
3217
|
+
},
|
|
2879
3218
|
|
|
2880
|
-
|
|
2881
|
-
|
|
3219
|
+
_setCommonFields(handle, fields) {
|
|
3220
|
+
requireWasmFunction(wasm, '_hd_eth_tx_set_nonce')(handle, fields.nonce);
|
|
3221
|
+
if (fields.gasLimit !== undefined) {
|
|
3222
|
+
requireWasmFunction(wasm, '_hd_eth_tx_set_gas_limit')(handle, fields.gasLimit);
|
|
3223
|
+
}
|
|
2882
3224
|
|
|
2883
|
-
if (
|
|
3225
|
+
if (fields.to !== undefined) {
|
|
2884
3226
|
const setToFn = requireWasmFunction(wasm, '_hd_eth_tx_set_to_hex');
|
|
2885
|
-
const toPtr = allocString(wasm,
|
|
3227
|
+
const toPtr = allocString(wasm, fields.to);
|
|
2886
3228
|
try {
|
|
2887
|
-
|
|
3229
|
+
// An address that does not parse fails here. Without a recipient
|
|
3230
|
+
// the transaction does not serialize or sign (it would otherwise
|
|
3231
|
+
// send the value to 0x00..00).
|
|
3232
|
+
checkResult(setToFn(handle, toPtr));
|
|
2888
3233
|
} finally {
|
|
2889
3234
|
wasm._hd_dealloc(toPtr);
|
|
2890
3235
|
}
|
|
2891
3236
|
}
|
|
2892
3237
|
|
|
2893
|
-
if (
|
|
2894
|
-
|
|
2895
|
-
setValueFn(
|
|
3238
|
+
if (fields.value !== undefined) {
|
|
3239
|
+
// Always the 256-bit setter: wei amounts reach 2^64 at ~18.45 ETH.
|
|
3240
|
+
const setValueFn = requireWasmFunction(wasm, '_hd_eth_tx_set_value_256');
|
|
3241
|
+
const valueBytes = new Uint8Array(32);
|
|
3242
|
+
for (let i = 31, v = fields.value; i >= 0 && v > 0n; i--, v >>= 8n) valueBytes[i] = Number(v & 0xffn);
|
|
3243
|
+
const { ptr: valuePtr, len: valueLen } = allocAndCopy(wasm, valueBytes);
|
|
3244
|
+
try {
|
|
3245
|
+
setValueFn(handle, valuePtr);
|
|
3246
|
+
} finally {
|
|
3247
|
+
wasm._hd_dealloc(valuePtr);
|
|
3248
|
+
}
|
|
2896
3249
|
}
|
|
2897
3250
|
|
|
2898
|
-
if (
|
|
3251
|
+
if (fields.data !== undefined) {
|
|
2899
3252
|
const setDataFn = requireWasmFunction(wasm, '_hd_eth_tx_set_data');
|
|
2900
|
-
const dataPtr = allocAndCopy(wasm,
|
|
3253
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, fields.data);
|
|
2901
3254
|
try {
|
|
2902
|
-
setDataFn(handle, dataPtr,
|
|
3255
|
+
setDataFn(handle, dataPtr, dataLen);
|
|
2903
3256
|
} finally {
|
|
2904
3257
|
wasm._hd_dealloc(dataPtr);
|
|
2905
3258
|
}
|
|
2906
3259
|
}
|
|
3260
|
+
},
|
|
3261
|
+
|
|
3262
|
+
// EIP-1559 transaction. nonce, chainId (>= 1), gasLimit, maxFeePerGas
|
|
3263
|
+
// and maxPriorityFeePerGas are required.
|
|
3264
|
+
createEIP1559(params) {
|
|
3265
|
+
const createFn = requireWasmFunction(wasm, '_hd_eth_tx_create_eip1559');
|
|
3266
|
+
const fields = this._readParams(params);
|
|
3267
|
+
if (fields.gasLimit === undefined) {
|
|
3268
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'gasLimit is required');
|
|
3269
|
+
}
|
|
3270
|
+
const maxPriorityFeePerGas = V.unsignedBigInt(fields.maxPriorityFeePerGas, 'maxPriorityFeePerGas', 64);
|
|
3271
|
+
const maxFeePerGas = V.unsignedBigInt(fields.maxFeePerGas, 'maxFeePerGas', 64);
|
|
3272
|
+
const handle = createFn(fields.chainId, maxPriorityFeePerGas, maxFeePerGas);
|
|
3273
|
+
if (!handle) throw new HDWalletError(ErrorCode.NOT_SUPPORTED);
|
|
3274
|
+
|
|
3275
|
+
try {
|
|
3276
|
+
this._setCommonFields(handle, fields);
|
|
3277
|
+
} catch (error) {
|
|
3278
|
+
requireWasmFunction(wasm, '_hd_eth_tx_destroy')(handle);
|
|
3279
|
+
throw error;
|
|
3280
|
+
}
|
|
2907
3281
|
|
|
2908
3282
|
return this._makeTxObj(handle);
|
|
2909
3283
|
},
|
|
2910
3284
|
|
|
2911
|
-
|
|
3285
|
+
// Legacy (EIP-155) transaction. nonce and chainId (>= 1) are required.
|
|
3286
|
+
// Before 2.1.1 every field but chainId was ignored, so the declared
|
|
3287
|
+
// to/value/gasPrice never reached the signed bytes.
|
|
3288
|
+
create(params = {}) {
|
|
2912
3289
|
const createFn = requireWasmFunction(wasm, '_hd_eth_tx_create');
|
|
2913
|
-
const
|
|
3290
|
+
const fields = this._readParams(params);
|
|
3291
|
+
const handle = createFn(fields.chainId);
|
|
2914
3292
|
if (!handle) throw new HDWalletError(ErrorCode.NOT_SUPPORTED);
|
|
3293
|
+
|
|
3294
|
+
try {
|
|
3295
|
+
if (fields.gasPrice !== undefined) requireWasmFunction(wasm, '_hd_eth_tx_set_gas_price')(handle, fields.gasPrice);
|
|
3296
|
+
this._setCommonFields(handle, fields);
|
|
3297
|
+
} catch (error) {
|
|
3298
|
+
requireWasmFunction(wasm, '_hd_eth_tx_destroy')(handle);
|
|
3299
|
+
throw error;
|
|
3300
|
+
}
|
|
3301
|
+
|
|
2915
3302
|
return this._makeTxObj(handle);
|
|
2916
3303
|
},
|
|
2917
3304
|
|
|
2918
3305
|
parse(data) {
|
|
2919
3306
|
const parseFn = requireWasmFunction(wasm, '_hd_eth_tx_parse');
|
|
2920
|
-
|
|
3307
|
+
V.bytes(data, 'data');
|
|
3308
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
2921
3309
|
try {
|
|
2922
|
-
const handle = parseFn(dataPtr,
|
|
3310
|
+
const handle = parseFn(dataPtr, dataLen);
|
|
2923
3311
|
if (!handle) throw new HDWalletError(ErrorCode.INVALID_TRANSACTION);
|
|
2924
3312
|
return this._makeTxObj(handle);
|
|
2925
3313
|
} finally {
|
|
@@ -2940,11 +3328,13 @@ function createModule(wasm) {
|
|
|
2940
3328
|
const cosmos = {
|
|
2941
3329
|
getAddress(publicKey, prefix = 'cosmos') {
|
|
2942
3330
|
const getAddressFn = requireWasmFunction(wasm, '_hd_cosmos_address');
|
|
2943
|
-
|
|
3331
|
+
V.publicKey(wasm, publicKey, Curve.SECP256K1, 'publicKey', [33]);
|
|
3332
|
+
V.string(prefix, 'prefix');
|
|
3333
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
2944
3334
|
const prefixPtr = allocString(wasm, prefix);
|
|
2945
3335
|
const outPtr = wasm._hd_alloc(128);
|
|
2946
3336
|
try {
|
|
2947
|
-
const result = getAddressFn(pubPtr,
|
|
3337
|
+
const result = getAddressFn(pubPtr, pubLen, prefixPtr, outPtr, 128);
|
|
2948
3338
|
checkResult(result);
|
|
2949
3339
|
return readString(wasm, outPtr);
|
|
2950
3340
|
} finally {
|
|
@@ -2956,6 +3346,9 @@ function createModule(wasm) {
|
|
|
2956
3346
|
|
|
2957
3347
|
validateAddress(address, prefix) {
|
|
2958
3348
|
const validateFn = requireWasmFunction(wasm, '_hd_cosmos_validate_address');
|
|
3349
|
+
if (prefix !== undefined && prefix !== null && prefix !== '') V.string(prefix, 'prefix');
|
|
3350
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
3351
|
+
if (prefix && !V.isCString(prefix)) return false;
|
|
2959
3352
|
const addrPtr = allocString(wasm, address);
|
|
2960
3353
|
const prefixPtr = prefix ? allocString(wasm, prefix) : 0;
|
|
2961
3354
|
try {
|
|
@@ -2984,15 +3377,19 @@ function createModule(wasm) {
|
|
|
2984
3377
|
|
|
2985
3378
|
signAmino(doc, privateKey) {
|
|
2986
3379
|
const signAminoFn = requireWasmFunction(wasm, '_hd_cosmos_sign_amino');
|
|
3380
|
+
if (doc === null || typeof doc !== 'object') {
|
|
3381
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'sign doc must be an object');
|
|
3382
|
+
}
|
|
3383
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
2987
3384
|
// Serialize the sign doc to canonical JSON bytes
|
|
2988
3385
|
const jsonStr = JSON.stringify(doc);
|
|
2989
3386
|
const enc = new TextEncoder();
|
|
2990
3387
|
const docBytes = enc.encode(jsonStr);
|
|
2991
|
-
const docPtr = allocAndCopy(wasm, docBytes);
|
|
2992
|
-
const keyPtr = allocAndCopy(wasm, privateKey);
|
|
3388
|
+
const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, docBytes);
|
|
3389
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
2993
3390
|
const sigPtr = wasm._hd_alloc(64);
|
|
2994
3391
|
try {
|
|
2995
|
-
const result = signAminoFn(docPtr,
|
|
3392
|
+
const result = signAminoFn(docPtr, docLen, keyPtr, sigPtr, 64);
|
|
2996
3393
|
checkResult(result);
|
|
2997
3394
|
return readBytes(wasm, sigPtr, 64);
|
|
2998
3395
|
} finally {
|
|
@@ -3009,11 +3406,13 @@ function createModule(wasm) {
|
|
|
3009
3406
|
// (bodyBytes, authInfoBytes, chainId, accountNumber, privateKey) forms
|
|
3010
3407
|
if (args.length === 2) {
|
|
3011
3408
|
const [signDocBytes, privateKey] = args;
|
|
3012
|
-
|
|
3013
|
-
|
|
3409
|
+
V.bytes(signDocBytes, 'signDocBytes');
|
|
3410
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
3411
|
+
const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, signDocBytes);
|
|
3412
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3014
3413
|
const sigPtr = wasm._hd_alloc(64);
|
|
3015
3414
|
try {
|
|
3016
|
-
const result = signDirectFn(docPtr,
|
|
3415
|
+
const result = signDirectFn(docPtr, docLen, keyPtr, sigPtr, 64);
|
|
3017
3416
|
checkResult(result);
|
|
3018
3417
|
return readBytes(wasm, sigPtr, 64);
|
|
3019
3418
|
} finally {
|
|
@@ -3024,7 +3423,16 @@ function createModule(wasm) {
|
|
|
3024
3423
|
}
|
|
3025
3424
|
} else {
|
|
3026
3425
|
// 5-arg form: build SignDoc from body, authInfo, chainId, accountNumber
|
|
3027
|
-
|
|
3426
|
+
if (args.length !== 5) {
|
|
3427
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'signDirect takes (signDocBytes, privateKey) or (bodyBytes, authInfoBytes, chainId, accountNumber, privateKey)');
|
|
3428
|
+
}
|
|
3429
|
+
const [bodyArg, authInfoArg, chainId, accountNumber, privateKey] = args;
|
|
3430
|
+
// Copies read once: the lengths below size the SignDoc.
|
|
3431
|
+
const bodyBytes = V.snapshot(bodyArg, 'bodyBytes');
|
|
3432
|
+
const authInfoBytes = V.snapshot(authInfoArg, 'authInfoBytes');
|
|
3433
|
+
V.string(chainId, 'chainId');
|
|
3434
|
+
V.unsignedBigInt(accountNumber, 'accountNumber', 64);
|
|
3435
|
+
V.privateKey(wasm, privateKey, Curve.SECP256K1);
|
|
3028
3436
|
// Build a minimal SignDoc by concatenating the fields
|
|
3029
3437
|
// The C function takes raw signDoc bytes, so we need to construct them
|
|
3030
3438
|
// For protobuf SignDoc: bodyBytes + authInfoBytes + chainId + accountNumber
|
|
@@ -3043,11 +3451,11 @@ function createModule(wasm) {
|
|
|
3043
3451
|
for (let i = 0; i < 8; i++) {
|
|
3044
3452
|
signDoc[off + i] = Number((accNum >> BigInt(i * 8)) & 0xFFn);
|
|
3045
3453
|
}
|
|
3046
|
-
const docPtr = allocAndCopy(wasm, signDoc);
|
|
3047
|
-
const keyPtr = allocAndCopy(wasm, privateKey);
|
|
3454
|
+
const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, signDoc);
|
|
3455
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3048
3456
|
const sigPtr = wasm._hd_alloc(64);
|
|
3049
3457
|
try {
|
|
3050
|
-
const result = signDirectFn(docPtr,
|
|
3458
|
+
const result = signDirectFn(docPtr, docLen, keyPtr, sigPtr, 64);
|
|
3051
3459
|
checkResult(result);
|
|
3052
3460
|
return readBytes(wasm, sigPtr, 64);
|
|
3053
3461
|
} finally {
|
|
@@ -3061,11 +3469,15 @@ function createModule(wasm) {
|
|
|
3061
3469
|
|
|
3062
3470
|
verifySignature(message, signature, publicKey) {
|
|
3063
3471
|
const verifyFn = requireWasmFunction(wasm, '_hd_cosmos_verify_signature');
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3472
|
+
V.bytes(message, 'message');
|
|
3473
|
+
V.bytes(signature, 'signature');
|
|
3474
|
+
V.bytes(publicKey, 'publicKey');
|
|
3475
|
+
if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 33) return false;
|
|
3476
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3477
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
3478
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
3067
3479
|
try {
|
|
3068
|
-
return verifyFn(msgPtr,
|
|
3480
|
+
return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
|
|
3069
3481
|
} finally {
|
|
3070
3482
|
wasm._hd_dealloc(msgPtr);
|
|
3071
3483
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -3089,10 +3501,12 @@ function createModule(wasm) {
|
|
|
3089
3501
|
const solana = {
|
|
3090
3502
|
getAddress(publicKey) {
|
|
3091
3503
|
const getAddressFn = requireWasmFunction(wasm, '_hd_sol_address');
|
|
3092
|
-
|
|
3504
|
+
// Any 32 bytes: Solana addresses include off-curve program addresses.
|
|
3505
|
+
V.bytesOfLength(publicKey, 32, 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
|
|
3506
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
3093
3507
|
const outPtr = wasm._hd_alloc(64);
|
|
3094
3508
|
try {
|
|
3095
|
-
const result = getAddressFn(pubPtr,
|
|
3509
|
+
const result = getAddressFn(pubPtr, pubLen, outPtr, 64);
|
|
3096
3510
|
checkResult(result);
|
|
3097
3511
|
return readString(wasm, outPtr);
|
|
3098
3512
|
} finally {
|
|
@@ -3103,6 +3517,7 @@ function createModule(wasm) {
|
|
|
3103
3517
|
|
|
3104
3518
|
validateAddress(address) {
|
|
3105
3519
|
const validateFn = requireWasmFunction(wasm, '_hd_sol_validate_address');
|
|
3520
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
3106
3521
|
const addrPtr = allocString(wasm, address);
|
|
3107
3522
|
try {
|
|
3108
3523
|
return validateFn(addrPtr) === 0;
|
|
@@ -3113,11 +3528,13 @@ function createModule(wasm) {
|
|
|
3113
3528
|
|
|
3114
3529
|
signMessage(message, privateKey) {
|
|
3115
3530
|
const signFn = requireWasmFunction(wasm, '_hd_sol_sign_message');
|
|
3116
|
-
|
|
3117
|
-
|
|
3531
|
+
V.bytes(message, 'message');
|
|
3532
|
+
V.privateKey(wasm, privateKey, Curve.ED25519);
|
|
3533
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3534
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3118
3535
|
const sigPtr = wasm._hd_alloc(64);
|
|
3119
3536
|
try {
|
|
3120
|
-
const result = signFn(msgPtr,
|
|
3537
|
+
const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 64);
|
|
3121
3538
|
checkResult(result);
|
|
3122
3539
|
return readBytes(wasm, sigPtr, 64);
|
|
3123
3540
|
} finally {
|
|
@@ -3130,11 +3547,15 @@ function createModule(wasm) {
|
|
|
3130
3547
|
|
|
3131
3548
|
verifyMessage(message, signature, publicKey) {
|
|
3132
3549
|
const verifyFn = requireWasmFunction(wasm, '_hd_sol_verify_message');
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3550
|
+
V.bytes(message, 'message');
|
|
3551
|
+
V.bytes(signature, 'signature');
|
|
3552
|
+
V.bytes(publicKey, 'publicKey');
|
|
3553
|
+
if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
|
|
3554
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3555
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
3556
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
3136
3557
|
try {
|
|
3137
|
-
return verifyFn(msgPtr,
|
|
3558
|
+
return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
|
|
3138
3559
|
} finally {
|
|
3139
3560
|
wasm._hd_dealloc(msgPtr);
|
|
3140
3561
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -3158,7 +3579,8 @@ function createModule(wasm) {
|
|
|
3158
3579
|
|
|
3159
3580
|
derivePubkey(privateKey) {
|
|
3160
3581
|
const fn = requireWasmFunction(wasm, '_hd_sol_derive_pubkey');
|
|
3161
|
-
|
|
3582
|
+
V.privateKey(wasm, privateKey, Curve.ED25519);
|
|
3583
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3162
3584
|
const outPtr = wasm._hd_alloc(32);
|
|
3163
3585
|
try {
|
|
3164
3586
|
const result = fn(keyPtr, outPtr, 32);
|
|
@@ -3173,21 +3595,34 @@ function createModule(wasm) {
|
|
|
3173
3595
|
|
|
3174
3596
|
findPDA(programId, seeds) {
|
|
3175
3597
|
const fn = requireWasmFunction(wasm, '_hd_sol_find_pda');
|
|
3176
|
-
|
|
3598
|
+
// The export reads a 32-byte program id; Solana allows up to 16 seeds
|
|
3599
|
+
// of up to 32 bytes.
|
|
3600
|
+
V.bytesOfLength(programId, 32, 'programId', ErrorCode.INVALID_PUBLIC_KEY);
|
|
3601
|
+
// The array and each seed are read once (an array or a Proxy could
|
|
3602
|
+
// answer a second read differently): the copies' lengths go to the heap.
|
|
3603
|
+
const count = Array.isArray(seeds) ? seeds.length : -1;
|
|
3604
|
+
if (!Number.isInteger(count) || count < 0 || count > 16) {
|
|
3605
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'seeds must be an array of at most 16 seeds');
|
|
3606
|
+
}
|
|
3607
|
+
const seedCopies = [];
|
|
3608
|
+
for (let i = 0; i < count; i++) {
|
|
3609
|
+
seedCopies.push(V.snapshot(V.bytesInRange(seeds[i], 0, 32, 'seed'), 'seed'));
|
|
3610
|
+
}
|
|
3611
|
+
const { ptr: progPtr } = allocAndCopy(wasm, programId);
|
|
3177
3612
|
let totalLen = 0;
|
|
3178
|
-
for (const s of
|
|
3613
|
+
for (const s of seedCopies) totalLen += s.length;
|
|
3179
3614
|
const flatSeeds = new Uint8Array(totalLen);
|
|
3180
3615
|
let off = 0;
|
|
3181
|
-
for (const s of
|
|
3182
|
-
const seedsPtr = totalLen > 0 ? allocAndCopy(wasm, flatSeeds) : 0;
|
|
3183
|
-
const seedLensPtr = wasm._hd_alloc(
|
|
3184
|
-
for (let i = 0; i <
|
|
3185
|
-
wasm.setValue(seedLensPtr + i * 4,
|
|
3616
|
+
for (const s of seedCopies) { flatSeeds.set(s, off); off += s.length; }
|
|
3617
|
+
const { ptr: seedsPtr } = totalLen > 0 ? allocAndCopy(wasm, flatSeeds) : { ptr: 0 };
|
|
3618
|
+
const seedLensPtr = wasm._hd_alloc(seedCopies.length * 4);
|
|
3619
|
+
for (let i = 0; i < seedCopies.length; i++) {
|
|
3620
|
+
wasm.setValue(seedLensPtr + i * 4, seedCopies[i].length, 'i32');
|
|
3186
3621
|
}
|
|
3187
3622
|
const addrPtr = wasm._hd_alloc(64);
|
|
3188
3623
|
const bumpPtr = wasm._hd_alloc(1);
|
|
3189
3624
|
try {
|
|
3190
|
-
const result = fn(progPtr, seedsPtr || 0, totalLen, seedLensPtr,
|
|
3625
|
+
const result = fn(progPtr, seedsPtr || 0, totalLen, seedLensPtr, seedCopies.length, addrPtr, 64, bumpPtr);
|
|
3191
3626
|
checkResult(result);
|
|
3192
3627
|
return {
|
|
3193
3628
|
address: readString(wasm, addrPtr),
|
|
@@ -3230,10 +3665,12 @@ function createModule(wasm) {
|
|
|
3230
3665
|
const polkadot = {
|
|
3231
3666
|
getAddress(publicKey, ss58Prefix = 0) {
|
|
3232
3667
|
const getAddressFn = requireWasmFunction(wasm, '_hd_dot_address');
|
|
3233
|
-
|
|
3668
|
+
V.bytesOfLength(publicKey, 32, 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
|
|
3669
|
+
V.integer(ss58Prefix, 'ss58Prefix', 0, 16383);
|
|
3670
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
3234
3671
|
const outPtr = wasm._hd_alloc(64);
|
|
3235
3672
|
try {
|
|
3236
|
-
const result = getAddressFn(pubPtr,
|
|
3673
|
+
const result = getAddressFn(pubPtr, pubLen, ss58Prefix, outPtr, 64);
|
|
3237
3674
|
checkResult(result);
|
|
3238
3675
|
return readString(wasm, outPtr);
|
|
3239
3676
|
} finally {
|
|
@@ -3244,6 +3681,8 @@ function createModule(wasm) {
|
|
|
3244
3681
|
|
|
3245
3682
|
validateAddress(address, prefix) {
|
|
3246
3683
|
const validateFn = requireWasmFunction(wasm, '_hd_dot_validate_address');
|
|
3684
|
+
if (prefix !== undefined) V.integer(prefix, 'prefix', 0, 16383);
|
|
3685
|
+
if (!V.isCString(V.string(address, 'address'))) return false;
|
|
3247
3686
|
const addrPtr = allocString(wasm, address);
|
|
3248
3687
|
try {
|
|
3249
3688
|
return validateFn(addrPtr, prefix !== undefined ? prefix : -1) === 0;
|
|
@@ -3273,6 +3712,7 @@ function createModule(wasm) {
|
|
|
3273
3712
|
|
|
3274
3713
|
convertPrefix(address, newPrefix) {
|
|
3275
3714
|
const convertFn = requireWasmFunction(wasm, '_hd_dot_convert_prefix');
|
|
3715
|
+
V.integer(newPrefix, 'newPrefix', 0, 16383);
|
|
3276
3716
|
const addrPtr = allocString(wasm, address);
|
|
3277
3717
|
const outPtr = wasm._hd_alloc(64);
|
|
3278
3718
|
try {
|
|
@@ -3287,7 +3727,8 @@ function createModule(wasm) {
|
|
|
3287
3727
|
|
|
3288
3728
|
derivePubkey(privateKey) {
|
|
3289
3729
|
const fn = requireWasmFunction(wasm, '_hd_dot_derive_pubkey');
|
|
3290
|
-
|
|
3730
|
+
V.privateKey(wasm, privateKey, Curve.ED25519);
|
|
3731
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3291
3732
|
const outPtr = wasm._hd_alloc(32);
|
|
3292
3733
|
try {
|
|
3293
3734
|
const result = fn(keyPtr, outPtr, 32);
|
|
@@ -3302,11 +3743,13 @@ function createModule(wasm) {
|
|
|
3302
3743
|
|
|
3303
3744
|
signMessage(message, privateKey) {
|
|
3304
3745
|
const signFn = requireWasmFunction(wasm, '_hd_dot_sign_message');
|
|
3305
|
-
|
|
3306
|
-
|
|
3746
|
+
V.bytes(message, 'message');
|
|
3747
|
+
V.privateKey(wasm, privateKey, Curve.ED25519);
|
|
3748
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3749
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
|
|
3307
3750
|
const sigPtr = wasm._hd_alloc(64);
|
|
3308
3751
|
try {
|
|
3309
|
-
const result = signFn(msgPtr,
|
|
3752
|
+
const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 64);
|
|
3310
3753
|
checkResult(result);
|
|
3311
3754
|
return readBytes(wasm, sigPtr, 64);
|
|
3312
3755
|
} finally {
|
|
@@ -3319,11 +3762,15 @@ function createModule(wasm) {
|
|
|
3319
3762
|
|
|
3320
3763
|
verifyMessage(message, signature, publicKey) {
|
|
3321
3764
|
const verifyFn = requireWasmFunction(wasm, '_hd_dot_verify_message');
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3765
|
+
V.bytes(message, 'message');
|
|
3766
|
+
V.bytes(signature, 'signature');
|
|
3767
|
+
V.bytes(publicKey, 'publicKey');
|
|
3768
|
+
if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
|
|
3769
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3770
|
+
const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
|
|
3771
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
3325
3772
|
try {
|
|
3326
|
-
return verifyFn(msgPtr,
|
|
3773
|
+
return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
|
|
3327
3774
|
} finally {
|
|
3328
3775
|
wasm._hd_dealloc(msgPtr);
|
|
3329
3776
|
wasm._hd_dealloc(sigPtr);
|
|
@@ -3419,10 +3866,10 @@ function createModule(wasm) {
|
|
|
3419
3866
|
|
|
3420
3867
|
async signTransaction(path, transaction) {
|
|
3421
3868
|
const pathPtr = allocString(wasm, path);
|
|
3422
|
-
const txPtr = allocAndCopy(wasm, transaction);
|
|
3869
|
+
const { ptr: txPtr, len: txLen } = allocAndCopy(wasm, transaction);
|
|
3423
3870
|
const sigPtr = wasm._hd_alloc(128);
|
|
3424
3871
|
try {
|
|
3425
|
-
const result = wasm._hd_hw_sign_transaction(this._handle, pathPtr, txPtr,
|
|
3872
|
+
const result = wasm._hd_hw_sign_transaction(this._handle, pathPtr, txPtr, txLen, sigPtr, 128);
|
|
3426
3873
|
checkResult(result);
|
|
3427
3874
|
return readBytes(wasm, sigPtr, 64);
|
|
3428
3875
|
} finally {
|
|
@@ -3487,13 +3934,13 @@ function createModule(wasm) {
|
|
|
3487
3934
|
addWallet(seed, name) {
|
|
3488
3935
|
const addWalletFn = getWasmFunction(wasm, '_hd_keyring_add_wallet');
|
|
3489
3936
|
if (!addWalletFn) return '';
|
|
3490
|
-
const seedPtr = allocAndCopy(wasm, seed);
|
|
3937
|
+
const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
|
|
3491
3938
|
const namePtr = name ? allocString(wasm, name) : 0;
|
|
3492
3939
|
try {
|
|
3493
|
-
const ptr = addWalletFn(this._handle, seedPtr,
|
|
3940
|
+
const ptr = addWalletFn(this._handle, seedPtr, seedLen, namePtr);
|
|
3494
3941
|
return readString(wasm, ptr);
|
|
3495
3942
|
} finally {
|
|
3496
|
-
wasm._hd_secure_wipe(seedPtr,
|
|
3943
|
+
wasm._hd_secure_wipe(seedPtr, seedLen);
|
|
3497
3944
|
wasm._hd_dealloc(seedPtr);
|
|
3498
3945
|
if (namePtr) wasm._hd_dealloc(namePtr);
|
|
3499
3946
|
}
|
|
@@ -3531,10 +3978,10 @@ function createModule(wasm) {
|
|
|
3531
3978
|
const signTransactionFn = requireWasmFunction(wasm, '_hd_keyring_sign_transaction', ErrorCode.NOT_SUPPORTED, 'Keyring is not available in the WASM build');
|
|
3532
3979
|
const idPtr = allocString(wasm, walletId);
|
|
3533
3980
|
const pathPtr = allocString(wasm, path);
|
|
3534
|
-
const txPtr = allocAndCopy(wasm, transaction);
|
|
3981
|
+
const { ptr: txPtr, len: txLen } = allocAndCopy(wasm, transaction);
|
|
3535
3982
|
const sigPtr = wasm._hd_alloc(128);
|
|
3536
3983
|
try {
|
|
3537
|
-
const result = signTransactionFn(this._handle, idPtr, pathPtr, txPtr,
|
|
3984
|
+
const result = signTransactionFn(this._handle, idPtr, pathPtr, txPtr, txLen, sigPtr, 128);
|
|
3538
3985
|
checkResult(result);
|
|
3539
3986
|
return readBytes(wasm, sigPtr, 64);
|
|
3540
3987
|
} finally {
|
|
@@ -3549,10 +3996,10 @@ function createModule(wasm) {
|
|
|
3549
3996
|
const signMessageFn = requireWasmFunction(wasm, '_hd_keyring_sign_message', ErrorCode.NOT_SUPPORTED, 'Keyring is not available in the WASM build');
|
|
3550
3997
|
const idPtr = allocString(wasm, walletId);
|
|
3551
3998
|
const pathPtr = allocString(wasm, path);
|
|
3552
|
-
const msgPtr = allocAndCopy(wasm, message);
|
|
3999
|
+
const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
|
|
3553
4000
|
const sigPtr = wasm._hd_alloc(128);
|
|
3554
4001
|
try {
|
|
3555
|
-
const result = signMessageFn(this._handle, idPtr, pathPtr, msgPtr,
|
|
4002
|
+
const result = signMessageFn(this._handle, idPtr, pathPtr, msgPtr, msgLen, sigPtr, 128);
|
|
3556
4003
|
checkResult(result);
|
|
3557
4004
|
return readBytes(wasm, sigPtr, 64);
|
|
3558
4005
|
} finally {
|
|
@@ -3587,11 +4034,10 @@ function createModule(wasm) {
|
|
|
3587
4034
|
const utils = {
|
|
3588
4035
|
// Hashing
|
|
3589
4036
|
sha256(data) {
|
|
3590
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4037
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3591
4038
|
const hashPtr = wasm._hd_alloc(32);
|
|
3592
4039
|
try {
|
|
3593
|
-
|
|
3594
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4040
|
+
checkLength(wasm._hd_hash_sha256(dataPtr, dataLen, hashPtr, 32), 32);
|
|
3595
4041
|
return readBytes(wasm, hashPtr, 32);
|
|
3596
4042
|
} finally {
|
|
3597
4043
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3600,11 +4046,10 @@ function createModule(wasm) {
|
|
|
3600
4046
|
},
|
|
3601
4047
|
|
|
3602
4048
|
sha512(data) {
|
|
3603
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4049
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3604
4050
|
const hashPtr = wasm._hd_alloc(64);
|
|
3605
4051
|
try {
|
|
3606
|
-
|
|
3607
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4052
|
+
checkLength(wasm._hd_hash_sha512(dataPtr, dataLen, hashPtr, 64), 64);
|
|
3608
4053
|
return readBytes(wasm, hashPtr, 64);
|
|
3609
4054
|
} finally {
|
|
3610
4055
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3613,11 +4058,10 @@ function createModule(wasm) {
|
|
|
3613
4058
|
},
|
|
3614
4059
|
|
|
3615
4060
|
keccak256(data) {
|
|
3616
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4061
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3617
4062
|
const hashPtr = wasm._hd_alloc(32);
|
|
3618
4063
|
try {
|
|
3619
|
-
|
|
3620
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4064
|
+
checkLength(wasm._hd_hash_keccak256(dataPtr, dataLen, hashPtr, 32), 32);
|
|
3621
4065
|
return readBytes(wasm, hashPtr, 32);
|
|
3622
4066
|
} finally {
|
|
3623
4067
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3626,11 +4070,10 @@ function createModule(wasm) {
|
|
|
3626
4070
|
},
|
|
3627
4071
|
|
|
3628
4072
|
ripemd160(data) {
|
|
3629
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4073
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3630
4074
|
const hashPtr = wasm._hd_alloc(20);
|
|
3631
4075
|
try {
|
|
3632
|
-
|
|
3633
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4076
|
+
checkLength(wasm._hd_hash_ripemd160(dataPtr, dataLen, hashPtr, 20), 20);
|
|
3634
4077
|
return readBytes(wasm, hashPtr, 20);
|
|
3635
4078
|
} finally {
|
|
3636
4079
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3639,11 +4082,10 @@ function createModule(wasm) {
|
|
|
3639
4082
|
},
|
|
3640
4083
|
|
|
3641
4084
|
hash160(data) {
|
|
3642
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4085
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3643
4086
|
const hashPtr = wasm._hd_alloc(20);
|
|
3644
4087
|
try {
|
|
3645
|
-
|
|
3646
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4088
|
+
checkLength(wasm._hd_hash_hash160(dataPtr, dataLen, hashPtr, 20), 20);
|
|
3647
4089
|
return readBytes(wasm, hashPtr, 20);
|
|
3648
4090
|
} finally {
|
|
3649
4091
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3652,11 +4094,12 @@ function createModule(wasm) {
|
|
|
3652
4094
|
},
|
|
3653
4095
|
|
|
3654
4096
|
blake2b(data, outputLength = 32) {
|
|
3655
|
-
|
|
4097
|
+
// The export checks the size against its 64-byte buffer.
|
|
4098
|
+
V.integer(outputLength, 'outputLength', 0, 0x7fffffff);
|
|
4099
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3656
4100
|
const hashPtr = wasm._hd_alloc(64);
|
|
3657
4101
|
try {
|
|
3658
|
-
|
|
3659
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4102
|
+
checkLength(wasm._hd_hash_blake2b(dataPtr, dataLen, hashPtr, 64, outputLength), outputLength);
|
|
3660
4103
|
return readBytes(wasm, hashPtr, outputLength);
|
|
3661
4104
|
} finally {
|
|
3662
4105
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3665,11 +4108,12 @@ function createModule(wasm) {
|
|
|
3665
4108
|
},
|
|
3666
4109
|
|
|
3667
4110
|
blake2s(data, outputLength = 32) {
|
|
3668
|
-
|
|
4111
|
+
// The export checks the size against its 32-byte buffer.
|
|
4112
|
+
V.integer(outputLength, 'outputLength', 0, 0x7fffffff);
|
|
4113
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3669
4114
|
const hashPtr = wasm._hd_alloc(32);
|
|
3670
4115
|
try {
|
|
3671
|
-
|
|
3672
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4116
|
+
checkLength(wasm._hd_hash_blake2s(dataPtr, dataLen, hashPtr, 32, outputLength), outputLength);
|
|
3673
4117
|
return readBytes(wasm, hashPtr, outputLength);
|
|
3674
4118
|
} finally {
|
|
3675
4119
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3679,16 +4123,20 @@ function createModule(wasm) {
|
|
|
3679
4123
|
|
|
3680
4124
|
// Key derivation (sync - uses Crypto++ in WASM)
|
|
3681
4125
|
hkdf(ikm, salt, info, length) {
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
4126
|
+
V.bytes(ikm, 'ikm');
|
|
4127
|
+
V.bytes(salt, 'salt');
|
|
4128
|
+
V.bytes(info, 'info');
|
|
4129
|
+
// The export refuses more than 255 blocks of output.
|
|
4130
|
+
V.integer(length, 'length', 1, 0x3fffffff);
|
|
4131
|
+
const { ptr: ikmPtr, len: ikmLen } = allocAndCopy(wasm, ikm);
|
|
4132
|
+
const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
|
|
4133
|
+
const { ptr: infoPtr, len: infoLen } = allocAndCopy(wasm, info);
|
|
3685
4134
|
const outPtr = wasm._hd_alloc(length);
|
|
3686
4135
|
try {
|
|
3687
|
-
|
|
3688
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4136
|
+
checkLength(wasm._hd_kdf_hkdf(ikmPtr, ikmLen, saltPtr, saltLen, infoPtr, infoLen, outPtr, length), length);
|
|
3689
4137
|
return readBytes(wasm, outPtr, length);
|
|
3690
4138
|
} finally {
|
|
3691
|
-
wasm._hd_secure_wipe(ikmPtr,
|
|
4139
|
+
wasm._hd_secure_wipe(ikmPtr, ikmLen);
|
|
3692
4140
|
wasm._hd_dealloc(ikmPtr);
|
|
3693
4141
|
wasm._hd_dealloc(saltPtr);
|
|
3694
4142
|
wasm._hd_dealloc(infoPtr);
|
|
@@ -3707,32 +4155,35 @@ function createModule(wasm) {
|
|
|
3707
4155
|
* @returns {{ciphertext: Uint8Array, tag: Uint8Array}}
|
|
3708
4156
|
*/
|
|
3709
4157
|
encrypt(key, plaintext, iv, aad = new Uint8Array(0)) {
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
const
|
|
3716
|
-
const
|
|
3717
|
-
const
|
|
4158
|
+
V.bytesOfLength(key, 32, 'Key');
|
|
4159
|
+
V.bytes(plaintext, 'plaintext');
|
|
4160
|
+
V.bytesOfLength(iv, 12, 'IV');
|
|
4161
|
+
V.bytes(aad, 'aad');
|
|
4162
|
+
|
|
4163
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
|
|
4164
|
+
const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
|
|
4165
|
+
const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
|
|
4166
|
+
const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
|
|
4167
|
+
const ctPtr = wasm._hd_alloc(ptLen);
|
|
3718
4168
|
const tagPtr = wasm._hd_alloc(16);
|
|
3719
4169
|
|
|
3720
4170
|
try {
|
|
3721
4171
|
const result = wasm._hd_aes_gcm_encrypt(
|
|
3722
|
-
keyPtr,
|
|
3723
|
-
ptPtr,
|
|
3724
|
-
ivPtr,
|
|
3725
|
-
aadPtr,
|
|
4172
|
+
keyPtr, keyLen,
|
|
4173
|
+
ptPtr, ptLen,
|
|
4174
|
+
ivPtr, ivLen,
|
|
4175
|
+
aadPtr, aadLen,
|
|
3726
4176
|
ctPtr, tagPtr
|
|
3727
4177
|
);
|
|
3728
|
-
|
|
4178
|
+
checkLength(result, ptLen);
|
|
3729
4179
|
|
|
3730
4180
|
return {
|
|
3731
|
-
ciphertext: readBytes(wasm, ctPtr,
|
|
4181
|
+
ciphertext: readBytes(wasm, ctPtr, ptLen),
|
|
3732
4182
|
tag: readBytes(wasm, tagPtr, 16)
|
|
3733
4183
|
};
|
|
3734
4184
|
} finally {
|
|
3735
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4185
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4186
|
+
wasm._hd_secure_wipe(ptPtr, ptLen);
|
|
3736
4187
|
wasm._hd_dealloc(keyPtr);
|
|
3737
4188
|
wasm._hd_dealloc(ptPtr);
|
|
3738
4189
|
wasm._hd_dealloc(ivPtr);
|
|
@@ -3753,40 +4204,44 @@ function createModule(wasm) {
|
|
|
3753
4204
|
* @throws {HDWalletError} If authentication fails
|
|
3754
4205
|
*/
|
|
3755
4206
|
decrypt(key, ciphertext, tag, iv, aad = new Uint8Array(0)) {
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
const
|
|
3763
|
-
const
|
|
3764
|
-
const
|
|
3765
|
-
const
|
|
4207
|
+
V.bytesOfLength(key, 32, 'Key');
|
|
4208
|
+
V.bytes(ciphertext, 'ciphertext');
|
|
4209
|
+
V.bytesOfLength(tag, 16, 'Tag');
|
|
4210
|
+
V.bytesOfLength(iv, 12, 'IV');
|
|
4211
|
+
V.bytes(aad, 'aad');
|
|
4212
|
+
|
|
4213
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
|
|
4214
|
+
const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
|
|
4215
|
+
const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
|
|
4216
|
+
const { ptr: tagPtr, len: tagLen } = allocAndCopy(wasm, tag);
|
|
4217
|
+
const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
|
|
4218
|
+
const ptPtr = wasm._hd_alloc(ctLen);
|
|
3766
4219
|
|
|
3767
4220
|
try {
|
|
3768
4221
|
const result = wasm._hd_aes_gcm_decrypt(
|
|
3769
|
-
keyPtr,
|
|
3770
|
-
ctPtr,
|
|
3771
|
-
ivPtr,
|
|
3772
|
-
aadPtr,
|
|
4222
|
+
keyPtr, keyLen,
|
|
4223
|
+
ctPtr, ctLen,
|
|
4224
|
+
ivPtr, ivLen,
|
|
4225
|
+
aadPtr, aadLen,
|
|
3773
4226
|
tagPtr, ptPtr
|
|
3774
4227
|
);
|
|
3775
|
-
if (result
|
|
4228
|
+
if (result !== ctLen) {
|
|
4229
|
+
const code = nativeErrorCode(result);
|
|
3776
4230
|
throw new HDWalletError(
|
|
3777
|
-
|
|
3778
|
-
|
|
4231
|
+
code,
|
|
4232
|
+
code === ErrorCode.VERIFICATION_FAILED ? 'AES-GCM authentication failed' : undefined
|
|
3779
4233
|
);
|
|
3780
4234
|
}
|
|
3781
4235
|
|
|
3782
|
-
return readBytes(wasm, ptPtr,
|
|
4236
|
+
return readBytes(wasm, ptPtr, ctLen);
|
|
3783
4237
|
} finally {
|
|
3784
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4238
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
3785
4239
|
wasm._hd_dealloc(keyPtr);
|
|
3786
4240
|
wasm._hd_dealloc(ctPtr);
|
|
3787
4241
|
wasm._hd_dealloc(ivPtr);
|
|
3788
4242
|
wasm._hd_dealloc(tagPtr);
|
|
3789
4243
|
if (aadPtr) wasm._hd_dealloc(aadPtr);
|
|
4244
|
+
wasm._hd_secure_wipe(ptPtr, ctLen);
|
|
3790
4245
|
wasm._hd_dealloc(ptPtr);
|
|
3791
4246
|
}
|
|
3792
4247
|
}
|
|
@@ -3794,6 +4249,7 @@ function createModule(wasm) {
|
|
|
3794
4249
|
|
|
3795
4250
|
/** Generate cryptographically secure random bytes */
|
|
3796
4251
|
getRandomBytes(length) {
|
|
4252
|
+
V.integer(length, 'length', 0, 0x7fffffff);
|
|
3797
4253
|
const bytes = new Uint8Array(length);
|
|
3798
4254
|
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
|
|
3799
4255
|
globalThis.crypto.getRandomValues(bytes);
|
|
@@ -3817,15 +4273,18 @@ function createModule(wasm) {
|
|
|
3817
4273
|
},
|
|
3818
4274
|
|
|
3819
4275
|
pbkdf2(password, salt, iterations, length) {
|
|
3820
|
-
|
|
3821
|
-
|
|
4276
|
+
V.bytes(password, 'password');
|
|
4277
|
+
V.bytes(salt, 'salt');
|
|
4278
|
+
V.integer(iterations, 'iterations', 1, 0x7fffffff);
|
|
4279
|
+
V.integer(length, 'length', 1, 0x3fffffff);
|
|
4280
|
+
const { ptr: pwdPtr, len: pwdLen } = allocAndCopy(wasm, password);
|
|
4281
|
+
const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
|
|
3822
4282
|
const outPtr = wasm._hd_alloc(length);
|
|
3823
4283
|
try {
|
|
3824
|
-
|
|
3825
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4284
|
+
checkLength(wasm._hd_kdf_pbkdf2(pwdPtr, pwdLen, saltPtr, saltLen, iterations, outPtr, length), length);
|
|
3826
4285
|
return readBytes(wasm, outPtr, length);
|
|
3827
4286
|
} finally {
|
|
3828
|
-
wasm._hd_secure_wipe(pwdPtr,
|
|
4287
|
+
wasm._hd_secure_wipe(pwdPtr, pwdLen);
|
|
3829
4288
|
wasm._hd_dealloc(pwdPtr);
|
|
3830
4289
|
wasm._hd_dealloc(saltPtr);
|
|
3831
4290
|
wasm._hd_dealloc(outPtr);
|
|
@@ -3833,15 +4292,20 @@ function createModule(wasm) {
|
|
|
3833
4292
|
},
|
|
3834
4293
|
|
|
3835
4294
|
scrypt(password, salt, n, r, p, length) {
|
|
3836
|
-
|
|
3837
|
-
|
|
4295
|
+
V.bytes(password, 'password');
|
|
4296
|
+
V.bytes(salt, 'salt');
|
|
4297
|
+
V.integer(n, 'n', 2, Number.MAX_SAFE_INTEGER);
|
|
4298
|
+
V.integer(r, 'r', 1, 0x7fffffff);
|
|
4299
|
+
V.integer(p, 'p', 1, 0x7fffffff);
|
|
4300
|
+
V.integer(length, 'length', 1, 0x3fffffff);
|
|
4301
|
+
const { ptr: pwdPtr, len: pwdLen } = allocAndCopy(wasm, password);
|
|
4302
|
+
const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
|
|
3838
4303
|
const outPtr = wasm._hd_alloc(length);
|
|
3839
4304
|
try {
|
|
3840
|
-
|
|
3841
|
-
if (result < 0) throw new HDWalletError(result);
|
|
4305
|
+
checkLength(wasm._hd_kdf_scrypt(pwdPtr, pwdLen, saltPtr, saltLen, BigInt(n), r, p, outPtr, length), length);
|
|
3842
4306
|
return readBytes(wasm, outPtr, length);
|
|
3843
4307
|
} finally {
|
|
3844
|
-
wasm._hd_secure_wipe(pwdPtr,
|
|
4308
|
+
wasm._hd_secure_wipe(pwdPtr, pwdLen);
|
|
3845
4309
|
wasm._hd_dealloc(pwdPtr);
|
|
3846
4310
|
wasm._hd_dealloc(saltPtr);
|
|
3847
4311
|
wasm._hd_dealloc(outPtr);
|
|
@@ -3850,13 +4314,14 @@ function createModule(wasm) {
|
|
|
3850
4314
|
|
|
3851
4315
|
// Encoding
|
|
3852
4316
|
encodeBase58(data) {
|
|
4317
|
+
V.bytes(data, 'data');
|
|
3853
4318
|
const encodeBase58Fn = getWasmFunction(wasm, '_hd_encode_base58');
|
|
3854
4319
|
if (!encodeBase58Fn) {
|
|
3855
4320
|
return encodeBase58Fallback(data);
|
|
3856
4321
|
}
|
|
3857
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4322
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3858
4323
|
try {
|
|
3859
|
-
const ptr = encodeBase58Fn(dataPtr,
|
|
4324
|
+
const ptr = encodeBase58Fn(dataPtr, dataLen);
|
|
3860
4325
|
return readString(wasm, ptr);
|
|
3861
4326
|
} finally {
|
|
3862
4327
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3864,6 +4329,7 @@ function createModule(wasm) {
|
|
|
3864
4329
|
},
|
|
3865
4330
|
|
|
3866
4331
|
decodeBase58(str) {
|
|
4332
|
+
V.string(str, 'str');
|
|
3867
4333
|
const decodeBase58Fn = getWasmFunction(wasm, '_hd_decode_base58');
|
|
3868
4334
|
if (!decodeBase58Fn) {
|
|
3869
4335
|
return decodeBase58Fallback(str);
|
|
@@ -3885,17 +4351,18 @@ function createModule(wasm) {
|
|
|
3885
4351
|
},
|
|
3886
4352
|
|
|
3887
4353
|
encodeBase58Check(data) {
|
|
4354
|
+
V.bytes(data, 'data');
|
|
3888
4355
|
const encodeBase58CheckFn = getWasmFunction(wasm, '_hd_encode_base58check');
|
|
3889
4356
|
if (!encodeBase58CheckFn) {
|
|
3890
4357
|
const checksum = this.sha256(this.sha256(data)).slice(0, 4);
|
|
3891
|
-
const payload = new Uint8Array(data
|
|
4358
|
+
const payload = new Uint8Array(V.byteLength(data) + checksum.length);
|
|
3892
4359
|
payload.set(data, 0);
|
|
3893
|
-
payload.set(checksum, data
|
|
4360
|
+
payload.set(checksum, V.byteLength(data));
|
|
3894
4361
|
return encodeBase58Fallback(payload);
|
|
3895
4362
|
}
|
|
3896
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4363
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3897
4364
|
try {
|
|
3898
|
-
const ptr = encodeBase58CheckFn(dataPtr,
|
|
4365
|
+
const ptr = encodeBase58CheckFn(dataPtr, dataLen);
|
|
3899
4366
|
return readString(wasm, ptr);
|
|
3900
4367
|
} finally {
|
|
3901
4368
|
wasm._hd_dealloc(dataPtr);
|
|
@@ -3903,6 +4370,7 @@ function createModule(wasm) {
|
|
|
3903
4370
|
},
|
|
3904
4371
|
|
|
3905
4372
|
decodeBase58Check(str) {
|
|
4373
|
+
V.string(str, 'str');
|
|
3906
4374
|
const decodeBase58CheckFn = getWasmFunction(wasm, '_hd_decode_base58check');
|
|
3907
4375
|
if (!decodeBase58CheckFn) {
|
|
3908
4376
|
const decoded = decodeBase58Fallback(str);
|
|
@@ -3936,14 +4404,16 @@ function createModule(wasm) {
|
|
|
3936
4404
|
},
|
|
3937
4405
|
|
|
3938
4406
|
encodeBech32(hrp, data) {
|
|
4407
|
+
V.string(hrp, 'hrp');
|
|
4408
|
+
V.bytes(data, 'data');
|
|
3939
4409
|
const encodeBech32Fn = getWasmFunction(wasm, '_hd_encode_bech32');
|
|
3940
4410
|
if (!encodeBech32Fn) {
|
|
3941
4411
|
return encodeBech32Fallback(hrp, data);
|
|
3942
4412
|
}
|
|
3943
4413
|
const hrpPtr = allocString(wasm, hrp);
|
|
3944
|
-
const dataPtr = allocAndCopy(wasm, data);
|
|
4414
|
+
const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
|
|
3945
4415
|
try {
|
|
3946
|
-
const ptr = encodeBech32Fn(hrpPtr, dataPtr,
|
|
4416
|
+
const ptr = encodeBech32Fn(hrpPtr, dataPtr, dataLen);
|
|
3947
4417
|
return readString(wasm, ptr);
|
|
3948
4418
|
} finally {
|
|
3949
4419
|
wasm._hd_dealloc(hrpPtr);
|
|
@@ -3952,6 +4422,7 @@ function createModule(wasm) {
|
|
|
3952
4422
|
},
|
|
3953
4423
|
|
|
3954
4424
|
decodeBech32(str) {
|
|
4425
|
+
V.string(str, 'str');
|
|
3955
4426
|
const decodeBech32Fn = getWasmFunction(wasm, '_hd_decode_bech32');
|
|
3956
4427
|
if (!decodeBech32Fn) {
|
|
3957
4428
|
return decodeBech32Fallback(str);
|
|
@@ -3978,29 +4449,38 @@ function createModule(wasm) {
|
|
|
3978
4449
|
},
|
|
3979
4450
|
|
|
3980
4451
|
encodeHex(data) {
|
|
3981
|
-
return encodeHexFallback(data);
|
|
4452
|
+
return encodeHexFallback(V.bytes(data, 'data'));
|
|
3982
4453
|
},
|
|
3983
4454
|
|
|
3984
4455
|
decodeHex(str) {
|
|
3985
|
-
return decodeHexFallback(str);
|
|
4456
|
+
return decodeHexFallback(V.string(str, 'str'));
|
|
3986
4457
|
},
|
|
3987
4458
|
|
|
3988
4459
|
encodeBase64(data) {
|
|
3989
|
-
return encodeBase64Fallback(data);
|
|
4460
|
+
return encodeBase64Fallback(V.bytes(data, 'data'));
|
|
3990
4461
|
},
|
|
3991
4462
|
|
|
3992
4463
|
decodeBase64(str) {
|
|
3993
|
-
return decodeBase64Fallback(str);
|
|
4464
|
+
return decodeBase64Fallback(V.string(str, 'str'));
|
|
3994
4465
|
},
|
|
3995
4466
|
|
|
3996
4467
|
// Memory
|
|
4468
|
+
/**
|
|
4469
|
+
* Zero, in place, the bytes under any typed array, DataView, ArrayBuffer
|
|
4470
|
+
* or SharedArrayBuffer (any realm), read from its internal slots, never
|
|
4471
|
+
* from overridable properties. Throws INVALID_ARGUMENT for anything else
|
|
4472
|
+
* and for a detached buffer (its bytes are no longer here to wipe).
|
|
4473
|
+
*/
|
|
3997
4474
|
secureWipe(data) {
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
}
|
|
4475
|
+
const region = binaryRegion(data);
|
|
4476
|
+
if (region === null) {
|
|
4477
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT,
|
|
4478
|
+
'secureWipe takes a typed array, DataView, ArrayBuffer or SharedArrayBuffer');
|
|
4003
4479
|
}
|
|
4480
|
+
if (region.detached) {
|
|
4481
|
+
throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'secureWipe: the buffer is detached');
|
|
4482
|
+
}
|
|
4483
|
+
TYPED_ARRAY_FILL.call(new Uint8Array(region.buffer, region.offset, region.length), 0);
|
|
4004
4484
|
}
|
|
4005
4485
|
};
|
|
4006
4486
|
|
|
@@ -4020,6 +4500,7 @@ function createModule(wasm) {
|
|
|
4020
4500
|
|
|
4021
4501
|
generatePrivateKey(curve) {
|
|
4022
4502
|
const fn = requireWasmFunction(wasm, '_hd_x509_generate_private_key');
|
|
4503
|
+
V.curve(curve);
|
|
4023
4504
|
return callDynamicBytesResult(
|
|
4024
4505
|
wasm,
|
|
4025
4506
|
(outPtr, outLenPtr) => fn(curve, outPtr, outLenPtr),
|
|
@@ -4029,22 +4510,23 @@ function createModule(wasm) {
|
|
|
4029
4510
|
|
|
4030
4511
|
exportPrivateKeyPem(curve, privateKey) {
|
|
4031
4512
|
const fn = requireWasmFunction(wasm, '_hd_x509_export_private_key_pem');
|
|
4032
|
-
const keyBytes = toUint8Array(privateKey, 'privateKey');
|
|
4033
|
-
const keyPtr = allocAndCopy(wasm, keyBytes);
|
|
4513
|
+
const keyBytes = V.privateKey(wasm, toUint8Array(privateKey, 'privateKey'), curve);
|
|
4514
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
|
|
4034
4515
|
try {
|
|
4035
4516
|
return callDynamicStringResult(
|
|
4036
4517
|
wasm,
|
|
4037
|
-
(outPtr, outLenPtr) => fn(curve, keyPtr,
|
|
4518
|
+
(outPtr, outLenPtr) => fn(curve, keyPtr, keyLen, outPtr, outLenPtr),
|
|
4038
4519
|
{ wipeOutput: true }
|
|
4039
4520
|
);
|
|
4040
4521
|
} finally {
|
|
4041
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4522
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4042
4523
|
wasm._hd_dealloc(keyPtr);
|
|
4043
4524
|
}
|
|
4044
4525
|
},
|
|
4045
4526
|
|
|
4046
4527
|
importPrivateKeyPem(curve, pem) {
|
|
4047
4528
|
const fn = requireWasmFunction(wasm, '_hd_x509_import_private_key_pem');
|
|
4529
|
+
V.curve(curve);
|
|
4048
4530
|
const pemPtr = allocString(wasm, pem);
|
|
4049
4531
|
const pemLen = wasm.lengthBytesUTF8(pem) + 1;
|
|
4050
4532
|
try {
|
|
@@ -4064,8 +4546,9 @@ function createModule(wasm) {
|
|
|
4064
4546
|
const optionsSpec = serializeX509Options(options);
|
|
4065
4547
|
const optionsPtr = allocString(wasm, optionsSpec);
|
|
4066
4548
|
const optionsLen = wasm.lengthBytesUTF8(optionsSpec) + 1;
|
|
4067
|
-
const keyBytes = toUint8Array(certificatePrivateKey, 'certificatePrivateKey')
|
|
4068
|
-
|
|
4549
|
+
const keyBytes = V.privateKey(wasm, toUint8Array(certificatePrivateKey, 'certificatePrivateKey'),
|
|
4550
|
+
certificateCurve, 'certificatePrivateKey');
|
|
4551
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
|
|
4069
4552
|
|
|
4070
4553
|
try {
|
|
4071
4554
|
const bytes = callDynamicBytesResult(
|
|
@@ -4074,7 +4557,7 @@ function createModule(wasm) {
|
|
|
4074
4557
|
optionsPtr,
|
|
4075
4558
|
certificateCurve,
|
|
4076
4559
|
keyPtr,
|
|
4077
|
-
|
|
4560
|
+
keyLen,
|
|
4078
4561
|
outputEncoding,
|
|
4079
4562
|
outPtr,
|
|
4080
4563
|
outLenPtr
|
|
@@ -4083,7 +4566,7 @@ function createModule(wasm) {
|
|
|
4083
4566
|
return decodeX509CertificateOutput(bytes, outputEncoding);
|
|
4084
4567
|
} finally {
|
|
4085
4568
|
wasm._hd_secure_wipe(optionsPtr, optionsLen);
|
|
4086
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4569
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4087
4570
|
wasm._hd_dealloc(optionsPtr);
|
|
4088
4571
|
wasm._hd_dealloc(keyPtr);
|
|
4089
4572
|
}
|
|
@@ -4104,16 +4587,18 @@ function createModule(wasm) {
|
|
|
4104
4587
|
const optionsSpec = serializeX509Options(options);
|
|
4105
4588
|
const optionsPtr = allocString(wasm, optionsSpec);
|
|
4106
4589
|
const optionsLen = wasm.lengthBytesUTF8(optionsSpec) + 1;
|
|
4107
|
-
const issuerKeyBytes = toUint8Array(issuerPrivateKey, 'issuerPrivateKey')
|
|
4108
|
-
|
|
4590
|
+
const issuerKeyBytes = V.privateKey(wasm, toUint8Array(issuerPrivateKey, 'issuerPrivateKey'),
|
|
4591
|
+
issuerCurve, 'issuerPrivateKey');
|
|
4592
|
+
const { ptr: issuerKeyPtr, len: issuerKeyLen } = allocAndCopy(wasm, issuerKeyBytes);
|
|
4109
4593
|
const issuerCertBytes = normalizeX509CertificateInput(
|
|
4110
4594
|
issuerCertificate,
|
|
4111
4595
|
issuerCertificateEncoding,
|
|
4112
4596
|
'issuerCertificate'
|
|
4113
4597
|
);
|
|
4114
|
-
const issuerCertPtr = allocAndCopy(wasm, issuerCertBytes);
|
|
4115
|
-
const subjectKeyBytes = toUint8Array(subjectPrivateKey, 'subjectPrivateKey')
|
|
4116
|
-
|
|
4598
|
+
const { ptr: issuerCertPtr, len: issuerCertLen } = allocAndCopy(wasm, issuerCertBytes);
|
|
4599
|
+
const subjectKeyBytes = V.privateKey(wasm, toUint8Array(subjectPrivateKey, 'subjectPrivateKey'),
|
|
4600
|
+
subjectCurve, 'subjectPrivateKey');
|
|
4601
|
+
const { ptr: subjectKeyPtr, len: subjectKeyLen } = allocAndCopy(wasm, subjectKeyBytes);
|
|
4117
4602
|
|
|
4118
4603
|
try {
|
|
4119
4604
|
const bytes = callDynamicBytesResult(
|
|
@@ -4122,13 +4607,13 @@ function createModule(wasm) {
|
|
|
4122
4607
|
optionsPtr,
|
|
4123
4608
|
issuerCurve,
|
|
4124
4609
|
issuerKeyPtr,
|
|
4125
|
-
|
|
4610
|
+
issuerKeyLen,
|
|
4126
4611
|
issuerCertPtr,
|
|
4127
|
-
|
|
4612
|
+
issuerCertLen,
|
|
4128
4613
|
issuerCertificateEncoding,
|
|
4129
4614
|
subjectCurve,
|
|
4130
4615
|
subjectKeyPtr,
|
|
4131
|
-
|
|
4616
|
+
subjectKeyLen,
|
|
4132
4617
|
outputEncoding,
|
|
4133
4618
|
outPtr,
|
|
4134
4619
|
outLenPtr
|
|
@@ -4137,8 +4622,8 @@ function createModule(wasm) {
|
|
|
4137
4622
|
return decodeX509CertificateOutput(bytes, outputEncoding);
|
|
4138
4623
|
} finally {
|
|
4139
4624
|
wasm._hd_secure_wipe(optionsPtr, optionsLen);
|
|
4140
|
-
wasm._hd_secure_wipe(issuerKeyPtr,
|
|
4141
|
-
wasm._hd_secure_wipe(subjectKeyPtr,
|
|
4625
|
+
wasm._hd_secure_wipe(issuerKeyPtr, issuerKeyLen);
|
|
4626
|
+
wasm._hd_secure_wipe(subjectKeyPtr, subjectKeyLen);
|
|
4142
4627
|
wasm._hd_dealloc(optionsPtr);
|
|
4143
4628
|
wasm._hd_dealloc(issuerKeyPtr);
|
|
4144
4629
|
wasm._hd_dealloc(issuerCertPtr);
|
|
@@ -4151,14 +4636,14 @@ function createModule(wasm) {
|
|
|
4151
4636
|
assertX509CertificateEncoding(outputEncoding, 'outputEncoding');
|
|
4152
4637
|
const fn = requireWasmFunction(wasm, '_hd_x509_convert_certificate');
|
|
4153
4638
|
const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
|
|
4154
|
-
const certPtr = allocAndCopy(wasm, certBytes);
|
|
4639
|
+
const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
|
|
4155
4640
|
|
|
4156
4641
|
try {
|
|
4157
4642
|
const bytes = callDynamicBytesResult(
|
|
4158
4643
|
wasm,
|
|
4159
4644
|
(outPtr, outLenPtr) => fn(
|
|
4160
4645
|
certPtr,
|
|
4161
|
-
|
|
4646
|
+
certLen,
|
|
4162
4647
|
inputEncoding,
|
|
4163
4648
|
outputEncoding,
|
|
4164
4649
|
outPtr,
|
|
@@ -4175,13 +4660,13 @@ function createModule(wasm) {
|
|
|
4175
4660
|
assertX509CertificateEncoding(inputEncoding, 'inputEncoding');
|
|
4176
4661
|
const fn = requireWasmFunction(wasm, '_hd_x509_parse_certificate_json');
|
|
4177
4662
|
const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
|
|
4178
|
-
const certPtr = allocAndCopy(wasm, certBytes);
|
|
4663
|
+
const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
|
|
4179
4664
|
|
|
4180
4665
|
try {
|
|
4181
4666
|
return normalizeParsedCertificateJson(JSON.parse(
|
|
4182
4667
|
callDynamicStringResult(
|
|
4183
4668
|
wasm,
|
|
4184
|
-
(outPtr, outLenPtr) => fn(certPtr,
|
|
4669
|
+
(outPtr, outLenPtr) => fn(certPtr, certLen, inputEncoding, outPtr, outLenPtr)
|
|
4185
4670
|
)
|
|
4186
4671
|
));
|
|
4187
4672
|
} finally {
|
|
@@ -4193,10 +4678,10 @@ function createModule(wasm) {
|
|
|
4193
4678
|
assertX509CertificateEncoding(inputEncoding, 'inputEncoding');
|
|
4194
4679
|
const fn = requireWasmFunction(wasm, '_hd_x509_verify_wallet_attestation');
|
|
4195
4680
|
const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
|
|
4196
|
-
const certPtr = allocAndCopy(wasm, certBytes);
|
|
4681
|
+
const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
|
|
4197
4682
|
|
|
4198
4683
|
try {
|
|
4199
|
-
const result = fn(certPtr,
|
|
4684
|
+
const result = fn(certPtr, certLen, inputEncoding);
|
|
4200
4685
|
if (result < 0) {
|
|
4201
4686
|
throw new HDWalletError(result);
|
|
4202
4687
|
}
|
|
@@ -4218,36 +4703,38 @@ function createModule(wasm) {
|
|
|
4218
4703
|
assertX509CertificateEncoding(certificateEncoding, 'certificateEncoding');
|
|
4219
4704
|
const fn = requireWasmFunction(wasm, '_hd_x509_export_pkcs12');
|
|
4220
4705
|
const certBytes = normalizeX509CertificateInput(certificate, certificateEncoding);
|
|
4221
|
-
const certPtr = allocAndCopy(wasm, certBytes);
|
|
4222
|
-
const keyBytes = toUint8Array(privateKey, 'privateKey');
|
|
4223
|
-
|
|
4706
|
+
const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
|
|
4707
|
+
const keyBytes = V.privateKey(wasm, toUint8Array(privateKey, 'privateKey'), privateKeyCurve);
|
|
4708
|
+
V.string(password, 'password');
|
|
4709
|
+
V.string(friendlyName, 'friendlyName');
|
|
4710
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
|
|
4224
4711
|
const passwordPtr = allocString(wasm, password);
|
|
4225
4712
|
const passwordLen = wasm.lengthBytesUTF8(password) + 1;
|
|
4226
4713
|
const friendlyNamePtr = allocString(wasm, friendlyName);
|
|
4227
4714
|
const chainBytes = normalizePemInput(chainPem, 'chainPem');
|
|
4228
|
-
const chainPtr = chainBytes
|
|
4715
|
+
const { ptr: chainPtr, len: chainLen } = V.byteLength(chainBytes) > 0 ? allocAndCopy(wasm, chainBytes) : { ptr: 0, len: 0 };
|
|
4229
4716
|
|
|
4230
4717
|
try {
|
|
4231
4718
|
return callDynamicBytesResult(
|
|
4232
4719
|
wasm,
|
|
4233
4720
|
(outPtr, outLenPtr) => fn(
|
|
4234
4721
|
certPtr,
|
|
4235
|
-
|
|
4722
|
+
certLen,
|
|
4236
4723
|
certificateEncoding,
|
|
4237
4724
|
privateKeyCurve,
|
|
4238
4725
|
keyPtr,
|
|
4239
|
-
|
|
4726
|
+
keyLen,
|
|
4240
4727
|
passwordPtr,
|
|
4241
4728
|
friendlyNamePtr,
|
|
4242
4729
|
chainPtr,
|
|
4243
|
-
|
|
4730
|
+
chainLen,
|
|
4244
4731
|
outPtr,
|
|
4245
4732
|
outLenPtr
|
|
4246
4733
|
),
|
|
4247
4734
|
{ wipeOutput: true }
|
|
4248
4735
|
);
|
|
4249
4736
|
} finally {
|
|
4250
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4737
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4251
4738
|
wasm._hd_secure_wipe(passwordPtr, passwordLen);
|
|
4252
4739
|
wasm._hd_dealloc(certPtr);
|
|
4253
4740
|
wasm._hd_dealloc(keyPtr);
|
|
@@ -4262,7 +4749,8 @@ function createModule(wasm) {
|
|
|
4262
4749
|
importPkcs12(pkcs12Bundle, password) {
|
|
4263
4750
|
const fn = requireWasmFunction(wasm, '_hd_x509_import_pkcs12_json');
|
|
4264
4751
|
const bundleBytes = toUint8Array(pkcs12Bundle, 'pkcs12Bundle');
|
|
4265
|
-
|
|
4752
|
+
V.string(password, 'password');
|
|
4753
|
+
const { ptr: bundlePtr, len: bundleLen } = allocAndCopy(wasm, bundleBytes);
|
|
4266
4754
|
const passwordPtr = allocString(wasm, password);
|
|
4267
4755
|
const passwordLen = wasm.lengthBytesUTF8(password) + 1;
|
|
4268
4756
|
|
|
@@ -4270,12 +4758,12 @@ function createModule(wasm) {
|
|
|
4270
4758
|
return normalizePkcs12ImportJson(JSON.parse(
|
|
4271
4759
|
callDynamicStringResult(
|
|
4272
4760
|
wasm,
|
|
4273
|
-
(outPtr, outLenPtr) => fn(bundlePtr,
|
|
4761
|
+
(outPtr, outLenPtr) => fn(bundlePtr, bundleLen, passwordPtr, outPtr, outLenPtr),
|
|
4274
4762
|
{ wipeOutput: true }
|
|
4275
4763
|
)
|
|
4276
4764
|
));
|
|
4277
4765
|
} finally {
|
|
4278
|
-
wasm._hd_secure_wipe(bundlePtr,
|
|
4766
|
+
wasm._hd_secure_wipe(bundlePtr, bundleLen);
|
|
4279
4767
|
wasm._hd_secure_wipe(passwordPtr, passwordLen);
|
|
4280
4768
|
wasm._hd_dealloc(bundlePtr);
|
|
4281
4769
|
wasm._hd_dealloc(passwordPtr);
|
|
@@ -4287,26 +4775,41 @@ function createModule(wasm) {
|
|
|
4287
4775
|
// ECIES API
|
|
4288
4776
|
// ==========================================================================
|
|
4289
4777
|
|
|
4290
|
-
const CURVE_NAME_MAP = { secp256k1: 0, ed25519: 1, p256: 2, p384: 3, x25519: 4 };
|
|
4778
|
+
const CURVE_NAME_MAP = Object.freeze({ secp256k1: 0, ed25519: 1, p256: 2, p384: 3, x25519: 4 });
|
|
4779
|
+
|
|
4780
|
+
// An ECIES curve: a Curve id or its name; an unknown name is an error, never
|
|
4781
|
+
// secp256k1 by default.
|
|
4782
|
+
function eciesCurve(curve) {
|
|
4783
|
+
const curveId = typeof curve === 'string' && Object.hasOwn(CURVE_NAME_MAP, curve)
|
|
4784
|
+
? CURVE_NAME_MAP[curve]
|
|
4785
|
+
: curve;
|
|
4786
|
+
return V.curve(curveId, [Curve.SECP256K1, Curve.P256, Curve.P384, Curve.X25519]);
|
|
4787
|
+
}
|
|
4291
4788
|
|
|
4292
4789
|
const ecies = {
|
|
4293
4790
|
encrypt(curve, recipientPublicKey, plaintext, aad = new Uint8Array(0)) {
|
|
4294
|
-
const curveId =
|
|
4791
|
+
const curveId = eciesCurve(curve);
|
|
4792
|
+
V.publicKey(wasm, recipientPublicKey, curveId, 'recipientPublicKey');
|
|
4793
|
+
V.bytes(plaintext, 'plaintext');
|
|
4794
|
+
V.bytes(aad, 'aad');
|
|
4295
4795
|
const encryptFn = requireWasmFunction(wasm, '_hd_ecies_encrypt');
|
|
4296
4796
|
const overheadFn = requireWasmFunction(wasm, '_hd_ecies_overhead');
|
|
4297
4797
|
const overhead = overheadFn(curveId);
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
const
|
|
4798
|
+
if (!Number.isInteger(overhead) || overhead <= 0) {
|
|
4799
|
+
throw new HDWalletError(ErrorCode.NOT_SUPPORTED, 'ECIES is not supported on this curve');
|
|
4800
|
+
}
|
|
4801
|
+
const outSize = V.byteLength(plaintext) + overhead;
|
|
4802
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, recipientPublicKey);
|
|
4803
|
+
const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
|
|
4804
|
+
const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
|
|
4302
4805
|
const outPtr = wasm._hd_alloc(outSize);
|
|
4303
4806
|
try {
|
|
4304
|
-
const result = encryptFn(curveId, pubPtr,
|
|
4305
|
-
ptPtr,
|
|
4306
|
-
if (result < 0) throw new HDWalletError(-result);
|
|
4807
|
+
const result = checkLength(encryptFn(curveId, pubPtr, pubLen,
|
|
4808
|
+
ptPtr, ptLen, aadPtr, aadLen, outPtr, outSize), outSize);
|
|
4307
4809
|
return readBytes(wasm, outPtr, result);
|
|
4308
4810
|
} finally {
|
|
4309
4811
|
wasm._hd_dealloc(pubPtr);
|
|
4812
|
+
wasm._hd_secure_wipe(ptPtr, ptLen);
|
|
4310
4813
|
wasm._hd_dealloc(ptPtr);
|
|
4311
4814
|
if (aadPtr) wasm._hd_dealloc(aadPtr);
|
|
4312
4815
|
wasm._hd_dealloc(outPtr);
|
|
@@ -4314,22 +4817,25 @@ function createModule(wasm) {
|
|
|
4314
4817
|
},
|
|
4315
4818
|
|
|
4316
4819
|
decrypt(curve, recipientPrivateKey, ciphertext, aad = new Uint8Array(0)) {
|
|
4317
|
-
const curveId =
|
|
4820
|
+
const curveId = eciesCurve(curve);
|
|
4821
|
+
V.privateKey(wasm, recipientPrivateKey, curveId, 'recipientPrivateKey');
|
|
4822
|
+
V.bytes(ciphertext, 'ciphertext');
|
|
4823
|
+
V.bytes(aad, 'aad');
|
|
4318
4824
|
const decryptFn = requireWasmFunction(wasm, '_hd_ecies_decrypt');
|
|
4319
|
-
const privPtr = allocAndCopy(wasm, recipientPrivateKey);
|
|
4320
|
-
const ctPtr = allocAndCopy(wasm, ciphertext);
|
|
4321
|
-
const aadPtr = aad
|
|
4322
|
-
const outPtr = wasm._hd_alloc(
|
|
4825
|
+
const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, recipientPrivateKey);
|
|
4826
|
+
const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
|
|
4827
|
+
const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
|
|
4828
|
+
const outPtr = wasm._hd_alloc(ctLen);
|
|
4323
4829
|
try {
|
|
4324
|
-
const result = decryptFn(curveId, privPtr,
|
|
4325
|
-
ctPtr,
|
|
4326
|
-
if (result < 0) throw new HDWalletError(-result);
|
|
4830
|
+
const result = checkLengthWithin(decryptFn(curveId, privPtr, privLen,
|
|
4831
|
+
ctPtr, ctLen, aadPtr, aadLen, outPtr, ctLen), 0, ctLen);
|
|
4327
4832
|
return readBytes(wasm, outPtr, result);
|
|
4328
4833
|
} finally {
|
|
4329
|
-
wasm._hd_secure_wipe(privPtr,
|
|
4834
|
+
wasm._hd_secure_wipe(privPtr, privLen);
|
|
4330
4835
|
wasm._hd_dealloc(privPtr);
|
|
4331
4836
|
wasm._hd_dealloc(ctPtr);
|
|
4332
4837
|
if (aadPtr) wasm._hd_dealloc(aadPtr);
|
|
4838
|
+
wasm._hd_secure_wipe(outPtr, ctLen);
|
|
4333
4839
|
wasm._hd_dealloc(outPtr);
|
|
4334
4840
|
}
|
|
4335
4841
|
}
|
|
@@ -4342,16 +4848,19 @@ function createModule(wasm) {
|
|
|
4342
4848
|
const aesCtr = {
|
|
4343
4849
|
encrypt(key, plaintext, iv) {
|
|
4344
4850
|
const encryptFn = requireWasmFunction(wasm, '_hd_aes_ctr_encrypt');
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
const
|
|
4851
|
+
V.bytesOfLength(key, [16, 24, 32], 'key');
|
|
4852
|
+
V.bytes(plaintext, 'plaintext');
|
|
4853
|
+
V.bytesOfLength(iv, 16, 'iv');
|
|
4854
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
|
|
4855
|
+
const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
|
|
4856
|
+
const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
|
|
4857
|
+
const outPtr = wasm._hd_alloc(ptLen);
|
|
4349
4858
|
try {
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
return readBytes(wasm, outPtr, plaintext.length);
|
|
4859
|
+
checkLength(encryptFn(keyPtr, keyLen, ptPtr, ptLen, ivPtr, ivLen, outPtr, ptLen), ptLen);
|
|
4860
|
+
return readBytes(wasm, outPtr, ptLen);
|
|
4353
4861
|
} finally {
|
|
4354
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4862
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4863
|
+
wasm._hd_secure_wipe(ptPtr, ptLen);
|
|
4355
4864
|
wasm._hd_dealloc(keyPtr);
|
|
4356
4865
|
wasm._hd_dealloc(ptPtr);
|
|
4357
4866
|
wasm._hd_dealloc(ivPtr);
|
|
@@ -4361,19 +4870,22 @@ function createModule(wasm) {
|
|
|
4361
4870
|
|
|
4362
4871
|
decrypt(key, ciphertext, iv) {
|
|
4363
4872
|
const decryptFn = requireWasmFunction(wasm, '_hd_aes_ctr_decrypt');
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
const
|
|
4873
|
+
V.bytesOfLength(key, [16, 24, 32], 'key');
|
|
4874
|
+
V.bytes(ciphertext, 'ciphertext');
|
|
4875
|
+
V.bytesOfLength(iv, 16, 'iv');
|
|
4876
|
+
const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
|
|
4877
|
+
const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
|
|
4878
|
+
const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
|
|
4879
|
+
const outPtr = wasm._hd_alloc(ctLen);
|
|
4368
4880
|
try {
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
return readBytes(wasm, outPtr, ciphertext.length);
|
|
4881
|
+
checkLength(decryptFn(keyPtr, keyLen, ctPtr, ctLen, ivPtr, ivLen, outPtr, ctLen), ctLen);
|
|
4882
|
+
return readBytes(wasm, outPtr, ctLen);
|
|
4372
4883
|
} finally {
|
|
4373
|
-
wasm._hd_secure_wipe(keyPtr,
|
|
4884
|
+
wasm._hd_secure_wipe(keyPtr, keyLen);
|
|
4374
4885
|
wasm._hd_dealloc(keyPtr);
|
|
4375
4886
|
wasm._hd_dealloc(ctPtr);
|
|
4376
4887
|
wasm._hd_dealloc(ivPtr);
|
|
4888
|
+
wasm._hd_secure_wipe(outPtr, ctLen);
|
|
4377
4889
|
wasm._hd_dealloc(outPtr);
|
|
4378
4890
|
}
|
|
4379
4891
|
}
|
|
@@ -4386,19 +4898,21 @@ function createModule(wasm) {
|
|
|
4386
4898
|
const slip10 = {
|
|
4387
4899
|
deriveEd25519Path(seed, path) {
|
|
4388
4900
|
const deriveFn = requireWasmFunction(wasm, '_hd_slip10_ed25519_derive_path');
|
|
4389
|
-
|
|
4901
|
+
V.bytesOfLength(seed, 64, 'seed', ErrorCode.INVALID_SEED);
|
|
4902
|
+
V.string(path, 'path');
|
|
4903
|
+
const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
|
|
4390
4904
|
const pathPtr = allocString(wasm, path);
|
|
4391
4905
|
const keyPtr = wasm._hd_alloc(32);
|
|
4392
4906
|
const chainCodePtr = wasm._hd_alloc(32);
|
|
4393
4907
|
try {
|
|
4394
|
-
const result = deriveFn(seedPtr,
|
|
4908
|
+
const result = deriveFn(seedPtr, seedLen, pathPtr, keyPtr, chainCodePtr);
|
|
4395
4909
|
checkResult(result);
|
|
4396
4910
|
return {
|
|
4397
4911
|
privateKey: readBytes(wasm, keyPtr, 32),
|
|
4398
4912
|
chainCode: readBytes(wasm, chainCodePtr, 32)
|
|
4399
4913
|
};
|
|
4400
4914
|
} finally {
|
|
4401
|
-
wasm._hd_secure_wipe(seedPtr,
|
|
4915
|
+
wasm._hd_secure_wipe(seedPtr, seedLen);
|
|
4402
4916
|
wasm._hd_secure_wipe(keyPtr, 32);
|
|
4403
4917
|
wasm._hd_dealloc(seedPtr);
|
|
4404
4918
|
wasm._hd_dealloc(pathPtr);
|
|
@@ -4420,11 +4934,15 @@ function createModule(wasm) {
|
|
|
4420
4934
|
* @returns {Uint8Array} PeerID as raw multihash bytes
|
|
4421
4935
|
*/
|
|
4422
4936
|
peerIdFromPublicKey(publicKey, curve) {
|
|
4423
|
-
|
|
4937
|
+
// libp2p key forms: secp256k1 compressed, Ed25519 32 bytes, P-256 SEC1.
|
|
4938
|
+
V.curve(curve, [Curve.SECP256K1, Curve.ED25519, Curve.P256]);
|
|
4939
|
+
V.publicKey(wasm, publicKey, curve, 'publicKey',
|
|
4940
|
+
curve === Curve.SECP256K1 ? [33] : V.publicKeyLengths(curve));
|
|
4941
|
+
const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
|
|
4424
4942
|
const outPtr = wasm._hd_alloc(64);
|
|
4425
4943
|
try {
|
|
4426
|
-
const result =
|
|
4427
|
-
|
|
4944
|
+
const result = checkLengthWithin(
|
|
4945
|
+
wasm._hd_libp2p_peer_id(pubPtr, pubLen, curve, outPtr, 64), 1, 64);
|
|
4428
4946
|
return readBytes(wasm, outPtr, result);
|
|
4429
4947
|
} finally {
|
|
4430
4948
|
wasm._hd_dealloc(pubPtr);
|
|
@@ -4453,20 +4971,20 @@ function createModule(wasm) {
|
|
|
4453
4971
|
// We could also re-derive from pubkey, but for the namespace API we accept
|
|
4454
4972
|
// pre-computed peerID bytes and just do the CID wrapping + encoding here.
|
|
4455
4973
|
// We still use WASM for base36 encoding.
|
|
4456
|
-
|
|
4974
|
+
// A peer ID multihash: identity or SHA-256, at most 64 bytes.
|
|
4975
|
+
const peerId = V.snapshot(V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes'), 'peerIdBytes');
|
|
4457
4976
|
const outPtr = wasm._hd_alloc(128);
|
|
4458
4977
|
// Build CID in JS (trivial: 2-byte prefix + multihash)
|
|
4459
|
-
const cid = new Uint8Array(2 +
|
|
4978
|
+
const cid = new Uint8Array(2 + peerId.length);
|
|
4460
4979
|
cid[0] = 0x01; // CIDv1
|
|
4461
4980
|
cid[1] = 0x72; // libp2p-key codec
|
|
4462
|
-
cid.set(
|
|
4463
|
-
const cidPtr = allocAndCopy(wasm, cid);
|
|
4981
|
+
cid.set(peerId, 2);
|
|
4982
|
+
const { ptr: cidPtr, len: cidLen } = allocAndCopy(wasm, cid);
|
|
4464
4983
|
try {
|
|
4465
|
-
const result = wasm._hd_encode_base36lower(cidPtr,
|
|
4984
|
+
const result = wasm._hd_encode_base36lower(cidPtr, cidLen, outPtr, 128);
|
|
4466
4985
|
checkResult(result);
|
|
4467
4986
|
return 'k' + readString(wasm, outPtr);
|
|
4468
4987
|
} finally {
|
|
4469
|
-
wasm._hd_dealloc(dataPtr);
|
|
4470
4988
|
wasm._hd_dealloc(cidPtr);
|
|
4471
4989
|
wasm._hd_dealloc(outPtr);
|
|
4472
4990
|
}
|
|
@@ -4478,14 +4996,15 @@ function createModule(wasm) {
|
|
|
4478
4996
|
* @returns {string}
|
|
4479
4997
|
*/
|
|
4480
4998
|
ipnsHashBase32(peerIdBytes) {
|
|
4481
|
-
const
|
|
4999
|
+
const peerId = V.snapshot(V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes'), 'peerIdBytes');
|
|
5000
|
+
const cid = new Uint8Array(2 + peerId.length);
|
|
4482
5001
|
cid[0] = 0x01; // CIDv1
|
|
4483
5002
|
cid[1] = 0x72; // libp2p-key codec
|
|
4484
|
-
cid.set(
|
|
4485
|
-
const cidPtr = allocAndCopy(wasm, cid);
|
|
5003
|
+
cid.set(peerId, 2);
|
|
5004
|
+
const { ptr: cidPtr, len: cidLen } = allocAndCopy(wasm, cid);
|
|
4486
5005
|
const outPtr = wasm._hd_alloc(128);
|
|
4487
5006
|
try {
|
|
4488
|
-
const result = wasm._hd_encode_base32lower(cidPtr,
|
|
5007
|
+
const result = wasm._hd_encode_base32lower(cidPtr, cidLen, outPtr, 128);
|
|
4489
5008
|
checkResult(result);
|
|
4490
5009
|
return 'b' + readString(wasm, outPtr);
|
|
4491
5010
|
} finally {
|
|
@@ -4575,11 +5094,12 @@ function createModule(wasm) {
|
|
|
4575
5094
|
|
|
4576
5095
|
// Entropy
|
|
4577
5096
|
injectEntropy(entropy) {
|
|
4578
|
-
|
|
5097
|
+
V.bytesInRange(entropy, 1, 0x100000, 'entropy');
|
|
5098
|
+
const { ptr: entropyPtr, len: entropyLen } = allocAndCopy(wasm, entropy);
|
|
4579
5099
|
try {
|
|
4580
|
-
wasm._hd_inject_entropy(entropyPtr,
|
|
5100
|
+
wasm._hd_inject_entropy(entropyPtr, entropyLen);
|
|
4581
5101
|
} finally {
|
|
4582
|
-
wasm._hd_secure_wipe(entropyPtr,
|
|
5102
|
+
wasm._hd_secure_wipe(entropyPtr, entropyLen);
|
|
4583
5103
|
wasm._hd_dealloc(entropyPtr);
|
|
4584
5104
|
}
|
|
4585
5105
|
},
|