4141import subprocess
4242import sys
4343import tempfile
44+ import textwrap
4445import unittest
4546
4647
@@ -49,6 +50,7 @@ class EntropySubprocessTests(unittest.TestCase):
4950 HASH_SECRET_BYTES = 24
5051 RANDOM_SEED_BYTES = 624 * 4
5152 HASH_AND_RANDOM_IMPORT_BYTES = HASH_SECRET_BYTES + RANDOM_SEED_BYTES
53+ SSL_DATA_DIR = os .path .join (os .path .dirname (__file__ ), "ssldata" )
5254
5355 def _run_with_init_device (self , data : bytes , code : str ):
5456 with tempfile .NamedTemporaryFile (delete = False ) as f :
@@ -140,6 +142,35 @@ def test_multiprocessing_process_import_does_not_use_initrandom(self):
140142 self .assert_subprocess_ok (result )
141143 self .assertEqual ("ok" , result .stdout .strip ())
142144
145+ def test_multiprocessing_deliver_challenge_does_not_mutate_random_state (self ):
146+ result = self ._run_with_init_source (
147+ "fixed:0x1234ABCD" ,
148+ textwrap .dedent ("""
149+ import random
150+ import multiprocessing.connection as mc
151+ auth = b'authkey'
152+
153+ class Dummy:
154+ def __init__(self):
155+ self.sent = []
156+
157+ def send_bytes(self, data):
158+ self.sent.append(data)
159+
160+ def recv_bytes(self, _max):
161+ msg = self.sent[-1][len(mc._CHALLENGE):]
162+ return mc._create_response(auth, msg)
163+
164+ before = random.getstate()
165+ d = Dummy()
166+ mc.deliver_challenge(d, auth)
167+ after = random.getstate()
168+ print(before == after)
169+ """ ),
170+ )
171+ self .assert_subprocess_ok (result )
172+ self .assertEqual ("True" , result .stdout .strip ())
173+
143174 def test_tempfile_candidate_names_use_initrandom (self ):
144175 result = self ._run_with_init_device (
145176 b"\x00 " * self .HASH_AND_RANDOM_IMPORT_BYTES ,
@@ -169,6 +200,47 @@ def test_email_generator_boundary_mutates_random_state(self):
169200 self .assert_subprocess_ok (result )
170201 self .assertEqual ("False" , result .stdout .strip ())
171202
203+ def test_imaplib_connect_mutates_random_state (self ):
204+ result = self ._run_with_init_source (
205+ "fixed:0x1234ABCD" ,
206+ textwrap .dedent ("""
207+ import random
208+ import imaplib
209+
210+ class Dummy(imaplib.IMAP4):
211+ def open(self, host='', port=imaplib.IMAP4_PORT, timeout=None):
212+ pass
213+
214+ def _get_response(self):
215+ self.untagged_responses = {'OK': [b'']}
216+ return 'OK'
217+
218+ def _get_capabilities(self):
219+ self.capabilities = ('IMAP4REV1',)
220+
221+ def shutdown(self):
222+ pass
223+
224+ before = random.getstate()
225+ d = Dummy.__new__(Dummy)
226+ d.debug = imaplib.Debug
227+ d.state = 'LOGOUT'
228+ d.literal = None
229+ d.tagged_commands = {}
230+ d.untagged_responses = {}
231+ d.continuation_response = ''
232+ d.is_readonly = False
233+ d.tagnum = 0
234+ d._tls_established = False
235+ d._mode_ascii()
236+ d._connect()
237+ after = random.getstate()
238+ print(before == after)
239+ """ ),
240+ )
241+ self .assert_subprocess_ok (result )
242+ self .assertEqual ("False" , result .stdout .strip ())
243+
172244 def test_pyexpat_import_does_not_use_additional_initrandom (self ):
173245 result = self ._run_with_init_device (
174246 b"\x00 " * self .HASH_SECRET_BYTES ,
@@ -177,6 +249,14 @@ def test_pyexpat_import_does_not_use_additional_initrandom(self):
177249 self .assert_subprocess_ok (result )
178250 self .assertEqual ("pyexpat" , result .stdout .strip ())
179251
252+ def test_pyexpat_parsercreate_does_not_use_additional_initrandom (self ):
253+ result = self ._run_with_init_device (
254+ b"\x00 " * self .HASH_SECRET_BYTES ,
255+ "import pyexpat; p = pyexpat.ParserCreate(); print(type(p).__name__)" ,
256+ )
257+ self .assert_subprocess_ok (result )
258+ self .assertEqual ("xmlparser" , result .stdout .strip ())
259+
180260 def test_sqlite3_import_does_not_use_additional_initrandom (self ):
181261 result = self ._run_with_init_device (
182262 b"\x00 " * self .HASH_SECRET_BYTES ,
@@ -185,6 +265,28 @@ def test_sqlite3_import_does_not_use_additional_initrandom(self):
185265 self .assert_subprocess_ok (result )
186266 self .assertEqual ("_sqlite3" , result .stdout .strip ())
187267
268+ def test_sqlite3_randomblob_does_not_use_initrandom (self ):
269+ result = self ._run_with_init_device (
270+ b"\x00 " * self .HASH_SECRET_BYTES ,
271+ "import sqlite3; "
272+ "conn = sqlite3.connect(':memory:'); "
273+ "print(conn.execute('select length(randomblob(16))').fetchone()[0])" ,
274+ )
275+ self .assert_subprocess_ok (result )
276+ self .assertEqual ("16" , result .stdout .strip ())
277+
278+ def test_ssl_load_cert_chain_does_not_use_initrandom (self ):
279+ cert = os .path .join (self .SSL_DATA_DIR , "signed_cert.pem" )
280+ result = self ._run_with_init_device (
281+ b"\x00 " * self .HASH_SECRET_BYTES ,
282+ f"import ssl; "
283+ f"ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER); "
284+ f"ctx.load_cert_chain(r'{ cert } '); "
285+ f"print('ok')" ,
286+ )
287+ self .assert_subprocess_ok (result )
288+ self .assertEqual ("ok" , result .stdout .strip ())
289+
188290 def test_uuid1_mutates_random_state (self ):
189291 result = self ._run_with_init_source (
190292 "fixed:0x1234ABCD" ,
0 commit comments