Blame random_seed.c

Packit ea8578
/*
Packit ea8578
 * random_seed.c
Packit ea8578
 *
Packit ea8578
 * Copyright (c) 2013 Metaparadigm Pte. Ltd.
Packit ea8578
 * Michael Clark <michael@metaparadigm.com>
Packit ea8578
 *
Packit ea8578
 * This library is free software; you can redistribute it and/or modify
Packit ea8578
 * it under the terms of the MIT license. See COPYING for details.
Packit ea8578
 *
Packit ea8578
 */
Packit ea8578
Packit ea8578
#include "strerror_override.h"
Packit ea8578
#include <stdio.h>
Packit ea8578
#include "config.h"
Packit ea8578
#include "random_seed.h"
Packit ea8578
Packit ea8578
#define DEBUG_SEED(s)
Packit ea8578
Packit ea8578
Packit ea8578
#if defined ENABLE_RDRAND
Packit ea8578
Packit ea8578
/* cpuid */
Packit ea8578
Packit ea8578
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
Packit ea8578
#define HAS_X86_CPUID 1
Packit ea8578
Packit ea8578
static void do_cpuid(int regs[], int h)
Packit ea8578
{
Packit ea8578
    __asm__ __volatile__(
Packit ea8578
#if defined __x86_64__
Packit ea8578
                         "pushq %%rbx;\n"
Packit ea8578
#else
Packit ea8578
                         "pushl %%ebx;\n"
Packit ea8578
#endif
Packit ea8578
                         "cpuid;\n"
Packit ea8578
#if defined __x86_64__
Packit ea8578
                         "popq %%rbx;\n"
Packit ea8578
#else
Packit ea8578
                         "popl %%ebx;\n"
Packit ea8578
#endif
Packit ea8578
                         : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
Packit ea8578
                         : "a"(h));
Packit ea8578
}
Packit ea8578
Packit ea8578
#elif defined _MSC_VER
Packit ea8578
Packit ea8578
#define HAS_X86_CPUID 1
Packit ea8578
#define do_cpuid __cpuid
Packit ea8578
Packit ea8578
#endif
Packit ea8578
Packit ea8578
/* has_rdrand */
Packit ea8578
Packit ea8578
#if HAS_X86_CPUID
Packit ea8578
Packit ea8578
static int has_rdrand()
Packit ea8578
{
Packit ea8578
    // CPUID.01H:ECX.RDRAND[bit 30] == 1
Packit ea8578
    int regs[4];
Packit ea8578
    do_cpuid(regs, 1);
Packit ea8578
    return (regs[2] & (1 << 30)) != 0;
Packit ea8578
}
Packit ea8578
Packit ea8578
#endif
Packit ea8578
Packit ea8578
/* get_rdrand_seed - GCC x86 and X64 */
Packit ea8578
Packit ea8578
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
Packit ea8578
Packit ea8578
#define HAVE_RDRAND 1
Packit ea8578
Packit ea8578
static int get_rdrand_seed()
Packit ea8578
{
Packit ea8578
    DEBUG_SEED("get_rdrand_seed");
Packit ea8578
    int _eax;
Packit ea8578
    // rdrand eax
Packit ea8578
    __asm__ __volatile__("1: .byte 0x0F\n"
Packit ea8578
                         "   .byte 0xC7\n"
Packit ea8578
                         "   .byte 0xF0\n"
Packit ea8578
                         "   jnc 1b;\n"
Packit ea8578
                         : "=a" (_eax));
Packit ea8578
    return _eax;
Packit ea8578
}
Packit ea8578
Packit ea8578
#endif
Packit ea8578
Packit ea8578
#if defined _MSC_VER
Packit ea8578
Packit ea8578
#if _MSC_VER >= 1700
Packit ea8578
#define HAVE_RDRAND 1
Packit ea8578
Packit ea8578
/* get_rdrand_seed - Visual Studio 2012 and above */
Packit ea8578
Packit ea8578
static int get_rdrand_seed()
Packit ea8578
{
Packit ea8578
    DEBUG_SEED("get_rdrand_seed");
Packit ea8578
    int r;
Packit ea8578
    while (_rdrand32_step(&r) == 0);
Packit ea8578
    return r;
Packit ea8578
}
Packit ea8578
Packit ea8578
#elif defined _M_IX86
Packit ea8578
#define HAVE_RDRAND 1
Packit ea8578
Packit ea8578
/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */
Packit ea8578
Packit ea8578
static int get_rdrand_seed()
Packit ea8578
{
Packit ea8578
	DEBUG_SEED("get_rdrand_seed");
Packit ea8578
	int _eax;
Packit ea8578
retry:
Packit ea8578
	// rdrand eax
Packit ea8578
	__asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
Packit ea8578
	__asm jnc retry
Packit ea8578
	__asm mov _eax, eax
Packit ea8578
	return _eax;
Packit ea8578
}
Packit ea8578
Packit ea8578
#endif
Packit ea8578
#endif
Packit ea8578
Packit ea8578
#endif /* defined ENABLE_RDRAND */
Packit ea8578
Packit ea8578
Packit ea8578
/* has_dev_urandom */
Packit ea8578
Packit ea8578
#if defined (__APPLE__) || defined(__unix__) || defined(__linux__)
Packit ea8578
Packit ea8578
#include <string.h>
Packit ea8578
#include <fcntl.h>
Packit ea8578
#include <unistd.h>
Packit ea8578
#include <stdlib.h>
Packit ea8578
#include <sys/stat.h>
Packit ea8578
Packit ea8578
#define HAVE_DEV_RANDOM 1
Packit ea8578
Packit ea8578
static const char *dev_random_file = "/dev/urandom";
Packit ea8578
Packit ea8578
static int has_dev_urandom()
Packit ea8578
{
Packit ea8578
    struct stat buf;
Packit ea8578
    if (stat(dev_random_file, &buf)) {
Packit ea8578
        return 0;
Packit ea8578
    }
Packit ea8578
    return ((buf.st_mode & S_IFCHR) != 0);
Packit ea8578
}
Packit ea8578
Packit ea8578
Packit ea8578
/* get_dev_random_seed */
Packit ea8578
Packit ea8578
static int get_dev_random_seed()
Packit ea8578
{
Packit ea8578
    DEBUG_SEED("get_dev_random_seed");
Packit ea8578
Packit ea8578
    int fd = open(dev_random_file, O_RDONLY);
Packit ea8578
    if (fd < 0) {
Packit ea8578
        fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
Packit ea8578
        exit(1);
Packit ea8578
    }
Packit ea8578
Packit ea8578
    int r;
Packit ea8578
    ssize_t nread = read(fd, &r, sizeof(r));
Packit ea8578
    if (nread != sizeof(r)) {
Packit ea8578
        fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
Packit ea8578
        exit(1);
Packit ea8578
    }
Packit ea8578
Packit ea8578
    close(fd);
Packit ea8578
    return r;
Packit ea8578
}
Packit ea8578
Packit ea8578
#endif
Packit ea8578
Packit ea8578
Packit ea8578
/* get_cryptgenrandom_seed */
Packit ea8578
Packit ea8578
#ifdef WIN32
Packit ea8578
Packit ea8578
#define HAVE_CRYPTGENRANDOM 1
Packit ea8578
Packit ea8578
#include <windows.h>
Packit ea8578
#include <wincrypt.h>
Packit ea8578
#ifndef __GNUC__
Packit ea8578
#pragma comment(lib, "advapi32.lib")
Packit ea8578
#endif
Packit ea8578
Packit ea8578
static int get_cryptgenrandom_seed()
Packit ea8578
{
Packit ea8578
    HCRYPTPROV hProvider = 0;
Packit ea8578
    int r;
Packit ea8578
Packit ea8578
    DEBUG_SEED("get_cryptgenrandom_seed");
Packit ea8578
Packit ea8578
    if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
Packit ea8578
        fprintf(stderr, "error CryptAcquireContextW");
Packit ea8578
        exit(1);
Packit ea8578
    }
Packit ea8578
Packit ea8578
    if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) {
Packit ea8578
        fprintf(stderr, "error CryptGenRandom");
Packit ea8578
        exit(1);
Packit ea8578
    }
Packit ea8578
Packit ea8578
    CryptReleaseContext(hProvider, 0);
Packit ea8578
Packit ea8578
    return r;
Packit ea8578
}
Packit ea8578
Packit ea8578
#endif
Packit ea8578
Packit ea8578
Packit ea8578
/* get_time_seed */
Packit ea8578
Packit ea8578
#include <time.h>
Packit ea8578
Packit ea8578
static int get_time_seed()
Packit ea8578
{
Packit ea8578
    DEBUG_SEED("get_time_seed");
Packit ea8578
Packit ea8578
    return (int)time(NULL) * 433494437;
Packit ea8578
}
Packit ea8578
Packit ea8578
Packit ea8578
/* json_c_get_random_seed */
Packit ea8578
Packit ea8578
int json_c_get_random_seed()
Packit ea8578
{
Packit ea8578
#if HAVE_RDRAND
Packit ea8578
    if (has_rdrand()) return get_rdrand_seed();
Packit ea8578
#endif
Packit ea8578
#if HAVE_DEV_RANDOM
Packit ea8578
    if (has_dev_urandom()) return get_dev_random_seed();
Packit ea8578
#endif
Packit ea8578
#if HAVE_CRYPTGENRANDOM
Packit ea8578
    return get_cryptgenrandom_seed();
Packit ea8578
#endif
Packit ea8578
    return get_time_seed();
Packit ea8578
}