elements_miniscript/confidential/
bare.rs

1// Miniscript
2// Written in 2023 by
3//     Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! "Bare Key" Confidential Descriptors
16
17use bitcoin::hashes::{sha256t_hash_newtype, Hash};
18use elements::encode::Encodable;
19use elements::secp256k1_zkp;
20
21use crate::ToPublicKey;
22
23sha256t_hash_newtype! {
24    pub struct TapTweakTag = hash_str("CT-Blinding-Key/1.0");
25    /// Taproot-tagged hash for elements tapscript Merkle tree leafs
26    #[hash_newtype(forward)]
27    pub struct TapTweakHash(_);
28}
29
30/// Tweaks a bare key using the scriptPubKey of a descriptor
31pub fn tweak_key<'a, Pk, V>(
32    secp: &secp256k1_zkp::Secp256k1<V>,
33    spk: &elements::Script,
34    pk: &Pk,
35) -> secp256k1_zkp::PublicKey
36where
37    Pk: ToPublicKey + 'a,
38    V: secp256k1_zkp::Verification,
39{
40    let mut eng = TapTweakHash::engine();
41    pk.to_public_key()
42        .write_into(&mut eng)
43        .expect("engines don't error");
44    spk.consensus_encode(&mut eng).expect("engines don't error");
45    let hash_bytes = TapTweakHash::from_engine(eng).to_byte_array();
46    let hash_scalar = secp256k1_zkp::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
47    pk.to_public_key()
48        .inner
49        .add_exp_tweak(secp, &hash_scalar)
50        .unwrap()
51}
52
53/// Tweaks a bare key using the scriptPubKey of a descriptor
54pub fn tweak_private_key<V>(
55    secp: &secp256k1_zkp::Secp256k1<V>,
56    spk: &elements::Script,
57    sk: &secp256k1_zkp::SecretKey,
58) -> secp256k1_zkp::SecretKey
59where
60    V: secp256k1_zkp::Signing,
61{
62    let mut eng = TapTweakHash::engine();
63    bitcoin::PublicKey::new(sk.public_key(secp))
64        .write_into(&mut eng)
65        .expect("engines don't error");
66    spk.consensus_encode(&mut eng).expect("engines don't error");
67    let hash_bytes = TapTweakHash::from_engine(eng).to_byte_array();
68    let hash_scalar = secp256k1_zkp::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
69    sk.add_tweak(&hash_scalar).unwrap()
70}
71
72#[cfg(test)]
73mod tests {
74    use bitcoin::hashes::sha256t::Tag;
75    use bitcoin::hashes::{sha256, HashEngine};
76
77    use super::*;
78
79    const MIDSTATE_HASH_TO_PRIVATE_HASH: [u8; 32] = [
80        0x2f, 0x85, 0x61, 0xec, 0x30, 0x88, 0xad, 0xa9, 0x5a, 0xe7, 0x43, 0xcd, 0x3c, 0x5f, 0x59,
81        0x7d, 0xc0, 0x4b, 0xd0, 0x7f, 0x06, 0x5f, 0x1c, 0x06, 0x47, 0x89, 0x36, 0x63, 0xf3, 0x92,
82        0x6e, 0x65,
83    ];
84
85    #[test]
86    fn tagged_hash() {
87        // Check that cached midstate is computed correctly
88        // This code taken from `tag_engine` in the rust-bitcoin tests; it is identical
89        // to that used by the BIP-0340 hashes in Taproot
90        let mut engine = sha256::Hash::engine();
91        let tag_hash = sha256::Hash::hash(b"CT-Blinding-Key/1.0");
92        engine.input(&tag_hash[..]);
93        engine.input(&tag_hash[..]);
94        assert_eq!(
95            MIDSTATE_HASH_TO_PRIVATE_HASH,
96            engine.midstate().to_byte_array()
97        );
98
99        // Test empty hash
100        assert_eq!(
101            TapTweakHash::from_engine(TapTweakTag::engine()).to_string(),
102            "d12a140aca856fbb917b931f263c42f064608985e2ce17ae5157daa17c55e8d9",
103        );
104        assert_eq!(
105            TapTweakHash::hash(&[]).to_string(),
106            "d12a140aca856fbb917b931f263c42f064608985e2ce17ae5157daa17c55e8d9",
107        );
108
109        // And hash of 100 bytes
110        let data: Vec<u8> = (0..80).collect();
111        assert_eq!(
112            TapTweakHash::hash(&data).to_string(),
113            "e1e52419a2934d278c50e29608969d2f23c1bd1243a09bfc8026d4ed4b085e39",
114        );
115    }
116
117    #[test]
118    fn tweak() {
119        // Check that tweaking blinding keys produce consistent results
120        let secp = secp256k1_zkp::Secp256k1::new();
121        let sk = secp256k1_zkp::SecretKey::from_slice(&[1u8; 32]).unwrap();
122        let pk = sk.public_key(&secp);
123        let spk = elements::Script::default();
124        let tweaked_pk = tweak_key(&secp, &spk, &pk);
125        let tweaked_sk = tweak_private_key(&secp, &spk, &sk);
126        assert_eq!(tweaked_pk, tweaked_sk.public_key(&secp));
127    }
128}