e2659e1cdf
Signed-off-by: Juhyung Park <qkrwngud825@gmail.com> Signed-off-by: UtsavBalar1231 <utsavbalar1231@gmail.com>
196 lines
6.0 KiB
C
196 lines
6.0 KiB
C
// 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 */
|