diff --git a/common/bech32_util.c b/common/bech32_util.c index 359bf9b86..26168b814 100644 --- a/common/bech32_util.c +++ b/common/bech32_util.c @@ -1,5 +1,6 @@ #include "bech32_util.h" -#include +#include +#include static u8 get_bit(const u8 *src, size_t bitoff) { @@ -23,3 +24,99 @@ void bech32_push_bits(u5 **data, const void *src, size_t nbits) data_len++; } } + +static u8 get_u5_bit(const u5 *src, size_t bitoff) +{ + return ((src[bitoff / 5] >> (4 - (bitoff % 5))) & 1); +} + +void bech32_pull_bits(u8 **data, const u5 *src, size_t nbits) +{ + size_t i; + size_t data_len = tal_count(*data); + + /* We discard trailing bits. */ + for (i = 0; i + 8 <= nbits; i += 8) { + tal_resize(data, data_len+1); + (*data)[data_len] = 0; + for (size_t b = 0; b < 8; b++) { + (*data)[data_len] <<= 1; + (*data)[data_len] |= get_u5_bit(src, i+b); + } + data_len++; + } +} + +/* Returns a char, tracks case. */ +static int fixup_char(int c, bool *upper, bool *lower) +{ + if (c >= 'A' && c <= 'Z') { + *upper = true; + return c + ('a' - 'A'); + } else if (c >= 'a' && c <= 'z') { + *lower = true; + } + return c; +} + +bool from_bech32_charset(const tal_t *ctx, + const char *bech32, + size_t bech32_len, + char **hrp, u8 **data) +{ + u5 *u5data; + const char *sep; + bool upper = false, lower = false; + size_t datalen; + + sep = memchr(bech32, '1', bech32_len); + if (!sep) + return false; + + *hrp = tal_strndup(ctx, bech32, sep - bech32); + for (size_t i = 0; i < strlen(*hrp); i++) + (*hrp)[i] = fixup_char((*hrp)[i], &upper, &lower); + + datalen = bech32_len - (sep + 1 - bech32); + u5data = tal_arr(NULL, u5, datalen); + for (size_t i = 0; i < datalen; i++) { + int c = sep[1+i]; + if (c < 0 || c > 128) + goto fail; + c = fixup_char(c, &upper, &lower); + if (bech32_charset_rev[c] == -1) + goto fail; + u5data[i] = bech32_charset_rev[c]; + } + + /* Check case consistency */ + if (upper && lower) + goto fail; + + *data = tal_arr(ctx, u8, 0); + bech32_pull_bits(data, u5data, tal_bytelen(u5data) * 5); + tal_free(u5data); + return true; + +fail: + *hrp = tal_free(*hrp); + tal_free(u5data); + return false; +} + +char *to_bech32_charset(const tal_t *ctx, + const char *hrp, const u8 *data) +{ + u5 *u5data = tal_arr(NULL, u5, 0); + char *ret; + + bech32_push_bits(&u5data, data, tal_bytelen(data) * 8); + ret = tal_dup_arr(ctx, char, hrp, strlen(hrp), + 1 + tal_bytelen(u5data) + 1); + ret[strlen(hrp)] = '1'; + for (size_t i = 0; i < tal_bytelen(u5data); i++) + ret[strlen(hrp) + 1 + i] = bech32_charset[u5data[i]]; + ret[strlen(hrp) + 1 + tal_bytelen(u5data)] = '\0'; + tal_free(u5data); + return ret; +} diff --git a/common/bech32_util.h b/common/bech32_util.h index 803d2f312..9299c0bb6 100644 --- a/common/bech32_util.h +++ b/common/bech32_util.h @@ -3,11 +3,28 @@ #include "config.h" #include +#include #include +#include /** * Push the bytes in src in 5 bit format onto the end of data. */ void bech32_push_bits(u5 **data, const void *src, size_t nbits); +/** + * Push the bytes in src in 8 bit format onto the end of data. + */ +void bech32_pull_bits(u8 **data, const u5 *src, size_t nbits); + +/** + * Checksumless bech32 routines. + */ +bool from_bech32_charset(const tal_t *ctx, + const char *bech32, size_t bech32_len, + char **hrp, u8 **data); + +char *to_bech32_charset(const tal_t *ctx, + const char *hrp, const u8 *data); + #endif /* LIGHTNING_COMMON_BECH32_UTIL_H */