def xkeys_from_seed(self, seed, passphrase): t = seed_type(seed) if not is_any_2fa_seed_type(t): raise Exception(f'unexpected seed type: {t}') words = seed.split() n = len(words) if t == '2fa': if n >= 20: # old scheme # note: pre-2.7 2fa seeds were typically 24-25 words, however they # could probabilistically be arbitrarily shorter due to a bug. (see #3611) # the probability of it being < 20 words is about 2^(-(256+12-19*11)) = 2^(-59) if passphrase != '': raise Exception('old 2fa seed cannot have passphrase') xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), t, '', "m/") xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), t, '', "m/") elif n == 12: # new scheme xprv1, xpub1 = self.get_xkeys(seed, t, passphrase, "m/0'/") xprv2, xpub2 = self.get_xkeys(seed, t, passphrase, "m/1'/") else: raise Exception( f'unrecognized seed length for "2fa" seed: {n}') elif t == '2fa_segwit': xprv1, xpub1 = self.get_xkeys(seed, t, passphrase, "m/0'/") xprv2, xpub2 = self.get_xkeys(seed, t, passphrase, "m/1'/") else: raise Exception(f'unexpected seed type: {t}') return xprv1, xpub1, xprv2, xpub2
def verify_seed(self, seed, bip39=False, slip39=False, wallet_type='standard', language='en'): self._logger.debug('bip39 ' + str(bip39)) self._logger.debug('slip39 ' + str(slip39)) seed_type = '' seed_valid = False validation_message = '' if not (bip39 or slip39): seed_type = mnemonic.seed_type(seed) if seed_type != '': seed_valid = True elif bip39: is_checksum, is_wordlist = bip39_is_checksum_valid(seed) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' validation_message = 'BIP39 (%s)' % status if is_checksum: seed_type = 'bip39' seed_valid = True elif slip39: # TODO: incomplete impl, this code only validates a single share. try: share = decode_mnemonic(seed) seed_type = 'slip39' validation_message = 'SLIP39: share #%d in %dof%d scheme' % ( share.group_index, share.group_threshold, share.group_count) except Slip39Error as e: validation_message = 'SLIP39: %s' % str(e) seed_valid = False # for now # cosigning seed if wallet_type != 'standard' and seed_type not in [ 'standard', 'segwit' ]: seed_type = '' seed_valid = False self.seedType = seed_type self.seedTypeChanged.emit() if self.validationMessage != validation_message: self.validationMessage = validation_message self.validationMessageChanged.emit() if self.seedValid != seed_valid: self.seedValid = seed_valid self.seedValidChanged.emit() self._logger.debug('seed verified: ' + str(seed_valid))
def on_edit(self): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) if self.seed_type == 'bip39': from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)' % status elif self.seed_type == 'slip39': self.slip39_mnemonics[self.slip39_mnemonic_index] = s try: slip39.decode_mnemonic(s) except slip39.Slip39Error as e: share_status = str(e) current_mnemonic_invalid = True else: share_status = _('Valid.') current_mnemonic_invalid = False label = _('SLIP39 share') + ' #%d: %s' % ( self.slip39_mnemonic_index + 1, share_status) # No need to process mnemonics if the current mnemonic remains invalid after editing. if not (self.slip39_current_mnemonic_invalid and current_mnemonic_invalid): self.slip39_seed, seed_status = slip39.process_mnemonics( self.slip39_mnemonics) self.seed_status.setText(seed_status) self.slip39_current_mnemonic_invalid = current_mnemonic_invalid b = self.slip39_seed is not None self.update_share_buttons() else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' if t and not b: # electrum seed, but does not conform to dialog rules msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _("Looks like you have entered a valid seed of type '{}' but this dialog does not support such seeds." ).format(t), _("If unsure, try restoring as '{}'.").format( _("Standard wallet")), ]) self.seed_warning.setText(msg) else: self.seed_warning.setText("") self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def on_edit(self, *, from_click=False): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) from electrum.keystore import bip39_is_checksum_valid from electrum.mnemonic import Wordlist, filenames lang = '' for type, file in filenames.items(): word_list = Wordlist.from_file(file) is_checksum, is_wordlist = bip39_is_checksum_valid(s, wordlist=word_list) if is_wordlist: lang = type break if self.seed_type == 'bip39': status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39 - ' + lang + ' (%s)'%status if lang and lang != self.lang: if lang == 'en': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = 'en' else: self.wordlist = list(Mnemonic(lang).wordlist) self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = lang b = is_checksum else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' if is_checksum and is_wordlist and not from_click: # This is a valid bip39 and this method was called from typing # Emulate selecting the bip39 option self.clayout.group.buttons()[1].click() return self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def on_edit(self): s = self.get_seed() b = self.is_seed(s) if not self.is_bip39: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' else: from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)'%status self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b)
def on_edit(self): s = self.get_seed() if self.is_bip39 or self.is_gold_wallet_import: b = bip39_is_checksum_valid(s)[0] else: b = bool(seed_type(s)) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed().split(" ")[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def on_edit(self): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) if self.seed_type == 'bip39': from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)' % status elif self.seed_type == 'slip39': self.slip39_mnemonics[self.slip39_mnemonic_index] = s try: slip39.decode_mnemonic(s) except slip39.Slip39Error as e: share_status = str(e) current_mnemonic_invalid = True else: share_status = _('Valid.') current_mnemonic_invalid = False label = _('SLIP39 share') + ' #%d: %s' % ( self.slip39_mnemonic_index + 1, share_status) # No need to process mnemonics if the current mnemonic remains invalid after editing. if not (self.slip39_current_mnemonic_invalid and current_mnemonic_invalid): self.slip39_seed, seed_status = slip39.process_mnemonics( self.slip39_mnemonics) self.seed_status.setText(seed_status) self.slip39_current_mnemonic_invalid = current_mnemonic_invalid b = self.slip39_seed is not None self.update_share_buttons() else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def on_edit(self): s = self.get_seed() b = self.is_seed(s) if not self.is_bip39: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' else: from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)'%status self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # to account for bip39 seeds for word in self.get_seed().split(" ")[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def from_seed(seed, passphrase, is_p2sh=False): t = seed_type(seed) if t == 'old': keystore = Old_KeyStore({}) keystore.add_seed(seed) elif t in ['standard', 'segwit']: keystore = BIP32_KeyStore({}) keystore.add_seed(seed) keystore.passphrase = passphrase bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) if t == 'standard': der = "m/" xtype = 'standard' else: der = "m/1'/" if is_p2sh else "m/0'/" xtype = 'p2wsh' if is_p2sh else 'p2wpkh' keystore.add_xprv_from_seed(bip32_seed, xtype, der) else: raise BitcoinException('Unexpected seed type {}'.format(repr(t))) return keystore
def on_edit(self): s = self.get_seed() b = bip39_is_checksum_valid(s)[0] if not self.is_bip39: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' else: is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)' % status self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed().split(" ")[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
def xkeys_from_seed(self, seed, passphrase): t = seed_type(seed) if not is_any_2fa_seed_type(t): raise Exception(f'unexpected seed type: {t}') words = seed.split() n = len(words) # old version use long seed phrases if n >= 20: # note: pre-2.7 2fa seeds were typically 24-25 words, however they # could probabilistically be arbitrarily shorter due to a bug. (see #3611) # the probability of it being < 20 words is about 2^(-(256+12-19*11)) = 2^(-59) if passphrase != '': raise Exception('old 2fa seed cannot have passphrase') xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), t, '', "m/") xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), t, '', "m/") elif not t == '2fa' or n == 12: xprv1, xpub1 = self.get_xkeys(seed, t, passphrase, "m/0'/") xprv2, xpub2 = self.get_xkeys(seed, t, passphrase, "m/1'/") else: raise Exception('unrecognized seed length: {} words'.format(n)) return xprv1, xpub1, xprv2, xpub2
def is_valid_seed(seed): t = seed_type(seed) return is_any_2fa_seed_type(t)
def test_seed_type(self): for idx, (seed_words, _type) in enumerate(self.mnemonics): with self.subTest(msg=f"seed_type_subcase_{idx}", seed_words=seed_words): self.assertEqual(_type, seed_type(seed_words), msg=seed_words)
def test_seed_type(self): for seed_words, _type in self.mnemonics: self.assertEqual(_type, seed_type(seed_words), msg=seed_words)
def on_edit(self): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) if self.seed_type == 'bip39': from electrum.keystore import bip39_is_checksum_valid from electrum.mnemonic import Wordlist, filenames lang = '' for type, file in filenames.items(): word_list = Wordlist.from_file(file) is_checksum, is_wordlist = bip39_is_checksum_valid(s, wordlist=word_list) if is_wordlist: lang = type break status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39 - ' + lang + ' (%s)'%status if lang and lang != self.lang: if lang == 'en': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = 'en' else: self.wordlist = list(Mnemonic(lang).wordlist) self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = lang elif self.seed_type == 'slip39': self.slip39_mnemonics[self.slip39_mnemonic_index] = s try: slip39.decode_mnemonic(s) except slip39.Slip39Error as e: share_status = str(e) current_mnemonic_invalid = True else: share_status = _('Valid.') current_mnemonic_invalid = False label = _('SLIP39 share') + ' #%d: %s' % (self.slip39_mnemonic_index + 1, share_status) # No need to process mnemonics if the current mnemonic remains invalid after editing. if not (self.slip39_current_mnemonic_invalid and current_mnemonic_invalid): self.slip39_seed, seed_status = slip39.process_mnemonics(self.slip39_mnemonics) self.seed_status.setText(seed_status) self.slip39_current_mnemonic_invalid = current_mnemonic_invalid b = self.slip39_seed is not None self.update_share_buttons() else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()