-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathlrng_drng_drbg.c
215 lines (179 loc) · 5.84 KB
/
lrng_drng_drbg.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Backend for the LRNG providing the cryptographic primitives using the
* kernel crypto API and its DRBG.
*
* Copyright (C) 2022, Stephan Mueller <[email protected]>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <crypto/drbg.h>
#include <linux/init.h>
#include <linux/lrng.h>
#include <linux/module.h>
#include "lrng_drng_drbg.h"
/*
* Define a DRBG plus a hash / MAC used to extract data from the entropy pool.
* For LRNG_HASH_NAME you can use a hash or a MAC (HMAC or CMAC) of your choice
* (Note, you should use the suggested selections below -- using SHA-1 or MD5
* is not wise). The idea is that the used cipher primitive can be selected to
* be the same as used for the DRBG. I.e. the LRNG only uses one cipher
* primitive using the same cipher implementation with the options offered in
* the following. This means, if the CTR DRBG is selected and AES-NI is present,
* both the CTR DRBG and the selected cmac(aes) use AES-NI.
*
* The security strengths of the DRBGs are all 256 bits according to
* SP800-57 section 5.6.1.
*
* This definition is allowed to be changed.
*/
#ifdef CONFIG_CRYPTO_DRBG_CTR
static unsigned int lrng_drbg_type = 0;
#elif defined CONFIG_CRYPTO_DRBG_HMAC
static unsigned int lrng_drbg_type = 1;
#elif defined CONFIG_CRYPTO_DRBG_HASH
static unsigned int lrng_drbg_type = 2;
#else
#error "Unknown DRBG in use"
#endif
/* The parameter must be r/o in sysfs as otherwise races appear. */
module_param(lrng_drbg_type, uint, 0444);
MODULE_PARM_DESC(lrng_drbg_type, "DRBG type used for LRNG (0->CTR_DRBG, 1->HMAC_DRBG, 2->Hash_DRBG)");
struct lrng_drbg {
const char* hash_name;
const char* drbg_core;
};
static const struct lrng_drbg lrng_drbg_types[] = {
{
/* CTR_DRBG with AES-256 using derivation function */
.drbg_core = "drbg_nopr_ctr_aes256",
}, {
/* HMAC_DRBG with SHA-512 */
.drbg_core = "drbg_nopr_hmac_sha512",
}, {
/* Hash_DRBG with SHA-512 using derivation function */
.drbg_core = "drbg_nopr_sha512"
}
};
static int lrng_drbg_drng_seed_helper(void* drng, const u8* inbuf, u32 inbuflen)
{
struct drbg_state* drbg = (struct drbg_state*)drng;
LIST_HEAD(seedlist);
struct drbg_string data;
int ret;
drbg_string_fill(&data, inbuf, inbuflen);
list_add_tail(&data.list, &seedlist);
ret = drbg->d_ops->update(drbg, &seedlist, drbg->seeded);
if (ret >= 0)
drbg->seeded = DRBG_SEED_STATE_FULL;
return ret;
}
static int lrng_drbg_drng_generate_helper(void* drng, u8* outbuf, u32 outbuflen)
{
struct drbg_state* drbg = (struct drbg_state*)drng;
return drbg->d_ops->generate(drbg, outbuf, outbuflen, NULL);
}
static void* lrng_drbg_drng_alloc(u32 sec_strength)
{
struct drbg_state* drbg;
int coreref = -1;
bool pr = false;
int ret;
drbg_convert_tfm_core(lrng_drbg_types[lrng_drbg_type].drbg_core,
&coreref, &pr);
if (coreref < 0)
return ERR_PTR(-EFAULT);
drbg = kzalloc(sizeof(struct drbg_state), GFP_KERNEL);
if (!drbg)
return ERR_PTR(-ENOMEM);
drbg->core = &drbg_cores[coreref];
drbg->seeded = DRBG_SEED_STATE_UNSEEDED;
ret = drbg_alloc_state(drbg);
if (ret)
goto err;
if (sec_strength > drbg_sec_strength(drbg->core->flags)) {
pr_err("Security strength of DRBG (%u bits) lower than requested by LRNG (%u bits)\n",
drbg_sec_strength(drbg->core->flags) * 8,
sec_strength * 8);
goto dealloc;
}
if (sec_strength < drbg_sec_strength(drbg->core->flags))
pr_warn("Security strength of DRBG (%u bits) higher than requested by LRNG (%u bits)\n",
drbg_sec_strength(drbg->core->flags) * 8,
sec_strength * 8);
pr_info("DRBG with %s core allocated\n", drbg->core->backend_cra_name);
return drbg;
dealloc:
if (drbg->d_ops)
drbg->d_ops->crypto_fini(drbg);
drbg_dealloc_state(drbg);
err:
kfree(drbg);
return ERR_PTR(-EINVAL);
}
static void lrng_drbg_drng_dealloc(void* drng)
{
struct drbg_state* drbg = (struct drbg_state*)drng;
if (drbg && drbg->d_ops)
drbg->d_ops->crypto_fini(drbg);
drbg_dealloc_state(drbg);
kfree_sensitive(drbg);
pr_info("DRBG deallocated\n");
}
static const char* lrng_drbg_name(void)
{
return lrng_drbg_types[lrng_drbg_type].drbg_core;
}
const struct lrng_drng_cb lrng_drbg_cb = {
.drng_name = lrng_drbg_name,
.drng_alloc = lrng_drbg_drng_alloc,
.drng_dealloc = lrng_drbg_drng_dealloc,
.drng_seed = lrng_drbg_drng_seed_helper,
.drng_generate = lrng_drbg_drng_generate_helper,
};
static int __init lrng_drbg_selftest(void)
{
struct crypto_rng *drbg;
/* Allocate the DRBG once to trigger the kernel crypto API self test */
drbg = crypto_alloc_rng(lrng_drbg_types[lrng_drbg_type].drbg_core, 0,
0);
if (IS_ERR(drbg)) {
pr_err("could not allocate DRBG and trigger self-test: %ld\n",
PTR_ERR(drbg));
return PTR_ERR(drbg);
}
crypto_free_rng(drbg);
return 0;
}
#ifndef CONFIG_LRNG_DFLT_DRNG_DRBG
static int __init lrng_drbg_init(void)
{
int ret = lrng_drbg_selftest();
if (ret)
return ret;
if (lrng_drbg_type >= ARRAY_SIZE(lrng_drbg_types)) {
pr_err("lrng_drbg_type parameter too large (given %u - max: %lu)",
lrng_drbg_type,
(unsigned long)ARRAY_SIZE(lrng_drbg_types) - 1);
return -EAGAIN;
}
return lrng_set_drng_cb(&lrng_drbg_cb);
}
static void __exit lrng_drbg_exit(void)
{
lrng_set_drng_cb(NULL);
}
late_initcall(lrng_drbg_init);
module_exit(lrng_drbg_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Stephan Mueller <[email protected]>");
MODULE_DESCRIPTION("Entropy Source and DRNG Manager - SP800-90A DRBG backend");
#else
/*
* Note, this call may result in the use of the DRBG before the self-test is
* run. However, that usage is not relevant to any FIPS-140 consideration as
* the data is used for non-cryptographic purposes. The call below guarantees
* that the self-tests are run before user space is started and thus callers
* with needs to comply with FIPS-140 appear.
*/
late_initcall(lrng_drbg_selftest);
#endif /* CONFIG_LRNG_DFLT_DRNG_DRBG */