diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 7e5175b01..b5fc5500a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -4,6 +4,7 @@ Jochen Hoenicke Dustin Laurence Ondrej Mikle Roman Zeyde +Alex Beregszaszi netanelkl Jan Pochyla Ondrej Mikle diff --git a/bignum.c b/bignum.c index 20e3718c3..d2ebd6350 100644 --- a/bignum.c +++ b/bignum.c @@ -2,6 +2,7 @@ * Copyright (c) 2013-2014 Tomas Dzetkulic * Copyright (c) 2013-2014 Pavol Rusnak * Copyright (c) 2015 Jochen Hoenicke + * Copyright (c) 2016 Alex Beregszaszi * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), @@ -69,6 +70,22 @@ inline void write_be(uint8_t *data, uint32_t x) data[3] = x; } +inline uint32_t read_le(const uint8_t *data) +{ + return (((uint32_t)data[3]) << 24) | + (((uint32_t)data[2]) << 16) | + (((uint32_t)data[1]) << 8) | + (((uint32_t)data[0])); +} + +inline void write_le(uint8_t *data, uint32_t x) +{ + data[3] = x >> 24; + data[2] = x >> 16; + data[1] = x >> 8; + data[0] = x; +} + // convert a raw bigendian 256 bit value into a normalized bignum. // out_number is partly reduced (since it fits in 256 bit). void bn_read_be(const uint8_t *in_number, bignum256 *out_number) @@ -104,6 +121,41 @@ void bn_write_be(const bignum256 *in_number, uint8_t *out_number) } } +// convert a raw little endian 256 bit value into a normalized bignum. +// out_number is partly reduced (since it fits in 256 bit). +void bn_read_le(const uint8_t *in_number, bignum256 *out_number) +{ + int i; + uint32_t temp = 0; + for (i = 0; i < 8; i++) { + // invariant: temp = (in_number % 2^(32i)) >> 30i + // get next limb = (in_number % 2^(32(i+1))) >> 32i + uint32_t limb = read_le(in_number + i * 4); + // temp = (in_number % 2^(32(i+1))) << 30i + temp |= limb << (2*i); + // store 30 bits into val[i] + out_number->val[i]= temp & 0x3FFFFFFF; + // prepare temp for next round + temp = limb >> (30 - 2*i); + } + out_number->val[8] = temp; +} + +// convert a normalized bignum to a raw little endian 256 bit number. +// in_number must be fully reduced. +void bn_write_le(const bignum256 *in_number, uint8_t *out_number) +{ + int i; + uint32_t temp = in_number->val[8] << 16; + for (i = 0; i < 8; i++) { + // invariant: temp = (in_number >> 30*(8-i)) << (16 + 2i) + uint32_t limb = in_number->val[7 - i]; + temp |= limb >> (14 - 2*i); + write_le(out_number + (7 - i) * 4, temp); + temp = limb << (18 + 2*i); + } +} + // sets a bignum to zero. void bn_zero(bignum256 *a) { diff --git a/bignum.h b/bignum.h index 4b51c5d42..65bea7ba7 100644 --- a/bignum.h +++ b/bignum.h @@ -39,10 +39,20 @@ uint32_t read_be(const uint8_t *data); // write 4 big endian bytes void write_be(uint8_t *data, uint32_t x); +// read 4 little endian bytes into uint32 +uint32_t read_le(const uint8_t *data); + +// write 4 little endian bytes +void write_le(uint8_t *data, uint32_t x); + void bn_read_be(const uint8_t *in_number, bignum256 *out_number); void bn_write_be(const bignum256 *in_number, uint8_t *out_number); +void bn_read_le(const uint8_t *in_number, bignum256 *out_number); + +void bn_write_le(const bignum256 *in_number, uint8_t *out_number); + void bn_zero(bignum256 *a); int bn_is_zero(const bignum256 *a); diff --git a/tests.c b/tests.c index fc1f83a63..f1e10ba2b 100644 --- a/tests.c +++ b/tests.c @@ -123,6 +123,36 @@ START_TEST(test_bignum_equal) } END_TEST +START_TEST(test_bignum_read_le) +{ + bignum256 a; + bignum256 b; + + bn_read_be(fromhex("c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), &a); + bn_read_le(fromhex("d58b6de8051f031eeca2c6d7fbe1b5d37c4314fe1068f96352dd0d8b85ce5ec5"), &b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_write_le) +{ + bignum256 a; + bignum256 b; + uint8_t tmp[32]; + + bn_read_be(fromhex("c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), &a); + bn_write_le(&a, tmp); + + bn_read_le(tmp, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + bn_read_be(fromhex("d58b6de8051f031eeca2c6d7fbe1b5d37c4314fe1068f96352dd0d8b85ce5ec5"), &a); + bn_read_be(tmp, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + // from https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_keys_valid.json START_TEST(test_base58) { @@ -2371,6 +2401,8 @@ Suite *test_suite(void) tcase_add_test(tc, test_bignum_read_be); tcase_add_test(tc, test_bignum_write_be); tcase_add_test(tc, test_bignum_equal); + tcase_add_test(tc, test_bignum_read_le); + tcase_add_test(tc, test_bignum_write_le); suite_add_tcase(s, tc); tc = tcase_create("base58");