def test_nopin_nopassphrase(self): mnemonic = self.mnemonic12 ret = self.client.call_raw( proto.RecoveryDevice(word_count=12, passphrase_protection=False, pin_protection=False, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) # Reminder UI assert isinstance(ret, proto.ButtonRequest) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck()) mnemonic_words = mnemonic.split(' ') for index, word in enumerate(mnemonic_words): for character in word: self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw( proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word( ) if word == auto_completed: if len(mnemonic_words) != index + 1: ret = self.client.call_raw( proto.CharacterAck(character=' ')) break # Send final ack self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(done=True)) # Workflow succesfully ended self.assertIsInstance(ret, proto.Success) # Mnemonic is the same self.client.init_device() self.assertEqual(self.client.debug.read_mnemonic(), self.mnemonic12) self.assertFalse(self.client.features.pin_protection) self.assertFalse(self.client.features.passphrase_protection) # Do passphrase-protected action, PassphraseRequest should NOT be raised resp = self.client.call_raw(proto.Ping(passphrase_protection=True)) self.assertIsInstance(resp, proto.Success) # Do PIN-protected action, PinRequest should NOT be raised resp = self.client.call_raw(proto.Ping(pin_protection=True)) self.assertIsInstance(resp, proto.Success)
def check_n_words(n): ret = self.client.call_raw( proto.RecoveryDevice(word_count=12, passphrase_protection=False, pin_protection=False, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) # Reminder UI assert isinstance(ret, proto.ButtonRequest) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck()) mnemonic_words = ['all'] * n for index, word in enumerate(mnemonic_words): if index >= 12: self.assertIsInstance(ret, proto.Success) self.assertEndsWith(ret.message, "Device recovered") return for character in word: self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw( proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word( ) if word == auto_completed: if len(mnemonic_words) != index + 1: ret = self.client.call_raw( proto.CharacterAck(character=' ')) break # Send final ack self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(done=True)) if n == 12: self.assertIsInstance(ret, proto.Success) self.assertEndsWith(ret.message, "Device recovered") else: self.assertIsInstance(ret, proto.Failure) self.assertEndsWith(ret.message, "words entered")
def test_vuln1971(self): self.setup_mnemonic_allallall() self.assertEquals( self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), True, None, script_type=proto_types.SPENDP2SHWITNESS), '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX') # Previously, there weren't good checks on the expected state of the # recovery cipher state machine, which led to this case triggering an # out of bounds memory access, as well as setting the device's mnemonic # to "". self.client.call_raw(proto.CharacterAck(done=True)) # The emulator, with ASan enabled, crashes on the out of bounds # memory access before even getting to the part where the empty # mnemonic is pushed into storage, but for posterity, let's make sure # we still get the correct address afterward: self.assertEquals( self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), True, None, script_type=proto_types.SPENDP2SHWITNESS), '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX')
def test_too_many_characters(self): ret = self.client.call_raw( proto.RecoveryDevice(word_count=12, passphrase_protection=False, pin_protection=False, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) # Reminder UI assert isinstance(ret, proto.ButtonRequest) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck()) mnemonic_words = ['all'] * 100 for index, word in enumerate(mnemonic_words): for character in word: if isinstance(ret, proto.Failure): self.assertEndsWith(ret.message, "Too many words entered") return self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw( proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word( ) if word == auto_completed: if len(mnemonic_words) != index + 1: ret = self.client.call_raw( proto.CharacterAck(character=' ')) break # Shouldn't ever get here, assuming the test worked self.assertEquals(True, False)
def test_character_fail(self): ret = self.client.call_raw(proto.RecoveryDevice(word_count=12, passphrase_protection=False, pin_protection=False, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(character='1')) self.assertIsInstance(ret, proto.Failure)
def recovery_loop(self, mnemonic, result): ret = self.client.call_raw( proto.RecoveryDevice( word_count=12, passphrase_protection=False, pin_protection=False, label="label", language="english", enforce_wordlist=True, dry_run=True, use_character_cipher=True, ) ) for index, word in enumerate(mnemonic): for character in word: self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw(proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word() if word == auto_completed: if len(mnemonic) != index + 1: ret = self.client.call_raw(proto.CharacterAck(character=' ')) break self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(done=True)) assert isinstance(ret, proto.ButtonRequest) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck()) assert isinstance(ret, result)
def callback_CharacterRequest(self, msg): from keepkeylib import messages_pb2 as proto if self.character_request_first_pass: self.character_request_first_pass = False # format mnemonic for console input_label = f'WORD {msg.word_pos + 1}:' while True: cur_str = msg.character_pos * '*' character_ascii = self.request_character(cur_str, input_label) # capture escape if character_ascii in (3, 4): return proto.Cancel() if 65 <= character_ascii <= 90: character_ascii = ord(chr(character_ascii).lower()) if 97 <= character_ascii <= 122 and msg.character_pos != 4: # capture characters a-z character = chr(character_ascii).lower() return proto.CharacterAck(character=character) elif character_ascii == 32 and msg.word_pos < 23 and msg.character_pos >= 3: # capture spaces return proto.CharacterAck(character=' ') elif character_ascii == 8 or character_ascii == 127 \ and (msg.word_pos > 0 or msg.character_pos > 0): # capture backspaces return proto.CharacterAck(delete=True) elif character_ascii == 13 and msg.word_pos in (11, 17, 23): # capture returns return proto.CharacterAck(done=True)
def test_pin_passphrase(self): mnemonic = self.mnemonic12 ret = self.client.call_raw(proto.RecoveryDevice(word_count=12, passphrase_protection=True, pin_protection=True, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) self.assertIsInstance(ret, proto.PinMatrixRequest) # Enter PIN for first time pin_encoded = self.client.debug.encode_pin(self.pin6) ret = self.client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) self.assertIsInstance(ret, proto.PinMatrixRequest) # Enter PIN for second time pin_encoded = self.client.debug.encode_pin(self.pin6) ret = self.client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) mnemonic_words = mnemonic.split(' ') for index, word in enumerate(mnemonic_words): for character in word: self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw(proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word() if word == auto_completed: if len(mnemonic_words) != index + 1: ret = self.client.call_raw(proto.CharacterAck(character=' ')) break # Send final ack self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(done=True)) # Workflow succesfully ended self.assertIsInstance(ret, proto.Success) # Mnemonic is the same self.client.init_device() self.assertEqual(self.client.debug.read_mnemonic(), self.mnemonic12) self.assertTrue(self.client.features.pin_protection) self.assertTrue(self.client.features.passphrase_protection) # Do passphrase-protected action, PassphraseRequest should be raised resp = self.client.call_raw(proto.Ping(passphrase_protection=True)) self.assertIsInstance(resp, proto.PassphraseRequest) self.client.call_raw(proto.Cancel()) self.client.clear_session() # Do PIN-protected action, PinRequest should be raised resp = self.client.call_raw(proto.Ping(pin_protection=True)) self.assertIsInstance(resp, proto.PinMatrixRequest) self.client.call_raw(proto.Cancel())
def test_reset_and_recover(self): for strength in (128, 192, 256): external_entropy = self.client._get_local_entropy() ret = self.client.call_raw(proto.ResetDevice(display_random=False, strength=strength, passphrase_protection=False, pin_protection=False, language='english', label='test')) # Provide entropy self.assertIsInstance(ret, proto.EntropyRequest) resp = self.client.call_raw(proto.EntropyAck(entropy=external_entropy)) mnemonic = [] while isinstance(resp, proto.ButtonRequest): mnemonic.append(self.client.debug.read_reset_word()) self.client.debug.press_yes() resp = self.client.call_raw(proto.ButtonAck()) mnemonic = ' '.join(mnemonic) # wipe device ret = self.client.call_raw(proto.WipeDevice()) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck()) # recover devce ret = self.client.call_raw(proto.RecoveryDevice(word_count=(strength/32*3), passphrase_protection=False, pin_protection=False, label='label', language='english', enforce_wordlist=True, use_character_cipher=True)) mnemonic_words = mnemonic.split(' ') for index, word in enumerate(mnemonic_words): for character in word: self.assertIsInstance(ret, proto.CharacterRequest) cipher = self.client.debug.read_recovery_cipher() encoded_character = cipher[ord(character) - 97] ret = self.client.call_raw(proto.CharacterAck(character=encoded_character)) auto_completed = self.client.debug.read_recovery_auto_completed_word() if word == auto_completed: if len(mnemonic_words) != index + 1: ret = self.client.call_raw(proto.CharacterAck(character=' ')) break # Send final ack self.assertIsInstance(ret, proto.CharacterRequest) ret = self.client.call_raw(proto.CharacterAck(done=True)) # Workflow succesfully ended self.assertIsInstance(ret, proto.Success) self.client.init_device() self.assertEqual(self.client.debug.read_mnemonic(), mnemonic) # wipe device ret = self.client.call_raw(proto.WipeDevice()) self.client.debug.press_yes() ret = self.client.call_raw(proto.ButtonAck())