mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-13 08:50:56 +00:00
core/slip39: generate and store slip39 identifier during reset
This commit is contained in:
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…
Reference in New Issue
Block a user