nehemiah.c revision 192774
14Srgrimes/*- 24Srgrimes * Copyright (c) 2004 Mark R V Murray 34Srgrimes * All rights reserved. 44Srgrimes * 54Srgrimes * Redistribution and use in source and binary forms, with or without 64Srgrimes * modification, are permitted provided that the following conditions 74Srgrimes * are met: 84Srgrimes * 1. Redistributions of source code must retain the above copyright 94Srgrimes * notice, this list of conditions and the following disclaimer 104Srgrimes * in this position and unchanged. 114Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 124Srgrimes * notice, this list of conditions and the following disclaimer in the 134Srgrimes * documentation and/or other materials provided with the distribution. 144Srgrimes * 154Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 164Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 174Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 184Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 194Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 204Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 214Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 224Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 234Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 244Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 254Srgrimes * 264Srgrimes */ 274Srgrimes 284Srgrimes#include <sys/cdefs.h> 294Srgrimes__FBSDID("$FreeBSD: head/sys/dev/random/nehemiah.c 192774 2009-05-25 22:50:11Z markm $"); 304Srgrimes 314Srgrimes#include <sys/param.h> 324Srgrimes#include <sys/time.h> 334Srgrimes#include <sys/lock.h> 344Srgrimes#include <sys/mutex.h> 354Srgrimes#include <sys/selinfo.h> 36619Srgrimes#include <sys/systm.h> 3716300Spst 384Srgrimes#include <dev/random/randomdev.h> 394Srgrimes 403185Ssos#define RANDOM_BLOCK_SIZE 256 413185Ssos#define CIPHER_BLOCK_SIZE 16 423185Ssos 433185Ssosstatic void random_nehemiah_init(void); 443185Ssosstatic void random_nehemiah_deinit(void); 452913Sachestatic int random_nehemiah_read(void *, int); 462913Sache 474Srgrimesstruct random_systat random_nehemiah = { 484Srgrimes .ident = "Hardware, VIA Nehemiah", 494Srgrimes .init = random_nehemiah_init, 5013228Swollman .deinit = random_nehemiah_deinit, 5116299Spst .read = random_nehemiah_read, 5213228Swollman .write = (random_write_func_t *)random_null_func, 532056Swollman .reseed = (random_reseed_func_t *)random_null_func, 542056Swollman .seeded = 1, 552056Swollman}; 562056Swollman 5715508Sbdeunion VIA_ACE_CW { 5815508Sbde uint64_t raw; 594180Sbde struct { 6015508Sbde u_int round_count : 4; 6115508Sbde u_int algorithm_type : 3; 6215508Sbde u_int key_generation_type : 1; 6315508Sbde u_int intermediate : 1; 642056Swollman u_int decrypt : 1; 6515508Sbde u_int key_size : 2; 662056Swollman u_int filler0 : 20; 672056Swollman u_int filler1 : 32; 687090Sbde u_int filler2 : 32; 692056Swollman u_int filler3 : 32; 702056Swollman } field; 714Srgrimes}; 722873Sbde 732873Sbde/* The extra 7 is to allow an 8-byte write on the last byte of the 742873Sbde * arrays. The ACE wants the AES data 16-byte/128-bit aligned, and 752873Sbde * it _always_ writes n*64 bits. The RNG does not care about alignment, 762873Sbde * and it always writes n*32 bits or n*64 bits. 772913Sache */ 782873Sbdestatic uint8_t key[CIPHER_BLOCK_SIZE+7] __aligned(16); 7915508Sbdestatic uint8_t iv[CIPHER_BLOCK_SIZE+7] __aligned(16); 804Srgrimesstatic uint8_t in[RANDOM_BLOCK_SIZE+7] __aligned(16); 814180Sbdestatic uint8_t out[RANDOM_BLOCK_SIZE+7] __aligned(16); 824180Sbde 834180Sbdestatic union VIA_ACE_CW acw __aligned(16); 844180Sbde 854180Sbdestatic struct mtx random_nehemiah_mtx; 864180Sbde 874180Sbde/* ARGSUSED */ 884180Sbdestatic __inline size_t 894180SbdeVIA_RNG_store(void *buf) 904180Sbde{ 914180Sbde#ifdef __GNUCLIKE_ASM 924180Sbde uint32_t retval = 0; 934180Sbde uint32_t rate = 0; 944180Sbde 954180Sbde /* The .byte line is really VIA C3 "xstore" instruction */ 964180Sbde __asm __volatile( 974180Sbde "movl $0,%%edx \n\t" 984180Sbde ".byte 0x0f, 0xa7, 0xc0" 9915045Sache : "=a" (retval), "+d" (rate), "+D" (buf) 10015045Sache : 10115045Sache : "memory" 10215045Sache ); 1038448Sbde if (rate == 0) 10413000Sdg return (retval&0x1f); 10515508Sbde#endif 10612533Swollman return (0); 10711452Swollman} 10811452Swollman 10913758Swollman/* ARGSUSED */ 1102017Swollmanstatic __inline void 11115345SnateVIA_ACE_cbc(void *in, void *out, size_t count, void *key, union VIA_ACE_CW *cw, void *iv) 1125291Sbde{ 1134180Sbde#ifdef __GNUCLIKE_ASM 1144180Sbde /* The .byte line is really VIA C3 "xcrypt-cbc" instruction */ 1154180Sbde __asm __volatile( 1161390Ssos "pushf \n\t" 1174180Sbde "popf \n\t" 1185291Sbde "rep \n\t" 1194180Sbde ".byte 0x0f, 0xa7, 0xc8" 1204180Sbde : "+a" (iv), "+c" (count), "+D" (out), "+S" (in) 1214180Sbde : "b" (key), "d" (cw) 1224180Sbde : "cc", "memory" 1234180Sbde ); 1244180Sbde#endif 1254180Sbde} 1264180Sbde 1274180Sbdestatic void 1284180Sbderandom_nehemiah_init(void) 1294180Sbde{ 1304180Sbde acw.raw = 0ULL; 1314180Sbde acw.field.round_count = 12; 13215345Snate 13315508Sbde mtx_init(&random_nehemiah_mtx, "random nehemiah", NULL, MTX_DEF); 13415508Sbde} 13515508Sbde 13615508Sbdevoid 13715508Sbderandom_nehemiah_deinit(void) 1384180Sbde{ 1394180Sbde mtx_destroy(&random_nehemiah_mtx); 1404180Sbde} 1414180Sbde 1423185Ssosstatic int 143798Swollmanrandom_nehemiah_read(void *buf, int c) 1443185Ssos{ 1451390Ssos int i; 1461549Srgrimes size_t count, ret; 1478448Sbde uint8_t *p; 1481549Srgrimes 1493185Ssos mtx_lock(&random_nehemiah_mtx); 15012724Sphk 1513185Ssos /* Get a random AES key */ 1522074Swollman count = 0; 1531549Srgrimes p = key; 1541442Ssos do { 1551442Ssos ret = VIA_RNG_store(p); 1568448Sbde p += ret; 1571442Ssos count += ret; 1581442Ssos } while (count < CIPHER_BLOCK_SIZE); 1594180Sbde 1604180Sbde /* Get a random AES IV */ 1611549Srgrimes count = 0; 1628448Sbde p = iv; 1634180Sbde do { 1641390Ssos ret = VIA_RNG_store(p); 1651442Ssos p += ret; 1661442Ssos count += ret; 1678448Sbde } while (count < CIPHER_BLOCK_SIZE); 1684180Sbde 1694180Sbde /* Get a block of random bytes */ 1704180Sbde count = 0; 1711442Ssos p = in; 1724180Sbde do { 1734180Sbde ret = VIA_RNG_store(p); 1744180Sbde p += ret; 1751442Ssos count += ret; 1764180Sbde } while (count < RANDOM_BLOCK_SIZE); 1771442Ssos 1781442Ssos /* This is a Davies-Meyer hash of the most paranoid variety; the 1791442Ssos * key, IV and the data are all read directly from the hardware RNG. 1801442Ssos * All of these are used precisely once. 1814180Sbde */ 1824180Sbde VIA_ACE_cbc(in, out, RANDOM_BLOCK_SIZE/CIPHER_BLOCK_SIZE, 1831549Srgrimes key, &acw, iv); 1848448Sbde for (i = 0; i < RANDOM_BLOCK_SIZE; i++) 1855291Sbde out[i] ^= in[i]; 1864180Sbde 1874180Sbde c = MIN(RANDOM_BLOCK_SIZE, c); 1881442Ssos memcpy(buf, out, (size_t)c); 1894180Sbde 1904180Sbde mtx_unlock(&random_nehemiah_mtx); 1914180Sbde return (c); 1924180Sbde} 1931442Ssos