Skip to content

Commit 6724db6

Browse files
erickcestarirustyrussell
authored andcommitted
BOLT11: Make payment secret field ('s') mandatory
Make the payment secret field ('s') mandatory for BOLT11 payment requests, implementing the requirement specified in BOLT11 spec PR #1242 (lightning/bolts#1242). This security enhancement prevents payment probing attacks by requiring all invoices to include payment secrets. Changes include: 1. Adding validation in bolt11_decode_nosig() to reject invoices without the 's' field 2. Adding payment secrets to all test vectors 3. Updating expected encoded values in test cases to include payment secrets 4. Adding a specific test case that verifies proper rejection of invoices missing the payment secret field Changelog-Changed: Made payment secret ('s' field) mandatory in BOLT11 payment requests for improved security.
1 parent 66b6c11 commit 6724db6

File tree

4 files changed

+21
-10
lines changed

4 files changed

+21
-10
lines changed

common/bolt11.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,9 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str,
929929
if (!have_field[bech32_charset_rev['p']])
930930
return decode_fail(b11, fail, "No valid 'p' field found");
931931

932+
if (!have_field[bech32_charset_rev['s']])
933+
return decode_fail(b11, fail, "Missing required payment secret (s field)");
934+
932935
if (have_field[bech32_charset_rev['h']] && description) {
933936
struct sha256 sha;
934937

common/test/run-bolt11.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ int main(int argc, char *argv[])
300300
b11 = new_bolt11(tmpctx, &msatoshi);
301301
b11->chain = chainparams_for_network("bitcoin");
302302
b11->timestamp = 1496314658;
303+
b11->payment_secret = tal(b11, struct secret);
304+
memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret));
303305
if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102",
304306
strlen("0001020304050607080900010203040506070809000102030405060708090102"),
305307
&b11->payment_hash, sizeof(b11->payment_hash)))
@@ -308,9 +310,7 @@ int main(int argc, char *argv[])
308310
b11->description = "1 cup coffee";
309311
b11->expiry = 60;
310312

311-
dev_bolt11_omit_c_value = true;
312-
test_b11("LNBC2500U1PVJLUEZPP5QQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQYPQDQ5XYSXXATSYP3K7ENXV4JSXQZPUAZTRNWNGZN3KDZW5HYDLZF03QDGM2HDQ27CQV3AGM2AWHZ5SE903VRUATFHQ77W3LS4EVS3CH9ZW97J25EMUDUPQ63NYW24CG27H2RSPFJ9SRP", b11, NULL);
313-
dev_bolt11_omit_c_value = false;
313+
test_b11("LNBC2500U1PVJLUEZSP5ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYGSPP5QQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQYPQDQ5XYSXXATSYP3K7ENXV4JSXQZPUCQPJEJ3HDW922T23KQLTFQDTUF2MGFQESVYFKKAQVE0HW8VD0CHQSTEJ60K02J4W6NP4CUJV87TUS82EDUYX7YTWFW4S58CJTG0U0A6TD6CQ3ADH8K", b11, NULL);
314314

315315
/* Unknown field handling */
316316
if (!node_id_from_hexstr("02330d13587b67a85c0a36ea001c4dba14bcd48dda8988f7303275b040bffb6abd", strlen("02330d13587b67a85c0a36ea001c4dba14bcd48dda8988f7303275b040bffb6abd"), &node))
@@ -319,6 +319,8 @@ int main(int argc, char *argv[])
319319
b11 = new_bolt11(tmpctx, &msatoshi);
320320
b11->chain = chainparams_for_network("testnet");
321321
b11->timestamp = 1554294928;
322+
b11->payment_secret = tal(b11, struct secret);
323+
memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret));
322324
if (!hex_decode("850aeaf5f69670e8889936fc2e0cff3ceb0c3b5eab8f04ae57767118db673a91",
323325
strlen("850aeaf5f69670e8889936fc2e0cff3ceb0c3b5eab8f04ae57767118db673a91"),
324326
&b11->payment_hash, sizeof(b11->payment_hash)))
@@ -334,9 +336,7 @@ int main(int argc, char *argv[])
334336
extra->data[i] = bech32_charset_rev[(u8)"dp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8g"[i]];
335337
list_add(&b11->extra_fields, &extra->list);
336338

337-
dev_bolt11_omit_c_value = true;
338-
test_b11("lntb30m1pw2f2yspp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8g4a3hx0v945csrmpm7yxyaamgt2xu7mu4xyt3vp7045n4k4czxf9kj0vw0m8dr5t3pjxuek04rtgyy8uzss5eet5gcyekd6m7u0mzv5sp7mdsag", b11, NULL);
339-
dev_bolt11_omit_c_value = false;
339+
test_b11("lntb30m1pw2f2yssp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqcqpjvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8gcz45tperrgam4d3t6hc9kaguyzx08mrqsfhfxkx5fmpufvxfvypypyjcpxzvnnzq9jwsm3htpkwkxqsp8jt95ekkzq8ck5vze4lpehqq5cdj22", b11, NULL);
340340

341341
/* BOLT #11:
342342
*
@@ -690,6 +690,13 @@ int main(int argc, char *argv[])
690690
assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x", NULL, NULL, NULL, &fail));
691691
assert(streq(fail, "Invalid sub-millisatoshi amount '2500000001p'"));
692692

693+
/* BOLT #11:
694+
* > ### Missing required `s` field.
695+
* > lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp49qdkj
696+
*/
697+
assert(!bolt11_decode(tmpctx, "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp49qdkj", NULL, NULL, NULL, &fail));
698+
assert(streq(fail, "Missing required payment secret (s field)"));
699+
693700
/* Invalid UTF-8 tests. */
694701
/* Description: Bad UTF-8: 0xC0" */
695702
assert(!bolt11_decode(tmpctx, "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5gfskggz423rz6wp6yrqqcqpjkkrsmq07c4ht7qgjdmf2a8savsafcy8lqn4av4gs80gz88ff2y780tdcve7sxp80kd4vk7hajt5mskcsegz2qfll4jywfwhap2q2n6cqyz5tv4", NULL, NULL, NULL, &fail));

tests/test_invoices.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,19 +571,20 @@ def test_waitanyinvoice_reversed(node_factory, executor):
571571
def test_decode_unknown(node_factory):
572572
l1 = node_factory.get_node()
573573

574-
b11 = l1.rpc.decode('lntb30m1pw2f2yspp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8gu2etcvsym36pdjpz04wm9nn96f9ntc3t3h5r08pe9d62p3js5wt5rkurqnrl7zkj2fjpvl3rmn7wwazt80letwxlm22hngu8n88g7hsp542qpl')
574+
# Made with "devtools/bolt11-cli encode 41bfd2660762506c9933ade59f1debf7e6495b10c14a92dbcd2d623da2507d3d s=0000000000000000000000000000000000000000000000000000000000000000 timestamp=1554294928 p=850aeaf5f69670e8889936fc2e0cff3ceb0c3b5eab8f04ae57767118db673a91 d='Payment request with multipart support' x=28800 118=0d011a07081c011a051c1713020e0912051819121c0c0911061017030e0d191a07001803100e090f0d151a16181d03090e011017041d011f0e1110160e0f0b0d0e151607081a0b100c05190708" amount=3000000000 currency=tb
575+
b11 = l1.rpc.decode('lntb30m1pw2f2yssp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqcqpjvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8g02pjf6p8tqx0fy6vmxnrvsjmnx7us54ml9uk0927wy4mq5wne8pr6g6mmew03y60lt5mvzyksf7yjwq4qxlun5cca9amgkxggr2xkvcqukuyuf')
575576
assert b11['type'] == 'bolt11 invoice'
576577
assert b11['currency'] == 'tb'
577578
assert b11['created_at'] == 1554294928
578579
assert b11['payment_hash'] == '850aeaf5f69670e8889936fc2e0cff3ceb0c3b5eab8f04ae57767118db673a91'
579580
assert b11['description'] == 'Payment request with multipart support'
580581
assert b11['expiry'] == 28800
581-
assert b11['payee'] == '02330d13587b67a85c0a36ea001c4dba14bcd48dda8988f7303275b040bffb6abd'
582+
assert b11['payee'] == '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518'
582583
assert b11['min_final_cltv_expiry'] == 18
583584
extra = only_one(b11['extra'])
584585
assert extra['tag'] == 'v'
585586
assert extra['data'] == 'dp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8g'
586-
assert b11['signature'] == '3045022100e2b2bc3204dc7416c8227d5db2ce65d24b35e22b8de8379c392b74a0c650a397022041db8304c7ff0ad25264167e23dcfce7744b3bff95b8dfda9579a38799ce8f5e'
587+
assert b11['signature'] == '304402207a8324e827580cf4934cd9a636425b99bdc852bbf97967955e712bb051d3c9c202203d235bde5cf8934ffae9b60896827c49381501bfc9d318e97bb458c840d46b33'
587588
assert 'fallbacks' not in b11
588589
assert 'routes' not in b11
589590

tests/test_pay.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2961,7 +2961,7 @@ def test_pay_no_secret(node_factory, bitcoind):
29612961

29622962
# Produced from old version (no secret!)
29632963
inv_nosecret = 'lnbcrt1u1pwue4vapp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdqaw3jhxazlwpshjhmwda0hxetrwfjhgxq8pmnt9qqcqp9570xsjyykvssa6ty8fjth6f2y8h09myngad9utesttwjwclv95fz3lgd402f9e5yzpnxmkypg55rkvpg522gcz4ymsjl2w3m4jhw4jsp55m7tl'
2964-
with pytest.raises(RpcError, match=r"INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.*'erring_index': 1"):
2964+
with pytest.raises(RpcError, match=r"Invalid bolt11: Missing required payment secret \(s field\)"):
29652965
l1.rpc.pay(inv_nosecret)
29662966

29672967

0 commit comments

Comments
 (0)