Skip to content
Open
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
3 changes: 2 additions & 1 deletion hsmd/hsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <wally_bip39.h>
#include <wally_core.h>
#include <wire/wire_io.h>
#include <wire/wire_sync.h>

/*~ Each subdaemon is started with stdin connected to lightningd (for status
* messages), and stderr untouched (for emergency printing). File descriptors
Expand Down Expand Up @@ -291,7 +292,7 @@ static void hsmd_send_init_reply_failure(enum hsm_secret_error error_code, enum
msg = towire_hsmd_init_reply_failure(NULL, error_code, formatted_msg);
if (msg) {
/* Send directly to lightningd via REQ_FD */
write_all(REQ_FD, msg, tal_bytelen(msg));
wire_sync_write(REQ_FD, msg);
tal_free(msg);
}

Expand Down
44 changes: 44 additions & 0 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -2492,3 +2492,47 @@ def test_pending_payments_cleanup(node_factory, bitcoind):
l1 = node_factory.get_node(dbfile='l1-pending-sendpays-with-no-htlc.sqlite3.xz', options={'database-upgrade': True})
assert [p['status'] for p in l1.rpc.listsendpays()['payments']] == ['failed', 'pending']
assert [p['status'] for p in l1.rpc.listpays()['pays']] == ['pending']


@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
def test_hsm_wrong_passphrase_crash(node_factory):
"""Test that hsmd handles wrong passphrase gracefully without crashing.

This test reproduces a bug where hsmd would crash with "HSM sent unknown message type"
when a wrong passphrase was provided. The issue was that hsmd_send_init_reply_failure
was using write_all() instead of wire_sync_write(), missing the length prefix.
"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path)

# Create hsm_secret with a passphrase
passphrase = "correct_passphrase"
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list")
write_all(master_fd, f"{mnemonic}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, f"{passphrase}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
os.close(master_fd)
os.close(slave_fd)

# Try to start with wrong passphrase
l1.daemon.opts["hsm-passphrase"] = None
master_fd2, slave_fd2 = os.openpty()
l1.daemon.start(stdin=slave_fd2, wait_for_initialized=False)
l1.daemon.wait_for_log("Enter hsm_secret passphrase:")
write_all(master_fd2, "wrong_passphrase\n".encode("utf-8"))

# Should fail gracefully with proper error message, not "unknown message type"
l1.daemon.wait_for_log("Failed to load hsm_secret: Wrong passphrase")
ret = l1.daemon.proc.wait(WAIT_TIMEOUT)
assert ret == HSM_BAD_PASSWORD
assert not l1.daemon.is_in_log("HSM sent unknown message type")

os.close(master_fd2)
os.close(slave_fd2)
Loading