/* * Copyright (c) 2012-2014, Intel Corporation * Authors: Richard B. Hill , * H. Peter Anvin , * John P. Mechalas * * 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 _GNU_SOURCE #ifndef HAVE_CONFIG_H #error Invalid or missing autoconf build environment #endif #include "rng-tools-config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBGCRYPT #include #endif #include "rngd.h" #include "fips.h" #include "exits.h" #include "rngd_entsource.h" #if defined(__i386__) || defined(__x86_64__) /* Struct for CPUID return values */ struct cpuid { uint32_t eax, ecx, edx, ebx; }; /* * Get data from RDRAND. The count is in bytes, but the function can * round *up* the count to the nearest 4- or 8-byte boundary. The caller * needs to take that into account. count must not be zero. * * The function returns the number of bytes actually written. */ extern unsigned int x86_rdrand_bytes(void *ptr, unsigned int count); /* * Get data from RDSEED (preferentially) or RDRAND into separate * buffers. Returns when either buffer is full. Same conditions * apply as for x86_rdrand_bytes(). */ extern void x86_rdseed_or_rdrand_bytes(void *seed_ptr, unsigned int *seed_cnt, void *rand_ptr, unsigned int *rand_cnt); /* Condition RDRAND for seed-grade entropy */ extern void x86_aes_mangle(void *data, void *state); /* Expand an AES key for future use */ extern void x86_aes_expand_key(const void *key); #ifdef __x86_64__ typedef uint64_t unative_t; /* x86-64 or x32 */ #else typedef uint32_t unative_t; /* i386 */ #endif /* Checking eflags to confirm cpuid instruction available */ static inline int x86_has_eflag(unative_t flag) { unative_t f0, f1; asm("pushf ; " "pushf ; " "pop %0 ; " "mov %0,%1 ; " "xor %2,%1 ; " "push %1 ; " "popf ; " "pushf ; " "pop %1 ; " "popf" : "=&r" (f0), "=&r" (f1) : "ri" (flag)); return !!((f0^f1) & flag); } static inline int x86_has_cpuid(void) { #ifdef __i386__ return x86_has_eflag(1 << 21); /* ID flag */ #else return 1; /* x86-64 always has CPUID */ #endif } /* Calling cpuid instruction to verify rdrand and aes-ni 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 drng in chunks of 128 bytes for AES scrambling */ #define AES_BLOCK 16 #define CHUNK_SIZE (AES_BLOCK*8) /* 8 parallel streams */ #define RDRAND_ROUNDS 512 /* 512:1 data reduction */ static unsigned char iv_buf[CHUNK_SIZE] __attribute__((aligned(128))); static int have_aesni, have_rdseed; /* Necessary if we have RDRAND but not AES-NI */ #ifdef HAVE_LIBGCRYPT #define MIN_GCRYPT_VERSION "1.0.0" static gcry_cipher_hd_t gcry_cipher_hd; #endif static inline int gcrypt_mangle(unsigned char *tmp) { #ifdef HAVE_LIBGCRYPT gcry_error_t gcry_error; /* Encrypt tmp in-place. */ gcry_error = gcry_cipher_encrypt(gcry_cipher_hd, tmp, AES_BLOCK * RDRAND_ROUNDS, NULL, 0); if (gcry_error) { message(LOG_DAEMON|LOG_ERR, "gcry_cipher_encrypt error: %s\n", gcry_strerror(gcry_error)); return -1; } return 0; #else (void)tmp; return -1; #endif } int xread_drng(void *buf, size_t size, struct rng *ent_src) { static unsigned char rdrand_buf[CHUNK_SIZE * RDRAND_ROUNDS] __attribute__((aligned(128))); static unsigned int rdrand_bytes = 0; unsigned char rdseed_buf[CHUNK_SIZE] __attribute__((aligned(128))); char *p = buf; size_t chunk; unsigned char *rdrand_ptr, *data; unsigned int rand_bytes, seed_bytes; (void)ent_src; while (size) { rand_bytes = (have_aesni ? CHUNK_SIZE * RDRAND_ROUNDS : AES_BLOCK * RDRAND_ROUNDS) - rdrand_bytes; if (rand_bytes == 0) { /* We already have a full rdrand_buf */ if (have_aesni) { x86_aes_mangle(rdrand_buf, iv_buf); data = iv_buf; chunk = CHUNK_SIZE; } else if (!gcrypt_mangle(rdrand_buf)) { data = rdrand_buf + AES_BLOCK * (RDRAND_ROUNDS - 1); chunk = AES_BLOCK; } else { return -1; } rdrand_bytes = 0; goto have_data; } rdrand_ptr = rdrand_buf + rdrand_bytes; if (have_rdseed) { seed_bytes = sizeof rdseed_buf; x86_rdseed_or_rdrand_bytes(rdseed_buf, &seed_bytes, rdrand_ptr, &rand_bytes); } else { rand_bytes = x86_rdrand_bytes(rdrand_ptr, rand_bytes); seed_bytes = 0; } rdrand_bytes += rand_bytes; if (seed_bytes) { data = rdseed_buf; chunk = seed_bytes; goto have_data; } continue; /* No data ready yet */ have_data: chunk = (chunk > size) ? size : chunk; memcpy(p, data, chunk); p += chunk; size -= chunk; } return 0; } static int init_aesni(const void *key) { if (!have_aesni) return 1; x86_aes_expand_key(key); return 0; } static int init_gcrypt(const void *key) { #ifdef HAVE_LIBGCRYPT gcry_error_t gcry_error; if (!gcry_check_version(MIN_GCRYPT_VERSION)) { message(LOG_DAEMON|LOG_ERR, "libgcrypt version mismatch: have %s, require >= %s\n", gcry_check_version(NULL), MIN_GCRYPT_VERSION); return 1; } gcry_error = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0); if (!gcry_error) gcry_error = gcry_cipher_setkey(gcry_cipher_hd, key, AES_BLOCK); if (!gcry_error) { /* * Only need the first 16 bytes of iv_buf. AES-NI can * encrypt multiple blocks in parallel but we can't. */ gcry_error = gcry_cipher_setiv(gcry_cipher_hd, iv_buf, AES_BLOCK); } if (gcry_error) { message(LOG_DAEMON|LOG_ERR, "could not set key or IV: %s\n", gcry_strerror(gcry_error)); gcry_cipher_close(gcry_cipher_hd); return 1; } return 0; #else (void)key; return 1; #endif } /* * Confirm RDRAND capabilities for drng entropy source */ int init_drng_entropy_source(struct rng *ent_src) { struct cpuid info; /* We need RDRAND, but AESni is optional */ const uint32_t features_ecx1_rdrand = 1 << 30; const uint32_t features_ecx1_aesni = 1 << 25; const uint32_t features_ebx7_rdseed = 1 << 18; uint32_t max_cpuid_leaf; static unsigned char key[AES_BLOCK] = { 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70, 0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0 }; /* AES data reduction key */ unsigned char xkey[AES_BLOCK]; /* Material to XOR into the key */ int fd; int i; if (!x86_has_cpuid()) return 1; /* No CPUID instruction */ cpuid(0, 0, &info); max_cpuid_leaf = info.eax; if (max_cpuid_leaf < 1) return 1; cpuid(1, 0, &info); if (!(info.ecx & features_ecx1_rdrand)) return 1; have_aesni = !!(info.ecx & features_ecx1_aesni); have_rdseed = 0; if (max_cpuid_leaf >= 7) { cpuid(7, 0, &info); if (info.ebx & features_ebx7_rdseed) have_rdseed = 1; } /* Randomize the AES data reduction key the best we can */ if (x86_rdrand_bytes(xkey, sizeof xkey) != sizeof xkey) return 1; fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { read(fd, key, sizeof key); close(fd); } for (i = 0; i < (int)sizeof key; i++) key[i] ^= xkey[i]; /* Initialize the IV buffer */ if (x86_rdrand_bytes(iv_buf, CHUNK_SIZE) != CHUNK_SIZE) return 1; if (init_aesni(key) && init_gcrypt(key)) return 1; /* We need one crypto or the other... */ 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 /* Not i386 or x86-64 */ int init_drng_entropy_source(struct rng *ent_src) { (void)ent_src; return 1; } int xread_drng(void *buf, size_t size, struct rng *ent_src) { (void)buf; (void)size; (void)ent_src; return -1; } #endif /* Not i386 or x86-64 */