Lightning Node Connect: A Technical Deep Dive
Today we’re excited to announce the alpha Lightning Node Connect (LNC) a new streamlined way to allow an application/wallet (and even a web application using WASM!) to connect to a remote Lightning Node using a macaroon-scoped gRPC connection. LNC is new a fundamental primitive for Lightning application development, as it allows a user to easily (but securely!) pair their node with a given application/wallet or web application. The recently released web version of Lightning Terminal leverages LNC to securely connect to a user’s node without having to worry about NAT traversal.
Lightning Node Connect improves on prior attempts at solving this problem (like LND Connect) by no longer requiring users to manually copy/paste TLS certificates or macaroons. Instead, Lightning Node Connect uses a flavor of Password Authenticated Key Exchange (PAKE) combined with the Noise Protocol Framework to create a pairing flow where a user only needs to enter a short human readable passphrase (scan or click a link) to establish a secure end-to-end encrypted connection with an application. LNC uses macaroons to allow users to set a granular set of capabilities a remote website/application is able to access.
LNC leverages Go’s excellent WASM toolchain in order to allow browsers to securely connect to a remote Lightning Node. This WASM integration will allow for richer web-based Lightning applications, which are a necessary component to realize the Lightning Native Web.
In this post we’ll be doing a deep-dive into the architecture of Lightning Node Connect, exploring in detail the various components that make up the final LNC protocol.
The Lightning Application Pairing Problem
Before diving deep into the technical details, it's it’s important to first frame the solution that LNC offers in the context of the existing difficulty of securely pairing a Lightning node to an external wallet. Today, lnd (and other Lightning implementations) utilize gRPC to expose a well-defined API for developers/services to build on top of. Establishing a connection requires two pieces of data: the TLS cert (usually self-signed) of the node, and also an initial macaroon (or some sort of other auth/api token which is used as a fine-grained authentication token. As a result, any application/wallet that wishes to be able to remotely interact with a Lightning node, must somehow have the user transmit both the cert and macaroon to the application context. In practice, pairing a node to an application requires users to manually handle potentially sensitive pieces of data in order to upload or copy/paste the two necessary pairing materials to an application context. Others solutions like LND Connect have users scan a very large QR code in order to transmit the information, which only really works in the mobile setting and can be somewhat cumbersome due to the size of the generated QR code.
When we set out to build the new version of Lightning Terminal we wanted to provide the most user-friendly way to have users securely pair their node to the new web-based version of Lightning Terminal. After sometime, we came up with the Lightning Node Connect flow:
-
A user creates a new session with their existing node using either the litd CLI command-line tool (litcli) or the configuration UI that’s now bundled with the latest version of the litd binary.
- At this point a user can optionally opt to create a restricted macaroon, like only giving the website access to reading graph related statistics maintained by the node, or having it expire after a period of time.
-
The user enters the generated pairing phrase (typically copy pasted, or encoded within a “magic link”) into the Lightning-enabled application, which then uses some crypto-magic and a simple messaging passing proxy to establish a secure end to end connection.
Using LNC, any Lightning-enabled web-application is able to securely interact with a remote node using the generated JS WASM interface, which allows for richer interaction on the web, enabling developers to create first-class experiences on the web, with the specifics of Lightning moving into the background. As an example, a website that allows for Reddit-like upvoting of news links no longer needs to display a new invoice with each click, and ask users to copy/paste then pay that. Instead, assuming the website has sufficient permissions, the website can instead carry out the payment in the background with just a click of the button.
Establishing A Secure RPC Connection Using gRPC+Noise+PAKE
Password Authenticated Key Exchange
One of the fundamental cryptographic primitives that LNC uses to allow users to easily set up an e2e encrypted session with a new application is Password Authenticated Key Exchange, or PAKE. PAKE is a cryptographic primitives that allows two parties to generate a cryptographic key with only knowledge of a shared password, that may be of low-entropy. The popular file-sharing tool Magic Wormhole uses PAKE to allow users to easily share a file between two computers using what they call “wormhole codes”. In our context, we’ll use PAKE to allow a browser application context (or any other application context) to securely connect to a remote Lightning node.
LNC's PAKE-Based Noise Handshake
The other half of the connection flow of LNC is its utilization of the Noise Protocol Framework that is already widely used in the Lightning Network (defined in BOLT 8 ) where it’s used to allow nodes to establish an encrypted connection, assuming one side knows the public key of the other side. LNC uses these long-term public keys to do away with TLS certs, utilizing two distinct handshakes. In the first handshake, the pairing phrase is used along with PAKE to allow both sides to establish a secure connection, deliver the macaroon auth token, and finally exchange long-term public keys. Subsequent connection attempts then no longer need to rely on the pairing phrase, as the application is able to establish a connection with the server using a modified Noise handshake and the server's long-term public key. The server is also able to authenticate the client, creating a system of mutual authentication that isn’t achieved with traditional usage of TLS.
Combining PAKE and Noise XX
The Noise Protocol Framework is divided into a series of handshake protocols, each with unique capabilities, such as enabling zero round-trip connection establishment. For LNC, we’ve taken the Noise XX handshake, and augmented it with a variant of Encrypted Key Exchange (EKE) based on a public key binding operation utilized by SPAKE2. At a high level, SPAKE 2 works by using the password to effectively blind a public key, in a manner that only allows a participant to unblind it with knowledge of the passphrase, as well as a distinct generator point we’ll refer to as M. In practice, M is typically generated in a fashion that ensures that no one knows the discrete logarithm (essentially the “private key”) for the point (typically known as a NUMs, or "Nothing Up My Sleeve Point). LNC ships with code that enables any part to verify the generator M used in the protocol was generated securely.
The first handshake used is fully parameterized by the protocol name:
Noise_XXeke+SPAKE2_secp256k1_ChaChaPoly_SHA256
. We use a similar set of
cryptographic primitives as BOLT
8, but then
augment the Noise XX handshake by blinding the first ephemeral key sent by the
initiator, thereby effectively “encrypting” the first ephemeral key sent. The
blinding operation is defined as:
me = e + M*h(pw)
The responder is then able to unblind the point only if they know the password with the following operation:
e = me - M*h(pw) = e + M*h(pw) - M*h(pw)
As you can see above, the blinded passphrase point cancels out, leaving only the original ephemeral key. The Noise handshake transcript includes the ephemeral point in the handshake transcript, which is used as associated data each time a MAC check is required. As a result, if the initiator uses the wrong passphrase, then the initial MAC check will fail, resulting in the pairing being aborted.
Delivering Macaroon Authentication During the Handshake
The Noise Protocol Framework allows each handshake message to optionally attach a portion of encrypted data appended to the normal handshake data (usually an ephemeral or static public key). LNC uses this feature to allow the server (in this case the user’s Lightning Node) to transmit a scoped macaroon to the initiator (the web application) during the handshake process, which ensures that the web application is able to immediately communicate with the node after the pairing has been completed. Once the initial pairing has been carried out, any subsequent connections will use the Noise IK handshake as it allows both sides to establish a connection in just a single round trip (knowledge of the public key of the responder is assumed to be known to the initiator).
With the above protocol, we’re able to easily establish a new encrypted connection between two parties that know a shared password/passphrase. However, this isn’t very useful in our context without also being able to allow the application to interact with the Lightning node using a structured API. This is where gRPC comes in along with its pluggable transport credential interface. This API of gRPC allows us to swap in the handshake described above in lieu of the normal TLS connection establishment. The end result is that an application is able to use gRPC as normal, with a slightly different set of interfaces passed in during configuration, and then is able to gain a secure connection to a remote gRPC server, using macaroons as its PerRPCCredential system.
Bypassing NAT Traversal w/ the Mailbox Proxy
With the above, we’re able to securely establish a gRPC connection between two endpoints, using only a shared passphrase. However, up until now, we’ve assumed that TCP has been used directly to establish a connection. This is a safe assumption in most cases however it also means that we assume the user has their node configured to accept new gRPC connections on the open internet. For technically savvy users, this may be an easy thing to do. On the other hand, less technical users may not be as familiar with things like port forwarding, or securely exposing an endpoint to the internet. As we want to provide an easy to use pairing experience to users, we don’t want to require them to set up such a connection. Instead, we’ll employ a simple message passing proxy that allows us to effectively sidestep NAT traversal.
LNC uses a simple message passing proxy that supports non-blocking asynchronous writes, and blocking reads. Connections to this proxy are simplex, meaning that you can only send or receive given what we call a mailbox ID (which is derived from the pairing phrase). In order to establish a bi-directional connection, one party establishes a mailbox they can use to receive messages from another, and the other establishes a mailbox connection they can use to send messages to the other party. Composing these, we’re able to simulate a full-duplex connection capable of supporting regular and streaming RPC connections.
Defensively, LNC assumes that the mailbox proxy connections are unreliable, and can potentially drop or fail or to deliver messages (due to a restart, etc). To ensure that the web application is able to recover from a reconnection or restart, we’ve implemented a simple Go-Back-N protocol to allow us to model the mailbox connection as a reliable in-order delivery mechanism. The Go-Back-N protocol encapsulates both the handshake messages, as well as the HTTP/2 gRPC messages, serving as the underlying transport layer. This is an important component, as it allows us to maintain the abstraction of a normal TCP-based gRPC connection.
Putting it All Together w/ WASM
The final component of LNC is the WASM layer that allows us to package everything described above (which is written in Go) into the browser context allowing it to be usable by a web application. The LNC reference implementation is able to generate a Javascript API, using Go’s WASM compilation layer that exposes the fundamental connection primitive, which can then be slotted into existing interfaces for gRPC connection establishment and interaction. Today the WASM layer is rather lightweight: it only handles session management and connection establishment, exposing a gRPC-like object to a JS environment. In the future, we aim to continue to expand the WASM component of LNC, turning it into a fully featured Lightning Browser SDK to enable the development of rich Lightning-enabled applications.
Conclusion & Future Directions
In this post, we’ve described the technical architecture of Lightning Node Connect, a new way to securely connect Lightning nodes to a web app. We envision LNC being used as a fundamental building block to enable richer Lightning-enabled applications on the web, thereby brining us one step closer to our vision of the Lightning Native Web. LNC is still in alpha, as we aim to make a number of robustness improvements, as well as being to flesh out the resulting Lightning web app SDK during this early phase of development. Once LNC leaves alpha, we aim to create a formal bLIP specification of the protocol, so that other implementations and developers can re-implement and utilize the solution more broadly.