Git fork

wrapper: allow generating insecure random bytes

The `csprng_bytes()` function generates randomness and writes it into a
caller-provided buffer. It abstracts over a couple of implementations,
where the exact one that is used depends on the platform.

These implementations have different guarantees: while some guarantee to
never fail (arc4random(3)), others may fail. There are two significant
failures to distinguish from one another:

- Systemic failure, where e.g. opening "/dev/urandom" fails or when
OpenSSL doesn't have a provider configured.

- Entropy failure, where the entropy pool is exhausted, and thus the
function cannot guarantee strong cryptographic randomness.

While we cannot do anything about the former, the latter failure can be
acceptable in some situations where we don't care whether or not the
randomness can be predicted.

Introduce a new `CSPRNG_BYTES_INSECURE` flag that allows callers to opt
into weak cryptographic randomness. The exact behaviour of the flag
depends on the underlying implementation:

- `arc4random_buf()` never returns an error, so it doesn't change.

- `getrandom()` pulls from "/dev/urandom" by default, which never
blocks on modern systems even when the entropy pool is empty.

- `getentropy()` seems to block when there is not enough randomness
available, and there is no way of changing that behaviour.

- `GtlGenRandom()` doesn't mention anything about its specific
failure mode.

- The fallback reads from "/dev/urandom", which also returns bytes in
case the entropy pool is drained in modern Linux systems.

That only leaves OpenSSL with `RAND_bytes()`, which returns an error in
case the returned data wouldn't be cryptographically safe. This function
is replaced with a call to `RAND_pseudo_bytes()`, which can indicate
whether or not the returned data is cryptographically secure via its
return value. If it is insecure, and if the `CSPRNG_BYTES_INSECURE` flag
is set, then we ignore the insecurity and return the data regardless.

It is somewhat questionable whether we really need the flag in the first
place, or whether we wouldn't just ignore the potentially-insecure data.
But the risk of doing that is that we might have or grow callsites that
aren't aware of the potential insecureness of the data in places where
it really matters. So using a flag to opt-in to that behaviour feels
like the more secure choice.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
1568d156 b74ff38a

+32 -20
+1 -1
builtin/gc.c
··· 1909 1909 if (getenv("GIT_TEST_MAINT_SCHEDULER")) 1910 1910 return 13; 1911 1911 1912 - return git_rand() % 60; 1912 + return git_rand(0) % 60; 1913 1913 } 1914 1914 1915 1915 static int is_launchctl_available(void)
+1 -1
reftable/stack.c
··· 659 659 static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max) 660 660 { 661 661 char buf[100]; 662 - uint32_t rnd = (uint32_t)git_rand(); 662 + uint32_t rnd = (uint32_t)git_rand(0); 663 663 snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x", 664 664 min, max, rnd); 665 665 reftable_buf_reset(dest);
+1 -1
t/helper/test-csprng.c
··· 15 15 16 16 while (count) { 17 17 unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf); 18 - if (csprng_bytes(buf, chunk) < 0) { 18 + if (csprng_bytes(buf, chunk, 0) < 0) { 19 19 perror("failed to read"); 20 20 return 5; 21 21 }
+3 -3
t/unit-tests/t-reftable-readwrite.c
··· 108 108 hash, to ensure that the compressed part is larger than the original. 109 109 */ 110 110 for (i = 0; i < REFTABLE_HASH_SIZE_SHA1; i++) { 111 - log.value.update.old_hash[i] = (uint8_t)(git_rand() % 256); 112 - log.value.update.new_hash[i] = (uint8_t)(git_rand() % 256); 111 + log.value.update.old_hash[i] = (uint8_t)(git_rand(0) % 256); 112 + log.value.update.new_hash[i] = (uint8_t)(git_rand(0) % 256); 113 113 } 114 114 reftable_writer_set_limits(w, update_index, update_index); 115 115 err = reftable_writer_add_log(w, &log); ··· 325 325 }; 326 326 327 327 for (i = 0; i < sizeof(message) - 1; i++) 328 - message[i] = (uint8_t)(git_rand() % 64 + ' '); 328 + message[i] = (uint8_t)(git_rand(0) % 64 + ' '); 329 329 330 330 reftable_writer_set_limits(w, 1, 1); 331 331
+14 -10
wrapper.c
··· 479 479 for (count = 0; count < TMP_MAX; ++count) { 480 480 int i; 481 481 uint64_t v; 482 - if (csprng_bytes(&v, sizeof(v)) < 0) 482 + if (csprng_bytes(&v, sizeof(v), 0) < 0) 483 483 return error_errno("unable to get random bytes for temporary file"); 484 484 485 485 /* Fill in the random bits. */ ··· 750 750 #endif 751 751 } 752 752 753 - int csprng_bytes(void *buf, size_t len) 753 + int csprng_bytes(void *buf, size_t len, MAYBE_UNUSED unsigned flags) 754 754 { 755 755 #if defined(HAVE_ARC4RANDOM) || defined(HAVE_ARC4RANDOM_LIBBSD) 756 756 /* This function never returns an error. */ ··· 785 785 return -1; 786 786 return 0; 787 787 #elif defined(HAVE_OPENSSL_CSPRNG) 788 - int res = RAND_bytes(buf, len); 789 - if (res == 1) 788 + switch (RAND_pseudo_bytes(buf, len)) { 789 + case 1: 790 790 return 0; 791 - if (res == -1) 792 - errno = ENOTSUP; 793 - else 791 + case 0: 792 + if (flags & CSPRNG_BYTES_INSECURE) 793 + return 0; 794 794 errno = EIO; 795 - return -1; 795 + return -1; 796 + default: 797 + errno = ENOTSUP; 798 + return -1; 799 + } 796 800 #else 797 801 ssize_t res; 798 802 char *p = buf; ··· 816 820 #endif 817 821 } 818 822 819 - uint32_t git_rand(void) 823 + uint32_t git_rand(unsigned flags) 820 824 { 821 825 uint32_t result; 822 826 823 - if (csprng_bytes(&result, sizeof(result)) < 0) 827 + if (csprng_bytes(&result, sizeof(result), flags) < 0) 824 828 die(_("unable to get random bytes")); 825 829 826 830 return result;
+12 -4
wrapper.h
··· 127 127 128 128 void sleep_millisec(int millisec); 129 129 130 + enum { 131 + /* 132 + * Accept insecure bytes, which some CSPRNG implementations may return 133 + * in case the entropy pool has been exhausted. 134 + */ 135 + CSPRNG_BYTES_INSECURE = (1 << 0), 136 + }; 137 + 130 138 /* 131 139 * Generate len bytes from the system cryptographically secure PRNG. 132 140 * Returns 0 on success and -1 on error, setting errno. The inability to 133 - * satisfy the full request is an error. 141 + * satisfy the full request is an error. Accepts CSPRNG flags. 134 142 */ 135 - int csprng_bytes(void *buf, size_t len); 143 + int csprng_bytes(void *buf, size_t len, unsigned flags); 136 144 137 145 /* 138 146 * Returns a random uint32_t, uniformly distributed across all possible 139 - * values. 147 + * values. Accepts CSPRNG flags. 140 148 */ 141 - uint32_t git_rand(void); 149 + uint32_t git_rand(unsigned flags); 142 150 143 151 /* Provide log2 of the given `size_t`. */ 144 152 static inline unsigned log2u(uintmax_t sz)