Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ c-lightning is a lighweight, highly customizable and [standard compliant][std] i
* [Configuration File](#configuration-file)
* [Further Information](#further-information)
* [Pruning](#pruning)
* [HD wallet encryption](#hd-wallet-encryption)
* [Developers](#developers)

## Project Status
Expand Down Expand Up @@ -102,8 +103,6 @@ Once you've started for the first time, there's a script called
`contrib/bootstrap-node.sh` which will connect you to other nodes on
the lightning network.

You can encrypt the BIP32 root seed (what is stored in `hsm_secret`) by passing the `--encrypted-hsm` startup argument. You can start `lightningd` with `--encrypted-hsm` on an already existing `lightning-dir` (with a not encrypted `hsm_secret`). If you pass that option, you __will not__ be able to start `lightningd` (with the same wallet) again without the password, so please beware with your password management. Also beware of not feeling too safe with an encrypted `hsm_secret`: unlike for `bitcoind` where the wallet encryption can restrict the usage of some RPC command, `lightningd` always need to access keys from the wallet which is thus __not locked__ (yet), even with an encrypted BIP32 master seed.

There are also numerous plugins available for c-lightning which add
capabilities: in particular there's a collection at:

Expand All @@ -112,6 +111,9 @@ capabilities: in particular there's a collection at:
Including [helpme][helpme-github] which guides you through setting up
your first channels and customizing your node.

For a less reckless experience, you can encrypt the HD wallet seed:
see [HD wallet encryption](#hd-wallet-encryption).

You can also chat to other users at [#c-lightning @ freenode.net][irc2];
we are always happy to help you get started!

Expand Down Expand Up @@ -202,6 +204,12 @@ If `bitcoind` prunes a block that c-lightning has not processed yet, e.g., c-lig
In order to avoid this situation you should be monitoring the gap between c-lightning's blockheight using `lightning-cli getinfo` and `bitcoind`'s blockheight using `bitcoin-cli getblockchaininfo`.
If the two blockheights drift apart it might be necessary to intervene.

### HD wallet encryption

You can encrypt the `hsm_secret` content (which is used to derive the HD wallet's master key) by passing the `--encrypted-hsm` startup argument, or by using the `hsmtools` tool with the `encrypthsm` method. You can unencrypt an encrypted `hsm_secret` using the `hsmtools` tool with the `decrypthsm` method.

If you encrypt your `hsm_secret`, you will have to pass the `--encrypted-hsm` startup option to `lightningd`. Once your `hsm_secret` is encrypted, you __will not__ be able to access your funds without your password, so please beware with your password management. Also beware of not feeling too safe with an encrypted `hsm_secret`: unlike for `bitcoind` where the wallet encryption can restrict the usage of some RPC command, `lightningd` always need to access keys from the wallet which is thus __not locked__ (yet), even with an encrypted BIP32 master seed.

### Developers

Developers wishing to contribute should start with the developer guide [here](doc/HACKING.md).
Expand Down
61 changes: 59 additions & 2 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ def test_transaction_annotations(node_factory, bitcoind):


@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
def test_hsm_secret_encryption(node_factory, executor):
def test_hsm_secret_encryption(node_factory):
l1 = node_factory.get_node()
password = "reckful\n"
# We need to simulate a terminal to use termios in `lightningd`.
Expand All @@ -588,7 +588,6 @@ def test_hsm_secret_encryption(node_factory, executor):
l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT,
wait_for_initialized=False)
time.sleep(3 if SLOW_MACHINE else 1)
os.write(master_fd, password[2:].encode("utf-8"))
err = "hsm_secret is encrypted, you need to pass the --encrypted-hsm startup option."
assert l1.daemon.is_in_log(err)

Expand All @@ -606,3 +605,61 @@ def test_hsm_secret_encryption(node_factory, executor):
os.write(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log("Server started with public key")
assert id == l1.rpc.getinfo()["id"]


@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
def test_hsmtools_secret_decryption(node_factory):
l1 = node_factory.get_node()
password = "reckless\n"
hsm_path = os.path.join(l1.daemon.lightning_dir, "hsm_secret")
# We need to simulate a terminal to use termios in `lightningd`.
master_fd, slave_fd = os.openpty()

# Encrypt the master seed
l1.rpc.stop()
l1.daemon.opts.update({"encrypted-hsm": None})
l1.daemon.start(stdin=slave_fd, wait_for_initialized=False)
time.sleep(3 if SLOW_MACHINE else 1)
os.write(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log("Server started with public key")
node_id = l1.rpc.getinfo()["id"]
l1.stop()

# We can't use a wrong password !
cmd_line = ["tools/hsmtools", "decrypthsm", hsm_path, "A wrong pass"]
assert subprocess.Popen(cmd_line).wait() != 0

# Decrypt it with hsmtools
cmd_line[3] = password[:-1]
assert subprocess.Popen(cmd_line).wait() == 0
# Then test we can now start it without password
l1.daemon.opts.pop("encrypted-hsm")
l1.daemon.start(stdin=slave_fd, wait_for_initialized=True)
assert node_id == l1.rpc.getinfo()["id"]
l1.stop()

# Test we can encrypt it offline
cmd_line[1] = "encrypthsm"
assert subprocess.Popen(cmd_line).wait() == 0
# Now we need to pass the encrypted-hsm startup option
l1.stop()
l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT,
wait_for_initialized=False)
time.sleep(3 if SLOW_MACHINE else 1)
err = "hsm_secret is encrypted, you need to pass the --encrypted-hsm startup option."
assert l1.daemon.is_in_log(err)
l1.daemon.opts.update({"encrypted-hsm": None})
l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT,
wait_for_initialized=False)
time.sleep(3 if SLOW_MACHINE else 1)
os.write(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log("Server started with public key")
assert node_id == l1.rpc.getinfo()["id"]
l1.stop()

# And finally test that we can also decrypt if encrypted with hsmtools
cmd_line[1] = "decrypthsm"
assert subprocess.Popen(cmd_line).wait() == 0
l1.daemon.opts.pop("encrypted-hsm")
l1.daemon.start(stdin=slave_fd, wait_for_initialized=True)
assert node_id == l1.rpc.getinfo()["id"]
13 changes: 11 additions & 2 deletions tools/Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
#! /usr/bin/make
TOOLS := tools/hsmtools tools/check-bolt
TOOLS_SRC := $(TOOLS:=.c)
TOOLS_OBJ := $(TOOLS_SRC:.c=.o)
ALL_PROGRAMS += $(TOOLS)
ALL_OBJS += $(TOOLS_OBJ)

TOOLS_COMMON_OBJS = common/utils.o

# We force make to relink this every time, to detect version changes.
# Do it atomically, otherwise parallel builds can get upset!
tools/headerversions: FORCE tools/headerversions.o $(CCAN_OBJS)
@trap "rm -f $@.tmp.$$$$" EXIT; $(LINK.o) tools/headerversions.o $(CCAN_OBJS) $(LOADLIBES) $(LDLIBS) -o $@.tmp.$$$$ && mv $@.tmp.$$$$ $@

tools/check-bolt: tools/check-bolt.o $(CCAN_OBJS) common/utils.o
tools/check-bolt.o: $(CCAN_HEADERS)
tools/check-bolt: tools/check-bolt.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS)

tools/hsmtools: tools/hsmtools.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/bech32.o common/bigsize.o common/derive_basepoints.o common/key_derive.o common/node_id.o common/type_to_string.o wire/fromwire.o wire/towire.o

clean: tools-clean

tools-clean:
$(RM) tools/check-bolt tools/*.o
$(RM) tools/headerversions
$(RM) $(TOOLS_OBJ) $(TOOLS)

include tools/test/Makefile
Loading