Skip to content

Commit

Permalink
Merge pull request #16 from mosadialiou/bech32-android
Browse files Browse the repository at this point in the history
Bech32 support on Litewallet Android
  • Loading branch information
kcw-grunt authored Jun 11, 2020
2 parents 2ea83a2 + 0bb8fa5 commit e8fc2bc
Show file tree
Hide file tree
Showing 19 changed files with 792 additions and 260 deletions.
86 changes: 62 additions & 24 deletions BRAddress.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "BRAddress.h"
#include "BRBase58.h"
#include "BRBech32.h"
#include "BRInt.h"
#include <inttypes.h>
#include <assert.h>
Expand Down Expand Up @@ -241,39 +242,51 @@ size_t BRAddressFromScriptPubKey(char *addr, size_t addrLen, const uint8_t *scri
if (! script || scriptLen == 0 || scriptLen > MAX_SCRIPT_LENGTH) return 0;

uint8_t data[21];
const uint8_t *elems[BRScriptElements(NULL, 0, script, scriptLen)], *d = NULL;
size_t count = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), script, scriptLen), l = 0;

data[0] = BITCOIN_PUBKEY_ADDRESS;
#if LITECOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif

const uint8_t *d, *elems[BRScriptElements(NULL, 0, script, scriptLen)];
char a[91];
size_t r = 0, l = 0, count = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), script, scriptLen);

if (count == 5 && *elems[0] == OP_DUP && *elems[1] == OP_HASH160 && *elems[2] == 20 &&
*elems[3] == OP_EQUALVERIFY && *elems[4] == OP_CHECKSIG) {
// pay-to-pubkey-hash scriptPubKey
d = BRScriptData(elems[2], &l);
if (l != 20) d = NULL;
if (d) memcpy(&data[1], d, 20);
data[0] = BITCOIN_PUBKEY_ADDRESS;
#if LITECOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif
memcpy(&data[1], BRScriptData(elems[2], &l), 20);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 3 && *elems[0] == OP_HASH160 && *elems[1] == 20 && *elems[2] == OP_EQUAL) {
// pay-to-script-hash scriptPubKey
data[0] = BITCOIN_SCRIPT_ADDRESS;
#if LITECOIN_TESTNET
data[0] = BITCOIN_SCRIPT_ADDRESS_TEST;
#endif
d = BRScriptData(elems[1], &l);
if (l != 20) d = NULL;
if (d) memcpy(&data[1], d, 20);
memcpy(&data[1], BRScriptData(elems[1], &l), 20);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 2 && (*elems[0] == 65 || *elems[0] == 33) && *elems[1] == OP_CHECKSIG) {
// pay-to-pubkey scriptPubKey
data[0] = BITCOIN_PUBKEY_ADDRESS;
#if LITECOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif
d = BRScriptData(elems[0], &l);
if (l != 65 && l != 33) d = NULL;
if (d) BRHash160(&data[1], d, l);
BRHash160(&data[1], d, l);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 2 && ((*elems[0] == OP_0 && (*elems[1] == 20 || *elems[1] == 32)) ||
(*elems[0] >= OP_1 && *elems[0] <= OP_16 && *elems[1] >= 2 && *elems[1] <= 40))) {
// pay-to-witness scriptPubKey
r = BRBech32Encode(a, "ltc", script);
#if LITECOIN_TESTNET
r = BRBech32Encode(a, "tltc", script);
#endif
if (addr && r > addrLen) r = 0;
if (addr) memcpy(addr, a, r);
}

return (d) ? BRBase58CheckEncode(addr, addrLen, data, sizeof(data)) : 0;
return r;
}

// writes the bitcoin address for a scriptSig to addr
Expand Down Expand Up @@ -310,23 +323,32 @@ size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script,
else if (count >= 1 && *elems[count - 1] <= OP_PUSHDATA4 && *elems[count - 1] > 0) { // pay-to-pubkey scriptSig
// TODO: implement Peter Wullie's pubKey recovery from signature
}

return (d) ? BRBase58CheckEncode(addr, addrLen, data, sizeof(data)) : 0;
// pay-to-witness scriptSig's are empty

return (d) ? BRBase58CheckEncode(addr, addrLen, data, 21) : 0;
}

// writes the bitcoin address for a witness to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromWitness(char *addr, size_t addrLen, const uint8_t *witness, size_t witLen)
{
return 0; // TODO: XXX implement
}

// writes the scriptPubKey for addr to script
// returns the number of bytes written, or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr)
{
static uint8_t pubkeyAddress = BITCOIN_PUBKEY_ADDRESS, scriptAddress = BITCOIN_SCRIPT_ADDRESS;
uint8_t data[21];
size_t r = 0;
uint8_t data[42], pubkeyAddress = BITCOIN_PUBKEY_ADDRESS, scriptAddress = BITCOIN_SCRIPT_ADDRESS;
char hrp[84], *bech32Prefix = "ltc";
size_t dataLen, r = 0;

assert(addr != NULL);

#if LITECOIN_TESTNET
pubkeyAddress = BITCOIN_PUBKEY_ADDRESS_TEST;
scriptAddress = BITCOIN_SCRIPT_ADDRESS_TEST;
bech32Prefix = "tltc";
#endif

if (BRBase58CheckDecode(data, sizeof(data), addr) == 21) {
Expand All @@ -353,14 +375,23 @@ size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr
r = (! script || 23 <= scriptLen) ? 23 : 0;
}
}
else {
dataLen = BRBech32Decode(hrp, data, addr);

if (dataLen > 2 && strcmp(hrp, bech32Prefix) == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32)) {
if (script && dataLen <= scriptLen) memcpy(script, data, dataLen);
r = (! script || dataLen <= scriptLen) ? dataLen : 0;
}
}

return r;
}

// returns true if addr is a valid bitcoin address
int BRAddressIsValid(const char *addr)
{
uint8_t data[21];
uint8_t data[42];
char hrp[84];
int r = 0;

assert(addr != NULL);
Expand All @@ -372,7 +403,14 @@ int BRAddressIsValid(const char *addr)
r = (data[0] == BITCOIN_PUBKEY_ADDRESS_TEST || data[0] == BITCOIN_SCRIPT_ADDRESS_TEST);
#endif
}

else if (BRBech32Decode(hrp, data, addr) > 2) {
r = (strcmp(hrp, "ltc") == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32));

#if LITECOIN_TESTNET
r = (strcmp(hrp, "tltc") == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32));
#endif
}

return r;
}

Expand Down
12 changes: 10 additions & 2 deletions BRAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ extern "C" {
#define OP_PUSHDATA1 0x4c
#define OP_PUSHDATA2 0x4d
#define OP_PUSHDATA4 0x4e
#define OP_1NEGATE 0x4f
#define OP_1 0x51
#define OP_16 0x60
#define OP_DUP 0x76
#define OP_EQUAL 0x87
#define OP_EQUALVERIFY 0x88
Expand Down Expand Up @@ -77,10 +80,11 @@ const uint8_t *BRScriptData(const uint8_t *elem, size_t *dataLen);
size_t BRScriptPushData(uint8_t *script, size_t scriptLen, const uint8_t *data, size_t dataLen);

typedef struct {
char s[36];
char s[75];
} BRAddress;

#define BR_ADDRESS_NONE ((BRAddress) { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" })
#define BR_ADDRESS_NONE ((BRAddress) { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"\
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" })

// writes the bitcoin address for a scriptPubKey to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
Expand All @@ -90,6 +94,10 @@ size_t BRAddressFromScriptPubKey(char *addr, size_t addrLen, const uint8_t *scri
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script, size_t scriptLen);

// writes the bitcoin address for a witness to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromWitness(char *addr, size_t addrLen, const uint8_t *witness, size_t witLen);

// writes the scriptPubKey for addr to script
// returns the number of bytes written, or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr);
Expand Down
6 changes: 3 additions & 3 deletions BRBIP38Key.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ int BRKeySetBIP38Key(BRKey *key, const char *bip38Key, const char *passphrase)

BRScrypt(&derived, sizeof(derived), passphrase, pwLen, addresshash, sizeof(uint32_t),
BIP38_SCRYPT_N, BIP38_SCRYPT_R, BIP38_SCRYPT_P);
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u64[4];
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u8[sizeof(UInt256)];
var_clean(&derived);

_BRAES256ECBDecrypt(&derived2, &encrypted1);
Expand All @@ -280,7 +280,7 @@ int BRKeySetBIP38Key(BRKey *key, const char *bip38Key, const char *passphrase)
BRSecp256k1PointGen(&passpoint, &passfactor); // passpoint = G*passfactor
derived = _BRBIP38DeriveKey(passpoint, addresshash, entropy);
var_clean(&passpoint);
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u64[4];
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u8[sizeof(UInt256)];
var_clean(&derived);
memcpy(&encrypted1, &data[15], sizeof(uint64_t));

Expand Down Expand Up @@ -365,7 +365,7 @@ size_t BRKeyBIP38Key(BRKey *key, char *bip38Key, size_t bip38KeyLen, const char

BRScrypt(&derived, sizeof(derived), passphrase, strlen(passphrase), &salt, sizeof(salt),
BIP38_SCRYPT_N, BIP38_SCRYPT_R, BIP38_SCRYPT_P);
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u64[4];
derived1 = *(UInt256 *)&derived, derived2 = *(UInt256 *)&derived.u8[sizeof(UInt256)];
var_clean(&derived);

// enctryped1 = AES256Encrypt(privkey[0...15] xor derived1[0...15], derived2)
Expand Down
9 changes: 4 additions & 5 deletions BRBase58.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@

// base58 and base58check encoding: https://en.bitcoin.it/wiki/Base58Check_encoding

static const char base58chars[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

// returns the number of characters written to str including NULL terminator, or total strLen needed if str is NULL
size_t BRBase58Encode(char *str, size_t strLen, const uint8_t *data, size_t dataLen)
{
static const char chars[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
size_t i, j, len, zcount = 0;

assert(data != NULL);
Expand All @@ -63,8 +62,8 @@ size_t BRBase58Encode(char *str, size_t strLen, const uint8_t *data, size_t data
len = (zcount + sizeof(buf) - i) + 1;

if (str && len <= strLen) {
while (zcount-- > 0) *(str++) = base58chars[0];
while (i < sizeof(buf)) *(str++) = base58chars[buf[i++]];
while (zcount-- > 0) *(str++) = chars[0];
while (i < sizeof(buf)) *(str++) = chars[buf[i++]];
*str = '\0';
}

Expand All @@ -78,7 +77,7 @@ size_t BRBase58Decode(uint8_t *data, size_t dataLen, const char *str)
size_t i = 0, j, len, zcount = 0;

assert(str != NULL);
while (str && *str == base58chars[0]) str++, zcount++; // count leading zeroes
while (str && *str == '1') str++, zcount++; // count leading zeroes

uint8_t buf[(str) ? strlen(str)*733/1000 + 1 : 0]; // log(58)/log(256), rounded up

Expand Down
144 changes: 144 additions & 0 deletions BRBech32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//
// BRBech32.c
//
// 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

#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, sep;
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; addr && addr[i]; i++) {
if (addr[i] < 33 || addr[i] > 126) return 0;
if (islower(addr[i])) lower = 1;
if (isupper(addr[i])) upper = 1;
}

addrLen = sep = i;
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;
assert(sep < 84);
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;
assert(bufLen <= 40);
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[])
{
static const char chars[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
char addr[91];
uint32_t x, chk = 1;
uint8_t ver, a, b = 0, c = 0;
size_t i, j, len;

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;
len = data[1];
if (ver > 16 || len < 2 || len > 40 || i + 1 + len + 6 >= 91) return 0;
chk = polymod(chk) ^ ver;
addr[i++] = chars[ver];

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

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

Loading

0 comments on commit e8fc2bc

Please sign in to comment.