Skip to content

Commit

Permalink
Allow 4-character abbreviations of mnemonic words when using `existin…
Browse files Browse the repository at this point in the history
…g-mnemonic`
  • Loading branch information
yorickdowne committed Mar 23, 2021
1 parent 7bac110 commit 23c61fb
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 15 deletions.
3 changes: 2 additions & 1 deletion eth2deposit/cli/existing_mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@


def validate_mnemonic(cts: click.Context, param: Any, mnemonic: str) -> str:
if verify_mnemonic(mnemonic, WORD_LISTS_PATH):
mnemonic = verify_mnemonic(mnemonic, WORD_LISTS_PATH)
if mnemonic is not None:
return mnemonic
else:
raise ValidationError('That is not a valid mnemonic, please check for typos.')
Expand Down
26 changes: 16 additions & 10 deletions eth2deposit/key_handling/key_derivation/mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ def determine_mnemonic_language(mnemonic: str, words_path: str) -> Sequence[str]
There are collisions between word-lists, so multiple candidate languages are returned.
"""
languages = get_languages(words_path)
word_language_map = {word: lang for lang in languages for word in _get_word_list(lang, words_path)}
word_language_map = {normalize('NFKC', word)[:4]: lang for lang in languages
for word in _get_word_list(lang, words_path)}
try:
mnemonic_list = mnemonic.split(' ')
mnemonic_list = [normalize('NFKC', word)[:4] for word in mnemonic.lower().split(' ')]
word_languages = [word_language_map[word] for word in mnemonic_list]
return list(set(word_languages))
except KeyError:
Expand All @@ -110,30 +111,35 @@ def _get_checksum(entropy: bytes) -> int:
return int.from_bytes(SHA256(entropy), 'big') >> (256 - checksum_length)


def verify_mnemonic(mnemonic: str, words_path: str) -> bool:
def verify_mnemonic(mnemonic: str, words_path: str) -> str:
"""
Given a mnemonic, verify it against its own checksum."
Given a mnemonic, verify it against its own checksum and return
a reconstructed full version - useful in case it was abbreviated.
"""
try:
languages = determine_mnemonic_language(mnemonic, words_path)
except ValueError:
return False
return None
for language in languages:
try:
word_list = _get_word_list(language, words_path)
mnemonic_list = mnemonic.split(' ')
word_list = [normalize('NFKC', word)[:4] for word in _get_word_list(language, words_path)]
mnemonic_list = [normalize('NFKC', word)[:4] for word in mnemonic.lower().split(' ')]
if len(mnemonic_list) not in range(12, 25, 3):
return False
return None
word_indices = [_word_to_index(word_list, word) for word in mnemonic_list]
mnemonic_int = _uint11_array_to_uint(word_indices)
checksum_length = len(mnemonic_list) // 3
checksum = mnemonic_int & 2**checksum_length - 1
entropy = (mnemonic_int - checksum) >> checksum_length
entropy_bits = entropy.to_bytes(checksum_length * 4, 'big')
return _get_checksum(entropy_bits) == checksum
full_word_list = _get_word_list(language, words_path)
if _get_checksum(entropy_bits) == checksum:
return ' '.join([_index_to_word(full_word_list, index) for index in word_indices])
else:
return None
except ValueError:
pass
return False
return None


def get_mnemonic(*, language: str, words_path: str, entropy: Optional[bytes]=None) -> str:
Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions tests/test_key_handling/test_key_derivation/test_mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ def test_bip39(language: str, test: Sequence[str]) -> None:


@pytest.mark.parametrize(
'test_mnemonic,is_valid',
[(test_mnemonic[1], True)
'test_mnemonic',
[(test_mnemonic[1])
for _, language_test_vectors in test_vectors.items()
for test_mnemonic in language_test_vectors]
)
def test_verify_mnemonic(test_mnemonic: str, is_valid: bool) -> None:
assert verify_mnemonic(test_mnemonic, WORD_LISTS_PATH) == is_valid
def test_verify_mnemonic(test_mnemonic: str) -> None:
assert verify_mnemonic(test_mnemonic, WORD_LISTS_PATH) is not None


@pytest.mark.parametrize(
Expand Down

0 comments on commit 23c61fb

Please sign in to comment.