diff --git a/eth2deposit/utils/crypto.py b/eth2deposit/utils/crypto.py index 9e93308..c6881ea 100644 --- a/eth2deposit/utils/crypto.py +++ b/eth2deposit/utils/crypto.py @@ -19,6 +19,8 @@ def SHA256(x: bytes) -> bytes: def scrypt(*, password: str, salt: str, n: int, r: int, p: int, dklen: int) -> bytes: + if n * r * p < 2**20: # 128 MB memory usage + raise ValueError("The Scrypt parameters chosen are not secure.") if n >= 2**(128 * r / 8): raise ValueError("The given `n` should be less than `2**(128 * r / 8)`." f"\tGot `n={n}`, r={r}, 2**(128 * r / 8)={2**(128 * r / 8)}") @@ -29,6 +31,14 @@ def scrypt(*, password: str, salt: str, n: int, r: int, p: int, dklen: int) -> b def PBKDF2(*, password: bytes, salt: bytes, dklen: int, c: int, prf: str) -> bytes: if 'sha' not in prf: raise ValueError(f"String 'sha' is not in `prf`({prf})") + if 'sha256' in prf and c < 2**18: + ''' + Verify the number of rounds of SHA256-PBKDF2. SHA512 bot checked as use in BIP39 + does not require and therefore doesn't use safe parameters (c=2048). + + Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#from-mnemonic-to-seed + ''' + raise ValueError("The PBKDF2 parameters chosen are not secure.") _hash = _sha256 if 'sha256' in prf else _sha512 res = _PBKDF2(password=password, salt=salt, dkLen=dklen, count=c, hmac_hash_module=_hash) # type: ignore return res if isinstance(res, bytes) else res[0] # PyCryptodome can return Tuple[bytes] diff --git a/tests/test_utils/test_crypto.py b/tests/test_utils/test_crypto.py index 92f6ca2..7561219 100644 --- a/tests/test_utils/test_crypto.py +++ b/tests/test_utils/test_crypto.py @@ -10,11 +10,12 @@ from eth2deposit.utils.crypto import ( @pytest.mark.parametrize( 'n, r, valid', [ - (int(2**(128 * 1 / 8)) // 2, 1, True), - (int(2**(128 * 1 / 8)), 1, False), + (int(2**(128 * 1 / 8)) * 2, 8, True), + (int(2**(128 * 1 / 8)) * 1, 8, False), # Unsafe Parameters + (int(2**(128 * 1 / 8)) * 1, 1, False), # Invalid n ] ) -def test_scrypt_invalid_n(n, r, valid): +def test_scrypt_invalid_params(n, r, valid): if valid: scrypt( password="mypassword", @@ -63,6 +64,34 @@ def test_PBKDF2_invalid_prf(prf, valid): ) +@pytest.mark.parametrize( + 'count, prf, valid', + [ + (2**18, "sha256", True), + (2**17, "sha256", False), + (2**11, "sha512", True), + ] +) +def test_PBKDF2_invalid_count(count, prf, valid): + if valid: + PBKDF2( + password="mypassword", + salt="mysalt", + dklen=64, + c=count, + prf=prf + ) + else: + with pytest.raises(ValueError): + PBKDF2( + password="mypassword", + salt="mysalt", + dklen=64, + c=2048, + prf=prf, + ) + + @pytest.mark.parametrize( 'key, iv, valid', [