Merge pull request #41 from ethereum/carl_eip2333_blsv2

Update tests for new EIP2333 (bls v2 compliant HKDF_mod_r)
This commit is contained in:
Carl Beekhuizen 2020-06-29 17:01:14 +02:00 committed by GitHub
commit 7ffe1edacd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 28 deletions

View File

@ -37,8 +37,14 @@ def _parent_SK_to_lamport_PK(*, parent_SK: int, index: int) -> bytes:
return compressed_PK return compressed_PK
def _HKDF_mod_r(*, IKM: bytes) -> int: def _HKDF_mod_r(*, IKM: bytes, key_info: bytes=b'') -> int:
okm = HKDF(salt=b'BLS-SIG-KEYGEN-SALT-', IKM=IKM, L=48) 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 return int.from_bytes(okm, byteorder='big') % bls_curve_order
@ -49,5 +55,5 @@ def derive_child_SK(*, parent_SK: int, index: int) -> int:
def derive_master_SK(seed: bytes) -> int: def derive_master_SK(seed: bytes) -> int:
assert(len(seed) >= 16) assert(len(seed) >= 32)
return _HKDF_mod_r(IKM=seed) return _HKDF_mod_r(IKM=seed)

View File

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

View File

@ -8,18 +8,22 @@ from eth2deposit.key_handling.key_derivation.tree import (
_HKDF_mod_r, _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_vector_filefolder = os.path.join(os.getcwd(), 'tests', 'test_key_handling', 'test_key_derivation',
'test_vectors', 'tree_kdf_intermediate.json') 'test_vectors', 'tree_kdf_intermediate.json')
with open(test_vector_filefolder, 'r') as f: with open(test_vector_filefolder, 'r') as f:
test_vector = json.load(f) test_vector = json.load(f)
def test_flip_bits_256(): def test_flip_bits_256() -> None:
test_vector_int = int(test_vector['seed'][:64], 16) # 64 comes from string chars containing .5 bytes 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 assert test_vector_int & _flip_bits_256(test_vector_int) == 0
def test_IKM_to_lamport_SK(): def test_IKM_to_lamport_SK() -> None:
test_vector_lamport_0 = [bytes.fromhex(x) for x in test_vector['lamport_0']] 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']] test_vector_lamport_1 = [bytes.fromhex(x) for x in test_vector['lamport_1']]
salt = test_vector['child_index'].to_bytes(4, 'big') salt = test_vector['child_index'].to_bytes(4, 'big')
@ -31,15 +35,23 @@ def test_IKM_to_lamport_SK():
assert test_vector_lamport_1 == lamport_1 assert test_vector_lamport_1 == lamport_1
def test_parent_SK_to_lamport_PK(): def test_parent_SK_to_lamport_PK() -> None:
parent_SK = test_vector['master_SK'] parent_SK = test_vector['master_SK']
index = test_vector['child_index'] index = test_vector['child_index']
lamport_PK = bytes.fromhex(test_vector['compressed_lamport_PK']) lamport_PK = bytes.fromhex(test_vector['compressed_lamport_PK'])
assert lamport_PK == _parent_SK_to_lamport_PK(parent_SK=parent_SK, index=index) assert lamport_PK == _parent_SK_to_lamport_PK(parent_SK=parent_SK, index=index)
def test_HKDF_mod_r(): def test_HKDF_mod_r() -> None:
test_0 = (bytes.fromhex(test_vector['seed']), test_vector['master_SK']) test_0 = (bytes.fromhex(test_vector['seed']), test_vector['master_SK'])
test_1 = (bytes.fromhex(test_vector['compressed_lamport_PK']), test_vector['child_SK']) test_1 = (bytes.fromhex(test_vector['compressed_lamport_PK']), test_vector['child_SK'])
for test in (test_0, test_1): for test in (test_0, test_1):
assert _HKDF_mod_r(IKM=test[0]) == test[1] assert _HKDF_mod_r(IKM=test[0]) == test[1]
def test_mnemonic_and_path_to_key() -> 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

View File

@ -1,8 +1,11 @@
import os import os
import json import json
from py_ecc.bls import G2ProofOfPossession as bls
import pytest
from eth2deposit.key_handling.key_derivation.tree import ( from eth2deposit.key_handling.key_derivation.tree import (
_HKDF_mod_r,
derive_child_SK, derive_child_SK,
derive_master_SK, derive_master_SK,
) )
@ -14,14 +17,46 @@ with open(test_vector_filefolder, 'r') as f:
test_vectors = json.load(f)['kdf_tests'] test_vectors = json.load(f)['kdf_tests']
def test_derive_master_SK(): @pytest.mark.parametrize(
for test in test_vectors: 'test',
seed = bytes.fromhex(test['seed']) test_vectors
master_SK = test['master_SK'] )
assert derive_master_SK(seed=seed) == master_SK def test_hkdf_mod_r(test) -> None:
seed = bytes.fromhex(test['seed'])
assert bls.KeyGen(seed) == _HKDF_mod_r(IKM=seed)
def test_derive_child_SK(): @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)
@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
for test in test_vectors: for test in test_vectors:
parent_SK = test['master_SK'] parent_SK = test['master_SK']
index = test['child_index'] index = test['child_index']

View File

@ -2,27 +2,27 @@
"kdf_tests": [ "kdf_tests": [
{ {
"seed": "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", "seed": "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
"master_SK": 12513733877922233913083619867448865075222526338446857121953625441395088009793, "master_SK": 5399117110774477986698372024995405256382522670366369834617409486544348441851,
"child_index": 0, "child_index": 0,
"child_SK": 7419543105316279183937430842449358701327973165530407166294956473095303972104 "child_SK": 11812940737387919040225825939013910852517748782307378293770044673328955938106
}, },
{ {
"seed": "3141592653589793238462643383279502884197169399375105820974944592", "seed": "3141592653589793238462643383279502884197169399375105820974944592",
"master_SK": 46029459550803682895343812821003080589696405386150182061394330539196052371668, "master_SK": 36167147331491996618072159372207345412841461318189449162487002442599770291484,
"child_index": 3141592653, "child_index": 3141592653,
"child_SK": 43469287647733616183478983885105537266268532274998688773496918571876759327260 "child_SK": 41787458189896526028601807066547832426569899195138584349427756863968330588237
}, },
{ {
"seed": "0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00", "seed": "0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00",
"master_SK": 45379166311535261329029945990467475187325618028073620882733843918126031931161, "master_SK": 13904094584487173309420026178174172335998687531503061311232927109397516192843,
"child_index": 4294967295, "child_index": 4294967295,
"child_SK": 46475244006136701976831062271444482037125148379128114617927607151318277762946 "child_SK": 12482522899285304316694838079579801944734479969002030150864436005368716366140
}, },
{ {
"seed": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "seed": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
"master_SK": 31740500954810567003972734830331791822878290325762596213711963944729383643688, "master_SK": 44010626067374404458092393860968061149521094673473131545188652121635313364506,
"child_index": 42, "child_index": 42,
"child_SK": 51041472511529980987749393477251359993058329222191894694692317000136653813011 "child_SK": 4011524214304750350566588165922015929937602165683407445189263506512578573606
} }
] ]
} }

File diff suppressed because one or more lines are too long