From 5e5b54fe2aad59ba4c85fb12bccbc062d3d583ef Mon Sep 17 00:00:00 2001 From: tadeubas Date: Wed, 31 Jul 2024 20:51:04 -0300 Subject: [PATCH 01/33] Check if mnemonic is a double mnemonic (12+12+24) --- src/krux/pages/__init__.py | 12 +++++++++--- src/krux/pages/home_pages/mnemonic_backup.py | 13 ++++++++----- src/krux/pages/login.py | 2 +- src/krux/pages/utils.py | 4 ++-- src/krux/wallet.py | 17 +++++++++++++++++ tests/pages/home_pages/test_mnemonic_backup.py | 2 +- 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index e87f4b4e9..fa2fe87d3 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -45,8 +45,9 @@ STATUS_BAR_HEIGHT, ) from ..qr import to_qr_codes -from ..krux_settings import t, Settings, DefaultWallet +from ..krux_settings import t, Settings from ..sd_card import SDHandler +from ..wallet import is_double_mnemonic MENU_CONTINUE = 0 MENU_EXIT = 1 @@ -341,13 +342,18 @@ def display_qr_codes(self, data, qr_format, title=""): done = True # interval done in input.py using timers - def display_mnemonic(self, mnemonic, suffix=""): + def display_mnemonic(self, mnemonic: str, suffix="", display_mnemonic: str = None): """Displays the 12 or 24-word list of words to the user""" - words = mnemonic.split(" ") + if display_mnemonic is None: + display_mnemonic = mnemonic + words = display_mnemonic.split(" ") word_list = [ str(i + 1) + "." + (" " if i + 1 < 10 else " ") + word for i, word in enumerate(words) ] + + if is_double_mnemonic(mnemonic): + suffix += "*" header = "BIP39" + " " + suffix self.ctx.display.clear() self.ctx.display.draw_hcentered_text(header) diff --git a/src/krux/pages/home_pages/mnemonic_backup.py b/src/krux/pages/home_pages/mnemonic_backup.py index 6d9db11b5..26b892a7e 100644 --- a/src/krux/pages/home_pages/mnemonic_backup.py +++ b/src/krux/pages/home_pages/mnemonic_backup.py @@ -93,9 +93,9 @@ def encrypt_qr_code(self): encrypt_qr_code = EncryptMnemonic(self.ctx) return encrypt_qr_code.encrypted_qr_code() - def show_mnemonic(self, mnemonic, suffix=""): + def show_mnemonic(self, mnemonic, suffix="", display_mnemonic=None): """Displays only the mnemonic words or indexes""" - self.display_mnemonic(mnemonic, suffix) + self.display_mnemonic(mnemonic, suffix, display_mnemonic) self.ctx.input.wait_for_button() # Avoid printing text on a cnc @@ -121,28 +121,31 @@ def display_mnemonic_numbers(self): ( t("Decimal"), lambda: self.show_mnemonic( + self.ctx.wallet.key.mnemonic, + Utils.BASE_DEC_SUFFIX, Utils.get_mnemonic_numbers( self.ctx.wallet.key.mnemonic, Utils.BASE_DEC ), - Utils.BASE_DEC_SUFFIX, ), ), ( t("Hexadecimal"), lambda: self.show_mnemonic( + self.ctx.wallet.key.mnemonic, + Utils.BASE_HEX_SUFFIX, Utils.get_mnemonic_numbers( self.ctx.wallet.key.mnemonic, Utils.BASE_HEX ), - Utils.BASE_HEX_SUFFIX, ), ), ( t("Octal"), lambda: self.show_mnemonic( + self.ctx.wallet.key.mnemonic, + Utils.BASE_OCT_SUFFIX, Utils.get_mnemonic_numbers( self.ctx.wallet.key.mnemonic, Utils.BASE_OCT ), - Utils.BASE_OCT_SUFFIX, ), ), ], diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index eefa910b6..7384fc585 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -199,7 +199,7 @@ def _load_key_from_words(self, words, charset=LETTERS): DIGITS_OCT: Utils.BASE_OCT_SUFFIX, } numbers_str = Utils.get_mnemonic_numbers(mnemonic, charset_type[charset]) - self.display_mnemonic(numbers_str, suffix_dict[charset]) + self.display_mnemonic(mnemonic, suffix_dict[charset], numbers_str) if not self.prompt(t("Continue?"), BOTTOM_PROMPT_LINE): return MENU_CONTINUE self.ctx.display.clear() diff --git a/src/krux/pages/utils.py b/src/krux/pages/utils.py index 3e5de2d92..fefdc2261 100644 --- a/src/krux/pages/utils.py +++ b/src/krux/pages/utils.py @@ -73,10 +73,10 @@ def load_file(self, file_ext="", prompt=True, only_get_filename=False): return "", None @staticmethod - def get_mnemonic_numbers(words, base=BASE_DEC): + def get_mnemonic_numbers(mnemonic: str, base=BASE_DEC): """Returns the mnemonic as indexes in decimal, hexadecimal, or octal""" word_numbers = [] - for word in words.split(" "): + for word in mnemonic.split(" "): word_numbers.append(WORDLIST.index(word) + 1) if base == Utils.BASE_HEX: diff --git a/src/krux/wallet.py b/src/krux/wallet.py index 367c7d20b..999d40057 100644 --- a/src/krux/wallet.py +++ b/src/krux/wallet.py @@ -455,3 +455,20 @@ def derivation_to_script_wrapper(derivation): format_str = "tr({})" return format_str + + +def is_double_mnemonic(mnemonic: str): + """Check if the mnemonic is a double mnemonic (12+12+24)""" + + words = mnemonic.split(" ") + if len(words) > 12: + from embit.bip39 import mnemonic_is_valid + + if ( + mnemonic_is_valid(" ".join(words[:12])) + and mnemonic_is_valid(" ".join(words[12:])) + and mnemonic_is_valid(mnemonic) + ): + return True + + return False diff --git a/tests/pages/home_pages/test_mnemonic_backup.py b/tests/pages/home_pages/test_mnemonic_backup.py index b00509179..eee7af4d0 100644 --- a/tests/pages/home_pages/test_mnemonic_backup.py +++ b/tests/pages/home_pages/test_mnemonic_backup.py @@ -89,7 +89,7 @@ def test_mnemonic_words(mocker, m5stickv, tdata): mnemonics.mnemonic() mnemonics.display_mnemonic.assert_called_with( - ctx.wallet.key.mnemonic, "Mnemonic" + ctx.wallet.key.mnemonic, "Mnemonic", None ) assert ctx.input.wait_for_button.call_count == len(case[2]) From 1ff7d1d40389e03215f07e88f61f6855b028252c Mon Sep 17 00:00:00 2001 From: tadeubas Date: Thu, 1 Aug 2024 22:37:47 -0300 Subject: [PATCH 02/33] display_mnemonic fingerprint paramenter --- src/krux/pages/__init__.py | 8 ++++++-- src/krux/pages/home_pages/bip85.py | 3 ++- tests/pages/home_pages/test_bip85.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index fa2fe87d3..08269fd6a 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -342,7 +342,9 @@ def display_qr_codes(self, data, qr_format, title=""): done = True # interval done in input.py using timers - def display_mnemonic(self, mnemonic: str, suffix="", display_mnemonic: str = None): + def display_mnemonic( + self, mnemonic: str, suffix="", display_mnemonic: str = None, fingerprint="" + ): """Displays the 12 or 24-word list of words to the user""" if display_mnemonic is None: display_mnemonic = mnemonic @@ -354,7 +356,9 @@ def display_mnemonic(self, mnemonic: str, suffix="", display_mnemonic: str = Non if is_double_mnemonic(mnemonic): suffix += "*" - header = "BIP39" + " " + suffix + if fingerprint: + fingerprint = "\n" + fingerprint + header = "BIP39" + " " + suffix + fingerprint self.ctx.display.clear() self.ctx.display.draw_hcentered_text(header) starting_y_offset = DEFAULT_PADDING // 4 + ( diff --git a/src/krux/pages/home_pages/bip85.py b/src/krux/pages/home_pages/bip85.py index 01aa44bcf..7a1aadde0 100644 --- a/src/krux/pages/home_pages/bip85.py +++ b/src/krux/pages/home_pages/bip85.py @@ -76,7 +76,8 @@ def export(self): if not Settings().security.hide_mnemonic: self.display_mnemonic( bip85_words, - suffix=t("Words") + "\n%s" % key.fingerprint_hex_str(True), + suffix=t("Words"), + fingerprint=key.fingerprint_hex_str(True), ) else: self.ctx.display.draw_centered_text(key.fingerprint_hex_str(True)) diff --git a/tests/pages/home_pages/test_bip85.py b/tests/pages/home_pages/test_bip85.py index d2809637c..b2ce2ec93 100644 --- a/tests/pages/home_pages/test_bip85.py +++ b/tests/pages/home_pages/test_bip85.py @@ -149,7 +149,7 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): if case[2]: bip85_ui.display_mnemonic.assert_called_with( - case[2], suffix="Words" + "\n⊚ %s" % case[3] + case[2], suffix="Words", fingerprint="⊚ %s" % case[3] ) else: bip85_ui.display_mnemonic.assert_not_called() From b55dba2f83240ea6435b13598f7957549902450b Mon Sep 17 00:00:00 2001 From: tadeubas Date: Thu, 8 Aug 2024 21:44:47 -0300 Subject: [PATCH 03/33] Create double mnemonic from camera --- src/krux/pages/__init__.py | 13 +++++++---- src/krux/pages/login.py | 47 ++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index d7965c880..e9e6551d6 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -922,14 +922,17 @@ def _draw_menu(self, selected_item_index): offset_y += delta_y -def choose_len_mnemonic(ctx): +def choose_len_mnemonic(ctx, double_mnemonic=False): """Reusable '12 or 24 words?" menu choice""" + items = [ + (t("12 words"), lambda: 12), + (t("24 words"), lambda: 24), + ] + if double_mnemonic: + items += [(t("Double mnemonic"), lambda: 48)] submenu = Menu( ctx, - [ - (t("12 words"), lambda: 12), - (t("24 words"), lambda: 24), - ], + items, back_status=lambda: None, ) _, num_words = submenu.run_loop() diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 248c5e5ba..96f1180d9 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -159,7 +159,7 @@ def new_key_from_dice(self, d_20=False): def new_key_from_snapshot(self): """Use camera's entropy to create a new mnemonic""" - len_mnemonic = choose_len_mnemonic(self.ctx) + len_mnemonic = choose_len_mnemonic(self.ctx, True) if not len_mnemonic: return MENU_CONTINUE @@ -182,9 +182,48 @@ def new_key_from_snapshot(self): t("SHA256 of snapshot:") + "\n\n%s" % entropy_hash ) self.ctx.input.wait_for_button() + + self.ctx.display.clear() + self.ctx.display.draw_centered_text(t("Processing..")) + num_bytes = 16 if len_mnemonic == 12 else 32 - words = bip39.mnemonic_from_bytes(entropy_bytes[:num_bytes]).split() - return self._load_key_from_words(words) + mnemonic_from_bytes = bip39.mnemonic_from_bytes( + entropy_bytes[:num_bytes] + ) + + # Double mnemonic check + if len_mnemonic == 48: + from ..wallet import is_double_mnemonic + + if not is_double_mnemonic(mnemonic_from_bytes): + from embit.bip39 import mnemonic_is_valid + from ..wdt import wdt + import time + + pre_t = time.ticks_ms() + tries = 0 + + # create two 12w mnemonic with the provided entropy + first_12 = bip39.mnemonic_from_bytes(entropy_bytes[:16]) + second_mnemonic_entropy = entropy_bytes[16:32] + double_mnemonic = False + while not double_mnemonic: + wdt.feed() + tries += 1 + # increment the second mnemonic entropy + second_mnemonic_entropy = ( + int.from_bytes(second_mnemonic_entropy, "big") + 1 + ).to_bytes(16, "big") + second_12 = bip39.mnemonic_from_bytes( + second_mnemonic_entropy + ) + mnemonic_from_bytes = first_12 + " " + second_12 + double_mnemonic = mnemonic_is_valid(mnemonic_from_bytes) + + post_t = time.ticks_ms() + print("Tries: %d" % tries, "/ %d" % (post_t - pre_t), "ms") + + return self._load_key_from_words(mnemonic_from_bytes.split()) return MENU_CONTINUE def _load_key_from_words(self, words, charset=LETTERS): @@ -296,7 +335,7 @@ def _encrypted_qr_code(self, data): self.flash_error(t("Key was not provided")) return MENU_CONTINUE self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) word_bytes = encrypted_qr.decrypt(key) if word_bytes is None: self.flash_error(t("Failed to decrypt")) From 2dafac9efce752b31cb7f5e25c8a8b73b4010257 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Thu, 8 Aug 2024 21:46:38 -0300 Subject: [PATCH 04/33] Using only 2 dots instead of 3 for t(Processing..) --- src/krux/pages/encryption_ui.py | 6 +++--- src/krux/pages/home_pages/home.py | 2 +- src/krux/pages/qr_view.py | 2 +- tests/pages/home_pages/test_home.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/krux/pages/encryption_ui.py b/src/krux/pages/encryption_ui.py index 3da7ed2a9..8a0f3ebbe 100644 --- a/src/krux/pages/encryption_ui.py +++ b/src/krux/pages/encryption_ui.py @@ -179,7 +179,7 @@ def store_mnemonic_on_memory(self, sd_card=False): return self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) words = self.ctx.wallet.key.mnemonic if mnemonic_storage.store_encrypted(key, mnemonic_id, words, sd_card, i_vector): self.ctx.display.clear() @@ -201,7 +201,7 @@ def encrypted_qr_code(self): key, mnemonic_id, i_vector = user_inputs self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) from ..encryption import EncryptedQRCode @@ -271,7 +271,7 @@ def _load_encrypted_mnemonic(self, mnemonic_id, sd_card=False): self.flash_error(t("Key was not provided")) return MENU_CONTINUE self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) mnemonic_storage = MnemonicStorage() try: words = mnemonic_storage.decrypt(key, mnemonic_id, sd_card).split() diff --git a/src/krux/pages/home_pages/home.py b/src/krux/pages/home_pages/home.py index 46e8b8656..36f0df350 100644 --- a/src/krux/pages/home_pages/home.py +++ b/src/krux/pages/home_pages/home.py @@ -350,7 +350,7 @@ def sign_psbt(self): return MENU_CONTINUE self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) outputs, fee_percent = signer.outputs() # Warn if fees greater than 10% of what is spent diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py index 2673cc02d..4b9bd81c0 100644 --- a/src/krux/pages/qr_view.py +++ b/src/krux/pages/qr_view.py @@ -373,7 +373,7 @@ def save_bmp_image(self, file_name, resolution): return self.ctx.display.clear() - self.ctx.display.draw_centered_text(t("Saving ...")) + self.ctx.display.draw_centered_text(t("Processing..")) bmp_img.save("/sd/" + file_name) self.flash_text(t("Saved to SD card") + ":\n%s" % file_name) diff --git a/tests/pages/home_pages/test_home.py b/tests/pages/home_pages/test_home.py index 9ca27b616..24716437d 100644 --- a/tests/pages/home_pages/test_home.py +++ b/tests/pages/home_pages/test_home.py @@ -1021,7 +1021,7 @@ def test_sign_high_fee(mocker, m5stickv, tdata): mocker.call( "Warning: Path mismatch\nWallet: m/84'/0'/0'\nPSBT: m/84'/1'/0'" ), - mocker.call("Processing ..."), + mocker.call("Processing.."), mocker.call("Warning: High fees!\n799.7% of the amount."), ] ) @@ -1069,7 +1069,7 @@ def test_sign_self(mocker, m5stickv, tdata): mocker.call( "Warning: Path mismatch\nWallet: m/84'/0'/0'\nPSBT: m/84'/1'/0'" ), - mocker.call("Processing ..."), + mocker.call("Processing.."), mocker.call("Warning: High fees!\n799.7% of the amount."), ] ) @@ -1118,7 +1118,7 @@ def test_sign_spent_and_self(mocker, m5stickv, tdata): mocker.call( "Warning: Path mismatch\nWallet: m/84'/0'/0'\nPSBT: m/84'/1'/0'" ), - mocker.call("Processing ..."), + mocker.call("Processing.."), mocker.call("Warning: High fees!\n235.9% of the amount."), ] ) From ea39873a03b2a6ba16a79247486c4784ed686a38 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Thu, 8 Aug 2024 21:48:10 -0300 Subject: [PATCH 05/33] translations --- i18n/translations/de-DE.json | 4 ++-- i18n/translations/es-MX.json | 4 ++-- i18n/translations/fr-FR.json | 4 ++-- i18n/translations/nl-NL.json | 4 ++-- i18n/translations/pl-PL.json | 4 ++-- i18n/translations/pt-BR.json | 4 ++-- i18n/translations/ru-RU.json | 4 ++-- i18n/translations/tr-TR.json | 4 ++-- i18n/translations/vi-VN.json | 4 ++-- src/krux/translations.py | 36 ++++++++++++++++++------------------ 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/i18n/translations/de-DE.json b/i18n/translations/de-DE.json index 0d8b292a1..3faa97cab 100644 --- a/i18n/translations/de-DE.json +++ b/i18n/translations/de-DE.json @@ -70,6 +70,7 @@ "Display": "Bildschirm", "Do not power off, it may take a while to complete.": "Schalten Sie das Gerät nicht aus, es kann eine Weile dauern.", "Done?": "Fertig?", + "Double mnemonic": "Doppelte Gedächtnisstütze", "Driver": "Driver", "Encrypted": "Verschlüsselt", "Encrypted QR Code": "Verschlüsselter QR-Code", @@ -201,7 +202,7 @@ "Printing": "Wird gedruckt", "Proceed anyway?": "Trotzdem fortfahren?", "Proceed?": "Weiter?", - "Processing ...": "Wird bearbeitet ...", + "Processing..": "Wird bearbeitet..", "QR Code": "QR-Code", "RX Pin": "RX Pin", "Receive": "Empfangen", @@ -227,7 +228,6 @@ "Save to SD card": "Auf SD-Karte speichern?", "Save to SD card?": "Auf SD-Karte speichern?", "Saved to SD card": "Auf SD-Karte gespeichert", - "Saving ...": "Wird gespeichert …", "Scale": "Skala", "Scan Address": "Adresse\nscannen", "Scan BIP39 Passphrase": "Scan BIP39 Passphrase", diff --git a/i18n/translations/es-MX.json b/i18n/translations/es-MX.json index cee8bed60..fee717913 100644 --- a/i18n/translations/es-MX.json +++ b/i18n/translations/es-MX.json @@ -70,6 +70,7 @@ "Display": "Pantalla", "Do not power off, it may take a while to complete.": "No apagues el dispositivo, puede tardar un tiempo en completarse.", "Done?": "¿Listo?", + "Double mnemonic": "Doble mnemónico", "Driver": "Operador", "Encrypted": "Cifrado", "Encrypted QR Code": "Código QR Cifrado", @@ -201,7 +202,7 @@ "Printing": "Imprimiendo", "Proceed anyway?": "¿Proceder de todas maneras?", "Proceed?": "¿Continuar?", - "Processing ...": "Procesando ...", + "Processing..": "Procesando..", "QR Code": "Código QR", "RX Pin": "RX Pin", "Receive": "Recepción", @@ -227,7 +228,6 @@ "Save to SD card": "Guardar en tarjeta SD", "Save to SD card?": "¿Guardar en la tarjeta SD?", "Saved to SD card": "Guardado en la tarjeta SD", - "Saving ...": "Guardando ...", "Scale": "Escala", "Scan Address": "Escanear Dirección", "Scan BIP39 Passphrase": "Escanear Passphrase BIP39", diff --git a/i18n/translations/fr-FR.json b/i18n/translations/fr-FR.json index 1e9d8da08..110164cfe 100644 --- a/i18n/translations/fr-FR.json +++ b/i18n/translations/fr-FR.json @@ -70,6 +70,7 @@ "Display": "Affichage", "Do not power off, it may take a while to complete.": "Ne pas éteindre, cela peut prendre un certain temps.", "Done?": "Terminé?", + "Double mnemonic": "Double mnémonique", "Driver": "Conducteur", "Encrypted": "Chiffré", "Encrypted QR Code": "Code QR crypté", @@ -201,7 +202,7 @@ "Printing": "Impression", "Proceed anyway?": "Procéder quand même ?", "Proceed?": "Procéder ?", - "Processing ...": "Traitement ...", + "Processing..": "Traitement..", "QR Code": "QR Code", "RX Pin": "RX Fiche", "Receive": "Recevoir", @@ -227,7 +228,6 @@ "Save to SD card": "Enregistrer sur la carte SD", "Save to SD card?": "Enregistrer sur la carte SD ?", "Saved to SD card": "Enregistré sur la carte SD", - "Saving ...": "Enregistrement en cours...", "Scale": "L'échelle", "Scan Address": "Scannez l'adresse", "Scan BIP39 Passphrase": "Scannez la phrase secrète BIP-39", diff --git a/i18n/translations/nl-NL.json b/i18n/translations/nl-NL.json index a4cd2426a..4de47aa29 100644 --- a/i18n/translations/nl-NL.json +++ b/i18n/translations/nl-NL.json @@ -70,6 +70,7 @@ "Display": "Weergave", "Do not power off, it may take a while to complete.": "Schakel het apparaat niet uit, het kan even duren voordat het klaar is.", "Done?": "Klaar?", + "Double mnemonic": "Dubbel geheugensteuntje", "Driver": "Driver", "Encrypted": "Versleuteld", "Encrypted QR Code": "Versleutelde QR code", @@ -201,7 +202,7 @@ "Printing": "Afdrukken", "Proceed anyway?": "Toch doorgaan?", "Proceed?": "Doorgaan?", - "Processing ...": "Verwerken...", + "Processing..": "Verwerken..", "QR Code": "QR code", "RX Pin": "RX pin", "Receive": "Ontvangen", @@ -227,7 +228,6 @@ "Save to SD card": "Opslaan op SD kaart", "Save to SD card?": "Opslaan op SD kaart?", "Saved to SD card": "Opgeslagen op SD kaart", - "Saving ...": "Opslaan bezig...", "Scale": "Schaal", "Scan Address": "Adres scannen", "Scan BIP39 Passphrase": "BIP-39 Wachtwoord Scannen", diff --git a/i18n/translations/pl-PL.json b/i18n/translations/pl-PL.json index 914897d12..0e58d04ee 100644 --- a/i18n/translations/pl-PL.json +++ b/i18n/translations/pl-PL.json @@ -70,6 +70,7 @@ "Display": "Wyświetlacz", "Do not power off, it may take a while to complete.": "Nie wyłączaj zasilania, może to chwilę potrwać.", "Done?": "Zrobione?", + "Double mnemonic": "Podwójna mnemotechnika", "Driver": "Kierowca", "Encrypted": "Zaszyfrowane", "Encrypted QR Code": "Zaszyfrowany kod QR", @@ -201,7 +202,7 @@ "Printing": "Drukowanie", "Proceed anyway?": "Kontynuować mimo to?", "Proceed?": "Przystępować?", - "Processing ...": "Przetwarzanie ...", + "Processing..": "Przetwarzanie..", "QR Code": "Kod QR", "RX Pin": "Pin Rx", "Receive": "Odbierać", @@ -227,7 +228,6 @@ "Save to SD card": "Zapisz na karcie SD", "Save to SD card?": "Zapisz na karcie SD?", "Saved to SD card": "Zapisano na karcie SD", - "Saving ...": "Zapisywanie...", "Scale": "Skala", "Scan Address": "Adres skanowania", "Scan BIP39 Passphrase": "Scan BIP39 Passphrase", diff --git a/i18n/translations/pt-BR.json b/i18n/translations/pt-BR.json index 71d26e104..4f54b96cc 100644 --- a/i18n/translations/pt-BR.json +++ b/i18n/translations/pt-BR.json @@ -70,6 +70,7 @@ "Display": "Display", "Do not power off, it may take a while to complete.": "Não desligue, pode demorar um pouco para concluir.", "Done?": "Feito?", + "Double mnemonic": "Duplo mnemônico", "Driver": "Driver", "Encrypted": "Criptografado", "Encrypted QR Code": "Código QR Criptografado", @@ -201,7 +202,7 @@ "Printing": "Imprimindo", "Proceed anyway?": "Continuar mesmo assim?", "Proceed?": "Seguir?", - "Processing ...": "Processando ...", + "Processing..": "Processando..", "QR Code": "Código QR", "RX Pin": "Pino RX", "Receive": "Recebimento", @@ -227,7 +228,6 @@ "Save to SD card": "Salvar no cartão SD", "Save to SD card?": "Salvar no cartão SD?", "Saved to SD card": "Salvo no cartão SD", - "Saving ...": "Salvando...", "Scale": "Escala", "Scan Address": "Escanear Endereço", "Scan BIP39 Passphrase": "Escanear a senha BIP39", diff --git a/i18n/translations/ru-RU.json b/i18n/translations/ru-RU.json index 90e4b900d..37a8b26a8 100644 --- a/i18n/translations/ru-RU.json +++ b/i18n/translations/ru-RU.json @@ -70,6 +70,7 @@ "Display": "Дисплеи", "Do not power off, it may take a while to complete.": "Не выключайте питание, это может занять некоторое время.", "Done?": "Готово?", + "Double mnemonic": "Двойная мнемоника", "Driver": "Драйвер", "Encrypted": "Зашифровано", "Encrypted QR Code": "Зашифрованный QR Код", @@ -201,7 +202,7 @@ "Printing": "Идет печать", "Proceed anyway?": "Все равно продолжить?", "Proceed?": "Продолжить?", - "Processing ...": "Обработка ...", + "Processing..": "Обработка..", "QR Code": "QR Код", "RX Pin": "RX Пин", "Receive": "Получить", @@ -227,7 +228,6 @@ "Save to SD card": "Сохранить на SD-карту", "Save to SD card?": "Сохранить на SD карту?", "Saved to SD card": "Сохранено на SD карту", - "Saving ...": "Сохранение...", "Scale": "Шкала", "Scan Address": "Отсканировать Адрес", "Scan BIP39 Passphrase": "Отсканировать BIP39 фразу-пароль", diff --git a/i18n/translations/tr-TR.json b/i18n/translations/tr-TR.json index 0a7e6d8e7..409d1cec1 100644 --- a/i18n/translations/tr-TR.json +++ b/i18n/translations/tr-TR.json @@ -70,6 +70,7 @@ "Display": "Ekran", "Do not power off, it may take a while to complete.": "Kapatmayın, tamamlanması biraz zaman alabilir.", "Done?": "Tamamlandı mı?", + "Double mnemonic": "Çifte anımsatıcı", "Driver": "Sürücü", "Encrypted": "Şifrelenmiş", "Encrypted QR Code": "Şifrelenmiş QR Kodu", @@ -201,7 +202,7 @@ "Printing": "Yazdırılıyor", "Proceed anyway?": "Yine de devam edilsin mi?", "Proceed?": "Devam edilsin mi?", - "Processing ...": "İşleniyor ...", + "Processing..": "İşleniyor..", "QR Code": "QR Kodu", "RX Pin": "RX Pini", "Receive": "Al", @@ -227,7 +228,6 @@ "Save to SD card": "SD karta kaydet", "Save to SD card?": "SD karta kaydedilsin mi?", "Saved to SD card": "SD karta kaydedildi", - "Saving ...": "Kaydediliyor ...", "Scale": "Ölçek", "Scan Address": "Adresi Tara", "Scan BIP39 Passphrase": "BIP39 Parolasını Tara", diff --git a/i18n/translations/vi-VN.json b/i18n/translations/vi-VN.json index 4918eb6cc..9be15fc23 100644 --- a/i18n/translations/vi-VN.json +++ b/i18n/translations/vi-VN.json @@ -70,6 +70,7 @@ "Display": "Hiển thị", "Do not power off, it may take a while to complete.": "Không được tắt máy, có thể mất một lúc để hoàn thành.", "Done?": "Hoàn tất?", + "Double mnemonic": "Từ gợi nhớ kép", "Driver": "Driver", "Encrypted": "Đã mã hóa", "Encrypted QR Code": "Mã QR được mã hóa", @@ -201,7 +202,7 @@ "Printing": "Đang in", "Proceed anyway?": "Vẫn tiếp tục?", "Proceed?": "Thực hiện?", - "Processing ...": "Đang xử lý ...", + "Processing..": "Đang xử lý..", "QR Code": "Mã QR", "RX Pin": "RX Pin", "Receive": "Nhận được", @@ -227,7 +228,6 @@ "Save to SD card": "Lưu vào thẻ SD", "Save to SD card?": "Lưu vào thẻ SD?", "Saved to SD card": "Đã lưu vào thẻ SD", - "Saving ...": "Đang lưu...", "Scale": "Tỉ lệ", "Scan Address": "Quét địa chỉ", "Scan BIP39 Passphrase": "Quét cụm mật khẩu BIP39", diff --git a/src/krux/translations.py b/src/krux/translations.py index 2642fa0c1..83d5c4ddd 100644 --- a/src/krux/translations.py +++ b/src/krux/translations.py @@ -93,6 +93,7 @@ 3278654271: "Bildschirm", 3895447625: "Schalten Sie das Gerät nicht aus, es kann eine Weile dauern.", 3836852788: "Fertig?", + 690625786: "Doppelte Gedächtnisstütze", 382368239: "Driver", 3582575312: "Verschlüsselt", 1244124409: "Verschlüsselter QR-Code", @@ -224,7 +225,7 @@ 3388542885: "Wird gedruckt", 3593149291: "Trotzdem fortfahren?", 2580599003: "Weiter?", - 556126964: "Wird bearbeitet ...", + 3108881025: "Wird bearbeitet..", 1848310591: "QR-Code", 710709610: "RX Pin", 2697857197: "Empfangen", @@ -250,7 +251,6 @@ 2163347007: "Auf SD-Karte speichern?", 3531742595: "Auf SD-Karte speichern?", 2940025484: "Auf SD-Karte gespeichert", - 3531363515: "Wird gespeichert …", 763824768: "Skala", 4117455079: "Adresse\nscannen", 4038076821: "Scan BIP39 Passphrase", @@ -404,6 +404,7 @@ 3278654271: "Pantalla", 3895447625: "No apagues el dispositivo, puede tardar un tiempo en completarse.", 3836852788: "¿Listo?", + 690625786: "Doble mnemónico", 382368239: "Operador", 3582575312: "Cifrado", 1244124409: "Código QR Cifrado", @@ -535,7 +536,7 @@ 3388542885: "Imprimiendo", 3593149291: "¿Proceder de todas maneras?", 2580599003: "¿Continuar?", - 556126964: "Procesando ...", + 3108881025: "Procesando..", 1848310591: "Código QR", 710709610: "RX Pin", 2697857197: "Recepción", @@ -561,7 +562,6 @@ 2163347007: "Guardar en tarjeta SD", 3531742595: "¿Guardar en la tarjeta SD?", 2940025484: "Guardado en la tarjeta SD", - 3531363515: "Guardando ...", 763824768: "Escala", 4117455079: "Escanear Dirección", 4038076821: "Escanear Passphrase BIP39", @@ -715,6 +715,7 @@ 3278654271: "Affichage", 3895447625: "Ne pas éteindre, cela peut prendre un certain temps.", 3836852788: "Terminé?", + 690625786: "Double mnémonique", 382368239: "Conducteur", 3582575312: "Chiffré", 1244124409: "Code QR crypté", @@ -846,7 +847,7 @@ 3388542885: "Impression", 3593149291: "Procéder quand même\u2009?", 2580599003: "Procéder\u2009?", - 556126964: "Traitement ...", + 3108881025: "Traitement..", 1848310591: "QR Code", 710709610: "RX Fiche", 2697857197: "Recevoir", @@ -872,7 +873,6 @@ 2163347007: "Enregistrer sur la carte SD", 3531742595: "Enregistrer sur la carte SD\u2009?", 2940025484: "Enregistré sur la carte SD", - 3531363515: "Enregistrement en cours...", 763824768: "L'échelle", 4117455079: "Scannez l'adresse", 4038076821: "Scannez la phrase secrète BIP-39", @@ -1026,6 +1026,7 @@ 3278654271: "Weergave", 3895447625: "Schakel het apparaat niet uit, het kan even duren voordat het klaar is.", 3836852788: "Klaar?", + 690625786: "Dubbel geheugensteuntje", 382368239: "Driver", 3582575312: "Versleuteld", 1244124409: "Versleutelde QR code", @@ -1157,7 +1158,7 @@ 3388542885: "Afdrukken", 3593149291: "Toch doorgaan?", 2580599003: "Doorgaan?", - 556126964: "Verwerken...", + 3108881025: "Verwerken..", 1848310591: "QR code", 710709610: "RX pin", 2697857197: "Ontvangen", @@ -1183,7 +1184,6 @@ 2163347007: "Opslaan op SD kaart", 3531742595: "Opslaan op SD kaart?", 2940025484: "Opgeslagen op SD kaart", - 3531363515: "Opslaan bezig...", 763824768: "Schaal", 4117455079: "Adres scannen", 4038076821: "BIP-39 Wachtwoord Scannen", @@ -1337,6 +1337,7 @@ 3278654271: "Wyświetlacz", 3895447625: "Nie wyłączaj zasilania, może to chwilę potrwać.", 3836852788: "Zrobione?", + 690625786: "Podwójna mnemotechnika", 382368239: "Kierowca", 3582575312: "Zaszyfrowane", 1244124409: "Zaszyfrowany kod QR", @@ -1468,7 +1469,7 @@ 3388542885: "Drukowanie", 3593149291: "Kontynuować mimo to?", 2580599003: "Przystępować?", - 556126964: "Przetwarzanie ...", + 3108881025: "Przetwarzanie..", 1848310591: "Kod QR", 710709610: "Pin Rx", 2697857197: "Odbierać", @@ -1494,7 +1495,6 @@ 2163347007: "Zapisz na karcie SD", 3531742595: "Zapisz na karcie SD?", 2940025484: "Zapisano na karcie SD", - 3531363515: "Zapisywanie...", 763824768: "Skala", 4117455079: "Adres skanowania", 4038076821: "Scan BIP39 Passphrase", @@ -1648,6 +1648,7 @@ 3278654271: "Display", 3895447625: "Não desligue, pode demorar um pouco para concluir.", 3836852788: "Feito?", + 690625786: "Duplo mnemônico", 382368239: "Driver", 3582575312: "Criptografado", 1244124409: "Código QR Criptografado", @@ -1779,7 +1780,7 @@ 3388542885: "Imprimindo", 3593149291: "Continuar mesmo assim?", 2580599003: "Seguir?", - 556126964: "Processando ...", + 3108881025: "Processando..", 1848310591: "Código QR", 710709610: "Pino RX", 2697857197: "Recebimento", @@ -1805,7 +1806,6 @@ 2163347007: "Salvar no cartão SD", 3531742595: "Salvar no cartão SD?", 2940025484: "Salvo no cartão SD", - 3531363515: "Salvando...", 763824768: "Escala", 4117455079: "Escanear Endereço", 4038076821: "Escanear a senha BIP39", @@ -1959,6 +1959,7 @@ 3278654271: "Дисплеи", 3895447625: "Не выключайте питание, это может занять некоторое время.", 3836852788: "Готово?", + 690625786: "Двойная мнемоника", 382368239: "Драйвер", 3582575312: "Зашифровано", 1244124409: "Зашифрованный QR Код", @@ -2090,7 +2091,7 @@ 3388542885: "Идет печать", 3593149291: "Все равно продолжить?", 2580599003: "Продолжить?", - 556126964: "Обработка ...", + 3108881025: "Обработка..", 1848310591: "QR Код", 710709610: "RX Пин", 2697857197: "Получить", @@ -2116,7 +2117,6 @@ 2163347007: "Сохранить на SD-карту", 3531742595: "Сохранить на SD карту?", 2940025484: "Сохранено на SD карту", - 3531363515: "Сохранение...", 763824768: "Шкала", 4117455079: "Отсканировать Адрес", 4038076821: "Отсканировать BIP39 фразу-пароль", @@ -2270,6 +2270,7 @@ 3278654271: "Ekran", 3895447625: "Kapatmayın, tamamlanması biraz zaman alabilir.", 3836852788: "Tamamlandı mı?", + 690625786: "Çifte anımsatıcı", 382368239: "Sürücü", 3582575312: "Şifrelenmiş", 1244124409: "Şifrelenmiş QR Kodu", @@ -2401,7 +2402,7 @@ 3388542885: "Yazdırılıyor", 3593149291: "Yine de devam edilsin mi?", 2580599003: "Devam edilsin mi?", - 556126964: "İşleniyor ...", + 3108881025: "İşleniyor..", 1848310591: "QR Kodu", 710709610: "RX Pini", 2697857197: "Al", @@ -2427,7 +2428,6 @@ 2163347007: "SD karta kaydet", 3531742595: "SD karta kaydedilsin mi?", 2940025484: "SD karta kaydedildi", - 3531363515: "Kaydediliyor ...", 763824768: "Ölçek", 4117455079: "Adresi Tara", 4038076821: "BIP39 Parolasını Tara", @@ -2581,6 +2581,7 @@ 3278654271: "Hiển thị", 3895447625: "Không được tắt máy, có thể mất một lúc để hoàn thành.", 3836852788: "Hoàn tất?", + 690625786: "Từ gợi nhớ kép", 382368239: "Driver", 3582575312: "Đã mã hóa", 1244124409: "Mã QR được mã hóa", @@ -2712,7 +2713,7 @@ 3388542885: "Đang in", 3593149291: "Vẫn tiếp tục?", 2580599003: "Thực hiện?", - 556126964: "Đang xử lý ...", + 3108881025: "Đang xử lý..", 1848310591: "Mã QR", 710709610: "RX Pin", 2697857197: "Nhận được", @@ -2738,7 +2739,6 @@ 2163347007: "Lưu vào thẻ SD", 3531742595: "Lưu vào thẻ SD?", 2940025484: "Đã lưu vào thẻ SD", - 3531363515: "Đang lưu...", 763824768: "Tỉ lệ", 4117455079: "Quét địa chỉ", 4038076821: "Quét cụm mật khẩu BIP39", From 2949cb5cdef968aba0393ee6462e2f1091a70a9b Mon Sep 17 00:00:00 2001 From: tadeubas Date: Thu, 8 Aug 2024 21:52:39 -0300 Subject: [PATCH 06/33] optimize import is_double_mnemonic --- src/krux/pages/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index e9e6551d6..f77a5330f 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -47,7 +47,6 @@ from ..qr import to_qr_codes from ..krux_settings import t, Settings from ..sd_card import SDHandler -from ..wallet import is_double_mnemonic MENU_CONTINUE = 0 MENU_EXIT = 1 @@ -346,6 +345,8 @@ def display_mnemonic( self, mnemonic: str, suffix="", display_mnemonic: str = None, fingerprint="" ): """Displays the 12 or 24-word list of words to the user""" + from ..wallet import is_double_mnemonic + if display_mnemonic is None: display_mnemonic = mnemonic words = display_mnemonic.split(" ") From b5ca15c64fc2fcc64a5d0caec8a4458e68c2be52 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Thu, 8 Aug 2024 21:54:36 -0400 Subject: [PATCH 07/33] experimentally uses bip39_via_accumulator; +3x faster --- src/krux/pages/login.py | 10 +++++----- src/krux/wallet.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 96f1180d9..5aece361d 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -24,6 +24,7 @@ from embit.networks import NETWORKS from embit.wordlists.bip39 import WORDLIST from embit import bip39 +from krux import bip39 as kruxbip39 from ..display import DEFAULT_PADDING, FONT_HEIGHT, BOTTOM_PROMPT_LINE from ..krux_settings import Settings from ..qr import FORMAT_UR @@ -187,7 +188,7 @@ def new_key_from_snapshot(self): self.ctx.display.draw_centered_text(t("Processing..")) num_bytes = 16 if len_mnemonic == 12 else 32 - mnemonic_from_bytes = bip39.mnemonic_from_bytes( + mnemonic_from_bytes = kruxbip39.mnemonic_from_bytes( entropy_bytes[:num_bytes] ) @@ -196,7 +197,6 @@ def new_key_from_snapshot(self): from ..wallet import is_double_mnemonic if not is_double_mnemonic(mnemonic_from_bytes): - from embit.bip39 import mnemonic_is_valid from ..wdt import wdt import time @@ -204,7 +204,7 @@ def new_key_from_snapshot(self): tries = 0 # create two 12w mnemonic with the provided entropy - first_12 = bip39.mnemonic_from_bytes(entropy_bytes[:16]) + first_12 = kruxbip39.mnemonic_from_bytes(entropy_bytes[:16]) second_mnemonic_entropy = entropy_bytes[16:32] double_mnemonic = False while not double_mnemonic: @@ -214,11 +214,11 @@ def new_key_from_snapshot(self): second_mnemonic_entropy = ( int.from_bytes(second_mnemonic_entropy, "big") + 1 ).to_bytes(16, "big") - second_12 = bip39.mnemonic_from_bytes( + second_12 = kruxbip39.mnemonic_from_bytes( second_mnemonic_entropy ) mnemonic_from_bytes = first_12 + " " + second_12 - double_mnemonic = mnemonic_is_valid(mnemonic_from_bytes) + double_mnemonic = kruxbip39.mnemonic_is_valid(mnemonic_from_bytes) post_t = time.ticks_ms() print("Tries: %d" % tries, "/ %d" % (post_t - pre_t), "ms") diff --git a/src/krux/wallet.py b/src/krux/wallet.py index 999d40057..fd23896b8 100644 --- a/src/krux/wallet.py +++ b/src/krux/wallet.py @@ -462,7 +462,7 @@ def is_double_mnemonic(mnemonic: str): words = mnemonic.split(" ") if len(words) > 12: - from embit.bip39 import mnemonic_is_valid + from krux.bip39 import mnemonic_is_valid if ( mnemonic_is_valid(" ".join(words[:12])) From 37587604ccd1650769b64245911ebe66f2d05ee8 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Fri, 9 Aug 2024 07:33:33 -0300 Subject: [PATCH 08/33] use of THIN_SPACE for fingerprint and derivation symbol --- src/krux/display.py | 3 +-- src/krux/key.py | 6 +++--- src/krux/pages/home_pages/addresses.py | 3 ++- src/krux/pages/qr_view.py | 3 ++- src/krux/psbt.py | 2 +- src/krux/settings.py | 2 ++ tests/pages/home_pages/test_bip85.py | 5 +++-- tests/pages/home_pages/test_home.py | 3 ++- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/krux/display.py b/src/krux/display.py index 3c8d60ea0..40045b114 100644 --- a/src/krux/display.py +++ b/src/krux/display.py @@ -24,6 +24,7 @@ import time from .themes import theme from .krux_settings import Settings +from .settings import THIN_SPACE DEFAULT_PADDING = 10 MINIMAL_PADDING = 5 @@ -51,8 +52,6 @@ SMALLEST_WIDTH = 135 SMALLEST_HEIGHT = 240 -THIN_SPACE = " " - # Splash will use horizontally-centered text plots. Uses Thin spaces to help with alignment SPLASH = [ "██" + THIN_SPACE * 3, diff --git a/src/krux/key.py b/src/krux/key.py index 05865048a..77b2a591a 100644 --- a/src/krux/key.py +++ b/src/krux/key.py @@ -31,7 +31,7 @@ from embit import bip32, bip39 from embit.wordlists.bip39 import WORDLIST from embit.networks import NETWORKS -from .settings import TEST_TXT +from .settings import TEST_TXT, THIN_SPACE DER_SINGLE = "m/%dh/%dh/%dh" DER_MULTI = "m/%dh/%dh/%dh/2h" @@ -191,13 +191,13 @@ def get_default_derivation(multisig, network, account=0, script_type=P2WPKH): @staticmethod def format_derivation(derivation, pretty=False): """Helper method to display the derivation path formatted""" - formatted_txt = DERIVATION_PATH_SYMBOL + " %s" if pretty else "%s" + formatted_txt = DERIVATION_PATH_SYMBOL + THIN_SPACE + "%s" if pretty else "%s" return (formatted_txt % derivation).replace("h", HARDENED_STR_REPLACE) @staticmethod def format_fingerprint(fingerprint, pretty=False): """Helper method to display the fingerprint formatted""" - formatted_txt = FINGERPRINT_SYMBOL + " %s" if pretty else "%s" + formatted_txt = FINGERPRINT_SYMBOL + THIN_SPACE + "%s" if pretty else "%s" return formatted_txt % hexlify(fingerprint).decode("utf-8") @staticmethod diff --git a/src/krux/pages/home_pages/addresses.py b/src/krux/pages/home_pages/addresses.py index 0e9382cae..24d32f2c7 100644 --- a/src/krux/pages/home_pages/addresses.py +++ b/src/krux/pages/home_pages/addresses.py @@ -21,8 +21,9 @@ # THE SOFTWARE. import gc -from ...display import BOTTOM_PROMPT_LINE, THIN_SPACE +from ...display import BOTTOM_PROMPT_LINE from ...krux_settings import t +from ...settings import THIN_SPACE from ...qr import FORMAT_NONE from .. import ( Page, diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py index 4b9bd81c0..d73358b40 100644 --- a/src/krux/pages/qr_view.py +++ b/src/krux/pages/qr_view.py @@ -25,8 +25,9 @@ from . import Page, Menu, MENU_CONTINUE, MENU_EXIT, ESC_KEY from ..themes import theme, WHITE, BLACK from ..krux_settings import t +from ..settings import THIN_SPACE from ..qr import get_size -from ..display import DEFAULT_PADDING, FONT_HEIGHT, SMALLEST_WIDTH, THIN_SPACE +from ..display import DEFAULT_PADDING, FONT_HEIGHT, SMALLEST_WIDTH from ..input import ( BUTTON_ENTER, BUTTON_PAGE, diff --git a/src/krux/psbt.py b/src/krux/psbt.py index fff82c01e..5a9594de3 100644 --- a/src/krux/psbt.py +++ b/src/krux/psbt.py @@ -26,10 +26,10 @@ from urtypes.crypto import CRYPTO_PSBT from .baseconv import base_decode from .krux_settings import t +from .settings import THIN_SPACE from .qr import FORMAT_PMOFN, FORMAT_BBQR from .key import Key, P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH, P2TR from .sats_vb import SatsVB -from .display import THIN_SPACE # PSBT Output Types: CHANGE = 0 diff --git a/src/krux/settings.py b/src/krux/settings.py index 51a308add..4d8a4765b 100644 --- a/src/krux/settings.py +++ b/src/krux/settings.py @@ -38,6 +38,8 @@ MAIN_TXT = "main" TEST_TXT = "test" +THIN_SPACE = " " + class SettingsNamespace: """Represents a settings namespace containing settings and child namespaces""" diff --git a/tests/pages/home_pages/test_bip85.py b/tests/pages/home_pages/test_bip85.py index b2ce2ec93..c8e86a7ef 100644 --- a/tests/pages/home_pages/test_bip85.py +++ b/tests/pages/home_pages/test_bip85.py @@ -3,6 +3,7 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): from krux.krux_settings import Settings + from krux.settings import THIN_SPACE from krux.pages.home_pages.bip85 import Bip85 from krux.wallet import Wallet from krux.input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV @@ -149,7 +150,7 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): if case[2]: bip85_ui.display_mnemonic.assert_called_with( - case[2], suffix="Words", fingerprint="⊚ %s" % case[3] + case[2], suffix="Words", fingerprint="⊚"+THIN_SPACE+"%s" % case[3] ) else: bip85_ui.display_mnemonic.assert_not_called() @@ -166,4 +167,4 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): bip85_ui = Bip85(ctx) mocker.spy(bip85_ui, "display_mnemonic") bip85_ui.export() - ctx.display.draw_centered_text.assert_called_with("⊚ %s" % case[3]) + ctx.display.draw_centered_text.assert_called_with("⊚"+THIN_SPACE+"%s" % case[3]) diff --git a/tests/pages/home_pages/test_home.py b/tests/pages/home_pages/test_home.py index 24716437d..372c1d9ed 100644 --- a/tests/pages/home_pages/test_home.py +++ b/tests/pages/home_pages/test_home.py @@ -738,6 +738,7 @@ def test_psbt_warnings(mocker, m5stickv, tdata): B64_PSBT_FILE_EXTENSION, SIGNED_FILE_SUFFIX, ) + from krux.settings import THIN_SPACE PSBT_FILE_NAME = "test.psbt" SIGNED_PSBT_FILE_NAME = "test-signed.psbt" @@ -805,7 +806,7 @@ def test_psbt_warnings(mocker, m5stickv, tdata): "Warning: Path mismatch\nWallet: m/48'/0'/0'/2'\nPSBT: m/48'/1'/0'/2'" ), mocker.call( - "PSBT policy:\np2wsh\n2 of 3\n⊚ 26bb83c4\n⊚ 0208cb77\n⊚ 73c5da0a" + "PSBT policy:\np2wsh\n2 of 3\n⊚"+THIN_SPACE+"26bb83c4\n⊚"+THIN_SPACE+"0208cb77\n⊚"+THIN_SPACE+"73c5da0a" ), ] ) From c97adb38f619fab02c391cea0aca71d3a516e29e Mon Sep 17 00:00:00 2001 From: tadeubas Date: Fri, 9 Aug 2024 07:34:18 -0300 Subject: [PATCH 09/33] removed post_t variable --- src/krux/pages/login.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 96f1180d9..f18a4e8b8 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -220,8 +220,11 @@ def new_key_from_snapshot(self): mnemonic_from_bytes = first_12 + " " + second_12 double_mnemonic = mnemonic_is_valid(mnemonic_from_bytes) - post_t = time.ticks_ms() - print("Tries: %d" % tries, "/ %d" % (post_t - pre_t), "ms") + print( + "Tries: %d" % tries, + "/ %d" % (time.ticks_ms() - pre_t), + "ms", + ) return self._load_key_from_words(mnemonic_from_bytes.split()) return MENU_CONTINUE From 9c2de164ca31dc82134fe894ab7970562eb6c23b Mon Sep 17 00:00:00 2001 From: tadeubas Date: Fri, 9 Aug 2024 08:59:05 -0300 Subject: [PATCH 10/33] black --- tests/pages/home_pages/test_bip85.py | 4 ++-- tests/pages/home_pages/test_home.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/pages/home_pages/test_bip85.py b/tests/pages/home_pages/test_bip85.py index c8e86a7ef..dfa21e31a 100644 --- a/tests/pages/home_pages/test_bip85.py +++ b/tests/pages/home_pages/test_bip85.py @@ -150,7 +150,7 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): if case[2]: bip85_ui.display_mnemonic.assert_called_with( - case[2], suffix="Words", fingerprint="⊚"+THIN_SPACE+"%s" % case[3] + case[2], suffix="Words", fingerprint="⊚" + THIN_SPACE + "%s" % case[3] ) else: bip85_ui.display_mnemonic.assert_not_called() @@ -167,4 +167,4 @@ def test_bip85_wallet_creation(mocker, amigo, tdata): bip85_ui = Bip85(ctx) mocker.spy(bip85_ui, "display_mnemonic") bip85_ui.export() - ctx.display.draw_centered_text.assert_called_with("⊚"+THIN_SPACE+"%s" % case[3]) + ctx.display.draw_centered_text.assert_called_with("⊚" + THIN_SPACE + "%s" % case[3]) diff --git a/tests/pages/home_pages/test_home.py b/tests/pages/home_pages/test_home.py index 372c1d9ed..4055c940d 100644 --- a/tests/pages/home_pages/test_home.py +++ b/tests/pages/home_pages/test_home.py @@ -806,7 +806,13 @@ def test_psbt_warnings(mocker, m5stickv, tdata): "Warning: Path mismatch\nWallet: m/48'/0'/0'/2'\nPSBT: m/48'/1'/0'/2'" ), mocker.call( - "PSBT policy:\np2wsh\n2 of 3\n⊚"+THIN_SPACE+"26bb83c4\n⊚"+THIN_SPACE+"0208cb77\n⊚"+THIN_SPACE+"73c5da0a" + "PSBT policy:\np2wsh\n2 of 3\n⊚" + + THIN_SPACE + + "26bb83c4\n⊚" + + THIN_SPACE + + "0208cb77\n⊚" + + THIN_SPACE + + "73c5da0a" ), ] ) From 0467c40c312f4d1b9c4158669514f627696521bb Mon Sep 17 00:00:00 2001 From: tadeubas Date: Mon, 12 Aug 2024 14:20:17 -0300 Subject: [PATCH 11/33] Processing ... to Processing.. --- src/krux/pages/capture_entropy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/krux/pages/capture_entropy.py b/src/krux/pages/capture_entropy.py index 362aec691..a0031094b 100644 --- a/src/krux/pages/capture_entropy.py +++ b/src/krux/pages/capture_entropy.py @@ -173,7 +173,7 @@ def capture(self, show_entropy_details=True): self.flash_text(t("Capture cancelled")) return None - self.ctx.display.draw_centered_text(t("Processing ...")) + self.ctx.display.draw_centered_text(t("Processing..")) self.entropy_measurement_update(img, all_at_once=True) From e2bea36f322870e4503182ae8f9db857e1d658a2 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Mon, 12 Aug 2024 16:43:50 -0300 Subject: [PATCH 12/33] translations --- i18n/translations/fr-FR.json | 1 + src/krux/translations.py | 1 + 2 files changed, 2 insertions(+) diff --git a/i18n/translations/fr-FR.json b/i18n/translations/fr-FR.json index 6472d968e..de249ca14 100644 --- a/i18n/translations/fr-FR.json +++ b/i18n/translations/fr-FR.json @@ -69,6 +69,7 @@ "Display": "Affichage", "Do not power off, it may take a while to complete.": "Ne pas éteindre, cela peut prendre un certain temps.", "Done?": "Terminé ?", + "Double mnemonic": "Double mnémonique", "Driver": "Conducteur", "Encrypted": "Chiffré", "Encrypted QR Code": "Code QR crypté", diff --git a/src/krux/translations.py b/src/krux/translations.py index 3da582b5a..9cd68e73d 100644 --- a/src/krux/translations.py +++ b/src/krux/translations.py @@ -712,6 +712,7 @@ 3278654271: "Affichage", 3895447625: "Ne pas éteindre, cela peut prendre un certain temps.", 3836852788: "Terminé\u2009?", + 690625786: "Double mnémonique", 382368239: "Conducteur", 3582575312: "Chiffré", 1244124409: "Code QR crypté", From 445ce8c21da8114edf5c33e3d63468e2fffc5f33 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Mon, 12 Aug 2024 16:02:13 -0400 Subject: [PATCH 13/33] replaces embit.bip39 -- only difference is mnemonic_to_bytes() --- src/krux/bip39.py | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/krux/bip39.py diff --git a/src/krux/bip39.py b/src/krux/bip39.py new file mode 100644 index 000000000..4c0feafb7 --- /dev/null +++ b/src/krux/bip39.py @@ -0,0 +1,95 @@ +# Mnemonic convertion to seed and to/from bytes +import hashlib +from embit.misc import const + +from embit.wordlists.bip39 import WORDLIST + +PBKDF2_ROUNDS = const(2048) + +WORDINDEX = {word: i for i, word in enumerate(WORDLIST)} + + +def mnemonic_to_bytes(mnemonic: str, ignore_checksum: bool = False, wordlist=WORDLIST): + words = mnemonic.strip().split() + if len(words) % 3 != 0 or len(words) < 12: + raise ValueError("Invalid recovery phrase") + + accumulator = 0 + for word in words: + try: + if wordlist is WORDLIST: + accumulator = (accumulator << 11) + WORDINDEX[word] + else: + accumulator = (accumulator << 11) + wordlist.index(word) + except Exception: + raise ValueError("Word '%s' is not in the dictionary" % word) + + entropy_length_bits = len(words) * 11 // 33 * 32 + checksum_length_bits = len(words) * 11 // 33 + checksum = accumulator & (2**checksum_length_bits -1) + accumulator >>= checksum_length_bits + data = accumulator.to_bytes(entropy_length_bits // 8, "big") + computed_checksum = hashlib.sha256(data).digest()[0] >> 8 - checksum_length_bits + + if not ignore_checksum and checksum != computed_checksum: + raise ValueError("Checksum verification failed") + return data + + +def mnemonic_is_valid(mnemonic: str, wordlist=WORDLIST): + """Checks if mnemonic is valid (checksum and words)""" + try: + mnemonic_to_bytes(mnemonic, wordlist=wordlist) + return True + except Exception as e: + return False + + +def mnemonic_to_seed(mnemonic: str, password: str = "", wordlist=WORDLIST): + # first we try to convert mnemonic to bytes + # and raise a correct error if it is invalid + # If wordlist is None - don't check mnemonic. + if wordlist is not None: + mnemonic_to_bytes(mnemonic, wordlist=wordlist) + return hashlib.pbkdf2_hmac( + "sha512", + mnemonic.encode("utf-8"), + ("mnemonic" + password).encode("utf-8"), + PBKDF2_ROUNDS, + 64, + ) + + +def _extract_index(bits, b, n): + value = 0 + for pos in range(n * bits, (n + 1) * bits): + value = value << 1 + if b[pos // 8] & (1 << (7 - pos % 8)): + value += 1 + return value + + +def mnemonic_from_bytes(entropy, wordlist=WORDLIST): + if len(entropy) % 4 != 0: + raise ValueError("Byte array should be multiple of 4 long (16, 20, ..., 32)") + total_bits = len(entropy) * 8 + checksum_bits = total_bits // 32 + total_mnemonics = (total_bits + checksum_bits) // 11 + # no need to truncate checksum - we already know total_mnemonics + checksum = bytearray(hashlib.sha256(entropy).digest()) + entropy += checksum + mnemonic = [] + for i in range(0, total_mnemonics): + idx = _extract_index(11, entropy, i) + mnemonic.append(wordlist[idx]) + return " ".join(mnemonic) + + +def find_candidates(word_part, nmax=5, wordlist=WORDLIST): + candidates = [] + for w in wordlist: + if w.startswith(word_part): + candidates.append(w) + if len(candidates) >= nmax: + break + return candidates From ec6c4b856c291aa378adb1646bf92edf2ddacaca Mon Sep 17 00:00:00 2001 From: Jean Do Date: Mon, 12 Aug 2024 17:09:46 -0400 Subject: [PATCH 14/33] reduces krux's bip39 to mnemonic_is_valid() and mnemonic_to_bytes() --- src/krux/bip39.py | 52 +---------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/krux/bip39.py b/src/krux/bip39.py index 4c0feafb7..f54f09123 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -26,7 +26,7 @@ def mnemonic_to_bytes(mnemonic: str, ignore_checksum: bool = False, wordlist=WOR entropy_length_bits = len(words) * 11 // 33 * 32 checksum_length_bits = len(words) * 11 // 33 - checksum = accumulator & (2**checksum_length_bits -1) + checksum = accumulator & (2**checksum_length_bits - 1) accumulator >>= checksum_length_bits data = accumulator.to_bytes(entropy_length_bits // 8, "big") computed_checksum = hashlib.sha256(data).digest()[0] >> 8 - checksum_length_bits @@ -43,53 +43,3 @@ def mnemonic_is_valid(mnemonic: str, wordlist=WORDLIST): return True except Exception as e: return False - - -def mnemonic_to_seed(mnemonic: str, password: str = "", wordlist=WORDLIST): - # first we try to convert mnemonic to bytes - # and raise a correct error if it is invalid - # If wordlist is None - don't check mnemonic. - if wordlist is not None: - mnemonic_to_bytes(mnemonic, wordlist=wordlist) - return hashlib.pbkdf2_hmac( - "sha512", - mnemonic.encode("utf-8"), - ("mnemonic" + password).encode("utf-8"), - PBKDF2_ROUNDS, - 64, - ) - - -def _extract_index(bits, b, n): - value = 0 - for pos in range(n * bits, (n + 1) * bits): - value = value << 1 - if b[pos // 8] & (1 << (7 - pos % 8)): - value += 1 - return value - - -def mnemonic_from_bytes(entropy, wordlist=WORDLIST): - if len(entropy) % 4 != 0: - raise ValueError("Byte array should be multiple of 4 long (16, 20, ..., 32)") - total_bits = len(entropy) * 8 - checksum_bits = total_bits // 32 - total_mnemonics = (total_bits + checksum_bits) // 11 - # no need to truncate checksum - we already know total_mnemonics - checksum = bytearray(hashlib.sha256(entropy).digest()) - entropy += checksum - mnemonic = [] - for i in range(0, total_mnemonics): - idx = _extract_index(11, entropy, i) - mnemonic.append(wordlist[idx]) - return " ".join(mnemonic) - - -def find_candidates(word_part, nmax=5, wordlist=WORDLIST): - candidates = [] - for w in wordlist: - if w.startswith(word_part): - candidates.append(w) - if len(candidates) >= nmax: - break - return candidates From a16f6eabacbd11d574bf9b8809133a6f55e4ee2d Mon Sep 17 00:00:00 2001 From: Jean Do Date: Mon, 12 Aug 2024 17:12:31 -0400 Subject: [PATCH 15/33] uses krux.bip39 to fail fast, else verifies against embit.bip39 --- src/krux/wallet.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/krux/wallet.py b/src/krux/wallet.py index fd23896b8..8ee075f65 100644 --- a/src/krux/wallet.py +++ b/src/krux/wallet.py @@ -462,13 +462,21 @@ def is_double_mnemonic(mnemonic: str): words = mnemonic.split(" ") if len(words) > 12: - from krux.bip39 import mnemonic_is_valid + from embit import bip39 + from krux import bip39 as kruxbip39 + # use an optimized version of mnemonic_to_bytes() via kruxbip39 if ( - mnemonic_is_valid(" ".join(words[:12])) - and mnemonic_is_valid(" ".join(words[12:])) - and mnemonic_is_valid(mnemonic) + kruxbip39.mnemonic_is_valid(" ".join(words[:12])) + and kruxbip39.mnemonic_is_valid(" ".join(words[12:])) + and kruxbip39.mnemonic_is_valid(mnemonic) ): - return True + # verify the well-known/well-tested version from embit.bip39 + if ( + bip39.mnemonic_is_valid(" ".join(words[:12])) + and bip39.mnemonic_is_valid(" ".join(words[12:])) + and bip39.mnemonic_is_valid(mnemonic) + ): + return True return False From 82471be4a904bc67268052b989902390ed2720ca Mon Sep 17 00:00:00 2001 From: Jean Do Date: Mon, 12 Aug 2024 17:14:53 -0400 Subject: [PATCH 16/33] uses embit.bip39 except where needing optimized krux.bip39 for speed --- src/krux/pages/login.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 63dd832c5..c8c9e3eff 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -188,7 +188,7 @@ def new_key_from_snapshot(self): self.ctx.display.draw_centered_text(t("Processing..")) num_bytes = 16 if len_mnemonic == 12 else 32 - mnemonic_from_bytes = kruxbip39.mnemonic_from_bytes( + mnemonic_from_bytes = bip39.mnemonic_from_bytes( entropy_bytes[:num_bytes] ) @@ -204,7 +204,7 @@ def new_key_from_snapshot(self): tries = 0 # create two 12w mnemonic with the provided entropy - first_12 = kruxbip39.mnemonic_from_bytes(entropy_bytes[:16]) + first_12 = bip39.mnemonic_from_bytes(entropy_bytes[:16]) second_mnemonic_entropy = entropy_bytes[16:32] double_mnemonic = False while not double_mnemonic: @@ -214,11 +214,13 @@ def new_key_from_snapshot(self): second_mnemonic_entropy = ( int.from_bytes(second_mnemonic_entropy, "big") + 1 ).to_bytes(16, "big") - second_12 = kruxbip39.mnemonic_from_bytes( + second_12 = bip39.mnemonic_from_bytes( second_mnemonic_entropy ) mnemonic_from_bytes = first_12 + " " + second_12 - double_mnemonic = kruxbip39.mnemonic_is_valid(mnemonic_from_bytes) + double_mnemonic = kruxbip39.mnemonic_is_valid( + mnemonic_from_bytes + ) print( "Tries: %d" % tries, From cbb69fd96df7b4d045b1c93ef6fec8ef7deaa8dd Mon Sep 17 00:00:00 2001 From: tadeubas Date: Mon, 12 Aug 2024 20:01:39 -0300 Subject: [PATCH 17/33] pylint for bip39 --- src/krux/bip39.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/krux/bip39.py b/src/krux/bip39.py index f54f09123..c11c4bf52 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -1,15 +1,14 @@ # Mnemonic convertion to seed and to/from bytes import hashlib -from embit.misc import const - from embit.wordlists.bip39 import WORDLIST -PBKDF2_ROUNDS = const(2048) - WORDINDEX = {word: i for i, word in enumerate(WORDLIST)} -def mnemonic_to_bytes(mnemonic: str, ignore_checksum: bool = False, wordlist=WORDLIST): +def mnemonic_to_bytes( + mnemonic: str, ignore_checksum: bool = False, wordlist=tuple(WORDLIST) +): + """Verifies the mnemonic checksum and returns it in bytes""" words = mnemonic.strip().split() if len(words) % 3 != 0 or len(words) < 12: raise ValueError("Invalid recovery phrase") @@ -36,10 +35,10 @@ def mnemonic_to_bytes(mnemonic: str, ignore_checksum: bool = False, wordlist=WOR return data -def mnemonic_is_valid(mnemonic: str, wordlist=WORDLIST): +def mnemonic_is_valid(mnemonic: str, wordlist=tuple(WORDLIST)): """Checks if mnemonic is valid (checksum and words)""" try: mnemonic_to_bytes(mnemonic, wordlist=wordlist) return True - except Exception as e: + except: return False From ef58d031eab877c3a98ea1ee902037afd3d0946f Mon Sep 17 00:00:00 2001 From: tadeubas Date: Mon, 12 Aug 2024 20:09:35 -0300 Subject: [PATCH 18/33] optimizing mnemonic_to_bytes --- src/krux/bip39.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/krux/bip39.py b/src/krux/bip39.py index c11c4bf52..2786aafe0 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -14,14 +14,15 @@ def mnemonic_to_bytes( raise ValueError("Invalid recovery phrase") accumulator = 0 - for word in words: - try: - if wordlist is WORDLIST: + try: + if wordlist is WORDLIST: + for word in words: accumulator = (accumulator << 11) + WORDINDEX[word] - else: + else: + for word in words: accumulator = (accumulator << 11) + wordlist.index(word) - except Exception: - raise ValueError("Word '%s' is not in the dictionary" % word) + except Exception: + raise ValueError("Word '%s' is not in the dictionary" % word) entropy_length_bits = len(words) * 11 // 33 * 32 checksum_length_bits = len(words) * 11 // 33 From e4f7b763818b98351b218a908883b98102c2f02b Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 06:38:24 -0300 Subject: [PATCH 19/33] fix pylint --- src/krux/bip39.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/krux/bip39.py b/src/krux/bip39.py index 2786aafe0..0fe2e9eaa 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -1,4 +1,6 @@ # Mnemonic convertion to seed and to/from bytes +#pylint: disable=W0102 + import hashlib from embit.wordlists.bip39 import WORDLIST @@ -6,7 +8,7 @@ def mnemonic_to_bytes( - mnemonic: str, ignore_checksum: bool = False, wordlist=tuple(WORDLIST) + mnemonic: str, ignore_checksum: bool = False, wordlist=WORDLIST ): """Verifies the mnemonic checksum and returns it in bytes""" words = mnemonic.strip().split() @@ -36,7 +38,7 @@ def mnemonic_to_bytes( return data -def mnemonic_is_valid(mnemonic: str, wordlist=tuple(WORDLIST)): +def mnemonic_is_valid(mnemonic: str, wordlist=WORDLIST): """Checks if mnemonic is valid (checksum and words)""" try: mnemonic_to_bytes(mnemonic, wordlist=wordlist) From 2da5ae4ca1ba6672a1d2c6cc4cef98f49dc51f6e Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 08:03:42 -0300 Subject: [PATCH 20/33] kruxbip39 tests --- src/krux/bip39.py | 6 ++--- src/krux/wallet.py | 9 +------- tests/test_bip39.py | 56 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 tests/test_bip39.py diff --git a/src/krux/bip39.py b/src/krux/bip39.py index 0fe2e9eaa..c52a21b16 100644 --- a/src/krux/bip39.py +++ b/src/krux/bip39.py @@ -1,5 +1,5 @@ # Mnemonic convertion to seed and to/from bytes -#pylint: disable=W0102 +# pylint: disable=W0102 import hashlib from embit.wordlists.bip39 import WORDLIST @@ -7,9 +7,7 @@ WORDINDEX = {word: i for i, word in enumerate(WORDLIST)} -def mnemonic_to_bytes( - mnemonic: str, ignore_checksum: bool = False, wordlist=WORDLIST -): +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: diff --git a/src/krux/wallet.py b/src/krux/wallet.py index 8ee075f65..92ebc6096 100644 --- a/src/krux/wallet.py +++ b/src/krux/wallet.py @@ -462,7 +462,6 @@ def is_double_mnemonic(mnemonic: str): words = mnemonic.split(" ") if len(words) > 12: - from embit import bip39 from krux import bip39 as kruxbip39 # use an optimized version of mnemonic_to_bytes() via kruxbip39 @@ -471,12 +470,6 @@ def is_double_mnemonic(mnemonic: str): and kruxbip39.mnemonic_is_valid(" ".join(words[12:])) and kruxbip39.mnemonic_is_valid(mnemonic) ): - # verify the well-known/well-tested version from embit.bip39 - if ( - bip39.mnemonic_is_valid(" ".join(words[:12])) - and bip39.mnemonic_is_valid(" ".join(words[12:])) - and bip39.mnemonic_is_valid(mnemonic) - ): - return True + return True return False diff --git a/tests/test_bip39.py b/tests/test_bip39.py new file mode 100644 index 000000000..527c58e0b --- /dev/null +++ b/tests/test_bip39.py @@ -0,0 +1,56 @@ +from krux import bip39 as kruxbip39 +from embit import bip39 +from embit.wordlists.bip39 import WORDLIST +import secrets + + +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 + ) + + +def test_edge_cases(): + cases = [8, 16] # 12w 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") + + assert ( + kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(ALL_ZERO_BYTES)) + == ALL_ZERO_BYTES + ) + assert ( + kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(ALL_ONE_BYTES)) + == ALL_ONE_BYTES + ) + + int_val = max_val = int.from_bytes(ALL_ONE_BYTES) + while int_val > 0: + int_val = int_val // 2 + b = int_val.to_bytes(16, "big") + assert kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(b)) == b + + b = (max_val - int_val).to_bytes(16, "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) + + assert ( + kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(token12w)) == token12w + ) + assert ( + kruxbip39.mnemonic_to_bytes(bip39.mnemonic_from_bytes(token24w)) == token24w + ) From 15db210f9e23397943a00755576283f985c5d1e1 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 11:58:30 -0300 Subject: [PATCH 21/33] input change validate_position default to False --- src/krux/input.py | 4 ++-- src/krux/pages/capture_entropy.py | 4 +--- src/krux/pages/tiny_seed.py | 4 +--- src/krux/touch.py | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/krux/input.py b/src/krux/input.py index 63a2c14d6..2179780a5 100644 --- a/src/krux/input.py +++ b/src/krux/input.py @@ -167,7 +167,7 @@ def page_prev_event(self, check_yahboom=False): return self.page_prev.event() return False - def touch_event(self, validate_position=True): + def touch_event(self, validate_position=False): """Intermediary method to pull button TOUCH event""" if self.touch is not None: return self.touch.event(validate_position) @@ -231,7 +231,7 @@ def _wait_for_press(self, block=True, wait_duration=QR_ANIM_PERIOD): return BUTTON_PAGE if self.page_prev_event(): return BUTTON_PAGE_PREV - if self.touch_event(): + if self.touch_event(validate_position=True): return BUTTON_TOUCH self.wdt_feed_inc_entropy() diff --git a/src/krux/pages/capture_entropy.py b/src/krux/pages/capture_entropy.py index a0031094b..383659656 100644 --- a/src/krux/pages/capture_entropy.py +++ b/src/krux/pages/capture_entropy.py @@ -61,9 +61,7 @@ def _callback(self): Returns PROCEED if user pressed ENTER or touched the screen, CANCEL if user pressed PAGE or PAGE_PREV, 0 otherwise """ - if self.ctx.input.enter_event() or self.ctx.input.touch_event( - validate_position=False - ): + if self.ctx.input.enter_event() or self.ctx.input.touch_event(): return PROCEED_PRESSED if self.ctx.input.page_event() or self.ctx.input.page_prev_event(): return CANCEL_PRESSED diff --git a/src/krux/pages/tiny_seed.py b/src/krux/pages/tiny_seed.py index 135587cd9..f5e84919f 100644 --- a/src/krux/pages/tiny_seed.py +++ b/src/krux/pages/tiny_seed.py @@ -999,9 +999,7 @@ def _exit_camera(self): self.ctx.display.clear() def _check_buttons(self, w24, page): - enter_or_touch = self.ctx.input.enter_event() or self.ctx.input.touch_event( - validate_position=False - ) + enter_or_touch = self.ctx.input.enter_event() or self.ctx.input.touch_event() if w24: if page == 0 and enter_or_touch: self.capturing = True diff --git a/src/krux/touch.py b/src/krux/touch.py index 518f509f4..fdec9a3bb 100644 --- a/src/krux/touch.py +++ b/src/krux/touch.py @@ -160,7 +160,7 @@ def current_state(self): print("Touch error") return self.state - def event(self, validate_position=True): + def event(self, validate_position=False): """Checks if a touch happened and stores the point""" current_time = time.ticks_ms() if current_time > self.sample_time + TOUCH_S_PERIOD: From 9e8186d128dc7efbe5d82afb8d850ae9fb699e62 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 12:00:19 -0300 Subject: [PATCH 22/33] optimized imports --- src/krux/pages/login.py | 43 ++++++++++++++++++++--------------------- src/krux/wallet.py | 9 ++++----- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 5b2c1cbfb..a7e9130dc 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -23,8 +23,6 @@ import sys from embit.networks import NETWORKS from embit.wordlists.bip39 import WORDLIST -from embit import bip39 -from krux import bip39 as kruxbip39 from ..display import DEFAULT_PADDING, FONT_HEIGHT, BOTTOM_PROMPT_LINE from ..krux_settings import Settings from ..qr import FORMAT_UR @@ -154,7 +152,9 @@ def new_key_from_dice(self, d_20=False): dice_entropy = DiceEntropy(self.ctx, d_20) captured_entropy = dice_entropy.new_key() if captured_entropy is not None: - words = bip39.mnemonic_from_bytes(captured_entropy).split() + from embit.bip39 import mnemonic_from_bytes + + words = mnemonic_from_bytes(captured_entropy).split() return self._load_key_from_words(words) return MENU_CONTINUE @@ -176,6 +176,7 @@ def new_key_from_snapshot(self): entropy_bytes = camera_entropy.capture() if entropy_bytes is not None: import binascii + from embit.bip39 import mnemonic_from_bytes entropy_hash = binascii.hexlify(entropy_bytes).decode() self.ctx.display.clear() @@ -188,39 +189,34 @@ def new_key_from_snapshot(self): self.ctx.display.draw_centered_text(t("Processing..")) num_bytes = 16 if len_mnemonic == 12 else 32 - mnemonic_from_bytes = bip39.mnemonic_from_bytes( - entropy_bytes[:num_bytes] - ) + entropy_mnemonic = mnemonic_from_bytes(entropy_bytes[:num_bytes]) # Double mnemonic check if len_mnemonic == 48: from ..wallet import is_double_mnemonic - if not is_double_mnemonic(mnemonic_from_bytes): + if not is_double_mnemonic(entropy_mnemonic): from ..wdt import wdt import time + from krux.bip39 import mnemonic_is_valid pre_t = time.ticks_ms() tries = 0 # create two 12w mnemonic with the provided entropy - first_12 = bip39.mnemonic_from_bytes(entropy_bytes[:16]) - second_mnemonic_entropy = entropy_bytes[16:32] + first_12 = mnemonic_from_bytes(entropy_bytes[:16]) + second_entropy_mnemonic = entropy_bytes[16:32] double_mnemonic = False while not double_mnemonic: wdt.feed() tries += 1 # increment the second mnemonic entropy - second_mnemonic_entropy = ( - int.from_bytes(second_mnemonic_entropy, "big") + 1 + second_entropy_mnemonic = ( + int.from_bytes(second_entropy_mnemonic, "big") + 1 ).to_bytes(16, "big") - second_12 = bip39.mnemonic_from_bytes( - second_mnemonic_entropy - ) - mnemonic_from_bytes = first_12 + " " + second_12 - double_mnemonic = kruxbip39.mnemonic_is_valid( - mnemonic_from_bytes - ) + second_12 = mnemonic_from_bytes(second_entropy_mnemonic) + entropy_mnemonic = first_12 + " " + second_12 + double_mnemonic = mnemonic_is_valid(entropy_mnemonic) print( "Tries: %d" % tries, @@ -228,7 +224,7 @@ def new_key_from_snapshot(self): "ms", ) - return self._load_key_from_words(mnemonic_from_bytes.split()) + return self._load_key_from_words(entropy_mnemonic.split()) return MENU_CONTINUE def _load_key_from_words(self, words, charset=LETTERS): @@ -333,6 +329,7 @@ def _encrypted_qr_code(self, data): public_data + "\n\n" + t("Decrypt?"), self.ctx.display.height() // 2 ): from .encryption_ui import EncryptionKey + from embit.bip39 import mnemonic_from_bytes key_capture = EncryptionKey(self.ctx) key = key_capture.encryption_key() @@ -345,7 +342,7 @@ def _encrypted_qr_code(self, data): if word_bytes is None: self.flash_error(t("Failed to decrypt")) return MENU_CONTINUE - return bip39.mnemonic_from_bytes(word_bytes).split() + return mnemonic_from_bytes(word_bytes).split() return MENU_CONTINUE # prompt NO return None @@ -386,9 +383,11 @@ def load_key_from_qr_code(self): except: pass + # CompactSeedQR format if len(data_bytes) in (16, 32): - # CompactSeedQR format - words = bip39.mnemonic_from_bytes(data_bytes).split() + from embit.bip39 import mnemonic_from_bytes + + words = mnemonic_from_bytes(data_bytes).split() # SeedQR format elif len(data_bytes) in (48, 96): words = [ diff --git a/src/krux/wallet.py b/src/krux/wallet.py index 92ebc6096..fd23896b8 100644 --- a/src/krux/wallet.py +++ b/src/krux/wallet.py @@ -462,13 +462,12 @@ def is_double_mnemonic(mnemonic: str): words = mnemonic.split(" ") if len(words) > 12: - from krux import bip39 as kruxbip39 + from krux.bip39 import mnemonic_is_valid - # use an optimized version of mnemonic_to_bytes() via kruxbip39 if ( - kruxbip39.mnemonic_is_valid(" ".join(words[:12])) - and kruxbip39.mnemonic_is_valid(" ".join(words[12:])) - and kruxbip39.mnemonic_is_valid(mnemonic) + mnemonic_is_valid(" ".join(words[:12])) + and mnemonic_is_valid(" ".join(words[12:])) + and mnemonic_is_valid(mnemonic) ): return True From 20dca8a5b6942b74afa2454e290734b26cd75d3e Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 12:00:46 -0300 Subject: [PATCH 23/33] double mnemonic tests --- tests/pages/test_login.py | 75 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/tests/pages/test_login.py b/tests/pages/test_login.py index 7338915af..491b54ed7 100644 --- a/tests/pages/test_login.py +++ b/tests/pages/test_login.py @@ -167,7 +167,7 @@ def test_new_12w_from_snapshot(m5stickv, mocker): from krux.input import BUTTON_ENTER, BUTTON_PAGE_PREV # mocks a result of a hashed image - mock_capture_entropy = mocker.patch( + mocker.patch( "krux.pages.capture_entropy.CameraEntropy.capture", return_value=b"\x01" * 32 ) @@ -196,6 +196,79 @@ def test_new_12w_from_snapshot(m5stickv, mocker): assert ctx.wallet.key.mnemonic == MNEMONIC +def test_new_24w_from_snapshot(m5stickv, mocker): + from krux.pages.login import Login + from krux.input import BUTTON_ENTER, BUTTON_PAGE + + # mocks a result of a hashed image + mocker.patch( + "krux.pages.capture_entropy.CameraEntropy.capture", return_value=b"\x01" * 32 + ) + + BTN_SEQUENCE = ( + # 1 move to select 24 words, 1 press to proceed + [BUTTON_PAGE, BUTTON_ENTER] + + + # 1 press to proceed msg + [BUTTON_ENTER] + + + # SHA256 + [BUTTON_ENTER] + + + # Words 2x + [BUTTON_ENTER, BUTTON_ENTER] + + + # Load Wallet + [BUTTON_ENTER] + ) + MNEMONIC = "absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice comic" + ctx = create_ctx(mocker, BTN_SEQUENCE) + login = Login(ctx) + login.new_key_from_snapshot() + + assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE) + assert ctx.wallet.key.mnemonic == MNEMONIC + + +def test_new_double_mnemonic_from_snapshot(m5stickv, mocker): + from krux.pages.login import Login + from krux.input import BUTTON_ENTER, BUTTON_PAGE + from krux.wallet import is_double_mnemonic + from krux.display import DEFAULT_PADDING + from krux.themes import WHITE, BLACK + + # mocks a result of a hashed image + mocker.patch( + "krux.pages.capture_entropy.CameraEntropy.capture", return_value=b"\x01" * 32 + ) + + BTN_SEQUENCE = ( + # 2 moves to select double mnemonic, 1 press to proceed + [BUTTON_PAGE, BUTTON_PAGE, BUTTON_ENTER] + + + # 1 press to proceed msg + [BUTTON_ENTER] + + + # SHA256 + [BUTTON_ENTER] + + + # Words 2x + [BUTTON_ENTER, BUTTON_ENTER] + + + # Load Wallet + [BUTTON_ENTER] + ) + MNEMONIC = "absurd amount doctor acoustic avoid letter advice cage absurd amount doctor adjust absurd amount doctor acoustic avoid letter advice cage absurd amount doll fancy" + ctx = create_ctx(mocker, BTN_SEQUENCE) + login = Login(ctx) + login.new_key_from_snapshot() + + assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE) + assert ctx.wallet.key.mnemonic == MNEMONIC + assert is_double_mnemonic(MNEMONIC) == True + ctx.display.draw_hcentered_text.assert_has_calls([mocker.call("BIP39 Mnemonic*")]) + + ########## load words from qrcode tests From 065bdd18c17d43cc2e45966c3e5fac6797bd20ff Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 12:01:36 -0300 Subject: [PATCH 24/33] fix test_touch --- tests/test_touch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_touch.py b/tests/test_touch.py index b85547176..3170b0e24 100644 --- a/tests/test_touch.py +++ b/tests/test_touch.py @@ -29,6 +29,6 @@ def test_touch_event(mocker, amigo): touch.touch_driver.irq_point = TOUCH_POINT_1 # Simulate touch event for _ in range(len(EVENTS)): - event = touch.event() + event = touch.event(validate_position=True) assert event == False assert touch.current_index() == 3 From 96afca05801cd1037171f5084938d0966f71bfe8 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 13:37:00 -0300 Subject: [PATCH 25/33] poetry / docs - added macros plugin --- mkdocs.yml | 2 ++ poetry.lock | 37 ++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 30e084574..ed7c3538c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,6 +51,7 @@ edit_uri: edit/main/docs docs_dir: docs site_dir: public extra: + latest_installer: krux-installer_0.0.13 social: - icon: fontawesome/solid/bullhorn link: https://bitcointalk.org/index.php?topic=5489022.0 @@ -101,6 +102,7 @@ nav: plugins: - search + - macros - i18n: docs_structure: suffix languages: diff --git a/poetry.lock b/poetry.lock index c8a7caa6c..b5cecf6a7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -671,6 +671,27 @@ mergedeep = ">=1.3.4" platformdirs = ">=2.2.0" pyyaml = ">=5.1" +[[package]] +name = "mkdocs-macros-plugin" +version = "1.0.5" +description = "Unleash the power of MkDocs with macros and variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs-macros-plugin-1.0.5.tar.gz", hash = "sha256:fe348d75f01c911f362b6d998c57b3d85b505876dde69db924f2c512c395c328"}, + {file = "mkdocs_macros_plugin-1.0.5-py3-none-any.whl", hash = "sha256:f60e26f711f5a830ddf1e7980865bf5c0f1180db56109803cdd280073c1a050a"}, +] + +[package.dependencies] +jinja2 = "*" +mkdocs = ">=0.17" +python-dateutil = "*" +pyyaml = "*" +termcolor = "*" + +[package.extras] +test = ["mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material (>=6.2)"] + [[package]] name = "mkdocs-material" version = "9.5.28" @@ -1478,6 +1499,20 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.8" +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + [[package]] name = "tomli" version = "2.0.1" @@ -1635,4 +1670,4 @@ simulator = ["Pillow", "numpy", "opencv-python", "pygame", "pyzbar"] [metadata] lock-version = "2.0" python-versions = "^3.9.1" -content-hash = "235e193b46ca306b56a2055685ac78418466ac23acfd19b3ac8c2bdf329024d5" +content-hash = "2721280557ad4eca70abac3b4f9454ec3a4166ab90e1b3ece4e695a668e6a02e" diff --git a/pyproject.toml b/pyproject.toml index 22e4b132a..bca3a1e0a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ mkdocs = "^1.6.0" mkdocs-material = "^9.5.28" mkdocs-static-i18n = "^1.2.3" pymdown-extensions = "^10.8.1" +mkdocs-macros-plugin = "^1.0.5" # Simulator dependencies. Optional extras numpy = { version = "^1.25.2", optional = true } From 603eb44ee2fced2145f7cec9fe823ed41a7e8a46 Mon Sep 17 00:00:00 2001 From: Jean Do Date: Tue, 13 Aug 2024 18:48:24 -0400 Subject: [PATCH 26/33] 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 c52a21b16..4c797fe8a 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 527c58e0b..81c7c8077 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 add60f81f32c02eed6f06673f319d221b61d2c74 Mon Sep 17 00:00:00 2001 From: tadeubas Date: Tue, 13 Aug 2024 20:13:19 -0300 Subject: [PATCH 27/33] docs added double mnemonic + overhaul --- docs/css/custom.css | 5 + docs/getting-started/features/tinyseed.en.md | 1 + .../getting-started/installing/from-gui.en.md | 48 ++++---- .../installing/from-pre-built-release.en.md | 10 +- .../installing/from-source.en.md | 2 +- .../installing/from-test-release.en.md | 8 +- .../usage/generating-a-mnemonic.en.md | 84 ++++++------- .../usage/loading-a-mnemonic.en.md | 116 ++++++------------ .../usage/navigating-the-main-menu.en.md | 59 ++++----- .../load-mnemonic-camera-options-150.en.png | Bin 13514 -> 15604 bytes ...ad-mnemonic-seq-double-mnemonic-150.en.png | Bin 0 -> 26378 bytes .../load-mnemonic-camera-options-125.en.png | Bin 36130 -> 38849 bytes docs/parts.en.md | 2 +- docs/snippets/camera-scan-tips.en.txt | 2 +- mkdocs.yml | 5 +- simulator/generate-device-screenshots.sh | 1 + .../load-mnemonic-double-mnemonic.txt | 9 ++ .../sequences/qrcodes/double-mnemonic.png | Bin 0 -> 3541 bytes 18 files changed, 148 insertions(+), 204 deletions(-) create mode 100644 docs/img/maixpy_amigo/load-mnemonic-seq-double-mnemonic-150.en.png create mode 100644 simulator/sequences/load-mnemonic-double-mnemonic.txt create mode 100644 simulator/sequences/qrcodes/double-mnemonic.png diff --git a/docs/css/custom.css b/docs/css/custom.css index a4b76c206..9bd546b1c 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -4,4 +4,9 @@ .md-typeset img.twemoji { margin: 0 !important; +} + +.md-typeset h5 { + color: black; + text-transform: none; } \ No newline at end of file diff --git a/docs/getting-started/features/tinyseed.en.md b/docs/getting-started/features/tinyseed.en.md index e135d260e..358e73c48 100644 --- a/docs/getting-started/features/tinyseed.en.md +++ b/docs/getting-started/features/tinyseed.en.md @@ -12,6 +12,7 @@ The examples below have been crated so that you can test the workflow for scanni ## Size, Offset and Padding Reference The general logic for how these are processed is: + 1. Krux first looks for a square (Which works best if with a well lit square, with clean edges, on a dark background) 2. This square is checked and if the ratio of length to height is within a defined range for the given seed type, the square is further processed. (Uses the aspect_high and aspect_low variables) 3. An X and Y offset are applied to work out the corner of the seed grid within the seed plate. Some devices like the Maix Amigo use a mirrored coordinate system and some seed types will have a slightly different layout on the front and back of the plate. (Uses the x_offset and y_offset variables, p0 for the front face and p1 for the reverse face) diff --git a/docs/getting-started/installing/from-gui.en.md b/docs/getting-started/installing/from-gui.en.md index d22a72366..d0d15cec2 100644 --- a/docs/getting-started/installing/from-gui.en.md +++ b/docs/getting-started/installing/from-gui.en.md @@ -6,13 +6,13 @@ available for Linux and Windows. Download the installer by choosing the right asset for your operating system from our [Github releases page](https://github.com/selfcustody/krux-installer/releases): -| **Operational System** | **File** | -|------------------------------------------------------------|:----------------------------------:| -| Windows | `krux-installer_0.0.13.exe*` | -| Debian-based: Ubuntu, PopOS, etc... | `krux-installer_0.0.13_amd64.deb*` | -| RedHat-based: Fedora, etc... | `krux-installer-0.0.13.x86_64.rpm*`| -| Any linux distribution | `krux-installer-0.0.13.AppImage*` | -| Package for Archlinux on [AUR](https://aur.archlinux.org/).| `krux-installer-bin` | +| **Operational System** | **File** | +|------------------------------------------------------------|:------------------------------------------:| +| Windows | `{{latest_installer_underline}}.exe` | +| Debian-based: Ubuntu, PopOS, etc... | `{{latest_installer_underline}}_amd64.deb` | +| RedHat-based: Fedora, etc... | `{{latest_installer}}.x86_64.rpm` | +| Any linux distribution | `{{latest_installer}}.AppImage` | +| Package for Archlinux on [AUR](https://aur.archlinux.org/).| `krux-installer-bin` | ### Verify files If you trust the project developers, you can skip to [install](#install): @@ -33,10 +33,10 @@ If you trust the project developers, you can skip to [install](#install): ```pwsh # Compare this output: - (Get-FileHash 'krux-installer_0.0.13.exe').Hash + (Get-FileHash '{{latest_installer_underline}}.exe').Hash # With this: - Get-Content 'krux-installer_0.0.13.exe.sha256.txt' + Get-Content '{{latest_installer_underline}}.exe.sha256.txt' ``` @@ -44,7 +44,7 @@ If you trust the project developers, you can skip to [install](#install): Debian-based ```bash - sha256sum --check ./krux-installer_0.0.13_amd64.deb.sha256.txt + sha256sum --check ./{{latest_installer_underline}}_amd64.deb.sha256.txt ``` @@ -52,7 +52,7 @@ If you trust the project developers, you can skip to [install](#install): RedHat-based ```bash - sha256txt --check ./krux-installer-0.0.13.x86_64.rpm.sha256.txt + sha256txt --check ./{{latest_installer}}.x86_64.rpm.sha256.txt ``` @@ -60,7 +60,7 @@ If you trust the project developers, you can skip to [install](#install): Any Linux distribution ```bash - sha256sum --check ./krux-installer-0.0.13.AppImage.sha256.txt + sha256sum --check ./{{latest_installer}}.AppImage.sha256.txt ``` @@ -80,10 +80,10 @@ Then you can verify: | System | Command | |------------------------|-------------------------------------------------------| -| Windows (powershell) | `gpg --verify krux-installer_0.0.13.exe.sig` | -| Debian-based | `gpg --verify ./krux-installer_0.0.13_amd64.deb.sig` | -| RedHat-based | `gpg --verify ./krux-installer-0.0.13.x86_64.rpm.sig` | -| Any Linux distribution | `gpg --verify ./krux-installer-0.0.13.AppImage.sig` | +| Windows (powershell) | `gpg --verify {{latest_installer_underline}}.exe.sig` | +| Debian-based | `gpg --verify ./{{latest_installer_underline}}_amd64.deb.sig` | +| RedHat-based | `gpg --verify ./{{latest_installer}}.x86_64.rpm.sig` | +| Any Linux distribution | `gpg --verify ./{{latest_installer}}.AppImage.sig` | > ⚠️ TIP: If the verification was successful, you may get a message similar to: `Good signature from "qlrddev "` @@ -102,7 +102,7 @@ Each system require different steps to install: Windows