android_kernel_xiaomi_sm7250/drivers/char/lrng/lrng_drng_chacha20.c
Juhyung Park e2659e1cdf lrng: merge v45
Signed-off-by: Juhyung Park <qkrwngud825@gmail.com>
Signed-off-by: UtsavBalar1231 <utsavbalar1231@gmail.com>
2022-11-12 11:23:17 +00:00

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 */