Coder Social home page Coder Social logo

acken2 / bip322-js Goto Github PK

View Code? Open in Web Editor NEW
19.0 1.0 13.0 281 KB

A Javascript library that provides utility functions related to the BIP-322 signature scheme

Home Page: https://acken2.github.io/bip322-js/

License: MIT License

TypeScript 100.00%
bitcoin bitcoinjs javascript typescript bip322 no-wasm

bip322-js's Introduction

BIP322-JS

Unit Test Status Coverage Status

A Javascript library that provides utility functions related to the BIP-322 signature scheme.

Documentation

Available at https://acken2.github.io/bip322-js/

Supported Features

The following features are supported on mainnet, testnet, and regtest for P2PKH, P2SH-P2WPKH, P2WPKH, and single-key-spend P2TR addresses:

  1. Generate raw toSpend and toSign BIP-322 transactions.
  2. Sign a BIP-322 signature using a private key.
  3. Verify a legacy BIP-137 signature loosely (see below).
  4. Verify a simple BIP-322 signature.

Usage

Use the Signer class to sign a BIP-322 signature:

Signer.sign(privateKey, address, message)

Use the Verifier class to verify a BIP-322 signature (which also validates a BIP-137 signature):

Verifier.verifySignature(address, message, signature)

Loose BIP-137 Verification

A BIP-322 signature is backward compatible with the legacy signature scheme (i.e., BIP-137 signature). As a result, this library also recognizes valid BIP-137 signatures.

In a BIP-137 signature, a header flag indicates the type of Bitcoin address for which the signature is signed:

  • 27-30: P2PKH uncompressed
  • 31-34: P2PKH compressed
  • 35-38: Segwit P2SH
  • 39-42: Segwit Bech32

However, some wallets' implementations of the BIP-137 signature did not strictly follow this header flag specification, and some may have signed signatures with the wrong header (e.g., using header 27 for a native segwit address).

It is trivial, however, to convert a BIP-137 signature with an incorrect header flag to one with the correct header flag since the "address type" component in the header flag is not part of the actual signature.

As such, some BIP-137 signature verifiers online, such as this one, actively help to swap out any erroneous header flags.

This library defines this behavior as "Loose BIP-137 Verification".

This behavior assumes that a signature proving ownership of the private key associated with public key $X$ is valid for all addresses $y_1$, $y_2$, ..., $y_n$ derivable from the same public key $X$.

This behavior is enabled by default in this library, but can be disabled by passing the optional useStrictVerification flag in Verifier.verifySignature:

Verifier.verifySignature(signerAddress, message, signatureBase64, true)

Consequently, this also allows BIP-137 signatures to be used for taproot addresses, which is technically out-of-spec according to both BIP-137 and BIP-322 specifications, as implemented by some wallet implementations. Please refer to issue #1 for relevant discussions.

Note that this behavior does not exist in actual BIP-322 signature due to how BIP-322 signature is constructed.

Example

// Import modules that are useful to you
const { BIP322, Signer, Verifier } = require('bip322-js');

// Signing a BIP-322 signature with a private key
const privateKey = 'L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k';
const address = 'bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l'; // P2WPKH address
const addressTestnet = 'tb1q9vza2e8x573nczrlzms0wvx3gsqjx7vaxwd45v'; // Equivalent testnet address
const addressRegtest = 'bcrt1q9vza2e8x573nczrlzms0wvx3gsqjx7vay85cr9'; // Equivalent regtest address
const taprootAddress = 'bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3'; // P2TR address
const nestedSegwitAddress = '37qyp7jQAzqb2rCBpMvVtLDuuzKAUCVnJb'; // P2SH-P2WPKH address
const message = 'Hello World';
const signature = Signer.sign(privateKey, address, message);
const signatureTestnet = Signer.sign(privateKey, addressTestnet, message); // Wworks with testnet address
const signatureRegtest = Signer.sign(privateKey, addressRegtest, message); // And regtest address
const signatureP2TR = Signer.sign(privateKey, taprootAddress, message); // Also works with P2TR address
const signatureP2SH = Signer.sign(privateKey, nestedSegwitAddress, message); // And P2SH-P2WPKH address
console.log({ signature, signatureTestnet, signatureRegtest, signatureP2TR, signatureP2SH });

// Verifying a simple BIP-322 signature
const validity = Verifier.verifySignature(address, message, signature);
const validityTestnet = Verifier.verifySignature(addressTestnet, message, signatureTestnet); // Works with testnet address
const validityRegtest = Verifier.verifySignature(addressRegtest, message, signatureRegtest); // And regtest address
const validityP2TR = Verifier.verifySignature(taprootAddress, message, signatureP2TR); // Also works with P2TR address
const validityP2SH = Verifier.verifySignature(nestedSegwitAddress, message, signatureP2SH); // And P2SH-P2WPKH address
console.log({ validity, validityTestnet, validityRegtest, validityP2TR, validityP2SH }); // True

// You can also get the raw unsigned BIP-322 toSpend and toSign transaction directly
const scriptPubKey = Buffer.from('00142b05d564e6a7a33c087f16e0f730d1440123799d', 'hex');
const toSpend = BIP322.buildToSpendTx(message, scriptPubKey); // bitcoin.Transaction
const toSpendTxId = toSpend.getId();
const toSign = BIP322.buildToSignTx(toSpendTxId, scriptPubKey); // bitcoin.Psbt
// Do whatever you want to do with the PSBT

More working examples can be found within the unit test for BIP322, Signer, and Verifier.

Migration Guide from v1.X

There are only two non-backward-compatible changes in the API:

  1. If you previously used Address.compressPublicKey or Address.uncompressPublicKey, replace them with Key.compressPublicKey and Key.uncompressPublicKey respectively.

  2. In v1.X, there was an option to pass the network parameter into Signer.sign: Signer.sign(privateKey, address, message, network). This option has been removed, as the network is now automatically inferred from the given address.

bip322-js's People

Contributors

acken2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

bip322-js's Issues

Validating messages

Hi, I am trying to validate a message signed from this address 3Agx7m86mJgVbLZP3Wk1qjYkzv6gGemz9X (p2sh). The verifySignature method states that "Verify a BIP-322 signature from P2WPKH, P2SH-P2WPKH, and single-key-spend P2TR address."

Should I be able to verify a signature of the following message?
message=48656c6c6f20426974636f696e2034352e3133302e3130352e313436
address=3Agx7m86mJgVbLZP3Wk1qjYkzv6gGemz9X
signature=JDkLNaM8vWoobA34PGQE9FIZaLF7peRh4r7DOqOHls1cP1DPwR3Hcy26+zk6yRb0qtJRHEdUflVxkScbwsOCSMw=

It failse with the error:

{
    "errorType": "RangeError",
    "errorMessage": "The value of \"offset\" is out of range. It must be >= 0 and <= 63. Received 300",
    "code": "ERR_OUT_OF_RANGE",
    "stack": [
        "RangeError [ERR_OUT_OF_RANGE]: The value of \"offset\" is out of range. It must be >= 0 and <= 63. Received 300",
        "    at new NodeError (node:internal/errors:405:5)",
        "    at boundsError (node:internal/buffer:88:9)",
        "    at Buffer.readUInt8 (node:internal/buffer:254:5)",
        "    at Object.decode (/var/task/node_modules/bip174/src/lib/converter/varint.js:43:24)",
        "    at readVarInt (/var/task/node_modules/bitcoinjs-lib/src/psbt.js:1492:24)",
        "    at readVarSlice (/var/task/node_modules/bitcoinjs-lib/src/psbt.js:1497:22)",
        "    at readVector (/var/task/node_modules/bitcoinjs-lib/src/psbt.js:1502:49)",
        "    at scriptWitnessToWitnessStack (/var/task/node_modules/bitcoinjs-lib/src/psbt.js:1505:10)",
        "    at /var/task/node_modules/bitcoinjs-lib/src/psbt.js:1557:29",
        "    at Array.forEach (<anonymous>)"
    ]
}

Inconsistency between Generated Signature and BIP-0322 Standard

Description:
I have encountered an issue with the generated signature using the Signer.sign. The signature produced by the implementation does not align with the expected format outlined in the BIP-0322 standard.

Steps to Reproduce:
Use the provided BIP-0322 implementation to generate a signature. Given below parameters:
private key = L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k
corresponding address = bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l
Message = "Hello World"

Expected Signatures:
AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=

Actual Signatures:
AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy

When using Verifier.verifySignature, both of the above signatures are valid.

I modified a signature and it still valid

Hey I am very confused how is possible that if I change some chars on the signature below it continues to be a valid signature

address=bc1qfgn6k2fe4mvjxkcwa4tt4jphx8epg0r0f87squ
message=48656c6c6f20426974636f696e203230362e38342e3134372e3635
signature=H2l6+L8G24sBpB1+J1iwUv4/4LB0bba9bsxnopUbLNA2Qnf/JROsLJYUAVTF1dBfzCw69VBlOe9ehwQEEl5YbIY=

modified and valid signatures
signature=H2l6+L8G24sBpB1+J1iwUv4/4LB0bba9bsxnopUbLNA2Qnf/JROsLJYUAVTF1dBfzCw69VBlOe9ehwQEEl5YbIa= (changed last letter by a)

signature=a2l6+L8G24sBpB1+J1iwUv4/4LB0bba9bsxnopUbLNA2Qnf/JROsLJYUAVTF1dBfzCw69VBlOe9ehwQEEl5YbIa= (changed first and last letter by a)

Can you clarify it for me please? I thought it would be invalid after the modification.

Is there a difference between this bip322-js Verifier and the one used in Ledger ?

I'm trying to validate a message signature made with ledger it validates with no problem in there but the script throws the following error:

RangeError [ERR_OUT_OF_RANGE]: The value of "offset" is out of range. It must be >= 0 and <= 64. Received 116
    at new NodeError (node:internal/errors:399:5)
    at boundsError (node:internal/buffer:88:9)
    at Buffer.readUInt8 (node:internal/buffer:254:5)
    at Object.decode (C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bip174\src\lib\converter\varint.js:43:24)
    at readVarInt (C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bitcoinjs-lib\src\psbt.js:1479:24)
    at readVarSlice (C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bitcoinjs-lib\src\psbt.js:1484:22)
    at readVector (C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bitcoinjs-lib\src\psbt.js:1489:49)
    at scriptWitnessToWitnessStack (C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bitcoinjs-lib\src\psbt.js:1492:10)
    at C:\Users\PIKO\Desktop\bip-322js\bip322-js\node_modules\bitcoinjs-lib\src\psbt.js:1544:29
    at Array.forEach (<anonymous>) {
  code: 'ERR_OUT_OF_RANGE'
}

is there any tips or suggestions.

(question) Verification of buffers.

Hello! Is it possible to sign/verify buffer/uint8array?
Strings are arguably not best representation of data, and when getting big, taking quite a some time for this library (and overall, any cryptographic algorithm) to verify.
I used hash of data to convert it to smaller one, however I feel like stringifying hash in order for this library to process it is not optimal.
Is there any issues with signing/verifying raw data?

Signature verification returns false even if it should return true

Greetings,
one of our users is signing with his Ordinal Key (using Xverse wallet) the JSON.stringify() result of this following message:

{
    "runeId": "840000:3",
    "orderType": 0,
    "orderPosition": "buy",
    "orderAmount": 1332,
    "orderPrice": 7.51,
    "ownerBtckey": "020bb343eb11c05861b5d5607391a71b14fc941b6e50c52070fbb7c0665e7a48e2",
    "ownerOrdKey": "56f4b67b0ade2e9ba69244db27dd980e58c4445ffb29c60dac4a2c7bc08688ac",
    "ownerBtcAddress": "3KZKi6aWvoZp2BjSYm8X126AeExpqdqvyh",
    "ownerOrdAddress": "bc1pvnkl5kgyjnr33yn5myvlqvw0nahnrqx2mj6a8j4ggpy09gvt3pdq4nf2sl"
}

Generating the signature:
AUBeB9HL+fbcTrVOSwzZvSn/oDluEckq0st4YMgr0nelFOT1qpA9HiohI6uGNDQnu47YUbFHWM0CrkiT7VdWf6Na

Using bip322-js to verify this signature, it returns FALSE:
bip322.Verifier.verifySignature(data.ordinalAddress, JSON.stringify(data.message), data.signature)

This bug here is happening to a few of our users (not all of them), which makes me think it must be something related to how the library uses the Ordinal Address of the user.

Please help us addressing the issue and let us know if you need more details.
Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.