diff options
| author | Michael Biebl <biebl@debian.org> | 2023-06-12 11:25:29 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2023-06-12 11:25:29 +0200 |
| commit | 150fe9eef8dd22307ee16687509acde616663982 (patch) | |
| tree | 200fa1178c9aeee2ff874bb63afe93623da4be0d /src/libnm-glib-aux | |
| parent | dfaaf221cfee4ffcc507d793dae051d402646679 (diff) | |
New upstream version 1.42.6 upstream/1.42.6
Diffstat (limited to 'src/libnm-glib-aux')
| -rw-r--r-- | src/libnm-glib-aux/nm-random-utils.c | 68 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-random-utils.h | 35 | ||||
| -rw-r--r-- | src/libnm-glib-aux/tests/test-shared-general.c | 50 |
3 files changed, 145 insertions, 8 deletions
diff --git a/src/libnm-glib-aux/nm-random-utils.c b/src/libnm-glib-aux/nm-random-utils.c index 93eee7c4..2050d2f9 100644 --- a/src/libnm-glib-aux/nm-random-utils.c +++ b/src/libnm-glib-aux/nm-random-utils.c @@ -187,9 +187,9 @@ _bad_random_init_seed(BadRandSeed *seed) int seed_idx; GRand *rand; - /* g_rand_new() reads /dev/urandom, but we already noticed that + /* g_rand_new() reads /dev/urandom too, but we already know that * /dev/urandom fails to give us good randomness (which is why - * we hit the "bad randomness" code path). So this may not be as + * we hit the "bad random" code path). So this may not be as * good as we wish, but let's hope that it it does something smart * to give some extra entropy... */ rand = g_rand_new(); @@ -231,14 +231,14 @@ _bad_random_bytes(guint8 *buf, gsize n) * to give us good randomness. Try our best. * * Our ability to get entropy for the CPRNG is very limited and thus the overall - * result will not be good randomness. See _bad_random_init_seed(). + * result will be bad randomness. * * Once we have some seed material, we combine GRand (which is not a cryptographically * secure PRNG) with some iterative sha256 hashing. It would be nice if we had * easy access to chacha20, but it's probably more cumbersome to fork those * implementations than hack a bad CPRNG by using sha256 hashing. After all, this - * is fallback code to get *some* randomness. And with the inability to get a good - * seed, the CPRNG is not going to give us truly good randomness. */ + * is fallback code to get *some* bad randomness. And with the inability to get a good + * seed, any CPRNG can only give us bad randomness. */ { static BadRandState gl_state; @@ -277,11 +277,11 @@ _bad_random_bytes(guint8 *buf, gsize n) nm_utils_checksum_get_digest(sum, gl_state.sha_digest.full); /* gl_state.sha_digest.full and gl_state.rand_vals contain now our - * random values, but they are also the state for the next iteration. + * bad random values, but they are also the state for the next iteration. * We must not directly expose that state to the caller, so XOR the values. * - * That means, per iteration we can generate 16 bytes of randomness. That - * is for example required to generate a random UUID. */ + * That means, per iteration we can generate 16 bytes of bad randomness. That + * is suitable to initialize a random UUID. */ for (i = 0; i < (int) (NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2); i++) { nm_assert(n > 0); buf[0] = gl_state.sha_digest.half_1[i] ^ gl_state.sha_digest.half_2[i] @@ -447,3 +447,55 @@ again_getrandom: return nm_utils_fd_read_loop_exact(fd, p, n, FALSE); } + +/*****************************************************************************/ + +guint64 +nm_random_u64_range_full(guint64 begin, guint64 end, gboolean crypto_bytes) +{ + gboolean bad_crypto_bytes = FALSE; + guint64 remainder; + guint64 maxvalue; + guint64 x; + guint64 m; + + /* Returns a random #guint64 equally distributed in the range [@begin..@end-1]. + * + * The function always set errno. It either sets it to zero or to EAGAIN + * (if crypto_bytes were requested but not obtained). In any case, the function + * will always return a random number in the requested range (worst case, it's + * not crypto_bytes despite being requested). Check errno if you care. */ + + if (begin >= end) { + /* systemd's random_u64_range(0) is an alias for nm_random_u64(). + * Not for us. It's a caller error to request an element from an empty range. */ + return nm_assert_unreachable_val(begin); + } + + m = end - begin; + + if (m == 1) { + x = 0; + goto out; + } + + remainder = G_MAXUINT64 % m; + maxvalue = G_MAXUINT64 - remainder; + + do + if (crypto_bytes) { + if (nm_random_get_crypto_bytes(&x, sizeof(x)) < 0) { + /* Cannot get good crypto numbers. We will try our best, but fail + * and set errno below. */ + crypto_bytes = FALSE; + bad_crypto_bytes = TRUE; + continue; + } + } else + nm_random_get_bytes(&x, sizeof(x)); + while (x >= maxvalue); + +out: + errno = bad_crypto_bytes ? EAGAIN : 0; + return begin + (x % m); +} diff --git a/src/libnm-glib-aux/nm-random-utils.h b/src/libnm-glib-aux/nm-random-utils.h index ab8aee1b..729d71a4 100644 --- a/src/libnm-glib-aux/nm-random-utils.h +++ b/src/libnm-glib-aux/nm-random-utils.h @@ -16,4 +16,39 @@ nm_random_get_bytes(void *p, size_t n) int nm_random_get_crypto_bytes(void *p, size_t n); +static inline guint32 +nm_random_u32(void) +{ + guint32 v; + + nm_random_get_bytes(&v, sizeof(v)); + return v; +} + +static inline guint64 +nm_random_u64(void) +{ + guint64 v; + + nm_random_get_bytes(&v, sizeof(v)); + return v; +} + +static inline bool +nm_random_bool(void) +{ + guint8 ch; + + nm_random_get_bytes(&ch, sizeof(ch)); + return ch % 2u; +} + +guint64 nm_random_u64_range_full(guint64 begin, guint64 end, gboolean crypto_bytes); + +static inline guint64 +nm_random_u64_range(guint64 end) +{ + return nm_random_u64_range_full(0, end, FALSE); +} + #endif /* __NM_RANDOM_UTILS_H__ */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 7503dc9b..3eaca547 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -137,6 +137,55 @@ test_nmhash(void) /*****************************************************************************/ +static void +test_nm_random(void) +{ + int i_run; + + for (i_run = 0; i_run < 1000; i_run++) { + guint64 begin; + guint64 end; + guint64 m; + guint64 x; + + m = nmtst_get_rand_uint64(); + m = m >> (nmtst_get_rand_uint32() % 64); + + if (m == 0) + continue; + + switch (nmtst_get_rand_uint32() % 4) { + case 0: + begin = 0; + break; + case 1: + begin = nmtst_get_rand_uint64() % 1000; + break; + case 2: + begin = ((G_MAXUINT64 - m) - 500) + (nmtst_get_rand_uint64() % 1000); + break; + default: + begin = nmtst_get_rand_uint64() % (G_MAXUINT64 - m); + break; + } + + end = (begin + m) - 10 + (nmtst_get_rand_uint64() % 5); + + if (begin >= end) + continue; + + if (begin == 0 && nmtst_get_rand_bool()) + x = nm_random_u64_range(end); + else + x = nm_random_u64_range_full(begin, end, nmtst_get_rand_bool()); + + g_assert_cmpuint(x, >=, begin); + g_assert_cmpuint(x, <, end); + } +} + +/*****************************************************************************/ + static const char * _make_strv_foo(void) { @@ -2417,6 +2466,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_inet_utils", test_inet_utils); g_test_add_func("/general/test_garray", test_garray); g_test_add_func("/general/test_nm_prioq", test_nm_prioq); + g_test_add_func("/general/test_nm_random", test_nm_random); return g_test_run(); } |