Skip to content
Permalink
Browse files

bech32

  • Loading branch information...
voisine committed Feb 1, 2018
1 parent e0b56b7 commit 2b17fe44619442c31f8a47c175f84b8992933346
Showing with 236 additions and 7 deletions.
  1. +2 −0 BRAddress.h
  2. +141 −0 BRBech32.c
  3. +49 −0 BRBech32.h
  4. +44 −7 test.c
@@ -49,6 +49,8 @@ extern "C" {
#define OP_PUSHDATA1 0x4c
#define OP_PUSHDATA2 0x4d
#define OP_PUSHDATA4 0x4e
#define OP_1NEGATE 0x4f
#define OP_1 0x51
#define OP_DUP 0x76
#define OP_EQUAL 0x87
#define OP_EQUALVERIFY 0x88
@@ -0,0 +1,141 @@
//
// BRBech32.c
// breadwallet-core
//
// Created by Aaron Voisine on 1/20/18.
// Copyright (c) 2018 breadwallet LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "BRBech32.h"
#include "BRAddress.h"
#include "BRCrypto.h"
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

// bech32 address format: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki

static const char bech32chars[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

#define polymod(x) ((((x) & 0x1ffffff) << 5) ^ (-(((x) >> 25) & 1) & 0x3b6a57b2) ^ (-(((x) >> 26) & 1) & 0x26508e6d) ^\
(-(((x) >> 27) & 1) & 0x1ea119fa) ^ (-(((x) >> 28) & 1) & 0x3d4233dd) ^ (-(((x) >> 29) & 1) & 0x2a1462b3))

// returns the number of bytes written to data42 (maximum of 42)
size_t BRBech32Decode(char *hrp84, uint8_t *data42, const char *addr)
{
size_t i, j, bufLen, addrLen = (addr) ? strlen(addr) : 0, sep = addrLen;
uint32_t x, chk = 1;
uint8_t c, ver = 0xff, buf[52], upper = 0, lower = 0;

assert(hrp84 != NULL);
assert(data42 != NULL);
assert(addr != NULL);

for (i = 0; i < addrLen; i++) {
if (addr[i] < 33 || addr[i] > 126) return 0;
if (islower(addr[i])) lower = 1;
if (isupper(addr[i])) upper = 1;
}

while (sep > 0 && addr[sep] != '1') sep--;
if (addrLen < 8 || addrLen > 90 || sep < 1 || sep + 2 + 6 > addrLen || (upper && lower)) return 0;
for (i = 0; i < sep; i++) chk = polymod(chk) ^ (tolower(addr[i]) >> 5);
chk = polymod(chk);
for (i = 0; i < sep; i++) chk = polymod(chk) ^ (addr[i] & 0x1f);
memset(buf, 0, sizeof(buf));

for (i = sep + 1, j = -1; i < addrLen; i++, j++) {
switch (tolower(addr[i])) {
case 'q': c = 0; break; case 'p': c = 1; break; case 'z': c = 2; break; case 'r': c = 3; break;
case 'y': c = 4; break; case '9': c = 5; break; case 'x': c = 6; break; case '8': c = 7; break;
case 'g': c = 8; break; case 'f': c = 9; break; case '2': c = 10; break; case 't': c = 11; break;
case 'v': c = 12; break; case 'd': c = 13; break; case 'w': c = 14; break; case '0': c = 15; break;
case 's': c = 16; break; case '3': c = 17; break; case 'j': c = 18; break; case 'n': c = 19; break;
case '5': c = 20; break; case '4': c = 21; break; case 'k': c = 22; break; case 'h': c = 23; break;
case 'c': c = 24; break; case 'e': c = 25; break; case '6': c = 26; break; case 'm': c = 27; break;
case 'u': c = 28; break; case 'a': c = 29; break; case '7': c = 30; break; case 'l': c = 31; break;
default: return 0; // invalid bech32 digit
}

chk = polymod(chk) ^ c;
if (j == -1) ver = c;
if (j == -1 || i + 6 >= addrLen) continue;
x = (j % 8)*5 - ((j % 8)*5/8)*8;
buf[(j/8)*5 + (j % 8)*5/8] |= (c << 3) >> x;
if (x > 3) buf[(j/8)*5 + (j % 8)*5/8 + 1] |= c << (11 - x);
}

bufLen = (addrLen - (sep + 2 + 6))*5/8;
if (hrp84 == NULL || data42 == NULL || chk != 1 || ver > 16 || bufLen < 2 || bufLen > 40) return 0;
for (i = 0; i < sep; i++) hrp84[i] = tolower(addr[i]);
hrp84[sep] = '\0';
data42[0] = (ver == 0) ? OP_0 : ver + OP_1 - 1;
data42[1] = bufLen;
memcpy(&data42[2], buf, bufLen);
return 2 + bufLen;
}

// data must contain a valid BIP141 witness program
// returns the number of bytes written to addr91 (maximum of 91)
size_t BRBech32Encode(char *addr91, const char *hrp, const uint8_t data[])
{
char addr[91];
uint32_t x, chk = 1;
uint8_t ver, a, b = 0, c = 0;
size_t i, j;

assert(addr91 != NULL);
assert(hrp != NULL);
assert(data != NULL);

for (i = 0; hrp && hrp[i]; i++) {
if (i > 83 || hrp[i] < 33 || hrp[i] > 126 || isupper(hrp[i])) return 0;
chk = polymod(chk) ^ (hrp[i] >> 5);
addr[i] = hrp[i];
}

chk = polymod(chk);
for (j = 0; j < i; j++) chk = polymod(chk) ^ (hrp[j] & 0x1f);
addr[i++] = '1';
if (i < 1 || data == NULL || (data[0] > OP_0 && data[0] < OP_1)) return 0;
ver = (data[0] >= OP_1) ? data[0] + 1 - OP_1 : 0;
if (ver > 16 || data[1] < 2 || data[1] > 40) return 0;
chk = polymod(chk) ^ ver;
addr[i++] = bech32chars[ver];

for (j = 0; j <= data[1]; j++) {
a = b, b = (j < data[1]) ? data[2 + j] : 0;
x = (j % 5)*8 - ((j % 5)*8/5)*5;
c = ((a << (5 - x)) | (b >> (3 + x))) & 0x1f;
if (j < data[1] || j % 5 > 0) chk = polymod(chk) ^ c, addr[i++] = bech32chars[c];
if (x >= 2) c = (b >> (x - 2)) & 0x1f;
if (x >= 2 && j < data[1]) chk = polymod(chk) ^ c, addr[i++] = bech32chars[c];
}

for (j = 0; j < 6; j++) chk = polymod(chk);
chk ^= 1;
for (j = 0; j < 6; ++j) addr[i++] = bech32chars[(chk >> ((5 - j)*5)) & 0x1f];
addr[i++] = '\0';
memcpy(addr91, addr, i);
return i;
}

@@ -0,0 +1,49 @@
//
// BRBech32.h
// breadwallet-core
//
// Created by Aaron Voisine on 1/20/18.
// Copyright (c) 2018 breadwallet LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef BRBech32_h
#define BRBech32_h

#include <stddef.h>
#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif

// bech32 address format: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki

// returns the number of bytes written to data42 (maximum of 42)
size_t BRBech32Decode(char *hrp84, uint8_t *data42, const char *addr);

// data must contain a valid BIP141 witness program
// returns the number of bytes written to addr91 (maximum of 91)
size_t BRBech32Encode(char *addr91, const char *hrp, const uint8_t data[]);

#ifdef __cplusplus
}
#endif

#endif // BRBech32_h
51 test.c
@@ -30,6 +30,7 @@
#include "BRBIP38Key.h"
#include "BRAddress.h"
#include "BRBase58.h"
#include "BRBech32.h"
#include "BRBIP39Mnemonic.h"
#include "BRBIP39WordsEn.h"
#include "BRPeer.h"
@@ -226,12 +227,12 @@ int BRBase58Tests()
uint8_t buf1[BRBase58Decode(NULL, 0, s)];
size_t len1 = BRBase58Decode(buf1, sizeof(buf1), s);

if (len1 != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 1\n", __func__);
if (len1 != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 1\n", __func__);

uint8_t buf2[BRBase58Decode(NULL, 0, "")];
size_t len2 = BRBase58Decode(buf2, sizeof(buf2), "");

if (len2 != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 2\n", __func__);
if (len2 != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 2\n", __func__);

s = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

@@ -240,7 +241,7 @@ int BRBase58Tests()
char str3[BRBase58Encode(NULL, 0, buf3, len3)];

BRBase58Encode(str3, sizeof(str3), buf3, len3);
if (strcmp(str3, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 3\n", __func__);
if (strcmp(str3, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 3\n", __func__);

s = "1111111111111111111111111111111111111111111111111111111111111111111";

@@ -249,7 +250,7 @@ int BRBase58Tests()
char str4[BRBase58Encode(NULL, 0, buf4, len4)];

BRBase58Encode(str4, sizeof(str4), buf4, len4);
if (strcmp(str4, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 4\n", __func__);
if (strcmp(str4, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 4\n", __func__);

s = "111111111111111111111111111111111111111111111111111111111111111111z";

@@ -258,7 +259,7 @@ int BRBase58Tests()
char str5[BRBase58Encode(NULL, 0, buf5, len5)];

BRBase58Encode(str5, sizeof(str5), buf5, len5);
if (strcmp(str5, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 5\n", __func__);
if (strcmp(str5, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 5\n", __func__);

s = "z";

@@ -267,7 +268,7 @@ int BRBase58Tests()
char str6[BRBase58Encode(NULL, 0, buf6, len6)];

BRBase58Encode(str6, sizeof(str6), buf6, len6);
if (strcmp(str6, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: Base58Decode() test 6\n", __func__);
if (strcmp(str6, s) != 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRBase58Decode() test 6\n", __func__);

s = NULL;

@@ -320,6 +321,40 @@ int BRBase58Tests()
return r;
}

int BRBech32Tests()
{
int r = 1;
uint8_t b[52];
char h[84];
char *s, addr[91];
size_t l;

s = "\x00\x14\x75\x1e\x76\xe8\x19\x91\x96\xd4\x54\x94\x1c\x45\xd1\xb3\xa3\x23\xf1\x43\x3b\xd6";
l = BRBech32Decode(h, b, "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4");
if (l != 22 || strcmp(h, "bc") || memcmp(s, b, l))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRBech32Decode() test 1", __func__);

l = BRBech32Decode(h, b, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
if (l != 22 || strcmp(h, "bc") || memcmp(s, b, l))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRBech32Decode() test 2", __func__);

l = BRBech32Encode(addr, "bc", b);
if (l == 0 || strcmp(addr, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRBech32Encode() test 2", __func__);

s = "\x52\x10\x75\x1e\x76\xe8\x19\x91\x96\xd4\x54\x94\x1c\x45\xd1\xb3\xa3\x23";
l = BRBech32Decode(h, b, "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj");
if (l != 18 || strcmp(h, "bc") || memcmp(s, b, l))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRBech32Decode() test 3", __func__);

l = BRBech32Encode(addr, "bc", b);
if (l == 0 || strcmp(addr, "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj"))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRBech32Encode() test 3", __func__);

if (! r) fprintf(stderr, "\n ");
return r;
}

int BRHashTests()
{
// test sha1
@@ -2578,6 +2613,8 @@ int BRRunTests()
printf("%s\n", (BRSetTests()) ? "success" : (fail++, "***FAIL***"));
printf("BRBase58Tests... ");
printf("%s\n", (BRBase58Tests()) ? "success" : (fail++, "***FAIL***"));
printf("BRBech32Tests... ");
printf("%s\n", (BRBech32Tests()) ? "success" : (fail++, "***FAIL***"));
printf("BRHashTests... ");
printf("%s\n", (BRHashTests()) ? "success" : (fail++, "***FAIL***"));
printf("BRMacTests... ");
@@ -2656,7 +2693,7 @@ int main(int argc, const char *argv[])
// BRWalletSetCallbacks(wallet, wallet, walletBalanceChanged, walletTxAdded, walletTxUpdated, walletTxDeleted);
// printf("wallet created with first receive address: %s\n", BRWalletReceiveAddress(wallet).s);
//
// manager = BRPeerManagerNew(&BR_CHAIN_PARAMS, wallet, BIP39_CREATION_TIME, NULL, 0, NULL, 0);
// manager = BRPeerManagerNew(&BRMainNetParams, wallet, BIP39_CREATION_TIME, NULL, 0, NULL, 0);
// BRPeerManagerSetCallbacks(manager, manager, syncStarted, syncStopped, txStatusUpdate, NULL, NULL, NULL, NULL);
//
// BRPeerManagerConnect(manager);

0 comments on commit 2b17fe4

Please sign in to comment.
You can’t perform that action at this time.