diff --git a/Makefile b/Makefile index 15247cf1f6..ea978be052 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ OBJS = $(SRCS:.c=.o) TESTLIBS = $(shell pkg-config --libs check) -lrt -lpthread -lm TESTSSLLIBS = -lcrypto -all: tests test-openssl libtrezor-crypto.so test_speed tools +all: tests test-openssl test_speed tools libtrezor-crypto.so %.o: %.c %.h options.h $(CC) $(CFLAGS) -o $@ -c $< diff --git a/bignum.c b/bignum.c index b2a20f9747..c06bd53dad 100644 --- a/bignum.c +++ b/bignum.c @@ -960,6 +960,58 @@ void bn_divmod1000(bignum256 *a, uint32_t *r) *r = rem; } +// 2^256 has 78 digits in decimal (+ 1 for decimal point, + 1 for leading zero, + 1 for trailing zero) +#define DIGITLEN (78 + 1 + 1 + 1) + +int bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, int decimals, char *out, int outlen) +{ + // convert bignum to characters + bignum256 val; + memcpy(&val, amnt, sizeof(bignum256)); + char digits[DIGITLEN]; + memset(digits, '0', DIGITLEN); + int pos = 1; // keep one trailing zero + for (int i = 0; i < 78 / 3; i++) { + uint32_t limb; + bn_divmod1000(&val, &limb); + if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; } + digits[DIGITLEN - 1 - pos] = '0' + (limb % 10); pos++; + if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; } + digits[DIGITLEN - 1 - pos] = '0' + ((limb / 10) % 10); pos++; + if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; } + digits[DIGITLEN - 1 - pos] = '0' + ((limb / 100) % 10); pos++; + } + + // drop leading zeroes + int digitstart = 0; + while (digitstart < DIGITLEN - 1 && digits[digitstart] == '0' && digits[digitstart + 1] >= '0' && digits[digitstart + 1] <= '9') { + digitstart++; + } + + // drop trailing zeroes + int digitend = DIGITLEN - 1; + while (digitend > 0 && digits[digitend] == '0' && digits[digitend - 1] >= '0' && digits[digitend - 1] <= '9') { + digitend--; + } + + int digitslen = digitend - digitstart + 1; + int prefixlen = prefix != NULL ? strlen(prefix) : 0; + int suffixlen = suffix != NULL ? strlen(suffix) : 0; + + // output buffer is too small + if (prefixlen + digitslen + suffixlen + 1 > outlen) { + return 0; + } + + // copy result to output buffer + memcpy(out, prefix, prefixlen); + memcpy(out + prefixlen, digits + digitstart, digitslen); + memcpy(out + prefixlen + digitslen, suffix, suffixlen); + out[prefixlen + digitslen + suffixlen] = 0; + + return prefixlen + digitslen + suffixlen; +} + #if USE_BN_PRINT void bn_print(const bignum256 *a) { diff --git a/bignum.h b/bignum.h index 83ae314ce9..8e23ab9760 100644 --- a/bignum.h +++ b/bignum.h @@ -145,6 +145,8 @@ void bn_divmod58(bignum256 *a, uint32_t *r); void bn_divmod1000(bignum256 *a, uint32_t *r); +int bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, int decimals, char *out, int outlen); + #if USE_BN_PRINT void bn_print(const bignum256 *a); void bn_print_raw(const bignum256 *a); diff --git a/tests.c b/tests.c index f77752837b..4c9c6be218 100644 --- a/tests.c +++ b/tests.c @@ -353,6 +353,138 @@ START_TEST(test_bignum_is_less) } END_TEST +START_TEST(test_bignum_format) { + bignum256 a; + char buf[128]; + int r; + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "0.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, "", "", 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "0.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, NULL, "SFFX", 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3 + 4); + ck_assert_str_eq(buf, "0.0SFFX"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, "PRFX", NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 4 + 3); + ck_assert_str_eq(buf, "PRFX0.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, "PRFX", "SFFX", 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 4 + 3 + 4); + ck_assert_str_eq(buf, "PRFX0.0SFFX"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a); + r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "0.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000001"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "1.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000002"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "2.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000005"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "5.0"); + + bn_read_be(fromhex("000000000000000000000000000000000000000000000000000000000000000a"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 4); + ck_assert_str_eq(buf, "10.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000014"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 4); + ck_assert_str_eq(buf, "20.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000032"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 4); + ck_assert_str_eq(buf, "50.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000064"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 5); + ck_assert_str_eq(buf, "100.0"); + + bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000000c8"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 5); + ck_assert_str_eq(buf, "200.0"); + + bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000001f4"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 5); + ck_assert_str_eq(buf, "500.0"); + + bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000003e8"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 6); + ck_assert_str_eq(buf, "1000.0"); + + bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000989680"), &a); + r = bn_format(&a, NULL, NULL, 7, buf, sizeof(buf)); + ck_assert_int_eq(r, 3); + ck_assert_str_eq(buf, "1.0"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf)); + ck_assert_int_eq(r, 80); + ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457584007913129639935.0"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, NULL, NULL, 1, buf, sizeof(buf)); + ck_assert_int_eq(r, 79); + ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945758400791312963993.5"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, NULL, NULL, 2, buf, sizeof(buf)); + ck_assert_int_eq(r, 79); + ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131296399.35"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, NULL, NULL, 8, buf, sizeof(buf)); + ck_assert_int_eq(r, 79); + ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131.29639935"); + + bn_read_be(fromhex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3bbb00"), &a); + r = bn_format(&a, NULL, NULL, 8, buf, sizeof(buf)); + ck_assert_int_eq(r, 72); + ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131.0"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf)); + ck_assert_int_eq(r, 79); + ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457.584007913129639935"); + + bn_read_be(fromhex("fffffffffffffffffffffffffffffffffffffffffffffffff7e52fe5afe40000"), &a); + r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf)); + ck_assert_int_eq(r, 62); + ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457.0"); + + bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); + r = bn_format(&a, "quite a long prefix", "even longer suffix", 60, buf, sizeof(buf)); + ck_assert_int_eq(r, 116); + ck_assert_str_eq(buf, "quite a long prefix115792089237316195.423570985008687907853269984665640564039457584007913129639935even longer suffix"); +} +END_TEST + // from https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_keys_valid.json START_TEST(test_base58) { @@ -2960,6 +3092,7 @@ Suite *test_suite(void) tcase_add_test(tc, test_bignum_is_odd); tcase_add_test(tc, test_bignum_bitcount); tcase_add_test(tc, test_bignum_is_less); + tcase_add_test(tc, test_bignum_format); suite_add_tcase(s, tc); tc = tcase_create("base58"); @@ -3107,7 +3240,7 @@ Suite *test_suite(void) tcase_add_test(tc, test_ethereum_pubkeyhash); suite_add_tcase(s, tc); - tc = tcase_create("multibyte_addresse"); + tc = tcase_create("multibyte_address"); tcase_add_test(tc, test_multibyte_address); suite_add_tcase(s, tc);