]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/random-util.c
ece0e419437f57e44f6218b514e84163daed3578
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <linux/random.h>
9 #include <sys/random.h>
13 #include "alloc-util.h"
17 #include "iovec-util.h"
19 #include "parse-util.h"
20 #include "pidfd-util.h"
21 #include "process-util.h"
22 #include "random-util.h"
24 #include "time-util.h"
26 /* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by
27 * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a
28 * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least
29 * better than libc rand(). */
30 static void fallback_random_bytes(void *p
, size_t n
) {
31 static thread_local
uint64_t fallback_counter
= 0;
34 uint64_t call_id
, block_id
;
35 usec_t stamp_mono
, stamp_real
;
40 /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
41 .call_id
= fallback_counter
++,
42 .stamp_mono
= now(CLOCK_MONOTONIC
),
43 .stamp_real
= now(CLOCK_REALTIME
),
44 .pid
= getpid_cached(),
48 memcpy(state
.label
, "systemd fallback random bytes v1", sizeof(state
.label
));
49 memcpy(state
.auxval
, ULONG_TO_PTR(getauxval(AT_RANDOM
)), sizeof(state
.auxval
));
50 (void) pidfd_get_inode_id_self_cached(&state
.pidfdid
);
53 struct sha256_ctx ctx
;
55 sha256_init_ctx(&ctx
);
56 sha256_process_bytes(&state
, sizeof(state
), &ctx
);
57 if (n
< SHA256_DIGEST_SIZE
) {
58 uint8_t partial
[SHA256_DIGEST_SIZE
];
59 sha256_finish_ctx(&ctx
, partial
);
60 memcpy(p
, partial
, n
);
63 sha256_finish_ctx(&ctx
, p
);
64 p
= (uint8_t *) p
+ SHA256_DIGEST_SIZE
;
65 n
-= SHA256_DIGEST_SIZE
;
70 void random_bytes(void *p
, size_t n
) {
71 static bool have_grndinsecure
= true;
81 l
= getrandom(p
, n
, have_grndinsecure
? GRND_INSECURE
: GRND_NONBLOCK
);
82 if (l
< 0 && errno
== EINVAL
&& have_grndinsecure
) {
83 /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
84 have_grndinsecure
= false;
88 break; /* Will block (with GRND_NONBLOCK), or unexpected error. Give up and fallback
92 return; /* Done reading, success. */
94 p
= (uint8_t *) p
+ l
;
96 /* Interrupted by a signal; keep going. */
99 _cleanup_close_
int fd
= open("/dev/urandom", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
100 if (fd
>= 0 && loop_read_exact(fd
, p
, n
, false) >= 0)
103 /* This is a terrible fallback. Oh well. */
104 fallback_random_bytes(p
, n
);
107 int crypto_random_bytes(void *p
, size_t n
) {
116 l
= getrandom(p
, n
, 0);
120 return -EIO
; /* Weird, should never happen. */
123 return 0; /* Done reading, success. */
125 p
= (uint8_t *) p
+ l
;
127 /* Interrupted by a signal; keep going. */
131 int crypto_random_bytes_allocate_iovec(size_t n
, struct iovec
*ret
) {
132 _cleanup_free_
void *p
= NULL
;
137 p
= malloc(MAX(n
, 1U));
141 r
= crypto_random_bytes(p
, n
);
145 *ret
= IOVEC_MAKE(TAKE_PTR(p
), n
);
149 size_t random_pool_size(void) {
150 _cleanup_free_
char *s
= NULL
;
153 /* Read pool size, if possible */
154 r
= read_one_line_file("/proc/sys/kernel/random/poolsize", &s
);
156 log_debug_errno(r
, "Failed to read pool size from kernel: %m");
160 r
= safe_atou(s
, &sz
);
162 log_debug_errno(r
, "Failed to parse pool size: %s", s
);
164 /* poolsize is in bits on 2.6, but we want bytes */
165 return CLAMP(sz
/ 8, RANDOM_POOL_SIZE_MIN
, RANDOM_POOL_SIZE_MAX
);
168 /* Use the minimum as default, if we can't retrieve the correct value */
169 return RANDOM_POOL_SIZE_MIN
;
172 int random_write_entropy(int fd
, const void *seed
, size_t size
, bool credit
) {
173 _cleanup_close_
int opened_fd
= -EBADF
;
176 assert(seed
|| size
== 0);
182 opened_fd
= open("/dev/urandom", O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
190 _cleanup_free_
struct rand_pool_info
*info
= NULL
;
192 /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any
193 * chance for confusion here. */
194 if (size
> INT_MAX
/ 8)
197 info
= malloc(offsetof(struct rand_pool_info
, buf
) + size
);
201 info
->entropy_count
= size
* 8;
202 info
->buf_size
= size
;
203 memcpy(info
->buf
, seed
, size
);
205 if (ioctl(fd
, RNDADDENTROPY
, info
) < 0)
208 r
= loop_write(fd
, seed
, size
);
216 uint64_t random_u64_range(uint64_t m
) {
217 uint64_t x
, remainder
;
219 /* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */
221 if (m
== 0) /* Let's take m == 0 as special case to return an integer from the full range */
226 remainder
= UINT64_MAX
% m
;
230 } while (x
>= UINT64_MAX
- remainder
);