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:
parent
07de336586
commit
10e5ec6135
@ -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():
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user