From 4cc3eaf79c4548cd6a016a4549a9c7be34d99d09 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 9 Jun 2021 09:25:46 +0930 Subject: [PATCH] common/bech32: update to bech32m reference. I did this by copying the updated bech32 code, and then re-patching in our minor changes: 1. Headers modded (we need size_t) 2. Explicit length for bech32_encode/decode (not 90). 3. Exposing and bech32_ prefix for convert_bits, charset, charset_rev. Signed-off-by: Rusty Russell --- common/bech32.c | 45 +++++++++++++++++++++++++++------------- common/bech32.h | 24 +++++++++++++++------ common/bolt11.c | 6 ++++-- connectd/connectd.c | 3 ++- tests/fuzz/fuzz-bech32.c | 6 ++++-- tools/hsmtool.c | 2 +- 6 files changed, 60 insertions(+), 26 deletions(-) diff --git a/common/bech32.c b/common/bech32.c index cc62abdc0..de8b05302 100644 --- a/common/bech32.c +++ b/common/bech32.c @@ -1,7 +1,7 @@ /* Stolen from https://github.com/sipa/bech32/blob/master/ref/c/segwit_addr.c, * with only the two ' > 90' checks hoisted, and more internals exposed */ -/* Copyright (c) 2017 Pieter Wuille +/* Copyright (c) 2017, 2021 Pieter Wuille * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ */ #include "bech32.h" +#include #include static uint32_t bech32_polymod_step(uint32_t pre) { @@ -35,7 +36,13 @@ static uint32_t bech32_polymod_step(uint32_t pre) { (-((b >> 4) & 1) & 0x2a1462b3UL); } -const char bech32_charset[32] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; +static uint32_t bech32_final_constant(bech32_encoding enc) { + if (enc == BECH32_ENCODING_BECH32) return 1; + if (enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3; + assert(0); +} + +const char bech32_charset[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; const int8_t bech32_charset_rev[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -48,7 +55,7 @@ const int8_t bech32_charset_rev[128] = { 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 }; -int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, size_t max_input_len) { +int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, size_t max_input_len, bech32_encoding enc) { uint32_t chk = 1; size_t i = 0; while (hrp[i] != 0) { @@ -76,7 +83,7 @@ int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t dat for (i = 0; i < 6; ++i) { chk = bech32_polymod_step(chk); } - chk ^= 1; + chk ^= bech32_final_constant(enc); for (i = 0; i < 6; ++i) { *(output++) = bech32_charset[(chk >> ((5 - i) * 5)) & 0x1f]; } @@ -84,15 +91,14 @@ int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t dat return 1; } -int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, - size_t max_input_len) { +bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t max_input_len) { uint32_t chk = 1; size_t i; size_t input_len = strlen(input); size_t hrp_len; int have_lower = 0, have_upper = 0; if (input_len < 8 || input_len > max_input_len) { - return 0; + return BECH32_ENCODING_NONE; } *data_len = 0; while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { @@ -100,13 +106,13 @@ int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, } hrp_len = input_len - (1 + *data_len); if (1 + *data_len >= input_len || *data_len < 6) { - return 0; + return BECH32_ENCODING_NONE; } *(data_len) -= 6; for (i = 0; i < hrp_len; ++i) { int ch = input[i]; if (ch < 33 || ch > 126) { - return 0; + return BECH32_ENCODING_NONE; } if (ch >= 'a' && ch <= 'z') { have_lower = 1; @@ -128,7 +134,7 @@ int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, if (input[i] >= 'a' && input[i] <= 'z') have_lower = 1; if (input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; if (v == -1) { - return 0; + return BECH32_ENCODING_NONE; } chk = bech32_polymod_step(chk) ^ v; if (i + 6 < input_len) { @@ -137,9 +143,15 @@ int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, ++i; } if (have_lower && have_upper) { - return 0; + return BECH32_ENCODING_NONE; + } + if (chk == bech32_final_constant(BECH32_ENCODING_BECH32)) { + return BECH32_ENCODING_BECH32; + } else if (chk == bech32_final_constant(BECH32_ENCODING_BECH32M)) { + return BECH32_ENCODING_BECH32M; + } else { + return BECH32_ENCODING_NONE; } - return chk == 1; } int bech32_convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) { @@ -167,23 +179,28 @@ int bech32_convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t int segwit_addr_encode(char *output, const char *hrp, int witver, const uint8_t *witprog, size_t witprog_len) { uint8_t data[65]; size_t datalen = 0; + bech32_encoding enc = BECH32_ENCODING_BECH32; if (witver > 16) return 0; if (witver == 0 && witprog_len != 20 && witprog_len != 32) return 0; if (witprog_len < 2 || witprog_len > 40) return 0; + if (witver > 0) enc = BECH32_ENCODING_BECH32M; data[0] = witver; bech32_convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); ++datalen; - return bech32_encode(output, hrp, data, datalen, 90); + return bech32_encode(output, hrp, data, datalen, 90, enc); } int segwit_addr_decode(int* witver, uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) { uint8_t data[84]; char hrp_actual[84]; size_t data_len; - if (!bech32_decode(hrp_actual, data, &data_len, addr, 90)) return 0; + bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr, 90); + if (enc == BECH32_ENCODING_NONE) return 0; if (data_len == 0 || data_len > 65) return 0; if (strncmp(hrp, hrp_actual, 84) != 0) return 0; if (data[0] > 16) return 0; + if (data[0] == 0 && enc != BECH32_ENCODING_BECH32) return 0; + if (data[0] > 0 && enc != BECH32_ENCODING_BECH32M) return 0; *witdata_len = 0; if (!bech32_convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) return 0; if (*witdata_len < 2 || *witdata_len > 40) return 0; diff --git a/common/bech32.h b/common/bech32.h index 16fb69dcd..614ad321e 100644 --- a/common/bech32.h +++ b/common/bech32.h @@ -1,7 +1,7 @@ /* Stolen from https://github.com/sipa/bech32/blob/master/ref/c/segwit_addr.h, * with only the two ' > 90' checks hoisted */ -/* Copyright (c) 2017 Pieter Wuille +/* Copyright (c) 2017, 2021 Pieter Wuille * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -69,7 +69,14 @@ int segwit_addr_decode( const char* addr ); -/** Encode a Bech32 string +/** Supported encodings. */ +typedef enum { + BECH32_ENCODING_NONE, + BECH32_ENCODING_BECH32, + BECH32_ENCODING_BECH32M +} bech32_encoding; + +/** Encode a Bech32 or Bech32m string * * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that * will be updated to contain the null-terminated Bech32 string. @@ -77,6 +84,7 @@ int segwit_addr_decode( * data : Pointer to an array of 5-bit values. * data_len: Length of the data array. * max_input_len: Maximum valid length of input (90 for segwit usage). + * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). * Returns 1 if successful. */ int bech32_encode( @@ -84,10 +92,11 @@ int bech32_encode( const char *hrp, const uint8_t *data, size_t data_len, - size_t max_input_len + size_t max_input_len, + bech32_encoding enc ); -/** Decode a Bech32 string +/** Decode a Bech32 or Bech32m string * * Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be * updated to contain the null-terminated human readable part. @@ -97,9 +106,11 @@ int bech32_encode( * of entries in data. * In: input: Pointer to a null-terminated Bech32 string. * max_input_len: Maximum valid length of input (90 for segwit usage). - * Returns 1 if successful. + * Returns BECH32_ENCODING_BECH32{,M} to indicate decoding was successful + * with the specified encoding standard. BECH32_ENCODING_NONE is returned if + * decoding failed. */ -int bech32_decode( +bech32_encoding bech32_decode( char *hrp, uint8_t *data, size_t *data_len, @@ -120,3 +131,4 @@ extern const char bech32_charset[32]; extern const int8_t bech32_charset_rev[128]; #endif /* LIGHTNING_COMMON_BECH32_H */ + diff --git a/common/bolt11.c b/common/bolt11.c index 36a1feacd..65769f24f 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -584,7 +584,8 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, hrp = tal_arr(tmpctx, char, strlen(str) - 6); data = tal_arr(tmpctx, u5, strlen(str) - 8); - if (!bech32_decode(hrp, data, &data_len, str, (size_t)-1)) + if (bech32_decode(hrp, data, &data_len, str, (size_t)-1) + != BECH32_ENCODING_BECH32) return decode_fail(b11, fail, "Bad bech32 string"); /* For signature checking at the end. */ @@ -1179,7 +1180,8 @@ char *bolt11_encode_(const tal_t *ctx, bech32_push_bits(&data, sig_and_recid, sizeof(sig_and_recid) * CHAR_BIT); output = tal_arr(ctx, char, strlen(hrp) + tal_count(data) + 8); - if (!bech32_encode(output, hrp, data, tal_count(data), (size_t)-1)) + if (!bech32_encode(output, hrp, data, tal_count(data), (size_t)-1, + BECH32_ENCODING_BECH32)) output = tal_free(output); return output; diff --git a/connectd/connectd.c b/connectd/connectd.c index 3aba28ddd..f9ccdafab 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1412,7 +1412,8 @@ static const char **seednames(const tal_t *ctx, const struct node_id *id) const char **seednames = tal_arr(ctx, const char *, 0); bech32_push_bits(&data, id->k, ARRAY_SIZE(id->k)*8); - bech32_encode(bech32, "ln", data, tal_count(data), sizeof(bech32)); + bech32_encode(bech32, "ln", data, tal_count(data), sizeof(bech32), + BECH32_ENCODING_BECH32); /* This is cdecker's seed */ tal_arr_expand(&seednames, tal_fmt(seednames, "%s.lseed.bitcoinstats.com", bech32)); /* This is darosior's seed */ diff --git a/tests/fuzz/fuzz-bech32.c b/tests/fuzz/fuzz-bech32.c index e9a8b6c0f..b3ab46f44 100644 --- a/tests/fuzz/fuzz-bech32.c +++ b/tests/fuzz/fuzz-bech32.c @@ -15,15 +15,17 @@ void run(const uint8_t *data, size_t size) uint8_t *data_out; size_t data_out_len; int wit_version; + bech32_encoding benc; /* Buffer size is defined in each function's doc comment. */ bech32_str = malloc(size + strlen(hrp_inv) + 8); + benc = data[0] ? BECH32_ENCODING_BECH32 : BECH32_ENCODING_BECH32M; /* FIXME: needs a dictionary / a startup seed corpus to pass this more * frequently. */ - if (bech32_encode(bech32_str, hrp_inv, data, size, size) == 1) { + if (bech32_encode(bech32_str, hrp_inv, data+1, size-1, size-1, benc) == 1) { hrp_out = malloc(strlen(bech32_str) - 6); data_out = malloc(strlen(bech32_str) - 8); - bech32_decode(hrp_out, data_out, &data_out_len, bech32_str, size); + assert(bech32_decode(hrp_out, data_out, &data_out_len, bech32_str, size) == benc); free(hrp_out); free(data_out); } diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 3bc076366..a202fdfb9 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -363,7 +363,7 @@ static int guess_to_remote(const char *address, struct node_id *node_id, size_t witlen; /* Get the hrp to accept addresses from any network. */ - if (bech32_decode(hrp, goal_pubkeyhash, &witlen, address, 90) != 1) + if (bech32_decode(hrp, goal_pubkeyhash, &witlen, address, 90) != BECH32_ENCODING_BECH32) errx(ERROR_USAGE, "Could not get address' network"); if (segwit_addr_decode(&witver, goal_pubkeyhash, &witlen, hrp, address) != 1) errx(ERROR_USAGE, "Wrong bech32 address");