From 2a386f88d26b054692328a09b272e773102194c1 Mon Sep 17 00:00:00 2001 From: Dakoda Greaves Date: Mon, 18 Mar 2024 15:50:32 -0700 Subject: [PATCH] crypto: adds chacha20 and fast_random_context --- CMakeLists.txt | 2 + Makefile.am | 3 + include/dogecoin/byteswap.h | 52 +++++++- include/dogecoin/chacha20.h | 54 ++++++++ include/dogecoin/common.h | 32 ++++- include/dogecoin/portable_endian.h | 8 ++ include/dogecoin/random.h | 33 ++++- include/test/utest.h | 54 +++++++- src/chacha20.c | 194 +++++++++++++++++++++++++++++ src/random.c | 114 ++++++++++++++++- test/chacha20_tests.c | 74 +++++++++++ test/random_tests.c | 40 +++++- test/unittester.c | 8 +- 13 files changed, 650 insertions(+), 18 deletions(-) create mode 100644 include/dogecoin/chacha20.h create mode 100644 src/chacha20.c create mode 100644 test/chacha20_tests.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b0ec045f2..7fc21c7d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,7 @@ TARGET_SOURCES(${LIBDOGECOIN_NAME} PRIVATE src/bip44.c src/block.c src/buffer.c + src/chacha20.c src/cstr.c src/ctaes.c src/ecc.c @@ -259,6 +260,7 @@ IF(USE_TESTS) test/bip44_tests.c test/block_tests.c test/buffer_tests.c + test/chacha20_tests.c test/cstr_tests.c test/ecc_tests.c test/hash_tests.c diff --git a/Makefile.am b/Makefile.am index ac92c2d87..eada1fd86 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ noinst_HEADERS = \ include/dogecoin/blockchain.h \ include/dogecoin/buffer.h \ include/dogecoin/byteswap.h \ + include/dogecoin/chacha20.h \ include/dogecoin/chainparams.h \ include/dogecoin/common.h \ include/dogecoin/cstr.h \ @@ -81,6 +82,7 @@ libdogecoin_la_SOURCES = \ src/bip44.c \ src/block.c \ src/buffer.c \ + src/chacha20.c \ src/chainparams.c \ src/cstr.c \ src/ctaes.c \ @@ -131,6 +133,7 @@ tests_SOURCES = \ test/bip44_tests.c \ test/block_tests.c \ test/buffer_tests.c \ + test/chacha20_tests.c \ test/cstr_tests.c \ test/ecc_tests.c \ test/hash_tests.c \ diff --git a/include/dogecoin/byteswap.h b/include/dogecoin/byteswap.h index 26c0869ba..a66a79ccd 100644 --- a/include/dogecoin/byteswap.h +++ b/include/dogecoin/byteswap.h @@ -1,7 +1,7 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers -// Copyright (c) 2023 bluezr -// Copyright (c) 2023 edtubbs -// Copyright (c) 2023 The Dogecoin Foundation +// Copyright (c) 2024 bluezr +// Copyright (c) 2024 edtubbs +// Copyright (c) 2024 The Dogecoin Foundation // // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -23,6 +23,52 @@ LIBDOGECOIN_BEGIN_DECL #include +static unsigned char bit_swap_table[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +#if HAVE_DECL_BSWAP_8 == 0 +DISABLE_WARNING_PUSH +DISABLE_WARNING(-Wunused-function) +LIBDOGECOIN_API static uint8_t bswap_8(uint8_t x) +{ + return bit_swap_table[x]; +} +DISABLE_WARNING_POP +#endif + #if defined(__APPLE__) #if !defined(bswap_16) diff --git a/include/dogecoin/chacha20.h b/include/dogecoin/chacha20.h new file mode 100644 index 000000000..d58ef0232 --- /dev/null +++ b/include/dogecoin/chacha20.h @@ -0,0 +1,54 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2017 The Bitcoin Core developers + Copyright (c) 2024 bluezr + Copyright (c) 2024 The Dogecoin Foundation + + 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 __LIBDOGECOIN_CHACHA20_H__ +#define __LIBDOGECOIN_CHACHA20_H__ + +#include + +LIBDOGECOIN_BEGIN_DECL + +typedef struct chacha20 { + uint32_t input[16]; + void (*setkey)(struct chacha20* this, const unsigned char* key, size_t keylen); + void (*setiv)(struct chacha20* this, uint64_t iv); + void (*seek)(struct chacha20* this, uint64_t pos); + void (*output)(struct chacha20* this, unsigned char* c, size_t bytes); +} chacha20; + +struct chacha20* chacha20_new(); +struct chacha20* chacha20_init(const unsigned char* key, size_t keylen); +void chacha20_set_key(struct chacha20* this, const unsigned char* key, size_t keylen); +void chacha20_set_iv(struct chacha20* this, uint64_t iv); +void chacha20_seek(struct chacha20* this, uint64_t pos); +void chacha20_output(struct chacha20* this, unsigned char* c, size_t bytes); +void chacha20_free(struct chacha20* this); + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_CHACHA20_H__ diff --git a/include/dogecoin/common.h b/include/dogecoin/common.h index a8c2b8c1c..28ad2c5e8 100644 --- a/include/dogecoin/common.h +++ b/include/dogecoin/common.h @@ -1,5 +1,6 @@ -// Copyright (c) 2023 bluezr -// Copyright (c) 2023 The Dogecoin Foundation +// Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2024 bluezr +// Copyright (c) 2024 The Dogecoin Foundation // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -41,6 +42,12 @@ LIBDOGECOIN_API static inline uint64_t read_le64(const unsigned char* ptr) return le64toh(x); } +LIBDOGECOIN_API static inline void write_le8(unsigned char* ptr, uint8_t x) +{ + uint8_t v = htole8(x); + memcpy_safe(ptr, (char*)&v, 1); +} + LIBDOGECOIN_API static inline void write_le16(unsigned char* ptr, uint16_t x) { uint16_t v = htole16(x); @@ -85,6 +92,27 @@ LIBDOGECOIN_API static inline void write_be64(unsigned char* ptr, uint64_t x) memcpy_safe(ptr, (char*)&v, 8); } +/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ +LIBDOGECOIN_API static inline uint64_t count_bits(uint64_t x) +{ +#if HAVE_BUILTIN_CLZL + if (sizeof(unsigned long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; + } +#endif +#if HAVE_BUILTIN_CLZLL + if (sizeof(unsigned long long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; + } +#endif + int ret = 0; + while (x) { + x >>= 1; + ++ret; + } + return ret; +} + LIBDOGECOIN_END_DECL #endif // __LIBDOGECOIN_COMMON_H__ diff --git a/include/dogecoin/portable_endian.h b/include/dogecoin/portable_endian.h index a17c3c388..6219d0725 100644 --- a/include/dogecoin/portable_endian.h +++ b/include/dogecoin/portable_endian.h @@ -14,6 +14,14 @@ LIBDOGECOIN_BEGIN_DECL +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define htole8(x) (x) +# elif __BYTE_ORDER == __BIG_ENDIAN +# define htole8(x) bswap_8(x) +#else +#error UNKNOWN BYTE ORDER +#endif + #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) #define __WINDOWS__ diff --git a/include/dogecoin/random.h b/include/dogecoin/random.h index 81d643356..b3ce0530d 100644 --- a/include/dogecoin/random.h +++ b/include/dogecoin/random.h @@ -3,8 +3,8 @@ The MIT License (MIT) Copyright (c) 2015 Douglas J. Bakkum - Copyright (c) 2022 bluezr - Copyright (c) 2022 The Dogecoin Foundation + Copyright (c) 2024 bluezr + Copyright (c) 2024 The Dogecoin Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -33,6 +33,35 @@ LIBDOGECOIN_BEGIN_DECL +#include +#include + +typedef struct fast_random_context { + dogecoin_bool requires_seed; + chacha20* rng; + unsigned char bytebuf[64]; + int bytebuf_size; + uint64_t bitbuf; + int bitbuf_size; + void (*random_seed)(struct fast_random_context* this); + void (*fill_byte_buffer)(struct fast_random_context* this); + void (*fill_bit_buffer)(struct fast_random_context* this); + uint256* (*rand256)(struct fast_random_context* this); + uint64_t (*rand64)(struct fast_random_context* this); + uint64_t (*randbits)(struct fast_random_context* this, int bits); + uint32_t (*rand32)(struct fast_random_context* this); + dogecoin_bool (*randbool)(struct fast_random_context* this); +} fast_random_context; + +struct fast_random_context* init_fast_random_context(dogecoin_bool f_deterministic, const uint256* seed); +uint256* rand256(struct fast_random_context* this); +uint64_t rand64(struct fast_random_context* this); +void free_fast_random_context(struct fast_random_context* this); + +static const ssize_t NUM_OS_RANDOM_BYTES = 32; +void get_os_rand(unsigned char* ent32); +void random_sanity_check(); + typedef struct dogecoin_rnd_mapper_ { void (*dogecoin_random_init)(void); dogecoin_bool (*dogecoin_random_bytes)(uint8_t* buf, uint32_t len, const uint8_t update_seed); diff --git a/include/test/utest.h b/include/test/utest.h index 054fd091c..8e6c7508c 100644 --- a/include/test/utest.h +++ b/include/test/utest.h @@ -1,8 +1,8 @@ /* Copyright (c) 2015 Douglas J. Bakkum - Copyright (c) 2023 bluezr - Copyright (c) 2023 The Dogecoin Foundation + Copyright (c) 2024 bluezr + Copyright (c) 2024 The Dogecoin Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -125,6 +125,21 @@ } while (0); \ } +#define u_assert_uint32_not_eq(R, E) \ + { \ + uint64_t r_ = (R); \ + uint64_t e_ = (E); \ + do { \ + if (r_ == e_) { \ + printf("FAILED - %s() - Line %d\n", __func__, __LINE__); \ + printf("\tExpect: \t%" PRIu64 "\n", e_); \ + printf("\tReceive:\t%" PRIu64 "\n", r_); \ + U_TESTS_FAIL++; \ + return; \ + }; \ + } while (0); \ + } + #define u_assert_uint64_eq(R, E) \ { \ uint64_t r_ = (R); \ @@ -140,6 +155,21 @@ } while (0); \ } +#define u_assert_uint64_not_eq(R, E) \ + { \ + uint64_t r_ = (R); \ + uint64_t e_ = (E); \ + do { \ + if (r_ == e_) { \ + printf("FAILED - %s() - Line %d\n", __func__, __LINE__); \ + printf("\tExpect: \t%" PRIu64 "\n", e_); \ + printf("\tReceive:\t%" PRIu64 "\n", r_); \ + U_TESTS_FAIL++; \ + return; \ + }; \ + } while (0); \ + } + #define u_assert_long_double_eq(R, E) \ { \ long double r_ = (R); \ @@ -157,8 +187,8 @@ #define u_assert_double_eq(R, E) \ { \ - double r_ = (R); \ - double e_ = (E); \ + double r_ = (R); \ + double e_ = (E); \ do { \ if (r_ != e_) { \ printf("FAILED - %s() - Line %d\n", __func__, __LINE__); \ @@ -246,6 +276,22 @@ } while (0); \ } +#define u_assert_mem_not_eq(R, E, L) \ + { \ + const void* r_ = (R); \ + const void* e_ = (E); \ + size_t l_ = (L); \ + do { \ + if (!memcmp(r_, e_, l_)) { \ + printf("FAILED - %s() - Line %d\n", __func__, __LINE__); \ + printf("\tExpect: \t%s\n", utils_uint8_to_hex(e_, l_)); \ + printf("\tReceive:\t%s\n", utils_uint8_to_hex(r_, l_)); \ + U_TESTS_FAIL++; \ + return; \ + }; \ + } while (0); \ + } + #define u_assert_is_null(R) \ { \ const void* r_ = (R); \ diff --git a/src/chacha20.c b/src/chacha20.c new file mode 100644 index 000000000..78a06cfaa --- /dev/null +++ b/src/chacha20.c @@ -0,0 +1,194 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Copyright (c) 2024 bluezr +// Copyright (c) 2024 The Dogecoin Foundation +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Based on the public domain implementation 'merged' by D. J. Bernstein +// See https://cr.yp.to/chacha.html. + +#include +#include + +#include + +static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); } + +#define QUARTERROUND(a,b,c,d) \ + a += b; d = rotl32(d ^ a, 16); \ + c += d; b = rotl32(b ^ c, 12); \ + a += b; d = rotl32(d ^ a, 8); \ + c += d; b = rotl32(b ^ c, 7); + +static const unsigned char sigma[] = "expand 32-byte k"; +static const unsigned char tau[] = "expand 16-byte k"; + +struct chacha20* chacha20_new() { + struct chacha20* this; + this = dogecoin_calloc(1, sizeof(*this)); + dogecoin_mem_zero(this->input, 16); + this->setkey = chacha20_set_key; + this->setiv = chacha20_set_iv; + this->seek = chacha20_seek; + this->output = chacha20_output; + return this; +} + +struct chacha20* chacha20_init(const unsigned char* key, size_t keylen) { + struct chacha20* this; + this = dogecoin_calloc(1, sizeof(*this)); + dogecoin_mem_zero(this->input, 16); + this->setkey = chacha20_set_key; + this->setiv = chacha20_set_iv; + this->seek = chacha20_seek; + this->output = chacha20_output; + this->setkey(this, key, keylen); + return this; +} + +void chacha20_set_key(struct chacha20* this, const unsigned char* k, size_t keylen) { + const unsigned char *constants; + + this->input[4] = read_le32(k + 0); + this->input[5] = read_le32(k + 4); + this->input[6] = read_le32(k + 8); + this->input[7] = read_le32(k + 12); + if (keylen == 32) { /* recommended */ + k += 16; + constants = sigma; + } else { /* keylen == 16 */ + constants = tau; + } + this->input[8] = read_le32(k + 0); + this->input[9] = read_le32(k + 4); + this->input[10] = read_le32(k + 8); + this->input[11] = read_le32(k + 12); + this->input[0] = read_le32(constants + 0); + this->input[1] = read_le32(constants + 4); + this->input[2] = read_le32(constants + 8); + this->input[3] = read_le32(constants + 12); + this->input[12] = 0; + this->input[13] = 0; + this->input[14] = 0; + this->input[15] = 0; +} + +void chacha20_set_iv(struct chacha20* this, uint64_t iv) { + this->input[14] = iv; + this->input[15] = iv >> 32; +} + +void chacha20_seek(struct chacha20* this, uint64_t pos) { + this->input[12] = pos; + this->input[13] = pos >> 32; +} + +void chacha20_output(struct chacha20* this, unsigned char* c, size_t bytes) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + unsigned char *ctarget = NULL; + unsigned char tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = this->input[0]; + j1 = this->input[1]; + j2 = this->input[2]; + j3 = this->input[3]; + j4 = this->input[4]; + j5 = this->input[5]; + j6 = this->input[6]; + j7 = this->input[7]; + j8 = this->input[8]; + j9 = this->input[9]; + j10 = this->input[10]; + j11 = this->input[11]; + j12 = this->input[12]; + j13 = this->input[13]; + j14 = this->input[14]; + j15 = this->input[15]; + + for (;;) { + if (bytes < 64) { + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; + + ++j12; + if (!j12) ++j13; + + write_le32(c + 0, x0); + write_le32(c + 4, x1); + write_le32(c + 8, x2); + write_le32(c + 12, x3); + write_le32(c + 16, x4); + write_le32(c + 20, x5); + write_le32(c + 24, x6); + write_le32(c + 28, x7); + write_le32(c + 32, x8); + write_le32(c + 36, x9); + write_le32(c + 40, x10); + write_le32(c + 44, x11); + write_le32(c + 48, x12); + write_le32(c + 52, x13); + write_le32(c + 56, x14); + write_le32(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + this->input[12] = j12; + this->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + } +} + +void chacha20_free(struct chacha20* this) { + dogecoin_free(this); +} diff --git a/src/random.c b/src/random.c index 8bfb94d9f..25b8742c7 100644 --- a/src/random.c +++ b/src/random.c @@ -3,8 +3,8 @@ The MIT License (MIT) Copyright (c) 2015 Douglas J. Bakkum - Copyright (c) 2022 bluezr - Copyright (c) 2022-2024 The Dogecoin Foundation + Copyright (c) 2024 bluezr + Copyright (c) 2024 The Dogecoin Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ */ +#include #include #include @@ -219,3 +220,112 @@ dogecoin_bool dogecoin_random_bytes_internal(uint8_t* buf, uint32_t len, const u #endif } #endif + +void random_seed(struct fast_random_context* this) +{ + dogecoin_random_init(); + uint256 seed; + dogecoin_mem_zero(seed, 32); + dogecoin_random_bytes(seed, 32, 0); + this->rng->setkey(this->rng, seed, 32); + this->requires_seed = false; +} + +void fill_byte_buffer(struct fast_random_context* this) +{ + if (this->requires_seed) { + random_seed(this); + } + this->rng->output(this->rng, this->bytebuf, sizeof(this->bytebuf)); + this->bytebuf_size = sizeof(this->bytebuf); +} + +uint256* rand256(struct fast_random_context* this) +{ + if (this->bytebuf_size < 32) { + fill_byte_buffer(this); + } + uint256* ret = dogecoin_uint256_vla(1); + memcpy(ret, this->bytebuf + 64 - this->bytebuf_size, 32); + this->bytebuf_size -= 32; + return ret; +} + +/** Generate a random 64-bit integer. */ +uint64_t rand64(struct fast_random_context* this) +{ + if (this->requires_seed) random_seed(this); + unsigned char buf[8]; + this->rng->output(this->rng, buf, 8); + return read_le64(buf); +} + +void fill_bit_buffer(struct fast_random_context* this) +{ + this->bitbuf = rand64(this); + this->bitbuf_size = 64; +} + +/** Generate a random (bits)-bit integer. */ +uint64_t randbits(struct fast_random_context* this, int bits) +{ + if (bits == 0) { + return 0; + } else if (bits > 32) { + return rand64(this) >> (64 - bits); + } else { + if (this->bitbuf_size < bits) fill_bit_buffer(this); + uint64_t zero = 0; + uint64_t ret = this->bitbuf & (~zero >> (64 - bits)); + this->bitbuf >>= bits; + this->bitbuf_size -= bits; + return ret; + } +} + +uint64_t randrange(struct fast_random_context* this, uint64_t range) +{ + assert(range); + --range; + int bits = count_bits(range); + while (true) { + uint64_t ret = randbits(this, bits); + if (ret <= range) return ret; + } +} + +/** Generate a random 32-bit integer. */ +uint32_t rand32(struct fast_random_context* this) { return randbits(this, 32); } + +/** Generate a random boolean. */ +dogecoin_bool randbool(struct fast_random_context* this) { return randbits(this, 1); } + +struct fast_random_context* init_fast_random_context(dogecoin_bool f_deterministic, const uint256* seed) { + struct fast_random_context* this = dogecoin_calloc(1, sizeof(*this)); + this->requires_seed = false; + this->random_seed = random_seed; + this->fill_bit_buffer = fill_bit_buffer; + this->fill_byte_buffer = fill_byte_buffer; + this->rand256 = rand256; + this->rand64 = rand64; + this->randbits = randbits; + this->rand32 = rand32; + this->randbool = randbool; + if (!f_deterministic) { + if (seed == NULL) { + this->rng = chacha20_new(); + random_seed(this); + } + return this; + } else { + this->rng = chacha20_init((const unsigned char*)seed, 32); + } + return this; +} + +void free_fast_random_context(struct fast_random_context* this) { + if (this->rng != NULL) { + chacha20_free(this->rng); + } + dogecoin_free(this); +} diff --git a/test/chacha20_tests.c b/test/chacha20_tests.c new file mode 100644 index 000000000..a2bbbba72 --- /dev/null +++ b/test/chacha20_tests.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2017 The Bitcoin Core developers * + * Copyright (c) 2024 bluezr * + * Copyright (c) 2024 The Dogecoin Foundation * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +void testchacha20(const char* hexkey, uint64_t nonce, uint64_t seek, const char* hexout) +{ + unsigned char* tmp = parse_hex(hexkey); + size_t key_size = hexkey ? strlen(hexkey) / 2 : 0; + cstring* key = cstr_new_sz(key_size); + cstr_append_buf(key, tmp, key_size); + chacha20* rng = chacha20_init((const unsigned char*)key->str, key->len); + rng->setiv(rng, nonce); + rng->seek(rng, seek); + size_t out_size = hexout ? strlen(hexout) / 2 : 0; + cstring* out = cstr_new_sz(out_size); + dogecoin_free(tmp); + tmp = parse_hex(hexout); + cstr_append_buf(out, (const void*)tmp, out_size); + + cstring* outres = cstr_new(NULL); + cstr_resize(outres, out->len); + rng->output(rng, (unsigned char*)outres->str, outres->len); + char* str1 = utils_uint8_to_hex((const uint8_t*)out->str, out->len), + *str2 = utils_uint8_to_hex((const uint8_t*)outres->str, outres->len); + u_assert_str_eq(str1, str2); + dogecoin_free(tmp); + cstr_free(key, true); + cstr_free(out, true); + cstr_free(outres, true); + chacha20_free(rng); +} + +void test_chacha20() { + // Test vector from RFC 7539 + testchacha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb" + "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a" + "832c89c167eacd901d7e2bf363"); + + // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 + testchacha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b" + "8f41518a11cc387b669b2ee6586"); + testchacha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0, + "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79" + "2b1c43fea817e9ad275ae546963"); + testchacha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, + "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770" + "62eb7a0433e445f41e3"); + testchacha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0, + "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4" + "97a0b466e7d6bbdb0041b2f586b"); + testchacha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, + "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b" + "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1" + "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5" + "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5" + "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78" + "fab78c9"); +} diff --git a/test/random_tests.c b/test/random_tests.c index f85973480..c3c484c58 100644 --- a/test/random_tests.c +++ b/test/random_tests.c @@ -1,7 +1,7 @@ /********************************************************************** * Copyright (c) 2015 Jonas Schnelli * - * Copyright (c) 2022 bluezr * - * Copyright (c) 2022-2023 The Dogecoin Foundation * + * Copyright (c) 2024 bluezr * + * Copyright (c) 2024 The Dogecoin Foundation * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -36,6 +36,42 @@ void test_random() dogecoin_random_init(); dogecoin_bool ret = dogecoin_random_bytes(r_buf, 32, 0); u_assert_int_eq(ret, true); + unsigned char r_buf64[64]; + dogecoin_mem_zero(r_buf64, 64); + dogecoin_random_init(); + dogecoin_bool ret64 = dogecoin_random_bytes(r_buf64, 64, 0); + u_assert_int_eq(ret64, true); + + fast_random_context* this = init_fast_random_context(true, (const uint256*)r_buf); + fast_random_context* this2 = init_fast_random_context(true, (const uint256*)r_buf); + dogecoin_mem_zero(r_buf, 32); + this->rng->output(this->rng, r_buf, 32); + this2->rng->output(this2->rng, r_buf, 32); + + assert(this->randbool == this2->randbool); + u_assert_int_eq(this->randbits(this, 3), this2->randbits(this2, 3)); + u_assert_uint32_eq(this->rand32(this), this2->rand32(this2)); + u_assert_uint64_eq(this->rand64(this), this->rand64(this2)); + uint256* r256_1 = this->rand256(this); + uint256* r256_2 = this2->rand256(this2); + u_assert_mem_eq(r256_1, r256_2, 32); + dogecoin_free(r256_1); + dogecoin_free(r256_2); + free_fast_random_context(this); + free_fast_random_context(this2); + + fast_random_context* this3 = init_fast_random_context(false, 0); + fast_random_context* this4 = init_fast_random_context(false, 0); + u_assert_uint32_not_eq(this3->rand32(this3), this4->rand32(this4)); + u_assert_uint64_not_eq(this3->rand64(this3), this4->rand64(this4)); + uint256* r256_3 = this3->rand256(this3); + uint256* r256_4 = this4->rand256(this4); + u_assert_mem_not_eq(r256_3, r256_4, 32); + dogecoin_free(r256_3); + dogecoin_free(r256_4); + free_fast_random_context(this3); + free_fast_random_context(this4); + dogecoin_rnd_mapper mymapper = {test_random_init_cb, test_random_bytes_cb}; dogecoin_rnd_set_mapper(mymapper); u_assert_int_eq(dogecoin_random_bytes(r_buf, 32, 0), false); diff --git a/test/unittester.c b/test/unittester.c index 98c0a0af9..ec6f8cbd0 100644 --- a/test/unittester.c +++ b/test/unittester.c @@ -1,7 +1,7 @@ /********************************************************************** - * Copyright (c) 2023 bluezr * - * Copyright (c) 2023 edtubbs * - * Copyright (c) 2023 The Dogecoin Foundation * + * Copyright (c) 2024 bluezr * + * Copyright (c) 2024 edtubbs * + * Copyright (c) 2024 The Dogecoin Foundation * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -45,6 +45,7 @@ extern void test_bip39(); extern void test_bip44(); extern void test_block_header(); extern void test_buffer(); +extern void test_chacha20(); extern void test_cstr(); extern void test_ecc(); extern void test_hash(); @@ -124,6 +125,7 @@ int main() #endif u_run_test(test_block_header); u_run_test(test_buffer); + u_run_test(test_chacha20); u_run_test(test_cstr); u_run_test(test_ecc); u_run_test(test_hash);