<p>If you are looking for working JavaScript code, I recommend <a href="https://github.com/ChrisCho-H/bitcoin-sdk-js" rel="nofollow noreferrer"><code>bitcoin-sdk-js</code></a> which supports BIP322 including taproot address signing &amp; verification.</p>
<p>Below is how to implement BIP322 signing &amp; verification for p2pkh(<code>legacy</code>), p2wpkh(<code>segwit</code>) and p2tr(<code>taproot</code>).</p>
<pre class="lang-javascript prettyprint-override"><code>import * as bitcoin from 'bitcoin-sdk-js'

const keyPair = await bitcoin.wallet.generateKeyPair();
const privkey = keyPair.privateKey;
const pubkey = keyPair.publicKey;
const legacyAddress = await bitcoin.address.generateAddress(
  pubkey,
  'legacy',
);
const segwitAddress = await bitcoin.address.generateAddress(
  pubkey,
  'segwit',
);
const tapAddress = await bitcoin.address.generateAddress(
  (
    await bitcoin.tapscript.getTapTweakedPubkey(
      pubkey.slice(2),
      await bitcoin.tapscript.getTapTweak(pubkey.slice(2)),
    )
  ).tweakedPubKey,
  'taproot',
);
const msg = 'message you want to sign';
// When
const sigLegacy = await bitcoin.crypto.signMessage(
   msg,
   privkey,
   legacyAddress,
);
const sigSegwit = await bitcoin.crypto.signMessage(
  msg,
  privkey,
  segwitAddress,
);
const sigTap = await bitcoin.crypto.signMessage(msg, privkey, tapAddress);
// Then
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigLegacy, legacyAddress),
  true,
);
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigSegwit, segwitAddress),
  true,
);
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigTap, tapAddress),
  true,
);
</code></pre>
<p>Disclaimer: I'm the owner of the library.</p>

CC BY-SA 4.0
2024-08-05T08:07:33.147Z
2024-08-05T08:07:33.147Z
Hyunhum Cho
<p>Assuming that <code>recoverPublicKey</code> is performing the ECDSA key recovery, this does not apply to BIP340 Schnorr Signatures. If you want to use BIP322 with Taproot addresses, you need the original public key.</p>

CC BY-SA 4.0
2023-05-19T17:55:04.370Z
so there's no way to get that information from the signature itself? How can I verify that an address has signed a message in that case, knowing that I have the address 'bc1p...', the signature and the message?
toto
CC BY-SA 4.0
2023-05-19T18:11:02.407Z
The address has the public key, encoded in base 32. Decode the address and the witness program part (what comes after the version) is a 32-byte vector containing an x coordinate for an x-only public key. Look for a public key parsing in the lib you're using
Davidson Souza
CC BY-SA 4.0
2023-05-19T22:52:52.910Z
ok thanks for the help
toto
CC BY-SA 4.0
2023-05-18T21:33:37.183Z
2023-05-18T21:33:37.183Z
Davidson Souza
<p>Using wallets like XVerse, users can sign a message using their ordinal btc address, let's say bc1XXX, the message is hashed based on BIP0322.</p>
<p>So I have:</p>
<ul>
<li>User wallet: bc1XXX</li>
<li>Message hash: YYY</li>
<li>Signature signed by bc1XXX: ZZZ</li>
</ul>
<p>So this data is sent to my backend server, and I want to verify that ZZZ was indeed signed by bc1XXX (and contains YYY as message).</p>
<p>I'm using this so far:</p>
<pre><code>    const msgHash = bip0322Hash(message);
    const signatureBuffer = Buffer.from(signatureStr, 'base64');

    const decodedSignature = signatureBuffer.slice(2, 66);
    const recoveryId = signatureBuffer[0];

    // Extract public key from the signature
    const recoveredPublicKeyBuffer = secp.recoverPublicKey(
        msgHash,
        decodedSignature,
        recoveryId, // Recovery ID (0 or 1)
        false
    );
    console.log(publicKeyToTaprootAddress(recoveredPublicKeyBuffer)); //no match with my original pubkey that signed the message

</code></pre>
<p>But I have a hard time getting the correct address from <code>recoveredPublicKeyBuffer</code> which I can't match with the public key address of my test set.</p>
<p>I'm trying to use this function, but the output doesn't match my pubkey:</p>
<pre><code>    function publicKeyToTaprootAddress(publicKey: Uint8Array) {
        // Compute the SHA-256 hash of the public key
        const hash = sha256(Buffer.from(publicKey));

        // Construct the human-readable part and the data part of the Bech32m string
        const hrp = 'bc';
        const data = sha256(Buffer.from([0x01].concat(Array.from(hash))));
        const data2 = bech32m.toWords(Buffer.from(data));

        // Encode the Bech32m string
        return bech32m.encode(hrp, data2);
    } 
</code></pre>

<taproot><message-signing>
How to verify that a signature was signed by a pubkey? Taproot and BIP0322
toto
# How to verify that a signature was signed by a pubkey? Taproot and BIP0322

(Asked by: username_0 on 2023-05-17)

Using wallets like XVerse, users can sign a message using their ordinal btc address, let's say bc1XXX, the message is hashed based on BIP0322.
So I have:

User wallet: bc1XXX
Message hash: YYY
Signature signed by bc1XXX: ZZZ

So this data is sent to my backend server, and I want to verify that ZZZ was indeed signed by bc1XXX (and contains YYY as message).
I'm using this so far:
    const msgHash = bip0322Hash(message);
    const signatureBuffer = Buffer.from(signatureStr, 'base64');

    const decodedSignature = signatureBuffer.slice(2, 66);
    const recoveryId = signatureBuffer[0];

    // Extract public key from the signature
    const recoveredPublicKeyBuffer = secp.recoverPublicKey(
        msgHash,
        decodedSignature,
        recoveryId, // Recovery ID (0 or 1)
        false
    );
    console.log(publicKeyToTaprootAddress(recoveredPublicKeyBuffer)); //no match with my original pubkey that signed the message


But I have a hard time getting the correct address from recoveredPublicKeyBuffer which I can't match with the public key address of my test set.
I'm trying to use this function, but the output doesn't match my pubkey:
    function publicKeyToTaprootAddress(publicKey: Uint8Array) {
        // Compute the SHA-256 hash of the public key
        const hash = sha256(Buffer.from(publicKey));

        // Construct the human-readable part and the data part of the Bech32m string
        const hrp = 'bc';
        const data = sha256(Buffer.from([0x01].concat(Array.from(hash))));
        const data2 = bech32m.toWords(Buffer.from(data));

        // Encode the Bech32m string
        return bech32m.encode(hrp, data2);
    }

**Tags:** taproot, message-signing


---

## 2 answers

### Answer by username_1 on 2024-08-05. Score: 1

If you are looking for working JavaScript code, I recommend bitcoin-sdk-js which supports BIP322 including taproot address signing & verification.
Below is how to implement BIP322 signing & verification for p2pkh(legacy), p2wpkh(segwit) and p2tr(taproot).
import * as bitcoin from 'bitcoin-sdk-js'

const keyPair = await bitcoin.wallet.generateKeyPair();
const privkey = keyPair.privateKey;
const pubkey = keyPair.publicKey;
const legacyAddress = await bitcoin.address.generateAddress(
  pubkey,
  'legacy',
);
const segwitAddress = await bitcoin.address.generateAddress(
  pubkey,
  'segwit',
);
const tapAddress = await bitcoin.address.generateAddress(
  (
    await bitcoin.tapscript.getTapTweakedPubkey(
      pubkey.slice(2),
      await bitcoin.tapscript.getTapTweak(pubkey.slice(2)),
    )
  ).tweakedPubKey,
  'taproot',
);
const msg = 'message you want to sign';
// When
const sigLegacy = await bitcoin.crypto.signMessage(
   msg,
   privkey,
   legacyAddress,
);
const sigSegwit = await bitcoin.crypto.signMessage(
  msg,
  privkey,
  segwitAddress,
);
const sigTap = await bitcoin.crypto.signMessage(msg, privkey, tapAddress);
// Then
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigLegacy, legacyAddress),
  true,
);
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigSegwit, segwitAddress),
  true,
);
assert.strictEqual(
  await bitcoin.crypto.verifyMessage(msg, sigTap, tapAddress),
  true,
);

Disclaimer: I'm the owner of the library.


---

### Answer by username_2 on 2023-05-18. Score: 0

Assuming that `recoverPublicKey` is performing the ECDSA key recovery, this does not apply to BIP340 Schnorr Signatures. If you want to use BIP322 with Taproot addresses, you need the original public key.

> **Comments:**
> **username_0**: so there's no way to get that information from the signature itself? How can I verify that an address has signed a message in that case, knowing that I have the address 'bc1p...', the signature and the message?  
> **username_2**: The address has the public key, encoded in base 32. Decode the address and the witness program part (what comes after the version) is a 32-byte vector containing an x coordinate for an x-only public key. Look for a public key parsing in the lib you're using  
> **username_0**: ok thanks for the help  

---

