diff --git a/Cargo.lock b/Cargo.lock index 9afcdcc8..2e2faecb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8108,6 +8108,7 @@ dependencies = [ "dkg", "flexible-transcript", "hex", + "k256", "log", "modular-frost", "parity-scale-codec", diff --git a/processor/bitcoin/Cargo.toml b/processor/bitcoin/Cargo.toml index 52cca1ae..2a69d234 100644 --- a/processor/bitcoin/Cargo.toml +++ b/processor/bitcoin/Cargo.toml @@ -24,6 +24,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std", "recommended"] } +k256 = { version = "0.13", default-features = false, features = ["std"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std", "secp256k1"] } dkg = { path = "../../crypto/dkg", default-features = false, features = ["std", "evrf-secp256k1"] } frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false } diff --git a/processor/bitcoin/src/key_gen.rs b/processor/bitcoin/src/key_gen.rs index 41544134..bc911676 100644 --- a/processor/bitcoin/src/key_gen.rs +++ b/processor/bitcoin/src/key_gen.rs @@ -1,6 +1,8 @@ use ciphersuite::{group::GroupEncoding, Ciphersuite, Secp256k1}; use frost::ThresholdKeys; +use bitcoin_serai::bitcoin::{hashes::Hash, TapTweakHash}; + use crate::{primitives::x_coord_to_even_point, scan::scanner}; pub(crate) struct KeyGenParams; @@ -10,6 +12,39 @@ impl key_gen::KeyGenParams for KeyGenParams { type ExternalNetworkCiphersuite = Secp256k1; fn tweak_keys(keys: &mut ThresholdKeys) { + /* + Offset the keys by their hash to prevent a malicious participant from inserting a script + path, as specified in + https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23 + + This isn't exactly the same, as we then increment the key until it happens to be even, yet + the goal is simply that someone who biases the key-gen can't insert their own script path. + By adding the hash of the key to the key, anyone who attempts such bias will change the key + used (changing the bias necessary). + + This is also potentially unnecessary for Serai, which uses an eVRF-based DKG. While that can + be biased (by manipulating who participates as we use it robustly and only require `t` + participants), contributions cannot be arbitrarily defined. That presumably requires + performing a search of the possible keys for some collision with 2**128 work. It's better to + offset regardless and avoid this question however. + */ + { + use k256::elliptic_curve::{ + bigint::{Encoding, U256}, + ops::Reduce, + }; + let tweak_hash = TapTweakHash::hash(&keys.group_key().to_bytes().as_slice()[1 ..]); + /* + https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#cite_ref-13-0 states how the + bias is negligible. This reduction shouldn't ever occur, yet if it did, the script path + would be unusable due to a check the script path hash is less than the order. That doesn't + impact us as we don't want the script path to be usable. + */ + *keys = keys.offset(::F::reduce(U256::from_be_bytes( + *tweak_hash.to_raw_hash().as_ref(), + ))); + } + *keys = bitcoin_serai::wallet::tweak_keys(keys); // Also create a scanner to assert these keys, and all expected paths, are usable scanner(keys.group_key());