diff --git a/bn_mp_rand.c b/bn_mp_rand.c index 5e0c1b3..6330e7f 100644 --- a/bn_mp_rand.c +++ b/bn_mp_rand.c @@ -13,34 +13,171 @@ * guarantee it works. */ -#if defined(MP_8BIT) || defined(MP_16BIT) -#define MP_GEN_RANDOM_SHIFT DIGIT_BIT -#else -#if MP_GEN_RANDOM_MAX == 0xffffffffu -#define MP_GEN_RANDOM_SHIFT 32 -#elif MP_GEN_RANDOM_MAX == 32767 -/* SHRT_MAX */ -#define MP_GEN_RANDOM_SHIFT 15 -#elif MP_GEN_RANDOM_MAX == 2147483647 -/* INT_MAX */ -#define MP_GEN_RANDOM_SHIFT 31 -#elif !defined(MP_GEN_RANDOM_SHIFT) -#error Thou shalt define their own valid MP_GEN_RANDOM_SHIFT -#endif -#endif +/* First the OS-specific special cases + * - *BSD + * - Windows + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#define MP_ARC4RANDOM +#define MP_GEN_RANDOM_MAX 0xffffffffu +#define MP_GEN_RANDOM_SHIFT 32 -/* makes a pseudo-random int of a given size */ -static mp_digit s_gen_random(void) +static int s_read_arc4random(mp_digit* p) { mp_digit d = 0, msk = 0; do { d <<= MP_GEN_RANDOM_SHIFT; - d |= ((mp_digit) MP_GEN_RANDOM()); + d |= ((mp_digit) arc4random()); msk <<= MP_GEN_RANDOM_SHIFT; msk |= (MP_MASK & MP_GEN_RANDOM_MAX); } while ((MP_MASK & msk) != MP_MASK); - d &= MP_MASK; - return d; + *p = d; + return MP_OKAY; +} +#endif + +#if defined(_WIN32) || defined(_WIN32_WCE) +#define MP_WIN_CSP + +#ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0400 +#endif +#ifdef _WIN32_WCE + #define UNDER_CE + #define ARM +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static HCRYPTPROV hProv = 0; + +static void s_cleanup_win_csp(void) +{ + CryptReleaseContext(hProv, 0); + hProv = 0; +} + +static int s_read_win_csp(mp_digit* p) +{ + int ret = -1; + if (hProv == 0) { + if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && + !CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) { + hProv = 0; + return ret; + } + atexit(s_cleanup_win_csp); + } + if (CryptGenRandom(hProv, sizeof(*p), (void*)p) == TRUE) { + ret = MP_OKAY; + } + return ret; +} +#endif /* WIN32 */ + +#if !defined(MP_WIN_CSP) && defined(__linux__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 25) +#define MP_GETRANDOM +#include +#include + +static int s_read_getrandom(mp_digit* p) +{ + int ret; + do { + ret = getrandom(p, sizeof(*p), 0); + } while((ret == -1) && (errno == EINTR)); + if (ret == sizeof(*p)) return MP_OKAY; + return -1; +} +#endif +#endif + +/* We assume all platforms besides windows provide "/dev/urandom". + * In case yours doesn't, define MP_NO_DEV_URANDOM at compile-time. + */ +#if !defined(MP_WIN_CSP) && !defined(MP_NO_DEV_URANDOM) +#ifndef MP_DEV_URANDOM +#define MP_DEV_URANDOM "/dev/urandom" +#endif +#include +#include +#include + +static int s_read_dev_urandom(mp_digit* p) +{ + ssize_t r; + int fd; + do { + fd = open(MP_DEV_URANDOM, O_RDONLY); + } while((fd == -1) && (errno == EINTR)); + if (fd == -1) return -1; + do { + r = read(fd, p, sizeof(*p)); + } while((r == -1) && (errno == EINTR)); + close(fd); + if (r != sizeof(*p)) return -1; + return MP_OKAY; +} +#endif + +#if defined(MP_PRNG_ENABLE_LTM_RNG) +unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void)); +void (*ltm_rng_callback)(void); + +static int s_read_ltm_rng(mp_digit* p) +{ + unsigned long ret; + if (ltm_rng == NULL) return -1; + ret = ltm_rng((void*)p, sizeof(*p), ltm_rng_callback); + if (ret != sizeof(*p)) return -1; + return MP_OKAY; +} +#endif + +static int s_rand_digit(mp_digit* p) +{ + int ret = -1; + +#if defined(MP_ARC4RANDOM) + ret = s_read_arc4random(p); + if (ret == MP_OKAY) return ret; +#endif + +#if defined(MP_WIN_CSP) + ret = s_read_win_csp(p); + if (ret == MP_OKAY) return ret; +#else + +#if defined(MP_GETRANDOM) + ret = s_read_getrandom(p); + if (ret == MP_OKAY) return ret; +#endif +#if defined(MP_DEV_URANDOM) + ret = s_read_dev_urandom(p); + if (ret == MP_OKAY) return ret; +#endif + +#endif /* MP_WIN_CSP */ + +#if defined(MP_PRNG_ENABLE_LTM_RNG) + ret = s_read_ltm_rng(p); + if (ret == MP_OKAY) return ret; +#endif + + return ret; +} + +/* makes a pseudo-random int of a given size */ +static int s_gen_random(mp_digit *r) +{ + int ret = s_rand_digit(r); + *r &= MP_MASK; + return ret; } int mp_rand(mp_int *a, int digits) @@ -55,7 +192,9 @@ int mp_rand(mp_int *a, int digits) /* first place a random non-zero digit */ do { - d = s_gen_random(); + if (s_gen_random(&d) != MP_OKAY) { + return MP_VAL; + } } while (d == 0u); if ((res = mp_add_d(a, d, a)) != MP_OKAY) { @@ -67,7 +206,10 @@ int mp_rand(mp_int *a, int digits) return res; } - if ((res = mp_add_d(a, s_gen_random(), a)) != MP_OKAY) { + if (s_gen_random(&d) != MP_OKAY) { + return MP_VAL; + } + if ((res = mp_add_d(a, d, a)) != MP_OKAY) { return res; } } diff --git a/tommath.h b/tommath.h index e26e1b9..b6b945a 100644 --- a/tommath.h +++ b/tommath.h @@ -100,18 +100,6 @@ typedef uint_least32_t mp_min_u32; typedef mp_digit mp_min_u32; #endif -/* use arc4random on platforms that support it */ -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) -# define MP_GEN_RANDOM() arc4random() -# define MP_GEN_RANDOM_MAX 0xffffffffu -#endif - -/* use rand() as fall-back if there's no better rand function */ -#ifndef MP_GEN_RANDOM -# define MP_GEN_RANDOM() rand() -# define MP_GEN_RANDOM_MAX RAND_MAX -#endif - #define MP_DIGIT_BIT DIGIT_BIT #define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) #define MP_DIGIT_MAX MP_MASK @@ -287,6 +275,14 @@ int mp_cnt_lsb(const mp_int *a); /* makes a pseudo-random int of a given size */ int mp_rand(mp_int *a, int digits); +#ifdef MP_PRNG_ENABLE_LTM_RNG +/* as last resort we will fall back to libtomcrypt's rng_get_bytes() + * in case you don't use libtomcrypt or use it w/o rng_get_bytes() + * you have to implement it somewhere else, as it's required */ +extern unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void)); +extern void (*ltm_rng_callback)(void); +#endif + /* ---> binary operations <--- */ /* c = a XOR b */ int mp_xor(const mp_int *a, const mp_int *b, mp_int *c);