Tagged Hash
A tagged hash is a domain-separated SHA-256 hash used throughout Bitcoin's Taproot upgrade to prevent cross-protocol hash collisions.
Key Takeaways
- A tagged hash prepends SHA-256(tag) twice before hashing the data, creating domain separation that prevents one protocol's hash from being reinterpreted in another context. This construction is defined in BIP 340.
- Taproot uses tagged hashes everywhere: key tweaking, signature challenges, leaf hashing, branch hashing, and transaction signing. Each operation uses a unique tag string like "TapTweak" or "TapLeaf".
- The 64-byte prefix aligns with SHA-256's block size, enabling implementations to pre-compute a midstate and make tagged hashing just as fast as a standard SHA-256 call.
What Is a Tagged Hash?
A tagged hash is a cryptographic hash function construction that adds a context-specific prefix to the input before hashing. Instead of computing SHA-256(data) directly, a tagged hash computes:
tagged_hash(tag, data) = SHA-256(SHA-256(tag) || SHA-256(tag) || data)The tag is a UTF-8 string describing the protocol context, such as "TapLeaf" or "BIP0340/challenge". By hashing the tag name itself and prepending it twice, the construction guarantees that identical data hashed under different tags produces completely different outputs. This property is called domain separation.
Tagged hashes were introduced in BIP 340 (Schnorr Signatures) and BIP 341 (Taproot) as part of Bitcoin's Taproot upgrade. They have since become the standard approach to domain separation in all new Bitcoin cryptographic proposals, including MuSig2 (BIP 327) and Silent Payments (BIP 352).
How It Works
The tagged hash algorithm is straightforward. Given a tag string and arbitrary input data:
- Compute SHA-256 of the tag string to get a 32-byte tag hash
- Concatenate two copies of the tag hash with the input data, creating a message of 64 + len(data) bytes
- Compute SHA-256 of the entire concatenated message
# Example: computing a TapTweak tagged hash
tag = "TapTweak"
tag_hash = SHA256(tag) # 32 bytes
prefix = tag_hash || tag_hash # 64 bytes
result = SHA256(prefix || data)Each protocol context uses a different tag string. This means that even if two protocols hash the same data, the outputs are unrelated. An attacker cannot take a valid hash from one context and present it as valid in another.
Tag Strings in Bitcoin
BIP 340 defines three tags for Schnorr signature operations:
| Tag | Purpose |
|---|---|
BIP0340/challenge | Computes the challenge value e during signature verification |
BIP0340/aux | Hashes auxiliary randomness during nonce derivation |
BIP0340/nonce | Derives the deterministic nonce from secret key material, public key, and message |
BIP 341 adds four more tags for Taproot tree construction and spending:
| Tag | Purpose |
|---|---|
TapLeaf | Hashes individual script leaves in the Taproot tree |
TapBranch | Hashes inner branch nodes combining two child hashes |
TapTweak | Derives the output key by tweaking the internal key with the Merkle root |
TapSighash | Hashes the signature message for Tapscript spending |
Midstate Optimization
The double-tag prefix is exactly 64 bytes: two 32-byte SHA-256 outputs. This is no coincidence. SHA-256 processes data in 64-byte blocks, so the prefix fills exactly one block. Implementations can process this first block once per tag, save the resulting internal state (the "midstate"), and reuse it for every subsequent hash with that tag.
With midstate caching, computing a tagged hash costs the same as computing a plain SHA-256 over just the data. The tag prefix adds zero runtime overhead. Without the optimization, implementations simply concatenate the prefix and hash everything, adding one extra block compression per call.
// Pseudocode: midstate optimization
const TAPLEAF_MIDSTATE = sha256_compress(
SHA256_IV,
SHA256("TapLeaf") || SHA256("TapLeaf")
);
function hash_tapleaf(data) {
// Start from cached midstate instead of standard IV
return sha256_finalize(TAPLEAF_MIDSTATE, data);
}Why Domain Separation Matters
Without tagged hashes, Bitcoin would use plain SHA-256 everywhere. This creates two specific risks that BIP 340 identifies:
- Signature reinterpretation: a valid BIP 340 signature could also satisfy a different signature scheme that hashes the same inputs in a different order. An attacker could present one signature as valid under two different protocols.
- Nonce leakage: if two protocols independently derive nonces using the same hash function without domain separation, a signer could accidentally produce the same nonce for both. Nonce reuse in Schnorr signatures directly reveals the private key.
BIP 340 also notes that no existing use of SHA-256 in Bitcoin feeds it a message starting with two single SHA-256 outputs. This makes collisions between tagged hashes and older Bitcoin hash constructions extremely unlikely, providing retroactive domain separation with all prior protocol uses of SHA-256.
Use Cases
Taproot Key Path Spending
When constructing a Taproot output, the internal public key is tweaked with the Merkle root of the script tree using the TapTweak tagged hash. The output key Q is derived as:
t = hash_TapTweak(internal_key || merkle_root)
Q = P + t * GThis binds the script tree to the output key. A key path spend proves knowledge of the tweaked private key, while a script path spend reveals a specific leaf and its Merkle proof. The tagged hash ensures the tweak value cannot collide with other protocol operations. For a detailed walkthrough, see the P2TR spend path deep dive.
Taproot Script Trees
The Taproot tree is a Merkle tree where leaf nodes and branch nodes use different tagged hashes. Leaf nodes are hashed with TapLeaf, including the leaf version and script:
leaf_hash = hash_TapLeaf(leaf_version || compact_size(script_len) || script)Branch nodes combine two children with TapBranch, sorting them lexicographically:
branch_hash = hash_TapBranch(min(left, right) || max(left, right))Using separate tags for leaves and branches prevents a leaf hash from being confused with a branch hash, closing a class of Merkle tree manipulation attacks.
MuSig2 and Multi-Party Signing
MuSig2 (BIP 327) defines six tagged hashes for key aggregation and nonce handling. Tags like KeyAgg coefficient and MuSig/nonce ensure that the key aggregation process cannot interfere with the signing process, even though both operate on the same elliptic curve group.
Silent Payments
Silent Payments (BIP 352) use three tagged hashes to derive unique output addresses for each transaction without requiring interaction between sender and receiver. The BIP0352/SharedSecret tag separates shared secret derivation from any other ECDH-based protocol in Bitcoin.
Risks and Considerations
Tag Collision
The security of tagged hashing depends on each protocol context choosing a unique tag string. If two protocols accidentally use the same tag, domain separation breaks and cross-protocol attacks become possible. The convention of prefixing tags with the BIP number (e.g., "BIP0340/challenge") mitigates this by creating a natural namespace.
Implementation Correctness
Implementations must use the exact tag strings specified in the relevant BIP. A single byte difference in the tag produces a completely different hash prefix. Common sources of bugs include encoding differences (the tag must be raw UTF-8, not null-terminated), incorrect concatenation order, and forgetting to double the tag hash.
No Backward Compatibility with Existing Hashes
Tagged hashes are intentionally incompatible with Bitcoin's older double-SHA-256 convention used in block headers and transaction IDs. Code that expects a standard SHA-256 or double-SHA-256 output cannot accept a tagged hash, and vice versa. This is a feature for security but requires developers to use the correct hash function for each context.
Post-Quantum Considerations
Tagged hashes rely on SHA-256, which remains secure against known quantum attacks. Grover's algorithm would reduce SHA-256's preimage resistance from 256 bits to 128 bits, which is still considered safe. However, the Schnorr signature scheme that tagged hashes support is vulnerable to quantum attacks on the elliptic curve discrete logarithm problem. Future post-quantum signature schemes would likely continue using tagged hashes for domain separation while replacing the underlying signature algorithm.
This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.