crypto.py: code tidy-ups
This commit is contained in:
parent
e1fb918588
commit
71058479a1
1 changed files with 68 additions and 104 deletions
162
mmgen/crypto.py
162
mmgen/crypto.py
|
|
@ -28,29 +28,12 @@ from mmgen.common import *
|
||||||
crmsg = {
|
crmsg = {
|
||||||
'usr_rand_notice': """
|
'usr_rand_notice': """
|
||||||
Since we don't fully trust our OS's random number generator, we'll provide
|
Since we don't fully trust our OS's random number generator, we'll provide
|
||||||
some additional entropy of our own. Please type %s symbols on your keyboard.
|
some additional entropy of our own. Please type {} symbols on your keyboard.
|
||||||
Type slowly and choose your symbols carefully for maximum randomness. Try to
|
Type slowly and choose your symbols carefully for maximum randomness. Try to
|
||||||
use both upper and lowercase as well as punctuation and numerals. What you
|
use both upper and lowercase as well as punctuation and numerals. What you
|
||||||
type will not be displayed on the screen. Note that the timings between your
|
type will not be displayed on the screen. Note that the timings between your
|
||||||
keystrokes will also be used as a source of randomness.
|
keystrokes will also be used as a source of randomness.
|
||||||
""",
|
"""
|
||||||
# 'incog_iv_id': """
|
|
||||||
# Check that the generated Incog ID above is correct.
|
|
||||||
# If it's not, then your incognito data is incorrect or corrupted.
|
|
||||||
# """,
|
|
||||||
# 'incog_iv_id_hidden': """
|
|
||||||
# Check that the generated Incog ID above is correct.
|
|
||||||
# If it's not, then your incognito data is incorrect or corrupted,
|
|
||||||
# or you've supplied an incorrect offset.
|
|
||||||
# """,
|
|
||||||
# 'incorrect_incog_passphrase_try_again': """
|
|
||||||
# Incorrect passphrase, hash preset, or maybe old-format incog wallet.
|
|
||||||
# Try again? (Y)es, (n)o, (m)ore information:
|
|
||||||
# """.strip(),
|
|
||||||
# 'confirm_seed_id': """
|
|
||||||
# If the Seed ID above is correct but you're seeing this message, then you need
|
|
||||||
# to exit and re-run the program with the '--old-incog-fmt' option.
|
|
||||||
# """.strip(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def sha256_rounds(s,n):
|
def sha256_rounds(s,n):
|
||||||
|
|
@ -59,11 +42,17 @@ def sha256_rounds(s,n):
|
||||||
s = sha256(s).digest()
|
s = sha256(s).digest()
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def encrypt_seed(seed, key):
|
def scramble_seed(seed,scramble_key,hash_rounds):
|
||||||
return encrypt_data(seed, key, iv=1, desc='seed')
|
import hmac
|
||||||
|
scr_seed = hmac.new(seed,scramble_key,sha256).digest()
|
||||||
|
fs = 'Seed: {}\nScramble key: {}\nScrambled seed: {}\nScrambled seed len: {}'
|
||||||
|
dmsg(fs.format(hexlify(seed),scramble_key,hexlify(scr_seed),len(scr_seed)))
|
||||||
|
return sha256_rounds(scr_seed,hash_rounds)
|
||||||
|
|
||||||
def decrypt_seed(enc_seed, key, seed_id, key_id):
|
def encrypt_seed(seed,key):
|
||||||
|
return encrypt_data(seed,key,iv=1,desc='seed')
|
||||||
|
|
||||||
|
def decrypt_seed(enc_seed,key,seed_id,key_id):
|
||||||
vmsg_r('Checking key...')
|
vmsg_r('Checking key...')
|
||||||
chk1 = make_chksum_8(key)
|
chk1 = make_chksum_8(key)
|
||||||
if key_id:
|
if key_id:
|
||||||
|
|
@ -71,10 +60,8 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
|
||||||
msg('Incorrect passphrase or hash preset')
|
msg('Incorrect passphrase or hash preset')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
dec_seed = decrypt_data(enc_seed, key, iv=1, desc='seed')
|
dec_seed = decrypt_data(enc_seed,key,iv=1,desc='seed')
|
||||||
|
|
||||||
chk2 = make_chksum_8(dec_seed)
|
chk2 = make_chksum_8(dec_seed)
|
||||||
|
|
||||||
if seed_id:
|
if seed_id:
|
||||||
if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
|
if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
|
||||||
qmsg('Passphrase is OK')
|
qmsg('Passphrase is OK')
|
||||||
|
|
@ -85,105 +72,81 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
|
||||||
msg('Key ID is correct but decryption of seed failed')
|
msg('Key ID is correct but decryption of seed failed')
|
||||||
else:
|
else:
|
||||||
msg('Incorrect passphrase or hash preset')
|
msg('Incorrect passphrase or hash preset')
|
||||||
|
|
||||||
vmsg('')
|
vmsg('')
|
||||||
return False
|
return False
|
||||||
# else:
|
# else:
|
||||||
# qmsg('Generated IDs (Seed/Key): %s/%s' % (chk2,chk1))
|
# qmsg('Generated IDs (Seed/Key): {}/{}'.format(chk2,chk1))
|
||||||
|
|
||||||
dmsg('Decrypted seed: %s' % hexlify(dec_seed))
|
|
||||||
|
|
||||||
|
dmsg('Decrypted seed: {}'.format(hexlify(dec_seed)))
|
||||||
return dec_seed
|
return dec_seed
|
||||||
|
|
||||||
def encrypt_data(data, key, iv=1, desc='data', verify=True):
|
def encrypt_data(data,key,iv=1,desc='data',verify=True):
|
||||||
|
|
||||||
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
|
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util import Counter
|
from Crypto.Util import Counter
|
||||||
|
vmsg('Encrypting {}'.format(desc))
|
||||||
vmsg('Encrypting %s' % desc)
|
c = AES.new(key,AES.MODE_CTR,counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||||
|
|
||||||
c = AES.new(key, AES.MODE_CTR,
|
|
||||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
|
||||||
enc_data = c.encrypt(data)
|
enc_data = c.encrypt(data)
|
||||||
|
|
||||||
if verify:
|
if verify:
|
||||||
vmsg_r('Performing a test decryption of the %s...' % desc)
|
vmsg_r('Performing a test decryption of the {}...'.format(desc))
|
||||||
|
c = AES.new(key,AES.MODE_CTR,counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||||
c = AES.new(key, AES.MODE_CTR,
|
|
||||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
|
||||||
dec_data = c.decrypt(enc_data)
|
dec_data = c.decrypt(enc_data)
|
||||||
|
|
||||||
if dec_data == data: vmsg('done')
|
if dec_data == data: vmsg('done')
|
||||||
else:
|
else:
|
||||||
die(2,"ERROR.\nDecrypted %s doesn't match original %s" % (desc,desc))
|
die(2,"ERROR.\nDecrypted {s} doesn't match original {s}".format(s=desc))
|
||||||
|
|
||||||
return enc_data
|
return enc_data
|
||||||
|
|
||||||
def decrypt_data(enc_data, key, iv=1, desc='data'):
|
def decrypt_data(enc_data,key,iv=1,desc='data'):
|
||||||
|
|
||||||
vmsg_r('Decrypting %s with key...' % desc)
|
|
||||||
|
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util import Counter
|
from Crypto.Util import Counter
|
||||||
|
vmsg_r('Decrypting {} with key...'.format(desc))
|
||||||
c = AES.new(key, AES.MODE_CTR,
|
c = AES.new(key,AES.MODE_CTR,counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
|
||||||
|
|
||||||
return c.decrypt(enc_data)
|
return c.decrypt(enc_data)
|
||||||
|
|
||||||
def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
|
def scrypt_hash_passphrase(passwd,salt,hash_preset,buflen=32):
|
||||||
|
import scrypt
|
||||||
# Buflen arg is for brainwallets only, which use this function to generate
|
# Buflen arg is for brainwallets only, which use this function to generate
|
||||||
# the seed directly.
|
# the seed directly.
|
||||||
|
|
||||||
N,r,p = get_hash_params(hash_preset)
|
N,r,p = get_hash_params(hash_preset)
|
||||||
|
return scrypt.hash(passwd,salt,2**N,r,p,buflen=buflen)
|
||||||
|
|
||||||
import scrypt
|
def make_key(passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
|
||||||
return scrypt.hash(passwd, salt, 2**N, r, p, buflen=buflen)
|
|
||||||
|
|
||||||
def make_key(passwd,salt,hash_preset,
|
|
||||||
desc='encryption key',from_what='passphrase',verbose=False):
|
|
||||||
|
|
||||||
if from_what: desc += ' from '
|
if from_what: desc += ' from '
|
||||||
if opt.verbose or verbose:
|
if opt.verbose or verbose:
|
||||||
msg_r('Generating %s%s...' % (desc,from_what))
|
msg_r('Generating {}{}...'.format(desc,from_what))
|
||||||
key = scrypt_hash_passphrase(passwd, salt, hash_preset)
|
key = scrypt_hash_passphrase(passwd,salt,hash_preset)
|
||||||
if opt.verbose or verbose: msg('done')
|
if opt.verbose or verbose: msg('done')
|
||||||
dmsg('Key: %s' % hexlify(key))
|
dmsg('Key: {}'.format(hexlify(key)))
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def _get_random_data_from_user(uchars):
|
def _get_random_data_from_user(uchars):
|
||||||
|
m = 'Enter {} random symbols' if opt.quiet else crmsg['usr_rand_notice']
|
||||||
if opt.quiet: msg('Enter %s random symbols' % uchars)
|
msg(m.format(uchars))
|
||||||
else: msg(crmsg['usr_rand_notice'] % uchars)
|
prompt = 'You may begin typing. {} symbols left: '
|
||||||
|
msg_r(prompt.format(uchars))
|
||||||
prompt = 'You may begin typing. %s symbols left: '
|
|
||||||
msg_r(prompt % uchars)
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from mmgen.term import get_char
|
from mmgen.term import get_char
|
||||||
# time.clock() always returns zero, so we'll use time.time()
|
# time.clock() always returns zero, so we'll use time.time()
|
||||||
saved_time = time.time()
|
saved_time = time.time()
|
||||||
|
|
||||||
key_data,time_data,pp = '',[],True
|
key_data,time_data,pp = '',[],True
|
||||||
|
|
||||||
for i in range(uchars):
|
for i in range(uchars):
|
||||||
key_data += get_char(immed_chars='ALL',prehold_protect=pp)
|
key_data += get_char(immed_chars='ALL',prehold_protect=pp)
|
||||||
pp = False
|
pp = False
|
||||||
msg_r('\r' + prompt % (uchars - i - 1))
|
msg_r('\r'+prompt.format(uchars-i-1))
|
||||||
now = time.time()
|
now = time.time()
|
||||||
time_data.append(now - saved_time)
|
time_data.append(now - saved_time)
|
||||||
saved_time = now
|
saved_time = now
|
||||||
|
|
||||||
if opt.quiet: msg_r('\r')
|
if opt.quiet: msg_r('\r')
|
||||||
else: msg_r("\rThank you. That's enough.%s\n\n" % (' '*18))
|
else: msg_r("\rThank you. That's enough.{}\n\n".format(' '*18))
|
||||||
|
|
||||||
fmt_time_data = ['{:.22f}'.format(i) for i in time_data]
|
fmt_time_data = ['{:.22f}'.format(i) for i in time_data]
|
||||||
|
dmsg('\nUser input:\n{}\nKeystroke time intervals:\n{}\n'.format(key_data,'\n'.join(fmt_time_data)))
|
||||||
dmsg('\nUser input:\n%s\nKeystroke time intervals:\n%s\n' %
|
|
||||||
(key_data,'\n'.join(fmt_time_data)))
|
|
||||||
|
|
||||||
prompt = 'User random data successfully acquired. Press ENTER to continue'
|
prompt = 'User random data successfully acquired. Press ENTER to continue'
|
||||||
prompt_and_get_char(prompt,'',enter_ok=True)
|
prompt_and_get_char(prompt,'',enter_ok=True)
|
||||||
|
|
||||||
|
|
@ -200,54 +163,55 @@ def get_random(length):
|
||||||
from_what += ' plus user-supplied entropy'
|
from_what += ' plus user-supplied entropy'
|
||||||
else:
|
else:
|
||||||
from_what += ' plus saved user-supplied entropy'
|
from_what += ' plus saved user-supplied entropy'
|
||||||
key = make_key(g.user_entropy, '', '2', from_what=from_what, verbose=True)
|
key = make_key(g.user_entropy,'','2',from_what=from_what,verbose=True)
|
||||||
return encrypt_data(os_rand,key,desc='random data',verify=False)
|
return encrypt_data(os_rand,key,desc='random data',verify=False)
|
||||||
else:
|
else:
|
||||||
return os_rand
|
return os_rand
|
||||||
|
|
||||||
def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
|
def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
|
||||||
p = """Enter hash preset for %s,
|
prompt = """Enter hash preset for {},
|
||||||
or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
|
or hit ENTER to accept the default value ('{}'): """.format(desc,hp)
|
||||||
while True:
|
while True:
|
||||||
ret = my_raw_input(p)
|
ret = my_raw_input(prompt)
|
||||||
if ret:
|
if ret:
|
||||||
if ret in g.hash_presets.keys(): return ret
|
if ret in g.hash_presets.keys():
|
||||||
|
return ret
|
||||||
else:
|
else:
|
||||||
msg('Invalid input. Valid choices are %s' %
|
m = 'Invalid input. Valid choices are {}'
|
||||||
', '.join(sorted(g.hash_presets.keys())))
|
msg(m.format(', '.join(sorted(g.hash_presets.keys()))))
|
||||||
continue
|
continue
|
||||||
else: return hp
|
else: return hp
|
||||||
|
|
||||||
# Vars for mmgen_*crypt functions only
|
_salt_len,_sha256_len,_nonce_len = 32,32,32
|
||||||
salt_len,sha256_len,nonce_len = 32,32,32
|
|
||||||
|
|
||||||
def mmgen_encrypt(data,desc='data',hash_preset=''):
|
def mmgen_encrypt(data,desc='data',hash_preset=''):
|
||||||
salt,iv,nonce = get_random(salt_len),\
|
salt = get_random(_salt_len)
|
||||||
get_random(g.aesctr_iv_len), \
|
iv = get_random(g.aesctr_iv_len)
|
||||||
get_random(nonce_len)
|
nonce = get_random(_nonce_len)
|
||||||
hp = hash_preset or get_hash_preset_from_user('3',desc)
|
hp = hash_preset or get_hash_preset_from_user('3',desc)
|
||||||
m = ('user-requested','default')[hp=='3']
|
m = ('user-requested','default')[hp=='3']
|
||||||
vmsg('Encrypting %s' % desc)
|
vmsg('Encrypting {}'.format(desc))
|
||||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
qmsg("Using {} hash preset of '{}'".format(m,hp))
|
||||||
passwd = get_new_passphrase(desc, {})
|
passwd = get_new_passphrase(desc,{})
|
||||||
key = make_key(passwd, salt, hp)
|
key = make_key(passwd,salt,hp)
|
||||||
enc_d = encrypt_data(sha256(nonce+data).digest() + nonce + data, key,
|
enc_d = encrypt_data(sha256(nonce+data).digest()+nonce+data,key,int(hexlify(iv),16),desc=desc)
|
||||||
int(hexlify(iv),16), desc=desc)
|
|
||||||
return salt+iv+enc_d
|
return salt+iv+enc_d
|
||||||
|
|
||||||
def mmgen_decrypt(data,desc='data',hash_preset=''):
|
def mmgen_decrypt(data,desc='data',hash_preset=''):
|
||||||
dstart = salt_len + g.aesctr_iv_len
|
dstart = _salt_len + g.aesctr_iv_len
|
||||||
salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
|
salt = data[:_salt_len]
|
||||||
vmsg('Preparing to decrypt %s' % desc)
|
iv = data[_salt_len:dstart]
|
||||||
|
enc_d = data[dstart:]
|
||||||
|
vmsg('Preparing to decrypt {}'.format(desc))
|
||||||
hp = hash_preset or get_hash_preset_from_user('3',desc)
|
hp = hash_preset or get_hash_preset_from_user('3',desc)
|
||||||
m = ('user-requested','default')[hp=='3']
|
m = ('user-requested','default')[hp=='3']
|
||||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
qmsg("Using {} hash preset of '{}'".format(m,hp))
|
||||||
passwd = get_mmgen_passphrase(desc)
|
passwd = get_mmgen_passphrase(desc)
|
||||||
key = make_key(passwd,salt,hp)
|
key = make_key(passwd,salt,hp)
|
||||||
dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), desc)
|
dec_d = decrypt_data(enc_d,key,int(hexlify(iv),16),desc)
|
||||||
if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
|
if dec_d[:_sha256_len] == sha256(dec_d[_sha256_len:]).digest():
|
||||||
vmsg('OK')
|
vmsg('OK')
|
||||||
return dec_d[sha256_len+nonce_len:]
|
return dec_d[_sha256_len+_nonce_len:]
|
||||||
else:
|
else:
|
||||||
msg('Incorrect passphrase or hash preset')
|
msg('Incorrect passphrase or hash preset')
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue