Skip to content

Commit baa6641

Browse files
committed
Merge pull request #17 from bashtage/entropy-fallback
Entropy fallback
2 parents b09e40f + 3697d2a commit baa6641

File tree

5 files changed

+111
-62
lines changed

5 files changed

+111
-62
lines changed

randomstate/entropy.pyx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
cimport numpy as np
2+
import numpy as np
3+
4+
from libc.stdint cimport uint32_t
5+
6+
cdef extern from "src/common/entropy.h":
7+
cdef bint entropy_getbytes(void* dest, size_t size)
8+
cdef bint entropy_fallback_getbytes(void *dest, size_t size)
9+
10+
cdef Py_ssize_t compute_numel(size):
11+
cdef Py_ssize_t i, n = 1
12+
if isinstance(size, tuple):
13+
for i in range(len(size)):
14+
n *= size[i]
15+
else:
16+
n = size
17+
return n
18+
19+
def random_entropy(size=None, source='system'):
20+
"""
21+
random_entropy(size=None)
22+
23+
Read entropy from the system cryptographic provider
24+
25+
Parameters
26+
----------
27+
size : int or tuple of ints, optional
28+
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
29+
``m * n * k`` samples are drawn. Default is None, in which case a
30+
single value is returned.
31+
source : str {'system', 'fallback'}
32+
Source of entropy. 'system' uses system cryptographic pool.
33+
'fallback' uses a has to the time and process id.
34+
35+
Returns
36+
-------
37+
entropy : scalar or ndarray
38+
Entropy bits in 32-bit unsigned integers
39+
40+
Notes
41+
-----
42+
On Unix-like machines, reads from /dev/urandom. On Windows machines reads
43+
from the RSA Full cryptographic service provider.
44+
45+
This function reads from the system entropy pool and so samples are
46+
not reproducible. In particular, it does *NOT* make use of a
47+
RandomState, and so seed, get_state and set_state have no effect.
48+
49+
Raises RuntimeError if the command fails.
50+
"""
51+
cdef bint success = True
52+
cdef Py_ssize_t n = 0
53+
cdef uint32_t random = 0
54+
cdef uint32_t [:] randoms
55+
56+
if source not in ('system', 'fallback'):
57+
raise ValueError('Unknown value in source.')
58+
59+
if size is None:
60+
if source == 'system':
61+
success = entropy_getbytes(<void *>&random, 4)
62+
else:
63+
success = entropy_fallback_getbytes(<void *>&random, 4)
64+
else:
65+
n = compute_numel(size)
66+
randoms = np.zeros(n, dtype=np.uint32)
67+
print(n)
68+
if source == 'system':
69+
success = entropy_getbytes(<void *>(&randoms[0]), 4 * n)
70+
else:
71+
success = entropy_fallback_getbytes(<void *>(&randoms[0]), 4 * n)
72+
if not success:
73+
raise RuntimeError('Unable to read from system cryptographic provider')
74+
75+
if n == 0:
76+
return random
77+
return np.asarray(randoms).reshape(size)

randomstate/interface.pyx

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,7 @@ from cython_overrides cimport PyFloat_AsDouble, PyInt_AsLong, PyErr_Occurred, Py
1919

2020
np.import_array()
2121

22-
#cdef extern from "Python.h":
23-
# double PyFloat_AsDouble(object ob)
24-
# long PyInt_AsLong(object ob)
25-
# int PyErr_Occurred()
26-
# void PyErr_Clear()
27-
2822
include "config.pxi"
29-
#include "src/common/binomial.pxi"
3023

3124
IF RNG_MOD_NAME == 'pcg32':
3225
include "shims/pcg-32/pcg-32.pxi"
@@ -56,8 +49,6 @@ cdef extern from "distributions.h":
5649

5750
cdef uint64_t random_uint64(aug_state* state) nogil
5851
cdef uint32_t random_uint32(aug_state* state) nogil
59-
cdef int64_t random_positive_int64(aug_state* state) nogil
60-
cdef int32_t random_positive_int32(aug_state* state) nogil
6152

6253
cdef long random_positive_int(aug_state* state) nogil
6354
cdef unsigned long random_uint(aug_state* state) nogil
@@ -116,61 +107,10 @@ cdef extern from "distributions.h":
116107
cdef void random_gauss_fill(aug_state* state, int count, double *out) nogil
117108
cdef void random_gauss_zig_julia_fill(aug_state* state, int count, double *out) nogil
118109

119-
cdef void entropy_fill(void *dest, size_t size)
120-
cdef bint entropy_getbytes(void* dest, size_t size)
121110

122111
include "array_utilities.pxi"
123112
include "bounded_integers.pxi"
124113

125-
def random_entropy(size=None):
126-
"""
127-
random_entropy(size=None)
128-
129-
Read entropy from the system cryptographic provider
130-
131-
Parameters
132-
----------
133-
size : int or tuple of ints, optional
134-
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
135-
``m * n * k`` samples are drawn. Default is None, in which case a
136-
single value is returned.
137-
138-
Returns
139-
-------
140-
entropy : scalar or ndarray
141-
Entropy bits in 32-bit unsigned integers
142-
143-
Notes
144-
-----
145-
On Unix-like machines, reads from /dev/urandom. On Windows machines reads
146-
from the RSA Full cryptographic service provider.
147-
148-
This function reads from the system entropy pool and so samples are
149-
not reproducible. In particular, it does *NOT* make use of a
150-
RandomState, and so seed, get_state and set_state have no effect.
151-
152-
Raises RuntimeError if the command fails.
153-
"""
154-
cdef bint success
155-
cdef size_t n = 0
156-
cdef uint32_t random = 0
157-
cdef uint32_t [:] randoms
158-
159-
if size is None:
160-
success = entropy_getbytes(<void *>&random, 4)
161-
else:
162-
n = compute_numel(size)
163-
randoms = np.zeros(n, dtype=np.uint32)
164-
print(n)
165-
success = entropy_getbytes(<void *>(&randoms[0]), 4 * n)
166-
if not success:
167-
raise RuntimeError('Unable to read from system cryptographic provider')
168-
169-
if n == 0:
170-
return random
171-
return np.asarray(randoms).reshape(size)
172-
173-
174114
cdef double kahan_sum(double *darr, np.npy_intp n):
175115
cdef double c, y, t, sum
176116
cdef np.npy_intp i

randomstate/src/common/entropy.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ typedef int bool;
3333

3434
extern void entropy_fill(void *dest, size_t size);
3535

36-
extern bool entropy_getbytes(void* dest, size_t size);
36+
extern bool entropy_getbytes(void* dest, size_t size);
37+
38+
extern bool entropy_fallback_getbytes(void *dest, size_t size);

randomstate/tests/test_smoke.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import time
12
import pickle
23
try:
34
import cPickle
@@ -7,6 +8,7 @@
78
import os
89
import unittest
910
import numpy as np
11+
import randomstate.entropy as entropy
1012
from randomstate.prng.mlfg_1279_861 import mlfg_1279_861
1113
from randomstate.prng.mrg32k3a import mrg32k3a
1214
from randomstate.prng.mt19937 import mt19937
@@ -506,6 +508,27 @@ def setup_class(cls):
506508
cls.initial_state = cls.rs.get_state()
507509
cls._extra_setup()
508510

511+
class TestEntropy(unittest.TestCase):
512+
def test_entropy(self):
513+
e1 = entropy.random_entropy()
514+
e2 = entropy.random_entropy()
515+
assert (e1 != e2)
516+
e1 = entropy.random_entropy(10)
517+
e2 = entropy.random_entropy(10)
518+
assert (e1 != e2).all()
519+
e1 = entropy.random_entropy(10, source='system')
520+
e2 = entropy.random_entropy(10, source='system')
521+
assert (e1 != e2).all()
522+
523+
def test_fallback(self):
524+
e1 = entropy.random_entropy(source='fallback')
525+
time.sleep(0.1)
526+
e2 = entropy.random_entropy(source='fallback')
527+
assert (e1 != e2)
528+
529+
530+
531+
509532
if __name__ == '__main__':
510533
import nose
511534
nose.run(argv=[__file__, '-vv'])

setup.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,15 @@ def write_config(file_name, config):
120120

121121
configs.append(config)
122122

123-
extensions = []
124123
# Generate files and extensions
124+
extensions = cythonize([Extension('randomstate.entropy',
125+
sources=[join(mod_dir, 'entropy.pyx'),
126+
join(mod_dir, 'src', 'common', 'entropy.c')],
127+
include_dirs=config['include_dirs'],
128+
define_macros=extra_defs,
129+
extra_compile_args=extra_compile_args,
130+
extra_link_args=extra_link_args)])
131+
125132
for config in configs:
126133
config_file_name = mod_dir + '/' + config['file_name'] + '-config.pxi'
127134
# Rewrite core_rng to replace generic #include "config.pxi"

0 commit comments

Comments
 (0)