@exodus/bip322-js 3.2.0 → 3.2.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/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.2.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/bip322-js@3.2.1...@exodus/bip322-js@3.2.2) (2026-06-29)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **bip322-js:** BIP137 segwit-flag signature verification ([#16085](https://github.com/ExodusMovement/exodus-hydra/issues/16085)) ([9363135](https://github.com/ExodusMovement/exodus-hydra/commit/9363135c13cef1a7b632d133adddb4fde8a28f04))
11
+
12
+ ## [3.2.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/bip322-js@3.2.0...@exodus/bip322-js@3.2.1) (2026-04-22)
13
+
14
+ ### Bug Fixes
15
+
16
+ - fix(bip322-js): reject oversized DER R/S lengths (ECDSA malleability) (#16083)
17
+
6
18
  ## [3.2.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/bip322-js@3.1.0...@exodus/bip322-js@3.2.0) (2025-12-26)
7
19
 
8
20
  ### Features
package/dist/Address.d.ts CHANGED
@@ -5,7 +5,7 @@ declare class Address {
5
5
  static isP2TR(address: string): boolean;
6
6
  static isP2WPKHWitness(witness: Buffer[]): boolean;
7
7
  static isSingleKeyP2TRWitness(witness: Buffer[]): boolean;
8
- static convertAdressToScriptPubkey(address: string): Buffer;
8
+ static convertAdressToScriptPubkey(address: string): Buffer<ArrayBufferLike>;
9
9
  static convertPubKeyIntoAddress(publicKey: Buffer, addressType: 'p2pkh' | 'p2sh-p2wpkh' | 'p2wpkh' | 'p2tr'): {
10
10
  mainnet: string | undefined;
11
11
  testnet: string | undefined;
package/dist/BIP322.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as bitcoin from '@exodus/bitcoinjs';
2
2
  declare class BIP322 {
3
- static TAG: Buffer;
3
+ static TAG: Buffer<ArrayBuffer>;
4
4
  static hashMessage(message: string): any;
5
5
  static buildToSpendTx(message: string, scriptPublicKey: Buffer): bitcoin.Transaction;
6
6
  static buildToSignTx(toSpendTxId: string, witnessScript: Buffer, isRedeemScript?: boolean, tapInternalKey?: Buffer): bitcoin.Psbt;
package/dist/Signer.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as bitcoin from '@exodus/bitcoinjs';
2
- import type { Signer as AssetSigner } from '@exodus/asset-types/src/signer';
2
+ import type { Signer as AssetSigner } from '@exodus/asset-types';
3
3
  export declare const createSigner: (encodedKey: string | Buffer, network: bitcoin.Network) => {
4
4
  signer: {
5
5
  getPublicKey: () => Promise<any>;
@@ -15,7 +15,6 @@ export declare const createSigner: (encodedKey: string | Buffer, network: bitcoi
15
15
  };
16
16
  declare class Signer {
17
17
  #private;
18
- static signAsync(signerOrKey: string | Buffer | AssetSigner, address: string, message: string, network?: bitcoin.Network): Promise<any>;
19
- private static checkPubKeyCorrespondToAddress;
18
+ static signAsync(signerOrKey: string | Buffer | AssetSigner, address: string, message: string, network?: bitcoin.Network): Promise<string | Buffer<ArrayBufferLike>>;
20
19
  }
21
20
  export default Signer;
package/dist/Signer.js CHANGED
@@ -63,7 +63,7 @@ class Signer {
63
63
  }
64
64
  static async #signAsyncInternal(signer, address, message, network = bitcoin.networks.bitcoin, compressed) {
65
65
  const publicKey = await signer.getPublicKey();
66
- assert(this.checkPubKeyCorrespondToAddress(publicKey, address), `Invalid signer for address "${address}".`);
66
+ assert(this.#checkPubKeyCorrespondToAddress(publicKey, address), `Invalid signer for address "${address}".`);
67
67
  if (Address.isP2PKH(address)) {
68
68
  const asyncSigner = {
69
69
  sign(hash, extraEntropy) {
@@ -103,7 +103,7 @@ class Signer {
103
103
  ]);
104
104
  return BIP322.encodeWitness(toSignTx.finalizeAllInputs());
105
105
  }
106
- static checkPubKeyCorrespondToAddress(publicKey, claimedAddress) {
106
+ static #checkPubKeyCorrespondToAddress(publicKey, claimedAddress) {
107
107
  let derivedAddresses;
108
108
  if (Address.isP2PKH(claimedAddress)) {
109
109
  derivedAddresses = Address.convertPubKeyIntoAddress(publicKey, 'p2pkh');
@@ -1,8 +1,5 @@
1
1
  declare class Verifier {
2
+ #private;
2
3
  static verifySignature(signerAddress: string, message: string, signature: Buffer): Promise<boolean>;
3
- private static verifyBIP137Signature;
4
- private static getHashForSigP2WPKH;
5
- private static getHashForSigP2SHInP2WPKH;
6
- private static getHashForSigP2TR;
7
4
  }
8
5
  export default Verifier;
package/dist/Verifier.js CHANGED
@@ -11,7 +11,7 @@ class Verifier {
11
11
  throw new TypeError('signature must be a Buffer');
12
12
  }
13
13
  if (Address.isP2PKH(signerAddress) || BIP137.isBIP137Signature(signature)) {
14
- return this.verifyBIP137Signature(signerAddress, message, signature);
14
+ return this.#verifyBIP137Signature(signerAddress, message, signature);
15
15
  }
16
16
  const scriptPubKey = Address.convertAdressToScriptPubkey(signerAddress);
17
17
  const toSpendTx = BIP322.buildToSpendTx(message, scriptPubKey);
@@ -27,7 +27,7 @@ class Verifier {
27
27
  const hashedPubkey = bitcoin.crypto.hash160(publicKey);
28
28
  let hashToSign;
29
29
  if (Address.isP2SH(signerAddress)) {
30
- hashToSign = this.getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey);
30
+ hashToSign = this.#getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey);
31
31
  const lockingScript = Buffer.concat([Buffer.from([0x00, 0x14]), hashedPubkey]);
32
32
  const hashedLockingScript = bitcoin.crypto.hash160(lockingScript);
33
33
  const hashedLockingScriptInScriptPubKey = scriptPubKey.subarray(2, -1);
@@ -36,7 +36,7 @@ class Verifier {
36
36
  }
37
37
  }
38
38
  else {
39
- hashToSign = this.getHashForSigP2WPKH(toSignTx);
39
+ hashToSign = this.#getHashForSigP2WPKH(toSignTx);
40
40
  const hashedPubkeyInScriptPubkey = scriptPubKey.subarray(2);
41
41
  if (Buffer.compare(hashedPubkey, hashedPubkeyInScriptPubkey) !== 0) {
42
42
  return false;
@@ -52,11 +52,11 @@ class Verifier {
52
52
  let hashToSign;
53
53
  let signature;
54
54
  if (encodedSignature.byteLength === 64) {
55
- hashToSign = this.getHashForSigP2TR(toSignTx, 0x00);
55
+ hashToSign = this.#getHashForSigP2TR(toSignTx, 0x00);
56
56
  signature = encodedSignature;
57
57
  }
58
58
  else if (encodedSignature.byteLength === 65) {
59
- hashToSign = this.getHashForSigP2TR(toSignTx, encodedSignature[64]);
59
+ hashToSign = this.#getHashForSigP2TR(toSignTx, encodedSignature[64]);
60
60
  signature = encodedSignature.subarray(0, -1);
61
61
  }
62
62
  else {
@@ -66,12 +66,11 @@ class Verifier {
66
66
  }
67
67
  throw new Error('Only P2WPKH, P2SH-P2WPKH, and single-key-spend P2TR BIP-322 verification is supported. Unsupported address is provided.');
68
68
  }
69
- static verifyBIP137Signature(signerAddress, message, signature) {
69
+ static #verifyBIP137Signature(signerAddress, message, signature) {
70
70
  if (Address.isP2PKH(signerAddress)) {
71
71
  return bitcoinMessage.verify(message, signerAddress, signature);
72
72
  }
73
73
  const publicKeySigned = BIP137.derivePubKey(message, signature);
74
- const legacySigningAddress = Address.convertPubKeyIntoAddress(publicKeySigned, 'p2pkh').mainnet;
75
74
  if (Address.isP2SH(signerAddress)) {
76
75
  const p2shAddressDerived = Address.convertPubKeyIntoAddress(publicKeySigned, 'p2sh-p2wpkh');
77
76
  if (p2shAddressDerived.mainnet !== signerAddress &&
@@ -96,9 +95,9 @@ class Verifier {
96
95
  else {
97
96
  return false;
98
97
  }
99
- return bitcoinMessage.verify(message, legacySigningAddress, signature);
98
+ return true;
100
99
  }
101
- static getHashForSigP2WPKH(toSignTx) {
100
+ static #getHashForSigP2WPKH(toSignTx) {
102
101
  const signingScript = bitcoin.payments.p2pkh({
103
102
  hash: toSignTx.data.inputs[0].witnessUtxo.script.subarray(2),
104
103
  }).output;
@@ -106,7 +105,7 @@ class Verifier {
106
105
  .extractTransaction()
107
106
  .hashForWitnessV0(0, signingScript, 0, bitcoin.Transaction.SIGHASH_ALL);
108
107
  }
109
- static getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey) {
108
+ static #getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey) {
110
109
  const signingScript = bitcoin.payments.p2pkh({
111
110
  hash: hashedPubkey,
112
111
  }).output;
@@ -114,7 +113,7 @@ class Verifier {
114
113
  .extractTransaction()
115
114
  .hashForWitnessV0(0, signingScript, 0, bitcoin.Transaction.SIGHASH_ALL);
116
115
  }
117
- static getHashForSigP2TR(toSignTx, hashType) {
116
+ static #getHashForSigP2TR(toSignTx, hashType) {
118
117
  if (hashType !== bitcoin.Transaction.SIGHASH_DEFAULT &&
119
118
  hashType !== bitcoin.Transaction.SIGHASH_ALL) {
120
119
  throw new Error('Invalid SIGHASH used in signature. Must be either SIGHASH_ALL or SIGHASH_DEFAULT.');
@@ -12,8 +12,10 @@ export function decodeScriptSignature(buffer) {
12
12
  function fromDER(x) {
13
13
  if (x[0] === 0x00)
14
14
  x = x.slice(1);
15
+ if (x.length > 32)
16
+ throw new Error('Value length is too long');
15
17
  const buffer = Buffer.alloc(32, 0);
16
- const bstart = Math.max(0, 32 - x.length);
18
+ const bstart = 32 - x.length;
17
19
  x.copy(buffer, bstart);
18
20
  return buffer;
19
21
  }
@@ -33,6 +35,8 @@ function decode2(buffer) {
33
35
  throw new Error('R length is zero');
34
36
  if (5 + lenR >= buffer.length)
35
37
  throw new Error('R length is too long');
38
+ if (lenR > 33)
39
+ throw new Error('R length is too long');
36
40
  if (buffer[4 + lenR] !== 0x02)
37
41
  throw new Error('Expected DER integer (2)');
38
42
  const lenS = buffer[5 + lenR];
@@ -40,6 +44,8 @@ function decode2(buffer) {
40
44
  throw new Error('S length is zero');
41
45
  if (6 + lenR + lenS !== buffer.length)
42
46
  throw new Error('S length is invalid');
47
+ if (lenS > 33)
48
+ throw new Error('S length is too long');
43
49
  if (buffer[4] & 0x80)
44
50
  throw new Error('R value is negative');
45
51
  if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) {
@@ -1 +1 @@
1
- export declare function verify(message: string, address: string, signature: Buffer, messagePrefix?: string, checkSegwitAlways?: boolean): boolean;
1
+ export declare function verify(message: string, address: string, signature: Buffer | string, messagePrefix?: string, checkSegwitAlways?: boolean): boolean;
@@ -1,6 +1,6 @@
1
1
  declare class BIP137 {
2
+ #private;
2
3
  static isBIP137Signature(signature: Buffer): boolean;
3
- static derivePubKey(message: string, signature: Buffer): Buffer;
4
- private static decodeSignature;
4
+ static derivePubKey(message: string, signature: Buffer): Buffer<ArrayBuffer>;
5
5
  }
6
6
  export default BIP137;
@@ -6,15 +6,15 @@ class BIP137 {
6
6
  }
7
7
  static derivePubKey(message, signature) {
8
8
  const messageHash = bitcoinMessage.magicHash(message);
9
- const signatureDecoded = this.decodeSignature(signature);
9
+ const signatureDecoded = this.#decodeSignature(signature);
10
10
  const recoveredPublicKey = recoverPublicKey(messageHash, signatureDecoded.signature, signatureDecoded.recovery, signatureDecoded.compressed);
11
11
  return Buffer.from(recoveredPublicKey);
12
12
  }
13
- static decodeSignature(signature) {
13
+ static #decodeSignature(signature) {
14
14
  if (signature.length !== 65)
15
15
  throw new Error('Invalid signature length');
16
16
  const flagByte = signature.readUInt8(0) - 27;
17
- if (flagByte > 19 || flagByte < 0) {
17
+ if (flagByte > 15 || flagByte < 0) {
18
18
  throw new Error('Invalid signature parameter');
19
19
  }
20
20
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bip322-js",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "A Javascript library that provides utility functions related to the BIP-322 signature scheme",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,12 +15,13 @@
15
15
  "!**/__tests__/**"
16
16
  ],
17
17
  "scripts": {
18
- "build": "run -T tsc -p tsconfig.build.json",
19
- "lint": "run -T eslint",
20
- "lint:fix": "yarn lint --fix",
18
+ "build": "pnpm exec tsc -p tsconfig.build.json",
19
+ "lint": "pnpm exec eslint",
20
+ "lint:fix": "pnpm run lint --fix",
21
21
  "doc": "typedoc src/index.ts",
22
- "test": "run -T exodus-test --jest --esbuild",
23
- "prepublishOnly": "yarn run -T build --scope @exodus/bip322-js"
22
+ "test": "pnpm exec exodus-test --jest --esbuild",
23
+ "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
24
+ "prepublishOnly": "pnpm -w run build --scope @exodus/bip322-js"
24
25
  },
25
26
  "keywords": [
26
27
  "bip322",
@@ -37,13 +38,13 @@
37
38
  "license": "MIT",
38
39
  "devDependencies": {
39
40
  "@types/minimalistic-assert": "^1.0.1",
40
- "@types/node": "^20.2.5",
41
+ "@types/node": "^22.7.4",
41
42
  "ecpair": "^2.0.1",
42
43
  "typedoc": "^0.24.8",
43
44
  "typescript": "^5.1.3"
44
45
  },
45
46
  "dependencies": {
46
- "@exodus/asset-types": "^0.3.0",
47
+ "@exodus/asset-types": "^0.5.0",
47
48
  "@exodus/bitcoinjs": "^2.0.0",
48
49
  "@exodus/bytes": "^1.0.0-rc.8",
49
50
  "@exodus/crypto": "^1.0.0-rc.26",
@@ -57,5 +58,5 @@
57
58
  "bugs": {
58
59
  "url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Abip322-js"
59
60
  },
60
- "gitHead": "dab4f446f30bf73babe598463627eaad6e14fa4b"
61
+ "gitHead": "8c91cf6b4dc913de523a9cc7dbb56ff871fefb14"
61
62
  }