mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-03 20:35:13 +00:00
keystore: stronger pbkdf for encryption
This commit is contained in:
parent
aceb022f9d
commit
48b0de7871
4 changed files with 140 additions and 52 deletions
|
@ -32,6 +32,7 @@ from typing import Union
|
||||||
import pyaes
|
import pyaes
|
||||||
|
|
||||||
from .util import assert_bytes, InvalidPassword, to_bytes, to_string
|
from .util import assert_bytes, InvalidPassword, to_bytes, to_string
|
||||||
|
from .i18n import _
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -90,37 +91,103 @@ def aes_decrypt_with_iv(key: bytes, iv: bytes, data: bytes) -> bytes:
|
||||||
raise InvalidPassword()
|
raise InvalidPassword()
|
||||||
|
|
||||||
|
|
||||||
def EncodeAES(secret: bytes, msg: bytes) -> bytes:
|
def EncodeAES_base64(secret: bytes, msg: bytes) -> bytes:
|
||||||
"""Returns base64 encoded ciphertext."""
|
"""Returns base64 encoded ciphertext."""
|
||||||
assert_bytes(msg)
|
e = EncodeAES_bytes(secret, msg)
|
||||||
iv = bytes(os.urandom(16))
|
|
||||||
ct = aes_encrypt_with_iv(secret, iv, msg)
|
|
||||||
e = iv + ct
|
|
||||||
return base64.b64encode(e)
|
return base64.b64encode(e)
|
||||||
|
|
||||||
|
|
||||||
def DecodeAES(secret: bytes, ciphertext_b64: Union[bytes, str]) -> bytes:
|
def EncodeAES_bytes(secret: bytes, msg: bytes) -> bytes:
|
||||||
e = bytes(base64.b64decode(ciphertext_b64))
|
assert_bytes(msg)
|
||||||
iv, e = e[:16], e[16:]
|
iv = bytes(os.urandom(16))
|
||||||
|
ct = aes_encrypt_with_iv(secret, iv, msg)
|
||||||
|
return iv + ct
|
||||||
|
|
||||||
|
|
||||||
|
def DecodeAES_base64(secret: bytes, ciphertext_b64: Union[bytes, str]) -> bytes:
|
||||||
|
ciphertext = bytes(base64.b64decode(ciphertext_b64))
|
||||||
|
return DecodeAES_bytes(secret, ciphertext)
|
||||||
|
|
||||||
|
|
||||||
|
def DecodeAES_bytes(secret: bytes, ciphertext: bytes) -> bytes:
|
||||||
|
assert_bytes(ciphertext)
|
||||||
|
iv, e = ciphertext[:16], ciphertext[16:]
|
||||||
s = aes_decrypt_with_iv(secret, iv, e)
|
s = aes_decrypt_with_iv(secret, iv, e)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def pw_encode(data: str, password: Union[bytes, str]) -> str:
|
PW_HASH_VERSION_LATEST = 2
|
||||||
|
KNOWN_PW_HASH_VERSIONS = (1, 2)
|
||||||
|
assert PW_HASH_VERSION_LATEST in KNOWN_PW_HASH_VERSIONS
|
||||||
|
|
||||||
|
|
||||||
|
class UnexpectedPasswordHashVersion(InvalidPassword):
|
||||||
|
def __init__(self, version):
|
||||||
|
self.version = version
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{unexpected}: {version}\n{please_update}".format(
|
||||||
|
unexpected=_("Unexpected password hash version"),
|
||||||
|
version=self.version,
|
||||||
|
please_update=_('You are most likely using an outdated version of Electrum. Please update.'))
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_password(password: Union[bytes, str], *, version: int, salt: bytes) -> bytes:
|
||||||
|
pw = to_bytes(password, 'utf8')
|
||||||
|
if version == 1:
|
||||||
|
return sha256d(pw)
|
||||||
|
elif version == 2:
|
||||||
|
if not isinstance(salt, bytes) or len(salt) < 16:
|
||||||
|
raise Exception('too weak salt', salt)
|
||||||
|
return hashlib.pbkdf2_hmac(hash_name='sha256',
|
||||||
|
password=pw,
|
||||||
|
salt=b'ELECTRUM_PW_HASH_V2'+salt,
|
||||||
|
iterations=50_000)
|
||||||
|
else:
|
||||||
|
assert version not in KNOWN_PW_HASH_VERSIONS
|
||||||
|
raise UnexpectedPasswordHashVersion(version)
|
||||||
|
|
||||||
|
|
||||||
|
def pw_encode(data: str, password: Union[bytes, str, None], *, version: int) -> str:
|
||||||
if not password:
|
if not password:
|
||||||
return data
|
return data
|
||||||
secret = sha256d(password)
|
if version not in KNOWN_PW_HASH_VERSIONS:
|
||||||
return EncodeAES(secret, to_bytes(data, "utf8")).decode('utf8')
|
raise UnexpectedPasswordHashVersion(version)
|
||||||
|
# derive key from password
|
||||||
|
if version == 1:
|
||||||
|
salt = b''
|
||||||
|
elif version == 2:
|
||||||
|
salt = bytes(os.urandom(16))
|
||||||
|
else:
|
||||||
|
assert False, version
|
||||||
|
secret = _hash_password(password, version=version, salt=salt)
|
||||||
|
# encrypt given data
|
||||||
|
e = EncodeAES_bytes(secret, to_bytes(data, "utf8"))
|
||||||
|
# return base64(salt + encrypted data)
|
||||||
|
ciphertext = salt + e
|
||||||
|
ciphertext_b64 = base64.b64encode(ciphertext)
|
||||||
|
return ciphertext_b64.decode('utf8')
|
||||||
|
|
||||||
|
|
||||||
def pw_decode(data: str, password: Union[bytes, str]) -> str:
|
def pw_decode(data: str, password: Union[bytes, str, None], *, version: int) -> str:
|
||||||
if password is None:
|
if password is None:
|
||||||
return data
|
return data
|
||||||
secret = sha256d(password)
|
if version not in KNOWN_PW_HASH_VERSIONS:
|
||||||
|
raise UnexpectedPasswordHashVersion(version)
|
||||||
|
data_bytes = bytes(base64.b64decode(data))
|
||||||
|
# derive key from password
|
||||||
|
if version == 1:
|
||||||
|
salt = b''
|
||||||
|
elif version == 2:
|
||||||
|
salt, data_bytes = data_bytes[:16], data_bytes[16:]
|
||||||
|
else:
|
||||||
|
assert False, version
|
||||||
|
secret = _hash_password(password, version=version, salt=salt)
|
||||||
|
# decrypt given data
|
||||||
try:
|
try:
|
||||||
d = to_string(DecodeAES(secret, data), "utf8")
|
d = to_string(DecodeAES_bytes(secret, data_bytes), "utf8")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
raise InvalidPassword()
|
raise InvalidPassword() from e
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ from .bip32 import (bip32_public_derivation, deserialize_xpub, CKD_pub,
|
||||||
bip32_private_key, bip32_derivation, BIP32_PRIME,
|
bip32_private_key, bip32_derivation, BIP32_PRIME,
|
||||||
is_xpub, is_xprv)
|
is_xpub, is_xprv)
|
||||||
from .ecc import string_to_number, number_to_string
|
from .ecc import string_to_number, number_to_string
|
||||||
from .crypto import pw_decode, pw_encode, sha256d
|
from .crypto import (pw_decode, pw_encode, sha256d, PW_HASH_VERSION_LATEST)
|
||||||
from .util import (PrintError, InvalidPassword, hfu, WalletFileException,
|
from .util import (PrintError, InvalidPassword, hfu, WalletFileException,
|
||||||
BitcoinException, bh2u, bfh, print_error, inv_dict)
|
BitcoinException, bh2u, bfh, print_error, inv_dict)
|
||||||
from .mnemonic import Mnemonic, load_wordlist
|
from .mnemonic import Mnemonic, load_wordlist
|
||||||
|
@ -92,8 +92,9 @@ class KeyStore(PrintError):
|
||||||
|
|
||||||
class Software_KeyStore(KeyStore):
|
class Software_KeyStore(KeyStore):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
KeyStore.__init__(self)
|
KeyStore.__init__(self)
|
||||||
|
self.pw_hash_version = d.get('pw_hash_version', 1)
|
||||||
|
|
||||||
def may_have_password(self):
|
def may_have_password(self):
|
||||||
return not self.is_watching_only()
|
return not self.is_watching_only()
|
||||||
|
@ -122,6 +123,12 @@ class Software_KeyStore(KeyStore):
|
||||||
if keypairs:
|
if keypairs:
|
||||||
tx.sign(keypairs)
|
tx.sign(keypairs)
|
||||||
|
|
||||||
|
def update_password(self, old_password, new_password):
|
||||||
|
raise NotImplementedError() # implemented by subclasses
|
||||||
|
|
||||||
|
def check_password(self, password):
|
||||||
|
raise NotImplementedError() # implemented by subclasses
|
||||||
|
|
||||||
|
|
||||||
class Imported_KeyStore(Software_KeyStore):
|
class Imported_KeyStore(Software_KeyStore):
|
||||||
# keystore for imported private keys
|
# keystore for imported private keys
|
||||||
|
@ -129,7 +136,7 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
type = 'imported'
|
type = 'imported'
|
||||||
|
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
Software_KeyStore.__init__(self)
|
Software_KeyStore.__init__(self, d)
|
||||||
self.keypairs = d.get('keypairs', {})
|
self.keypairs = d.get('keypairs', {})
|
||||||
|
|
||||||
def is_deterministic(self):
|
def is_deterministic(self):
|
||||||
|
@ -142,6 +149,7 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
return {
|
return {
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
'keypairs': self.keypairs,
|
'keypairs': self.keypairs,
|
||||||
|
'pw_hash_version': self.pw_hash_version,
|
||||||
}
|
}
|
||||||
|
|
||||||
def can_import(self):
|
def can_import(self):
|
||||||
|
@ -161,14 +169,14 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
# there will only be one pubkey-privkey pair for it in self.keypairs,
|
# there will only be one pubkey-privkey pair for it in self.keypairs,
|
||||||
# and the privkey will encode a txin_type but that txin_type cannot be trusted.
|
# and the privkey will encode a txin_type but that txin_type cannot be trusted.
|
||||||
# Removing keys complicates this further.
|
# Removing keys complicates this further.
|
||||||
self.keypairs[pubkey] = pw_encode(serialized_privkey, password)
|
self.keypairs[pubkey] = pw_encode(serialized_privkey, password, version=self.pw_hash_version)
|
||||||
return txin_type, pubkey
|
return txin_type, pubkey
|
||||||
|
|
||||||
def delete_imported_key(self, key):
|
def delete_imported_key(self, key):
|
||||||
self.keypairs.pop(key)
|
self.keypairs.pop(key)
|
||||||
|
|
||||||
def get_private_key(self, pubkey, password):
|
def get_private_key(self, pubkey, password):
|
||||||
sec = pw_decode(self.keypairs[pubkey], password)
|
sec = pw_decode(self.keypairs[pubkey], password, version=self.pw_hash_version)
|
||||||
txin_type, privkey, compressed = deserialize_privkey(sec)
|
txin_type, privkey, compressed = deserialize_privkey(sec)
|
||||||
# this checks the password
|
# this checks the password
|
||||||
if pubkey != ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed):
|
if pubkey != ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed):
|
||||||
|
@ -189,16 +197,17 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
if new_password == '':
|
if new_password == '':
|
||||||
new_password = None
|
new_password = None
|
||||||
for k, v in self.keypairs.items():
|
for k, v in self.keypairs.items():
|
||||||
b = pw_decode(v, old_password)
|
b = pw_decode(v, old_password, version=self.pw_hash_version)
|
||||||
c = pw_encode(b, new_password)
|
c = pw_encode(b, new_password, version=PW_HASH_VERSION_LATEST)
|
||||||
self.keypairs[k] = c
|
self.keypairs[k] = c
|
||||||
|
self.pw_hash_version = PW_HASH_VERSION_LATEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Deterministic_KeyStore(Software_KeyStore):
|
class Deterministic_KeyStore(Software_KeyStore):
|
||||||
|
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
Software_KeyStore.__init__(self)
|
Software_KeyStore.__init__(self, d)
|
||||||
self.seed = d.get('seed', '')
|
self.seed = d.get('seed', '')
|
||||||
self.passphrase = d.get('passphrase', '')
|
self.passphrase = d.get('passphrase', '')
|
||||||
|
|
||||||
|
@ -206,12 +215,14 @@ class Deterministic_KeyStore(Software_KeyStore):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
d = {}
|
d = {
|
||||||
|
'type': self.type,
|
||||||
|
'pw_hash_version': self.pw_hash_version,
|
||||||
|
}
|
||||||
if self.seed:
|
if self.seed:
|
||||||
d['seed'] = self.seed
|
d['seed'] = self.seed
|
||||||
if self.passphrase:
|
if self.passphrase:
|
||||||
d['passphrase'] = self.passphrase
|
d['passphrase'] = self.passphrase
|
||||||
d['type'] = self.type
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def has_seed(self):
|
def has_seed(self):
|
||||||
|
@ -226,10 +237,13 @@ class Deterministic_KeyStore(Software_KeyStore):
|
||||||
self.seed = self.format_seed(seed)
|
self.seed = self.format_seed(seed)
|
||||||
|
|
||||||
def get_seed(self, password):
|
def get_seed(self, password):
|
||||||
return pw_decode(self.seed, password)
|
return pw_decode(self.seed, password, version=self.pw_hash_version)
|
||||||
|
|
||||||
def get_passphrase(self, password):
|
def get_passphrase(self, password):
|
||||||
return pw_decode(self.passphrase, password) if self.passphrase else ''
|
if self.passphrase:
|
||||||
|
return pw_decode(self.passphrase, password, version=self.pw_hash_version)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class Xpub:
|
class Xpub:
|
||||||
|
@ -312,10 +326,10 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_master_private_key(self, password):
|
def get_master_private_key(self, password):
|
||||||
return pw_decode(self.xprv, password)
|
return pw_decode(self.xprv, password, version=self.pw_hash_version)
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
xprv = pw_decode(self.xprv, password)
|
xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
|
||||||
if deserialize_xprv(xprv)[4] != deserialize_xpub(self.xpub)[4]:
|
if deserialize_xprv(xprv)[4] != deserialize_xpub(self.xpub)[4]:
|
||||||
raise InvalidPassword()
|
raise InvalidPassword()
|
||||||
|
|
||||||
|
@ -325,13 +339,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
new_password = None
|
new_password = None
|
||||||
if self.has_seed():
|
if self.has_seed():
|
||||||
decoded = self.get_seed(old_password)
|
decoded = self.get_seed(old_password)
|
||||||
self.seed = pw_encode(decoded, new_password)
|
self.seed = pw_encode(decoded, new_password, version=PW_HASH_VERSION_LATEST)
|
||||||
if self.passphrase:
|
if self.passphrase:
|
||||||
decoded = self.get_passphrase(old_password)
|
decoded = self.get_passphrase(old_password)
|
||||||
self.passphrase = pw_encode(decoded, new_password)
|
self.passphrase = pw_encode(decoded, new_password, version=PW_HASH_VERSION_LATEST)
|
||||||
if self.xprv is not None:
|
if self.xprv is not None:
|
||||||
b = pw_decode(self.xprv, old_password)
|
b = pw_decode(self.xprv, old_password, version=self.pw_hash_version)
|
||||||
self.xprv = pw_encode(b, new_password)
|
self.xprv = pw_encode(b, new_password, version=PW_HASH_VERSION_LATEST)
|
||||||
|
self.pw_hash_version = PW_HASH_VERSION_LATEST
|
||||||
|
|
||||||
def is_watching_only(self):
|
def is_watching_only(self):
|
||||||
return self.xprv is None
|
return self.xprv is None
|
||||||
|
@ -362,7 +377,7 @@ class Old_KeyStore(Deterministic_KeyStore):
|
||||||
self.mpk = d.get('mpk')
|
self.mpk = d.get('mpk')
|
||||||
|
|
||||||
def get_hex_seed(self, password):
|
def get_hex_seed(self, password):
|
||||||
return pw_decode(self.seed, password).encode('utf8')
|
return pw_decode(self.seed, password, version=self.pw_hash_version).encode('utf8')
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
d = Deterministic_KeyStore.dump(self)
|
d = Deterministic_KeyStore.dump(self)
|
||||||
|
@ -484,8 +499,9 @@ class Old_KeyStore(Deterministic_KeyStore):
|
||||||
if new_password == '':
|
if new_password == '':
|
||||||
new_password = None
|
new_password = None
|
||||||
if self.has_seed():
|
if self.has_seed():
|
||||||
decoded = pw_decode(self.seed, old_password)
|
decoded = pw_decode(self.seed, old_password, version=self.pw_hash_version)
|
||||||
self.seed = pw_encode(decoded, new_password)
|
self.seed = pw_encode(decoded, new_password, version=PW_HASH_VERSION_LATEST)
|
||||||
|
self.pw_hash_version = PW_HASH_VERSION_LATEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from electrum.crypto import sha256d, EncodeAES, DecodeAES
|
from electrum.crypto import sha256d, EncodeAES_base64, DecodeAES_base64
|
||||||
from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh,
|
from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh,
|
||||||
is_address)
|
is_address)
|
||||||
from electrum.bip32 import serialize_xpub, deserialize_xpub
|
from electrum.bip32 import serialize_xpub, deserialize_xpub
|
||||||
|
@ -396,10 +396,10 @@ class DigitalBitbox_Client():
|
||||||
reply = ""
|
reply = ""
|
||||||
try:
|
try:
|
||||||
secret = sha256d(self.password)
|
secret = sha256d(self.password)
|
||||||
msg = EncodeAES(secret, msg)
|
msg = EncodeAES_base64(secret, msg)
|
||||||
reply = self.hid_send_plain(msg)
|
reply = self.hid_send_plain(msg)
|
||||||
if 'ciphertext' in reply:
|
if 'ciphertext' in reply:
|
||||||
reply = DecodeAES(secret, ''.join(reply["ciphertext"]))
|
reply = DecodeAES_base64(secret, ''.join(reply["ciphertext"]))
|
||||||
reply = to_string(reply, 'utf8')
|
reply = to_string(reply, 'utf8')
|
||||||
reply = json.loads(reply)
|
reply = json.loads(reply)
|
||||||
if 'error' in reply:
|
if 'error' in reply:
|
||||||
|
@ -716,7 +716,7 @@ class DigitalBitboxPlugin(HW_PluginBase):
|
||||||
key_s = base64.b64decode(self.digitalbitbox_config['encryptionprivkey'])
|
key_s = base64.b64decode(self.digitalbitbox_config['encryptionprivkey'])
|
||||||
args = 'c=data&s=0&dt=0&uuid=%s&pl=%s' % (
|
args = 'c=data&s=0&dt=0&uuid=%s&pl=%s' % (
|
||||||
self.digitalbitbox_config['comserverchannelid'],
|
self.digitalbitbox_config['comserverchannelid'],
|
||||||
EncodeAES(key_s, json.dumps(payload).encode('ascii')).decode('ascii'),
|
EncodeAES_base64(key_s, json.dumps(payload).encode('ascii')).decode('ascii'),
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
requests.post(url, args)
|
requests.post(url, args)
|
||||||
|
|
|
@ -11,11 +11,11 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
|
||||||
from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation,
|
from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation,
|
||||||
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
|
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
|
||||||
is_xpub, convert_bip32_path_to_list_of_uint32)
|
is_xpub, convert_bip32_path_to_list_of_uint32)
|
||||||
from electrum.crypto import sha256d
|
from electrum.crypto import sha256d, KNOWN_PW_HASH_VERSIONS
|
||||||
from electrum import ecc, crypto, constants
|
from electrum import ecc, crypto, constants
|
||||||
from electrum.ecc import number_to_string, string_to_number
|
from electrum.ecc import number_to_string, string_to_number
|
||||||
from electrum.transaction import opcodes
|
from electrum.transaction import opcodes
|
||||||
from electrum.util import bfh, bh2u
|
from electrum.util import bfh, bh2u, InvalidPassword
|
||||||
from electrum.storage import WalletStorage
|
from electrum.storage import WalletStorage
|
||||||
from electrum.keystore import xtype_from_derivation
|
from electrum.keystore import xtype_from_derivation
|
||||||
|
|
||||||
|
@ -219,23 +219,26 @@ class Test_bitcoin(SequentialTestCase):
|
||||||
"""Make sure AES is homomorphic."""
|
"""Make sure AES is homomorphic."""
|
||||||
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
||||||
password = u'secret'
|
password = u'secret'
|
||||||
enc = crypto.pw_encode(payload, password)
|
for version in KNOWN_PW_HASH_VERSIONS:
|
||||||
dec = crypto.pw_decode(enc, password)
|
enc = crypto.pw_encode(payload, password, version=version)
|
||||||
self.assertEqual(dec, payload)
|
dec = crypto.pw_decode(enc, password, version=version)
|
||||||
|
self.assertEqual(dec, payload)
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
def test_aes_encode_without_password(self):
|
def test_aes_encode_without_password(self):
|
||||||
"""When not passed a password, pw_encode is noop on the payload."""
|
"""When not passed a password, pw_encode is noop on the payload."""
|
||||||
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
||||||
enc = crypto.pw_encode(payload, None)
|
for version in KNOWN_PW_HASH_VERSIONS:
|
||||||
self.assertEqual(payload, enc)
|
enc = crypto.pw_encode(payload, None, version=version)
|
||||||
|
self.assertEqual(payload, enc)
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
def test_aes_deencode_without_password(self):
|
def test_aes_deencode_without_password(self):
|
||||||
"""When not passed a password, pw_decode is noop on the payload."""
|
"""When not passed a password, pw_decode is noop on the payload."""
|
||||||
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0'
|
||||||
enc = crypto.pw_decode(payload, None)
|
for version in KNOWN_PW_HASH_VERSIONS:
|
||||||
self.assertEqual(payload, enc)
|
enc = crypto.pw_decode(payload, None, version=version)
|
||||||
|
self.assertEqual(payload, enc)
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
def test_aes_decode_with_invalid_password(self):
|
def test_aes_decode_with_invalid_password(self):
|
||||||
|
@ -243,8 +246,10 @@ class Test_bitcoin(SequentialTestCase):
|
||||||
payload = u"blah"
|
payload = u"blah"
|
||||||
password = u"uber secret"
|
password = u"uber secret"
|
||||||
wrong_password = u"not the password"
|
wrong_password = u"not the password"
|
||||||
enc = crypto.pw_encode(payload, password)
|
for version in KNOWN_PW_HASH_VERSIONS:
|
||||||
self.assertRaises(Exception, crypto.pw_decode, enc, wrong_password)
|
enc = crypto.pw_encode(payload, password, version=version)
|
||||||
|
with self.assertRaises(InvalidPassword):
|
||||||
|
crypto.pw_decode(enc, wrong_password, version=version)
|
||||||
|
|
||||||
def test_sha256d(self):
|
def test_sha256d(self):
|
||||||
self.assertEqual(b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4',
|
self.assertEqual(b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4',
|
||||||
|
|
Loading…
Add table
Reference in a new issue