Skip to content

Commit

Permalink
Merge pull request #43 from ethereum/dev
Browse files Browse the repository at this point in the history
Merge `dev` to `master` (v0.12.x mode)
  • Loading branch information
CarlBeek authored Jul 6, 2020
2 parents e6fb258 + 5e77d38 commit 4ff0754
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 73 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@

This software is a pre-release version which has not yet been audited and therefore should not yet be trusted to keys with the intent of securing actual ETH.

### BLS versioning

The eth2specs changed their BLS version from [BLS v0](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00), and [hash to curve v4](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-04) to [BLS v2](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02), and [hash to curve v7](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-07). Because of this this version of the deposit CLI, is not compatible with eth2 versions >= `0.12.x` which includes main net.

**Using this version for Mainnet deposits will result in loss of funds**

## Tutorial for users

### Requirements
Expand Down
4 changes: 2 additions & 2 deletions eth2deposit/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ def __init__(self, *, mnemonic: str, index: int, amount: int, fork_version: byte

@property
def signing_pk(self) -> bytes:
return bls.PrivToPub(self.signing_sk)
return bls.SkToPk(self.signing_sk)

@property
def withdrawal_pk(self) -> bytes:
return bls.PrivToPub(self.withdrawal_sk)
return bls.SkToPk(self.withdrawal_sk)

@property
def withdrawal_credentials(self) -> bytes:
Expand Down
12 changes: 9 additions & 3 deletions eth2deposit/key_handling/key_derivation/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ def _parent_SK_to_lamport_PK(*, parent_SK: int, index: int) -> bytes:
return compressed_PK


def _HKDF_mod_r(*, IKM: bytes) -> int:
okm = HKDF(salt=b'BLS-SIG-KEYGEN-SALT-', IKM=IKM, L=48)
def _HKDF_mod_r(*, IKM: bytes, key_info: bytes=b'') -> int:
L = 48 # `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the order of the BLS 12-381 curve
okm = HKDF(
salt=b'BLS-SIG-KEYGEN-SALT-',
IKM=IKM + b'\x00', # add postfix `I2OSP(0, 1)`
L=L,
info=key_info + L.to_bytes(2, 'big'),
)
return int.from_bytes(okm, byteorder='big') % bls_curve_order


Expand All @@ -49,5 +55,5 @@ def derive_child_SK(*, parent_SK: int, index: int) -> int:


def derive_master_SK(seed: bytes) -> int:
assert(len(seed) >= 16)
assert(len(seed) >= 32)
return _HKDF_mod_r(IKM=seed)
2 changes: 1 addition & 1 deletion eth2deposit/key_handling/keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def encrypt(cls, *, secret: bytes, password: str, path: str='',
cipher = AES_128_CTR(key=decryption_key[:16], **keystore.crypto.cipher.params)
keystore.crypto.cipher.message = cipher.encrypt(secret)
keystore.crypto.checksum.message = SHA256(decryption_key[16:32] + keystore.crypto.cipher.message)
keystore.pubkey = bls.PrivToPub(int.from_bytes(secret, 'big')).hex()
keystore.pubkey = bls.SkToPk(int.from_bytes(secret, 'big')).hex()
keystore.path = path
return keystore

Expand Down
15 changes: 7 additions & 8 deletions eth2deposit/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@ class BaseChainSetting(NamedTuple):
GENESIS_FORK_VERSION: bytes


MainnetSetting = BaseChainSetting(
GENESIS_FORK_VERSION=bytes.fromhex('00000000'),
)


# Eth2 Mainet setting
MainnetSetting = BaseChainSetting(GENESIS_FORK_VERSION=bytes.fromhex('00000000'))
# Eth2 spec v0.11.3 testnet
WittiSetting = BaseChainSetting(
GENESIS_FORK_VERSION=bytes.fromhex('00000113'),
)
WittiSetting = BaseChainSetting(GENESIS_FORK_VERSION=bytes.fromhex('00000113'))
# Eth2 spec v0.12.1 testnet
AltonaSetting = BaseChainSetting(GENESIS_FORK_VERSION=bytes.fromhex('00000121'))


MAINNET = 'mainnet'
WITTI = 'witti'
ALTONA = 'altona'
ALL_CHAINS: Dict[str, BaseChainSetting] = {
MAINNET: MainnetSetting,
WITTI: WittiSetting,
ALTONA: AltonaSetting,
}


Expand Down
4 changes: 2 additions & 2 deletions eth2deposit/utils/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def PBKDF2(*, password: str, salt: bytes, dklen: int, c: int, prf: str) -> bytes
return res if isinstance(res, bytes) else res[0] # PyCryptodome can return Tuple[bytes]


def HKDF(*, salt: bytes, IKM: bytes, L: int) -> bytes:
res = _HKDF(master=IKM, key_len=L, salt=salt, hashmod=_sha256)
def HKDF(*, salt: bytes, IKM: bytes, L: int, info: bytes=b'') -> bytes:
res = _HKDF(master=IKM, key_len=L, salt=salt, hashmod=_sha256, context=info)
return res if isinstance(res, bytes) else res[0] # PyCryptodome can return Tuple[bytes]


Expand Down
9 changes: 6 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
py-ecc==2.0.0 \
--hash=sha256:0ad540e3a3df332692e35dfac8e550592c345b249d54d6d6c9a91f57bd2a75af \
--hash=sha256:c32cb3aa13b6d5a555f6cad629fb5d2d4af3cd607f239c3422654e1df208ed78
py-ecc==4.0.0 \
--hash=sha256:0712a1ebc2d45417088aa613f28518c1714c99d023998e50244c91e3acbb0d6c \
--hash=sha256:a637edcce7e31ddefae0a3c1018f16e25c9428fcd524b1ac5ceeb2adfc433276
pycryptodome==3.9.7 \
--hash=sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f \
--hash=sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad \
Expand Down Expand Up @@ -63,3 +63,6 @@ toolz==0.10.0 \
six==1.14.0 \
--hash=sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a \
--hash=sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c
cached-property==1.5.1 \
--hash=sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f \
--hash=sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504
14 changes: 9 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

import pytest

from typing import (
Optional,
)

from click.testing import CliRunner

from eth2deposit import deposit
Expand All @@ -11,7 +15,7 @@
from eth2deposit.key_handling.keystore import Keystore


def clean_key_folder(my_folder_path):
def clean_key_folder(my_folder_path: str) -> None:
validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
if not os.path.exists(validator_keys_folder_path):
return
Expand All @@ -23,9 +27,9 @@ def clean_key_folder(my_folder_path):
os.rmdir(my_folder_path)


def test_deposit(monkeypatch):
def test_deposit(monkeypatch) -> None:
# monkeypatch get_mnemonic
def get_mnemonic(language, words_path, entropy=None):
def get_mnemonic(language: str, words_path: str, entropy: Optional[bytes]=None) -> str:
return "fakephrase"

monkeypatch.setattr(deposit, "get_mnemonic", get_mnemonic)
Expand All @@ -47,7 +51,7 @@ def get_mnemonic(language, words_path, entropy=None):
validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME)
_, _, key_files = next(os.walk(validator_keys_folder_path))

def get_uuid(key_file):
def get_uuid(key_file: str) -> str:
keystore = Keystore.from_json(key_file)
return keystore.uuid

Expand All @@ -63,7 +67,7 @@ def get_uuid(key_file):


@pytest.mark.asyncio
async def test_script():
async def test_script() -> None:
my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER')
if not os.path.exists(my_folder_path):
os.mkdir(my_folder_path)
Expand Down
20 changes: 11 additions & 9 deletions tests/test_key_handling/test_key_derivation/test_mnemonic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os
import pytest
import json
from typing import (
Sequence,
)

from eth2deposit.key_handling.key_derivation.mnemonic import (
get_seed,
Expand All @@ -17,14 +20,13 @@


@pytest.mark.parametrize(
'language,language_test_vectors',
[(a, b) for a, b in test_vectors.items()]
'language,test',
[(language, test) for language, language_test_vectors in test_vectors.items() for test in language_test_vectors]
)
def test_bip39(language, language_test_vectors):
for test in language_test_vectors:
test_entropy = bytes.fromhex(test[0])
test_mnemonic = test[1]
test_seed = bytes.fromhex(test[2])
def test_bip39(language: str, test: Sequence[str]) -> None:
test_entropy = bytes.fromhex(test[0])
test_mnemonic = test[1]
test_seed = bytes.fromhex(test[2])

assert get_mnemonic(language=language, words_path=WORD_LISTS_PATH, entropy=test_entropy) == test_mnemonic
assert get_seed(mnemonic=test_mnemonic, password='TREZOR') == test_seed
assert get_mnemonic(language=language, words_path=WORD_LISTS_PATH, entropy=test_entropy) == test_mnemonic
assert get_seed(mnemonic=test_mnemonic, password='TREZOR') == test_seed
43 changes: 38 additions & 5 deletions tests/test_key_handling/test_key_derivation/test_path.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import json
import pytest

from eth2deposit.key_handling.key_derivation.tree import (
_flip_bits_256,
Expand All @@ -8,18 +9,30 @@
_HKDF_mod_r,
)

from eth2deposit.key_handling.key_derivation.path import (
mnemonic_and_path_to_key,
)

test_vector_filefolder = os.path.join(os.getcwd(), 'tests', 'test_key_handling', 'test_key_derivation',
'test_vectors', 'tree_kdf_intermediate.json')
with open(test_vector_filefolder, 'r') as f:
test_vector = json.load(f)
test_vector_dict = json.load(f)


def test_flip_bits_256():
@pytest.mark.parametrize(
'test_vector',
[test_vector_dict]
)
def test_flip_bits_256(test_vector) -> None:
test_vector_int = int(test_vector['seed'][:64], 16) # 64 comes from string chars containing .5 bytes
assert test_vector_int & _flip_bits_256(test_vector_int) == 0


def test_IKM_to_lamport_SK():
@pytest.mark.parametrize(
'test_vector',
[test_vector_dict]
)
def test_IKM_to_lamport_SK(test_vector) -> None:
test_vector_lamport_0 = [bytes.fromhex(x) for x in test_vector['lamport_0']]
test_vector_lamport_1 = [bytes.fromhex(x) for x in test_vector['lamport_1']]
salt = test_vector['child_index'].to_bytes(4, 'big')
Expand All @@ -31,15 +44,35 @@ def test_IKM_to_lamport_SK():
assert test_vector_lamport_1 == lamport_1


def test_parent_SK_to_lamport_PK():
@pytest.mark.parametrize(
'test_vector',
[test_vector_dict]
)
def test_parent_SK_to_lamport_PK(test_vector) -> None:
parent_SK = test_vector['master_SK']
index = test_vector['child_index']
lamport_PK = bytes.fromhex(test_vector['compressed_lamport_PK'])
assert lamport_PK == _parent_SK_to_lamport_PK(parent_SK=parent_SK, index=index)


def test_HKDF_mod_r():
@pytest.mark.parametrize(
'test_vector',
[test_vector_dict]
)
def test_HKDF_mod_r(test_vector) -> None:
test_0 = (bytes.fromhex(test_vector['seed']), test_vector['master_SK'])
test_1 = (bytes.fromhex(test_vector['compressed_lamport_PK']), test_vector['child_SK'])
for test in (test_0, test_1):
assert _HKDF_mod_r(IKM=test[0]) == test[1]


@pytest.mark.parametrize(
'test_vector',
[test_vector_dict]
)
def test_mnemonic_and_path_to_key(test_vector) -> None:
mnemonic = test_vector['mnemonic']
password = test_vector['password']
path = test_vector['path']
key = test_vector['child_SK']
assert mnemonic_and_path_to_key(mnemonic=mnemonic, path=path, password=password) == key
52 changes: 41 additions & 11 deletions tests/test_key_handling/test_key_derivation/test_tree.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import os
import json
from py_ecc.bls import G2ProofOfPossession as bls
import pytest


from eth2deposit.key_handling.key_derivation.tree import (
_HKDF_mod_r,
derive_child_SK,
derive_master_SK,
)
Expand All @@ -14,16 +17,43 @@
test_vectors = json.load(f)['kdf_tests']


def test_derive_master_SK():
for test in test_vectors:
seed = bytes.fromhex(test['seed'])
master_SK = test['master_SK']
assert derive_master_SK(seed=seed) == master_SK
@pytest.mark.parametrize(
'test',
test_vectors
)
def test_hkdf_mod_r(test) -> None:
seed = bytes.fromhex(test['seed'])
assert bls.KeyGen(seed) == _HKDF_mod_r(IKM=seed)


@pytest.mark.parametrize(
'seed',
[b'\x00' * 32]
)
@pytest.mark.parametrize(
'key_info',
[b'\x00' * 32, b'\x01\x23\x45\x67\x89\xAB\xBC\xDE\xFF', b'\xFF' * 16]
)
def test_hkdf_mod_r_key_info(seed: bytes, key_info: bytes) -> None:
assert bls.KeyGen(seed, key_info) == _HKDF_mod_r(IKM=seed, key_info=key_info)


def test_derive_child_SK():
for test in test_vectors:
parent_SK = test['master_SK']
index = test['child_index']
child_SK = test['child_SK']
assert derive_child_SK(parent_SK=parent_SK, index=index) == child_SK
@pytest.mark.parametrize(
'test',
test_vectors
)
def test_derive_master_SK(test) -> None:
seed = bytes.fromhex(test['seed'])
master_SK = test['master_SK']
assert derive_master_SK(seed=seed) == master_SK


@pytest.mark.parametrize(
'test',
test_vectors
)
def test_derive_child_SK(test) -> None:
parent_SK = test['master_SK']
index = test['child_index']
child_SK = test['child_SK']
assert derive_child_SK(parent_SK=parent_SK, index=index) == child_SK
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
"kdf_tests": [
{
"seed": "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
"master_SK": 12513733877922233913083619867448865075222526338446857121953625441395088009793,
"master_SK": 5399117110774477986698372024995405256382522670366369834617409486544348441851,
"child_index": 0,
"child_SK": 7419543105316279183937430842449358701327973165530407166294956473095303972104
"child_SK": 11812940737387919040225825939013910852517748782307378293770044673328955938106
},
{
"seed": "3141592653589793238462643383279502884197169399375105820974944592",
"master_SK": 46029459550803682895343812821003080589696405386150182061394330539196052371668,
"master_SK": 36167147331491996618072159372207345412841461318189449162487002442599770291484,
"child_index": 3141592653,
"child_SK": 43469287647733616183478983885105537266268532274998688773496918571876759327260
"child_SK": 41787458189896526028601807066547832426569899195138584349427756863968330588237
},
{
"seed": "0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00",
"master_SK": 45379166311535261329029945990467475187325618028073620882733843918126031931161,
"master_SK": 13904094584487173309420026178174172335998687531503061311232927109397516192843,
"child_index": 4294967295,
"child_SK": 46475244006136701976831062271444482037125148379128114617927607151318277762946
"child_SK": 12482522899285304316694838079579801944734479969002030150864436005368716366140
},
{
"seed": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
"master_SK": 31740500954810567003972734830331791822878290325762596213711963944729383643688,
"master_SK": 44010626067374404458092393860968061149521094673473131545188652121635313364506,
"child_index": 42,
"child_SK": 51041472511529980987749393477251359993058329222191894694692317000136653813011
"child_SK": 4011524214304750350566588165922015929937602165683407445189263506512578573606
}
]
}

Large diffs are not rendered by default.

Loading

0 comments on commit 4ff0754

Please sign in to comment.