lightning_signer/util/
crypto_utils.rs

1use crate::prelude::*;
2use bitcoin::address::{Payload, WitnessProgram, WitnessVersion};
3use bitcoin::hashes::hash160::Hash as BitcoinHash160;
4use bitcoin::hashes::sha256::Hash as BitcoinSha256;
5use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine};
6use bitcoin::key::XOnlyPublicKey;
7use bitcoin::secp256k1::constants::SCHNORR_SIGNATURE_SIZE;
8use bitcoin::secp256k1::{
9    self, ecdsa::Signature, schnorr, Message, PublicKey, Secp256k1, SecretKey, ThirtyTwoByteHash,
10};
11use bitcoin::sighash::{EcdsaSighashType, TapSighash};
12use bitcoin::taproot::TapTweakHash;
13use bitcoin::{PrivateKey, ScriptBuf};
14use lightning::ln::channel_keys::{RevocationBasepoint, RevocationKey};
15
16fn hkdf_extract_expand(salt: &[u8], secret: &[u8], info: &[u8], output: &mut [u8]) {
17    let mut hmac = HmacEngine::<BitcoinSha256>::new(salt);
18    hmac.input(secret);
19    let prk = Hmac::from_engine(hmac).to_byte_array();
20
21    let mut t = [0; 32];
22    let mut n: u8 = 0;
23
24    for chunk in output.chunks_mut(32) {
25        let mut hmac = HmacEngine::<BitcoinSha256>::new(&prk[..]);
26        n = n.checked_add(1).expect("HKDF size limit exceeded.");
27        if n != 1 {
28            hmac.input(&t);
29        }
30        hmac.input(&info);
31        hmac.input(&[n]);
32        t = Hmac::from_engine(hmac).to_byte_array();
33        chunk.copy_from_slice(&t);
34    }
35}
36
37/// derive a secret from another secret using HKDF-SHA256
38pub fn hkdf_sha256(secret: &[u8], info: &[u8], salt: &[u8]) -> [u8; 32] {
39    let mut result = [0u8; 32];
40    hkdf_extract_expand(salt, secret, info, &mut result);
41    result
42}
43
44pub(crate) fn hkdf_sha256_keys(secret: &[u8], info: &[u8], salt: &[u8]) -> [u8; 32 * 6] {
45    let mut result = [0u8; 32 * 6];
46    hkdf_extract_expand(salt, secret, info, &mut result);
47    result
48}
49
50pub(crate) fn derive_public_key<T: secp256k1::Signing>(
51    secp_ctx: &Secp256k1<T>,
52    per_commitment_point: &PublicKey,
53    base_point: &PublicKey,
54) -> Result<PublicKey, secp256k1::Error> {
55    let mut sha = BitcoinSha256::engine();
56    sha.input(&per_commitment_point.serialize());
57    sha.input(&base_point.serialize());
58    let res = BitcoinSha256::from_engine(sha).to_byte_array();
59
60    let hashkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&res)?);
61    base_point.combine(&hashkey)
62}
63
64// only used in test_utils.rs, so warns when that file is not included
65#[allow(unused)]
66pub(crate) fn payload_for_p2wpkh(key: &PublicKey) -> Payload {
67    let mut hash_engine = BitcoinHash160::engine();
68    hash_engine.input(&key.serialize());
69    Payload::WitnessProgram(
70        WitnessProgram::new(
71            WitnessVersion::V0,
72            BitcoinHash160::from_engine(hash_engine)[..].to_vec(),
73        )
74        .expect("unable to calculate sha256"),
75    )
76}
77
78pub(crate) fn payload_for_p2wsh(script: &ScriptBuf) -> Payload {
79    let mut hash_engine = BitcoinSha256::engine();
80    hash_engine.input(&script.as_bytes());
81    Payload::WitnessProgram(
82        WitnessProgram::new(
83            WitnessVersion::V0,
84            BitcoinSha256::from_engine(hash_engine)[..].to_vec(),
85        )
86        .expect("unable to calculate sha256"),
87    )
88}
89
90/// Convert a [Signature] to Bitcoin signature bytes, with SIGHASH_ALL
91pub fn signature_to_bitcoin_vec(sig: Signature) -> Vec<u8> {
92    let mut sigvec = sig.serialize_der().to_vec();
93    sigvec.push(EcdsaSighashType::All as u8);
94    sigvec
95}
96
97/// Convert a [Signature] to Bitcoin signature bytes, with SIGHASH_ALL
98pub fn schnorr_signature_to_bitcoin_vec(sig: schnorr::Signature) -> Vec<u8> {
99    // taproot sighash type defaults to ALL
100    let mut sigvec = Vec::with_capacity(SCHNORR_SIGNATURE_SIZE);
101    sigvec.extend_from_slice(&sig[..]);
102    sigvec
103}
104
105/// Convert a Bitcoin signature bytes, with the specified EcdsaSighashType, to [Signature]
106pub fn bitcoin_vec_to_signature(
107    sigvec: &[u8],
108    sighash_type: EcdsaSighashType,
109) -> Result<Signature, secp256k1::Error> {
110    let len = sigvec.len();
111    if len == 0 {
112        return Err(secp256k1::Error::InvalidSignature);
113    }
114    let mut sv = sigvec.to_vec();
115    let mode = sv.pop().ok_or_else(|| secp256k1::Error::InvalidSignature)?;
116    if mode != sighash_type as u8 {
117        return Err(secp256k1::Error::InvalidSignature);
118    }
119    Ok(Signature::from_der(&sv[..])?)
120}
121
122/// Use the provided seed, or generate a random one
123pub fn maybe_generate_seed(seed_opt: Option<[u8; 32]>) -> [u8; 32] {
124    seed_opt.unwrap_or_else(generate_seed)
125}
126
127/// Generate a seed
128pub fn generate_seed() -> [u8; 32] {
129    #[cfg(feature = "std")]
130    {
131        use secp256k1::rand::RngCore;
132        let mut seed = [0; 32];
133        let mut rng = secp256k1::rand::rngs::OsRng;
134        rng.fill_bytes(&mut seed);
135        seed
136    }
137    #[cfg(not(feature = "std"))]
138    unimplemented!("no RNG available in no_std environments yet");
139}
140
141/// Hash the serialized heartbeat message for signing
142pub fn sighash_from_heartbeat(ser_heartbeat: &[u8]) -> Message {
143    let mut sha = BitcoinSha256::engine();
144    sha.input("vls".as_bytes());
145    sha.input("heartbeat".as_bytes());
146    sha.input(ser_heartbeat);
147    let hash = BitcoinSha256::from_engine(sha);
148    Message::from(hash)
149}
150
151pub(crate) fn ecdsa_sign<H: Hash + ThirtyTwoByteHash>(
152    secp_ctx: &Secp256k1<secp256k1::All>,
153    privkey: &PrivateKey,
154    sighash: &H,
155) -> Signature {
156    let message = Message::from(*sighash);
157    secp_ctx.sign_ecdsa(&message, &privkey.inner)
158}
159
160pub(crate) fn taproot_sign(
161    secp_ctx: &Secp256k1<secp256k1::All>,
162    privkey: &PrivateKey,
163    sighash: TapSighash,
164    aux_rand: &[u8; 32],
165) -> schnorr::Signature {
166    let message = Message::from(sighash);
167    let keypair = secp256k1::KeyPair::from_secret_key(secp_ctx, &privkey.inner);
168    let (internal_key, _parity) = XOnlyPublicKey::from_keypair(&keypair);
169    let tweak = TapTweakHash::from_key_and_tweak(internal_key, None);
170    let tweaked_keypair = keypair.add_xonly_tweak(secp_ctx, &tweak.to_scalar()).unwrap();
171
172    secp_ctx.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand)
173}
174
175/// Derives a per-commitment-transaction revocation public key from its constituent parts. This is
176/// the public equivalent of derive_private_revocation_key - using only public keys to derive a
177/// public key instead of private keys.
178///
179/// Only the cheating participant owns a valid witness to propagate a revoked
180/// commitment transaction, thus per_commitment_point always come from cheater
181/// and revocation_base_point always come from punisher, which is the broadcaster
182/// of the transaction spending with this key knowledge.
183pub(crate) fn derive_public_revocation_key<T: secp256k1::Verification>(
184    secp_ctx: &Secp256k1<T>,
185    per_commitment_point: &PublicKey,
186    countersignatory_revocation_base_point: &RevocationBasepoint,
187) -> Result<RevocationKey, ()> {
188    let revocation_key = RevocationKey::from_basepoint(
189        secp_ctx,
190        &countersignatory_revocation_base_point,
191        per_commitment_point,
192    );
193    Ok(revocation_key)
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_hkdf() {
202        let secret = [1u8];
203        let info = [2u8];
204        let salt = [3u8];
205        let mut output = [0u8; 32 * 6];
206        hkdf_extract_expand(&salt, &secret, &info, &mut output);
207        assert_eq!(hex::encode(output), "13a04658302cc5173a8077f2f296662a7a3ddb2359be92770b13e0b9e63a23d0efbbb13e74af4687137801e1628d1d1876d251b31d1321383568a9387da7c0baa7dee83ba374bba3774ef01140e4c4293791a512e536764bf4405aea511be32d5fd71a0b7a7ef3638312e476eb323fbac5f3d549ccf0fe0eabb38fe7bc16ad01db2288e57de45eabecd561ede4dc89164099ed7f0b0db5250e2b377e2aa84f520838612dccbde870f7b06a1e03f3cd79d30da717c55e15442a0b4dd02aafcd86");
208        let mut output = [0u8; 32];
209        hkdf_extract_expand(&salt, &secret, &info, &mut output);
210        assert_eq!(
211            hex::encode(output),
212            "13a04658302cc5173a8077f2f296662a7a3ddb2359be92770b13e0b9e63a23d0"
213        );
214    }
215
216    #[test]
217    fn test_schnorr_signature_to_bitcoin_vec() {
218        let test_signature_bytes: Vec<u8> = vec![0; 64];
219
220        let test_signature = schnorr::Signature::from_slice(&test_signature_bytes).unwrap();
221
222        let result = schnorr_signature_to_bitcoin_vec(test_signature);
223
224        assert_eq!(test_signature_bytes, result);
225    }
226
227    #[test]
228    fn test_bitcoin_vec_to_signature() {
229        let sighash_type = EcdsaSighashType::All;
230        let sigvec: Vec<u8> = vec![];
231
232        let result = bitcoin_vec_to_signature(&sigvec, sighash_type);
233
234        assert_eq!(result, Err(secp256k1::Error::InvalidSignature));
235
236        let mut sigvec = hex::decode(
237            "304402202e1f64d831e89e2b4a0dc8565cb2d0a4d6061a89f9b48f2c26d5ac0b3b9a0bb102200c8d396f8b2e9c6c623bebc015c47f1f41e8824fabe7cb028f174a0e5df3c0a0"
238        ).unwrap();
239
240        sigvec.push(1 as u8);
241
242        let result = bitcoin_vec_to_signature(&sigvec, sighash_type).unwrap();
243
244        sigvec.pop();
245
246        let parsed_signature = Signature::from_der(&sigvec).expect("valid DER signature");
247
248        assert_eq!(result, parsed_signature);
249    }
250
251    #[test]
252    fn test_maybe_generate_seed() {
253        let known_seed: [u8; 32] = [1; 32];
254
255        let result = maybe_generate_seed(Some(known_seed));
256
257        assert_eq!(result, known_seed);
258
259        let result = maybe_generate_seed(None);
260
261        assert_eq!(result.len(), 32);
262    }
263
264    #[test]
265    fn test_taproot_sign() {
266        let secp = Secp256k1::new();
267
268        let privkey_bytes =
269            hex::decode("d8d3a3140ba89f14144b0dfe40e04220e02ed68736a5773e050a3c4116b1e31c")
270                .unwrap();
271        let secret_key =
272            SecretKey::from_slice(&privkey_bytes).expect("32 bytes, within curve order");
273
274        let privkey = PrivateKey::new(secret_key, bitcoin::Network::Bitcoin);
275
276        let sighash = TapSighash::hash(&[0]);
277
278        let aux_rand: [u8; 32] = [0u8; 32];
279
280        let signature = taproot_sign(&secp, &privkey, sighash, &aux_rand);
281
282        let expected_signature_hex =
283            "14262eb13409cd8928536ab60f431b95193d2d9c7cc476e9f43e8b8f98a8d5a8c38d3edc7bf43c389a12c9e5fad9485ee5d59df2d35f46c3f77ca07197ee1db2";
284
285        assert_eq!(expected_signature_hex, signature.to_string());
286    }
287}