diff --git a/README.md b/README.md
index fbc93040..8e90bfaa 100644
--- a/README.md
+++ b/README.md
@@ -201,7 +201,7 @@ Donate:
[R]: ../../wiki/Getting-Started-with-MMGen-Wallet#a_rbf
[B]: ../../wiki/command-help-txbump
[69]: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
-[O]: ../../wiki/XOR-Seed-Splitting:-Theory-and-Practice
+[O]: ../../wiki/XOR-Seed-Splitting-^-Theory-and-Practice
[ms]: ../../wiki/command-help-seedsplit
[ta]: ../../wiki/Tool-API
[L]: ../../wiki/command-help-tool
diff --git a/SIGNING_KEYS.pub b/SIGNING_KEYS.pub
index cdbd7ec2..6ff4a1da 100644
--- a/SIGNING_KEYS.pub
+++ b/SIGNING_KEYS.pub
@@ -52,7 +52,6 @@ dRrWK1eeHDwJ7AnLSfwxBGc=
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1
mQSuBFqMFuERDACE4JN6sXMel/fc2YB7F1N4h6utfdcTBHKtx97eTfq0yH1laFa/
IpkBrDZ5GOdxTDdW3NVB4q/Wx8fR0BVUktv3cXF/d8b/B/z4OSSWbRYgNw6+C9Ol
@@ -79,20 +78,28 @@ m+8wy4zDYefUQuF8ydgNcebBRWnTU6Tb3ujVMW22WIGRK1cbo8BfnlbFvSd1ka2T
FKjiUgGypA7aLQKDt4s0QQ6fNnTn/BGcJj0tFwoZ28CRdruCG9cbdDVMKQQOERr+
msnL7OOAKrwpLNZ3+g20lFbp8dTQZpJEiH1bog27M6U231/OUavo/6HyAH+rs5Lk
MMdeJ3BYd6UszbJoAUWTcHgxN3HiEe+UmU8J1Spl6xQjCICVAKg87SOjjAQwTiWc
-77QnTU1HZW4gKERTQSBzaWduaW5nIGtleSkgPG1tZ2VuQHR1dGEuaW8+iHoEExEI
-ACIFAlqMFuECGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJED+LGGHjK32i
-VP4A/0bL7rJTGuyGL5YSMJRsRx8iqvWEpuLo20/CW68C69OmAP9rc0OAcTsLbT+x
-4wT+3d58sBuCn9fLl1inoHo5XFZSEIkCHAQQAQIABgUCWowXfAAKCRBi2+nlIS8F
-vlfmD/4hl3XSidJjeWNVKs+qjtOSm3MOCLqqK9PukzX1B98eyWgiBd2UsHv0yWAI
-u4ldppIgpJY/2NyntWTNZojmoNftc/nRnz3DIOzmlm+9G61TfsIxvEuQn6Vxs9rD
-LzF/8X6ChaaVQI41d4C4wFBoKdxt6mmm7mYgWcY/IzIAnjYWgC9sBwZxuvXYorfk
-8xFU0zB/fTQQUjkidpXX48Zyox+2l/0KN24jbpoNAQcqRSkdgjL9+5BuYryp3Pvc
-oJHJYA4VI9LscYXhyiUZQQIOx272JIo6Ycg1Lb7YXv8SU9p9r5anxJfW7mZNULGi
-KUPR+WqpFkG4kjSh6JbCd4KIw60vSCmQiXOi+9tDFJHBtZiUweSK/qzazieLTIDu
-sqv2BX4WVOtTPuBFuFYzXUkEbaB916QlFgE9IGWdxDRgGHEVe6hAS33mRAPaMGJ7
-SN4A9R18YCEzfUC/ZSu3iZIL6Y/hvGI3aSENwsfyJ5ipk/1JvhWLrP7tLVRmqVzB
-vP8uJpjTKJTn+xs0r7yv3VveHo44fPOoDGiC/P5MzL2oni3MRPv88/zTCjlebTne
-id850rPAZGY4J325pPE9grvuF5A/tbPm01CUWwRVzaBYSdFO2haRJjyeNW5s305L
-s8+pI8KYEcBY9jsjMKSRU+7O7N6ANl079US9wRf6shjTklbB6A==
-=x3WZ
+77Q6VGhlIE1NR2VuIFByb2plY3QgKHJlcG9zaXRvcnkgc2lnbmluZyBrZXkpIDxt
+bWdlbkB0dXRhLmlvPoiQBBMRCAA4FiEEXITLRa7iJQ8xpqVwP4sYYeMrfaIFAl2t
+5dsCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQP4sYYeMrfaL3sAD9HvMy
+95DddVooCJud2r02urQ6O63239DkEn9Uqdv3fu8A/iwOzmOg7q21yUceJY8+xqgZ
+pYtctenGqYZKtBB/y/YNtCdNTUdlbiAoRFNBIHNpZ25pbmcga2V5KSA8bW1nZW5A
+dHV0YS5pbz6IegQTEQgAIgUCWowW4QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
+F4AACgkQP4sYYeMrfaJU/gD/RsvuslMa7IYvlhIwlGxHHyKq9YSm4ujbT8JbrwLr
+06YA/2tzQ4BxOwttP7HjBP7d3nywG4Kf18uXWKegejlcVlIQiQIcBBABAgAGBQJa
+jBd8AAoJEGLb6eUhLwW+V+YP/iGXddKJ0mN5Y1Uqz6qO05Kbcw4Iuqor0+6TNfUH
+3x7JaCIF3ZSwe/TJYAi7iV2mkiCklj/Y3Ke1ZM1miOag1+1z+dGfPcMg7OaWb70b
+rVN+wjG8S5CfpXGz2sMvMX/xfoKFppVAjjV3gLjAUGgp3G3qaabuZiBZxj8jMgCe
+NhaAL2wHBnG69diit+TzEVTTMH99NBBSOSJ2ldfjxnKjH7aX/Qo3biNumg0BBypF
+KR2CMv37kG5ivKnc+9ygkclgDhUj0uxxheHKJRlBAg7HbvYkijphyDUtvthe/xJT
+2n2vlqfEl9buZk1QsaIpQ9H5aqkWQbiSNKHolsJ3gojDrS9IKZCJc6L720MUkcG1
+mJTB5Ir+rNrOJ4tMgO6yq/YFfhZU61M+4EW4VjNdSQRtoH3XpCUWAT0gZZ3ENGAY
+cRV7qEBLfeZEA9owYntI3gD1HXxgITN9QL9lK7eJkgvpj+G8YjdpIQ3Cx/InmKmT
+/Um+FYus/u0tVGapXMG8/y4mmNMolOf7GzSvvK/dW94ejjh886gMaIL8/kzMvaie
+LcxE+/zz/NMKOV5tOd6J3znSs8BkZjgnfbmk8T2Cu+4XkD+1s+bTUJRbBFXNoFhJ
+0U7aFpEmPJ41bmzfTkuzz6kjwpgRwFj2OyMwpJFT7s7s3oA2XTv1RL3BF/qyGNOS
+VsHouDgEahCWthIKKwYBBAGXVQEFAQEHQNYXr/lI34GouI0536OljciggBQlgs0+
+IavFkpP6zVFkAwEIB4h4BBgRCAAgFiEEXITLRa7iJQ8xpqVwP4sYYeMrfaIFAmoQ
+lrYCGwwACgkQP4sYYeMrfaJB6AEAncaaUqjyqL0acGFoKdFTS+gi8jt5KvCEHtVR
+hWKP3XsBAKT/fsAIIbXDvu1+g3/HIRlfbZaMFQs45FNtzLOVu7vy
+=7u5V
-----END PGP PUBLIC KEY BLOCK-----
diff --git a/doc/wiki/Altcoin-and-Forkcoin-Support.md b/doc/wiki/Altcoin-and-Forkcoin-Support.md
index c9bd7dc4..f9d9eb85 100644
--- a/doc/wiki/Altcoin-and-Forkcoin-Support.md
+++ b/doc/wiki/Altcoin-and-Forkcoin-Support.md
@@ -244,8 +244,9 @@ commands. It’s that simple!
Monero is fully supported by MMGen Wallet.
-Make sure that [Monerod][M] is installed and running and that
-`monero-wallet-rpc` is located in your executable path.
+Make sure that [Monerod][M] is installed and running on your online machine and
+that `monero-wallet-rpc` is installed to your executable path on both online and
+offline machines.
Install the Python XMR requirements:
diff --git a/doc/wiki/XOR-Seed-Splitting-Theory-and-Practice.md b/doc/wiki/XOR-Seed-Splitting-^-Theory-and-Practice.md
similarity index 98%
rename from doc/wiki/XOR-Seed-Splitting-Theory-and-Practice.md
rename to doc/wiki/XOR-Seed-Splitting-^-Theory-and-Practice.md
index 5a2e0402..ac3d6b7d 100644
--- a/doc/wiki/XOR-Seed-Splitting-Theory-and-Practice.md
+++ b/doc/wiki/XOR-Seed-Splitting-^-Theory-and-Practice.md
@@ -8,8 +8,9 @@
### XOR Seed Splitting: A Theoretical Introduction
-The bitwise exclusive-or operation (usually denoted as `XOR`, or “![⊕]”)
-has interesting properties that make it very useful in cryptography.
+The bitwise exclusive-or operation (usually denoted as `XOR`, “![⊕]”, or “^” in
+programming languages), has interesting properties that make it very useful in
+cryptography.
Suppose we have two bytes, *a* and *b*:
diff --git a/extmod/secp256k1mod.c b/extmod/secp256k1mod.c
index 8fdea24c..bd1a7dc1 100755
--- a/extmod/secp256k1mod.c
+++ b/extmod/secp256k1mod.c
@@ -428,12 +428,19 @@ PyMODINIT_FUNC PyInit_secp256k1(void) {
if (module == NULL)
INITERROR;
+
struct module_state *st = GETSTATE(module);
st->error = PyErr_NewException("secp256k1.Error", NULL, NULL);
+
if (st->error == NULL) {
Py_DECREF(module);
INITERROR;
}
+
+#ifdef Py_GIL_DISABLED
+ PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED);
+#endif
+
return module;
}
diff --git a/mmgen/altcoin/util.py b/mmgen/altcoin/util.py
index 89dd185a..d2811be2 100755
--- a/mmgen/altcoin/util.py
+++ b/mmgen/altcoin/util.py
@@ -14,7 +14,7 @@ altcoin.util: various altcoin-related utilities
from ..util import die
-def decrypt_keystore(data, passwd, *, mac_algo=None, mac_params={}):
+def decrypt_keystore(cfg, data, passwd, *, mac_algo=None, mac_params={}):
"""
Decrypt the encrypted data in a cross-chain keystore
Returns the decrypted data as a bytestring
@@ -70,12 +70,8 @@ def decrypt_keystore(data, passwd, *, mac_algo=None, mac_params={}):
die(1, 'incorrect password')
# Decrypt data:
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- from cryptography.hazmat.backends import default_backend
- cipher_len = int(cipher.split('-')[1]) // 8
- c = Cipher(
- algorithms.AES(hashed_pw[:cipher_len]),
- modes.CTR(bytes.fromhex(cdata['cipherparams']['iv'])),
- backend = default_backend())
- encryptor = c.encryptor()
- return encryptor.update(bytes.fromhex(cdata['ciphertext'])) + encryptor.finalize()
+ from ..crypto import Crypto
+ return Crypto(cfg).encrypt_aes_ctr(
+ hashed_pw[:int(cipher.split('-')[1]) // 8],
+ bytes.fromhex(cdata['cipherparams']['iv']),
+ bytes.fromhex(cdata['ciphertext']))
diff --git a/mmgen/amt.py b/mmgen/amt.py
index 3796fe39..b4b9899d 100755
--- a/mmgen/amt.py
+++ b/mmgen/amt.py
@@ -138,7 +138,7 @@ class CoinAmt(Decimal, Hilite, InitErrors): # abstract class
def __sub__(self, other, *args, **kwargs):
if type(other) is type(self):
- return type(self)(Decimal.__sub__(self, other, *args, **kwargs), from_decimal=True)
+ return type(self)(Decimal.__sub__(Decimal(self), Decimal(other), *args, **kwargs), from_decimal=True)
raise TypeError(
f'operand {other} is of incorrect type ({type(other).__name__} != {type(self).__name__})')
diff --git a/mmgen/autosign/__init__.py b/mmgen/autosign/__init__.py
index 1cd5c565..6adca942 100755
--- a/mmgen/autosign/__init__.py
+++ b/mmgen/autosign/__init__.py
@@ -401,7 +401,10 @@ class Autosign:
cfg = self.cfg,
prompt = f'Default wallet ‘{wf}’ found.\nUse default wallet for autosigning?',
default_yes = True):
- ss_in = Wallet(Config(), fn=wf)
+ ss_in = Wallet(Config({
+ 'test_suite': self.cfg.test_suite,
+ 'aes_backend': self.cfg.aes_backend
+ }), fn=wf)
else:
ss_in = get_mn_wallet()
diff --git a/mmgen/cfg.py b/mmgen/cfg.py
index a5a3442c..f47fd01c 100755
--- a/mmgen/cfg.py
+++ b/mmgen/cfg.py
@@ -228,6 +228,8 @@ class Config(Lockable):
enable_erigon = False
autochg_ignore_labels = False
autosign = False
+ threaded_python = not sys._is_gil_enabled()
+ aes_backend = 'cryptography'
# regtest:
bob = False
@@ -332,50 +334,52 @@ class Config(Lockable):
_env_opts = (
'MMGEN_DEBUG_ALL', # special: there is no `debug_all` attribute
+ 'MMGEN_AES_BACKEND',
+ 'MMGEN_BLACKLIST_DAEMONS',
+ 'MMGEN_BOGUS_SEND',
+ 'MMGEN_BOGUS_UNSPENT_DATA',
'MMGEN_COLUMNS',
+ 'MMGEN_DAEMON_STATE_TIMEOUT',
+ 'MMGEN_DEBUG',
+ 'MMGEN_DEBUG_ADDRLIST',
+ 'MMGEN_DEBUG_DAEMON',
+ 'MMGEN_DEBUG_EVM',
+ 'MMGEN_DEBUG_OPTS',
+ 'MMGEN_DEBUG_RPC',
+ 'MMGEN_DEBUG_SUBSEED',
+ 'MMGEN_DEBUG_TW',
+ 'MMGEN_DEBUG_UTF8',
+ 'MMGEN_DEVTOOLS',
+ 'MMGEN_DISABLE_COLOR',
+ 'MMGEN_ENABLE_ERIGON',
+ 'MMGEN_EXEC_WRAPPER',
+ 'MMGEN_FORCE_256_COLOR',
+ 'MMGEN_HOLD_PROTECT_DISABLE',
+ 'MMGEN_HTTP_TIMEOUT',
+ 'MMGEN_IGNORE_DAEMON_VERSION',
+ 'MMGEN_IGNORE_TEST_PY_EXCEPTION',
+ 'MMGEN_NO_LICENSE',
+ 'MMGEN_QUIET',
+ 'MMGEN_REGTEST',
+ 'MMGEN_RPC_BACKEND',
+ 'MMGEN_RPC_FAIL_ON_COMMAND',
+ 'MMGEN_RPC_HOST',
+ 'MMGEN_TESTNET',
'MMGEN_TEST_SUITE',
'MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE',
'MMGEN_TEST_SUITE_AUTOSIGN_THREADED',
- 'MMGEN_TEST_SUITE_DEVNET_BLOCK_PERIOD',
- 'MMGEN_TEST_SUITE_XMR_AUTOSIGN',
'MMGEN_TEST_SUITE_CFGTEST',
'MMGEN_TEST_SUITE_DETERMINISTIC',
+ 'MMGEN_TEST_SUITE_DEVNET_BLOCK_PERIOD',
'MMGEN_TEST_SUITE_ENABLE_COLOR',
'MMGEN_TEST_SUITE_PEXPECT',
'MMGEN_TEST_SUITE_PEXPECT_TIMEOUT',
'MMGEN_TEST_SUITE_POPEN_SPAWN',
'MMGEN_TEST_SUITE_ROOT_PFX',
+ 'MMGEN_TEST_SUITE_XMR_AUTOSIGN',
+ 'MMGEN_THREADED_PYTHON',
'MMGEN_TRACEBACK',
- 'MMGEN_BLACKLIST_DAEMONS',
- 'MMGEN_BOGUS_SEND',
- 'MMGEN_BOGUS_UNSPENT_DATA',
- 'MMGEN_DAEMON_STATE_TIMEOUT',
- 'MMGEN_DEBUG',
- 'MMGEN_DEBUG_DAEMON',
- 'MMGEN_DEBUG_EVM',
- 'MMGEN_DEBUG_OPTS',
- 'MMGEN_DEBUG_RPC',
- 'MMGEN_DEBUG_ADDRLIST',
- 'MMGEN_DEBUG_TW',
- 'MMGEN_DEBUG_UTF8',
- 'MMGEN_DEBUG_SUBSEED',
- 'MMGEN_DEVTOOLS',
- 'MMGEN_FORCE_256_COLOR',
- 'MMGEN_HOLD_PROTECT_DISABLE',
- 'MMGEN_HTTP_TIMEOUT',
- 'MMGEN_QUIET',
- 'MMGEN_NO_LICENSE',
- 'MMGEN_RPC_HOST',
- 'MMGEN_RPC_FAIL_ON_COMMAND',
- 'MMGEN_TESTNET',
- 'MMGEN_REGTEST',
- 'MMGEN_EXEC_WRAPPER',
- 'MMGEN_IGNORE_TEST_PY_EXCEPTION',
- 'MMGEN_RPC_BACKEND',
- 'MMGEN_IGNORE_DAEMON_VERSION',
- 'MMGEN_USE_STANDALONE_SCRYPT_MODULE',
- 'MMGEN_ENABLE_ERIGON',
- 'MMGEN_DISABLE_COLOR')
+ 'MMGEN_USE_STANDALONE_SCRYPT_MODULE')
_infile_opts = (
'keys_from_file',
diff --git a/mmgen/crypto.py b/mmgen/crypto.py
index f9fad0fe..b148cb3c 100755
--- a/mmgen/crypto.py
+++ b/mmgen/crypto.py
@@ -31,7 +31,6 @@ class Crypto:
mmenc_ext = 'mmenc'
scramble_hash_rounds = 10
- salt_len = 16
aesctr_iv_len = 16
aesctr_dfl_iv = int.to_bytes(1, aesctr_iv_len, 'big')
hincog_chk_len = 8
@@ -68,6 +67,29 @@ class Crypto:
def __init__(self, cfg):
self.cfg = cfg
self.util = cfg._util
+ if cfg.test_suite and self.cfg.aes_backend == 'pyaes':
+ self.get_aes_ctr = self.get_aes_ctr_pyaes
+ self.encrypt_aes_ctr = self.encrypt_aes_ctr_pyaes
+
+ @staticmethod
+ def get_aes_ctr(key, iv):
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+ from cryptography.hazmat.backends import default_backend
+ return Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend()).encryptor()
+
+ def encrypt_aes_ctr(self, key, iv, data):
+ encryptor = self.get_aes_ctr(key, iv)
+ return encryptor.update(data) + encryptor.finalize()
+
+ @staticmethod
+ def get_aes_ctr_pyaes(key, iv):
+ import pyaes
+ class MyAES(pyaes.AESModeOfOperationCTR):
+ update = pyaes.AESModeOfOperationCTR.encrypt
+ return MyAES(key, pyaes.Counter(int.from_bytes(iv)))
+
+ def encrypt_aes_ctr_pyaes(self, key, iv, data):
+ return self.get_aes_ctr_pyaes(key, iv).encrypt(data)
def get_hash_params(self, hash_preset):
if hash_preset in self.hash_presets:
@@ -127,20 +149,14 @@ class Crypto:
verify = True,
silent = False):
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- from cryptography.hazmat.backends import default_backend
if not silent:
self.util.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()
+
+ enc_data = self.encrypt_aes_ctr(key, iv, data)
if verify:
self.util.vmsg_r(f'Performing a test decryption of the {desc}...')
- c = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
- encryptor = c.encryptor()
- dec_data = encryptor.update(enc_data) + encryptor.finalize()
- if dec_data != data:
+ if self.encrypt_aes_ctr(key, iv, enc_data) != data:
die(2, f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
if not silent:
self.util.vmsg('done')
@@ -155,12 +171,9 @@ class Crypto:
iv = aesctr_dfl_iv,
desc = 'data'):
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- from cryptography.hazmat.backends import default_backend
self.util.vmsg_r(f'Decrypting {desc} with key...')
- c = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
- encryptor = c.encryptor()
- return encryptor.update(enc_data) + encryptor.finalize()
+
+ return self.encrypt_aes_ctr(key, iv, enc_data)
def scrypt_hash_passphrase(
self,
diff --git a/mmgen/data/version b/mmgen/data/version
index e0228cf1..6dbf8228 100644
--- a/mmgen/data/version
+++ b/mmgen/data/version
@@ -1 +1 @@
-16.1.0
+16.2.dev1
diff --git a/mmgen/proto/eth/util.py b/mmgen/proto/eth/util.py
index e5edec09..3a59928e 100755
--- a/mmgen/proto/eth/util.py
+++ b/mmgen/proto/eth/util.py
@@ -27,6 +27,7 @@ def decrypt_geth_keystore(cfg, wallet_fn, passwd, *, check_addr=True):
from ...altcoin.util import decrypt_keystore
key = decrypt_keystore(
+ cfg,
wallet_data,
passwd,
mac_algo = get_keccak())
diff --git a/mmgen/tool/fileutil.py b/mmgen/tool/fileutil.py
index 6330af93..dcd62a3c 100755
--- a/mmgen/tool/fileutil.py
+++ b/mmgen/tool/fileutil.py
@@ -70,7 +70,7 @@ class tool_cmd(tool_cmd_base):
os.close(f)
return True
- def rand2file(self, outfile: str, nbytes: str, *, threads=4, silent=False):
+ def rand2file(self, outfile: str, nbytes: str, *, silent=False):
"""
write ‘nbytes’ bytes of random data to specified file (dd-style byte specifiers supported)
@@ -94,15 +94,12 @@ class tool_cmd(tool_cmd_base):
"""
from threading import Thread
from queue import Queue
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- from cryptography.hazmat.backends import default_backend
from ..util2 import parse_bytespec
+ from ..crypto import Crypto
def encrypt_worker():
- ctr_init_val = os.urandom(Crypto.aesctr_iv_len)
- c = Cipher(algorithms.AES(key), modes.CTR(ctr_init_val), backend=default_backend())
- encryptor = c.encryptor()
+ encryptor = Crypto(self.cfg).get_aes_ctr(key, os.urandom(Crypto.aesctr_iv_len))
while True:
q2.put(encryptor.update(q1.get()))
q1.task_done()
@@ -121,7 +118,12 @@ class tool_cmd(tool_cmd_base):
key = Crypto(self.cfg).get_random(32)
q1, q2 = (Queue(), Queue())
- for i in range(max(1, threads-2)):
+ try:
+ threads = os.process_cpu_count() # Python 3.13
+ except AttributeError:
+ threads = os.cpu_count()
+
+ for i in range(max(1, threads - 1)):
t = Thread(target=encrypt_worker)
t.daemon = True
t.start()
@@ -161,7 +163,7 @@ class tool_cmd(tool_cmd_base):
with open(wallet_file) as fh:
data = json.loads(fh.read())
from ..altcoin.util import decrypt_keystore
- ret = decrypt_keystore(data[0]['keystore'], passwd)
+ ret = decrypt_keystore(self.cfg, data[0]['keystore'], passwd)
return ret.hex() if output_hex else ret
def decrypt_geth_keystore(self, wallet_file: str, *, check_addr=True):
diff --git a/mmgen/wallet/enc.py b/mmgen/wallet/enc.py
index 7e2423cd..21fd3d17 100755
--- a/mmgen/wallet/enc.py
+++ b/mmgen/wallet/enc.py
@@ -94,7 +94,7 @@ class wallet(wallet):
d.passwd = self._get_new_passphrase()
from hashlib import sha256
- d.salt = sha256(self.crypto.get_random(128)).digest()[:self.crypto.salt_len]
+ d.salt = sha256(self.crypto.get_random(128)).digest()[:self.salt_len]
key = self.crypto.make_key(d.passwd, d.salt, d.hash_preset)
d.key_id = make_chksum_8(key)
d.enc_seed = self.crypto.encrypt_seed(self.seed.data, key)
diff --git a/mmgen/wallet/incog_base.py b/mmgen/wallet/incog_base.py
index 0766ce31..b2b43f80 100755
--- a/mmgen/wallet/incog_base.py
+++ b/mmgen/wallet/incog_base.py
@@ -18,6 +18,8 @@ from .enc import wallet
class wallet(wallet):
+ salt_len = 16
+
_msg = {
'check_incog_id': """
Check the generated Incog ID above against your records. If it doesn't
@@ -36,7 +38,7 @@ class wallet(wallet):
def _get_incog_data_len(self, seed_len):
return (
self.crypto.aesctr_iv_len
- + self.crypto.salt_len
+ + self.salt_len
+ (0 if self.cfg.old_incog_fmt else self.crypto.hincog_chk_len)
+ seed_len//8)
@@ -71,7 +73,7 @@ class wallet(wallet):
self.cfg._util.qmsg('Make a record of this value')
self.cfg._util.vmsg('\n ' + self.msg['record_incog_id'].strip()+'\n')
- d.salt = crypto.get_random(crypto.salt_len)
+ d.salt = crypto.get_random(self.salt_len)
seed_key = crypto.make_key(
passwd = d.passwd,
salt = d.salt,
@@ -167,8 +169,8 @@ class wallet(wallet):
iv = d.iv,
desc = 'incog data')
- d.salt = dd[0:crypto.salt_len]
- d.enc_seed = dd[crypto.salt_len:]
+ d.salt = dd[0:self.salt_len]
+ d.enc_seed = dd[self.salt_len:]
seed_key = crypto.make_key(
passwd = d.passwd,
diff --git a/mmgen/wallet/mmgen.py b/mmgen/wallet/mmgen.py
index 4c43319c..139cf125 100755
--- a/mmgen/wallet/mmgen.py
+++ b/mmgen/wallet/mmgen.py
@@ -24,6 +24,7 @@ from .enc import wallet
class wallet(wallet):
desc = 'MMGen wallet'
+ salt_len = 32
def __init__(self, *args, **kwargs):
if self.cfg.label:
diff --git a/nix/user-packages.nix b/nix/user-packages.nix
index 3c6e2c8b..cf61e739 100644
--- a/nix/user-packages.nix
+++ b/nix/user-packages.nix
@@ -54,5 +54,6 @@ rec {
# pydantic = pydantic; # eth-keys
# pure-protobuf = pure-protobuf; # THORChain
# bip-utils = bip-utils; # bip_hd
+ # pyaes = pyaes; # developer testing
};
}
diff --git a/test/cmdtest.py b/test/cmdtest.py
index 92574ca9..c8dfd3d5 100755
--- a/test/cmdtest.py
+++ b/test/cmdtest.py
@@ -122,6 +122,8 @@ opts_data = {
-x, --debug-pexpect Produce debugging output for pexpect calls
--, --demo Add extra delay after each send to make input visible.
Implies --exact-output --pexpect-spawn --buf-keypress
+--, --dev-mode Run spawned scripts in Python Development Mode
+ (PYTHONDEVMODE=1 PYTHONTRACEMALLOC=10)
-d, --deps-only Run a command or command subgroup’s dependencies without
running the command or command group itself.
-D, --no-daemon-stop Don't stop auto-started daemons after running tests
diff --git a/test/cmdtest_d/base.py b/test/cmdtest_d/base.py
index 6cda6a27..1cec2938 100755
--- a/test/cmdtest_d/base.py
+++ b/test/cmdtest_d/base.py
@@ -95,21 +95,21 @@ class CmdTestBase:
except:
msg(f'{fn}: file does not exist or could not be deleted')
- def skip_for_platform(self, name, extra_msg=None):
- if gc.platform == name:
- msg(gray('Skipping test {!r} for {} platform{}'.format(
+ def skip_on_condition(self, condition, message, extra_msg):
+ if condition:
+ msg(gray('Skipping test {!r} {}{}'.format(
self.test_name,
- name,
+ message,
f' ({extra_msg})' if extra_msg else "")))
return True
else:
return False
def skip_for_mac(self, extra_msg=None):
- return self.skip_for_platform('darwin', extra_msg)
+ return self.skip_on_condition(gc.platform=='darwin', 'for macOS platform', extra_msg)
def skip_for_win(self, extra_msg=None):
- return self.skip_for_platform('win32', extra_msg)
+ return self.skip_on_condition(gc.platform=='win32', 'for win32 platform', extra_msg)
def spawn_chk(self, *args, **kwargs):
"""
diff --git a/test/cmdtest_d/include/pexpect.py b/test/cmdtest_d/include/pexpect.py
index 32c08aec..ea9ba5a2 100755
--- a/test/cmdtest_d/include/pexpect.py
+++ b/test/cmdtest_d/include/pexpect.py
@@ -67,7 +67,8 @@ class CmdTestPexpect:
timeout = int(
timeout
or cfg.pexpect_timeout
- or cfg.test_suite_pexpect_timeout) or (60, 5)[bool(cfg.debug_pexpect)]
+ or cfg.test_suite_pexpect_timeout) or (
+ 5 if cfg.debug_pexpect else 180 if cfg.dev_mode else 60)
if pexpect_spawn:
self.p = pexpect.spawn(args[0], args[1:], encoding='utf8', timeout=timeout, env=spawn_env)
else:
diff --git a/test/cmdtest_d/include/runner.py b/test/cmdtest_d/include/runner.py
index 86a69068..06d6ffae 100755
--- a/test/cmdtest_d/include/runner.py
+++ b/test/cmdtest_d/include/runner.py
@@ -105,14 +105,19 @@ class CmdTestRunner:
def set_spawn_env(self):
self.spawn_env = dict(os.environ)
+
self.spawn_env.update({
'MMGEN_NO_LICENSE': '1',
'MMGEN_BOGUS_SEND': '1',
'MMGEN_TEST_SUITE_PEXPECT': '1',
'EXEC_WRAPPER_DO_RUNTIME_MSG':'1',
# if cmdtest.py itself is running under exec_wrapper, disable writing of traceback file for spawned script
- 'EXEC_WRAPPER_TRACEBACK': '' if os.getenv('MMGEN_EXEC_WRAPPER') else '1',
- })
+ 'EXEC_WRAPPER_TRACEBACK': '' if os.getenv('MMGEN_EXEC_WRAPPER') else '1'})
+
+ if self.cfg.dev_mode:
+ self.spawn_env.update({
+ 'PYTHONDEVMODE': '1',
+ 'PYTHONTRACEMALLOC': '10'})
if self.cfg.exact_output:
from mmgen.term import get_terminal_size
diff --git a/test/cmdtest_d/input.py b/test/cmdtest_d/input.py
index e558dbf2..0743fb0e 100755
--- a/test/cmdtest_d/input.py
+++ b/test/cmdtest_d/input.py
@@ -106,6 +106,15 @@ class CmdTestInput(CmdTestBase):
)
}
+ def skip_no_readline_insert(self, extra_msg=None):
+ return self.skip_on_condition(
+ gc.platform == 'darwin' or self.cfg.threaded_python,
+ ' (no readline insert support)',
+ extra_msg)
+
+ def skip_no_readline_edit(self, extra_msg=None):
+ return self.skip_on_condition(self.cfg.threaded_python, ' (no readline edit support)', extra_msg)
+
def get_seed_from_stdin(self):
self.spawn(msg_only=True)
from subprocess import run, PIPE
@@ -336,7 +345,7 @@ class CmdTestInput(CmdTestBase):
False)
def line_input_insert_term1(self):
- if self.skip_for_mac('readline text buffer issues'):
+ if self.skip_no_readline_insert():
return 'skip'
return self._line_input(
['prompt> ', True, 'foo', True],
@@ -346,7 +355,7 @@ class CmdTestInput(CmdTestBase):
hold_protect_delay)
def line_input_insert_term2(self):
- if self.skip_for_mac('readline text buffer issues'):
+ if self.skip_no_readline_insert():
return 'skip'
return self._line_input(
['prompt> ', True, 'foo', False],
@@ -355,6 +364,8 @@ class CmdTestInput(CmdTestBase):
True)
def line_input_edit_term(self):
+ if self.skip_no_readline_edit():
+ return 'skip'
return self._line_input(
['prompt> ', True, '', True],
'\b\bφυφυ\b\bβαρ',
@@ -363,7 +374,7 @@ class CmdTestInput(CmdTestBase):
hold_protect_delay)
def line_input_edit_term_insert(self):
- if self.skip_for_mac('readline text buffer issues'):
+ if self.skip_no_readline_edit() or self.skip_no_readline_insert():
return 'skip'
return self._line_input(
['prompt> ', True, 'φυφυ', True],
@@ -373,7 +384,7 @@ class CmdTestInput(CmdTestBase):
hold_protect_delay)
def line_input_erase_term(self):
- if self.skip_for_mac('readline text buffer issues'):
+ if self.skip_no_readline_insert():
return 'skip'
return self._line_input(
['prompt> ', True, 'foobarbaz', True],
diff --git a/test/cmdtest_d/wallet.py b/test/cmdtest_d/wallet.py
index 42dd3dab..bc45b567 100755
--- a/test/cmdtest_d/wallet.py
+++ b/test/cmdtest_d/wallet.py
@@ -53,13 +53,12 @@ class CmdTestWalletConv(CmdTestBase, CmdTestShared):
'hic_wallet_old': '1378FC64-B55E9958-D85FF20C[192,1].incog-old.offset123',
},
'256': {
- 'ref_wallet': '98831F3A-27F2BF93[256,1].mmdat',
+ 'ref_wallet': '98831F3A-F825E2A0[256,1].mmdat',
'ic_wallet': '98831F3A-5482381C-18460FB1[256,1].mmincog',
'ic_wallet_hex': '98831F3A-1630A9F2-870376A9[256,1].mmincox',
'hic_wallet': '98831F3A-F59B07A0-559CEF19[256,1].incog.offset123',
'hic_wallet_old': '98831F3A-F59B07A0-848535F3[256,1].incog-old.offset123',
-
},
}
cmd_group = (
diff --git a/test/modtest_d/dep.py b/test/modtest_d/dep.py
index 411b198a..b52c2c53 100755
--- a/test/modtest_d/dep.py
+++ b/test/modtest_d/dep.py
@@ -74,12 +74,9 @@ class unit_tests:
msg('Is the ‘pysocks’ package installed?')
return False
- def cryptography(self, name, ut):
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- from cryptography.hazmat.backends import default_backend
- c = Cipher(algorithms.AES(b'deadbeef'*4), modes.CTR(b'deadbeef'*2), backend=default_backend())
- encryptor = c.encryptor()
- encryptor.update(b'foo') + encryptor.finalize()
+ def aes(self, name, ut):
+ from mmgen.crypto import Crypto
+ Crypto(cfg).encrypt_aes_ctr(b'deadbeef' * 4, b'deadbeef' * 2, b'foo')
return True
def ecdsa(self, name, ut):
diff --git a/test/ref/98831F3A-27F2BF93[256,1].mmdat b/test/ref/98831F3A-27F2BF93[256,1].mmdat
deleted file mode 100644
index 9874260c..00000000
--- a/test/ref/98831F3A-27F2BF93[256,1].mmdat
+++ /dev/null
@@ -1,6 +0,0 @@
-cd828f
-"#$%&()*+,- ./0123456789:;<=>?@AIZ[\]^_`aiz{|}~'
-98831f3a 27f2bf93 256 NE 20150405_075000
-1: 12 8 1
-9440eb NBDH bKqG a23q FtYi nRo1 kk
-7a2f32 5nZf LqjP R9bj vgzc sMGr WPtu PR7S 6NpZ pgGd fc4e QCKt
diff --git a/test/ref/98831F3A-E2687906[256,1].mmdat b/test/ref/98831F3A-E2687906[256,1].mmdat
deleted file mode 100644
index a56c3d71..00000000
--- a/test/ref/98831F3A-E2687906[256,1].mmdat
+++ /dev/null
@@ -1,6 +0,0 @@
-9cc19b
-test.py ref. wallet (pw 'abc', seed len 256)
-98831f3a e2687906 256 NE 20161110_135346
-1: 12 8 1
-70413d 74ev zjeq Zw2g DspF RKpE 7H
-7c26e6 1otd mVTn 5MCR cDTF sZqY uNKA rsAm mjTw EJmS yzwX ZPJd
diff --git a/test/ref/98831F3A-F825E2A0[256,1].mmdat b/test/ref/98831F3A-F825E2A0[256,1].mmdat
new file mode 100644
index 00000000..2d7fcb0d
--- /dev/null
+++ b/test/ref/98831F3A-F825E2A0[256,1].mmdat
@@ -0,0 +1,6 @@
+42f7d8
+"#$%&()*+,- ./0123456789:;<=>?@AIZ[\]^_`aiz{|}~'
+98831f3a f825e2a0 256 NE 20260520_074802
+1: 12 8 1
+384094 8MgZ HHVn QTmH RbiQ hufm obJw 9FkX 7Uv5 AbtK k4yU hqba
+2a4856 8kQ7 JVwg zDQq isws f4g1 7oWi pKS7 gcvq siCX VX3i BeP7