Skip to content

Commit 343d570

Browse files
committed
Add LNURL-auth support
Implements LNURL-auth (LUD-04) specification for secure, privacy-preserving authentication with Lightning services using domain-specific key derivation. I used LUD-05 for deriving the keys to mimic what we do for VSS auth.
1 parent 7ad0d63 commit 343d570

File tree

8 files changed

+347
-4
lines changed

8 files changed

+347
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ bdk_esplora = { version = "0.22.0", default-features = false, features = ["async
5656
bdk_electrum = { version = "0.23.0", default-features = false, features = ["use-rustls-ring"]}
5757
bdk_wallet = { version = "2.3.0", default-features = false, features = ["std", "keys-bip39"]}
5858

59-
bitreq = { version = "0.3", default-features = false, features = ["async-https"] }
59+
bitreq = { version = "0.3", default-features = false, features = ["async-https", "json-using-serde"] }
6060
rustls = { version = "0.23", default-features = false }
6161
rusqlite = { version = "0.31.0", features = ["bundled"] }
6262
bitcoin = "0.32.7"

bindings/ldk_node.udl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ interface Node {
166166
UnifiedPayment unified_payment();
167167
LSPS1Liquidity lsps1_liquidity();
168168
[Throws=NodeError]
169+
void lnurl_auth(string lnurl);
170+
[Throws=NodeError]
169171
void connect(PublicKey node_id, SocketAddress address, boolean persist);
170172
[Throws=NodeError]
171173
void disconnect(PublicKey node_id);
@@ -364,6 +366,9 @@ enum NodeError {
364366
"InvalidBlindedPaths",
365367
"AsyncPaymentServicesDisabled",
366368
"HrnParsingFailed",
369+
"LnurlAuthFailed",
370+
"LnurlAuthTimeout",
371+
"InvalidLnurl",
367372
};
368373

369374
dictionary NodeStatus {

src/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::io::{
6868
use crate::liquidity::{
6969
LSPS1ClientConfig, LSPS2ClientConfig, LSPS2ServiceConfig, LiquiditySourceBuilder,
7070
};
71+
use crate::lnurl_auth::LnurlAuth;
7172
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7273
use crate::message_handler::NodeCustomMessageHandler;
7374
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
@@ -1765,6 +1766,8 @@ fn build_with_store_internal(
17651766
None
17661767
};
17671768

1769+
let lnurl_auth = Arc::new(LnurlAuth::new(xprv, Arc::clone(&logger)));
1770+
17681771
let (stop_sender, _) = tokio::sync::watch::channel(());
17691772
let (background_processor_stop_sender, _) = tokio::sync::watch::channel(());
17701773
let is_running = Arc::new(RwLock::new(false));
@@ -1810,6 +1813,7 @@ fn build_with_store_internal(
18101813
scorer,
18111814
peer_store,
18121815
payment_store,
1816+
lnurl_auth,
18131817
is_running,
18141818
node_metrics,
18151819
om_mailbox,

src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ pub(crate) const EXTERNAL_PATHFINDING_SCORES_SYNC_TIMEOUT_SECS: u64 = 5;
107107
// The timeout after which we abort a parsing/looking up an HRN resolution.
108108
pub(crate) const HRN_RESOLUTION_TIMEOUT_SECS: u64 = 5;
109109

110+
// The timeout after which we abort an LNURL-auth operation.
111+
pub(crate) const LNURL_AUTH_TIMEOUT_SECS: u64 = 15;
112+
110113
#[derive(Debug, Clone)]
111114
/// Represents the configuration of an [`Node`] instance.
112115
///

src/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ pub enum Error {
131131
AsyncPaymentServicesDisabled,
132132
/// Parsing a Human-Readable Name has failed.
133133
HrnParsingFailed,
134+
/// LNURL-auth authentication failed.
135+
LnurlAuthFailed,
136+
/// LNURL-auth authentication timed out.
137+
LnurlAuthTimeout,
138+
/// The provided lnurl is invalid.
139+
InvalidLnurl,
134140
}
135141

136142
impl fmt::Display for Error {
@@ -213,6 +219,9 @@ impl fmt::Display for Error {
213219
Self::HrnParsingFailed => {
214220
write!(f, "Failed to parse a human-readable name.")
215221
},
222+
Self::LnurlAuthFailed => write!(f, "LNURL-auth authentication failed."),
223+
Self::LnurlAuthTimeout => write!(f, "LNURL-auth authentication timed out."),
224+
Self::InvalidLnurl => write!(f, "The provided lnurl is invalid."),
216225
}
217226
}
218227
}

src/io/vss_store.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use vss_client::util::storable_builder::{EntropySource, StorableBuilder};
4343

4444
use crate::entropy::NodeEntropy;
4545
use crate::io::utils::check_namespace_key_validity;
46+
use crate::lnurl_auth::LNURL_AUTH_HARDENED_CHILD_INDEX;
4647

4748
type CustomRetryPolicy = FilteredRetryPolicy<
4849
JitteredRetryPolicy<
@@ -68,7 +69,6 @@ impl_writeable_tlv_based_enum!(VssSchemaVersion,
6869
);
6970

7071
const VSS_HARDENED_CHILD_INDEX: u32 = 877;
71-
const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138;
7272
const VSS_SCHEMA_VERSION_KEY: &str = "vss_schema_version";
7373

7474
// We set this to a small number of threads that would still allow to make some progress if one
@@ -902,7 +902,7 @@ impl VssStoreBuilder {
902902
let lnurl_auth_xprv = vss_xprv
903903
.derive_priv(
904904
&secp_ctx,
905-
&[ChildNumber::Hardened { index: VSS_LNURL_AUTH_HARDENED_CHILD_INDEX }],
905+
&[ChildNumber::Hardened { index: LNURL_AUTH_HARDENED_CHILD_INDEX }],
906906
)
907907
.map_err(|_| VssStoreBuildError::KeyDerivationFailed)?;
908908

src/lib.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub mod graph;
9696
mod hex_utils;
9797
pub mod io;
9898
pub mod liquidity;
99+
mod lnurl_auth;
99100
pub mod logger;
100101
mod message_handler;
101102
pub mod payment;
@@ -124,7 +125,8 @@ pub use builder::NodeBuilder as Builder;
124125
use chain::ChainSource;
125126
use config::{
126127
default_user_config, may_announce_channel, AsyncPaymentsRole, ChannelConfig, Config,
127-
NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
128+
LNURL_AUTH_TIMEOUT_SECS, NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL,
129+
RGS_SYNC_INTERVAL,
128130
};
129131
use connection::ConnectionManager;
130132
pub use error::Error as NodeError;
@@ -148,6 +150,7 @@ use lightning::util::persist::KVStoreSync;
148150
use lightning::util::wallet_utils::Wallet as LdkWallet;
149151
use lightning_background_processor::process_events_async;
150152
use liquidity::{LSPS1Liquidity, LiquiditySource};
153+
use lnurl_auth::LnurlAuth;
151154
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
152155
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
153156
use payment::asynchronous::static_invoice_store::StaticInvoiceStore;
@@ -220,6 +223,7 @@ pub struct Node {
220223
scorer: Arc<Mutex<Scorer>>,
221224
peer_store: Arc<PeerStore<Arc<Logger>>>,
222225
payment_store: Arc<PaymentStore>,
226+
lnurl_auth: Arc<LnurlAuth>,
223227
is_running: Arc<RwLock<bool>>,
224228
node_metrics: Arc<RwLock<NodeMetrics>>,
225229
om_mailbox: Option<Arc<OnionMessageMailbox>>,
@@ -1005,6 +1009,26 @@ impl Node {
10051009
))
10061010
}
10071011

1012+
/// Authenticates the user via [LNURL-auth] for the given LNURL string.
1013+
///
1014+
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
1015+
pub fn lnurl_auth(&self, lnurl: String) -> Result<(), Error> {
1016+
let auth = Arc::clone(&self.lnurl_auth);
1017+
self.runtime.block_on(async move {
1018+
let res = tokio::time::timeout(
1019+
Duration::from_secs(LNURL_AUTH_TIMEOUT_SECS),
1020+
auth.authenticate(&lnurl),
1021+
)
1022+
.await;
1023+
1024+
match res {
1025+
Ok(Ok(())) => Ok(()),
1026+
Ok(Err(e)) => Err(e),
1027+
Err(_) => Err(Error::LnurlAuthTimeout),
1028+
}
1029+
})
1030+
}
1031+
10081032
/// Returns a liquidity handler allowing to request channels via the [bLIP-51 / LSPS1] protocol.
10091033
///
10101034
/// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md

0 commit comments

Comments
 (0)