|
@@ -43,6 +43,7 @@
|
|
|
#include <ctype.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
+#define _GNU_SOURCE
|
|
|
#include <unistd.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_FCNTL_H
|
|
@@ -51,6 +52,9 @@
|
|
|
#ifdef HAVE_SYS_FCNTL_H
|
|
|
#include <sys/fcntl.h>
|
|
|
#endif
|
|
|
+#ifdef HAVE_SYS_SYSCALL_H
|
|
|
+#include <sys/syscall.h>
|
|
|
+#endif
|
|
|
|
|
|
#include "torlog.h"
|
|
|
#include "aes.h"
|
|
@@ -68,6 +72,9 @@
|
|
|
|
|
|
#define MAX_DNS_LABEL_SIZE 63
|
|
|
|
|
|
+
|
|
|
+#define MAX_STRONGEST_RAND_SIZE 256
|
|
|
+
|
|
|
|
|
|
#define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n)
|
|
|
|
|
@@ -2344,23 +2351,18 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
|
|
|
}
|
|
|
|
|
|
|
|
|
- * storing it into <b>out</b>. Return -1 on success, 0 on failure.
|
|
|
+ * via system calls, storing it into <b>out</b>. Return -1 on success, 0 on
|
|
|
+ * failure. A maximum request size of 256 bytes is imposed.
|
|
|
*/
|
|
|
-int
|
|
|
-crypto_strongest_rand(uint8_t *out, size_t out_len)
|
|
|
+static int
|
|
|
+crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
|
|
|
{
|
|
|
-#ifdef _WIN32
|
|
|
+ tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
|
|
|
+
|
|
|
+#if defined(_WIN32)
|
|
|
static int provider_set = 0;
|
|
|
static HCRYPTPROV provider;
|
|
|
-#else
|
|
|
- static const char *filenames[] = {
|
|
|
- "/dev/srandom", "/dev/urandom", "/dev/random", NULL
|
|
|
- };
|
|
|
- int fd, i;
|
|
|
- size_t n;
|
|
|
-#endif
|
|
|
|
|
|
-#ifdef _WIN32
|
|
|
if (!provider_set) {
|
|
|
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
|
|
|
CRYPT_VERIFYCONTEXT)) {
|
|
@@ -2375,7 +2377,79 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
+#elif defined(__linux__) && defined(SYS_getrandom)
|
|
|
+ static int getrandom_works = 1;
|
|
|
+
|
|
|
+
|
|
|
+ * no glibc wrapper.
|
|
|
+ *
|
|
|
+ * As far as I can tell from getrandom(2) and the source code, the
|
|
|
+ * requests we issue will always succeed (though it will block on the
|
|
|
+ * call if /dev/urandom isn't seeded yet), since we are NOT specifying
|
|
|
+ * GRND_NONBLOCK and the request is <= 256 bytes.
|
|
|
+ *
|
|
|
+ * The manpage is unclear on what happens if a signal interrupts the call
|
|
|
+ * while the request is blocked due to lack of entropy....
|
|
|
+ *
|
|
|
+ * We optimistically assume that getrandom() is available and functional
|
|
|
+ * because it is the way of the future, and 2 branch mispredicts pale in
|
|
|
+ * comparision to the overheads involved with failing to open
|
|
|
+ * /dev/srandom followed by opening and reading from /dev/urandom.
|
|
|
+ */
|
|
|
+ if (PREDICT_LIKELY(getrandom_works)) {
|
|
|
+ int ret;
|
|
|
+ do {
|
|
|
+
|
|
|
+ * block if insufficient entropy is available to service the
|
|
|
+ * request.
|
|
|
+ */
|
|
|
+ ret = syscall(SYS_getrandom, out, out_len, 0);
|
|
|
+ } while (ret == -1 && ((errno == EINTR) ||(errno == EAGAIN)));
|
|
|
+
|
|
|
+ if (PREDICT_UNLIKELY(ret == -1)) {
|
|
|
+ tor_assert(errno != EAGAIN);
|
|
|
+ tor_assert(errno != EINTR);
|
|
|
+
|
|
|
+
|
|
|
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom().");
|
|
|
+ getrandom_works = 0;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_assert(ret == (int)out_len);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+#elif defined(HAVE_GETENTROPY)
|
|
|
+
|
|
|
+ * the only gotcha is that requests are limited to 256 bytes.
|
|
|
+ */
|
|
|
+ return getentropy(out, out_len);
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * via the per-platform fallback mechanism, storing it into <b>out</b>.
|
|
|
+ * Return -1 on success, 0 on failure. A maximum request size of 256 bytes
|
|
|
+ * is imposed.
|
|
|
+ */
|
|
|
+static int
|
|
|
+crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
|
|
|
+{
|
|
|
+#ifdef _WIN32
|
|
|
+
|
|
|
+ return -1;
|
|
|
#else
|
|
|
+ static const char *filenames[] = {
|
|
|
+ "/dev/srandom", "/dev/urandom", "/dev/random", NULL
|
|
|
+ };
|
|
|
+ int fd, i;
|
|
|
+ size_t n;
|
|
|
+
|
|
|
for (i = 0; filenames[i]; ++i) {
|
|
|
log_debug(LD_FS, "Opening %s for entropy", filenames[i]);
|
|
|
fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0);
|
|
@@ -2393,11 +2467,57 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- log_warn(LD_CRYPTO, "Cannot get strong entropy: no entropy source found.");
|
|
|
return -1;
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * storing it into <b>out</b>. Return -1 on success, 0 on failure. A maximum
|
|
|
+ * request size of 256 bytes is imposed.
|
|
|
+ */
|
|
|
+int
|
|
|
+crypto_strongest_rand(uint8_t *out, size_t out_len)
|
|
|
+{
|
|
|
+ static const size_t sanity_min_size = 16;
|
|
|
+ static const int max_attempts = 3;
|
|
|
+ tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
|
|
|
+
|
|
|
+
|
|
|
+ * zero filling the buffer and ensuring that it actually was at least
|
|
|
+ * partially modified.
|
|
|
+ *
|
|
|
+ * Checking that any individual byte is non-zero seems like it would
|
|
|
+ * fail too often (p = out_len * 1/256) for comfort, but this is an
|
|
|
+ * "adjust according to taste" sort of check.
|
|
|
+ */
|
|
|
+ memwipe(out, 0, out_len);
|
|
|
+ for (int i = 0; i < max_attempts; i++) {
|
|
|
+
|
|
|
+ if (crypto_strongest_rand_syscall(out, out_len) != 0) {
|
|
|
+
|
|
|
+ if (crypto_strongest_rand_fallback(out, out_len) != 0) {
|
|
|
+
|
|
|
+ * since we're basically boned without good entropy.
|
|
|
+ */
|
|
|
+ log_warn(LD_CRYPTO,
|
|
|
+ "Cannot get strong entropy: no entropy source found.");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((out_len < sanity_min_size) || !tor_mem_is_zero((char*)out, out_len))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * and each time it returned all '0's. Either the system entropy
|
|
|
+ * source is busted, or the user should go out and buy a ticket to
|
|
|
+ * every lottery on the planet.
|
|
|
+ */
|
|
|
+ log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer.");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* system. Return 0 on success, -1 on failure.
|
|
|
*/
|