Skip to content
DonateБГ

Cryptography

Schnorr signatures

Bitcoin Schnorr signatures, defined in BIP 340, are the scheme behind Taproot. Learn how to sign, verify and encode them, with a worked example.

By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.

Schnorr signatures are a signature scheme that works on the same elliptic curve as ECDSA. Bitcoin adopted them with the Taproot upgrade, which activated at block 709,632 in November 2021. Their formulas are simpler, a Schnorr signature is always 64 bytes, and several signatures can be combined. Taproot outputs use them for key-path spends, and the full scheme is defined in BIP 340.

Why are Schnorr signatures simpler than ECDSA?

The ECDSA signature needs a modular inverse of k. A Schnorr signature needs only addition and multiplication:

ECDSA:    s = k⁻¹ (z + r·d)  mod n
Schnorr:  s = k + e·d        mod n

This linear form has three practical results. The signing step is cheaper, since no inverse is needed. Several signatures can be checked together in one batch. And signatures can be added, so several keys can produce one signature. Adding keys naively is unsafe, so the MuSig design adds safeguards.

Batch verification works like this. Each signature's check is multiplied by a random factor, and the results are added into one equation. If every signature is valid, the equation holds. If one is invalid, the equation fails, except with negligible probability.

Schnorr signatures are also easier to prove secure. BIP 340 cites a security proof in the random oracle model, which assumes that the elliptic curve discrete logarithm problem is hard. ECDSA has no proof of comparable strength.

Schnorr is not new. Claus Schnorr described the scheme in 1989, and the US patent on it was granted in 1991. The patent expired in February 2010, a year after Bitcoin launched. Satoshi Nakamoto used ECDSA instead, which OpenSSL already provided.

How are keys encoded?

Schnorr keys in Bitcoin use only the x coordinate, 32 bytes. For any x there are two possible y values, one even and one odd. BIP 340 always chooses the even one, so the sign of y is not needed. A 33-byte compressed public key becomes a 32-byte x-only key by dropping its first byte. Two private keys, d and n − d, give the same x-only key. They also make the same signatures, because signing negates the key when needed.

Turning an x-only key back into a point is called lift_x. It solves y² = x³ + 7 for y, and it picks the even root:

def lift_x(x: int) -> tuple[int, int]:
    p = 2**256 - 2**32 - 977
    c = (x**3 + 7) % p
    y = pow(c, (p + 1) // 4, p)    # square root, since p ≡ 3 (mod 4)
    if y * y % p != c:
        raise ValueError("x is not on the curve")
    if y % 2:
        y = p - y                  # choose the even y
    return x, y

The square-root shortcut works because p is 3 modulo 4. If the result does not square back to x³ + 7, no point has that x, and the key is invalid.

How do you sign with Schnorr?

BIP 340 Schnorr signing in five steps: make the key even, make the nonce, make the challenge, then the signature s. 1. Key P = d'·G. If P.y is odd, d = n − d'. 2. Auxiliary mix t = d XOR hashBIP0340/aux(a) 3. Nonce k' = hashBIP0340/nonce(t ‖ P.x ‖ m) mod n R = k'·G. If R.y is odd, k = n − k'. 4. Challenge e = hashBIP0340/challenge(R.x‖P.x‖m) mod n 5. Signature r = R.x s = (k + e·d) mod n signature = r ‖ s, 64 bytes
Each step feeds the next. The final signature is the 32-byte R.x followed by the 32-byte s value.

The signer has a private key d', the message m and 32 bytes of auxiliary data a. BIP 340 defines these steps:

  1. Make the key even. Compute the public key P = d'·G. If P.y is odd, set d = n − d'. Otherwise d = d'. Negating the key flips only the sign of y, so P.x stays the same.
  2. Mix in the auxiliary data. Compute t = d XOR hashBIP0340/aux(a).
  3. Make the nonce. Set k' = hashBIP0340/nonce(t ‖ P.x ‖ m) mod n and R = k'·G. If R.y is odd, set k = n − k'. Otherwise k = k'.
  4. Make the challenge. Set e = hashBIP0340/challenge(R.x ‖ P.x ‖ m) mod n.
  5. Make the signature. Set r = R.x and s = (k + e·d) mod n. The signature is the 32-byte r followed by the 32-byte s.

The challenge depends on the public key and the message as well as the nonce. This is called key-prefixing. It stops a related-key attack. Without it, a signature for one key could be turned into a valid signature for a key shifted by a known amount, such as an unhardened BIP 32 child key or a Taproot tweak. The nonce depends on the private key and the message, not only on the auxiliary bytes. A poor random source therefore does not expose the key on its own. The auxiliary bytes add protection against side-channel attacks, and signing stays secure even if they are all zero.

How do you verify a Schnorr signature?

BIP 340 Schnorr verification: lift the public key x to an even point, compute R = sG − eP and check R.x equals r. Inputs: the public key x (32 bytes), the message m, and the signature r ‖ s Lift x to the point P with even y. Challenge: e from r, P.x and m e = hashBIP0340/challenge(r‖P.x‖m) mod n R = s·G + (n − e)·P Note (n − e)·P is the same as −e·P. Valid if R.y is even and R.x = r Anything else fails.
The verifier needs the 32-byte key, the message and the 64-byte signature. Nothing secret is required.

The verifier has the 32-byte public key, the message m and the signature (r, s). The steps are:

  1. Compute P = lift_x(public key). Fail if this is not possible.
  2. Read r and s. Fail if r ≥ p or s ≥ n.
  3. Compute e = hashBIP0340/challenge(r ‖ P.x ‖ m) mod n. This is the same e the signer made.
  4. Compute R = s·G + (n − e)·P. The term (n − e)·P is the same as −e·P, so R = sG − eP.
  5. Accept only if R is not the point at infinity, R.y is even and R.x = r.

The reason it works is the same as for ECDSA, but the maths is simpler. If the signer built s = k + e·d, then s·G = kG + e·dG = R + e·P, so R = sG − e·P.

Here is the tagged hash that BIP 340 uses in each step. The hash function page explains the construction in full.

import hashlib

def tagged_hash(tag: str, msg: bytes) -> bytes:
    t = hashlib.sha256(tag.encode()).digest()
    return hashlib.sha256(t + t + msg).digest()

What does a worked example look like?

These values are test vector 1 from BIP 340. Running the steps above on them gives the same public key and signature, and the signature verifies. Change one byte of the signature and verification fails.

secret key  B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
public key  DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659
aux         0000000000000000000000000000000000000000000000000000000000000001
message     243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89
signature   6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A

The public key is the x coordinate only. The signature is the 64 bytes r followed by s. BIP 340 publishes a file of test vectors with more cases, and it is the right place to check your own code: test-vectors.csv.

Why do Schnorr signatures resist tampering?

ECDSA signatures can be changed in a harmless-looking way. Replacing s with n − s gives another valid signature. For Schnorr, that edit does not work. The check in step 5 fails, so a valid signature cannot be turned into another valid one by that simple change. This removes the malleability that the low-s rule had to patch for ECDSA.

Where are Schnorr signatures used?

Schnorr signatures sign spends from P2TR (Taproot) outputs. A key-path spend has one 64-byte signature, which replaces the 70 to 72 bytes of ECDSA's DER encoding. The sighash type can add one byte, so the signature is 65 bytes when it is not the default type. Read the P2TR script page and the Taproot upgrade page for the full picture.

ECDSA is still used for older scripts, so both schemes live side by side. The keys you already have work for either one, since the curve is the same.

If you have not read ECDSA, start there. It uses the same curve in a different way, and the comparison makes the Schnorr design easier to follow. Next, the private key page shows how a secret number becomes the public key that every signature here checks against.

In this section: Cryptography

All of Bitcoin from zero

Something look wrong? Tell us