From 0fe69908838248c28624beb540257892db6bdcbd Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Tue, 12 Apr 2022 22:20:58 +0600 Subject: [PATCH] Refactor crypto RNG; add getrandom() support /dev/urandom and /dev/random are ubiquitous, but take an open file descriptor, and may not actually be present inside badly configured containers. --- src/control.c | 2 +- src/{gcrypt => }/crypto.c | 18 ++----- src/crypto.h | 2 - src/ed25519/ecdh.c | 2 +- src/ed25519/ecdsagen.c | 2 +- src/gcrypt/meson.build | 1 - src/have.h | 5 +- src/invitation.c | 1 + src/linux/meson.build | 8 ++- src/meson.build | 10 +++- src/net_packet.c | 1 + src/nolegacy/crypto.c | 96 ---------------------------------- src/nolegacy/meson.build | 5 +- src/openssl/crypto.c | 72 ------------------------- src/protocol_auth.c | 2 +- src/protocol_key.c | 1 + src/random.c | 53 +++++++++++++++++++ src/random.h | 10 ++++ src/sptps.c | 2 +- src/sptps_keypair.c | 78 +++++++++++++++------------ src/sptps_speed.c | 17 ++++-- src/sptps_test.c | 23 ++++---- src/tincctl.c | 32 ++++++++---- src/tincd.c | 4 +- src/windows/meson.build | 1 + src/windows/random.c | 25 +++++++++ src/xoshiro.c | 1 + test/unit/meson.build | 15 +++++- test/unit/test_random.c | 88 +++++++++++++++++++++++++++++++ test/unit/test_random_noinit.c | 23 ++++++++ 30 files changed, 341 insertions(+), 259 deletions(-) rename src/{gcrypt => }/crypto.c (81%) delete mode 100644 src/nolegacy/crypto.c create mode 100644 src/random.c create mode 100644 src/random.h create mode 100644 src/windows/random.c create mode 100644 test/unit/test_random.c create mode 100644 test/unit/test_random_noinit.c diff --git a/src/control.c b/src/control.c index 86e0f68a..4c0ab511 100644 --- a/src/control.c +++ b/src/control.c @@ -18,7 +18,6 @@ */ #include "system.h" -#include "crypto.h" #include "conf.h" #include "control.h" #include "control_common.h" @@ -30,6 +29,7 @@ #include "route.h" #include "utils.h" #include "xalloc.h" +#include "random.h" char controlcookie[65]; diff --git a/src/gcrypt/crypto.c b/src/crypto.c similarity index 81% rename from src/gcrypt/crypto.c rename to src/crypto.c index cf5d0e64..20d917d9 100644 --- a/src/gcrypt/crypto.c +++ b/src/crypto.c @@ -17,18 +17,8 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "../system.h" +#include "crypto.h" -#include - -#include "../crypto.h" - -void crypto_init(void) { -} - -void crypto_exit(void) { -} - -void randomize(void *out, size_t outlen) { - gcry_create_nonce(out, outlen); -} +// No-op for those cryptographic libraries that +// do not require any additional initialization. +void crypto_init(void) {} diff --git a/src/crypto.h b/src/crypto.h index 9fc4156c..ac96ea88 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -23,11 +23,9 @@ */ extern void crypto_init(void); -extern void crypto_exit(void); extern uint64_t xoshiro(void); extern void prng_init(void); extern void prng_randomize(void *buf, size_t buflen); -extern void randomize(void *buf, size_t buflen); static inline uint32_t prng(uint32_t limit) { uint64_t bins = UINT64_MAX / limit; diff --git a/src/ed25519/ecdh.c b/src/ed25519/ecdh.c index 302fafd8..469f502e 100644 --- a/src/ed25519/ecdh.c +++ b/src/ed25519/ecdh.c @@ -18,6 +18,7 @@ */ #include "../system.h" +#include "../random.h" #include "ed25519.h" @@ -26,7 +27,6 @@ typedef struct ecdh_t { uint8_t private[64]; } ecdh_t; -#include "../crypto.h" #include "../ecdh.h" #include "../xalloc.h" diff --git a/src/ed25519/ecdsagen.c b/src/ed25519/ecdsagen.c index 06b41c86..bc14fd29 100644 --- a/src/ed25519/ecdsagen.c +++ b/src/ed25519/ecdsagen.c @@ -27,10 +27,10 @@ typedef struct { uint8_t public[32]; } ecdsa_t; -#include "../crypto.h" #include "../ecdsagen.h" #include "../utils.h" #include "../xalloc.h" +#include "../random.h" // Generate ECDSA key diff --git a/src/gcrypt/meson.build b/src/gcrypt/meson.build index ac93c809..9cfe466e 100644 --- a/src/gcrypt/meson.build +++ b/src/gcrypt/meson.build @@ -1,6 +1,5 @@ src_lib_crypto = files( 'cipher.c', - 'crypto.c', 'digest.c', 'pem.c', 'prf.c', diff --git a/src/have.h b/src/have.h index 5d99cc23..6c9d6754 100644 --- a/src/have.h +++ b/src/have.h @@ -125,11 +125,14 @@ #include #endif +#ifdef HAVE_SYS_RANDOM_H +#include +#endif + #ifdef HAVE_SYS_TIME_H #include #endif - #ifdef HAVE_SYS_TYPES_H #include #endif diff --git a/src/invitation.c b/src/invitation.c index 22fd6146..c008be2a 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -34,6 +34,7 @@ #include "tincctl.h" #include "utils.h" #include "xalloc.h" +#include "random.h" #include "ed25519/sha512.h" diff --git a/src/linux/meson.build b/src/linux/meson.build index 5725b4ad..0213d060 100644 --- a/src/linux/meson.build +++ b/src/linux/meson.build @@ -1,10 +1,14 @@ check_headers += [ 'linux/if_tun.h', - 'sys/epoll.h', 'netpacket/packet.h', + 'sys/epoll.h', + 'sys/random.h', ] -check_functions += 'recvmmsg' +check_functions += [ + 'recvmmsg', + 'getrandom', +] src_tincd += files('device.c') diff --git a/src/meson.build b/src/meson.build index d2b76cbe..d96dd71b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -106,6 +106,7 @@ src_lib_common = [ 'dropin.c', 'keys.c', 'list.c', + 'logger.c', 'names.c', 'netutl.c', 'script.c', @@ -115,7 +116,6 @@ src_lib_common = [ 'utils.c', 'version.c', 'xoshiro.c', - 'logger.c', ] src_tinc = [ @@ -163,6 +163,10 @@ deps_common = [] deps_tinc = [] deps_tincd = [cc.find_library('m', required: false)] +if os_name != 'windows' + src_lib_common += 'random.c' +endif + if os_name in ['linux', 'android'] subdir('linux') elif os_name.endswith('bsd') or os_name in ['dragonfly', 'darwin'] @@ -330,6 +334,10 @@ endif subdir(opt_crypto) +if opt_crypto != 'openssl' + src_lib_crypto += 'crypto.c' +endif + if opt_crypto != 'nolegacy' src_lib_crypto += ['cipher.c', 'digest.c'] endif diff --git a/src/net_packet.c b/src/net_packet.c index d171fec6..28459ce5 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -53,6 +53,7 @@ #include "protocol.h" #include "route.h" #include "utils.h" +#include "random.h" /* The minimum size of a probe is 14 bytes, but since we normally use CBC mode encryption, we can add a few extra random bytes without increasing the diff --git a/src/nolegacy/crypto.c b/src/nolegacy/crypto.c deleted file mode 100644 index 6965218b..00000000 --- a/src/nolegacy/crypto.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - crypto.c -- Cryptographic miscellaneous functions and initialisation - Copyright (C) 2007-2021 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "../system.h" - -#include "../crypto.h" - -#ifndef HAVE_WINDOWS - -static int random_fd = -1; - -static void random_init(void) { - random_fd = open("/dev/urandom", O_RDONLY); - - if(random_fd < 0) { - random_fd = open("/dev/random", O_RDONLY); - } - - if(random_fd < 0) { - fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno)); - abort(); - } -} - -static void random_exit(void) { - close(random_fd); -} - -void randomize(void *vout, size_t outlen) { - uint8_t *out = vout; - - while(outlen) { - ssize_t len = read(random_fd, out, outlen); - - if(len <= 0) { - if(len == -1 && (errno == EAGAIN || errno == EINTR)) { - continue; - } - - fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno)); - abort(); - } - - out += len; - outlen -= len; - } -} - -#else - -#include -HCRYPTPROV prov; - -static void random_init(void) { - if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - fprintf(stderr, "CryptAcquireContext() failed!\n"); - abort(); - } -} - -static void random_exit(void) { - CryptReleaseContext(prov, 0); -} - -void randomize(void *out, size_t outlen) { - if(!CryptGenRandom(prov, outlen, out)) { - fprintf(stderr, "CryptGenRandom() failed\n"); - abort(); - } -} - -#endif - -void crypto_init(void) { - random_init(); -} - -void crypto_exit(void) { - random_exit(); -} diff --git a/src/nolegacy/meson.build b/src/nolegacy/meson.build index 323a8314..c9ea62f4 100644 --- a/src/nolegacy/meson.build +++ b/src/nolegacy/meson.build @@ -1,7 +1,4 @@ -src_lib_crypto = files( - 'crypto.c', - 'prf.c', -) +src_lib_crypto = files('prf.c') dep_crypto = dependency('', required: false) diff --git a/src/openssl/crypto.c b/src/openssl/crypto.c index fe5a5997..3960c3e8 100644 --- a/src/openssl/crypto.c +++ b/src/openssl/crypto.c @@ -24,75 +24,7 @@ #include "../crypto.h" -#ifndef HAVE_WINDOWS - -static int random_fd = -1; - -static void random_init(void) { - random_fd = open("/dev/urandom", O_RDONLY); - - if(random_fd < 0) { - random_fd = open("/dev/random", O_RDONLY); - } - - if(random_fd < 0) { - fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno)); - abort(); - } -} - -static void random_exit(void) { - close(random_fd); -} - -void randomize(void *vout, size_t outlen) { - uint8_t *out = vout; - - while(outlen) { - ssize_t len = read(random_fd, out, outlen); - - if(len <= 0) { - if(len == -1 && (errno == EAGAIN || errno == EINTR)) { - continue; - } - - fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno)); - abort(); - } - - out += len; - outlen -= len; - } -} - -#else - -#include -HCRYPTPROV prov; - -static void random_init(void) { - if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - fprintf(stderr, "CryptAcquireContext() failed!\n"); - abort(); - } -} - -static void random_exit(void) { - CryptReleaseContext(prov, 0); -} - -void randomize(void *out, size_t outlen) { - if(!CryptGenRandom(prov, outlen, out)) { - fprintf(stderr, "CryptGenRandom() failed\n"); - abort(); - } -} - -#endif - void crypto_init(void) { - random_init(); - #if OPENSSL_VERSION_MAJOR < 3 ENGINE_load_builtin_engines(); #endif @@ -102,7 +34,3 @@ void crypto_init(void) { abort(); } } - -void crypto_exit(void) { - random_exit(); -} diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 4d6c9911..0e6ac5de 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -25,7 +25,6 @@ #include "control.h" #include "control_common.h" #include "cipher.h" -#include "crypto.h" #include "digest.h" #include "ecdsa.h" #include "edge.h" @@ -42,6 +41,7 @@ #include "sptps.h" #include "utils.h" #include "xalloc.h" +#include "random.h" #include "ed25519/sha512.h" #include "keys.h" diff --git a/src/protocol_key.c b/src/protocol_key.c index 1efeaa83..2796c7ed 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -32,6 +32,7 @@ #include "sptps.h" #include "utils.h" #include "compression.h" +#include "random.h" void send_key_changed(void) { #ifndef DISABLE_LEGACY diff --git a/src/random.c b/src/random.c new file mode 100644 index 00000000..fad7173f --- /dev/null +++ b/src/random.c @@ -0,0 +1,53 @@ +#include "system.h" + +#include "random.h" + +#ifndef HAVE_GETRANDOM +static int random_fd = -1; +#endif + +void random_init(void) { +#ifndef HAVE_GETRANDOM + random_fd = open("/dev/urandom", O_RDONLY); + + if(random_fd < 0) { + random_fd = open("/dev/random", O_RDONLY); + } + + if(random_fd < 0) { + fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno)); + abort(); + } + +#endif +} + +void random_exit(void) { +#ifndef HAVE_GETRANDOM + close(random_fd); +#endif +} + +void randomize(void *vout, size_t outlen) { + uint8_t *out = vout; + + while(outlen) { +#ifdef HAVE_GETRANDOM + ssize_t len = getrandom(out, outlen, 0); +#else + ssize_t len = read(random_fd, out, outlen); +#endif + + if(len <= 0) { + if(len == -1 && (errno == EAGAIN || errno == EINTR)) { + continue; + } + + fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno)); + abort(); + } + + out += len; + outlen -= len; + } +} diff --git a/src/random.h b/src/random.h new file mode 100644 index 00000000..15690d36 --- /dev/null +++ b/src/random.h @@ -0,0 +1,10 @@ +#ifndef TINC_RANDOM_H +#define TINC_RANDOM_H + +#include "system.h" + +extern void random_init(void); +extern void random_exit(void); +extern void randomize(void *vout, size_t outlen); + +#endif // TINC_RANDOM_H diff --git a/src/sptps.c b/src/sptps.c index 9fe93cc7..a0483c34 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -21,12 +21,12 @@ #include "system.h" #include "chacha-poly1305/chacha-poly1305.h" -#include "crypto.h" #include "ecdh.h" #include "ecdsa.h" #include "logger.h" #include "prf.h" #include "sptps.h" +#include "random.h" unsigned int sptps_replaywin = 16; diff --git a/src/sptps_keypair.c b/src/sptps_keypair.c index 7e47d06f..17d26f93 100644 --- a/src/sptps_keypair.c +++ b/src/sptps_keypair.c @@ -20,6 +20,7 @@ #include "system.h" #include "crypto.h" +#include "random.h" #include "ecdsagen.h" #include "logger.h" #include "names.h" @@ -49,40 +50,7 @@ static struct option const long_options[] = { {NULL, 0, NULL, 0} }; -int main(int argc, char *argv[]) { - program_name = argv[0]; - int r; - int option_index = 0; - - while((r = getopt_long(argc, argv, "", long_options, &option_index)) != EOF) { - switch(r) { - case 0: /* long option */ - break; - - case '?': /* wrong options */ - usage(); - return 1; - - case 1: /* help */ - usage(); - return 0; - - default: - break; - } - } - - argc -= optind - 1; - argv += optind - 1; - - if(argc != 3) { - fprintf(stderr, "Wrong number of arguments.\n"); - usage(); - return 1; - } - - crypto_init(); - +static int generate_keypair(char *argv[]) { ecdsa_t *key = ecdsa_generate(); if(!key) { @@ -121,3 +89,45 @@ int main(int argc, char *argv[]) { return 1; } } + +int main(int argc, char *argv[]) { + program_name = argv[0]; + int r; + int option_index = 0; + + while((r = getopt_long(argc, argv, "", long_options, &option_index)) != EOF) { + switch(r) { + case 0: /* long option */ + break; + + case '?': /* wrong options */ + usage(); + return 1; + + case 1: /* help */ + usage(); + return 0; + + default: + break; + } + } + + argc -= optind - 1; + argv += optind - 1; + + if(argc != 3) { + fprintf(stderr, "Wrong number of arguments.\n"); + usage(); + return 1; + } + + random_init(); + crypto_init(); + + int result = generate_keypair(argv); + + random_exit(); + + return result; +} diff --git a/src/sptps_speed.c b/src/sptps_speed.c index 53cff4df..c7c6e546 100644 --- a/src/sptps_speed.c +++ b/src/sptps_speed.c @@ -29,6 +29,7 @@ #include "meta.h" #include "protocol.h" #include "sptps.h" +#include "random.h" // Symbols necessary to link with logger.o bool send_request(struct connection_t *c, const char *msg, ...) { @@ -104,15 +105,13 @@ static bool clock_countto(double seconds) { return false; } -int main(int argc, char *argv[]) { +static int run_benchmark(int argc, char *argv[]) { ecdsa_t *key1, *key2; ecdh_t *ecdh1, *ecdh2; sptps_t sptps1, sptps2; uint8_t buf1[4096], buf2[4096], buf3[4096]; double duration = argc > 1 ? atof(argv[1]) : 10; - crypto_init(); - randomize(buf1, sizeof(buf1)); randomize(buf2, sizeof(buf2)); randomize(buf3, sizeof(buf3)); @@ -316,7 +315,17 @@ int main(int argc, char *argv[]) { close(fd[1]); ecdsa_free(key1); ecdsa_free(key2); - crypto_exit(); return 0; } + +int main(int argc, char *argv[]) { + random_init(); + crypto_init(); + + int result = run_benchmark(argc, argv); + + random_exit(); + + return result; +} diff --git a/src/sptps_test.c b/src/sptps_test.c index 50057e29..249f2e4f 100644 --- a/src/sptps_test.c +++ b/src/sptps_test.c @@ -30,6 +30,7 @@ #include "sptps.h" #include "utils.h" #include "names.h" +#include "random.h" #ifndef HAVE_WINDOWS #define closesocket(s) close(s) @@ -314,7 +315,7 @@ static void print_listening_msg(int sock) { fflush(stderr); } -int main(int argc, char *argv[]) { +static int run_test(int argc, char *argv[]) { program_name = argv[0]; bool initiator = false; bool datagram = false; @@ -523,9 +524,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Connected\n"); } - crypto_init(); - prng_init(); - FILE *fp = fopen(argv[1], "r"); if(!fp) { @@ -709,12 +707,19 @@ int main(int argc, char *argv[]) { free(mykey); free(hiskey); + closesocket(sock); - if(!stopped) { - return 1; - } + return !stopped; +} - closesocket(sock); +int main(int argc, char *argv[]) { + random_init(); + crypto_init(); + prng_init(); - return 0; + int result = run_test(argc, argv); + + random_exit(); + + return result; } diff --git a/src/tincctl.c b/src/tincctl.c index 5ae76d31..fe00912c 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -40,6 +40,7 @@ #include "version.h" #include "subnet.h" #include "keys.h" +#include "random.h" #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -3266,6 +3267,22 @@ static void cleanup(void) { free_names(); } +static int run_command(int argc, char *argv[]) { + if(optind >= argc) { + return cmd_shell(argc, argv); + } + + for(int i = 0; commands[i].command; i++) { + if(!strcasecmp(argv[optind], commands[i].command)) { + return commands[i].function(argc - optind, argv + optind); + } + } + + fprintf(stderr, "Unknown command `%s'.\n", argv[optind]); + usage(true); + return 1; +} + int main(int argc, char *argv[]) { program_name = argv[0]; orig_argv = argv; @@ -3301,20 +3318,13 @@ int main(int argc, char *argv[]) { #endif gettimeofday(&now, NULL); + random_init(); crypto_init(); prng_init(); - if(optind >= argc) { - return cmd_shell(argc, argv); - } + int result = run_command(argc, argv); - for(int i = 0; commands[i].command; i++) { - if(!strcasecmp(argv[optind], commands[i].command)) { - return commands[i].function(argc - optind, argv + optind); - } - } + random_exit(); - fprintf(stderr, "Unknown command `%s'.\n", argv[optind]); - usage(true); - return 1; + return result; } diff --git a/src/tincd.c b/src/tincd.c index 9850eebb..947e7b3e 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -54,6 +54,7 @@ #include "utils.h" #include "xalloc.h" #include "version.h" +#include "random.h" /* If nonzero, display usage information and exit. */ static bool show_help = false; @@ -487,6 +488,7 @@ int main(int argc, char **argv) { #endif gettimeofday(&now, NULL); + random_init(); crypto_init(); prng_init(); @@ -619,7 +621,7 @@ end: free(priority); - crypto_exit(); + random_exit(); return status; } diff --git a/src/windows/meson.build b/src/windows/meson.build index 8766bc1b..b78b901d 100644 --- a/src/windows/meson.build +++ b/src/windows/meson.build @@ -14,5 +14,6 @@ foreach libname : win_common_libs deps_common += dep endforeach +src_lib_common += files('random.c') src_tincd += files('device.c') diff --git a/src/windows/random.c b/src/windows/random.c new file mode 100644 index 00000000..69c8cba5 --- /dev/null +++ b/src/windows/random.c @@ -0,0 +1,25 @@ +#include "../system.h" + +#include + +#include "../random.h" + +static HCRYPTPROV prov; + +void random_init(void) { + if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + fprintf(stderr, "CryptAcquireContext() failed!\n"); + abort(); + } +} + +void random_exit(void) { + CryptReleaseContext(prov, 0); +} + +void randomize(void *vout, size_t outlen) { + if(!CryptGenRandom(prov, outlen, vout)) { + fprintf(stderr, "CryptGenRandom() failed\n"); + abort(); + } +} diff --git a/src/xoshiro.c b/src/xoshiro.c index 481c2d52..11eaf5ba 100644 --- a/src/xoshiro.c +++ b/src/xoshiro.c @@ -9,6 +9,7 @@ See . */ #include "system.h" #include "crypto.h" +#include "random.h" /* This is xoshiro256** 1.0, one of our all-purpose, rock-solid generators. It has excellent (sub-ns) speed, a state (256 bits) that is diff --git a/test/unit/meson.build b/test/unit/meson.build index 0240dc96..d83a080a 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -17,9 +17,17 @@ link_tincd = { 'lib': lib_tincd, 'dep': deps_tincd } # 'code': 'test1.c', // or ['test1.c', 'test1_util.c'] # 'mock': ['foo', 'bar'], // list of functions to mock (default: empty) # 'link': link_tinc, // which binary to link with (default: tincd) +# 'fail': true, // whether the test should fail (default: false) # } tests = { + 'random': { + 'code': 'test_random.c', + }, + 'random_noinit': { + 'code': 'test_random_noinit.c', + 'fail': true, + }, 'net': { 'code': 'test_net.c', 'mock': ['execute_script', 'environment_init', 'environment_exit'], @@ -59,11 +67,14 @@ foreach test, data : tests include_directories: inc_conf, build_by_default: false) + must_fail = data.get('fail', false) + test(test, exe, suite: 'unit', timeout: 60, - protocol: 'tap', - env: env) + protocol: must_fail ? 'exitcode' : 'tap', + env: env, + should_fail: must_fail) endforeach diff --git a/test/unit/test_random.c b/test/unit/test_random.c new file mode 100644 index 00000000..723257be --- /dev/null +++ b/test/unit/test_random.c @@ -0,0 +1,88 @@ +#include "unittest.h" +#include "../../src/random.h" +#include "../../src/xalloc.h" + +static int setup(void **state) { + (void)state; + random_init(); + return 0; +} + +static int teardown(void **state) { + (void)state; + random_exit(); + return 0; +} + +#define zerolen 128 +static const uint8_t zero[zerolen] = {0}; + +static void test_randomize_zero_must_not_change_memory(void **state) { + (void)state; + + uint8_t buf[zerolen] = {0}; + randomize(buf, 0); + + assert_memory_equal(zero, buf, sizeof(buf)); +} + +static void test_randomize_does_not_overflow(void **state) { + (void)state; + + uint8_t buf[zerolen] = {0}; + const size_t half = sizeof(buf) / 2; + randomize(buf, half); + + assert_memory_not_equal(zero, buf, half); + assert_memory_equal(zero, &buf[half], half); +} + +static void test_randomize_full_changes_memory(void **state) { + (void)state; + + uint8_t buf[zerolen] = {0}; + randomize(buf, sizeof(buf)); + + assert_memory_not_equal(zero, buf, sizeof(buf)); +} + +static void test_randomize_does_not_repeat(void **state) { + (void)state; + + // Ask randomize() for small chunks so there's more + // chance for it to repeat itself (within reason). +#define chunklen 16 + + const size_t chunks = 1024; + uint8_t (*buffers)[chunklen] = xzalloc(chunks * chunklen); + + // Fill buffers with (hopefully) random data + for(size_t i = 0; i < chunks; ++i) { + randomize(buffers[i], chunklen); + + // Check there was no overflow to the right + if(i < chunks - 1) { + assert_memory_equal(zero, buffers[i + 1], chunklen); + } + } + + // Check there were no repetitions (with 128-bit buffers collisions are very unlikely) + for(size_t i = 0; i < chunks - 1; ++i) { + for(size_t j = i + 1; j < chunks; ++j) { + assert_memory_not_equal(buffers[i], buffers[j], chunklen); + } + } + + free(buffers); +#undef chunklen +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_randomize_zero_must_not_change_memory), + cmocka_unit_test(test_randomize_does_not_overflow), + cmocka_unit_test(test_randomize_full_changes_memory), + cmocka_unit_test(test_randomize_does_not_repeat), + }; + return cmocka_run_group_tests(tests, setup, teardown); +} diff --git a/test/unit/test_random_noinit.c b/test/unit/test_random_noinit.c new file mode 100644 index 00000000..d02f4532 --- /dev/null +++ b/test/unit/test_random_noinit.c @@ -0,0 +1,23 @@ +// Test that randomize() kills the process when called without initialization + +#include "unittest.h" + +#ifdef HAVE_GETRANDOM +int main(void) { + return 1; +} +#else +#include "../../src/random.h" + +static void on_abort(int sig) { + (void)sig; + exit(1); +} + +int main(void) { + signal(SIGABRT, on_abort); + u_int8_t buf[16]; + randomize(buf, sizeof(buf)); + return 0; +} +#endif // HAVE_GETRANDOM -- 2.20.1