blob: e754378ce3fcfa1aa5e9294f704983d1215a2e88 [file] [log] [blame]
Jean-Paul Calderone8210b922013-02-09 09:03:18 -08001"""
2PRNG management routines, thin wrappers.
3
4See the file RATIONALE for a short explanation of why this module was written.
5"""
6
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05007from functools import partial
Jean-Paul Calderone8210b922013-02-09 09:03:18 -08008
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05009from six import integer_types as _integer_types
10
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
14 exception_from_error_queue as _exception_from_error_queue)
15
16
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080017class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050018 """
19 An error occurred in an `OpenSSL.rand` API.
20 """
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080021
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050022_raise_current_error = partial(_exception_from_error_queue, Error)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080023
24_unspecified = object()
25
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050026_builtin_bytes = bytes
27
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080028def bytes(num_bytes):
29 """
30 Get some random bytes as a string.
31
32 :param num_bytes: The number of bytes to fetch
33 :return: A string of random bytes
34 """
Jean-Paul Calderone53a5e132014-01-11 08:31:19 -050035 if not isinstance(num_bytes, _integer_types):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080036 raise TypeError("num_bytes must be an integer")
37
38 if num_bytes < 0:
39 raise ValueError("num_bytes must not be negative")
40
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050041 result_buffer = _ffi.new("char[]", num_bytes)
42 result_code = _lib.RAND_bytes(result_buffer, num_bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080043 if result_code == -1:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050044 # TODO: No tests for this code path. Triggering a RAND_bytes failure
45 # might involve supplying a custom ENGINE? That's hard.
46 _raise_current_error()
47
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050048 return _ffi.buffer(result_buffer)[:]
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080049
50
51
52def add(buffer, entropy):
53 """
54 Add data with a given entropy to the PRNG
55
56 :param buffer: Buffer with random data
57 :param entropy: The entropy (in bytes) measurement of the buffer
58 :return: None
59 """
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050060 if not isinstance(buffer, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080061 raise TypeError("buffer must be a byte string")
62
63 if not isinstance(entropy, int):
64 raise TypeError("entropy must be an integer")
65
66 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050067 _lib.RAND_add(buffer, len(buffer), entropy)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080068
69
70
71def seed(buffer):
72 """
73 Alias for rand_add, with entropy equal to length
74
75 :param buffer: Buffer with random data
76 :return: None
77 """
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050078 if not isinstance(buffer, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080079 raise TypeError("buffer must be a byte string")
80
81 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050082 _lib.RAND_seed(buffer, len(buffer))
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080083
84
85
86def status():
87 """
88 Retrieve the status of the PRNG
89
90 :return: True if the PRNG is seeded enough, false otherwise
91 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050092 return _lib.RAND_status()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080093
94
95
96def egd(path, bytes=_unspecified):
97 """
98 Query an entropy gathering daemon (EGD) for random data and add it to the
99 PRNG. I haven't found any problems when the socket is missing, the function
100 just returns 0.
101
102 :param path: The path to the EGD socket
103 :param bytes: (optional) The number of bytes to read, default is 255
104 :returns: The number of bytes read (NB: a value of 0 isn't necessarily an
105 error, check rand.status())
106 """
Jean-Paul Calderonea8f7a942014-01-11 08:45:37 -0500107 if not isinstance(path, _builtin_bytes):
108 raise TypeError("path must be a byte string")
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800109
110 if bytes is _unspecified:
111 bytes = 255
112 elif not isinstance(bytes, int):
113 raise TypeError("bytes must be an integer")
114
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500115 return _lib.RAND_egd_bytes(path, bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800116
117
118
119def cleanup():
120 """
121 Erase the memory used by the PRNG.
122
123 :return: None
124 """
125 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500126 _lib.RAND_cleanup()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800127
128
129
130def load_file(filename, maxbytes=_unspecified):
131 """
132 Seed the PRNG with data from a file
133
134 :param filename: The file to read data from
135 :param maxbytes: (optional) The number of bytes to read, default is
136 to read the entire file
137 :return: The number of bytes read
138 """
Jean-Paul Calderone7c55d452014-01-11 09:46:35 -0500139 if not isinstance(filename, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800140 raise TypeError("filename must be a string")
141
142 if maxbytes is _unspecified:
143 maxbytes = -1
144 elif not isinstance(maxbytes, int):
145 raise TypeError("maxbytes must be an integer")
146
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 return _lib.RAND_load_file(filename, maxbytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800148
149
150
151def write_file(filename):
152 """
153 Save PRNG state to a file
154
155 :param filename: The file to write data to
156 :return: The number of bytes written
157 """
Jean-Paul Calderone7c55d452014-01-11 09:46:35 -0500158 if not isinstance(filename, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800159 raise TypeError("filename must be a string")
160
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500161 return _lib.RAND_write_file(filename)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800162
163
164# TODO There are no tests for screen at all
165def screen():
166 """
167 Add the current contents of the screen to the PRNG state. Availability:
168 Windows.
169
170 :return: None
171 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500172 _lib.RAND_screen()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800173
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500174if getattr(_lib, 'RAND_screen', None) is None:
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800175 del screen
176
177
178# TODO There are no tests for the RAND strings being loaded, whatever that
179# means.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500180_lib.ERR_load_RAND_strings()