Repository navigation
Description
Issue and Steps to Reproduce
bolt11_decode_nosig multiplies the amount by its multiplier in an unchecked u64, so the product wraps for any amount of 2^64 pico-bitcoin or more, about 18,446,744.07 BTC. This happens on master at 1ebc494 and in v26.06.9.
This test goes in common/test/run-bolt11-amount.c. It signs two invoices with the BOLT 11 example key, changes only the amount between them, and decodes each.
#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, sign them with the example key, and change
* only the amount. msat is NULL to leave the amount out of struct bolt11 and
* carry it in the prefix instead, for an amount too large to fit a u64 msat.
*/
static struct bolt11 *encode_then_decode(const struct chainparams *chain,
const struct amount_msat *msat,
const char **enc, const char **fail)
{
struct bolt11 *b11 = new_bolt11(tmpctx, msat);
b11->chain = chain;
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);
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[])
{
/* A mutable copy of the bitcoin parameters, so the amount can go in the
* prefix for the case that does not fit a u64 msat. */
struct chainparams big = *chainparams_for_network("bitcoin");
struct amount_msat fits_msat;
struct bolt11 *toobig, *fits;
const char *toobig_str, *fits_str, *toobig_fail, *fits_fail;
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();
/* Amount 184467441 BTC is 18446744100000000000 msat, which does not fit
* in a u64, so a reader MUST fail it. BOLT #11: a reader, "if the
* `multiplier` is present: MUST multiply `amount` by the `multiplier`
* value to derive the amount required for payment." */
big.lightning_hrp = "bc184467441";
toobig = encode_then_decode(&big, NULL, &toobig_str, &toobig_fail);
assert(strstarts(toobig_str, "lnbc1844674411"));
if (toobig)
warnx("%s decodes with %s", toobig_str,
fmt_amount_msat(tmpctx, *toobig->msat));
else
warnx("%s rejected: %s", toobig_str, toobig_fail);
/* Amount 21000000 BTC is 2100000000000000000 msat, which fits in a u64,
* so a reader MUST decode it with that amount. */
fits_msat = amount_msat(21000000ULL * MSAT_PER_BTC);
fits = encode_then_decode(chainparams_for_network("bitcoin"), &fits_msat,
&fits_str, &fits_fail);
assert(strstarts(fits_str, "lnbc210000001"));
if (fits)
warnx("%s decodes with %s", fits_str,
fmt_amount_msat(tmpctx, *fits->msat));
else
warnx("%s rejected: %s", fits_str, fits_fail);
assert(!toobig);
assert(fits && amount_msat_eq(*fits->msat, fits_msat));
common_shutdown();
}Build and run it with make unittest/common/test/run-bolt11-amount. The output on master 1ebc494 is:
run-bolt11-amount: lnbc1844674411pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgqn8teqkgvquuadwwv98jhqwstzxxrwqazmmqzx0pq80keejv4mv39shhlf06h3ggwk7kwr0527dyw257jzpw25rl8fk074k6204q8e0spyysvdc decodes with 26290448384msat
run-bolt11-amount: lnbc210000001pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgq9mzumzkvfvf548wuypxkkkw0eaezff0ckq24karyxpwaxv8ya4wznh7v0ual5fqtcrc6dw43jjeuw3aul8l2l00jslleql0zqvkemjcqwjrj3j rejected: Invalid sub-millisatoshi amount '21000000p'
run-bolt11-amount: common/test/run-bolt11-amount.c:129: main: Assertion `!toobig' failed.
make: *** [Makefile:942: unittest/common/test/run-bolt11-amount] Error 134
The product is amount * m10 at bolt11.c:823 and bolt11.c:865-870, and the sub-millisatoshi check runs on the wrapped value, which most wrapped values fail. When the product wraps around 2^64 a multiple of five times, first at 2^63 msat, the wrapped value passes and decode returns it. Then xpay.c:2847 takes it as the amount to pay. The error message also puts a literal p after the amount whatever the multiplier.
decode should fail the first invoice, which asks for 18,446,744,100,000,000,000 msat and does not fit in a u64. The second asks for 2,100,000,000,000,000,000 msat, which fits, and it should decode with that amount.
getinfo output
master 1ebc4943ed37: "version": "1ebc494", "network": "regtest"
v26.06.9, a2176589f235: "version": "a217658", "network": "regtest"
Activity