From f34e8fe1dd7edc5c723541e0ffd07058d9db1d35 Mon Sep 17 00:00:00 2001 From: markm Date: Sat, 17 Apr 2004 19:26:53 +0000 Subject: 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. --- sys/dev/random/nehemiah.c | 136 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 124 insertions(+), 12 deletions(-) (limited to 'sys') 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 __FBSDID("$FreeBSD$"); -#include +#include #include #include #include #include +#include #include +#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); } -- cgit v1.1