Edit

kc3-lang/libtommath/bn_mp_rand.c

Branch :

  • Show log

    Commit

  • Author : Steffen Jaeckel
    Date : 2019-01-15 18:20:24
    Hash : 1c8c5348
    Message : Merge pull request #147 from czurnieden/moarvm_workarounds workaround for the MoarVM problem with MP_GEN_RAND et al. (cherry picked from commit 11d6e0391421bc996b2cf780f3f4c3fa634001d0)

  • bn_mp_rand.c
  • #include "tommath_private.h"
    #ifdef BN_MP_RAND_C
    /* LibTomMath, multiple-precision integer library -- Tom St Denis
     *
     * LibTomMath is a library that provides multiple-precision
     * integer arithmetic as well as number theoretic functionality.
     *
     * The library was designed directly after the MPI library by
     * Michael Fromberger but has been written from scratch with
     * additional optimizations in place.
     *
     * SPDX-License-Identifier: Unlicense
     */
    
    /* 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
    
    static int s_read_arc4random(mp_digit *p)
    {
       mp_digit d = 0, msk = 0;
       do {
          d <<= MP_GEN_RANDOM_SHIFT;
          d |= ((mp_digit) arc4random());
          msk <<= MP_GEN_RANDOM_SHIFT;
          msk |= (MP_MASK & MP_GEN_RANDOM_MAX);
       } while ((MP_MASK & msk) != MP_MASK);
       *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 <windows.h>
    #include <wincrypt.h>
    
    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 <sys/random.h>
    #include <errno.h>
    
    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 <fcntl.h>
    #include <errno.h>
    #include <unistd.h>
    
    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 */
    int mp_rand_digit(mp_digit *r)
    {
       int ret = s_rand_digit(r);
       *r &= MP_MASK;
       return ret;
    }
    
    int mp_rand(mp_int *a, int digits)
    {
       int     res;
       mp_digit d;
    
       mp_zero(a);
       if (digits <= 0) {
          return MP_OKAY;
       }
    
       /* first place a random non-zero digit */
       do {
          if (mp_rand_digit(&d) != MP_OKAY) {
             return MP_VAL;
          }
       } while (d == 0u);
    
       if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
          return res;
       }
    
       while (--digits > 0) {
          if ((res = mp_lshd(a, 1)) != MP_OKAY) {
             return res;
          }
    
          if (mp_rand_digit(&d) != MP_OKAY) {
             return MP_VAL;
          }
          if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
             return res;
          }
       }
    
       return MP_OKAY;
    }
    #endif
    
    /* ref:         $Format:%D$ */
    /* git commit:  $Format:%H$ */
    /* commit time: $Format:%ai$ */