hd-wallet-wasm 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -442,9 +442,14 @@ export class AlignedSigner {
442
442
  signBatch(privateKey, hashes, curve = AlignedCurve.SECP256K1) {
443
443
  const wasm = this.#wasm;
444
444
  V.curve(curve, BATCH_CURVES);
445
- if (!Array.isArray(hashes) || hashes.length > MAX_BATCH) {
445
+ // The array and each entry are read once (a Proxy or a getter could answer
446
+ // a second read differently); only the copies are used below.
447
+ const hashCount = Array.isArray(hashes) ? hashes.length : -1;
448
+ if (!Number.isInteger(hashCount) || hashCount < 0 || hashCount > MAX_BATCH) {
446
449
  throw new AlignedError(AlignedErrorCode.INVALID_ARGUMENT, `hashes must be an array of at most ${MAX_BATCH}`);
447
450
  }
451
+ const hashCopies = [];
452
+ for (let i = 0; i < hashCount; i++) hashCopies.push(V.snapshot(hashes[i], 'hash'));
448
453
  // The request holds a 32-byte key: secp256k1 and P-256 keys must be
449
454
  // 0 < d < n; Ed25519 seeds are any 32 bytes. P-384 and X25519 entries
450
455
  // report NOT_SUPPORTED.
@@ -453,8 +458,8 @@ export class AlignedSigner {
453
458
  } else {
454
459
  V.bytesOfLength(privateKey, 32, 'privateKey', AlignedErrorCode.INVALID_PRIVATE_KEY);
455
460
  }
456
- for (const hash of hashes) V.bytesOfLength(hash, 32, 'hash');
457
- const count = hashes.length;
461
+ for (const hash of hashCopies) V.bytesOfLength(hash, 32, 'hash');
462
+ const count = hashCopies.length;
458
463
 
459
464
  // Allocate request
460
465
  const requestPtr = allocZeroed(wasm, BATCHSIGNREQUEST_SIZE);
@@ -474,7 +479,7 @@ export class AlignedSigner {
474
479
  for (let i = 0; i < count; i++) {
475
480
  const entry = hashesArray.at(i);
476
481
  entry.index = i;
477
- entry.hash_data.set(hashes[i]);
482
+ entry.hash_data.set(hashCopies[i]);
478
483
  }
479
484
 
480
485
  // Allocate results
@@ -533,38 +538,43 @@ export class AlignedSigner {
533
538
  verifyBatch(publicKey, entries, curve = AlignedCurve.SECP256K1) {
534
539
  const wasm = this.#wasm;
535
540
  V.curve(curve, BATCH_CURVES);
536
- if (!Array.isArray(entries) || entries.length > MAX_BATCH) {
541
+ // The array, each entry and each field are read once (a Proxy or a getter
542
+ // could answer a second read differently); only the copies are used below.
543
+ const entryCount = Array.isArray(entries) ? entries.length : -1;
544
+ if (!Number.isInteger(entryCount) || entryCount < 0 || entryCount > MAX_BATCH) {
537
545
  throw new AlignedError(AlignedErrorCode.INVALID_ARGUMENT, `entries must be an array of at most ${MAX_BATCH}`);
538
546
  }
539
547
  // Types are programmer errors (throw); wrong lengths or an invalid key
540
548
  // only make the affected entries invalid.
541
- V.bytes(publicKey, 'publicKey');
542
- for (const entry of entries) {
549
+ const publicKeyCopy = V.snapshot(publicKey, 'publicKey');
550
+ const entryCopies = [];
551
+ for (let i = 0; i < entryCount; i++) {
552
+ const entry = entries[i];
543
553
  if (entry === null || typeof entry !== 'object') {
544
554
  throw new AlignedError(AlignedErrorCode.INVALID_ARGUMENT, 'each entry must be { hash, signature }');
545
555
  }
546
- V.bytes(entry.hash, 'hash');
547
- V.bytes(entry.signature, 'signature');
556
+ const { hash, signature } = entry;
557
+ entryCopies.push({ hash: V.snapshot(hash, 'hash'), signature: V.snapshot(signature, 'signature') });
548
558
  }
549
- const count = entries.length;
559
+ const count = entryCopies.length;
550
560
 
551
561
  // The request holds a 33-byte key: a compressed secp256k1 or P-256 key,
552
562
  // or an Ed25519 key after one padding byte.
553
563
  let keyBytes = null;
554
564
  if (curve === AlignedCurve.ED25519) {
555
- if (publicKey.length === 32) {
565
+ if (publicKeyCopy.length === 32) {
556
566
  keyBytes = new Uint8Array(33);
557
- keyBytes.set(publicKey, 1);
558
- } else if (publicKey.length === 33) {
559
- keyBytes = publicKey;
567
+ keyBytes.set(publicKeyCopy, 1);
568
+ } else if (publicKeyCopy.length === 33) {
569
+ keyBytes = publicKeyCopy;
560
570
  }
561
571
  } else if (curve === AlignedCurve.SECP256K1 || curve === AlignedCurve.P256) {
562
- if (V.isPublicKey(wasm, publicKey, curve, [33])) keyBytes = publicKey;
572
+ if (V.isPublicKey(wasm, publicKeyCopy, curve, [33])) keyBytes = publicKeyCopy;
563
573
  } else {
564
574
  // P-384 / X25519: the export reports NOT_SUPPORTED for every entry.
565
575
  keyBytes = new Uint8Array(33);
566
576
  }
567
- const entryValid = entries.map((entry) => keyBytes !== null &&
577
+ const entryValid = entryCopies.map((entry) => keyBytes !== null &&
568
578
  entry.hash.length === 32 && entry.signature.length === 64);
569
579
 
570
580
  // Allocate request
@@ -587,8 +597,8 @@ export class AlignedSigner {
587
597
  const view = entriesArray.at(i);
588
598
  view.index = i;
589
599
  if (entryValid[i]) {
590
- view.hash_data.set(entries[i].hash);
591
- view.signature_data.set(entries[i].signature);
600
+ view.hash_data.set(entryCopies[i].hash);
601
+ view.signature_data.set(entryCopies[i].signature);
592
602
  }
593
603
  }
594
604
 
@@ -12,8 +12,16 @@
12
12
  // Import aligned API types
13
13
  import { AlignedAPI } from './aligned.js';
14
14
 
15
- export type BinaryLike = Uint8Array | ArrayBuffer | ArrayBufferView;
16
- export type X509CertificateInput = string | BinaryLike;
15
+ /**
16
+ * Binary input to every wallet API: a Uint8Array (Node Buffers included) of
17
+ * this realm. A DataView, an ArrayBuffer, another typed array, a detached or
18
+ * cross-realm array is refused.
19
+ */
20
+ export type BinaryLike = Uint8Array;
21
+ /** A PEM string or its bytes, or DER bytes. */
22
+ export type X509CertificateInput = string | Uint8Array;
23
+ /** What an SDN plugin capability callback may return (it is copied to a Uint8Array). */
24
+ export type PluginBinaryLike = Uint8Array | ArrayBuffer | ArrayBufferView;
17
25
 
18
26
  export interface SdnPluginTypeRef {
19
27
  schemaName: string | null;
@@ -113,7 +121,7 @@ export interface SdnPluginInvocationResult {
113
121
  }
114
122
 
115
123
  export interface SdnPluginCapabilities {
116
- randomBytes?: (length: number, context?: Record<string, unknown>) => BinaryLike;
124
+ randomBytes?: (length: number, context?: Record<string, unknown>) => PluginBinaryLike;
117
125
  /**
118
126
  * Wallet-held signer for sign_detached. For secp256k1 it receives the message
119
127
  * (digest is null) and must return ECDSA over SHA-256(messageBytes), hashed once.
@@ -124,8 +132,8 @@ export interface SdnPluginCapabilities {
124
132
  messageBytes: Uint8Array;
125
133
  digest: Uint8Array | null;
126
134
  }) => {
127
- signature: BinaryLike;
128
- publicKey: BinaryLike;
135
+ signature: PluginBinaryLike;
136
+ publicKey: PluginBinaryLike;
129
137
  algorithm?: string;
130
138
  };
131
139
  }
@@ -818,7 +826,12 @@ export interface BitcoinTxBuilder {
818
826
 
819
827
  export interface BitcoinTx {
820
828
  addInput(txid: string, vout: number, sequence?: number): this;
821
- addOutput(address: string, amount: bigint): this;
829
+ /**
830
+ * Pay `address`: P2PKH, P2SH, P2WPKH (v0, 20 bytes), P2WSH (v0, 32 bytes)
831
+ * or P2TR (v1, 32 bytes) of `network` (mainnet by default). Any other
832
+ * address, or one of the other network, throws (BIP-350).
833
+ */
834
+ addOutput(address: string, amount: bigint, network?: Network): this;
822
835
  sign(inputIndex: number, privateKey: Uint8Array, redeemScript?: Uint8Array): this;
823
836
  serialize(): Uint8Array;
824
837
  getTxid(): string;
@@ -867,6 +880,11 @@ export interface EthereumAPI {
867
880
  tx: EthereumTxBuilder;
868
881
  }
869
882
 
883
+ /**
884
+ * `nonce` and `chainId` are required: a missing nonce or chainId, or a chainId
885
+ * outside 1 to 2^63 - 37 (EIP-2294), throws (an explicit nonce 0 is valid).
886
+ * Each field is read once.
887
+ */
870
888
  export interface EthereumTxBuilder {
871
889
  create(params: {
872
890
  nonce: number;
@@ -1044,7 +1062,7 @@ export interface UtilsAPI {
1044
1062
 
1045
1063
  export interface X509WalletAttestationOptions {
1046
1064
  curve: Curve;
1047
- privateKey: BinaryLike;
1065
+ privateKey: Uint8Array;
1048
1066
  keyLabel?: string;
1049
1067
  commentPrefix?: string;
1050
1068
  }
@@ -1090,22 +1108,22 @@ export interface X509Pkcs12Bundle {
1090
1108
  export interface X509API {
1091
1109
  isAvailable(): boolean;
1092
1110
  generatePrivateKey(curve: Curve): Uint8Array;
1093
- exportPrivateKeyPem(curve: Curve, privateKey: BinaryLike): string;
1111
+ exportPrivateKeyPem(curve: Curve, privateKey: Uint8Array): string;
1094
1112
  importPrivateKeyPem(curve: Curve, pem: string): Uint8Array;
1095
1113
  createSelfSignedCertificate(
1096
1114
  options: X509CertificateOptions,
1097
1115
  certificateCurve: Curve,
1098
- certificatePrivateKey: BinaryLike,
1116
+ certificatePrivateKey: Uint8Array,
1099
1117
  outputEncoding?: X509Encoding
1100
1118
  ): string | Uint8Array;
1101
1119
  issueCertificate(
1102
1120
  options: X509CertificateOptions,
1103
1121
  issuerCurve: Curve,
1104
- issuerPrivateKey: BinaryLike,
1122
+ issuerPrivateKey: Uint8Array,
1105
1123
  issuerCertificate: X509CertificateInput,
1106
1124
  issuerCertificateEncoding: X509Encoding,
1107
1125
  subjectCurve: Curve,
1108
- subjectPrivateKey: BinaryLike,
1126
+ subjectPrivateKey: Uint8Array,
1109
1127
  outputEncoding?: X509Encoding
1110
1128
  ): string | Uint8Array;
1111
1129
  convertCertificate(
@@ -1119,12 +1137,12 @@ export interface X509API {
1119
1137
  certificate: X509CertificateInput,
1120
1138
  certificateEncoding: X509Encoding,
1121
1139
  privateKeyCurve: Curve,
1122
- privateKey: BinaryLike,
1140
+ privateKey: Uint8Array,
1123
1141
  password: string,
1124
1142
  friendlyName?: string,
1125
1143
  chainPem?: X509CertificateInput
1126
1144
  ): Uint8Array;
1127
- importPkcs12(pkcs12Bundle: BinaryLike, password: string): X509Pkcs12Bundle;
1145
+ importPkcs12(pkcs12Bundle: Uint8Array, password: string): X509Pkcs12Bundle;
1128
1146
  }
1129
1147
 
1130
1148
  // =============================================================================