Skip to content

Commit

Permalink
char/lrng: CPU entropy source
Browse files Browse the repository at this point in the history
Certain CPUs provide instructions giving access to an entropy source
(e.g. RDSEED on Intel/AMD, DARN on POWER, etc.). The LRNG can utilize
the entropy source to seed its DRNG from.

CC: Torsten Duwe <duwe@lst.de>
CC: "Eric W. Biederman" <ebiederm@xmission.com>
CC: "Alexander E. Patrakov" <patrakov@gmail.com>
CC: "Ahmed S. Darwish" <darwish.07@gmail.com>
CC: "Theodore Y. Ts'o" <tytso@mit.edu>
CC: Willy Tarreau <w@1wt.eu>
CC: Matthew Garrett <mjg59@srcf.ucam.org>
CC: Vito Caputo <vcaputo@pengaru.com>
CC: Andreas Dilger <adilger.kernel@dilger.ca>
CC: Jan Kara <jack@suse.cz>
CC: Ray Strode <rstrode@redhat.com>
CC: William Jon McCann <mccann@jhu.edu>
CC: zhangjs <zachary@baishancloud.com>
CC: Andy Lutomirski <luto@kernel.org>
CC: Florian Weimer <fweimer@redhat.com>
CC: Lennart Poettering <mzxreary@0pointer.de>
CC: Nicolai Stange <nstange@suse.de>
Reviewed-by: Alexander Lobakin <alobakin@pm.me>
Tested-by: Alexander Lobakin <alobakin@pm.me>
Mathematical aspects Reviewed-by: "Peter, Matthias" <matthias.peter@bsi.bund.de>
Reviewed-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
Reviewed-by: Roman Drahtmueller <draht@schaltsekun.de>
Tested-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
Tested-by: Neil Horman <nhorman@redhat.com>
Tested-by: Jirka Hladky <jhladky@redhat.com>
Reviewed-by: Jirka Hladky <jhladky@redhat.com>
Signed-off-by: Stephan Mueller <smueller@chronox.de>
  • Loading branch information
smuellerDD authored and xanmod committed Jan 12, 2022
1 parent 78be60b commit 98be513
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
38 changes: 38 additions & 0 deletions drivers/char/lrng/Kconfig
Expand Up @@ -215,6 +215,44 @@ config LRNG_IRQ_ENTROPY_RATE
interrupt entropy source will still deliver data but without
being credited with entropy.

comment "CPU Entropy Source"

config LRNG_CPU
bool "Enable CPU Entropy Source as LRNG Seed Source"
default y
help
Current CPUs commonly contain entropy sources which can be
used to seed the LRNG. For example, the Intel RDSEED
instruction, or the POWER DARN instruction will be sourced
to seed the LRNG if this option is enabled.

Note, if this option is enabled and the underlying CPU
does not offer such entropy source, the LRNG will automatically
detect this and ignore the hardware.

config LRNG_CPU_FULL_ENT_MULTIPLIER
int
default 1 if !LRNG_TEST_CPU_ES_COMPRESSION
default 123 if LRNG_TEST_CPU_ES_COMPRESSION

config LRNG_CPU_ENTROPY_RATE
int "CPU Entropy Source Entropy Rate"
depends on LRNG_CPU
range 0 256
default 8
help
The option defines the amount of entropy the LRNG applies to 256
bits of data obtained from the CPU entropy source. The LRNG
enforces the limit that this value must be in the range between
0 and 256.

When configuring this value to 0, the CPU entropy source will
provide 256 bits of data without being credited to contain
entropy.

Note, this option is overwritten when the option
CONFIG_RANDOM_TRUST_CPU is set.

endmenu # "Entropy Source Configuration"

endif # LRNG
1 change: 1 addition & 0 deletions drivers/char/lrng/Makefile
Expand Up @@ -10,3 +10,4 @@ obj-y += lrng_es_mgr.o lrng_aux.o \
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.o
226 changes: 226 additions & 0 deletions drivers/char/lrng/lrng_es_archrandom.c
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG Fast Entropy Source: CPU-based entropy source
*
* Copyright (C) 2016 - 2021, Stephan Mueller <smueller@chronox.de>
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <crypto/hash.h>
#include <linux/lrng.h>
#include <linux/random.h>

#include "lrng_internal.h"

/*
* Estimated entropy of data is a 32th of LRNG_DRNG_SECURITY_STRENGTH_BITS.
* As we have no ability to review the implementation of those noise sources,
* it is prudent to have a conservative estimate here.
*/
#define LRNG_ARCHRANDOM_DEFAULT_STRENGTH CONFIG_LRNG_CPU_ENTROPY_RATE
#define LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH LRNG_DRNG_SECURITY_STRENGTH_BITS
#ifdef CONFIG_RANDOM_TRUST_CPU
static u32 archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
#else
static u32 archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
#endif
#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
module_param(archrandom, uint, 0644);
MODULE_PARM_DESC(archrandom, "Entropy in bits of 256 data bits from CPU noise source (e.g. RDSEED)");
#endif

static int __init lrng_parse_trust_cpu(char *arg)
{
int ret;
bool trust_cpu = false;

ret = kstrtobool(arg, &trust_cpu);
if (ret)
return ret;

if (trust_cpu) {
archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
lrng_pool_add_entropy();
} else {
archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
}

return 0;
}
early_param("random.trust_cpu", lrng_parse_trust_cpu);

u32 lrng_archrandom_entropylevel(u32 requested_bits)
{
return lrng_fast_noise_entropylevel(archrandom, requested_bits);
}

static u32 lrng_get_arch_data(u8 *outbuf, u32 requested_bits)
{
u32 i;

/* operate on full blocks */
BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BYTES % sizeof(unsigned long));
BUILD_BUG_ON(CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS %
sizeof(unsigned long));
/* ensure we have aligned buffers */
BUILD_BUG_ON(LRNG_KCAPI_ALIGN % sizeof(unsigned long));

for (i = 0; i < (requested_bits >> 3);
i += sizeof(unsigned long)) {
if (!arch_get_random_seed_long((unsigned long *)(outbuf + i)) &&
!arch_get_random_long((unsigned long *)(outbuf + i))) {
archrandom = 0;
return 0;
}
}

return requested_bits;
}

static u32 inline lrng_get_arch_data_compress(u8 *outbuf, u32 requested_bits,
u32 data_multiplier)
{
SHASH_DESC_ON_STACK(shash, NULL);
const struct lrng_crypto_cb *crypto_cb;
struct lrng_drng *drng = lrng_drng_init_instance();
unsigned long flags;
u32 ent_bits = 0, i, partial_bits = 0,
full_bits = requested_bits * data_multiplier;
void *hash;

/* Calculate oversampling for SP800-90C */
if (lrng_sp80090c_compliant()) {
/* Complete amount of bits to be pulled */
full_bits += CONFIG_LRNG_OVERSAMPLE_ES_BITS * data_multiplier;
/* Full blocks that will be pulled */
data_multiplier = full_bits / requested_bits;
/* Partial block in bits to be pulled */
partial_bits = full_bits - (data_multiplier * requested_bits);
}

lrng_hash_lock(drng, &flags);
crypto_cb = drng->crypto_cb;
hash = drng->hash;

if (crypto_cb->lrng_hash_init(shash, hash))
goto out;

/* Hash all data from the CPU entropy source */
for (i = 0; i < data_multiplier; i++) {
ent_bits = lrng_get_arch_data(outbuf, requested_bits);
if (!ent_bits)
goto out;

if (crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
goto err;
}

/* Hash partial block, if applicable */
ent_bits = lrng_get_arch_data(outbuf, partial_bits);
if (ent_bits &&
crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
goto err;

pr_debug("pulled %u bits from CPU RNG entropy source\n", full_bits);

/* Generate the compressed data to be returned to the caller */
ent_bits = crypto_cb->lrng_hash_digestsize(hash) << 3;
if (requested_bits < ent_bits) {
u8 digest[LRNG_MAX_DIGESTSIZE];

if (crypto_cb->lrng_hash_final(shash, digest))
goto err;

/* Truncate output data to requested size */
memcpy(outbuf, digest, requested_bits >> 3);
memzero_explicit(digest, crypto_cb->lrng_hash_digestsize(hash));
ent_bits = requested_bits;
} else {
if (crypto_cb->lrng_hash_final(shash, outbuf))
goto err;
}

out:
crypto_cb->lrng_hash_desc_zero(shash);
lrng_hash_unlock(drng, flags);
return ent_bits;

err:
ent_bits = 0;
goto out;
}

/*
* If CPU entropy source requires does not return full entropy, return the
* multiplier of how much data shall be sampled from it.
*/
static u32 lrng_arch_multiplier(void)
{
static u32 data_multiplier = 0;

if (data_multiplier > 0) {
return data_multiplier;
} else {
unsigned long v;

if (IS_ENABLED(CONFIG_X86) && !arch_get_random_seed_long(&v)) {
/*
* Intel SPEC: pulling 512 blocks from RDRAND ensures
* one reseed making it logically equivalent to RDSEED.
*/
data_multiplier = 512;
} else if (IS_ENABLED(CONFIG_PPC)) {
/*
* PowerISA defines DARN to deliver at least 0.5 bits of
* entropy per data bit.
*/
data_multiplier = 2;
} else {
/* CPU provides full entropy */
data_multiplier = CONFIG_LRNG_CPU_FULL_ENT_MULTIPLIER;
}
}
return data_multiplier;
}

/*
* lrng_get_arch() - Get CPU entropy source entropy
*
* @outbuf: buffer to store entropy of size requested_bits
*
* Return:
* * > 0 on success where value provides the added entropy in bits
* * 0 if no fast source was available
*/
u32 lrng_get_arch(u8 *outbuf, u32 requested_bits)
{
u32 ent_bits, data_multiplier = lrng_arch_multiplier();

if (data_multiplier <= 1) {
ent_bits = lrng_get_arch_data(outbuf, requested_bits);
} else {
ent_bits = lrng_get_arch_data_compress(outbuf, requested_bits,
data_multiplier);
}

ent_bits = lrng_archrandom_entropylevel(ent_bits);
pr_debug("obtained %u bits of entropy from CPU RNG entropy source\n",
ent_bits);
return ent_bits;
}

void lrng_arch_es_state(unsigned char *buf, size_t buflen)
{
const struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
u32 data_multiplier = lrng_arch_multiplier();

/* Assume the lrng_drng_init lock is taken by caller */
snprintf(buf, buflen,
"CPU ES properties:\n"
" Hash for compressing data: %s\n"
" Data multiplier: %u\n",
(data_multiplier <= 1) ?
"N/A" : lrng_drng_init->crypto_cb->lrng_hash_name(),
data_multiplier);
}

0 comments on commit 98be513

Please sign in to comment.