diff --git a/libs/sdk-common/src/lnurl/specs/auth.rs b/libs/sdk-common/src/lnurl/specs/auth.rs index 1c1244ff1..897ef2bba 100644 --- a/libs/sdk-common/src/lnurl/specs/auth.rs +++ b/libs/sdk-common/src/lnurl/specs/auth.rs @@ -1,13 +1,21 @@ use std::str::FromStr; -use bitcoin::hashes::{hex::ToHex, sha256, Hash, HashEngine, Hmac, HmacEngine}; -use bitcoin::secp256k1::{Message, Secp256k1}; -use bitcoin::util::bip32::{ChildNumber, ExtendedPrivKey}; -use bitcoin::KeyPair; +use bitcoin::hashes::hex::ToHex; +use bitcoin::util::bip32::ChildNumber; use reqwest::Url; use crate::prelude::*; +pub trait LnurAuthSigner { + fn derive_bip32_pub_key(&self, derivation_path: &[ChildNumber]) -> LnUrlResult>; + fn sign_ecdsa(&self, msg: &[u8], derivation_path: &[ChildNumber]) -> LnUrlResult>; + fn hmac_sha256( + &self, + key_derivation_path: &[ChildNumber], + input: &[u8], + ) -> LnUrlResult>; +} + /// Performs the third and last step of LNURL-auth, as per /// /// @@ -15,25 +23,24 @@ use crate::prelude::*; /// https://github.com/lnurl/luds/blob/luds/05.md /// /// See the [parse] docs for more detail on the full workflow. -pub async fn perform_lnurl_auth( - linking_keys: KeyPair, - req_data: LnUrlAuthRequestData, +pub async fn perform_lnurl_auth( + req_data: &LnUrlAuthRequestData, + signer: &S, ) -> LnUrlResult { - let k1_to_sign = Message::from_slice( - &hex::decode(req_data.k1) - .map_err(|e| LnUrlError::Generic(format!("Error decoding k1: {e}")))?, - )?; - let sig = Secp256k1::new().sign_ecdsa(&k1_to_sign, &linking_keys.secret_key()); + let url = Url::from_str(&req_data.url).map_err(|e| LnUrlError::InvalidUri(e.to_string()))?; + let derivation_path = get_derivation_path(signer, url)?; + let sig = signer.sign_ecdsa(req_data.k1.as_bytes(), &derivation_path)?; + let xpub = signer.derive_bip32_pub_key(&derivation_path)?; // ?&sig=&key= let mut callback_url = Url::from_str(&req_data.url).map_err(|e| LnUrlError::InvalidUri(e.to_string()))?; callback_url .query_pairs_mut() - .append_pair("sig", &sig.serialize_der().to_hex()); + .append_pair("sig", &sig.to_hex()); callback_url .query_pairs_mut() - .append_pair("key", &linking_keys.public_key().to_hex()); + .append_pair("key", xpub.to_hex().as_str()); get_parse_and_log_response(callback_url.as_ref(), false) .await @@ -81,21 +88,15 @@ pub fn validate_request( }) } -fn hmac_sha256(key: &[u8], input: &[u8]) -> Hmac { - let mut engine = HmacEngine::::new(key); - engine.input(input); - Hmac::::from_engine(engine) -} - -pub fn get_derivation_path( - hashing_key: ExtendedPrivKey, +pub fn get_derivation_path( + signer: &S, url: Url, ) -> LnUrlResult> { let domain = url .domain() .ok_or(LnUrlError::invalid_uri("Could not determine domain"))?; - let hmac = hmac_sha256(&hashing_key.to_priv().to_bytes(), domain.as_bytes()); + let hmac = signer.hmac_sha256(&[ChildNumber::from_hardened_idx(138)?], domain.as_bytes())?; // m/138'//// Ok(vec![ diff --git a/libs/sdk-core/src/breez_services.rs b/libs/sdk-core/src/breez_services.rs index 06f6d1d74..739ae9084 100644 --- a/libs/sdk-core/src/breez_services.rs +++ b/libs/sdk-core/src/breez_services.rs @@ -11,9 +11,8 @@ use bitcoin::hashes::{sha256, Hash}; use bitcoin::util::bip32::ChildNumber; use chrono::Local; use futures::TryFutureExt; -use gl_client::bitcoin::secp256k1::Secp256k1; use log::{LevelFilter, Metadata, Record}; -use reqwest::{header::CONTENT_TYPE, Body, Url}; +use reqwest::{header::CONTENT_TYPE, Body}; use sdk_common::grpc; use sdk_common::prelude::*; use serde::Serialize; @@ -33,6 +32,7 @@ use crate::error::{ RedeemOnchainResult, SdkError, SdkResult, SendOnchainError, SendPaymentError, }; use crate::greenlight::{GLBackupTransport, Greenlight}; +use crate::lnurl::auth::SDKLnurlAuthSigner; use crate::lnurl::pay::*; use crate::lsp::LspInformation; use crate::models::{ @@ -573,20 +573,7 @@ impl BreezServices { &self, req_data: LnUrlAuthRequestData, ) -> Result { - // m/138'/0 - let hashing_key = self.node_api.derive_bip32_key(vec![ - ChildNumber::from_hardened_idx(138).map_err(Into::::into)?, - ChildNumber::from(0), - ])?; - - let url = - Url::from_str(&req_data.url).map_err(|e| LnUrlError::InvalidUri(e.to_string()))?; - - let derivation_path = get_derivation_path(hashing_key, url)?; - let linking_key = self.node_api.derive_bip32_key(derivation_path)?; - let linking_keys = linking_key.to_keypair(&Secp256k1::new()); - - Ok(perform_lnurl_auth(linking_keys, req_data).await?) + Ok(perform_lnurl_auth(&req_data, &SDKLnurlAuthSigner::new(self.node_api.clone())).await?) } /// Creates an bolt11 payment request. diff --git a/libs/sdk-core/src/lnurl/auth.rs b/libs/sdk-core/src/lnurl/auth.rs new file mode 100644 index 000000000..a46e24d23 --- /dev/null +++ b/libs/sdk-core/src/lnurl/auth.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use gl_client::{ + bitcoin::{ + hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine}, + secp256k1::{Message, Secp256k1}, + util::bip32::ChildNumber, + }, + lightning::util::ser::Writeable, +}; +use sdk_common::prelude::{LnUrlError, LnUrlResult, LnurAuthSigner}; + +use crate::node_api::NodeAPI; + +pub(crate) struct SDKLnurlAuthSigner { + node_api: Arc, +} + +impl SDKLnurlAuthSigner { + pub fn new(node_api: Arc) -> Self { + Self { node_api } + } +} + +impl LnurAuthSigner for SDKLnurlAuthSigner { + fn derive_bip32_pub_key(&self, derivation_path: &[ChildNumber]) -> LnUrlResult> { + Ok(self + .node_api + .derive_bip32_key(derivation_path.to_vec())? + .to_keypair(&Secp256k1::new()) + .public_key() + .encode()) + } + + fn sign_ecdsa(&self, msg: &[u8], derivation_path: &[ChildNumber]) -> LnUrlResult> { + let xpriv = self.node_api.derive_bip32_key(derivation_path.to_vec())?; + let sig = Secp256k1::new().sign_ecdsa( + &Message::from_slice(msg).map_err(|_| LnUrlError::generic("failed to sign"))?, + &xpriv.private_key, + ); + Ok(sig.encode()) + } + + fn hmac_sha256( + &self, + key_derivation_path: &[ChildNumber], + input: &[u8], + ) -> LnUrlResult> { + let priv_key = self + .node_api + .derive_bip32_key(key_derivation_path.to_vec())?; + let mut engine = HmacEngine::::new(priv_key.encode().as_slice()); + engine.input(input); + Ok(Hmac::::from_engine(engine) + .as_inner() + .to_vec()) + } +} diff --git a/libs/sdk-core/src/lnurl/mod.rs b/libs/sdk-core/src/lnurl/mod.rs index d0c0bda84..b4f5b9bdd 100644 --- a/libs/sdk-core/src/lnurl/mod.rs +++ b/libs/sdk-core/src/lnurl/mod.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod pay; #[cfg(test)]