From 589c37806b79bde625b25747b76f71a92133a949 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 10 Feb 2022 12:51:43 +0000 Subject: [PATCH] crypto.py: improve get_random() implementation --- mmgen/crypto.py | 52 ++++++++++++++++++++++++++----------- mmgen/globalvars.py | 3 +-- test/test_py_d/ts_main.py | 3 +-- test/test_py_d/ts_wallet.py | 2 +- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/mmgen/crypto.py b/mmgen/crypto.py index 76ce1822..7c1e9720 100755 --- a/mmgen/crypto.py +++ b/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): - return add_user_random( - rand_bytes = os.urandom(length), - desc = 'generated by your operating system' ) -def add_user_random(rand_bytes,desc): + 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_rand, + desc = 'from your operating system' ) + +def add_user_random( + rand_bytes, + desc, + urand = {'data':b'', 'counter':0} ): + 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 diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index 7e33fa0d..624c7ea6 100755 --- a/mmgen/globalvars.py +++ b/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 diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index c3b01559..1b471e13 100755 --- a/test/test_py_d/ts_main.py +++ b/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: ') diff --git a/test/test_py_d/ts_wallet.py b/test/test_py_d/ts_wallet.py index 1846de9b..8bb006fb 100755 --- a/test/test_py_d/ts_wallet.py +++ b/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':