1use std::sync::Arc;
2
3use anyhow::anyhow;
4use bip39::Mnemonic;
5use boltz_client::PublicKey;
6use lwk_common::Signer as LwkSigner;
7use lwk_wollet::bitcoin::bip32::Xpriv;
8use lwk_wollet::bitcoin::Network;
9use lwk_wollet::elements_miniscript::{self, ToPublicKey as _};
10use lwk_wollet::elements_miniscript::{
11 bitcoin::{self, bip32::DerivationPath},
12 elements::{
13 bitcoin::bip32::{self, Fingerprint, Xpub},
14 hashes::Hash,
15 pset::PartiallySignedTransaction,
16 secp256k1_zkp::{All, Secp256k1},
17 sighash::SighashCache,
18 },
19 elementssig_to_rawsig,
20 psbt::PsbtExt,
21 slip77::MasterBlindingKey,
22};
23use lwk_wollet::hashes::{sha256, HashEngine, Hmac, HmacEngine};
24use lwk_wollet::secp256k1::ecdsa::Signature;
25use lwk_wollet::secp256k1::Message;
26
27use crate::model::{Signer, SignerError};
28
29#[derive(thiserror::Error, Debug)]
30pub enum SignError {
31 #[error(transparent)]
32 Pset(#[from] elements_miniscript::elements::pset::Error),
33
34 #[error(transparent)]
35 ElementsEncode(#[from] elements_miniscript::elements::encode::Error),
36
37 #[error(transparent)]
38 Sighash(#[from] elements_miniscript::psbt::SighashError),
39
40 #[error(transparent)]
41 PsetParse(#[from] elements_miniscript::elements::pset::ParseError),
42
43 #[error(transparent)]
44 Bip32(#[from] bip32::Error),
45
46 #[error(transparent)]
47 Generic(#[from] anyhow::Error),
48
49 #[error(transparent)]
50 UserSignerError(#[from] crate::model::SignerError),
51}
52
53#[derive(thiserror::Error, Debug)]
55pub enum NewError {
56 #[error(transparent)]
57 Bip39(#[from] bip39::Error),
58
59 #[error(transparent)]
60 Bip32(#[from] bip32::Error),
61
62 #[error(transparent)]
63 Seed(#[from] anyhow::Error),
64}
65
66pub struct SdkLwkSigner {
68 sdk_signer: Arc<Box<dyn Signer>>,
69}
70
71impl SdkLwkSigner {
72 pub fn new(sdk_signer: Arc<Box<dyn Signer>>) -> Result<Self, NewError> {
77 Ok(Self { sdk_signer })
78 }
79
80 pub(crate) fn xpub(&self) -> Result<Xpub, SignError> {
81 let xpub = self.sdk_signer.xpub()?;
82 Ok(Xpub::decode(&xpub)?)
83 }
84
85 pub fn fingerprint(&self) -> Result<Fingerprint, SignError> {
86 let f: Fingerprint = self.xpub()?.identifier()[0..4]
87 .try_into()
88 .map_err(|_| SignError::Generic(anyhow::anyhow!("Wrong fingerprint length")))?;
89 Ok(f)
90 }
91
92 pub(crate) fn sign_ecdsa_recoverable(&self, msg: &Message) -> Result<Vec<u8>, SignError> {
93 let sig_bytes = self
94 .sdk_signer
95 .sign_ecdsa_recoverable(msg.as_ref().to_vec())?;
96 Ok(sig_bytes)
97 }
98}
99
100impl LwkSigner for SdkLwkSigner {
101 type Error = SignError;
102
103 fn sign(&self, pset: &mut PartiallySignedTransaction) -> Result<u32, Self::Error> {
104 let tx = pset.extract_tx()?;
105 let mut sighash_cache = SighashCache::new(&tx);
106 let mut signature_added = 0;
107
108 let genesis_hash = elements_miniscript::elements::BlockHash::all_zeros();
110 let mut messages = vec![];
111 for i in 0..pset.inputs().len() {
112 let msg = pset
115 .sighash_msg(i, &mut sighash_cache, None, genesis_hash)?
116 .to_secp_msg();
117 messages.push(msg);
118 }
119
120 let hash_ty = elements_miniscript::elements::EcdsaSighashType::All;
122
123 let signer_fingerprint = self.fingerprint()?;
124 for (input, msg) in pset.inputs_mut().iter_mut().zip(messages) {
125 for (want_public_key, (fingerprint, derivation_path)) in input.bip32_derivation.iter() {
126 if &signer_fingerprint == fingerprint {
127 let xpub = self.derive_xpub(derivation_path)?;
128 let public_key: PublicKey = xpub.public_key.into();
129 if want_public_key == &public_key {
130 let sig_bytes = self
132 .sdk_signer
133 .sign_ecdsa(msg.as_ref().to_vec(), derivation_path.to_string())?;
134 let sig = Signature::from_der(&sig_bytes).map_err(|_| {
135 SignError::Generic(anyhow::anyhow!("Invalid esda signature"))
136 })?;
137 let sig = elementssig_to_rawsig(&(sig, hash_ty));
138
139 let inserted = input.partial_sigs.insert(public_key, sig);
140 if inserted.is_none() {
141 signature_added += 1;
142 }
143 }
144 }
145 }
146 }
147
148 Ok(signature_added)
149 }
150
151 fn slip77_master_blinding_key(&self) -> Result<MasterBlindingKey, Self::Error> {
152 let bytes: [u8; 32] = self
153 .sdk_signer
154 .slip77_master_blinding_key()?
155 .try_into()
156 .map_err(|_| {
157 SignError::Generic(anyhow::anyhow!("Wrong slip77 master blinding key length"))
158 })?;
159 Ok(bytes.into())
160 }
161
162 fn derive_xpub(&self, path: &DerivationPath) -> Result<Xpub, Self::Error> {
163 let pubkey_bytes = if path.is_empty() {
164 self.sdk_signer.xpub()?
165 } else {
166 self.sdk_signer.derive_xpub(path.to_string())?
167 };
168 let xpub = Xpub::decode(pubkey_bytes.as_slice())?;
169 Ok(xpub)
170 }
171
172 fn sign_message(
177 &self,
178 _message: &str,
179 _path: &DerivationPath,
180 ) -> Result<bitcoin::sign_message::MessageSignature, Self::Error> {
181 Err(SignError::Generic(anyhow!(
182 "sign_message is not supported by the Breez SDK signer"
183 )))
184 }
185}
186
187pub struct SdkSigner {
188 xprv: Xpriv,
189 secp: Secp256k1<All>, seed: Vec<u8>,
191 network: Network,
192}
193
194impl SdkSigner {
195 pub fn new(mnemonic: &str, passphrase: &str, is_mainnet: bool) -> Result<Self, NewError> {
196 let mnemonic: Mnemonic = mnemonic.parse()?;
197 let seed = mnemonic.to_seed(passphrase).to_vec();
198
199 Self::new_with_seed(seed, is_mainnet)
200 }
201
202 pub fn new_with_seed(seed: Vec<u8>, is_mainnet: bool) -> Result<Self, NewError> {
203 if seed.len() < 32 {
204 return Err(NewError::Seed(anyhow!("Seed must be at least 32 bytes")));
205 }
206
207 let secp = Secp256k1::new();
208 let network = if is_mainnet {
209 bitcoin::Network::Bitcoin
210 } else {
211 bitcoin::Network::Testnet
212 };
213
214 let xprv = Xpriv::new_master(network, &seed)?;
215
216 Ok(Self {
217 xprv,
218 secp,
219 seed,
220 network,
221 })
222 }
223}
224
225impl Signer for SdkSigner {
226 fn xpub(&self) -> Result<Vec<u8>, SignerError> {
227 Ok(Xpub::from_priv(&self.secp, &self.xprv).encode().to_vec())
228 }
229
230 fn derive_xpub(&self, derivation_path: String) -> Result<Vec<u8>, SignerError> {
231 let der: DerivationPath = derivation_path.parse()?;
232 let derived = self.xprv.derive_priv(&self.secp, &der)?;
233 Ok(Xpub::from_priv(&self.secp, &derived).encode().to_vec())
234 }
235
236 fn sign_ecdsa(&self, msg: Vec<u8>, derivation_path: String) -> Result<Vec<u8>, SignerError> {
237 let der: DerivationPath = derivation_path.parse()?;
238 let ext_derived = self.xprv.derive_priv(&self.secp, &der)?;
239 let sig = self.secp.sign_ecdsa_low_r(
240 &Message::from_digest(
241 msg.try_into()
242 .map_err(|_| anyhow::anyhow!("failed to sign"))?,
243 ),
244 &ext_derived.private_key,
245 );
246 Ok(sig.serialize_der().to_vec())
247 }
248
249 fn slip77_master_blinding_key(&self) -> Result<Vec<u8>, SignerError> {
250 let master_blinding_key = MasterBlindingKey::from_seed(&self.seed);
251 Ok(master_blinding_key.as_bytes().to_vec())
252 }
253
254 fn sign_ecdsa_recoverable(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
255 let secp = Secp256k1::new();
256 let keypair = Xpriv::new_master(self.network, &self.seed)
257 .map_err(|e| anyhow::anyhow!("Could not get signer keypair: {e}"))?
258 .to_keypair(&secp);
259 let s = msg.as_slice();
260
261 let msg: Message = Message::from_digest_slice(s)
262 .map_err(|e| SignerError::Generic { err: e.to_string() })?;
263 let recoverable_sig = secp.sign_ecdsa_recoverable(&msg, &keypair.secret_key());
265 let (recovery_id, sig) = recoverable_sig.serialize_compact();
266 let mut complete_signature = vec![31 + recovery_id.to_i32() as u8];
267 complete_signature.extend_from_slice(&sig);
268 Ok(complete_signature)
269 }
270
271 fn hmac_sha256(&self, msg: Vec<u8>, derivation_path: String) -> Result<Vec<u8>, SignerError> {
272 let der: DerivationPath = derivation_path.parse()?;
273 let priv_key = self.xprv.derive_priv(&self.secp, &der)?;
274 let mut engine = HmacEngine::<sha256::Hash>::new(priv_key.to_priv().to_bytes().as_slice());
275
276 engine.input(msg.as_slice());
277 Ok(Hmac::<sha256::Hash>::from_engine(engine)
278 .as_byte_array()
279 .to_vec())
280 }
281
282 fn ecies_encrypt(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
283 let keypair = self.xprv.to_keypair(&self.secp);
284 let rc_pub = keypair.public_key().to_public_key().to_bytes();
285 ecies::encrypt(&rc_pub, &msg).map_err(|err| SignerError::Generic {
286 err: format!("Could not encrypt data: {err}"),
287 })
288 }
289
290 fn ecies_decrypt(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
291 let rc_prv = self.xprv.to_priv().to_bytes();
292 ecies::decrypt(&rc_prv, &msg).map_err(|err| SignerError::Generic {
293 err: format!("Could not decrypt data: {err}"),
294 })
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use bip32::KeySource;
302 use bip39::rand::{self, RngCore};
303 use bitcoin::PublicKey;
304 use elements::{
305 pset::{Input, Output, PartiallySignedTransaction},
306 AssetId, TxOut, Txid,
307 };
308 use lwk_common::{singlesig_desc, Singlesig};
309 use lwk_signer::SwSigner;
310 use lwk_wollet::{
311 elements::{self, Script},
312 FakeStore, Network, WolletBuilder, WolletDescriptor,
313 };
314 use std::collections::BTreeMap;
315
316 #[cfg(feature = "browser-tests")]
317 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
318
319 fn get_descriptor<S: LwkSigner>(signer: &S) -> Result<WolletDescriptor, anyhow::Error> {
320 let descriptor_str = singlesig_desc(
321 signer,
322 Singlesig::Wpkh,
323 lwk_common::DescriptorBlindingKey::Slip77,
324 )
325 .map_err(|e| anyhow::anyhow!("Invalid descriptor: {e}"))?;
326 Ok(descriptor_str.parse()?)
327 }
328
329 fn create_signers(mnemonic: &str) -> (SwSigner, SdkLwkSigner) {
330 let sw_signer = SwSigner::new(mnemonic, false).unwrap();
331 let sdk_signer: Box<dyn Signer> = Box::new(SdkSigner::new(mnemonic, "", false).unwrap());
332 let sdk_signer = SdkLwkSigner::new(Arc::new(sdk_signer)).unwrap();
333 (sw_signer, sdk_signer)
334 }
335
336 fn create_pset<S: LwkSigner>(signer: &S) -> PartiallySignedTransaction {
337 let mut pset = PartiallySignedTransaction::new_v2();
339
340 let prev_txid = Txid::from_slice(&[0; 32]).unwrap();
342 let prev_vout = 0;
343
344 let derivation_path: DerivationPath = "m/84'/0'/0'/0/0".parse().unwrap();
345 let xpub = signer.derive_xpub(&derivation_path).unwrap();
346 let mut bip32_derivation_map: BTreeMap<PublicKey, KeySource> = BTreeMap::new();
347 bip32_derivation_map.insert(
348 xpub.public_key.into(),
349 (signer.fingerprint().unwrap(), derivation_path),
350 );
351 let input = Input {
352 non_witness_utxo: None,
353 witness_utxo: Some(TxOut::new_fee(
354 100_000_000,
355 AssetId::from_slice(&[1; 32]).unwrap(),
356 )),
357 previous_txid: prev_txid,
358 previous_output_index: prev_vout,
359 bip32_derivation: bip32_derivation_map,
360 ..Default::default()
361 };
362
363 pset.add_input(input);
364
365 let output_script = Script::new();
367 let output_amount = 99_000_000;
368 let output_asset = AssetId::from_slice(&[1; 32]).unwrap();
369 let output = Output::new_explicit(
370 output_script,
371 output_amount,
372 output_asset,
373 None, );
375 pset.add_output(output);
376 pset
377 }
378
379 #[sdk_macros::test_all]
380 fn test_invalid_signer() {
381 let mut rng = rand::thread_rng();
382
383 assert!(SwSigner::new("", false).is_err());
384
385 assert!(SdkSigner::new("", "", false).is_err());
386
387 assert!(SdkSigner::new_with_seed(vec![], false).is_err());
388
389 let mut seed1 = [0u8; 31];
390 rng.fill_bytes(&mut seed1);
391 assert!(SdkSigner::new_with_seed(seed1.to_vec(), false).is_err());
392
393 let mut seed2 = [0u8; 32];
394 rng.fill_bytes(&mut seed2);
395 assert!(SdkSigner::new_with_seed(seed2.to_vec(), false).is_ok());
396 }
397
398 #[sdk_macros::test_all]
399 fn test_sign() {
400 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
401 let (sw_signer, sdk_signer) = create_signers(mnemonic);
402
403 let mut pset_sw = create_pset(&sw_signer);
405 let mut pset_sdk = create_pset(&sdk_signer);
406
407 let sw_sig_count = sw_signer.sign(&mut pset_sw).unwrap();
409 assert_eq!(sw_sig_count, 1);
410
411 let sdk_sig_count = sdk_signer.sign(&mut pset_sdk).unwrap();
413 assert_eq!(sdk_sig_count, 1);
414
415 assert_eq!(pset_sw, pset_sdk);
417
418 let tx_sw = pset_sw.extract_tx().unwrap();
420 let tx_sdk = pset_sdk.extract_tx().unwrap();
421 assert_eq!(tx_sw, tx_sdk);
422 }
423
424 #[sdk_macros::test_all]
425 fn test_slip77_master_blinding_key() {
426 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
427 let (sw_signer, sdk_signer) = create_signers(mnemonic);
428
429 let sw_key = sw_signer.slip77_master_blinding_key().unwrap();
430 let sdk_key = sdk_signer.slip77_master_blinding_key().unwrap();
431
432 assert_eq!(
433 sw_key, sdk_key,
434 "SLIP77 master blinding keys should be identical"
435 );
436 }
437
438 #[sdk_macros::test_all]
439 fn test_derive_xpub() {
440 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
441 let (sw_signer, sdk_signer) = create_signers(mnemonic);
442
443 let path = "m/84'/0'/0'/0/0".parse().unwrap();
444 let sw_xpub = sw_signer.derive_xpub(&path).unwrap();
445 let sdk_xpub = sdk_signer.derive_xpub(&path).unwrap();
446
447 assert_eq!(sw_xpub, sdk_xpub, "Derived xpubs should be identical");
448 }
449
450 #[sdk_macros::test_all]
451 fn test_identifier() {
452 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
453 let (sw_signer, sdk_signer) = create_signers(mnemonic);
454
455 let sw_identifier = sw_signer.xpub().identifier();
456 let sdk_identifier = sdk_signer.xpub().unwrap().identifier();
457
458 assert_eq!(
459 sw_identifier, sdk_identifier,
460 "Identifiers should be identical"
461 );
462 }
463
464 #[sdk_macros::test_all]
465 fn test_fingerprint() {
466 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
467 let (sw_signer, sdk_signer) = create_signers(mnemonic);
468
469 let sw_fingerprint = sw_signer.fingerprint();
470 let sdk_fingerprint = sdk_signer.fingerprint().unwrap();
471 let manual_finger_print = sdk_signer.xpub().unwrap().identifier()[0..4]
472 .try_into()
473 .unwrap();
474 assert_eq!(
475 sw_fingerprint, sdk_fingerprint,
476 "Fingerprints should be identical"
477 );
478
479 assert_eq!(
480 sw_fingerprint, manual_finger_print,
481 "Fingerprints should be identical"
482 );
483 }
484
485 #[sdk_macros::test_all]
486 fn test_sdk_signer_vs_sw_signer() {
487 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
489 let network = Network::TestnetLiquid;
490
491 let sw_signer = SwSigner::new(mnemonic, false).unwrap();
493 let sw_wallet = WolletBuilder::new(network, get_descriptor(&sw_signer).unwrap())
494 .with_updates_store(Arc::new(FakeStore::new()))
495 .build()
496 .unwrap();
497
498 let sdk_signer: Box<dyn Signer> = Box::new(SdkSigner::new(mnemonic, "", false).unwrap());
500 let sdk_signer = SdkLwkSigner::new(Arc::new(sdk_signer)).unwrap();
501 let sdk_wallet = WolletBuilder::new(network, get_descriptor(&sdk_signer).unwrap())
502 .with_updates_store(Arc::new(FakeStore::new()))
503 .build()
504 .unwrap();
505
506 let sw_address = sw_wallet.address(None).unwrap();
508 let sdk_address = sdk_wallet.address(None).unwrap();
509
510 assert_eq!(
511 sw_address.address().to_string(),
512 sdk_address.address().to_string(),
513 "Addresses should be identical"
514 );
515 }
516}