Browse Source

crypto.py: improve get_random() implementation

The MMGen Project 3 years ago
parent
commit
589c3780
4 changed files with 39 additions and 19 deletions
  1. 36 14
      mmgen/crypto.py
  2. 1 2
      mmgen/globalvars.py
  3. 1 2
      test/test_py_d/ts_main.py
  4. 1 1
      test/test_py_d/ts_wallet.py

+ 36 - 14
mmgen/crypto.py

@@ -112,10 +112,11 @@ def decrypt_seed(enc_seed,key,seed_id,key_id):
 	dmsg(f'Decrypted seed: {dec_seed.hex()}')
 	return dec_seed
 
-def encrypt_data(data,key,iv=aesctr_dfl_iv,desc='data',verify=True):
+def encrypt_data(data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False):
 	from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 	from cryptography.hazmat.backends import default_backend
-	vmsg(f'Encrypting {desc}')
+	if not silent:
+		vmsg(f'Encrypting {desc}')
 	c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 	encryptor = c.encryptor()
 	enc_data = encryptor.update(data) + encryptor.finalize()
@@ -127,7 +128,8 @@ def encrypt_data(data,key,iv=aesctr_dfl_iv,desc='data',verify=True):
 		dec_data = encryptor.update(enc_data) + encryptor.finalize()
 		if dec_data != data:
 			die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
-		vmsg('done')
+		if not silent:
+			vmsg('done')
 
 	return enc_data
 
@@ -243,22 +245,42 @@ def _get_random_data_from_user(uchars,desc):
 	return ret.encode()
 
 def get_random(length):
+
+	os_rand = os.urandom(length)
+	assert len(os_rand) == length, f'OS random number generator returned {len(os_rand)} (!= {length}) bytes!'
+
 	return add_user_random(
-		rand_bytes = os.urandom(length),
-		desc       = 'generated by your operating system' )
+		rand_bytes = os_rand,
+		desc       = 'from your operating system' )
+
+def add_user_random(
+		rand_bytes,
+		desc,
+		urand = {'data':b'', 'counter':0} ):
 
-def add_user_random(rand_bytes,desc):
 	assert type(rand_bytes) == bytes, 'add_user_random_chk1'
+
 	if opt.usr_randchars:
-		if not g.user_entropy:
+
+		if not urand['data']:
 			from hashlib import sha256
-			g.user_entropy = sha256(_get_random_data_from_user(opt.usr_randchars,desc)).digest()
-			urand_desc = 'user-supplied entropy'
-		else:
-			urand_desc = 'saved user-supplied entropy'
-		key = make_key(g.user_entropy,b'','2',from_what=urand_desc,verbose=True)
-		msg(f'Encrypting random data {desc} with key')
-		return encrypt_data(rand_bytes,key,desc=desc,verify=False)
+			urand['data'] = sha256(_get_random_data_from_user(opt.usr_randchars,desc)).digest()
+
+		# counter protects against very evil rng that might repeatedly output the same data
+		urand['counter'] += 1
+
+		os_rand = os.urandom(8)
+		assert len(os_rand) == 8, f'OS random number generator returned {len(os_rand)} (!= 8) bytes!'
+
+		import hmac
+		key = hmac.digest(
+			urand['data'],
+			os_rand + int.to_bytes(urand['counter'],8,'big'),
+			'sha256' )
+
+		msg('Encrypting random data {} with ephemeral key #{}'.format( desc, urand['counter'] ))
+
+		return encrypt_data( data=rand_bytes, key=key, desc=desc, verify=False, silent=True )
 	else:
 		return rand_bytes
 

+ 1 - 2
mmgen/globalvars.py

@@ -40,7 +40,7 @@ class GlobalContext(Lockable):
 	  3 - command line
 	"""
 	_autolock = False
-	_set_ok = ('user_entropy','session')
+	_set_ok = ('session',)
 	_reset_ok = ('stdout','stderr','accept_defaults')
 	_use_class_attr = True
 
@@ -66,7 +66,6 @@ class GlobalContext(Lockable):
 
 	# Variables - these might be altered at runtime:
 
-	user_entropy    = b''
 	dfl_hash_preset = '3'
 	usr_randchars   = 30
 

+ 1 - 2
test/test_py_d/ts_main.py

@@ -253,7 +253,6 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		t = self.spawn('mmgen-walletgen', args + [self.usr_rand_arg])
 		t.license()
 		t.usr_rand(self.usr_rand_chars)
-		t.expect('Generating')
 		wcls = MMGenWallet
 		t.passphrase_new('new '+wcls.desc,self.wpasswd)
 		t.label()
@@ -591,7 +590,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 			t.usr_rand(self.usr_rand_chars)
 
 		if ocls.type.startswith('incog'):
-			m = 'Encrypting random data generated by your operating system with key'
+			m = 'Encrypting random data from your operating system with ephemeral key'
 			t.expect(m)
 			t.expect(m)
 			incog_id = t.expect_getend('New Incog Wallet ID: ')

+ 1 - 1
test/test_py_d/ts_wallet.py

@@ -210,7 +210,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 			t.usr_rand(self.usr_rand_chars)
 		if wcls.type.startswith('incog'):
 			for i in (1,2,3):
-				t.expect('Encrypting random data generated by your operating system with key')
+				t.expect('Encrypting random data from your operating system with ephemeral key')
 		if wcls.type == 'incog_hidden':
 			t.hincog_create(hincog_bytes)
 		if out_fmt == 'w':