Skip to content

Commit a9667c3

Browse files
Sheppard, KevinSheppard, Kevin
authored andcommitted
REF: Move entropy functions to standalone module
Move entropy to standalone module Add fallback to entropy function Add minimal test of entropy functions
1 parent 74573bf commit a9667c3

File tree

5 files changed

+104
-51
lines changed

5 files changed

+104
-51
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: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -114,59 +114,11 @@ cdef extern from "distributions.h":
114114

115115
cdef void entropy_fill(void *dest, size_t size)
116116
cdef bint entropy_getbytes(void* dest, size_t size)
117+
cdef bint entropy_fallback_getbytes(void *dest, size_t size)
117118

118119
include "array_utilities.pxi"
119120
include "bounded_integers.pxi"
120121

121-
def random_entropy(size=None):
122-
"""
123-
random_entropy(size=None)
124-
125-
Read entropy from the system cryptographic provider
126-
127-
Parameters
128-
----------
129-
size : int or tuple of ints, optional
130-
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
131-
``m * n * k`` samples are drawn. Default is None, in which case a
132-
single value is returned.
133-
134-
Returns
135-
-------
136-
entropy : scalar or ndarray
137-
Entropy bits in 32-bit unsigned integers
138-
139-
Notes
140-
-----
141-
On Unix-like machines, reads from /dev/urandom. On Windows machines reads
142-
from the RSA Full cryptographic service provider.
143-
144-
This function reads from the system entropy pool and so samples are
145-
not reproducible. In particular, it does *NOT* make use of a
146-
RandomState, and so seed, get_state and set_state have no effect.
147-
148-
Raises RuntimeError if the command fails.
149-
"""
150-
cdef bint success
151-
cdef size_t n = 0
152-
cdef uint32_t random = 0
153-
cdef uint32_t [:] randoms
154-
155-
if size is None:
156-
success = entropy_getbytes(<void *>&random, 4)
157-
else:
158-
n = compute_numel(size)
159-
randoms = np.zeros(n, dtype=np.uint32)
160-
print(n)
161-
success = entropy_getbytes(<void *>(&randoms[0]), 4 * n)
162-
if not success:
163-
raise RuntimeError('Unable to read from system cryptographic provider')
164-
165-
if n == 0:
166-
return random
167-
return np.asarray(randoms).reshape(size)
168-
169-
170122
cdef double kahan_sum(double *darr, np.npy_intp n):
171123
cdef double c, y, t, sum
172124
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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import unittest
99
import numpy as np
10+
import randomstate.entropy as entropy
1011
from randomstate.prng.mlfg_1279_861 import mlfg_1279_861
1112
from randomstate.prng.mrg32k3a import mrg32k3a
1213
from randomstate.prng.mt19937 import mt19937
@@ -506,6 +507,20 @@ def setup_class(cls):
506507
cls.initial_state = cls.rs.get_state()
507508
cls._extra_setup()
508509

510+
class TestEntropy(unittest.TestCase):
511+
def test_entropy(self):
512+
e1 = entropy.random_entropy()
513+
e2 = entropy.random_entropy()
514+
assert (e1 != e2)
515+
e1 = entropy.random_entropy(source='fallback')
516+
e2 = entropy.random_entropy(source='fallback')
517+
assert (e1 != e2)
518+
e1 = entropy.random_entropy(10)
519+
e2 = entropy.random_entropy(10)
520+
assert (e1 != e2).all()
521+
522+
523+
509524
if __name__ == '__main__':
510525
import nose
511526
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)