diff options
author | Brad Hill <richard.b.hill@intel.com> | 2012-07-31 12:26:51 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2012-07-31 12:26:51 -0400 |
commit | 2a01453686f065ac95916fe49a02407914e05dd4 (patch) | |
tree | c6b8cc3c65913c3eabf2032a4bc8f163d0755edb | |
parent | a9b9bb1df997bd979ad2c6f1f72563d8eec7930b (diff) | |
download | rng-tools-2a01453686f065ac95916fe49a02407914e05dd4.tar.gz |
Add RDRAND support
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | rdrand_asm.S | 195 | ||||
-rw-r--r-- | rngd.8.in | 4 | ||||
-rw-r--r-- | rngd.c | 34 | ||||
-rw-r--r-- | rngd.h | 1 | ||||
-rw-r--r-- | rngd_entsource.c | 148 | ||||
-rw-r--r-- | rngd_entsource.h | 17 | ||||
-rw-r--r-- | rngd_linux.c | 1 |
9 files changed, 390 insertions, 14 deletions
diff --git a/Makefile.am b/Makefile.am index 6822181..b86042e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ man_MANS = rngd.8 rngtest.1 noinst_LIBRARIES = librngd.a rngd_SOURCES = rngd.h rngd.c rngd_entsource.h rngd_entsource.c \ - rngd_linux.h rngd_linux.c util.c + rngd_linux.h rngd_linux.c util.c rdrand_asm.S rngd_LDADD = librngd.a rngtest_SOURCES = exits.h stats.h stats.c rngtest.c diff --git a/configure.ac b/configure.ac index 7b4a409..aff6103 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,8 @@ dnl ----------------- dnl Configure options dnl ----------------- +AM_PROG_AS + dnl -------------------------- dnl autoconf output generation dnl -------------------------- diff --git a/rdrand_asm.S b/rdrand_asm.S new file mode 100644 index 0000000..c5567d8 --- /dev/null +++ b/rdrand_asm.S @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2011, Intel Corporation + * Authors: Fenghua Yu <fenghua.yu@intel.com>, + * H. Peter Anvin <hpa@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#define ENTRY(x) \ + .balign 64 ; \ + .globl x ; \ +x: + +#define ENDPROC(x) \ + .size x, .-x ; \ + .type x, @function + +#define RDRAND_RETRY_LIMIT 10 + +#if defined(__x86_64__) + +ENTRY(x86_rdrand_nlong) +1: + mov $RDRAND_RETRY_LIMIT, %eax +2: + .byte 0x48,0x0f,0xc7,0xf2 /* rdrand %rdx */ + jnc 3f + mov %rdx, (%rdi) + add $8, %rdi + sub $1, %esi + jnz 1b + ret +3: + sub $1, %eax + rep;nop + jnz 2b + ret +ENDPROC(x86_rdrand_nlong) + +#define SETPTR(var,ptr) leaq var(%rip),ptr +#define PTR0 %rdi +#define PTR1 %rsi +#define PTR2 %rcx +#define NPTR2 1 /* %rcx = %r1, only 0-7 valid here */ + +#elif defined(__i386__) + +ENTRY(x86_rdrand_nlong) + push %ebp + mov %esp, %ebp + push %edi + movl 8(%ebp), %ecx + movl 12(%ebp), %edx +1: + mov $RDRAND_RETRY_LIMIT, %eax +2: + .byte 0x0f,0xc7,0xf7 /* rdrand %edi */ + jnc 3f + mov %edi, (%ecx) + add $4, %ecx + sub $1, %edx + jnz 2b + pop %edi + pop %ebp + ret +3: + sub $1, %eax + rep;nop + jnz 2b + pop %edi + pop %ebp + ret +ENDPROC(x86_rdrand_nlong) + +#define SETPTR(var,ptr) movl $(var),ptr +#define PTR0 %eax +#define PTR1 %edx +#define PTR2 %ecx +#define NPTR2 1 /* %rcx = %r1 */ + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +ENTRY(x86_aes_mangle) +#if defined(__i386__) + push %ebp + mov %esp, %ebp + movl 8(%ebp), %eax + movl 12(%ebp), %edx +#endif + + SETPTR(aes_round_keys, PTR2) + + movdqa (0*16)(PTR0), %xmm0 + movdqa (1*16)(PTR0), %xmm1 + movdqa (2*16)(PTR0), %xmm2 + movdqa (3*16)(PTR0), %xmm3 + movdqa (4*16)(PTR0), %xmm4 + movdqa (5*16)(PTR0), %xmm5 + movdqa (6*16)(PTR0), %xmm6 + movdqa (7*16)(PTR0), %xmm7 + + pxor (0*16)(PTR1), %xmm0 + pxor (1*16)(PTR1), %xmm1 + pxor (2*16)(PTR1), %xmm2 + pxor (3*16)(PTR1), %xmm3 + pxor (4*16)(PTR1), %xmm4 + pxor (5*16)(PTR1), %xmm5 + pxor (6*16)(PTR1), %xmm6 + pxor (7*16)(PTR1), %xmm7 + + .rept 10 + .byte 0x66,0x0f,0x38,0xdc,0x00+NPTR2 /* aesenc (PTR2), %xmm0 */ + .byte 0x66,0x0f,0x38,0xdc,0x08+NPTR2 /* aesenc (PTR2), %xmm1 */ + .byte 0x66,0x0f,0x38,0xdc,0x10+NPTR2 /* aesenc (PTR2), %xmm2 */ + .byte 0x66,0x0f,0x38,0xdc,0x18+NPTR2 /* aesenc (PTR2), %xmm3 */ + .byte 0x66,0x0f,0x38,0xdc,0x20+NPTR2 /* aesenc (PTR2), %xmm4 */ + .byte 0x66,0x0f,0x38,0xdc,0x28+NPTR2 /* aesenc (PTR2), %xmm5 */ + .byte 0x66,0x0f,0x38,0xdc,0x30+NPTR2 /* aesenc (PTR2), %xmm6 */ + .byte 0x66,0x0f,0x38,0xdc,0x38+NPTR2 /* aesenc (PTR2), %xmm7 */ + add $16, PTR2 + .endr + + .byte 0x66,0x0f,0x38,0xdd,0x00+NPTR2 /* aesenclast (PTR2), %xmm0 */ + .byte 0x66,0x0f,0x38,0xdd,0x08+NPTR2 /* aesenclast (PTR2), %xmm1 */ + .byte 0x66,0x0f,0x38,0xdd,0x10+NPTR2 /* aesenclast (PTR2), %xmm2 */ + .byte 0x66,0x0f,0x38,0xdd,0x18+NPTR2 /* aesenclast (PTR2), %xmm3 */ + .byte 0x66,0x0f,0x38,0xdd,0x20+NPTR2 /* aesenclast (PTR2), %xmm4 */ + .byte 0x66,0x0f,0x38,0xdd,0x28+NPTR2 /* aesenclast (PTR2), %xmm5 */ + .byte 0x66,0x0f,0x38,0xdd,0x30+NPTR2 /* aesenclast (PTR2), %xmm6 */ + .byte 0x66,0x0f,0x38,0xdd,0x38+NPTR2 /* aesenclast (PTR2), %xmm7 */ + + movdqa %xmm0, (0*16)(PTR0) + movdqa %xmm1, (1*16)(PTR0) + movdqa %xmm2, (2*16)(PTR0) + movdqa %xmm3, (3*16)(PTR0) + movdqa %xmm4, (4*16)(PTR0) + movdqa %xmm5, (5*16)(PTR0) + movdqa %xmm6, (6*16)(PTR0) + movdqa %xmm7, (7*16)(PTR0) + + movdqa %xmm0, (0*16)(PTR1) + movdqa %xmm1, (1*16)(PTR1) + movdqa %xmm2, (2*16)(PTR1) + movdqa %xmm3, (3*16)(PTR1) + movdqa %xmm4, (4*16)(PTR1) + movdqa %xmm5, (5*16)(PTR1) + movdqa %xmm6, (6*16)(PTR1) + movdqa %xmm7, (7*16)(PTR1) + +#if defined(__i386__) + pop %ebp +#endif + ret +ENDPROC(x86_aes_mangle) + /* + * AES round keys for an arbitrary key: + * 00102030405060708090A0B0C0D0E0F0 + */ + .section ".rodata","a" + .balign 16 +aes_round_keys: + .long 0x00102030, 0x40506070, 0x8090A0B0, 0xC0D0E0F0 + .long 0x89D810E8, 0x855ACE68, 0x2D1843D8, 0xCB128FE4 + .long 0x4915598F, 0x55E5D7A0, 0xDACA94FA, 0x1F0A63F7 + .long 0xFA636A28, 0x25B339C9, 0x40668A31, 0x57244D17 + .long 0x24724023, 0x6966B3FA, 0x6ED27532, 0x88425B6C + .long 0xC81677BC, 0x9B7AC93B, 0x25027992, 0xB0261996 + .long 0xC62FE109, 0xF75EEDC3, 0xCC79395D, 0x84F9CF5D + .long 0xD1876C0F, 0x79C4300A, 0xB45594AD, 0xD66FF41F + .long 0xFDE3BAD2, 0x05E5D0D7, 0x3547964E, 0xF1FE37F1 + .long 0xBD6E7C3D, 0xF2B5779E, 0x0B61216E, 0x8B10B689 + .long 0x69C4E0D8, 0x6A7B0430, 0xD8CDB780, 0x70B4C55A + .size aes_round_keys, .-aes_round_keys + + .bss + .balign 16 +aes_fwd_state: + .space 16 + .size aes_fwd_state, .-aes_fwd_state + +#endif /* i386 or x86_64 */ @@ -14,6 +14,7 @@ rngd \- Check and feed random data from hardware device to kernel random device [\fB\-r\fR, \fB\-\-rng-device=\fIfile\fR] [\fB\-s\fR, \fB\-\-random-step=\fInnn\fR] [\fB\-W\fR, \fB\-\-fill-watermark=\fInnn\fR] +[\fB\-d\fR, \fB\-\-no-drng=\fI1|0\fR] [\fB\-n\fR, \fB\-\-no-tpm=\fI1|0\fR] [\fB\-q\fR, \fB\-\-quiet\fR] [\fB\-v\fR, \fB\-\-verbose\fR] @@ -71,6 +72,9 @@ entropy pool. Low values will hurt system performance during entropy starves. Do not set \fIfill-watermark\fR above the size of the entropy pool (usually 4096 bits). .TP +\fB\-d\fI 1|0\fR, \fB\-\-no-drng=\fI1|0\fR +Do not use drng as a source of random number input (default:0) +.TP \fB\-n\fI 1|0\fR, \fB\-\-no-tpm=\fI1|0\fR Do not use tpm as a source of random number input (default:0) .TP @@ -99,8 +99,11 @@ static struct argp_option options[] = { { "verbose" ,'v', 0, 0, "Report available entropy sources" }, + { "no-drng", 'd', "1|0", 0, + "Do not use drng as a source of random number input (default: 0)" }, + { "no-tpm", 'n', "1|0", 0, - "do not use tpm as a source of random number input (default: 0)" }, + "Do not use tpm as a source of random number input (default: 0)" }, { 0 }, }; @@ -111,6 +114,7 @@ static struct arguments default_arguments = { .random_step = 64, .fill_watermark = 2048, .daemon = true, + .enable_drng = true, .enable_tpm = true, .quiet = false, .verbose = false, @@ -123,6 +127,12 @@ static struct rng rng_default = { .xread = xread, }; +static struct rng rng_drng = { + .rng_name = "drng", + .rng_fd = -1, + .xread = xread_drng, +}; + static struct rng rng_tpm = { .rng_name = "/dev/tpm0", .rng_fd = -1, @@ -170,6 +180,14 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) case 'v': arguments->verbose = true; break; + case 'd': { + int n; + if ((sscanf(arg,"%i", &n) == 0) || ((n | 1)!=1)) + argp_usage(state); + else + arguments->enable_drng = false; + break; + } case 'n': { int n; if ((sscanf(arg,"%i", &n) == 0) || ((n | 1)!=1)) @@ -240,7 +258,7 @@ static void do_loop(int random_step) rc = update_kernel_random(random_step, buf, iter->fipsctx); if (rc == 0) - continue; /* succeeded, work done */ + break; /* succeeded, work done */ iter->failures++; if (iter->failures == MAX_RNG_FAILURES) { @@ -267,8 +285,9 @@ static void term_signal(int signo) int main(int argc, char **argv) { - int rc_rng = 0; - int rc_tpm = 0; + int rc_rng = 1; + int rc_drng = 1; + int rc_tpm = 1; int pid_fd = -1; openlog("rngd", 0, LOG_DAEMON); @@ -278,10 +297,12 @@ int main(int argc, char **argv) /* Init entropy sources, and open TRNG device */ rc_rng = init_entropy_source(&rng_default); + if (arguments->enable_drng) + rc_drng = init_drng_entropy_source(&rng_drng); if (arguments->enable_tpm) rc_tpm = init_tpm_entropy_source(&rng_tpm); - if (rc_rng && rc_tpm) { + if (rc_rng && rc_drng && rc_tpm) { if (!arguments->quiet) { message(LOG_DAEMON|LOG_ERR, "can't open entropy source(tpm or intel/amd rng)"); @@ -295,11 +316,14 @@ int main(int argc, char **argv) printf("Available entropy sources:\n"); if (!rc_rng) printf("\tIntel/AMD hardware rng\n"); + if (!rc_drng) + printf("\tDRNG\n"); if (!rc_tpm) printf("\tTPM\n"); } if (rc_rng + && (rc_drng || !arguments->enable_drng) && (rc_tpm || !arguments->enable_tpm)) { if (!arguments->quiet) message(LOG_DAEMON|LOG_ERR, @@ -48,6 +48,7 @@ struct arguments { bool quiet; bool verbose; bool daemon; + bool enable_drng; bool enable_tpm; }; extern struct arguments *arguments; diff --git a/rngd_entsource.c b/rngd_entsource.c index 0b2bb59..9a37081 100644 --- a/rngd_entsource.c +++ b/rngd_entsource.c @@ -35,6 +35,7 @@ #include <errno.h> #include <syslog.h> #include <string.h> +#include <stddef.h> #include "rngd.h" #include "fips.h" @@ -46,6 +47,49 @@ * it is 14 bytes.*/ #define TPM_GET_RNG_OVERHEAD 14 +/* Checking eflags to confirm cpuid instruction available */ +/* Only necessary for 32 bit processors */ +#if defined (__i386__) +int x86_has_eflag(uint32_t flag) +{ + uint32_t f0, f1; + asm("pushfl ; " + "pushfl ; " + "popl %0 ; " + "movl %0,%1 ; " + "xorl %2,%1 ; " + "pushl %1 ; " + "popfl ; " + "pushfl ; " + "popl %1 ; " + "popfl" + : "=&r" (f0), "=&r" (f1) + : "ri" (flag)); + return !!((f0^f1) & flag); +} +#endif + +/* Calling cpuid instruction to verify rdrand capability */ +static void cpuid(unsigned int leaf, unsigned int subleaf, struct cpuid *out) +{ +#ifdef __i386__ + /* %ebx is a forbidden register if we compile with -fPIC or -fPIE */ + asm volatile("movl %%ebx,%0 ; cpuid ; xchgl %%ebx,%0" + : "=r" (out->ebx), + "=a" (out->eax), + "=c" (out->ecx), + "=d" (out->edx) + : "a" (leaf), "c" (subleaf)); +#else + asm volatile("cpuid" + : "=b" (out->ebx), + "=a" (out->eax), + "=c" (out->ecx), + "=d" (out->edx) + : "a" (leaf), "c" (subleaf)); +#endif +} + /* Read data from the entropy source */ int xread(void *buf, size_t size, struct rng *ent_src) { @@ -69,6 +113,59 @@ int xread(void *buf, size_t size, struct rng *ent_src) return 0; } +/* Initialization vector and msg sizes for standard AES usage */ +#define IV_SIZE (16*1) +#define MSG_SIZE (16*7) +#define CHUNK_SIZE (16*8) + +/* Read data from the drng + * in chunks of 128 bytes for AES scrambling */ +int xread_drng(void *buf, size_t size, struct rng *ent_src) +{ + size_t psize = size; + size_t off = 0; + ssize_t r = 0; + int rdrand_round_count = size / 128; + + static unsigned char iv_buf[IV_SIZE] __attribute__((aligned(128))); + static unsigned char m_buf[MSG_SIZE] __attribute__((aligned(128))); + static unsigned char tmp[CHUNK_SIZE] __attribute__((aligned(128))); + static unsigned char fwd[CHUNK_SIZE] __attribute__((aligned(128))); + int i; + + while (size > 0 && size <= psize) { + for (i = 0; i < rdrand_round_count && size <= psize; i++) { + if (!x86_rdrand_nlong(iv_buf, sizeof(iv_buf)/sizeof(long))) { + r = -1; + break; + } + if (!x86_rdrand_nlong(m_buf, sizeof(m_buf)/sizeof(long))) { + r = -1; + break; + } + memcpy(tmp, iv_buf, IV_SIZE); + memcpy(tmp+IV_SIZE, m_buf, MSG_SIZE); + + x86_aes_mangle(tmp, fwd); + r = (sizeof(tmp) > size)? size : sizeof(tmp); + + if (r <= 0) + break; + memcpy(buf+off, tmp, r); + off += r; + size -= r; + } + if (r <= 0) + break; + } + + if (size > 0 && size < psize) { + message(LOG_DAEMON|LOG_ERR, "read error\n"); + return -1; + } + return 0; +} + /* tpm rng read call to kernel has 13 bytes of overhead * the logic to process this involves reading to a temporary_buf * and copying the no generated to buf */ @@ -88,6 +185,7 @@ int xread_tpm(void *buf, size_t size, struct rng *ent_src) ent_src->rng_fd = open(ent_src->rng_name, O_RDWR); if (ent_src->rng_fd == -1) { + message(LOG_ERR|LOG_INFO,"Unable to open file: %s",ent_src->rng_name); return -1; } @@ -95,6 +193,7 @@ int xread_tpm(void *buf, size_t size, struct rng *ent_src) memset(temp_buf, 0, (size+TPM_GET_RNG_OVERHEAD)); if (temp_buf == NULL) { message(LOG_ERR|LOG_INFO,"No memory"); + close(ent_src->rng_fd); return -1; } /* 32 bits has been reserved for random byte size */ @@ -125,14 +224,13 @@ int xread_tpm(void *buf, size_t size, struct rng *ent_src) goto error_out; } r = read(ent_src->rng_fd, temp_buf,size); - if (r <= 0) { + r = (r - TPM_GET_RNG_OVERHEAD); + if(r <= 0) { message(LOG_ERR|LOG_INFO, "Error reading from TPM, no entropy gathered"); retval = -1; goto error_out; } - - r = (r - TPM_GET_RNG_OVERHEAD); bytes_read = bytes_read + r; if (bytes_read > size) { memcpy(offset,temp_buf + TPM_GET_RNG_OVERHEAD, @@ -159,10 +257,10 @@ static int discard_initial_data(struct rng *ent_src) * The kernel drivers should be doing this at device powerup, * but at least up to 2.4.24, it doesn't. */ unsigned char tempbuf[4]; - xread(tempbuf, sizeof tempbuf, ent_src); + xread(tempbuf, sizeof(tempbuf), ent_src); /* Return 32 bits of bootstrap data */ - xread(tempbuf, sizeof tempbuf, ent_src); + xread(tempbuf, sizeof(tempbuf), ent_src); return tempbuf[0] | (tempbuf[1] << 8) | (tempbuf[2] << 16) | (tempbuf[3] << 24); @@ -185,14 +283,50 @@ int init_entropy_source(struct rng *ent_src) } /* + * Confirm RDRAND capabilities for drng entropy source + */ +int init_drng_entropy_source(struct rng *ent_src) +{ +#if defined (__x86_64__) || defined(__i386__) + struct cpuid info; + int got_intel_cpu = 0; + +#if defined (__i386__) + uint32_t flag = 0x200000; + + if(!x86_has_eflag(flag)) return 1; //check 32 bit processor for cpuid +#endif + + cpuid(0,0, &info); + if(memcmp((char *)(&info.ebx), "Genu", 4) == 0 && + memcmp((char *)(&info.edx), "ineI", 4) == 0 && + memcmp((char *)(&info.ecx), "ntel", 4) == 0) { + got_intel_cpu = 1; + } + if(got_intel_cpu == 0) return 1; + + cpuid(1,0,&info); + if ((info.ecx & 0x40000000)!=0x40000000) return 1; + + src_list_add(ent_src); + /* Bootstrap FIPS tests */ + ent_src->fipsctx = malloc(sizeof(fips_ctx_t)); + fips_init(ent_src->fipsctx, 0); + return 0; +#else + //No intel processor, no drng + return 1; +#endif +} + +/* * Open tpm entropy source, and initialize it */ int init_tpm_entropy_source(struct rng *ent_src) { ent_src->rng_fd = open(ent_src->rng_name, O_RDWR); if (ent_src->rng_fd == -1) { - message(LOG_ERR|LOG_INFO,"Unable to open file: %s", - ent_src->rng_name); + message(LOG_ERR|LOG_INFO,"Unable to open file: %s",ent_src->rng_name); return 1; } src_list_add(ent_src); diff --git a/rngd_entsource.h b/rngd_entsource.h index dd1d116..64d8e48 100644 --- a/rngd_entsource.h +++ b/rngd_entsource.h @@ -26,20 +26,37 @@ #include <unistd.h> #include <stdint.h> +/* Struct for CPUID return values */ +struct cpuid { + uint32_t eax, ecx, edx, ebx; +}; + /* Logic and contexts */ extern fips_ctx_t fipsctx; /* Context for the FIPS tests */ extern fips_ctx_t tpm_fipsctx; /* Context for the tpm FIPS tests */ +/* Inline assembly to check eflags */ +/* Only necessary on 32 bit processor */ +#if defined (__i386__) +int x86_has_eflag(uint32_t flag); +#endif + +/* Inline assembly for CPUID call for RDRAND */ +extern int x86_rdrand_nlong(void *ptr, size_t count); /* RDRAND-access logic */ +extern void x86_aes_mangle(void *data, void *state); /* Conditioning RDRAND for seed-grade entropy */ + /* * Initialize entropy source and entropy conditioning * * sourcedev is the path to the entropy source */ extern int init_entropy_source(struct rng *); +extern int init_drng_entropy_source(struct rng *); extern int init_tpm_entropy_source(struct rng *); /* Read data from the entropy source */ extern int xread(void *buf, size_t size, struct rng *ent_src); +extern int xread_drng(void *buf, size_t size, struct rng *ent_src); extern int xread_tpm(void *buf, size_t size, struct rng *ent_src); #endif /* RNGD_ENTSOURCE__H */ diff --git a/rngd_linux.c b/rngd_linux.c index 9b76198..128d560 100644 --- a/rngd_linux.c +++ b/rngd_linux.c @@ -91,7 +91,6 @@ void random_add_entropy(void *buf, size_t size) void random_sleep() { - int ent_count; struct pollfd pfd = { fd: random_fd, events: POLLOUT, |