summaryrefslogtreecommitdiffstats
path: root/sys/dev/random
diff options
context:
space:
mode:
authormarkm <markm@FreeBSD.org>2004-04-17 19:26:53 +0000
committermarkm <markm@FreeBSD.org>2004-04-17 19:26:53 +0000
commitf34e8fe1dd7edc5c723541e0ffd07058d9db1d35 (patch)
tree6983dfac8ec94794d99ea8771fca0563093d7a65 /sys/dev/random
parent8106e11ada74dc594bd89dca8e427dfa1f1b8ba9 (diff)
downloadFreeBSD-src-f34e8fe1dd7edc5c723541e0ffd07058d9db1d35.zip
FreeBSD-src-f34e8fe1dd7edc5c723541e0ffd07058d9db1d35.tar.gz
Add a Davies-Meyer style hash to the output. This is still pure
Nehemiah chip, but the work is all done in hardware. There are three opportunities to add other entropy; the Data Buffer, the Cipher's IV and the Cipher's key. A future commit will exploit these opportunities.
Diffstat (limited to 'sys/dev/random')
-rw-r--r--sys/dev/random/nehemiah.c136
1 files changed, 124 insertions, 12 deletions
diff --git a/sys/dev/random/nehemiah.c b/sys/dev/random/nehemiah.c
index e496828..c0dba9e 100644
--- a/sys/dev/random/nehemiah.c
+++ b/sys/dev/random/nehemiah.c
@@ -28,19 +28,24 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/time.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/selinfo.h>
+#include <sys/systm.h>
#include <dev/random/randomdev.h>
+#define RANDOM_BLOCK_SIZE 256
+#define CIPHER_BLOCK_SIZE 16
+
+static void random_nehemiah_init(void);
static int random_nehemiah_read(void *, int);
struct random_systat random_nehemiah = {
.ident = "Hardware, VIA Nehemiah",
- .init = (random_init_func_t *)random_null_func,
+ .init = random_nehemiah_init,
.deinit = (random_deinit_func_t *)random_null_func,
.read = random_nehemiah_read,
.write = (random_write_func_t *)random_null_func,
@@ -48,19 +53,126 @@ struct random_systat random_nehemiah = {
.seeded = 1,
};
+union VIA_ACE_CW {
+ uint64_t raw;
+ struct {
+ u_int round_count : 4;
+ u_int algorithm_type : 3;
+ u_int key_generation_type : 1;
+ u_int intermediate : 1;
+ u_int decrypt : 1;
+ u_int key_size : 2;
+ u_int filler0 : 20;
+ u_int filler1 : 32;
+ u_int filler2 : 32;
+ u_int filler3 : 32;
+ } field;
+};
+
+/* The extra 7 is to allow an 8-byte write on the last byte of the
+ * arrays. The ACE wants the AES data 16-byte/128-bit aligned, and
+ * it _always_ writes n*64 bits. The RNG does not care about alignment,
+ * and it always writes n*32 bits or n*64 bits.
+ */
+static uint8_t key[CIPHER_BLOCK_SIZE+7] __aligned(16);
+static uint8_t iv[CIPHER_BLOCK_SIZE+7] __aligned(16);
+static uint8_t in[RANDOM_BLOCK_SIZE+7] __aligned(16);
+static uint8_t out[RANDOM_BLOCK_SIZE+7] __aligned(16);
+
+static union VIA_ACE_CW acw __aligned(16);
+
/* ARGSUSED */
+static __inline size_t
+VIA_RNG_store(void *buf)
+{
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+ uint32_t retval = 0;
+ uint32_t rate = 0;
+
+ /* The .byte line is really VIA C3 "xstore" instruction */
+ __asm __volatile(
+ "movl $0,%%edx \n\t"
+ ".byte 0x0f, 0xa7, 0xc0"
+ : "=a" (retval), "+d" (rate), "+D" (buf)
+ :
+ : "memory"
+ );
+ if (rate == 0)
+ return (retval&0x1f);
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+static __inline void
+VIA_ACE_cbc(void *in, void *out, size_t count, void *key, union VIA_ACE_CW *cw, void *iv)
+{
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+ /* The .byte line is really VIA C3 "xcrypt-cbc" instruction */
+ __asm __volatile(
+ "pushf \n\t"
+ "popf \n\t"
+ "rep \n\t"
+ ".byte 0x0f, 0xa7, 0xc8"
+ : "+a" (iv), "+c" (count), "+D" (out), "+S" (in)
+ : "b" (key), "d" (cw)
+ : "cc", "memory"
+ );
+#endif
+}
+
+static void
+random_nehemiah_init(void)
+{
+ acw.raw = 0ULL;
+ acw.field.round_count = 12;
+}
+
static int
random_nehemiah_read(void *buf, int c)
{
-#if (defined(__GNUC__) || defined(__INTEL_COMPILER)) && defined(__i386__)
- int count = c;
- int rate = 0;
-
- /* VIA C3 Nehemiah "rep; xstore" */
- __asm __volatile("rep; .byte 0x0f, 0xa7, 0xc0"
- : "+D" (buf), "+c" (count), "=d" (rate)
- :
- : "memory");
-#endif
+ int i;
+ size_t count, ret;
+ uint8_t *p;
+
+ /* Get a random AES key */
+ count = 0;
+ p = key;
+ do {
+ ret = VIA_RNG_store(p);
+ p += ret;
+ count += ret;
+ } while (count < CIPHER_BLOCK_SIZE);
+
+ /* Get a random AES IV */
+ count = 0;
+ p = iv;
+ do {
+ ret = VIA_RNG_store(p);
+ p += ret;
+ count += ret;
+ } while (count < CIPHER_BLOCK_SIZE);
+
+ /* Get a block of random bytes */
+ count = 0;
+ p = in;
+ do {
+ ret = VIA_RNG_store(p);
+ p += ret;
+ count += ret;
+ } while (count < RANDOM_BLOCK_SIZE);
+
+ /* This is a Davies-Meyer hash of the most paranoid variety; the
+ * key, IV and the data are all read directly from the hardware RNG.
+ * All of these are used precisely once.
+ */
+ VIA_ACE_cbc(in, out, RANDOM_BLOCK_SIZE/CIPHER_BLOCK_SIZE,
+ key, &acw, iv);
+ for (i = 0; i < RANDOM_BLOCK_SIZE; i++)
+ out[i] ^= in[i];
+
+ c = MIN(RANDOM_BLOCK_SIZE, c);
+ memcpy(buf, out, (size_t)c);
+
return (c);
}
OpenPOWER on IntegriCloud