forked from smuellerDD/lrng
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlrng_drng_atomic.c
130 lines (108 loc) · 3.38 KB
/
lrng_drng_atomic.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG DRNG for atomic contexts
*
* Copyright (C) 2022, Stephan Mueller <[email protected]>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/lrng.h>
#include "lrng_drng_atomic.h"
#include "lrng_drng_chacha20.h"
#include "lrng_es_aux.h"
#include "lrng_es_mgr.h"
#include "lrng_sha.h"
static struct chacha20_state chacha20_atomic = {
LRNG_CC20_INIT_RFC7539(.block)
};
/*
* DRNG usable in atomic context. This DRNG will always use the ChaCha20
* DRNG. It will never benefit from a DRNG switch like the "regular" DRNG. If
* there was no DRNG switch, the atomic DRNG is identical to the "regular" DRNG.
*
* The reason for having this is due to the fact that DRNGs other than
* the ChaCha20 DRNG may sleep.
*/
static struct lrng_drng lrng_drng_atomic = {
LRNG_DRNG_STATE_INIT(lrng_drng_atomic,
&chacha20_atomic, NULL,
&lrng_cc20_drng_cb, &lrng_sha_hash_cb),
.spin_lock = __SPIN_LOCK_UNLOCKED(lrng_drng_atomic.spin_lock)
};
struct lrng_drng *lrng_get_atomic(void)
{
return &lrng_drng_atomic;
}
void lrng_drng_atomic_reset(void)
{
unsigned long flags;
spin_lock_irqsave(&lrng_drng_atomic.spin_lock, flags);
lrng_drng_reset(&lrng_drng_atomic);
spin_unlock_irqrestore(&lrng_drng_atomic.spin_lock, flags);
}
void lrng_drng_atomic_force_reseed(void)
{
lrng_drng_atomic.force_reseed = lrng_drng_atomic.fully_seeded;
}
static bool lrng_drng_atomic_must_reseed(struct lrng_drng *drng)
{
return (!drng->fully_seeded ||
atomic_read(&lrng_drng_atomic.requests) == 0 ||
drng->force_reseed ||
time_after(jiffies,
drng->last_seeded + lrng_drng_reseed_max_time * HZ));
}
void lrng_drng_atomic_seed_drng(struct lrng_drng *regular_drng)
{
u8 seedbuf[LRNG_DRNG_SECURITY_STRENGTH_BYTES]
__aligned(LRNG_KCAPI_ALIGN);
int ret;
if (!lrng_drng_atomic_must_reseed(&lrng_drng_atomic))
return;
/*
* Reseed atomic DRNG another DRNG "regular" while this regular DRNG
* is reseeded. Therefore, this code operates in non-atomic context and
* thus can use the lrng_drng_get function to get random numbers from
* the just freshly seeded DRNG.
*/
ret = lrng_drng_get(regular_drng, seedbuf, sizeof(seedbuf));
if (ret < 0) {
pr_warn("Error generating random numbers for atomic DRNG: %d\n",
ret);
} else {
unsigned long flags;
spin_lock_irqsave(&lrng_drng_atomic.spin_lock, flags);
lrng_drng_inject(&lrng_drng_atomic, seedbuf, ret,
regular_drng->fully_seeded, "atomic");
spin_unlock_irqrestore(&lrng_drng_atomic.spin_lock, flags);
}
memzero_explicit(&seedbuf, sizeof(seedbuf));
}
static void lrng_drng_atomic_get(u8 *outbuf, u32 outbuflen)
{
struct lrng_drng *drng = &lrng_drng_atomic;
unsigned long flags;
if (!outbuf || !outbuflen)
return;
outbuflen = min_t(size_t, outbuflen, INT_MAX);
while (outbuflen) {
u32 todo = min_t(u32, outbuflen, LRNG_DRNG_MAX_REQSIZE);
int ret;
atomic_dec(&drng->requests);
spin_lock_irqsave(&drng->spin_lock, flags);
ret = drng->drng_cb->drng_generate(drng->drng, outbuf, todo);
spin_unlock_irqrestore(&drng->spin_lock, flags);
if (ret <= 0) {
pr_warn("getting random data from DRNG failed (%d)\n",
ret);
return;
}
outbuflen -= ret;
outbuf += ret;
}
}
void lrng_get_random_bytes(void *buf, int nbytes)
{
lrng_drng_atomic_get((u8 *)buf, (u32)nbytes);
lrng_debug_report_seedlevel("lrng_get_random_bytes");
}
EXPORT_SYMBOL(lrng_get_random_bytes);