Skip to main content

breez_sdk_spark/signer/
lnurl_auth.rs

1use std::sync::Arc;
2
3use bitcoin::bip32::{ChildNumber, DerivationPath};
4use bitcoin::secp256k1::Message;
5use breez_sdk_common::lnurl::auth::LnurlAuthSigner;
6use breez_sdk_common::lnurl::error::{LnurlError, LnurlResult};
7
8use crate::signer::{BreezSigner, HmacSigner};
9
10/// Adapter that implements `LnurlAuthSigner` by delegating signing to
11/// [`BreezSigner`] and the HMAC step to [`HmacSigner`].
12pub struct LnurlAuthSignerAdapter {
13    signer: Arc<dyn BreezSigner>,
14    hmac: Arc<dyn HmacSigner>,
15}
16
17impl LnurlAuthSignerAdapter {
18    pub fn new(signer: Arc<dyn BreezSigner>, hmac: Arc<dyn HmacSigner>) -> Self {
19        Self { signer, hmac }
20    }
21}
22
23#[macros::async_trait]
24impl LnurlAuthSigner for LnurlAuthSignerAdapter {
25    async fn derive_public_key(
26        &self,
27        derivation_path: &[ChildNumber],
28    ) -> LnurlResult<bitcoin::secp256k1::PublicKey> {
29        // Convert ChildNumber slice to DerivationPath
30        let path = DerivationPath::from(derivation_path.to_vec());
31
32        // Delegate to BreezSigner to get public key directly
33        self.signer
34            .derive_public_key(&path)
35            .await
36            .map_err(|e| LnurlError::General(e.to_string()))
37    }
38
39    async fn sign_ecdsa(
40        &self,
41        msg: &[u8],
42        derivation_path: &[ChildNumber],
43    ) -> LnurlResult<Vec<u8>> {
44        let path = DerivationPath::from(derivation_path.to_vec());
45
46        // LNURL-auth requires single SHA256 hash of the k1 challenge
47        let message = Message::from_digest(
48            msg.try_into()
49                .map_err(|_| LnurlError::General("Invalid lnurl message".to_string()))?,
50        );
51
52        // Delegate to BreezSigner for ECDSA signing
53        let sig = self
54            .signer
55            .sign_ecdsa(message, &path)
56            .await
57            .map_err(|e| LnurlError::General(e.to_string()))?;
58
59        // Return DER-encoded signature
60        Ok(sig.serialize_der().to_vec())
61    }
62
63    async fn hmac_sha256(
64        &self,
65        key_derivation_path: &[ChildNumber],
66        input: &[u8],
67    ) -> LnurlResult<Vec<u8>> {
68        use bitcoin::hashes::Hash;
69
70        let path = DerivationPath::from(key_derivation_path.to_vec());
71
72        // Delegate to HmacSigner for HMAC-SHA256
73        let hmac = self
74            .hmac
75            .hmac_sha256(&path, input)
76            .await
77            .map_err(|e| LnurlError::General(e.to_string()))?;
78
79        // Convert Hmac<sha256::Hash> to Vec<u8>
80        Ok(hmac.as_byte_array().to_vec())
81    }
82}