def test_iteration_exponent(self): identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(1, [(3, 5)], identifier, 1, self.EMS) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.recover_ems(mnemonics[1:4]) self.assertEqual(ems, self.EMS) identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(1, [(3, 5)], identifier, 2, self.EMS) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.recover_ems(mnemonics[1:4]) self.assertEqual(ems, self.EMS)
def test_iteration_exponent(self): identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data(self.MS, identifier, 1, [(3, 5)], b"TREZOR", 1) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS) identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data(self.MS, identifier, 1, [(3, 5)], b"TREZOR", 2) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
def test_group_sharing(self): group_threshold = 2 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( self.MS, identifier, group_threshold, list(zip(member_thresholds, group_sizes)) ) # Test all valid combinations of mnemonics. for groups in combinations(zip(mnemonics, member_thresholds), group_threshold): for group1_subset in combinations(groups[0][0], groups[0][1]): for group2_subset in combinations(groups[1][0], groups[1][1]): mnemonic_subset = list(group1_subset + group2_subset) random.shuffle(mnemonic_subset) identifier, exponent, ems = slip39.combine_mnemonics(mnemonic_subset) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS) # Minimal sets of mnemonics. identifier, exponent, ems = slip39.combine_mnemonics([mnemonics[2][0], mnemonics[2][2], mnemonics[3][0]]) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS) self.assertEqual(slip39.combine_mnemonics([mnemonics[2][3], mnemonics[3][0], mnemonics[2][4]])[2], ems) # One complete group and one incomplete group out of two groups required. with self.assertRaises(slip39.MnemonicError): slip39.combine_mnemonics(mnemonics[0][2:] + [mnemonics[1][0]]) # One group of two required. with self.assertRaises(slip39.MnemonicError): slip39.combine_mnemonics(mnemonics[0][1:4])
def test_invalid_sharing(self): identifier = slip39.generate_random_identifier() # Short master secret. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS[:14], identifier, 1, [(2, 3)]) # Odd length master secret. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS + b"X", identifier,1, [(2, 3)]) # Group threshold exceeds number of groups. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS, identifier, 3, [(3, 5), (2, 5)]) # Invalid group threshold. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS, identifier, 0, [(3, 5), (2, 5)]) # Member threshold exceeds number of members. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS, identifier, 2, [(3, 2), (2, 5)]) # Invalid member threshold. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS, identifier, 2, [(0, 2), (2, 5)]) # Group with multiple members and threshold 1. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.MS, identifier, 2, [(3, 5), (1, 3), (2, 5)])
def test_invalid_sharing(self): identifier = slip39.generate_random_identifier() # Group threshold exceeds number of groups. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.EMS, identifier, 3, [(3, 5), (2, 5)], 1) # Invalid group threshold. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.EMS, identifier, 0, [(3, 5), (2, 5)], 1) # Member threshold exceeds number of members. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.EMS, identifier, 2, [(3, 2), (2, 5)], 1) # Invalid member threshold. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.EMS, identifier, 2, [(0, 2), (2, 5)], 1) # Group with multiple members and threshold 1. with self.assertRaises(ValueError): slip39.generate_mnemonics_from_data(self.EMS, identifier, 2, [(3, 5), (1, 3), (2, 5)], 1)
def test_basic_sharing_random(self): ems = random.bytes(32) identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(1, [(3, 5)], identifier, 1, ems) mnemonics = mnemonics[0] self.assertEqual(slip39.recover_ems(mnemonics[:3]), slip39.recover_ems(mnemonics[2:]))
def test_all_groups_exist(self): for group_threshold in (1, 2, 5): identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( self.MS, identifier, group_threshold, [(3, 5), (1, 1), (2, 3), (2, 5), (3, 5)] ) self.assertEqual(len(mnemonics), 5) self.assertEqual(len(sum(mnemonics, [])), 19)
def test_basic_sharing_random(self): ems = random.bytes(32) identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( ems, identifier, 1, [(3, 5)], 1) mnemonics = mnemonics[0] self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]), slip39.combine_mnemonics(mnemonics[2:]))
def test_all_groups_exist(self): for group_threshold in (1, 2, 5): identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(group_threshold, [(3, 5), (1, 1), (2, 3), (2, 5), (3, 5)], identifier, 1, self.EMS) self.assertEqual(len(mnemonics), 5) self.assertEqual(len(sum(mnemonics, [])), 19)
def test_iteration_exponent(self): identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( self.EMS, identifier, 1, [(3, 5)], 1) mnemonics = mnemonics[0] identifier, exponent, ems, group_count = slip39.combine_mnemonics( mnemonics[1:4]) self.assertEqual(ems, self.EMS) self.assertEqual(group_count, 1) identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( self.EMS, identifier, 1, [(3, 5)], 2) mnemonics = mnemonics[0] identifier, exponent, ems, group_count = slip39.combine_mnemonics( mnemonics[1:4]) self.assertEqual(group_count, 1) self.assertEqual(ems, self.EMS)
def test_basic_sharing_fixed(self): generated_identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data(self.MS, generated_identifier, 1, [(3, 5)]) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[:3]) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS) self.assertEqual(generated_identifier, identifier) self.assertEqual(slip39.combine_mnemonics(mnemonics[1:4])[2], ems) with self.assertRaises(slip39.MnemonicError): slip39.combine_mnemonics(mnemonics[1:3])
def test_basic_sharing_fixed(self): generated_identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(1, [(3, 5)], generated_identifier, 1, self.EMS) mnemonics = mnemonics[0] identifier, exponent, ems = slip39.recover_ems(mnemonics[:3]) self.assertEqual(ems, self.EMS) self.assertEqual(generated_identifier, identifier) self.assertEqual(slip39.recover_ems(mnemonics[1:4])[2], ems) with self.assertRaises(slip39.MnemonicError): slip39.recover_ems(mnemonics[1:3])
def test_group_sharing_threshold_1(self): group_threshold = 1 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) identifier = slip39.generate_random_identifier() mnemonics = slip39.generate_mnemonics_from_data( self.MS, identifier, group_threshold, list(zip(member_thresholds, group_sizes)) ) # Test all valid combinations of mnemonics. for group, threshold in zip(mnemonics, member_thresholds): for group_subset in combinations(group, threshold): mnemonic_subset = list(group_subset) random.shuffle(mnemonic_subset) identifier, exponent, ems = slip39.combine_mnemonics(mnemonic_subset) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
def test_group_sharing_threshold_1(self): group_threshold = 1 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) identifier = slip39.generate_random_identifier() mnemonics = slip39.split_ems(group_threshold, list(zip(member_thresholds, group_sizes)), identifier, 1, self.EMS) # Test all valid combinations of mnemonics. for group, threshold in zip(mnemonics, member_thresholds): for group_subset in combinations(group, threshold): mnemonic_subset = list(group_subset) random.shuffle(mnemonic_subset) identifier, exponent, ems = slip39.recover_ems(mnemonic_subset) self.assertEqual(ems, self.EMS)
async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success: # validate parameters and device state _validate_reset_device(msg) is_slip39_simple = msg.backup_type == ResetDeviceBackupType.Slip39_Single_Group # make sure user knows he's setting up a new wallet await _show_reset_device_warning(ctx, is_slip39_simple) # request new PIN if msg.pin_protection: newpin = await request_pin_confirm(ctx) else: newpin = "" # generate and display internal entropy int_entropy = random.bytes(32) if __debug__: debug.reset_internal_entropy = int_entropy if msg.display_random: await layout.show_internal_entropy(ctx, int_entropy) # request external entropy and compute the master secret entropy_ack = await ctx.call(EntropyRequest(), EntropyAck) ext_entropy = entropy_ack.entropy # For SLIP-39 this is the Encrypted Master Secret secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength) if is_slip39_simple: storage.device.set_slip39_identifier( slip39.generate_random_identifier()) storage.device.set_slip39_iteration_exponent( slip39.DEFAULT_ITERATION_EXPONENT) # should we back up the wallet now? if not msg.no_backup and not msg.skip_backup: if not await layout.confirm_backup(ctx): if not await layout.confirm_backup_again(ctx): msg.skip_backup = True # generate and display backup information for the master secret if not msg.no_backup and not msg.skip_backup: if is_slip39_simple: await backup_slip39_wallet(ctx, secret) else: await backup_bip39_wallet(ctx, secret) # write PIN into storage if not config.change_pin(pin_to_int(""), pin_to_int(newpin)): raise wire.ProcessError("Could not change PIN") # write settings and master secret into storage storage.device.load_settings(label=msg.label, use_passphrase=msg.passphrase_protection) if is_slip39_simple: storage.device.store_mnemonic_secret( secret, # this is the EMS in SLIP-39 terminology mnemonic.TYPE_SLIP39, needs_backup=msg.skip_backup, no_backup=msg.no_backup, ) else: # in BIP-39 we store mnemonic string instead of the secret storage.device.store_mnemonic_secret( bip39.from_data(secret).encode(), mnemonic.TYPE_BIP39, needs_backup=msg.skip_backup, no_backup=msg.no_backup, ) # if we backed up the wallet, show success message if not msg.no_backup and not msg.skip_backup: await layout.show_backup_success(ctx) return Success(message="Initialized")
async def reset_device(ctx, msg): # validate parameters and device state _validate_reset_device(msg) # make sure user knows he's setting up a new wallet await layout.show_reset_device_warning(ctx, msg.slip39) # request new PIN if msg.pin_protection: newpin = await request_pin_confirm(ctx) else: newpin = "" # generate and display internal entropy int_entropy = random.bytes(32) if __debug__: debug.reset_internal_entropy = int_entropy if msg.display_random: await layout.show_internal_entropy(ctx, int_entropy) # request external entropy and compute the master secret entropy_ack = await ctx.call(EntropyRequest(), MessageType.EntropyAck) ext_entropy = entropy_ack.entropy secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength) if msg.slip39: storage.set_slip39_identifier(slip39.generate_random_identifier()) storage.set_slip39_iteration_exponent( slip39.DEFAULT_ITERATION_EXPONENT) # should we back up the wallet now? if not msg.no_backup and not msg.skip_backup: if not await layout.confirm_backup(ctx): if not await layout.confirm_backup_again(ctx): msg.skip_backup = True # generate and display backup information for the master secret if not msg.no_backup and not msg.skip_backup: if msg.slip39: await backup_slip39_wallet(ctx, secret) else: await backup_bip39_wallet(ctx, secret) # write PIN into storage if not config.change_pin(pin_to_int(""), pin_to_int(newpin)): raise wire.ProcessError("Could not change PIN") # write settings and master secret into storage storage.load_settings(label=msg.label, use_passphrase=msg.passphrase_protection) if msg.slip39: mnemonic.slip39.store(secret=secret, needs_backup=msg.skip_backup, no_backup=msg.no_backup) else: # in BIP-39 we store mnemonic string instead of the secret mnemonic.bip39.store( secret=bip39.from_data(secret).encode(), needs_backup=msg.skip_backup, no_backup=msg.no_backup, ) # if we backed up the wallet, show success message if not msg.no_backup and not msg.skip_backup: await layout.show_backup_success(ctx) return Success(message="Initialized")
async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success: # validate parameters and device state _validate_reset_device(msg) # make sure user knows they're setting up a new wallet if msg.backup_type == BackupType.Slip39_Basic: prompt = "Create a new wallet\nwith Shamir Backup?" elif msg.backup_type == BackupType.Slip39_Advanced: prompt = "Create a new wallet\nwith Super Shamir?" else: prompt = "Do you want to create\na new wallet?" await confirm_reset_device(ctx, prompt) await LoadingAnimation() # wipe storage to make sure the device is in a clear state storage.reset() # request and set new PIN if msg.pin_protection: newpin = await request_pin_confirm(ctx) if not config.change_pin("", newpin, None, None): raise wire.ProcessError("Failed to set PIN") # generate and display internal entropy int_entropy = random.bytes(32) if __debug__: debug.reset_internal_entropy = int_entropy if msg.display_random: await layout.show_internal_entropy(ctx, int_entropy) # request external entropy and compute the master secret entropy_ack = await ctx.call(EntropyRequest(), EntropyAck) ext_entropy = entropy_ack.entropy # For SLIP-39 this is the Encrypted Master Secret secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength) # Check backup type, perform type-specific handling if msg.backup_type == BackupType.Bip39: # in BIP-39 we store mnemonic string instead of the secret secret = bip39.from_data(secret).encode() elif msg.backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced): # generate and set SLIP39 parameters storage.device.set_slip39_identifier(slip39.generate_random_identifier()) storage.device.set_slip39_iteration_exponent(slip39.DEFAULT_ITERATION_EXPONENT) else: # Unknown backup type. raise RuntimeError # If either of skip_backup or no_backup is specified, we are not doing backup now. # Otherwise, we try to do it. perform_backup = not msg.no_backup and not msg.skip_backup # If doing backup, ask the user to confirm. if perform_backup: perform_backup = await confirm_backup(ctx) # generate and display backup information for the master secret if perform_backup: await backup_seed(ctx, msg.backup_type, secret) # write settings and master secret into storage if msg.label is not None: storage.device.set_label(msg.label) storage.device.set_passphrase_enabled(bool(msg.passphrase_protection)) storage.device.store_mnemonic_secret( secret, # for SLIP-39, this is the EMS msg.backup_type, needs_backup=not perform_backup, no_backup=msg.no_backup, ) # if we backed up the wallet, show success message if perform_backup: await layout.show_backup_success(ctx) return Success(message="Initialized")
async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success: # validate parameters and device state _validate_reset_device(msg) # make sure user knows they're setting up a new wallet await layout.show_reset_device_warning(ctx, msg.backup_type) # request new PIN if msg.pin_protection: newpin = await request_pin_confirm(ctx) else: newpin = "" # generate and display internal entropy int_entropy = random.bytes(32) if __debug__: debug.reset_internal_entropy = int_entropy if msg.display_random: await layout.show_internal_entropy(ctx, int_entropy) # request external entropy and compute the master secret entropy_ack = await ctx.call(EntropyRequest(), EntropyAck) ext_entropy = entropy_ack.entropy # For SLIP-39 this is the Encrypted Master Secret secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength) if msg.backup_type != BackupType.Bip39: storage.device.set_slip39_identifier( slip39.generate_random_identifier()) storage.device.set_slip39_iteration_exponent( slip39.DEFAULT_ITERATION_EXPONENT) # If either of skip_backup or no_backup is specified, we are not doing backup now. # Otherwise, we try to do it. perform_backup = not msg.no_backup and not msg.skip_backup # If doing backup, ask the user to confirm. if perform_backup: perform_backup = await layout.confirm_backup(ctx) # Check backup type, convert seed accordingly if msg.backup_type == BackupType.Bip39: # in BIP-39 we store mnemonic string instead of the secret secret = bip39.from_data(secret).encode() elif msg.backup_type not in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced): # Unknown backup type. # This check might seem superfluous, because we are checking # in `_validate_reset_device` already, however, this is critical part, # so just to make sure. raise RuntimeError # generate and display backup information for the master secret if perform_backup: await backup_seed(ctx, msg.backup_type, secret) # write PIN into storage if not config.change_pin(pin_to_int(""), pin_to_int(newpin), None, None): raise wire.ProcessError("Could not change PIN") # write settings and master secret into storage storage.device.load_settings(label=msg.label, use_passphrase=msg.passphrase_protection) storage.device.store_mnemonic_secret( secret, # for SLIP-39, this is the EMS msg.backup_type, needs_backup=not perform_backup, no_backup=msg.no_backup, ) # if we backed up the wallet, show success message if perform_backup: await layout.show_backup_success(ctx) return Success(message="Initialized")