diff --git a/CMakeLists.txt b/CMakeLists.txt index da714d93b..007f1a6c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -set(SOURCES bignum.c ecdsa.c secp256k1.c sha2.c rand.c hmac.c bip32.c ripemd160.c bip39.c pbkdf2.c) +set(SOURCES bignum.c ecdsa.c secp256k1.c sha2.c rand.c hmac.c bip32.c ripemd160.c bip39.c pbkdf2.c base58.c) if(MSVC) set_source_files_properties(${SOURCES} PROPERTIES LANGUAGE CXX) endif(MSVC) diff --git a/Makefile b/Makefile index e96dee000..d2f9e61dd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc CFLAGS = -Wall -Wextra -Os -OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o bip39.o pbkdf2.o +OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o bip39.o pbkdf2.o base58.o OBJS += aescrypt.o aeskey.o aestab.o all: tests test-openssl diff --git a/base58.c b/base58.c new file mode 100644 index 000000000..aaefa3548 --- /dev/null +++ b/base58.c @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * 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 +#include "base58.h" +#include "sha2.h" + +int base58_encode_check(const uint8_t *data, int len, char *str) +{ + int outlen; + switch (len) { + case 78: // xpub/xprv 78 + outlen = 111; + break; + case 34: // WIF privkey 1+32 + outlen = 51; + break; + case 21: // address 1+20 + outlen = 34; + break; + default: + return 0; + } + uint8_t mydata[82], hash[32]; + sha256_Raw(data, len, hash); + sha256_Raw(hash, 32, hash); + memcpy(mydata, data, len); + memcpy(mydata + len, hash, 4); // checksum + uint32_t rem, tmp; + const char code[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + int i, j; + for (j = 0; j < outlen; j++) { + rem = mydata[0] % 58; + mydata[0] /= 58; + for (i = 1; i < len + 4; i++) { + tmp = rem * 24 + mydata[i]; // 2^8 == 4*58 + 24 + mydata[i] = rem * 4 + (tmp / 58); + rem = tmp % 58; + } + str[outlen - 1 - j] = code[rem]; + } + str[outlen] = 0; + return outlen; +} + +int base58_decode_check(const char *str, uint8_t *data) +{ + const char decode[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, + 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, + 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57 + }; + uint8_t mydata[82], hash[32]; + int j, k, outlen; + switch (strlen(str)) { + case 111: // xpub/xprv + outlen = 78; + break; + case 51: // WIF privkey + outlen = 34; + break; + case 27: // address + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + outlen = 21; + break; + default: + return 0; + } + memset(mydata, 0, sizeof(mydata)); + while (*str) { + if (*str < 0 || *str >= (int)sizeof(decode)) { // invalid character + return 0; + } + k = decode[(int)*str]; + if (k == -1) { // invalid character + return 0; + } + for (j = outlen + 4 - 1; j >= 0; j--) { + k += mydata[j] * 58; + mydata[j] = k & 0xFF; + k >>= 8; + } + str++; + } + sha256_Raw(mydata, outlen, hash); + sha256_Raw(hash, 32, hash); + if (memcmp(mydata + outlen, hash, 4)) { // wrong checksum + return 0; + } + memcpy(data, mydata, outlen); + return outlen; +} diff --git a/base58.h b/base58.h new file mode 100644 index 000000000..491f248bf --- /dev/null +++ b/base58.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * 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 __BASE58_H__ +#define __BASE58_H__ + +#include + +int base58_encode_check(const uint8_t *data, int len, char *str); +int base58_decode_check(const char *str, uint8_t *data); + +#endif diff --git a/bip32.c b/bip32.c index 1d3ce8d19..d9b25eee3 100644 --- a/bip32.c +++ b/bip32.c @@ -29,6 +29,7 @@ #include "bip32.h" #include "sha2.h" #include "ripemd160.h" +#include "base58.h" void hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, uint8_t *chain_code, uint8_t *public_key, HDNode *out) { @@ -145,10 +146,7 @@ void hdnode_fill_public_key(HDNode *node) void hdnode_serialize(const HDNode *node, uint32_t version, char use_public, char *str) { - uint8_t node_data[82], a[32]; - int i,j; - uint32_t rem, tmp; - const char code[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + uint8_t node_data[78]; write_be(node_data, version); node_data[4] = node->depth; write_be(node_data + 5, node->fingerprint); @@ -160,20 +158,7 @@ void hdnode_serialize(const HDNode *node, uint32_t version, char use_public, cha node_data[45] = 0; memcpy(node_data + 46, node->private_key, 32); } - sha256_Raw(node_data, 78, a); - sha256_Raw(a, 32, a); - memcpy(node_data + 78, a, 4); // checksum - for (j = 110; j >= 0; j--) { - rem = node_data[0] % 58; - node_data[0] /= 58; - for (i = 1; i < 82; i++) { - tmp = rem * 24 + node_data[i]; // 2^8 == 4*58 + 24 - node_data[i] = rem * 4 + (tmp / 58); - rem = tmp % 58; - } - str[j] = code[rem]; - } - str[111] = 0; + base58_encode_check(node_data, 78, str); } void hdnode_serialize_public(const HDNode *node, char *str) @@ -189,55 +174,22 @@ void hdnode_serialize_private(const HDNode *node, char *str) // check for validity of curve point in case of public data not performed int hdnode_deserialize(const char *str, HDNode *node) { - uint8_t node_data[82], a[32]; - const char decode[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, - 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, - 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, - -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57 - }; + uint8_t node_data[78]; memset(node, 0, sizeof(HDNode)); - memset(node_data, 0, sizeof(node_data)); - if (strlen(str) != 111) { // invalid data length + if (!base58_decode_check(str, node_data)) { return -1; } - int i, j, k; - for (i = 0; i < 111; i++) { - if (str[i] < 0 || str[i] >= (int)sizeof(decode)) { // invalid character - return -2; - } - k = decode[(int)str[i]]; - if (k == -1) { // invalid character - return -2; - } - for (j = 81; j >= 0; j--) { - k += node_data[j] * 58; - node_data[j] = k & 0xFF; - k >>= 8; - } - } - sha256_Raw(node_data, 78, a); - sha256_Raw(a, 32, a); - if (memcmp(node_data + 78, a, 4)) { // wrong checksum - return -3; - } uint32_t version = read_be(node_data); if (version == 0x0488B21E) { // public node memcpy(node->public_key, node_data + 45, 33); } else if (version == 0x0488ADE4) { // private node if (node_data[45]) { // invalid data - return -4; + return -2; } memcpy(node->private_key, node_data + 46, 32); hdnode_fill_public_key(node); } else { - return -5; // invalid version + return -3; // invalid version } node->depth = node_data[4]; node->fingerprint = read_be(node_data + 5);