bip322 0.0.11 fixes P2WPKH signature verification bypass
Versions 0.0.6 to 0.0.10 of the bip322 crate accept forged BIP-322 proofs for P2WPKH and P2SH-P2WPKH addresses. Change the manifest to require 0.0.11, update the lockfile and run cargo audit.

On October 9, 2026, RustSec issued RUSTSEC-2026-0334 (alias GHSA-5chw-87w3-j9cv) for the bip322 crate: versions 0.0.6 through 0.0.10 accept a BIP-322 proof signed with any key as valid for a victim's P2WPKH or P2SH-P2WPKH address. If your service uses bip322 to check that a user controls a Bitcoin address, upgrade to 0.0.11 now and review any logins or address claims you accepted with the old versions.
What changed in RUSTSEC-2026-0334
The advisory, titled "BIP-322 address ownership verification bypass for P2WPKH and P2SH-P2WPKH addresses", was reported on August 14, 2026 and issued on October 9, 2026. It has a CVSS 3.1 score of 9.1 (Critical) with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N. That means the attack works over the network, is easy to carry out, and needs no privileges and no user interaction.
The bug is in the P2WPKH path. A BIP-322 proof carries a witness, and for P2WPKH that witness contains a signature and a public key. In verify_full, the crate took the public key from the caller-supplied witness and passed it to verify_full_p2wpkh. That function then read the key from the same witness and compared it with itself:
// bip322 0.0.6 to 0.0.10 (from the advisory)
let witness_pub_key = &witness.to_vec()[1];
if &pub_key.to_bytes() != witness_pub_key {
return Err(Error::PublicKeyMismatch);
}
Both values come from the same witness element, so the check always passed. The crate verified that the signature matched the embedded key. It never checked that the key hashes to the claimed address. In the advisory's words, this violated BIP-322's rule that the signature must satisfy the challenge for "the claimed address's scriptPubKey".
The attack works like this: pick any victim P2WPKH or P2SH-P2WPKH address, build the BIP-322 challenge for that address and a message, sign it with your own unrelated private key, and submit it. The proof is accepted.
The fix is referenced as commit e8accbe in rust-bitcoin/bip322. The verifier now builds the expected scriptPubKey from the witness public key (P2WPKH(HASH160(pubkey)), wrapped in P2SH for the nested case). That script must exactly equal the claimed address's scriptPubKey before signature verification runs. Otherwise verification fails with Error::PublicKeyMismatch.
| bip322 version | Status |
|---|---|
<0.0.6 | Unaffected |
>=0.0.6, <0.0.11 | Vulnerable, yanked from crates.io |
>=0.0.11 | Patched |
Who is affected and why it matters
You are affected if your code calls any of these four functions on a version from 0.0.6 up to (but not including) 0.0.11:
bip322::verify_simplebip322::verify_simple_encodedbip322::verify_fullbip322::verify_full_encoded
The risk is highest where a passed check grants something: "Sign in with Bitcoin" logins, proving ownership of a deposit or withdrawal address, airdrop or allowlist claims, and proof-of-reserves tools that accept signed messages. The advisory calls the result a "complete authentication bypass". The attacker needs no victim key and no victim interaction.
Two common assumptions do not protect you. First, application-level nonces do not help, because the attacker signs the current challenge with their own key. Second, the bug is in the P2WPKH and P2SH-P2WPKH paths only. P2TR (Taproot) verification is not affected, because the public key comes from the address's witness program, not from the caller's witness. That is little comfort if your service accepts SegWit v0 addresses at all.
The crate may also reach your build through another dependency, such as a wallet library or an indexer. Check the full dependency tree, not only your direct dependencies.
What to do now
The advisory says "There is no known workaround". Upgrade to 0.0.11.
- Find every copy of the crate. Run
cargo tree -i bip322in each workspace to see which crates pull it in and at which version. - Edit the manifest. Cargo treats a caret requirement on a
0.0.zversion as an exact pin. Its docs say^0.0.3means>=0.0.3, <0.0.4. Sobip322 = "0.0.10"will never resolve to 0.0.11, andcargo updatealone will not fix it. Change the requirement:
# Cargo manifest, [dependencies]
- bip322 = "0.0.10"
+ bip322 = "0.0.11"
- Update the lockfile. Yanking does not remove a version from an existing
Cargo.lock, so builds with a pinned vulnerable version keep working until you change them:
cargo update -p bip322
cargo tree -i bip322 # confirm only 0.0.11 remains
- Handle copies that come in through other crates. If another crate depends on 0.0.6 to 0.0.10, upgrade that crate or ask its maintainers to release a version that requires 0.0.11. Until then, you are still exposed.
- Add the advisory check to CI.
cargo auditreads the RustSec database and fails on RUSTSEC-2026-0334:
cargo install cargo-audit --locked
cargo audit
- Add a negative test. Sign a challenge for a fixed P2WPKH address with an unrelated key, and assert that verification returns an error. On 0.0.11 it should fail with
Error::PublicKeyMismatch. - Review past decisions. For any ownership proofs accepted for P2WPKH or P2SH-P2WPKH addresses while a vulnerable version was deployed, check whether the address hashes to the public key in the witness. Revoke sessions or claims that fail that check.
Caveats
On GitHub, the referenced fix commit e8accbe is titled "Add multisig support (#65)". It also adds things that are not part of the security fix, such as a new error for missing private keys. If you call signing APIs as well as the verify functions, read the diff between 0.0.10 and 0.0.11 before you upgrade. With no workaround available, staying on a vulnerable version is not a safe option.
Get the wire in your inbox
Every new signal, straight from the generator. No noise, unsubscribe anytime.


