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