Refactor crypto RNG; add getrandom() support
authorKirill Isakov <bootctl@gmail.com>
Tue, 12 Apr 2022 16:20:58 +0000 (22:20 +0600)
committerGuus Sliepen <guus@tinc-vpn.org>
Tue, 12 Apr 2022 17:42:11 +0000 (19:42 +0200)
/dev/urandom and /dev/random are ubiquitous, but take an open
file descriptor, and may not actually be present inside badly
configured containers.

30 files changed:
src/control.c
src/crypto.c [moved from src/gcrypt/crypto.c with 81% similarity]
src/crypto.h
src/ed25519/ecdh.c
src/ed25519/ecdsagen.c
src/gcrypt/meson.build
src/have.h
src/invitation.c
src/linux/meson.build
src/meson.build
src/net_packet.c
src/nolegacy/crypto.c [deleted file]
src/nolegacy/meson.build
src/openssl/crypto.c
src/protocol_auth.c
src/protocol_key.c
src/random.c [new file with mode: 0644]
src/random.h [new file with mode: 0644]
src/sptps.c
src/sptps_keypair.c
src/sptps_speed.c
src/sptps_test.c
src/tincctl.c
src/tincd.c
src/windows/meson.build
src/windows/random.c [new file with mode: 0644]
src/xoshiro.c
test/unit/meson.build
test/unit/test_random.c [new file with mode: 0644]
test/unit/test_random_noinit.c [new file with mode: 0644]

index 86e0f68..4c0ab51 100644 (file)
@@ -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];
 
similarity index 81%
rename from src/gcrypt/crypto.c
rename to src/crypto.c
index cf5d0e6..20d917d 100644 (file)
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "../system.h"
+#include "crypto.h"
 
-#include <gcrypt.h>
-
-#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) {}
index 9fc4156..ac96ea8 100644 (file)
 */
 
 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;
index 302fafd..469f502 100644 (file)
@@ -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"
 
index 06b41c8..bc14fd2 100644 (file)
@@ -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
 
index ac93c80..9cfe466 100644 (file)
@@ -1,6 +1,5 @@
 src_lib_crypto = files(
   'cipher.c',
-  'crypto.c',
   'digest.c',
   'pem.c',
   'prf.c',
index 5d99cc2..6c9d675 100644 (file)
 #include <syslog.h>
 #endif
 
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
 
-
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
index 22fd614..c008be2 100644 (file)
@@ -34,6 +34,7 @@
 #include "tincctl.h"
 #include "utils.h"
 #include "xalloc.h"
+#include "random.h"
 
 #include "ed25519/sha512.h"
 
index 5725b4a..0213d06 100644 (file)
@@ -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')
 
index d2b76cb..d96dd71 100644 (file)
@@ -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
index d171fec..28459ce 100644 (file)
@@ -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 (file)
index 6965218..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-    crypto.c -- Cryptographic miscellaneous functions and initialisation
-    Copyright (C) 2007-2021 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 <wincrypt.h>
-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();
-}
index 323a831..c9ea62f 100644 (file)
@@ -1,7 +1,4 @@
-src_lib_crypto = files(
-  'crypto.c',
-  'prf.c',
-)
+src_lib_crypto = files('prf.c')
 
 dep_crypto = dependency('', required: false)
 
index fe5a599..3960c3e 100644 (file)
 
 #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 <wincrypt.h>
-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();
-}
index 4d6c991..0e6ac5d 100644 (file)
@@ -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"
index 1efeaa8..2796c7e 100644 (file)
@@ -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 (file)
index 0000000..fad7173
--- /dev/null
@@ -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 (file)
index 0000000..15690d3
--- /dev/null
@@ -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
index 9fe93cc..a0483c3 100644 (file)
 #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;
 
index 7e47d06..17d26f9 100644 (file)
@@ -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;
+}
index 53cff4d..c7c6e54 100644 (file)
@@ -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;
+}
index 50057e2..249f2e4 100644 (file)
@@ -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;
 }
index 5ae76d3..fe00912 100644 (file)
@@ -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;
 }
index 9850eeb..947e7b3 100644 (file)
@@ -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;
 }
index 8766bc1..b78b901 100644 (file)
@@ -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 (file)
index 0000000..69c8cba
--- /dev/null
@@ -0,0 +1,25 @@
+#include "../system.h"
+
+#include <wincrypt.h>
+
+#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();
+       }
+}
index 481c2d5..11eaf5b 100644 (file)
@@ -9,6 +9,7 @@ See <http://creativecommons.org/publicdomain/zero/1.0/>. */
 #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
index 0240dc9..d83a080 100644 (file)
@@ -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 (file)
index 0000000..723257b
--- /dev/null
@@ -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 (file)
index 0000000..d02f453
--- /dev/null
@@ -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