|
12 | 12 | check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND,
|
13 | 13 | mine_funding_to_announce, VALGRIND
|
14 | 14 | )
|
| 15 | +from tests.test_wallet import HsmTool, write_all, WAIT_TIMEOUT |
15 | 16 |
|
16 | 17 | import ast
|
17 | 18 | import json
|
@@ -4141,7 +4142,6 @@ def test_important_plugin_shutdown(node_factory):
|
4141 | 4142 | l1.rpc.plugin_start(os.path.join(os.getcwd(), 'plugins/pay'))
|
4142 | 4143 |
|
4143 | 4144 |
|
4144 |
| -@pytest.mark.skip(reason="Temporarily disabled expose secrets test") |
4145 | 4145 | @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
|
4146 | 4146 | def test_exposesecret(node_factory):
|
4147 | 4147 | l1, l2 = node_factory.get_nodes(2, opts=[{'exposesecret-passphrase': "test_exposesecret"}, {}])
|
@@ -4196,30 +4196,96 @@ def test_exposesecret(node_factory):
|
4196 | 4196 | l1.start()
|
4197 | 4197 |
|
4198 | 4198 | assert l1.rpc.exposesecret(passphrase='test_exposesecret') == {'codex32': 'cl10junxsd35kw6r5de5kueedxyqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6mdtn5lql6p8m',
|
4199 |
| - 'identifier': 'junx'} |
| 4199 | + 'identifier': 'junx'} |
4200 | 4200 |
|
4201 |
| - # Test with encrypted-hsm (fails!) |
4202 |
| - password = 'test_exposesecret' |
4203 |
| - l1.stop() |
4204 |
| - # We need to simulate a terminal to use termios in `lightningd`. |
4205 |
| - master_fd, slave_fd = os.openpty() |
4206 | 4201 |
|
4207 |
| - def write_all(fd, bytestr): |
4208 |
| - """Wrapper, since os.write can do partial writes""" |
4209 |
| - off = 0 |
4210 |
| - while off < len(bytestr): |
4211 |
| - off += os.write(fd, bytestr[off:]) |
4212 |
| - |
4213 |
| - l1.daemon.opts.update({"encrypted-hsm": None}) |
4214 |
| - l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) |
4215 |
| - l1.daemon.wait_for_log(r'Enter hsm_secret password') |
4216 |
| - write_all(master_fd, (password + '\n').encode("utf-8")) |
4217 |
| - l1.daemon.wait_for_log(r'Confirm hsm_secret password') |
4218 |
| - write_all(master_fd, (password + '\n').encode("utf-8")) |
| 4202 | +@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") |
| 4203 | +def test_exposesecret_with_hsm_passphrase(node_factory): |
| 4204 | + """Test that exposesecret plugin correctly handles hsm-passphrase option""" |
| 4205 | + # Create a node with exposesecret-passphrase option |
| 4206 | + l1 = node_factory.get_node(options={ |
| 4207 | + 'exposesecret-passphrase': "test_exposesecret", |
| 4208 | + }, start=False) |
| 4209 | + |
| 4210 | + hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret") |
| 4211 | + if os.path.exists(hsm_path): |
| 4212 | + os.remove(hsm_path) |
| 4213 | + |
| 4214 | + # Generate hsm_secret with mnemonic and passphrase using hsmtool |
| 4215 | + mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" |
| 4216 | + hsm_passphrase = "test_hsm_passphrase" # Any passphrase, since we expect exposesecret to fail |
| 4217 | + expected_format = "mnemonic with passphrase" |
| 4218 | + |
| 4219 | + hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path) |
| 4220 | + master_fd, slave_fd = os.openpty() |
| 4221 | + hsmtool.start(stdin=slave_fd) |
| 4222 | + hsmtool.wait_for_log(r"Introduce your BIP39 word list") |
| 4223 | + write_all(master_fd, f"{mnemonic}\n".encode("utf-8")) |
| 4224 | + hsmtool.wait_for_log(r"Enter your passphrase:") |
| 4225 | + write_all(master_fd, f"{hsm_passphrase}\n".encode("utf-8")) |
| 4226 | + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 |
| 4227 | + hsmtool.is_in_log(r"New hsm_secret file created") |
| 4228 | + hsmtool.is_in_log(f"Format: {expected_format}") |
| 4229 | + os.close(master_fd) |
| 4230 | + os.close(slave_fd) |
| 4231 | + |
| 4232 | + # Add --hsm-passphrase option to trigger interactive prompting |
| 4233 | + l1.daemon.opts["hsm-passphrase"] = None |
| 4234 | + |
| 4235 | + # Create a pty to handle the interactive passphrase prompt |
| 4236 | + master_fd2, slave_fd2 = os.openpty() |
| 4237 | + l1.daemon.start(stdin=slave_fd2, wait_for_initialized=False) |
| 4238 | + |
| 4239 | + # Wait for the passphrase prompt and provide it |
| 4240 | + l1.daemon.wait_for_log("Enter hsm_secret passphrase:") |
| 4241 | + print(f"DEBUG: About to send passphrase: '{hsm_passphrase}'") |
| 4242 | + passphrase_bytes = f"{hsm_passphrase}\n".encode("utf-8") |
| 4243 | + print(f"DEBUG: Passphrase bytes: {passphrase_bytes}") |
| 4244 | + write_all(master_fd2, passphrase_bytes) |
| 4245 | + print("DEBUG: Passphrase sent!") |
| 4246 | + |
| 4247 | + # Wait for the node to be ready |
4219 | 4248 | l1.daemon.wait_for_log("Server started with public key")
|
| 4249 | + |
| 4250 | + os.close(master_fd2) |
| 4251 | + os.close(slave_fd2) |
| 4252 | + |
| 4253 | + # Test that exposesecret fails with mnemonic+passphrase format since it needs a passphrase |
| 4254 | + with pytest.raises(RpcError, match="Secret with passphrase is not supported"): |
| 4255 | + l1.rpc.exposesecret(passphrase="test_exposesecret") |
| 4256 | + |
| 4257 | + |
| 4258 | +@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") |
| 4259 | +def test_exposesecret_with_mnemonic_no_passphrase(node_factory): |
| 4260 | + """Test that exposesecret plugin works correctly with mnemonic-based hsm_secret without passphrase""" |
| 4261 | + # Create a node with exposesecret-passphrase option |
| 4262 | + l1 = node_factory.get_node(options={ |
| 4263 | + 'exposesecret-passphrase': "test_exposesecret", |
| 4264 | + }, start=False) |
| 4265 | + hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret") |
| 4266 | + if os.path.exists(hsm_path): |
| 4267 | + os.remove(hsm_path) |
| 4268 | + |
| 4269 | + # Generate hsm_secret with mnemonic and no passphrase using hsmtool |
| 4270 | + mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" |
| 4271 | + hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path) |
| 4272 | + master_fd, slave_fd = os.openpty() |
| 4273 | + hsmtool.start(stdin=slave_fd) |
| 4274 | + hsmtool.wait_for_log(r"Introduce your BIP39 word list") |
| 4275 | + write_all(master_fd, f"{mnemonic}\n".encode("utf-8")) |
| 4276 | + hsmtool.wait_for_log(r"Enter your passphrase:") |
| 4277 | + write_all(master_fd, "\n".encode("utf-8")) |
| 4278 | + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 |
| 4279 | + |
| 4280 | + # Start the daemon normally (no hsm-passphrase option needed for mnemonic without passphrase) |
| 4281 | + l1.start() |
| 4282 | + |
| 4283 | + # Test that exposesecret works correctly with mnemonic-based hsm_secret without passphrase |
| 4284 | + result = l1.rpc.exposesecret(passphrase="test_exposesecret") |
| 4285 | + assert 'codex32' in result |
| 4286 | + assert 'identifier' in result |
| 4287 | + assert 'mnemonic' in result |
4220 | 4288 |
|
4221 |
| - with pytest.raises(RpcError, match="maybe encrypted"): |
4222 |
| - l1.rpc.exposesecret(passphrase=password) |
4223 | 4289 |
|
4224 | 4290 |
|
4225 | 4291 | def test_peer_storage(node_factory, bitcoind):
|
|
0 commit comments