core/slip39: generate and store slip39 identifier during reset

pull/263/head
Tomas Susanka 5 years ago
parent 07de336586
commit 10e5ec6135

@ -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():

@ -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):

@ -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):

@ -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)

@ -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):

Loading…
Cancel
Save