diff --git a/Makefile b/Makefile index 16e667a59..c62447b17 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ CFLAGS += -DED25519_NO_INLINE_ASM CFLAGS += -DED25519_FORCE_32BIT=1 CFLAGS += -Ied25519-donna -Icurve25519-donna -I. CFLAGS += -DUSE_ETHEREUM=1 +CFLAGS += -DUSE_GRAPHENE=1 # disable certain optimizations and features when small footprint is required ifdef SMALL diff --git a/base58.c b/base58.c index 5355fcb1e..2c44d3985 100644 --- a/base58.c +++ b/base58.c @@ -27,6 +27,7 @@ #include "base58.h" #include "sha2.h" #include "macros.h" +#include "ripemd160.h" static const int8_t b58digits_map[] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, @@ -217,3 +218,58 @@ int base58_decode_check(const char *str, uint8_t *data, int datalen) memcpy(data, nd, res - 4); return res - 4; } + +#if USE_GRAPHENE +int b58gphcheck(const void *bin, size_t binsz, const char *base58str) +{ + unsigned char buf[32]; + const uint8_t *binc = bin; + unsigned i; + if (binsz < 4) + return -4; + ripemd160(bin, binsz - 4, buf); // No double SHA256, but a single RIPEMD160 + if (memcmp(&binc[binsz - 4], buf, 4)) + return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid possibility of accessing base58str beyond the end) + for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) + {} // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') + return -3; + + return binc[0]; +} + +int base58gph_encode_check(const uint8_t *data, int datalen, char *str, int strsize) +{ + if (datalen > 128) { + return 0; + } + uint8_t buf[datalen + 32]; + uint8_t *hash = buf + datalen; + memcpy(buf, data, datalen); + ripemd160(data, datalen, hash); // No double SHA256, but a single RIPEMD160 + size_t res = strsize; + bool success = b58enc(str, &res, buf, datalen + 4); + MEMSET_BZERO(buf, sizeof(buf)); + return success ? res : 0; +} + +int base58gph_decode_check(const char *str, uint8_t *data, int datalen) +{ + if (datalen > 128) { + return 0; + } + uint8_t d[datalen + 4]; + size_t res = datalen + 4; + if (b58tobin(d, &res, str) != true) { + return 0; + } + uint8_t *nd = d + datalen + 4 - res; + if (b58gphcheck(nd, res, str) < 0) { + return 0; + } + memcpy(data, nd, res - 4); + return res - 4; +} +#endif diff --git a/base58.h b/base58.h index a53e4385b..094849bae 100644 --- a/base58.h +++ b/base58.h @@ -35,4 +35,10 @@ bool b58tobin(void *bin, size_t *binszp, const char *b58); int b58check(const void *bin, size_t binsz, const char *base58str); bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz); +#if USE_GRAPHENE +int base58gph_encode_check(const uint8_t *data, int datalen, char *str, int strsize); +int base58gph_decode_check(const char *str, uint8_t *data, int datalen); +int b58gphcheck(const void *bin, size_t binsz, const char *base58str); +#endif + #endif diff --git a/options.h b/options.h index fabca9f9b..6b7de989b 100644 --- a/options.h +++ b/options.h @@ -61,6 +61,11 @@ #define USE_ETHEREUM 0 #endif +// support Graphene operations (STEEM, BitShares) +#ifndef USE_GRAPHENE +#define USE_GRAPHENE 0 +#endif + // support Keccak hashing #ifndef USE_KECCAK #define USE_KECCAK USE_ETHEREUM diff --git a/tests.c b/tests.c index 40a3d6dd4..3a492db10 100644 --- a/tests.c +++ b/tests.c @@ -442,6 +442,38 @@ START_TEST(test_base58) } END_TEST +// Graphene Base85CheckEncoding +START_TEST(test_base58gph) +{ + static const char *base58_vector[] = { + "02e649f63f8e8121345fd7f47d0d185a3ccaa843115cd2e9392dcd9b82263bc680", "6dumtt9swxCqwdPZBGXh9YmHoEjFFnNfwHaTqRbQTghGAY2gRz", + "021c7359cd885c0e319924d97e3980206ad64387aff54908241125b3a88b55ca16", "5725vivYpuFWbeyTifZ5KevnHyqXCi5hwHbNU9cYz1FHbFXCxX", + "02f561e0b57a552df3fa1df2d87a906b7a9fc33a83d5d15fa68a644ecb0806b49a", "6kZKHSuxqAwdCYsMvwTcipoTsNE2jmEUNBQufGYywpniBKXWZK", + "03e7595c3e6b58f907bee951dc29796f3757307e700ecf3d09307a0cc4a564eba3", "8b82mpnH8YX1E9RHnU2a2YgLTZ8ooevEGP9N15c1yFqhoBvJur", + 0, 0, + }; + const char **raw = base58_vector; + const char **str = base58_vector + 1; + uint8_t rawn[34]; + char strn[53]; + int r; + while (*raw && *str) { + int len = strlen(*raw) / 2; + + memcpy(rawn, fromhex(*raw), len); + r = base58gph_encode_check(rawn, len, strn, sizeof(strn)); + ck_assert_int_eq((size_t)r, strlen(*str) + 1); + ck_assert_str_eq(strn, *str); + + r = base58gph_decode_check(strn, rawn, len); + ck_assert_int_eq(r, len); + ck_assert_mem_eq(rawn, fromhex(*raw), len); + + raw += 2; str += 2; + } +} +END_TEST + START_TEST(test_bignum_divmod) { uint32_t r; @@ -2603,7 +2635,6 @@ START_TEST(test_ethereum_pubkeyhash) } END_TEST - // define test suite and cases Suite *test_suite(void) { @@ -2634,6 +2665,10 @@ Suite *test_suite(void) tcase_add_test(tc, test_base58); suite_add_tcase(s, tc); + tc = tcase_create("base58gph"); + tcase_add_test(tc, test_base58gph); + suite_add_tcase(s, tc); + tc = tcase_create("bignum_divmod"); tcase_add_test(tc, test_bignum_divmod); suite_add_tcase(s, tc);