lightning_signer/util/
crypto_utils.rs1use crate::prelude::*;
2use bitcoin::hashes::sha256::Hash as BitcoinSha256;
3use bitcoin::hashes::{sha256d, Hash, HashEngine, Hmac, HmacEngine};
4use bitcoin::key::XOnlyPublicKey;
5use bitcoin::secp256k1::constants::SCHNORR_SIGNATURE_SIZE;
6use bitcoin::secp256k1::{
7 self, ecdsa::Signature, schnorr, Message, PublicKey, Secp256k1, SecretKey,
8};
9use bitcoin::sighash::{EcdsaSighashType, TapSighash};
10use bitcoin::taproot::TapTweakHash;
11use bitcoin::PrivateKey;
12use lightning::ln::channel_keys::{RevocationBasepoint, RevocationKey};
13
14fn hkdf_extract_expand(salt: &[u8], secret: &[u8], info: &[u8], output: &mut [u8]) {
15 let mut hmac = HmacEngine::<BitcoinSha256>::new(salt);
16 hmac.input(secret);
17 let prk = Hmac::from_engine(hmac).to_byte_array();
18
19 let mut t = [0; 32];
20 let mut n: u8 = 0;
21
22 for chunk in output.chunks_mut(32) {
23 let mut hmac = HmacEngine::<BitcoinSha256>::new(&prk[..]);
24 n = n.checked_add(1).expect("HKDF size limit exceeded.");
25 if n != 1 {
26 hmac.input(&t);
27 }
28 hmac.input(&info);
29 hmac.input(&[n]);
30 t = Hmac::from_engine(hmac).to_byte_array();
31 chunk.copy_from_slice(&t);
32 }
33}
34
35pub fn hkdf_sha256(secret: &[u8], info: &[u8], salt: &[u8]) -> [u8; 32] {
37 let mut result = [0u8; 32];
38 hkdf_extract_expand(salt, secret, info, &mut result);
39 result
40}
41
42pub(crate) fn hkdf_sha256_keys(secret: &[u8], info: &[u8], salt: &[u8]) -> [u8; 32 * 6] {
43 let mut result = [0u8; 32 * 6];
44 hkdf_extract_expand(salt, secret, info, &mut result);
45 result
46}
47
48pub(crate) fn derive_public_key<T: secp256k1::Signing>(
49 secp_ctx: &Secp256k1<T>,
50 per_commitment_point: &PublicKey,
51 base_point: &PublicKey,
52) -> Result<PublicKey, secp256k1::Error> {
53 let mut sha = BitcoinSha256::engine();
54 sha.input(&per_commitment_point.serialize());
55 sha.input(&base_point.serialize());
56 let res = BitcoinSha256::from_engine(sha).to_byte_array();
57
58 let hashkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&res)?);
59 base_point.combine(&hashkey)
60}
61
62pub fn signature_to_bitcoin_vec(sig: Signature) -> Vec<u8> {
64 let mut sigvec = sig.serialize_der().to_vec();
65 sigvec.push(EcdsaSighashType::All as u8);
66 sigvec
67}
68
69pub fn schnorr_signature_to_bitcoin_vec(sig: schnorr::Signature) -> Vec<u8> {
71 let mut sigvec = Vec::with_capacity(SCHNORR_SIGNATURE_SIZE);
73 sigvec.extend_from_slice(&sig[..]);
74 sigvec
75}
76
77pub fn bitcoin_vec_to_signature(
79 sigvec: &[u8],
80 sighash_type: EcdsaSighashType,
81) -> Result<Signature, secp256k1::Error> {
82 let len = sigvec.len();
83 if len == 0 {
84 return Err(secp256k1::Error::InvalidSignature);
85 }
86 let mut sv = sigvec.to_vec();
87 let mode = sv.pop().ok_or_else(|| secp256k1::Error::InvalidSignature)?;
88 if mode != sighash_type as u8 {
89 return Err(secp256k1::Error::InvalidSignature);
90 }
91 Ok(Signature::from_der(&sv[..])?)
92}
93
94pub fn maybe_generate_seed(seed_opt: Option<[u8; 32]>) -> [u8; 32] {
96 seed_opt.unwrap_or_else(generate_seed)
97}
98
99pub fn generate_seed() -> [u8; 32] {
101 #[cfg(feature = "std")]
102 {
103 use secp256k1::rand::RngCore;
104 let mut seed = [0; 32];
105 let mut rng = secp256k1::rand::rngs::OsRng;
106 rng.fill_bytes(&mut seed);
107 seed
108 }
109 #[cfg(not(feature = "std"))]
110 unimplemented!("no RNG available in no_std environments yet");
111}
112
113pub fn sighash_from_heartbeat(ser_heartbeat: &[u8]) -> Message {
115 let mut sha = BitcoinSha256::engine();
116 sha.input("vls".as_bytes());
117 sha.input("heartbeat".as_bytes());
118 sha.input(ser_heartbeat);
119 let hash = BitcoinSha256::from_engine(sha);
120 Message::from_digest(hash.to_byte_array())
121}
122
123pub(crate) fn ecdsa_sign(
124 secp_ctx: &Secp256k1<secp256k1::All>,
125 privkey: &PrivateKey,
126 sighash: sha256d::Hash,
127) -> Signature {
128 let message = Message::from_digest(sighash.to_byte_array());
129 secp_ctx.sign_ecdsa(&message, &privkey.inner)
130}
131
132pub(crate) fn taproot_sign(
133 secp_ctx: &Secp256k1<secp256k1::All>,
134 privkey: &PrivateKey,
135 sighash: TapSighash,
136 aux_rand: &[u8; 32],
137) -> schnorr::Signature {
138 let message = Message::from(sighash);
139 let keypair = secp256k1::Keypair::from_secret_key(secp_ctx, &privkey.inner);
140 let (internal_key, _parity) = XOnlyPublicKey::from_keypair(&keypair);
141 let tweak = TapTweakHash::from_key_and_tweak(internal_key, None);
142 let tweaked_keypair = keypair.add_xonly_tweak(secp_ctx, &tweak.to_scalar()).unwrap();
143
144 secp_ctx.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand)
145}
146
147pub(crate) fn derive_public_revocation_key<T: secp256k1::Verification>(
156 secp_ctx: &Secp256k1<T>,
157 per_commitment_point: &PublicKey,
158 countersignatory_revocation_base_point: &RevocationBasepoint,
159) -> Result<RevocationKey, ()> {
160 let revocation_key = RevocationKey::from_basepoint(
161 secp_ctx,
162 &countersignatory_revocation_base_point,
163 per_commitment_point,
164 );
165 Ok(revocation_key)
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_hkdf() {
174 let secret = [1u8];
175 let info = [2u8];
176 let salt = [3u8];
177 let mut output = [0u8; 32 * 6];
178 hkdf_extract_expand(&salt, &secret, &info, &mut output);
179 assert_eq!(hex::encode(output), "13a04658302cc5173a8077f2f296662a7a3ddb2359be92770b13e0b9e63a23d0efbbb13e74af4687137801e1628d1d1876d251b31d1321383568a9387da7c0baa7dee83ba374bba3774ef01140e4c4293791a512e536764bf4405aea511be32d5fd71a0b7a7ef3638312e476eb323fbac5f3d549ccf0fe0eabb38fe7bc16ad01db2288e57de45eabecd561ede4dc89164099ed7f0b0db5250e2b377e2aa84f520838612dccbde870f7b06a1e03f3cd79d30da717c55e15442a0b4dd02aafcd86");
180 let mut output = [0u8; 32];
181 hkdf_extract_expand(&salt, &secret, &info, &mut output);
182 assert_eq!(
183 hex::encode(output),
184 "13a04658302cc5173a8077f2f296662a7a3ddb2359be92770b13e0b9e63a23d0"
185 );
186 }
187
188 #[test]
189 fn test_schnorr_signature_to_bitcoin_vec() {
190 let test_signature_bytes: Vec<u8> = vec![0; 64];
191
192 let test_signature = schnorr::Signature::from_slice(&test_signature_bytes).unwrap();
193
194 let result = schnorr_signature_to_bitcoin_vec(test_signature);
195
196 assert_eq!(test_signature_bytes, result);
197 }
198
199 #[test]
200 fn test_bitcoin_vec_to_signature() {
201 let sighash_type = EcdsaSighashType::All;
202 let sigvec: Vec<u8> = vec![];
203
204 let result = bitcoin_vec_to_signature(&sigvec, sighash_type);
205
206 assert_eq!(result, Err(secp256k1::Error::InvalidSignature));
207
208 let mut sigvec = hex::decode(
209 "304402202e1f64d831e89e2b4a0dc8565cb2d0a4d6061a89f9b48f2c26d5ac0b3b9a0bb102200c8d396f8b2e9c6c623bebc015c47f1f41e8824fabe7cb028f174a0e5df3c0a0"
210 ).unwrap();
211
212 sigvec.push(1 as u8);
213
214 let result = bitcoin_vec_to_signature(&sigvec, sighash_type).unwrap();
215
216 sigvec.pop();
217
218 let parsed_signature = Signature::from_der(&sigvec).expect("valid DER signature");
219
220 assert_eq!(result, parsed_signature);
221 }
222
223 #[test]
224 fn test_maybe_generate_seed() {
225 let known_seed: [u8; 32] = [1; 32];
226
227 let result = maybe_generate_seed(Some(known_seed));
228
229 assert_eq!(result, known_seed);
230
231 let result = maybe_generate_seed(None);
232
233 assert_eq!(result.len(), 32);
234 }
235
236 #[test]
237 fn test_taproot_sign() {
238 let secp = Secp256k1::new();
239
240 let privkey_bytes =
241 hex::decode("d8d3a3140ba89f14144b0dfe40e04220e02ed68736a5773e050a3c4116b1e31c")
242 .unwrap();
243 let secret_key =
244 SecretKey::from_slice(&privkey_bytes).expect("32 bytes, within curve order");
245
246 let privkey = PrivateKey::new(secret_key, bitcoin::Network::Bitcoin);
247
248 let sighash = TapSighash::hash(&[0]);
249
250 let aux_rand: [u8; 32] = [0u8; 32];
251
252 let signature = taproot_sign(&secp, &privkey, sighash, &aux_rand);
253
254 let expected_signature_hex =
255 "14262eb13409cd8928536ab60f431b95193d2d9c7cc476e9f43e8b8f98a8d5a8c38d3edc7bf43c389a12c9e5fad9485ee5d59df2d35f46c3f77ca07197ee1db2";
256
257 assert_eq!(expected_signature_hex, signature.to_string());
258 }
259}