/*1* Copyright (c) 2017 Thomas Pornin <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining4* a copy of this software and associated documentation files (the5* "Software"), to deal in the Software without restriction, including6* without limitation the rights to use, copy, modify, merge, publish,7* distribute, sublicense, and/or sell copies of the Software, and to8* permit persons to whom the Software is furnished to do so, subject to9* the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#define BR_ENABLE_INTRINSICS 125#include "inner.h"2627#if BR_USE_GETENTROPY28#include <unistd.h>29#endif3031#if BR_USE_URANDOM32#include <sys/types.h>33#include <unistd.h>34#include <fcntl.h>35#include <errno.h>36#endif3738#if BR_USE_WIN32_RAND39#include <windows.h>40#include <wincrypt.h>41#pragma comment(lib, "advapi32")42#endif4344/*45* Seeder that uses the RDRAND opcodes (on x86 CPU).46*/47#if BR_RDRAND48BR_TARGETS_X86_UP49BR_TARGET("rdrnd")50static int51seeder_rdrand(const br_prng_class **ctx)52{53unsigned char tmp[32];54size_t u;5556for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) {57int j;58uint32_t x;5960/*61* We use the 32-bit intrinsic so that code is compatible62* with both 32-bit and 64-bit architectures.63*64* Intel recommends trying at least 10 times in case of65* failure.66*67* AMD bug: there are reports that some AMD processors68* have a bug that makes them fail silently after a69* suspend/resume cycle, in which case RDRAND will report70* a success but always return 0xFFFFFFFF.71* see: https://bugzilla.kernel.org/show_bug.cgi?id=8591172*73* As a mitigation, if the 32-bit value is 0 or -1, then74* it is considered a failure and tried again. This should75* reliably detect the buggy case, at least. This also76* implies that the selected seed values can never be77* 0x00000000 or 0xFFFFFFFF, which is not a problem since78* we are generating a seed for a PRNG, and we overdo it79* a bit (we generate 32 bytes of randomness, and 256 bits80* of entropy are really overkill).81*/82for (j = 0; j < 10; j ++) {83if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {84goto next_word;85}86}87return 0;88next_word:89br_enc32le(tmp + u, x);90}91(*ctx)->update(ctx, tmp, sizeof tmp);92return 1;93}94BR_TARGETS_X86_DOWN9596static int97rdrand_supported(void)98{99/*100* The RDRND support is bit 30 of ECX, as returned by CPUID.101*/102return br_cpuid(0, 0, 0x40000000, 0);103}104#endif105106/*107* Seeder that uses /dev/urandom (on Unix-like systems).108*/109#if BR_USE_URANDOM110static int111seeder_urandom(const br_prng_class **ctx)112{113int f;114115f = open("/dev/urandom", O_RDONLY);116if (f >= 0) {117unsigned char tmp[32];118size_t u;119120for (u = 0; u < sizeof tmp;) {121ssize_t len;122123len = read(f, tmp + u, (sizeof tmp) - u);124if (len < 0) {125if (errno == EINTR) {126continue;127}128break;129}130u += (size_t)len;131}132close(f);133if (u == sizeof tmp) {134(*ctx)->update(ctx, tmp, sizeof tmp);135return 1;136}137}138return 0;139}140#endif141142/*143* Seeder that uses getentropy() (backed by getrandom() on some systems,144* e.g. Linux). On failure, it will use the /dev/urandom seeder (if145* enabled).146*/147#if BR_USE_GETENTROPY148static int149seeder_getentropy(const br_prng_class **ctx)150{151unsigned char tmp[32];152153if (getentropy(tmp, sizeof tmp) == 0) {154(*ctx)->update(ctx, tmp, sizeof tmp);155return 1;156}157#if BR_USE_URANDOM158return seeder_urandom(ctx);159#else160return 0;161#endif162}163#endif164165/*166* Seeder that uses CryptGenRandom() (on Windows).167*/168#if BR_USE_WIN32_RAND169static int170seeder_win32(const br_prng_class **ctx)171{172HCRYPTPROV hp;173174if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL,175CRYPT_VERIFYCONTEXT | CRYPT_SILENT))176{177BYTE buf[32];178BOOL r;179180r = CryptGenRandom(hp, sizeof buf, buf);181CryptReleaseContext(hp, 0);182if (r) {183(*ctx)->update(ctx, buf, sizeof buf);184return 1;185}186}187return 0;188}189#endif190191/*192* An aggregate seeder that uses RDRAND, and falls back to an OS-provided193* source if RDRAND fails.194*/195#if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND)196static int197seeder_rdrand_with_fallback(const br_prng_class **ctx)198{199if (!seeder_rdrand(ctx)) {200#if BR_USE_GETENTROPY201return seeder_getentropy(ctx);202#elif BR_USE_URANDOM203return seeder_urandom(ctx);204#elif BR_USE_WIN32_RAND205return seeder_win32(ctx);206#else207#error "macro selection has gone wrong"208#endif209}210return 1;211}212#endif213214/* see bearssl_rand.h */215br_prng_seeder216br_prng_seeder_system(const char **name)217{218#if BR_RDRAND219if (rdrand_supported()) {220if (name != NULL) {221*name = "rdrand";222}223#if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND224return &seeder_rdrand_with_fallback;225#else226return &seeder_rdrand;227#endif228}229#endif230#if BR_USE_GETENTROPY231if (name != NULL) {232*name = "getentropy";233}234return &seeder_getentropy;235#elif BR_USE_URANDOM236if (name != NULL) {237*name = "urandom";238}239return &seeder_urandom;240#elif BR_USE_WIN32_RAND241if (name != NULL) {242*name = "win32";243}244return &seeder_win32;245#else246if (name != NULL) {247*name = "none";248}249return 0;250#endif251}252253254