Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hsm_encryption: read from STDIN if not in a TTY #4571

Merged
merged 4 commits into from
Jun 3, 2021

Conversation

openoms
Copy link
Contributor

@openoms openoms commented Jun 1, 2021

fixes: #4570 as discussed.

The temporary terminal with ECHO disabled is not created if the input is piped instead of typed in a terminal.

Passwords are still not visible when typed, but allows for automation.

Copy link
Contributor

@vincenzopalazzo vincenzopalazzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

utack 28e5494

@openoms
Copy link
Contributor Author

openoms commented Jun 1, 2021

thank you @vincenzopalazzo , just modified the commit to add the changelog as in https://lightning.readthedocs.io/STYLE.html#changelog-entries-in-commit-messages

@niftynei niftynei requested a review from darosior June 1, 2021 16:29
@niftynei
Copy link
Contributor

niftynei commented Jun 1, 2021

concept ACK 1b060d6

@niftynei niftynei added this to the v0.10.1 milestone Jun 1, 2021
Copy link
Contributor

@darosior darosior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

Nice improvement! This definitely needs a test though, and probably a changelog entry?

common/hsm_encryption.c Outdated Show resolved Hide resolved
if (tcgetattr(fileno(stdin), &current_term) != 0) {
*reason = "Could not get current terminal options.";
return NULL;
if (isatty(fileno(stdin))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that if this fails in lightningd/options.c, we'd fail. This is a bit unfortunate as we'd like to use it for lightningd as well i guess?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, indeed the piped password still fails for lightningd --encrypted_hsm
It is because the temporary terminal is being created as a duplicate in

if (tcgetattr(fileno(stdin), &current_term) != 0)
before calling the read_stdin_pass.
Should be able to clean that, testing now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved with: 8e86a57

tested ok with:
$ (echo "test"; echo "test") | lightningd --encrypted-hsm

and when called in the command line the password (echo) is still hidden

lightningd --encrypted-hsm
The hsm_secret is encrypted with a password. In order to decrypt it and start the node you must provide the password.
Enter hsm_secret password:
Confirm hsm_secret password:

Changelog-Added: hsmtool: allow piped passwords
Copy link
Contributor

@rustyrussell rustyrussell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack 8e86a57

Nice fix! Waiting for CI now...

@@ -2,6 +2,8 @@
#include <common/hsm_encryption.h>
#include <sodium/utils.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hidden whitespace at the end of line makes it fail make check-source:

common/hsm_encryption.c:6:#include <stdio.h>	
Extraneous whitespace found
make: *** [Makefile:455: check-whitespace/common/hsm_encryption.c] Error 1

Damn our pedantic source checks!

(I would normally just push a fix directly, but some people consider that rude :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you, fixed in 07269b0. It's fair, every character counts!

@rustyrussell
Copy link
Contributor

Ack cf5eabc

@openoms
Copy link
Contributor Author

openoms commented Jun 3, 2021

Now not sure how a CI test failure in

def test_funder_contribution_limits(node_factory, bitcoind):
is related to the changes here?

@cdecker
Copy link
Member

cdecker commented Jun 3, 2021

Likely just a deadlock that was addressed in antoher PR. Restarted CI.

@cdecker cdecker merged commit fab1ed6 into ElementsProject:master Jun 3, 2021
@darosior
Copy link
Contributor

darosior commented Jun 14, 2021

Post-merge ACK cf5eabc.

Tested manually and with:

diff --git a/tests/test_wallet.py b/tests/test_wallet.py
index d7c809da8..dee189ef2 100644
--- a/tests/test_wallet.py
+++ b/tests/test_wallet.py
@@ -993,7 +993,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):
     l1 = node_factory.get_node(may_fail=True)  # May fail when started without key
-    password = "reckful\n"
+    password = "reckful&é🍕\n"
     # We need to simulate a terminal to use termios in `lightningd`.
     master_fd, slave_fd = os.openpty()
 
@@ -1033,11 +1033,21 @@ def test_hsm_secret_encryption(node_factory):
     os.write(master_fd, password.encode("utf-8"))
     l1.daemon.wait_for_log("Server started with public key")
     assert id == l1.rpc.getinfo()["id"]
+    l1.stop()
+
+    # We can restore the same wallet with the same password provided through stdin
+    l1.daemon.start(stdin=subprocess.PIPE, wait_for_initialized=False)
+    l1.daemon.proc.stdin.write(password.encode("utf-8"))
+    l1.daemon.proc.stdin.write(password.encode("utf-8"))
+    l1.daemon.proc.stdin.flush()
+    l1.daemon.wait_for_log("Server started with public key")
+    assert id == l1.rpc.getinfo()["id"]
 
 
 class HsmTool(TailableProc):
     """Helper for testing the hsmtool as a subprocess"""
     def __init__(self, *args):
+        self.prefix = "hsmtool"
         TailableProc.__init__(self)
         assert hasattr(self, "env")
         self.cmd_line = ["tools/hsmtool", *args]
@@ -1046,7 +1056,7 @@ class HsmTool(TailableProc):
 @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
 def test_hsmtool_secret_decryption(node_factory):
     l1 = node_factory.get_node()
-    password = "reckless\n"
+    password = "reckless123#{ù}\n"
     hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
     # We need to simulate a terminal to use termios in `lightningd`.
     master_fd, slave_fd = os.openpty()
@@ -1127,6 +1137,26 @@ def test_hsmtool_secret_decryption(node_factory):
     l1.daemon.start(stdin=slave_fd, wait_for_initialized=True)
     assert node_id == l1.rpc.getinfo()["id"]
 
+    # We can roundtrip encryption and decryption using a password provided
+    # through stdin.
+    hsmtool = HsmTool("encrypt", hsm_path)
+    hsmtool.start(stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                  stderr=subprocess.PIPE)
+    hsmtool.proc.stdin.write(password.encode("utf-8"))
+    hsmtool.proc.stdin.write(password.encode("utf-8"))
+    hsmtool.proc.stdin.flush()
+    hsmtool.wait_for_log("Successfully encrypted")
+    assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
+
+    master_fd, slave_fd = os.openpty()
+    hsmtool = HsmTool("decrypt", hsm_path)
+    hsmtool.start(stdin=slave_fd,
+                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    hsmtool.wait_for_log("Enter hsm_secret password:")
+    os.write(master_fd, password.encode("utf-8"))
+    hsmtool.wait_for_log("Successfully decrypted")
+    assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
+
 
 @unittest.skipIf(TEST_NETWORK == 'liquid-regtest', '')
 def test_hsmtool_dump_descriptors(node_factory, bitcoind):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature request] allow reading passwords from piped STDIN
6 participants