1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-24 15:38:22 +00:00

core/slip39: generate and store slip39 identifier during reset

This commit is contained in:
Tomas Susanka 2019-06-25 14:36:30 +02:00
parent 07de336586
commit 10e5ec6135
5 changed files with 54 additions and 38 deletions

View File

@ -3,16 +3,13 @@ from trezor.crypto import slip39
from apps.common import mnemonic, storage 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. Generates new Shamir backup for 'master_secret'. Multiple groups are not yet supported.
""" """
identifier, group_mnemonics = slip39.generate_single_group_mnemonics_from_data( return slip39.generate_single_group_mnemonics_from_data(
master_secret, threshold, count 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(): def get_type():

View File

@ -1,5 +1,5 @@
from trezor import config, wire 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 import MessageType
from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.EntropyRequest import EntropyRequest
from trezor.messages.Success import Success from trezor.messages.Success import Success
@ -38,6 +38,10 @@ async def reset_device(ctx, msg):
ext_entropy = entropy_ack.entropy ext_entropy = entropy_ack.entropy
secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength) 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? # should we back up the wallet now?
if not msg.no_backup and not msg.skip_backup: if not msg.no_backup and not msg.skip_backup:
if not await layout.confirm_backup(ctx): if not await layout.confirm_backup(ctx):

View File

@ -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.""" """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") 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( def generate_single_group_mnemonics_from_data(
master_secret, master_secret,
identifier,
threshold, threshold,
count, count,
passphrase=b"", passphrase=b"",
iteration_exponent=DEFAULT_ITERATION_EXPONENT, iteration_exponent=DEFAULT_ITERATION_EXPONENT,
) -> (int, list): ) -> list:
identifier, mnemonics = generate_mnemonics_from_data( return generate_mnemonics_from_data(
master_secret, 1, [(threshold, count)], passphrase, iteration_exponent master_secret,
) identifier,
return identifier, mnemonics[0] 1,
[(threshold, count)],
passphrase,
iteration_exponent,
)[0]
def generate_mnemonics_from_data( def generate_mnemonics_from_data(
master_secret, master_secret,
identifier,
group_threshold, group_threshold,
groups, groups,
passphrase=b"", passphrase=b"",
iteration_exponent=DEFAULT_ITERATION_EXPONENT, iteration_exponent=DEFAULT_ITERATION_EXPONENT,
) -> (int, list): ) -> list:
""" """
Splits a master secret into mnemonic shares using Shamir's secret sharing scheme. 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 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 :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 is the number of shares to generate for the group and member_threshold is the number of members required to
reconstruct the group secret. reconstruct the group secret.
:type groups: List of pairs of integers. :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. :param passphrase: The passphrase used to encrypt the master secret.
:type passphrase: Array of bytes. :type passphrase: Array of bytes.
:param int iteration_exponent: The iteration exponent. :param int iteration_exponent: The iteration exponent.
@ -509,8 +516,6 @@ def generate_mnemonics_from_data(
:rtype: int. :rtype: int.
""" """
identifier = _generate_random_identifier()
if len(master_secret) * 8 < _MIN_STRENGTH_BITS: if len(master_secret) * 8 < _MIN_STRENGTH_BITS:
raise ValueError( raise ValueError(
"The length of the master secret ({} bytes) must be at least {} bytes.".format( "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) mnemonics.append(group_mnemonics)
return identifier, mnemonics return mnemonics
def combine_mnemonics(mnemonics): def combine_mnemonics(mnemonics):

View File

@ -177,7 +177,7 @@ class Slip39Keyboard(ui.Layout):
def is_input_final(self) -> bool: def is_input_final(self) -> bool:
# returns True if mask has exactly one bit set to 1 or is 0 # 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: def check_mask(self, index: int) -> bool:
return bool((1 << (index - 1)) & self.mask) return bool((1 << (index - 1)) & self.mask)

View File

@ -26,23 +26,27 @@ class TestCryptoSlip39(unittest.TestCase):
def test_basic_sharing_random(self): def test_basic_sharing_random(self):
ms = random.bytes(32) 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] mnemonics = mnemonics[0]
self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]), slip39.combine_mnemonics(mnemonics[2:])) self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]), slip39.combine_mnemonics(mnemonics[2:]))
def test_basic_sharing_fixed(self): 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] mnemonics = mnemonics[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[:3]) identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[:3])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS) 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) self.assertEqual(slip39.combine_mnemonics(mnemonics[1:4])[2], ems)
with self.assertRaises(slip39.MnemonicError): with self.assertRaises(slip39.MnemonicError):
slip39.combine_mnemonics(mnemonics[1:3]) slip39.combine_mnemonics(mnemonics[1:3])
def test_passphrase(self): 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] mnemonics = mnemonics[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
@ -50,13 +54,15 @@ class TestCryptoSlip39(unittest.TestCase):
def test_iteration_exponent(self): 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] mnemonics = mnemonics[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), 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] mnemonics = mnemonics[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4]) identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS) self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
@ -67,8 +73,9 @@ class TestCryptoSlip39(unittest.TestCase):
group_threshold = 2 group_threshold = 2
group_sizes = (5, 3, 5, 1) group_sizes = (5, 3, 5, 1)
member_thresholds = (3, 2, 2, 1) member_thresholds = (3, 2, 2, 1)
_, mnemonics = slip39.generate_mnemonics_from_data( identifier = slip39.generate_random_identifier()
self.MS, group_threshold, list(zip(member_thresholds, group_sizes)) mnemonics = slip39.generate_mnemonics_from_data(
self.MS, identifier, group_threshold, list(zip(member_thresholds, group_sizes))
) )
# Test all valid combinations of mnemonics. # Test all valid combinations of mnemonics.
@ -99,8 +106,9 @@ class TestCryptoSlip39(unittest.TestCase):
group_threshold = 1 group_threshold = 1
group_sizes = (5, 3, 5, 1) group_sizes = (5, 3, 5, 1)
member_thresholds = (3, 2, 2, 1) member_thresholds = (3, 2, 2, 1)
_, mnemonics = slip39.generate_mnemonics_from_data( identifier = slip39.generate_random_identifier()
self.MS, group_threshold, list(zip(member_thresholds, group_sizes)) mnemonics = slip39.generate_mnemonics_from_data(
self.MS, identifier, group_threshold, list(zip(member_thresholds, group_sizes))
) )
# Test all valid combinations of mnemonics. # Test all valid combinations of mnemonics.
@ -114,41 +122,43 @@ class TestCryptoSlip39(unittest.TestCase):
def test_all_groups_exist(self): def test_all_groups_exist(self):
for group_threshold in (1, 2, 5): for group_threshold in (1, 2, 5):
_, mnemonics = slip39.generate_mnemonics_from_data( identifier = slip39.generate_random_identifier()
self.MS, group_threshold, [(3, 5), (1, 1), (2, 3), (2, 5), (3, 5)] 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(mnemonics), 5)
self.assertEqual(len(sum(mnemonics, [])), 19) self.assertEqual(len(sum(mnemonics, [])), 19)
def test_invalid_sharing(self): def test_invalid_sharing(self):
identifier = slip39.generate_random_identifier()
# Short master secret. # Short master secret.
with self.assertRaises(ValueError): 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. # Odd length master secret.
with self.assertRaises(ValueError): 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. # Group threshold exceeds number of groups.
with self.assertRaises(ValueError): 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. # Invalid group threshold.
with self.assertRaises(ValueError): 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. # Member threshold exceeds number of members.
with self.assertRaises(ValueError): 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. # Invalid member threshold.
with self.assertRaises(ValueError): 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. # Group with multiple members and threshold 1.
with self.assertRaises(ValueError): 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): def test_vectors(self):