diff --git a/crypto/segwit_addr.c b/crypto/segwit_addr.c index ef928c81b..bd8c775ae 100644 --- a/crypto/segwit_addr.c +++ b/crypto/segwit_addr.c @@ -1,4 +1,4 @@ -/* 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 @@ -24,7 +24,7 @@ #include "segwit_addr.h" -uint32_t bech32_polymod_step(uint32_t pre) { +static uint32_t bech32_polymod_step(uint32_t pre) { uint8_t b = pre >> 25; return ((pre & 0x1FFFFFF) << 5) ^ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ @@ -34,6 +34,12 @@ uint32_t bech32_polymod_step(uint32_t pre) { (-((b >> 4) & 1) & 0x2a1462b3UL); } +static uint32_t bech32_final_constant(bech32_encoding enc) { + if (enc == BECH32_ENCODING_BECH32) return 1; + if (enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3; + return 0; +} + static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; static const int8_t charset_rev[128] = { @@ -47,7 +53,7 @@ static const int8_t 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) { +int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, bech32_encoding enc) { uint32_t chk = 1; size_t i = 0; while (hrp[i] != 0) { @@ -75,7 +81,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++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; } @@ -83,14 +89,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) { +bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input) { uint32_t chk = 1; size_t i = 0; size_t input_len = strlen(input); size_t hrp_len = 0; int have_lower = 0, have_upper = 0; if (input_len < 8 || input_len > 90) { - return 0; + return BECH32_ENCODING_NONE; } *data_len = 0; while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { @@ -98,13 +104,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; @@ -126,7 +132,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) { @@ -135,9 +141,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; } static int convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) { @@ -165,23 +177,28 @@ static int 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] = {0}; 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; convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); ++datalen; - return bech32_encode(output, hrp, data, datalen); + return bech32_encode(output, hrp, data, datalen, enc); } int segwit_addr_decode(int* witver, uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) { uint8_t data[84] = {0}; char hrp_actual[84] = {0}; size_t data_len = 0; - if (!bech32_decode(hrp_actual, data, &data_len, addr)) return 0; + bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr); + 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 (!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/crypto/segwit_addr.h b/crypto/segwit_addr.h index c842fca2a..511970ab8 100644 --- a/crypto/segwit_addr.h +++ b/crypto/segwit_addr.h @@ -1,4 +1,4 @@ -/* 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 @@ -24,6 +24,7 @@ #include + /** Encode a SegWit address * * Out: output: Pointer to a buffer of size 73 + strlen(hrp) that will be @@ -35,8 +36,13 @@ * prog_len: Number of data bytes in prog. * Returns 1 if successful. */ -int segwit_addr_encode(char *output, const char *hrp, int ver, - const uint8_t *prog, size_t prog_len); +int segwit_addr_encode( + char *output, + const char *hrp, + int ver, + const uint8_t *prog, + size_t prog_len +); /** Decode a SegWit address * @@ -44,27 +50,47 @@ int segwit_addr_encode(char *output, const char *hrp, int ver, * program version (between 0 and 16 inclusive). * prog: Pointer to a buffer of size 40 that will be updated to * contain the witness program bytes. - * prog_len: Pointer to a size_t that will be updated to contain the - * length of bytes in prog. hrp: Pointer to the null-terminated human - * readable part that is expected (chain/network specific). addr: Pointer to - * the null-terminated address. Returns 1 if successful. + * prog_len: Pointer to a size_t that will be updated to contain the length + * of bytes in prog. + * hrp: Pointer to the null-terminated human readable part that is + * expected (chain/network specific). + * addr: Pointer to the null-terminated address. + * Returns 1 if successful. */ -int segwit_addr_decode(int *ver, uint8_t *prog, size_t *prog_len, - const char *hrp, const char *addr); +int segwit_addr_decode( + int* ver, + uint8_t* prog, + size_t* prog_len, + const char* hrp, + const char* addr +); + +/** Supported encodings. */ +typedef enum { + BECH32_ENCODING_NONE, + BECH32_ENCODING_BECH32, + BECH32_ENCODING_BECH32M +} bech32_encoding; -/** Encode a Bech32 string +/** 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. * In: hrp : Pointer to the null-terminated human readable part. * data : Pointer to an array of 5-bit values. * data_len: Length of the data array. + * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). * Returns 1 if successful. */ -int bech32_encode(char *output, const char *hrp, const uint8_t *data, - size_t data_len); +int bech32_encode( + char *output, + const char *hrp, + const uint8_t *data, + size_t data_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. @@ -73,9 +99,15 @@ int bech32_encode(char *output, const char *hrp, const uint8_t *data, * data_len: Pointer to a size_t that will be updated to be the number * of entries in data. * In: input: Pointer to a null-terminated Bech32 string. - * Returns 1 if succesful. + * 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(char *hrp, uint8_t *data, size_t *data_len, - const char *input); +bech32_encoding bech32_decode( + char *hrp, + uint8_t *data, + size_t *data_len, + const char *input +); #endif diff --git a/crypto/tests/test_check_segwit.h b/crypto/tests/test_check_segwit.h index ac71ee086..3a599355a 100644 --- a/crypto/tests/test_check_segwit.h +++ b/crypto/tests/test_check_segwit.h @@ -106,7 +106,7 @@ START_TEST(test_segwit) { size_t data_len; int res = bech32_decode(hrp, data, &data_len, valid_checksum[i]); ck_assert_int_eq(res, 1); - res = bech32_encode(rebuild, hrp, data, data_len); + res = bech32_encode(rebuild, hrp, data, data_len, BECH32_ENCODING_BECH32); ck_assert_int_eq(res, 1); ck_assert_int_eq(my_strncasecmp(rebuild, valid_checksum[i], 92), 0); } diff --git a/legacy/firmware/.changelog.d/1656.added b/legacy/firmware/.changelog.d/1656.added new file mode 100644 index 000000000..08d61672e --- /dev/null +++ b/legacy/firmware/.changelog.d/1656.added @@ -0,0 +1 @@ +Support sending to Taproot addresses.