From 603eb44ee2fced2145f7cec9fe823ed41a7e8a46 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Tue, 13 Aug 2024 18:48:24 -0400 Subject: [PATCH 1/3] limit mnemonic <= 24w; more tests against embit.bip39 --- src/krux/bip39.py | 2 +- tests/test_bip39.py | 69 +++++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/krux/bip39.py b/src/krux/bip39.py index c52a21b1..4c797fe8 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -10,7 +10,7 @@ def mnemonic_to_bytes(mnemonic: str, ignore_checksum: bool = False, wordlist=WORDLIST): """Verifies the mnemonic checksum and returns it in bytes""" words = mnemonic.strip().split() - if len(words) % 3 != 0 or len(words) < 12: + if len(words) % 3 != 0 or not 12 <= len(words) <= 24: raise ValueError("Invalid recovery phrase") accumulator = 0 diff --git a/tests/test_bip39.py b/tests/test_bip39.py index 527c58e0..81c7c807 100644 --- a/tests/test_bip39.py +++ b/tests/test_bip39.py @@ -2,27 +2,25 @@ from embit import bip39 from embit.wordlists.bip39 import WORDLIST import secrets +import pytest def test_one_word_mnemonics(): - for word in WORDLIST: - mnemonic = (word + " ") * 12 - assert kruxbip39.mnemonic_is_valid(mnemonic) == bip39.mnemonic_is_valid( - mnemonic - ) - - for word in WORDLIST: - mnemonic = (word + " ") * 24 - assert kruxbip39.mnemonic_is_valid(mnemonic) == bip39.mnemonic_is_valid( - mnemonic - ) + for numwords in (12, 15, 18, 21, 24): + for word in WORDLIST: + mnemonic = (word + " ") * numwords + assert kruxbip39.mnemonic_is_valid(mnemonic) == bip39.mnemonic_is_valid( + mnemonic + ) def test_edge_cases(): - cases = [8, 16] # 12w and 24w + cases = [16, 20, 24, 28, 32] # 12w, 15w, 18w, 21w and 24w for case in cases: - ALL_ZERO_BYTES = int(0).to_bytes(16, "big") - ALL_ONE_BYTES = int.from_bytes(bytearray([255] * case)).to_bytes(16, "big") + ALL_ZERO_BYTES = int(0).to_bytes(case, "big") + ALL_ONE_BYTES = int.from_bytes(bytearray([255] * case), "big").to_bytes( + case, "big" + ) assert ( kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(ALL_ZERO_BYTES)) @@ -33,24 +31,45 @@ def test_edge_cases(): == ALL_ONE_BYTES ) - int_val = max_val = int.from_bytes(ALL_ONE_BYTES) + int_val = max_val = int.from_bytes(ALL_ONE_BYTES, "big") while int_val > 0: int_val = int_val // 2 - b = int_val.to_bytes(16, "big") + b = int_val.to_bytes(case, "big") assert kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(b)) == b - b = (max_val - int_val).to_bytes(16, "big") + b = (max_val - int_val).to_bytes(case, "big") assert kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(b)) == b def test_random_cases(): for _ in range(20000): - token12w = secrets.token_bytes(16) - token24w = secrets.token_bytes(32) + for size in (16, 20, 24, 28, 32): + token_bytes = secrets.token_bytes(size) + assert ( + kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(token_bytes)) + == token_bytes + ) - assert ( - kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(token12w)) == token12w - ) - assert ( - kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(token24w)) == token24w - ) + +def test_invalid_words(): + cases = [ + "not all twelve of these words are in the english bip39 wordslist", + "not all fifteen of these words are in the english bip39 wordslist thirteen fourteen fifteen", + "not all eighteen of these words are in the english bip39 wordslist thirteen fourteen fifteen sixteen seventeen eighteen", + "not all twenty-one of these words are in the english bip39 wordslist thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty twenty-one", + "not all twenty-four of these words are in the english bip39 wordslist thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty twenty-one twenty-two twenty-three twenty-four", + ] + for case in cases: + with pytest.raises(ValueError, match=" is not in the dictionary"): + kruxbip39.mnemonic_to_bytes(case) + + +def test_invalid_mnemonic_length(): + cases = [ + "nine is divisible by three but is not valid", + "thirteen is between twelve and twenty-four but it is not divisible by three", + "twenty-seven is divisible by three but it is not a mnemonic with valid length because bip39 support mnemonics of length twelve fifteen eighteen twenty-one and twenty-four only", + ] + for case in cases: + with pytest.raises(ValueError, match="Invalid recovery phrase"): + kruxbip39.mnemonic_to_bytes(case) From 9e5c5b4f5a2b60de01681aab6b4f6d270d27fb19 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Wed, 14 Aug 2024 04:58:44 -0400 Subject: [PATCH 2/3] simpler test_edge_cases; added test to cover custom wordlist --- tests/test_bip39.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/test_bip39.py b/tests/test_bip39.py index 81c7c807..400565c4 100644 --- a/tests/test_bip39.py +++ b/tests/test_bip39.py @@ -17,10 +17,8 @@ def test_one_word_mnemonics(): def test_edge_cases(): cases = [16, 20, 24, 28, 32] # 12w, 15w, 18w, 21w and 24w for case in cases: - ALL_ZERO_BYTES = int(0).to_bytes(case, "big") - ALL_ONE_BYTES = int.from_bytes(bytearray([255] * case), "big").to_bytes( - case, "big" - ) + ALL_ZERO_BYTES = b'\x00' * case + ALL_ONE_BYTES = b'\xff' * case assert ( kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(ALL_ZERO_BYTES)) @@ -51,6 +49,19 @@ def test_random_cases(): ) +def test_random_cases_custom_wordlist(): + wordlist = tuple(kruxbip39.WORDLIST) + for _ in range(200): + for size in (16, 20, 24, 28, 32): + token_bytes = secrets.token_bytes(size) + assert ( + kruxbip39.mnemonic_to_bytes( + bip39.mnemonic_from_bytes(token_bytes), wordlist=wordlist + ) + == token_bytes + ) + + def test_invalid_words(): cases = [ "not all twelve of these words are in the english bip39 wordslist", From 638965b377188dae334fccab7dc360ac0a20e7c2 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Wed, 14 Aug 2024 05:09:58 -0400 Subject: [PATCH 3/3] black --- tests/test_bip39.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_bip39.py b/tests/test_bip39.py index 400565c4..d6fd6780 100644 --- a/tests/test_bip39.py +++ b/tests/test_bip39.py @@ -17,8 +17,8 @@ def test_one_word_mnemonics(): def test_edge_cases(): cases = [16, 20, 24, 28, 32] # 12w, 15w, 18w, 21w and 24w for case in cases: - ALL_ZERO_BYTES = b'\x00' * case - ALL_ONE_BYTES = b'\xff' * case + ALL_ZERO_BYTES = b"\x00" * case + ALL_ONE_BYTES = b"\xff" * case assert ( kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(ALL_ZERO_BYTES))