-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathlrng_drng_chacha20.c
195 lines (162 loc) · 5.99 KB
/
lrng_drng_chacha20.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
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Backend for the LRNG providing the cryptographic primitives using
* ChaCha20 cipher implementations.
*
* Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <crypto/chacha.h>
#include <linux/lrng.h>
#include <linux/random.h>
#include <linux/slab.h>
#include "lrng_drng_chacha20.h"
/******************************* ChaCha20 DRNG *******************************/
#define CHACHA_BLOCK_WORDS (CHACHA_BLOCK_SIZE / sizeof(u32))
/*
* Update of the ChaCha20 state by either using an unused buffer part or by
* generating one ChaCha20 block which is half of the state of the ChaCha20.
* The block is XORed into the key part of the state. This shall ensure
* backtracking resistance as well as a proper mix of the ChaCha20 state once
* the key is injected.
*/
static void lrng_chacha20_update(struct chacha20_state *chacha20_state,
__le32 *buf, u32 used_words)
{
struct chacha20_block *chacha20 = &chacha20_state->block;
u32 i;
__le32 tmp[CHACHA_BLOCK_WORDS];
BUILD_BUG_ON(sizeof(struct chacha20_block) != CHACHA_BLOCK_SIZE);
BUILD_BUG_ON(CHACHA_BLOCK_SIZE != 2 * CHACHA_KEY_SIZE);
if (used_words > CHACHA_KEY_SIZE_WORDS) {
chacha20_block(&chacha20->constants[0], (u8 *)tmp);
for (i = 0; i < CHACHA_KEY_SIZE_WORDS; i++)
chacha20->key.u[i] ^= le32_to_cpu(tmp[i]);
memzero_explicit(tmp, sizeof(tmp));
} else {
for (i = 0; i < CHACHA_KEY_SIZE_WORDS; i++)
chacha20->key.u[i] ^= le32_to_cpu(buf[i + used_words]);
}
/* Deterministic increment of nonce as required in RFC 7539 chapter 4 */
chacha20->nonce[0]++;
if (chacha20->nonce[0] == 0) {
chacha20->nonce[1]++;
if (chacha20->nonce[1] == 0)
chacha20->nonce[2]++;
}
/* Leave counter untouched as it is start value is undefined in RFC */
}
/*
* Seed the ChaCha20 DRNG by injecting the input data into the key part of
* the ChaCha20 state. If the input data is longer than the ChaCha20 key size,
* perform a ChaCha20 operation after processing of key size input data.
* This operation shall spread out the entropy into the ChaCha20 state before
* new entropy is injected into the key part.
*/
static int lrng_cc20_drng_seed_helper(void *drng, const u8 *inbuf, u32 inbuflen)
{
struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
struct chacha20_block *chacha20 = &chacha20_state->block;
while (inbuflen) {
u32 i, todo = min_t(u32, inbuflen, CHACHA_KEY_SIZE);
for (i = 0; i < todo; i++)
chacha20->key.b[i] ^= inbuf[i];
/* Break potential dependencies between the inbuf key blocks */
lrng_chacha20_update(chacha20_state, NULL,
CHACHA_BLOCK_WORDS);
inbuf += todo;
inbuflen -= todo;
}
return 0;
}
/*
* Chacha20 DRNG generation of random numbers: the stream output of ChaCha20
* is the random number. After the completion of the generation of the
* stream, the entire ChaCha20 state is updated.
*
* Note, as the ChaCha20 implements a 32 bit counter, we must ensure
* that this function is only invoked for at most 2^32 - 1 ChaCha20 blocks
* before a reseed or an update happens. This is ensured by the variable
* outbuflen which is a 32 bit integer defining the number of bytes to be
* generated by the ChaCha20 DRNG. At the end of this function, an update
* operation is invoked which implies that the 32 bit counter will never be
* overflown in this implementation.
*/
static int lrng_cc20_drng_generate_helper(void *drng, u8 *outbuf, u32 outbuflen)
{
struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
struct chacha20_block *chacha20 = &chacha20_state->block;
__le32 aligned_buf[CHACHA_BLOCK_WORDS];
u32 ret = outbuflen, used = CHACHA_BLOCK_WORDS;
int zeroize_buf = 0;
while (outbuflen >= CHACHA_BLOCK_SIZE) {
chacha20_block(&chacha20->constants[0], outbuf);
outbuf += CHACHA_BLOCK_SIZE;
outbuflen -= CHACHA_BLOCK_SIZE;
}
if (outbuflen) {
chacha20_block(&chacha20->constants[0], (u8 *)aligned_buf);
memcpy(outbuf, aligned_buf, outbuflen);
used = ((outbuflen + sizeof(aligned_buf[0]) - 1) /
sizeof(aligned_buf[0]));
zeroize_buf = 1;
}
lrng_chacha20_update(chacha20_state, aligned_buf, used);
if (zeroize_buf)
memzero_explicit(aligned_buf, sizeof(aligned_buf));
return ret;
}
/*
* Allocation of the DRNG state
*/
static void *lrng_cc20_drng_alloc(u32 sec_strength)
{
struct chacha20_state *state = NULL;
if (sec_strength > CHACHA_KEY_SIZE) {
pr_err("Security strength of ChaCha20 DRNG (%u bits) lower than requested by LRNG (%u bits)\n",
CHACHA_KEY_SIZE * 8, sec_strength * 8);
return ERR_PTR(-EINVAL);
}
if (sec_strength < CHACHA_KEY_SIZE)
pr_warn("Security strength of ChaCha20 DRNG (%u bits) higher than requested by LRNG (%u bits)\n",
CHACHA_KEY_SIZE * 8, sec_strength * 8);
state = kmalloc(sizeof(struct chacha20_state), GFP_KERNEL);
if (!state)
return ERR_PTR(-ENOMEM);
pr_debug("memory for ChaCha20 core allocated\n");
lrng_cc20_init_rfc7539(&state->block);
return state;
}
static void lrng_cc20_drng_dealloc(void *drng)
{
struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
pr_debug("ChaCha20 core zeroized and freed\n");
kfree_sensitive(chacha20_state);
}
static const char *lrng_cc20_drng_name(void)
{
return "ChaCha20 DRNG";
}
const struct lrng_drng_cb lrng_cc20_drng_cb = {
.drng_name = lrng_cc20_drng_name,
.drng_alloc = lrng_cc20_drng_alloc,
.drng_dealloc = lrng_cc20_drng_dealloc,
.drng_seed = lrng_cc20_drng_seed_helper,
.drng_generate = lrng_cc20_drng_generate_helper,
};
#if !defined(CONFIG_LRNG_DFLT_DRNG_CHACHA20) && \
!defined(CONFIG_LRNG_DRNG_ATOMIC)
static int __init lrng_cc20_drng_init(void)
{
return lrng_set_drng_cb(&lrng_cc20_drng_cb);
}
static void __exit lrng_cc20_drng_exit(void)
{
lrng_set_drng_cb(NULL);
}
late_initcall(lrng_cc20_drng_init);
module_exit(lrng_cc20_drng_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
MODULE_DESCRIPTION("Entropy Source and DRNG Manager - ChaCha20-based DRNG backend");
#endif /* CONFIG_LRNG_DFLT_DRNG_CHACHA20 */