diff --git a/crates/bip322/RUSTSEC-0000-0000.md b/crates/bip322/RUSTSEC-0000-0000.md new file mode 100644 index 000000000..974e66606 --- /dev/null +++ b/crates/bip322/RUSTSEC-0000-0000.md @@ -0,0 +1,72 @@ +```toml +[advisory] +id = "RUSTSEC-0000-0000" +package = "bip322" +date = "2026-08-14" +url = "https://github.com/rust-bitcoin/bip322/commit/e8accbe7d39f48f030e44f333b4796e28f6aad80" +categories = ["crypto-failure"] +cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N" +keywords = ["bip322", "bitcoin", "signature-verification"] +aliases = ["GHSA-5chw-87w3-j9cv"] + +[affected.functions] +"bip322::verify_simple" = [">= 0.0.6, < 0.0.11"] +"bip322::verify_simple_encoded" = [">= 0.0.6, < 0.0.11"] +"bip322::verify_full" = [">= 0.0.6, < 0.0.11"] +"bip322::verify_full_encoded" = [">= 0.0.6, < 0.0.11"] + +[versions] +patched = [">= 0.0.11"] +unaffected = ["< 0.0.6"] +``` + +# BIP-322 address ownership verification bypass for P2WPKH and P2SH-P2WPKH addresses + +Affected versions of `bip322` accepted a BIP-322 proof created with an +attacker-controlled private key as a valid signature for an unrelated victim +P2WPKH or P2SH-P2WPKH address, allowing complete authentication bypass in +applications using `verify_simple`, `verify_simple_encoded`, `verify_full`, or +`verify_full_encoded` as proof that a user controls a Bitcoin address. + +In `verify_full`, the verifier obtained the public key from the +caller-controlled witness and passed it to `verify_full_p2wpkh`, where the +supposed public-key mismatch check read the key from the same witness and +compared it with itself: + +```rust +let witness_pub_key = &witness.to_vec()[1]; + +if &pub_key.to_bytes() != witness_pub_key { + return Err(Error::PublicKeyMismatch); +} +``` + +Since `pub_key` was originally parsed from that exact witness element, this +comparison was tautological. The code verified that the signature was valid +for the public key embedded in the witness, but never verified that this +public key hashes to the claimed address, violating BIP-322's requirement +that the `message_signature` satisfy the `message_challenge` (the claimed +address's scriptPubKey). + +As a result, an attacker could select any victim P2WPKH or P2SH-P2WPKH +address, construct the BIP-322 challenge for that address and message, sign +it with their own unrelated private key, and have the proof accepted. No +victim key or victim interaction was required. Application-level nonces do +not mitigate this, since the attacker can construct and sign the current +challenge message with their own key. + +P2TR verification is not affected: the public key is derived from the +address's witness program rather than from the caller-controlled witness. + +The flaw was corrected in commit +[e8accbe](https://github.com/rust-bitcoin/bip322/commit/e8accbe7d39f48f030e44f333b4796e28f6aad80) +by deriving the expected scriptPubKey from the witness public key +(`P2WPKH(HASH160(pubkey))`, wrapped in P2SH for the nested case) and +requiring it to exactly equal the scriptPubKey of the claimed address before +signature verification, failing with `Error::PublicKeyMismatch` otherwise. +The fix is included in version +[0.0.11](https://crates.io/crates/bip322/0.0.11). Affected versions 0.0.6 +through 0.0.10 have been yanked from crates.io. + +There is no known workaround that mitigates the vulnerability. Upgrading to +version 0.0.11 is the recommended course of action.