blob: de158f6e534af1a5495625858a10894f06944d12 [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
7import __builtin__
8
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05009from functools import partial
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080010
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
26def bytes(num_bytes):
27 """
28 Get some random bytes as a string.
29
30 :param num_bytes: The number of bytes to fetch
31 :return: A string of random bytes
32 """
33 if not isinstance(num_bytes, int):
34 raise TypeError("num_bytes must be an integer")
35
36 if num_bytes < 0:
37 raise ValueError("num_bytes must not be negative")
38
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050039 result_buffer = _ffi.new("char[]", num_bytes)
40 result_code = _lib.RAND_bytes(result_buffer, num_bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080041 if result_code == -1:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050042 # TODO: No tests for this code path. Triggering a RAND_bytes failure
43 # might involve supplying a custom ENGINE? That's hard.
44 _raise_current_error()
45
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050046 return _ffi.buffer(result_buffer)[:]
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080047
48
49
50def add(buffer, entropy):
51 """
52 Add data with a given entropy to the PRNG
53
54 :param buffer: Buffer with random data
55 :param entropy: The entropy (in bytes) measurement of the buffer
56 :return: None
57 """
58 if not isinstance(buffer, __builtin__.bytes):
59 raise TypeError("buffer must be a byte string")
60
61 if not isinstance(entropy, int):
62 raise TypeError("entropy must be an integer")
63
64 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050065 _lib.RAND_add(buffer, len(buffer), entropy)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080066
67
68
69def seed(buffer):
70 """
71 Alias for rand_add, with entropy equal to length
72
73 :param buffer: Buffer with random data
74 :return: None
75 """
76 if not isinstance(buffer, __builtin__.bytes):
77 raise TypeError("buffer must be a byte string")
78
79 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050080 _lib.RAND_seed(buffer, len(buffer))
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080081
82
83
84def status():
85 """
86 Retrieve the status of the PRNG
87
88 :return: True if the PRNG is seeded enough, false otherwise
89 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050090 return _lib.RAND_status()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -080091
92
93
94def egd(path, bytes=_unspecified):
95 """
96 Query an entropy gathering daemon (EGD) for random data and add it to the
97 PRNG. I haven't found any problems when the socket is missing, the function
98 just returns 0.
99
100 :param path: The path to the EGD socket
101 :param bytes: (optional) The number of bytes to read, default is 255
102 :returns: The number of bytes read (NB: a value of 0 isn't necessarily an
103 error, check rand.status())
104 """
105 if not isinstance(path, str):
106 raise TypeError("path must be a string")
107
108 if bytes is _unspecified:
109 bytes = 255
110 elif not isinstance(bytes, int):
111 raise TypeError("bytes must be an integer")
112
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500113 return _lib.RAND_egd_bytes(path, bytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800114
115
116
117def cleanup():
118 """
119 Erase the memory used by the PRNG.
120
121 :return: None
122 """
123 # TODO Nothing tests this call actually being made, or made properly.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500124 _lib.RAND_cleanup()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800125
126
127
128def load_file(filename, maxbytes=_unspecified):
129 """
130 Seed the PRNG with data from a file
131
132 :param filename: The file to read data from
133 :param maxbytes: (optional) The number of bytes to read, default is
134 to read the entire file
135 :return: The number of bytes read
136 """
137 if not isinstance(filename, str):
138 raise TypeError("filename must be a string")
139
140 if maxbytes is _unspecified:
141 maxbytes = -1
142 elif not isinstance(maxbytes, int):
143 raise TypeError("maxbytes must be an integer")
144
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 return _lib.RAND_load_file(filename, maxbytes)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800146
147
148
149def write_file(filename):
150 """
151 Save PRNG state to a file
152
153 :param filename: The file to write data to
154 :return: The number of bytes written
155 """
156 if not isinstance(filename, str):
157 raise TypeError("filename must be a string")
158
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 return _lib.RAND_write_file(filename)
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800160
161
162# TODO There are no tests for screen at all
163def screen():
164 """
165 Add the current contents of the screen to the PRNG state. Availability:
166 Windows.
167
168 :return: None
169 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500170 _lib.RAND_screen()
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800171
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500172if getattr(_lib, 'RAND_screen', None) is None:
Jean-Paul Calderone8210b922013-02-09 09:03:18 -0800173 del screen
174
175
176# TODO There are no tests for the RAND strings being loaded, whatever that
177# means.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500178_lib.ERR_load_RAND_strings()