From 10e5ec61359ac7f91464d8a07db94498a111a9d7 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 25 Jun 2019 14:36:30 +0200 Subject: [PATCH] core/slip39: generate and store slip39 identifier during reset --- core/src/apps/common/mnemonic/slip39.py | 9 ++--- core/src/apps/management/reset_device.py | 6 +++- core/src/trezor/crypto/slip39.py | 29 ++++++++------- core/src/trezor/ui/mnemonic_slip39.py | 2 +- core/tests/test_trezor.crypto.slip39.py | 46 ++++++++++++++---------- 5 files changed, 54 insertions(+), 38 deletions(-) diff --git a/core/src/apps/common/mnemonic/slip39.py b/core/src/apps/common/mnemonic/slip39.py index cf0d4a679..954edb4c7 100644 --- a/core/src/apps/common/mnemonic/slip39.py +++ b/core/src/apps/common/mnemonic/slip39.py @@ -3,16 +3,13 @@ from trezor.crypto import slip39 from apps.common import mnemonic, storage -def generate_from_secret(master_secret: bytes, count: int, threshold: int) -> str: +def generate_from_secret(master_secret: bytes, count: int, threshold: int) -> list: """ Generates new Shamir backup for 'master_secret'. Multiple groups are not yet supported. """ - identifier, group_mnemonics = slip39.generate_single_group_mnemonics_from_data( - master_secret, threshold, count + return slip39.generate_single_group_mnemonics_from_data( + master_secret, storage.get_slip39_identifier(), threshold, count ) - storage.set_slip39_iteration_exponent(slip39.DEFAULT_ITERATION_EXPONENT) - storage.set_slip39_identifier(identifier) - return group_mnemonics def get_type(): diff --git a/core/src/apps/management/reset_device.py b/core/src/apps/management/reset_device.py index 36f552dda..28e8cacb6 100644 --- a/core/src/apps/management/reset_device.py +++ b/core/src/apps/management/reset_device.py @@ -1,5 +1,5 @@ from trezor import config, wire -from trezor.crypto import bip39, hashlib, random +from trezor.crypto import bip39, hashlib, random, slip39 from trezor.messages import MessageType from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.Success import Success @@ -38,6 +38,10 @@ async def reset_device(ctx, msg): 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): diff --git a/core/src/trezor/crypto/slip39.py b/core/src/trezor/crypto/slip39.py index 866ee783f..ea1247fdc 100644 --- a/core/src/trezor/crypto/slip39.py +++ b/core/src/trezor/crypto/slip39.py @@ -464,7 +464,7 @@ def _decode_mnemonics(mnemonics): ) -def _generate_random_identifier() -> int: +def generate_random_identifier() -> int: """Returns a randomly generated integer in the range 0, ... , 2**_ID_LENGTH_BITS - 1.""" identifier = int.from_bytes(random.bytes(bits_to_bytes(_ID_LENGTH_BITS)), "big") @@ -473,33 +473,40 @@ def _generate_random_identifier() -> int: def generate_single_group_mnemonics_from_data( master_secret, + identifier, threshold, count, passphrase=b"", iteration_exponent=DEFAULT_ITERATION_EXPONENT, -) -> (int, list): - identifier, mnemonics = generate_mnemonics_from_data( - master_secret, 1, [(threshold, count)], passphrase, iteration_exponent - ) - return identifier, mnemonics[0] +) -> list: + return generate_mnemonics_from_data( + master_secret, + identifier, + 1, + [(threshold, count)], + passphrase, + iteration_exponent, + )[0] def generate_mnemonics_from_data( master_secret, + identifier, group_threshold, groups, passphrase=b"", iteration_exponent=DEFAULT_ITERATION_EXPONENT, -) -> (int, list): +) -> list: """ Splits a master secret into mnemonic shares using Shamir's secret sharing scheme. + :param master_secret: The master secret to split. + :type master_secret: Array of bytes. + :param int identifier :param int group_threshold: The number of groups required to reconstruct the master secret. :param groups: A list of (member_threshold, member_count) pairs for each group, where member_count is the number of shares to generate for the group and member_threshold is the number of members required to reconstruct the group secret. :type groups: List of pairs of integers. - :param master_secret: The master secret to split. - :type master_secret: Array of bytes. :param passphrase: The passphrase used to encrypt the master secret. :type passphrase: Array of bytes. :param int iteration_exponent: The iteration exponent. @@ -509,8 +516,6 @@ def generate_mnemonics_from_data( :rtype: int. """ - identifier = _generate_random_identifier() - if len(master_secret) * 8 < _MIN_STRENGTH_BITS: raise ValueError( "The length of the master secret ({} bytes) must be at least {} bytes.".format( @@ -570,7 +575,7 @@ def generate_mnemonics_from_data( ) ) mnemonics.append(group_mnemonics) - return identifier, mnemonics + return mnemonics def combine_mnemonics(mnemonics): diff --git a/core/src/trezor/ui/mnemonic_slip39.py b/core/src/trezor/ui/mnemonic_slip39.py index 1976e0d68..f5b7b4da5 100644 --- a/core/src/trezor/ui/mnemonic_slip39.py +++ b/core/src/trezor/ui/mnemonic_slip39.py @@ -177,7 +177,7 @@ class Slip39Keyboard(ui.Layout): def is_input_final(self) -> bool: # returns True if mask has exactly one bit set to 1 or is 0 - return not (self.mask & (self.mask-1)) + return not (self.mask & (self.mask - 1)) def check_mask(self, index: int) -> bool: return bool((1 << (index - 1)) & self.mask) diff --git a/core/tests/test_trezor.crypto.slip39.py b/core/tests/test_trezor.crypto.slip39.py index 057203daa..59c43719c 100644 --- a/core/tests/test_trezor.crypto.slip39.py +++ b/core/tests/test_trezor.crypto.slip39.py @@ -26,23 +26,27 @@ class TestCryptoSlip39(unittest.TestCase): def test_basic_sharing_random(self): ms = random.bytes(32) - _, mnemonics = slip39.generate_mnemonics_from_data(ms, 1, [(3, 5)]) + identifier = slip39.generate_random_identifier() + mnemonics = slip39.generate_mnemonics_from_data(ms, identifier, 1, [(3, 5)]) mnemonics = mnemonics[0] self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]), slip39.combine_mnemonics(mnemonics[2:])) def test_basic_sharing_fixed(self): - _, mnemonics = slip39.generate_mnemonics_from_data(self.MS, 1, [(3, 5)]) + 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_passphrase(self): - _, mnemonics = slip39.generate_mnemonics_from_data(self.MS, 1, [(3, 5)], b"TREZOR") + identifier = slip39.generate_random_identifier() + mnemonics = slip39.generate_mnemonics_from_data(self.MS, identifier, 1, [(3, 5)], b"TREZOR") mnemonics = mnemonics[0] identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) @@ -50,13 +54,15 @@ class TestCryptoSlip39(unittest.TestCase): def test_iteration_exponent(self): - _, mnemonics = slip39.generate_mnemonics_from_data(self.MS, 1, [(3, 5)], b"TREZOR", 1) + 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) - _, mnemonics = slip39.generate_mnemonics_from_data(self.MS, 1, [(3, 5)], b"TREZOR", 2) + 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) @@ -67,8 +73,9 @@ class TestCryptoSlip39(unittest.TestCase): group_threshold = 2 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) - _, mnemonics = slip39.generate_mnemonics_from_data( - self.MS, group_threshold, list(zip(member_thresholds, group_sizes)) + 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. @@ -99,8 +106,9 @@ class TestCryptoSlip39(unittest.TestCase): group_threshold = 1 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) - _, mnemonics = slip39.generate_mnemonics_from_data( - self.MS, group_threshold, list(zip(member_thresholds, group_sizes)) + 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. @@ -114,41 +122,43 @@ class TestCryptoSlip39(unittest.TestCase): def test_all_groups_exist(self): for group_threshold in (1, 2, 5): - _, mnemonics = slip39.generate_mnemonics_from_data( - self.MS, group_threshold, [(3, 5), (1, 1), (2, 3), (2, 5), (3, 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_invalid_sharing(self): + identifier = slip39.generate_random_identifier() # Short master secret. with self.assertRaises(ValueError): - slip39.generate_mnemonics_from_data(self.MS[:14], 1, [(2, 3)]) + 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", 1, [(2, 3)]) + 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, 3, [(3, 5), (2, 5)]) + 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, 0, [(3, 5), (2, 5)]) + 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, 2, [(3, 2), (2, 5)]) + 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, 2, [(0, 2), (2, 5)]) + 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, 2, [(3, 5), (1, 3), (2, 5)]) + slip39.generate_mnemonics_from_data(self.MS, identifier, 2, [(3, 5), (1, 3), (2, 5)]) def test_vectors(self):