crypto: rng - FIPS 140-3 compliance for random number generation
Subsystems (2)
cover crypto: rng - FIPS 140-3 compliance for random number generation
This patch series implements FIPS 140-3 compliance requirements for random number generation in the Linux kernel 6.12 stable. The changes ensure that when the kernel is operating in FIPS mode, FIPS-compliant random number generators from the Crypto API are used to override the default /dev/random implementation.
The series consists of two patches:
1. "random: Add hook to override device reads and getrandom(2)" - This patch introduces the infrastructure to allow external RNGs to override the default random number generation. Originally authored by Herbert Xu, this has been adapted for kernel 6.12.
2. "crypto: rng - Override drivers/char/random only after FIPS RNGs available" - This patch implements the actual FIPS mode override using a workqueue-based approach to ensure proper initialization timing. It addresses timing issues in a previous commit "crypto: rng - Override drivers/char/random only after FIPS RNGs available" where the crypto RNG would attempt to override before dependencies were ready, preventing potential boot failures.
These patches are required for FIPS 140-3 certification and compliance in government and enterprise environments where cryptographic standards must be strictly enforced.
Herbert Xu (1):
random: Add hook to override device reads and getrandom(2)
Jay Wang (1):
crypto: rng - Override drivers/char/random only after FIPS RNGs
available
crypto/rng.c | 92 +++++++++++++++++++++++++++++++++ drivers/char/random.c | 114 +++++++++++++++++++++++++++++++++++++++++ include/linux/random.h | 7 +++ 3 files changed, 213 insertions(+)
--
2.47.1
PATCH 1/2 random: Add hook to override device reads and getrandom(2)
From: Herbert Xu <[email protected]>
This patch introduces a hook mechanism to drivers/char/random to allow the reads on /dev/*random as well as getrandom(2) to be overridden by an external RNG.
This will be used to override drivers/char/random with a FIPS RNG in a subsequent patch.
[6.1: Handle some context in getrandom() and random.h, and convert the extrng_*_fops to use random_write_iter().]
--- drivers/char/random.c | 114 +++++++++++++++++++++++++++++++++++++++++ include/linux/random.h | 7 +++ 2 files changed, 121 insertions(+)
drivers/char/random.c +114 -0
@@ -54,6 +54,7 @@#include <linux/suspend.h> #include <linux/siphash.h> #include <linux/sched/isolation.h> +#include <linux/rcupdate.h> #include <crypto/chacha.h> #include <crypto/blake2s.h> #ifdef CONFIG_VDSO_GETRANDOM
@@ -330,6 +331,11 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],memzero_explicit (first_block,sizeof (first_block)); } +/* +* Hook for external RNG. +*/ +static const struct random_extrng __rcu *extrng; +/* * This function returns a ChaCha state that you may use for generating * random data. It also returns up to 32 bytes on its own of random data
@@ -610,6 +616,9 @@ int __cold random_prepare_cpu(unsigned int cpu)#endif +static const struct file_operations extrng_random_fops; +static const struct file_operations extrng_urandom_fops; +/********************************************************************** * * Entropy accumulation and extraction routines.
@@ -980,6 +989,19 @@ void __init add_bootloader_randomness(const void *buf, size_t len)credit_init_bits (len *8 ); } +void random_register_extrng (const struct random_extrng *rng) +{ +rcu_assign_pointer (extrng, rng); +} +EXPORT_SYMBOL_GPL (random_register_extrng); + +void random_unregister_extrng (void ) +{ +RCU_INIT_POINTER (extrng,NULL ); +synchronize_rcu (); +} +EXPORT_SYMBOL_GPL (random_unregister_extrng); +#if IS_ENABLED (CONFIG_VMGENID )static BLOCKING_NOTIFIER_HEAD (vmfork_chain);
@@ -1387,6 +1409,7 @@ static void __cold try_to_generate_entropy(void) SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags) { +const struct random_extrng *rng;struct iov_iter iter;int ret;
@@ -1400,6 +1423,18 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flagsif ((flags & (GRND_INSECURE |GRND_RANDOM )) == (GRND_INSECURE |GRND_RANDOM ))return -EINVAL ; +rcu_read_lock (); + rng =rcu_dereference (extrng); +if (rng && !try_module_get (rng->owner)) + rng =NULL ; +rcu_read_unlock (); + +if (rng) { + ret = rng->extrng_read (ubuf, len); +module_put (rng->owner); +return ret; + } +if (!crng_ready () && !(flags &GRND_INSECURE )) {if (flags &GRND_NONBLOCK )return -EAGAIN ;
@@ -1420,6 +1455,13 @@ static __poll_t random_poll(struct file *file, poll_table *wait)return crng_ready () ?EPOLLIN |EPOLLRDNORM :EPOLLOUT |EPOLLWRNORM ; } +static __poll_t +extrng_poll (struct file *file,poll_table * wait) +{ +/* extrng pool is always full, always read, no writes */ +return EPOLLIN |EPOLLRDNORM ; +} +static ssize_t write_pool_user (struct iov_iter *iter) {u8 block[BLAKE2S_BLOCK_SIZE ];
@@ -1560,9 +1602,60 @@ static int random_fasync(int fd, struct file *filp, int on)return fasync_helper (fd, filp, on, &fasync); } +static int random_open (struct inode *inode,struct file *filp) +{ +const struct random_extrng *rng; + +rcu_read_lock (); + rng =rcu_dereference (extrng); +if (rng && !try_module_get (rng->owner)) + rng =NULL ; +rcu_read_unlock (); + +if (!rng) +return 0 ; + + filp->f_op = &extrng_random_fops; + filp->private_data = rng->owner; + +return 0 ; +} + +static int urandom_open (struct inode *inode,struct file *filp) +{ +const struct random_extrng *rng; + +rcu_read_lock (); + rng =rcu_dereference (extrng); +if (rng && !try_module_get (rng->owner)) + rng =NULL ; +rcu_read_unlock (); + +if (!rng) +return 0 ; + + filp->f_op = &extrng_urandom_fops; + filp->private_data = rng->owner; + +return 0 ; +} + +static int extrng_release (struct inode *inode,struct file *filp) +{ +module_put (filp->private_data); +return 0 ; +} + +static ssize_t +extrng_read (struct file *file,char __user *buf,size_t nbytes,loff_t *ppos) +{ +return rcu_dereference_raw (extrng)->extrng_read (buf, nbytes); +} +const struct file_operations random_fops = { .read_iter = random_read_iter, .write_iter = random_write_iter, + .open = random_open, .poll = random_poll, .unlocked_ioctl = random_ioctl, .compat_ioctl = compat_ptr_ioctl,
@@ -1575,6 +1668,7 @@ const struct file_operations random_fops = {const struct file_operations urandom_fops = { .read_iter = urandom_read_iter, .write_iter = random_write_iter, + .open = urandom_open, .unlocked_ioctl = random_ioctl, .compat_ioctl = compat_ptr_ioctl, .fasync = random_fasync,
@@ -1583,6 +1677,26 @@ const struct file_operations urandom_fops = { .splice_write = iter_file_splice_write, }; +static const struct file_operations extrng_random_fops = { + .open = random_open, + .read = extrng_read, + .write_iter = random_write_iter, + .poll = extrng_poll, + .unlocked_ioctl = random_ioctl, + .fasync = random_fasync, + .llseek = noop_llseek, + .release = extrng_release, +}; + +static const struct file_operations extrng_urandom_fops = { + .open = urandom_open, + .read = extrng_read, + .write_iter = random_write_iter, + .unlocked_ioctl = random_ioctl, + .fasync = random_fasync, + .llseek = noop_llseek, + .release = extrng_release, +};/******************************************************************** *
include/linux/random.h +7 -0
@@ -9,6 +9,11 @@#include <uapi/linux/random.h> +struct random_extrng { +ssize_t (*extrng_read)(void __user *buf,size_t buflen); +struct module *owner; +}; +struct notifier_block ;void add_device_randomness (const void *buf,size_t len);
@@ -120,6 +125,8 @@ void __init random_init(void);bool rng_is_initialized (void );int wait_for_random_bytes (void );int execute_with_initialized_rng (struct notifier_block *nb); +void random_register_extrng (const struct random_extrng *rng); +void random_unregister_extrng (void );/* Calls wait_for_random_bytes() and then calls get_random_bytes(buf, nbytes). * Returns the result of the call to wait_for_random_bytes. */
--
2.47.1
PATCH 2/2 crypto: rng - Override drivers/char/random only after FIPS RNGs available
In reply to Jay WangThis commit overrides the drivers/char/random RNGs with the FIPS RNG from Crypto API when FIPS mode is enabled. This commit is developed based on a previous commit "crypto: rng - Override drivers/char/random in FIPS mode", but it has a timing issue where the crypto RNG was attempting to override the drivers/char/random interface before the default RNG became available. The previous implementation would immediately register the external RNG during module initialization, which could fail if the default RNG wasn't ready.
Changes compared to previous commit:
- Introduce workqueue-based initialization for FIPS mode
- Add crypto_rng_register_work_func() to wait for default RNG availability
- Move random_register_extrng() call to the work function with proper error handling
This ensures the crypto ext RNG is properly registered only after all dependencies are satisfied, preventing potential boot failures in FIPS-enabled environments.
--- crypto/rng.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
crypto/rng.c +92 -0
@@ -12,13 +12,17 @@#include <linux/atomic.h> #include <linux/cryptouser.h> #include <linux/err.h> +#include <linux/fips.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/random.h> #include <linux/seq_file.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/workqueue.h> #include <net/netlink.h> #include "internal.h"
@@ -217,5 +221,93 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count) }EXPORT_SYMBOL_GPL (crypto_unregister_rngs); +static ssize_t crypto_devrandom_read (void __user *buf,size_t buflen) +{ +u8 tmp[256 ]; +ssize_t ret; + +if (!buflen) +return 0 ; + + ret =crypto_get_default_rng (); +if (ret) +return ret; + +for (;;) { +int err; +int i; + + i =min_t (int, buflen,sizeof (tmp)); + err =crypto_rng_get_bytes (crypto_default_rng, tmp, i); +if (err) { + ret = err; +break ; + } + +if (copy_to_user (buf, tmp, i)) { + ret = -EFAULT ; +break ; + } + + buflen -= i; + buf += i; + ret += i; + +if (!buflen) +break ; + +if (need_resched ()) { +if (signal_pending (current)) +break ; +schedule (); + } + } + +crypto_put_default_rng (); +memzero_explicit (tmp,sizeof (tmp)); + +return ret; +} + +static const struct random_extrng crypto_devrandom_rng = { + .extrng_read = crypto_devrandom_read, + .owner =THIS_MODULE , +}; + +static struct work_struct crypto_rng_register_work; + +static void crypto_rng_register_work_func (struct work_struct *work) +{ +/* Wait until default rng becomes avaiable, then +Overwrite the extrng. +*/ +int ret =crypto_get_default_rng (); +if (ret){ +printk (KERN_ERR "crypto_rng: Failed to get default RNG (error %d)\n" , ret); +return ; + } +printk (KERN_INFO "Overwrite extrng\n" ); +random_register_extrng (&crypto_devrandom_rng); +} + +static int __init crypto_rng_init (void ) +{ +if (fips_enabled) { +INIT_WORK (&crypto_rng_register_work, crypto_rng_register_work_func); +schedule_work (&crypto_rng_register_work); + } + +return 0 ; +} + +static void __exit crypto_rng_exit(void) +{ +cancel_work_sync (&crypto_rng_register_work); +random_unregister_extrng (); +} + +late_initcall (crypto_rng_init); +module_exit (crypto_rng_exit); +MODULE_LICENSE ("GPL" );MODULE_DESCRIPTION ("Random Number Generator" );
--
2.47.1