blob: b3f7f6b525f8c4c7ecfcf393b4ebb214f59d6027 [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 Calderonec86bb7d2013-12-29 10:25:59 -05009from OpenSSL._util import (
10 ffi as _ffi,
11 lib as _lib,
12 exception_from_error_queue as _exception_from_error_queue)
13
14
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080015class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050016 """
17 An error occurred in an `OpenSSL.rand` API.
18 """
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080019
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050020_raise_current_error = partial(_exception_from_error_queue, Error)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080021
22_unspecified = object()
23
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050024_builtin_bytes = bytes
25
Jean-Paul Calderone53a5e132014-01-11 08:31:19 -050026try:
27 _integer_types = (int, long)
28except NameError:
29 _integer_types = (int,)
30
31
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080032def bytes(num_bytes):
33 """
34 Get some random bytes as a string.
35
36 :param num_bytes: The number of bytes to fetch
37 :return: A string of random bytes
38 """
Jean-Paul Calderone53a5e132014-01-11 08:31:19 -050039 if not isinstance(num_bytes, _integer_types):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080040 raise TypeError("num_bytes must be an integer")
41
42 if num_bytes < 0:
43 raise ValueError("num_bytes must not be negative")
44
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050045 result_buffer = _ffi.new("char[]", num_bytes)
46 result_code = _lib.RAND_bytes(result_buffer, num_bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080047 if result_code == -1:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050048 # TODO: No tests for this code path. Triggering a RAND_bytes failure
49 # might involve supplying a custom ENGINE? That's hard.
50 _raise_current_error()
51
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050052 return _ffi.buffer(result_buffer)[:]
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080053
54
55
56def add(buffer, entropy):
57 """
58 Add data with a given entropy to the PRNG
59
60 :param buffer: Buffer with random data
61 :param entropy: The entropy (in bytes) measurement of the buffer
62 :return: None
63 """
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050064 if not isinstance(buffer, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080065 raise TypeError("buffer must be a byte string")
66
67 if not isinstance(entropy, int):
68 raise TypeError("entropy must be an integer")
69
70 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050071 _lib.RAND_add(buffer, len(buffer), entropy)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080072
73
74
75def seed(buffer):
76 """
77 Alias for rand_add, with entropy equal to length
78
79 :param buffer: Buffer with random data
80 :return: None
81 """
Jean-Paul Calderonee80a25a2014-01-09 14:33:42 -050082 if not isinstance(buffer, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080083 raise TypeError("buffer must be a byte string")
84
85 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050086 _lib.RAND_seed(buffer, len(buffer))
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080087
88
89
90def status():
91 """
92 Retrieve the status of the PRNG
93
94 :return: True if the PRNG is seeded enough, false otherwise
95 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050096 return _lib.RAND_status()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080097
98
99
100def egd(path, bytes=_unspecified):
101 """
102 Query an entropy gathering daemon (EGD) for random data and add it to the
103 PRNG. I haven't found any problems when the socket is missing, the function
104 just returns 0.
105
106 :param path: The path to the EGD socket
107 :param bytes: (optional) The number of bytes to read, default is 255
108 :returns: The number of bytes read (NB: a value of 0 isn't necessarily an
109 error, check rand.status())
110 """
Jean-Paul Calderonea8f7a942014-01-11 08:45:37 -0500111 if not isinstance(path, _builtin_bytes):
112 raise TypeError("path must be a byte string")
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800113
114 if bytes is _unspecified:
115 bytes = 255
116 elif not isinstance(bytes, int):
117 raise TypeError("bytes must be an integer")
118
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500119 return _lib.RAND_egd_bytes(path, bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800120
121
122
123def cleanup():
124 """
125 Erase the memory used by the PRNG.
126
127 :return: None
128 """
129 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 _lib.RAND_cleanup()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800131
132
133
134def load_file(filename, maxbytes=_unspecified):
135 """
136 Seed the PRNG with data from a file
137
138 :param filename: The file to read data from
139 :param maxbytes: (optional) The number of bytes to read, default is
140 to read the entire file
141 :return: The number of bytes read
142 """
Jean-Paul Calderone7c55d452014-01-11 09:46:35 -0500143 if not isinstance(filename, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800144 raise TypeError("filename must be a string")
145
146 if maxbytes is _unspecified:
147 maxbytes = -1
148 elif not isinstance(maxbytes, int):
149 raise TypeError("maxbytes must be an integer")
150
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500151 return _lib.RAND_load_file(filename, maxbytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800152
153
154
155def write_file(filename):
156 """
157 Save PRNG state to a file
158
159 :param filename: The file to write data to
160 :return: The number of bytes written
161 """
Jean-Paul Calderone7c55d452014-01-11 09:46:35 -0500162 if not isinstance(filename, _builtin_bytes):
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800163 raise TypeError("filename must be a string")
164
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500165 return _lib.RAND_write_file(filename)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800166
167
168# TODO There are no tests for screen at all
169def screen():
170 """
171 Add the current contents of the screen to the PRNG state. Availability:
172 Windows.
173
174 :return: None
175 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500176 _lib.RAND_screen()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800177
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500178if getattr(_lib, 'RAND_screen', None) is None:
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800179 del screen
180
181
182# TODO There are no tests for the RAND strings being loaded, whatever that
183# means.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500184_lib.ERR_load_RAND_strings()