#include <linux/spinlock.h>
#include <linux/percpu.h>
#include <linux/cryptohash.h>
+#include <linux/fips.h>
+
+#ifdef CONFIG_GENERIC_HARDIRQS
+# include <linux/irq.h>
+#endif
#include <asm/processor.h>
#include <asm/uaccess.h>
/* read-write data: */
spinlock_t lock;
unsigned add_ptr;
- int entropy_count; /* Must at no time exceed ->POOLBITS! */
+ int entropy_count;
int input_rotate;
+ __u8 *last_data;
};
static __u32 input_pool_data[INPUT_POOL_WORDS];
unsigned dont_count_entropy:1;
};
-#ifdef CONFIG_HAVE_DYN_ARRAY
-static struct timer_rand_state **irq_timer_state;
-DEFINE_DYN_ARRAY(irq_timer_state, sizeof(struct timer_rand_state *), nr_irqs, PAGE_SIZE, NULL);
-#else
+#ifndef CONFIG_GENERIC_HARDIRQS
+
static struct timer_rand_state *irq_timer_state[NR_IRQS];
-#endif
static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
{
- if (irq >= nr_irqs)
- return NULL;
-
return irq_timer_state[irq];
}
-static void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state)
+static void set_timer_rand_state(unsigned int irq,
+ struct timer_rand_state *state)
{
- if (irq >= nr_irqs)
- return;
-
irq_timer_state[irq] = state;
}
+#else
+
+static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
+{
+ struct irq_desc *desc;
+
+ desc = irq_to_desc(irq);
+
+ return desc->timer_rand_state;
+}
+
+static void set_timer_rand_state(unsigned int irq,
+ struct timer_rand_state *state)
+{
+ struct irq_desc *desc;
+
+ desc = irq_to_desc(irq);
+
+ desc->timer_rand_state = state;
+}
+#endif
+
static struct timer_rand_state input_timer_state;
/*
{
unsigned long flags;
- BUG_ON(r->entropy_count > r->poolinfo->POOLBITS);
-
/* Hold lock while accounting */
spin_lock_irqsave(&r->lock, flags);
+ BUG_ON(r->entropy_count > r->poolinfo->POOLBITS);
DEBUG_ENT("trying to extract %d bits from %s\n",
nbytes * 8, r->name);
{
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
+ unsigned long flags;
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, min, reserved);
while (nbytes) {
extract_buf(r, tmp);
+
+ if (r->last_data) {
+ spin_lock_irqsave(&r->lock, flags);
+ if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
+ panic("Hardware RNG duplicated output!\n");
+ memcpy(r->last_data, tmp, EXTRACT_SIZE);
+ spin_unlock_irqrestore(&r->lock, flags);
+ }
i = min_t(int, nbytes, EXTRACT_SIZE);
memcpy(buf, tmp, i);
nbytes -= i;
now = ktime_get_real();
mix_pool_bytes(r, &now, sizeof(now));
mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
+ /* Enable continuous test in fips mode */
+ if (fips_enabled)
+ r->last_data = kmalloc(EXTRACT_SIZE, GFP_KERNEL);
}
static int rand_initialize(void)
{
struct timer_rand_state *state;
- if (irq >= nr_irqs)
- return;
-
state = get_timer_rand_state(irq);
if (state)
return fasync_helper(fd, filp, on, &fasync);
}
-static int random_release(struct inode *inode, struct file *filp)
-{
- return fasync_helper(-1, filp, 0, &fasync);
-}
-
const struct file_operations random_fops = {
.read = random_read,
.write = random_write,
.poll = random_poll,
.unlocked_ioctl = random_ioctl,
.fasync = random_fasync,
- .release = random_release,
};
const struct file_operations urandom_fops = {
.write = random_write,
.unlocked_ioctl = random_ioctl,
.fasync = random_fasync,
- .release = random_release,
};
/***************************************************************
* as an ASCII string in the standard UUID format. If accesses via the
* sysctl system call, it is returned as 16 bytes of binary data.
*/
-static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
+static int proc_do_uuid(ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
ctl_table fake_table;
fake_table.data = buf;
fake_table.maxlen = sizeof(buf);
- return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos);
+ return proc_dostring(&fake_table, write, buffer, lenp, ppos);
}
-static int uuid_strategy(ctl_table *table, int __user *name, int nlen,
+static int uuid_strategy(ctl_table *table,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen)
{
keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS;
smp_wmb();
ip_cnt++;
- schedule_delayed_work(&rekey_work, REKEY_INTERVAL);
+ schedule_delayed_work(&rekey_work,
+ round_jiffies_relative(REKEY_INTERVAL));
}
static inline struct keydata *get_keyptr(void)
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
+DEFINE_PER_CPU(__u32 [4], get_random_int_hash);
unsigned int get_random_int(void)
{
- /*
- * Use IP's RNG. It suits our purpose perfectly: it re-keys itself
- * every second, from the entropy pool (and thus creates a limited
- * drain on it), and uses halfMD4Transform within the second. We
- * also mix it with jiffies and the PID:
- */
- return secure_ip_id((__force __be32)(current->pid + jiffies));
+ struct keydata *keyptr;
+ __u32 *hash = get_cpu_var(get_random_int_hash);
+ int ret;
+
+ keyptr = get_keyptr();
+ hash[0] += current->pid + jiffies + get_cycles();
+
+ ret = half_md4_transform(hash, keyptr->secret);
+ put_cpu_var(get_random_int_hash);
+
+ return ret;
}
/*