-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Paratonnerre is a protocol to safely control a remote lightning node from an untrusted application using a secure hardware device.
- Loading branch information
Showing
3 changed files
with
330 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,326 @@ | ||
``` | ||
bLIP: 28 | ||
Title: Paratonnerre | ||
Status: Draft | ||
Author: Bastien Teinturier <[email protected]> | ||
Created: 2023-09-05 | ||
License: CC0 | ||
``` | ||
|
||
## Abstract | ||
|
||
This bLIP details how hardware devices can be used to securely control a remote lightning node from an untrusted machine. | ||
|
||
## Motivation | ||
|
||
Lightning nodes are effectively hot wallets, which makes them valuable targets for attackers. | ||
Node operators thus need to take great care when securing access to their node. | ||
But on the other hand, lightning nodes regularly require actions from the node operator (to open and close channels, pay invoices, etc). | ||
We'd like node operators to be able to complete those tasks without exposing their node to additional threats. | ||
|
||
The most common ways of completing such tasks are: | ||
|
||
- directly connecting to the node's machine (e.g. using ssh) and calling its local RPCs | ||
- exposing the node's RPCs over the internet, with some form of authentication | ||
|
||
Both of these options are dangerous if the machine used to perform those tasks is compromised. This where hardware devices such as hardware wallets come in handy: they let their owner securely perform critical operations from a potentially compromised machine. | ||
|
||
## Specification | ||
|
||
### Terminology | ||
|
||
We use the following three components: | ||
|
||
- lightning node: the user's lightning node, running on a remote server | ||
- hardware device: a secure hardware device with a trusted display (e.g. Ledger Nano S) | ||
- companion app: an untrusted application used to communicate with the hardware device and the lightning node | ||
|
||
The companion app can run on any kind of untrusted hardware: laptop, desktop, mobile phone, smart watch...anything that has network connectivity and a way to communicate with the hardware device (e.g. bluetooth). | ||
|
||
### High-level Architecture | ||
|
||
```diagram | ||
+-----------------+ commands +---------------+ +----------------+ | ||
| |<-----------------------| | TCP packets | | | ||
| Hardware Device | TCP packets | Companion app |<---------------------->| Lightning Node | | ||
| |<---------------------->| | encrypted (Bolt 8) | | | ||
+-----------------+ encrypted (Bolt 8) +---------------+ +----------------+ | ||
``` | ||
|
||
### Initial Setup | ||
|
||
On the hardware device: | ||
|
||
- generate a secp256k1 private key and securely store it (e.g. in a secure element) | ||
- display the corresponding public key (`hd_node_id`) on the trusted display | ||
|
||
On the lightning node: | ||
|
||
- add the corresponding `hd_node_id` to a list of trusted hardware devices | ||
|
||
The lightning node will then accept custom lightning messages (defined in the [messages](#messages) section below) from Bolt 8 connections that were initiated from a `node_id` in its list of trusted hardware devices. | ||
|
||
### Protocol flow | ||
|
||
The protocol is fairly simple: the hardware device implements Bolt 8 and establishes a connection to the lightning node, with the help of the companion app which simply forwards encrypted TCP packets in both directions. Since Bolt 8 encryption and decryption is done entirely within the hardware device, the companion app cannot read or modify any of the messages exchanged between the hardware device and the lightning node. | ||
|
||
Before sending any message, the hardware device prints its details on its trusted display and waits for confirmation from the node operator. Similarly, messages from the lightning node are printed on the hardware device's trusted display, which lets the node operator verify the result of the commands that were issued. | ||
|
||
The companion app is used to provide an easy-to-use interface to prepare commands. If the companion app tries to trick the hardware device into sending a different command to the lightning node, the node operator will notice it when confirming the command on the hardware device's trusted display. | ||
|
||
```diagram | ||
Node Operator Companion App Lightning Node | ||
| create command | | | ||
|------------------------------------------------------->| | | ||
| | | | ||
| Hardware Device | | | ||
| | send(command) | | | ||
| |<------------------------------| | | ||
| confirm command | | | | ||
|<-----------------------| | | | ||
| ok | | | | ||
|----------------------->| | | | ||
| | | | | ||
| +---| | | | ||
| encrypt | | | | | ||
| +-->| | | | ||
| | forward(encrypted_packet) | | | ||
| |------------------------------>| | | ||
| | | encrypted_packet | | ||
| | |------------------------->| | ||
| | | |---+ | ||
| | | | | - verify that remote `node_id` | ||
| | | | | is allowed to issue command | ||
| | | | | - execute command | ||
| | | | | - send response | ||
| | | |<--+ | ||
| | | encrypted_packet | | ||
| | |<-------------------------| | ||
| | receive(encrypted_packet) | | | ||
| |<------------------------------| | | ||
| +---| | | | ||
| decrypt | | | | | ||
| +-->| | | | ||
| | | | | ||
| command result | | | | ||
|<-----------------------| | | | ||
| | | | | ||
``` | ||
|
||
### Messages | ||
|
||
We define the following new lightning messages: | ||
|
||
| Type | Name | | ||
|-------|---------------------| | ||
| 73760 | `hd_init` | | ||
| 73761 | `hd_command` | | ||
| 73762 | `hd_command_result` | | ||
|
||
#### The `hd_init` message | ||
|
||
The `hd_init` message is used to initialize a connection between a hardware device and a lightning node and negotiate the commands that can be issued on that connection. | ||
|
||
1. type: 73760 (`hd_init`) | ||
2. data: | ||
- [`u16`:`commands_len`] | ||
- [`commands_len*byte`:`commands`] | ||
|
||
The `commands` field is a bitset where the bit at index `i` indicates support for the `hd_command` with `command_type=i`. | ||
|
||
#### The `hd_command` message | ||
|
||
The `hd_command` message is always sent by the hardware device. It wraps an actual command. | ||
|
||
1. type: 73761 (`hd_command`) | ||
2. data: | ||
- [`u16`:`command_type`] | ||
- [`...byte`:`command`] | ||
|
||
#### The `hd_command_result` message | ||
|
||
The `hd_command_result` message is always sent by the lightning node in response to an `hd_command`. | ||
It lets the node operator know whether the command was successfully applied or not. | ||
|
||
1. type: 73762 (`hd_command_result`) | ||
2. data: | ||
- [`u16`:`command_result_type`] | ||
- [`...byte`:`command_result`] | ||
|
||
#### Commands | ||
|
||
##### Connect | ||
|
||
The `connect` command tells the lightning node to connect to another lightning node. | ||
The `address` field uses the same encoding as the corresponding `node_announcement` field. | ||
|
||
1. command type: 0 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`...*byte`:`address`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 0 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`byte`:`success`] | ||
|
||
##### Disconnect | ||
|
||
The `disconnect` command tells the lightning node to disconnect from one of its peers. | ||
|
||
1. command type: 1 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 1 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`byte`:`success`] | ||
|
||
##### Open Channel | ||
|
||
The `open_channel` command tells the lightning node to open a channel to a given peer. | ||
|
||
1. command type: 2 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`u64`:`funding_satoshis`] | ||
- [`u32`:`feerate_per_kw`] | ||
- [`byte`:`announce_channel`] | ||
- [`u16`:`channel_type_len`] | ||
- [`channel_type_len*byte`:`channel_type`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 2 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`byte`:`success`] | ||
- [`channel_id`:`channel_id`] | ||
- [`u64`:`funding_fee_satoshis`] | ||
|
||
##### Close Channel | ||
|
||
The `close_channel` command tells the lightning node to cooperatively close a channel. | ||
|
||
1. command type: 3 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`channel_id`:`channel_id`] | ||
- [`u32`:`feerate_per_kw`] | ||
- [`u16`:`len`] | ||
- [`len*byte`:`scriptpubkey`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 3 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`channel_id`:`channel_id`] | ||
- [`byte`:`success`] | ||
|
||
##### Force-Close Channel | ||
|
||
The `force_close_channel` command tells the lightning node to unilaterally close a channel. | ||
|
||
1. command type: 4 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`channel_id`:`channel_id`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 4 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`channel_id`:`channel_id`] | ||
- [`byte`:`success`] | ||
|
||
##### Update Relay Fee | ||
|
||
The `update_relay_fee` command tells the lightning node to update the routing fees for channels with a given peer. | ||
|
||
1. command type: 5 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`u32`:`fee_base_msat`] | ||
- [`u32`:`fee_proportional_millionths`] | ||
- [`u16`:`cltv_expiry_delta`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 5 | ||
2. data: | ||
- [`point`:`remote_node_id`] | ||
- [`byte`:`success`] | ||
- [`u32`:`previous_fee_base_msat`] | ||
- [`u32`:`fee_base_msat`] | ||
- [`u32`:`previous_fee_proportional_millionths`] | ||
- [`u32`:`fee_proportional_millionths`] | ||
- [`u16`:`previous_cltv_expiry_delta`] | ||
- [`u16`:`cltv_expiry_delta`] | ||
|
||
##### Create Invoice | ||
|
||
The `create_invoice` command tells the lightning node to create a Bolt 11 invoice. | ||
|
||
1. command type: 6 | ||
2. data: | ||
- [`u32`:`amount_msat`] | ||
- [`u16`:`description_len`] | ||
- [`description_len*byte`:`description`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 6 | ||
2. data: | ||
- [`u16`:`invoice_len`] | ||
- [`invoice_len*byte`:`invoice`] | ||
|
||
##### Pay Invoice | ||
|
||
The `pay_invoice` command tells the lightning node to pay a given Bolt 11 invoice. | ||
|
||
1. command type: 7 | ||
2. data: | ||
- [`u16`:`invoice_len`] | ||
- [`invoice_len*byte`:`invoice`] | ||
- [`u32`:`amount_msat`] | ||
- [`u32`:`max_fee_msat`] | ||
|
||
The corresponding `hd_command_result` is: | ||
|
||
1. command type: 7 | ||
2. data: | ||
- [`byte`:`success`] | ||
- [`32*byte`:`payment_preimage`] | ||
- [`u32`:`routing_fees_msat`] | ||
|
||
### Requirements | ||
|
||
The hardware device: | ||
|
||
- MUST display the `node_id` of the lightning node and wait for confirmation before initiating a connection | ||
- MUST encrypted packets as specified in Bolt 8 before sending them to the companion app | ||
- MUST send `hd_init` instead of the standard `init` message once the connection has been established | ||
- MUST display commands and wait for confirmation before sending them | ||
- MUST send `ping` messages to keep the lightning connection alive | ||
- MUST respond to `ping` messages | ||
- SHOULD NOT require confirmation before sending `ping` and `pong` messages | ||
|
||
The lightning node, when receiving a connection from a `node_id` in its configured list of trusted hardware devices: | ||
|
||
- MUST send `hd_init` instead of the standard `init` message once the connection has been established | ||
- MUST only send the following messages on that connection: | ||
- `hd_init` | ||
- `hd_command_result` | ||
- `ping` | ||
- `pong` | ||
|
||
## Copyright | ||
|
||
This bLIP is licensed under the CC0 license. |