bolt11 decode accepts a duplicate p or s field and keeps the first
メンテナーはふだん 2 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
issue のテストを common/test/run-bolt11-duplicate-p-s.c に落とし込み、make unittest/common/test/run-bolt11-duplicate-p-s を実行して 3 つの失敗を確認する。次に common/bolt11.c の 924 行目付近にあるタグ付きフィールドのループを読み、そこで繰り返しが have_field[type] && !decoder->allow_duplicates 分岐に入り unknown_field に振り分けられることを確認する。他の無効な invoice に対して decode がどのように失敗を報告するかを確認する。完了とは、3 つの invoice がすべてエラー文字列付きでデコードに失敗し、一方で common/test/run-bolt11.c 内の有効な BOLT 11 の例は引き続き通ることである。
索引モデルが issue の本文から書いたものです。
説明
Issue and Steps to Reproduce
bolt11_decode_nosig accepts an invoice with two distinct p fields, the same p twice, or two distinct s fields, and keeps the first copy. This happens on master at 1ebc4943 and in v26.06.9.
This test goes in common/test/run-bolt11-duplicate-p-s.c. It signs an otherwise valid invoice with the BOLT 11 example key. It adds a second p or s field, matching the three invalid vectors in the spec.
#include "config.h"
#include "../amount.c"
#include "../bech32.c"
#include "../bech32_util.c"
#include "../bolt11.c"
#include "../features.c"
#include "../node_id.c"
#include "../hash_u5.c"
#include "../wire/fromwire.c"
#include "../wire/towire.c"
#include <ccan/err/err.h>
#include <ccan/str/str.h>
#include <common/setup.h>
#include <stdio.h>
/* AUTOGENERATED MOCKS START */
/* Generated stub for siphash_seed */
const struct siphash_seed *siphash_seed(void)
{ fprintf(stderr, "siphash_seed called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static struct privkey privkey;
/* As in run-bolt11.c: sign with the BOLT #11 example key. */
static bool test_sign(const u5 *u5bytes,
const u8 *hrpu8,
secp256k1_ecdsa_recoverable_signature *rsig,
void *unused UNUSED)
{
struct hash_u5 hu5;
char *hrp;
struct sha256 sha;
hrp = tal_dup_arr(NULL, char, (char *)hrpu8, tal_count(hrpu8), 1);
hrp[tal_count(hrpu8)] = '\0';
hash_u5_init(&hu5, hrp);
hash_u5(&hu5, u5bytes, tal_count(u5bytes));
hash_u5_done(&hu5, &sha);
tal_free(hrp);
if (!secp256k1_ecdsa_sign_recoverable(secp256k1_ctx, rsig,
(const u8 *)&sha,
privkey.secret.data,
NULL, NULL))
abort();
return true;
}
/* BOLT #11:
*
* > ### Please send $3 for a cup of coffee to the same peer, within one minute
*
* We take that example's fields and sign them with the example key. The first
* `p` is payment_hash 0001..0102 and the first `s` is secret 0x1111..1111.
*/
static struct bolt11 *coffee(void)
{
struct amount_msat msat = AMOUNT_MSAT(2500 * (1000ULL * 100000000) / 1000000);
struct bolt11 *b11 = new_bolt11(tmpctx, &msat);
b11->chain = chainparams_for_network("bitcoin");
b11->timestamp = 1496314658;
b11->payment_secret = tal(b11, struct secret);
memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret));
if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102",
strlen("0001020304050607080900010203040506070809000102030405060708090102"),
&b11->payment_hash, sizeof(b11->payment_hash)))
abort();
b11->description = "1 cup coffee";
b11->expiry = 60;
set_feature_bit(&b11->features, 8);
set_feature_bit(&b11->features, 14);
return b11;
}
/* The 52 u5s of a 256-bit `p` or `s` field, as the encoder lays them out. */
static u5 *field256(const tal_t *ctx, const void *src)
{
u5 *d = tal_arr(ctx, u5, 0);
bech32_push_bits(&d, src, 256);
return d;
}
/* Add a second copy of tag `type` (a well-formed 52-u5 field) and decode. */
static struct bolt11 *second_field_then_decode(struct bolt11 *b11, char type,
const void *data,
const char **enc,
const char **fail)
{
struct bolt11_field *extra = tal(b11, struct bolt11_field);
extra->tag = type;
extra->data = field256(extra, data);
list_add_tail(&b11->extra_fields, &extra->list);
dev_bolt11_omit_c_value = true;
*enc = bolt11_encode(tmpctx, b11, false, test_sign, NULL);
dev_bolt11_omit_c_value = false;
return bolt11_decode(tmpctx, *enc, NULL, NULL, NULL, fail);
}
int main(int argc, char *argv[])
{
struct bolt11 *two_p, *same_p, *two_s;
const char *two_p_str, *same_p_str, *two_s_str;
const char *two_p_fail, *same_p_fail, *two_s_fail;
struct sha256 first_hash, other_hash;
struct secret other_secret;
common_setup(argv[0]);
/* BOLT #11:
*
* NB: all the following examples are signed with `priv_key`=`e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734`.
*/
if (!hex_decode("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734",
strlen("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734"),
&privkey, sizeof(privkey)))
abort();
memset(&first_hash, 0, sizeof(first_hash));
if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102",
strlen("0001020304050607080900010203040506070809000102030405060708090102"),
&first_hash, sizeof(first_hash)))
abort();
memset(&other_hash, 0xff, sizeof(other_hash));
memset(&other_secret, 0x22, sizeof(other_secret));
/* BOLT #11, "Examples of Invalid Invoices":
*
* > ### Two distinct p fields in an otherwise valid invoice.
*/
two_p = second_field_then_decode(coffee(), 'p', &other_hash,
&two_p_str, &two_p_fail);
if (two_p)
warnx("two distinct p accepted (first payment_hash kept: %s): %s",
memcmp(&two_p->payment_hash, &first_hash,
sizeof(first_hash)) == 0 ? "yes" : "no",
two_p_str);
else
warnx("two distinct p rejected: %s", two_p_fail);
/* BOLT #11:
*
* > ### The same p field twice in an otherwise valid invoice.
*/
same_p = second_field_then_decode(coffee(), 'p', &first_hash,
&same_p_str, &same_p_fail);
if (same_p)
warnx("same p twice accepted (first payment_hash kept: %s): %s",
memcmp(&same_p->payment_hash, &first_hash,
sizeof(first_hash)) == 0 ? "yes" : "no",
same_p_str);
else
warnx("same p twice rejected: %s", same_p_fail);
/* BOLT #11:
*
* > ### Two distinct s fields in an otherwise valid invoice.
*/
two_s = second_field_then_decode(coffee(), 's', &other_secret,
&two_s_str, &two_s_fail);
if (two_s)
warnx("two distinct s accepted (first payment_secret kept: %s): %s",
(two_s->payment_secret
&& memcmp(two_s->payment_secret, "\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11",
sizeof(*two_s->payment_secret)) == 0) ? "yes" : "no",
two_s_str);
else
warnx("two distinct s rejected: %s", two_s_fail);
/* BOLT #11:
*
* A reader...
* - MUST fail the payment unless exactly one `p` field and exactly one
* `s` field are present.
*/
assert(!two_p);
assert(!same_p);
assert(!two_s);
common_shutdown();
}
Build and run it with make unittest/common/test/run-bolt11-duplicate-p-s. The output on master 1ebc4943 is:
run-bolt11-duplicate-p-s: two distinct p accepted (first payment_hash kept: yes): lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgqpp5lllllllllllllllllllllllllllllllllllllllllllllllllllskg75fphvn0pgjw6vtsm7nmp76y2nffzgydpl40veyegmve0q3wl5pg5ky9xd4cnx7cuel8jn6v9ysns20vd6sehh0zv0quf56ugl36gp5n4uqy
run-bolt11-duplicate-p-s: same p twice accepted (first payment_hash kept: yes): lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgqpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsqwpc2yzcv68femngadqjkp2p5xdx20gv79puw5my3jqxh764fz9fxg0ukyl9sn3klfw8aus7gc2sr2q4htfey2y8leyjfyxvttgdysqyz59qd
run-bolt11-duplicate-p-s: two distinct s accepted (first payment_secret kept: yes): lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgqsp5yg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3q6mwaes2cclntjrk4aggagps5zns7rd70cm56pw2v49lqq60styln50g29ju2xpvqdvv3my00nkhzv70srfkawqa46c9z4aefvupctfcqdfc55d
run-bolt11-duplicate-p-s: common/test/run-bolt11-duplicate-p-s.c:180: main: Assertion `!two_p' failed.
make: *** [Makefile:942: unittest/common/test/run-bolt11-duplicate-p-s] Error 134
bolt11_decode_nosig sends the repeat to unknown_field through the have_field[type] && !decoder->allow_duplicates branch at bolt11.c:924. decode returns the invoice as valid, with the first p as payment_hash and the first s as payment_secret, and the second copy listed under extra.
decode should fail each invoice. BOLT 11 Tagged Fields, Requirements, line 215, says a reader "MUST fail the payment unless exactly one p field and exactly one s field are present."
getinfo output
master 1ebc4943ed37: "version": "1ebc494", "network": "regtest"
v26.06.9, a2176589f235: "version": "a217658", "network": "regtest"
- 主要言語
- C
- スター
- 3.1k
- フォーク
- 1k
- 平均マージ
- 4日 11時間
- マージ済み PR(30日)
- 28
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
ElementsProject/lightning のほかの issue
-
common/amount: parse_amount_sat misparses sub-satoshi msat with leading zero and accepts invalid suffixes対応中かも @bhuvan-somisetty が 4 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
ElementsProject/lightning#9674 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
ElementsProject/lightning#9673 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
ElementsProject/lightning#9634 ·
メンテナーはふだん 2 日以内に返信
-
plugins/recover: missing comma in nodes_for_gossip concatenates two bootstrap addresses into invalid address対応中かも @bhuvan-somisetty が 17 日前に担当しました。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
ElementsProject/lightning#9630 ·
メンテナーはふだん 2 日以内に返信
-
common/json_parse: json_to_bitcoin_amount fails to detect overflow and accepts negative/empty inputs対応中かも @bhuvan-somisetty が 21 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
ElementsProject/lightning#9617 ·
メンテナーはふだん 2 日以内に返信
ElementsProject/lightning の issue をすべて見る
似ている issue
-
LSP: Invalid buffer id in previous_result_ids during workspace/diagnostic/refresh対応中かも @sharky720 が今日担当しました。 オープンdiagnostic lsp
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
neovim/neovim#42629 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
epics-base/epics-base#1034 ·
メンテナーはふだん 2 日以内に返信
-
documentation
難易度 1/5 1〜3時間 初心者へのやさしさ 72/100
-
kind: bug preview-effort: small priority: medium
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
メンテナーはふだん 1 日以内に返信