2024-01-26 11:16:55 +00:00
|
|
|
from common import * # isort:skip
|
2023-06-28 10:58:54 +00:00
|
|
|
|
2019-04-12 09:41:32 +00:00
|
|
|
from slip39_vectors import vectors
|
2024-01-26 11:16:55 +00:00
|
|
|
from trezor.crypto import random, slip39
|
2019-04-12 09:41:32 +00:00
|
|
|
|
2022-09-15 10:57:17 +00:00
|
|
|
|
2019-05-03 16:28:14 +00:00
|
|
|
def combinations(iterable, r):
|
|
|
|
# Taken from https://docs.python.org/3.7/library/itertools.html#itertools.combinations
|
|
|
|
pool = tuple(iterable)
|
|
|
|
n = len(pool)
|
|
|
|
if r > n:
|
|
|
|
return
|
|
|
|
indices = list(range(r))
|
|
|
|
yield tuple(pool[i] for i in indices)
|
|
|
|
while True:
|
|
|
|
for i in reversed(range(r)):
|
|
|
|
if indices[i] != i + n - r:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
indices[i] += 1
|
2023-06-28 10:46:29 +00:00
|
|
|
for j in range(i + 1, r):
|
|
|
|
indices[j] = indices[j - 1] + 1
|
2019-05-03 16:28:14 +00:00
|
|
|
yield tuple(pool[i] for i in indices)
|
2019-04-12 09:41:32 +00:00
|
|
|
|
2023-06-28 10:46:29 +00:00
|
|
|
|
2019-04-12 09:41:32 +00:00
|
|
|
class TestCryptoSlip39(unittest.TestCase):
|
2019-08-12 10:27:02 +00:00
|
|
|
EMS = b"ABCDEFGHIJKLMNOP"
|
2019-04-12 19:17:47 +00:00
|
|
|
|
|
|
|
def test_basic_sharing_random(self):
|
2019-09-19 07:37:23 +00:00
|
|
|
ems = random.bytes(32)
|
2019-06-25 12:36:30 +00:00
|
|
|
identifier = slip39.generate_random_identifier()
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
|
|
|
mnemonics = slip39.split_ems(1, [(3, 5)], identifier, extendable, 1, ems)
|
|
|
|
mnemonics = mnemonics[0]
|
|
|
|
self.assertEqual(
|
|
|
|
slip39.recover_ems(mnemonics[:3]), slip39.recover_ems(mnemonics[2:])
|
|
|
|
)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2024-10-04 19:29:42 +00:00
|
|
|
def test_basic_sharing_extend(self):
|
|
|
|
identifier = slip39.generate_random_identifier()
|
|
|
|
for extendable in (False, True):
|
|
|
|
mnemonics = slip39.split_ems(1, [(2, 3)], identifier, extendable, 1, self.EMS)
|
|
|
|
mnemonics = mnemonics[0]
|
|
|
|
extended_mnemonics = slip39.extend_mnemonics(4, mnemonics[1:])
|
|
|
|
self.assertEqual(mnemonics, extended_mnemonics[:3])
|
|
|
|
for i in range(3):
|
|
|
|
self.assertEqual(slip39.recover_ems([extended_mnemonics[3], mnemonics[i]])[3], self.EMS)
|
|
|
|
|
2019-04-12 19:17:47 +00:00
|
|
|
def test_basic_sharing_fixed(self):
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
|
|
|
generated_identifier = slip39.generate_random_identifier()
|
|
|
|
mnemonics = slip39.split_ems(1, [(3, 5)], generated_identifier, extendable, 1, self.EMS)
|
|
|
|
mnemonics = mnemonics[0]
|
|
|
|
identifier, _, _, ems = slip39.recover_ems(mnemonics[:3])
|
|
|
|
self.assertEqual(ems, self.EMS)
|
|
|
|
self.assertEqual(generated_identifier, identifier)
|
|
|
|
self.assertEqual(slip39.recover_ems(mnemonics[1:4])[3], ems)
|
|
|
|
with self.assertRaises(slip39.MnemonicError):
|
|
|
|
slip39.recover_ems(mnemonics[1:3])
|
2019-04-12 19:17:47 +00:00
|
|
|
|
|
|
|
def test_iteration_exponent(self):
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
|
|
|
identifier = slip39.generate_random_identifier()
|
|
|
|
mnemonics = slip39.split_ems(1, [(3, 5)], identifier, extendable, 1, self.EMS)
|
|
|
|
mnemonics = mnemonics[0]
|
|
|
|
identifier, extendable, exponent, ems = slip39.recover_ems(mnemonics[1:4])
|
|
|
|
self.assertEqual(ems, self.EMS)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
identifier = slip39.generate_random_identifier()
|
|
|
|
mnemonics = slip39.split_ems(1, [(3, 5)], identifier, extendable, 2, self.EMS)
|
|
|
|
mnemonics = mnemonics[0]
|
|
|
|
identifier, extendable, exponent, ems = slip39.recover_ems(mnemonics[1:4])
|
|
|
|
self.assertEqual(ems, self.EMS)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
|
|
|
def test_group_sharing(self):
|
2019-05-03 16:28:14 +00:00
|
|
|
group_threshold = 2
|
|
|
|
group_sizes = (5, 3, 5, 1)
|
|
|
|
member_thresholds = (3, 2, 2, 1)
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
|
|
|
identifier = slip39.generate_random_identifier()
|
|
|
|
mnemonics = slip39.split_ems(
|
|
|
|
group_threshold,
|
|
|
|
list(zip(member_thresholds, group_sizes)),
|
|
|
|
identifier,
|
|
|
|
extendable,
|
|
|
|
1,
|
|
|
|
self.EMS,
|
|
|
|
)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Test all valid combinations of mnemonics.
|
|
|
|
for groups in combinations(zip(mnemonics, member_thresholds), group_threshold):
|
|
|
|
for group1_subset in combinations(groups[0][0], groups[0][1]):
|
|
|
|
for group2_subset in combinations(groups[1][0], groups[1][1]):
|
|
|
|
mnemonic_subset = list(group1_subset + group2_subset)
|
|
|
|
random.shuffle(mnemonic_subset)
|
|
|
|
identifier, _, _, ems = slip39.recover_ems(mnemonic_subset)
|
|
|
|
self.assertEqual(ems, self.EMS)
|
|
|
|
|
|
|
|
# Minimal sets of mnemonics.
|
|
|
|
identifier, _, _, ems = slip39.recover_ems(
|
|
|
|
[mnemonics[2][0], mnemonics[2][2], mnemonics[3][0]]
|
|
|
|
)
|
|
|
|
self.assertEqual(ems, self.EMS)
|
|
|
|
self.assertEqual(
|
|
|
|
slip39.recover_ems([mnemonics[2][3], mnemonics[3][0], mnemonics[2][4]])[3],
|
|
|
|
ems,
|
|
|
|
)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# One complete group and one incomplete group out of two groups required.
|
|
|
|
with self.assertRaises(slip39.MnemonicError):
|
|
|
|
slip39.recover_ems(mnemonics[0][2:] + [mnemonics[1][0]])
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# One group of two required.
|
|
|
|
with self.assertRaises(slip39.MnemonicError):
|
|
|
|
slip39.recover_ems(mnemonics[0][1:4])
|
2019-04-12 19:17:47 +00:00
|
|
|
|
2019-06-11 09:26:29 +00:00
|
|
|
def test_group_sharing_threshold_1(self):
|
|
|
|
group_threshold = 1
|
|
|
|
group_sizes = (5, 3, 5, 1)
|
|
|
|
member_thresholds = (3, 2, 2, 1)
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
2019-06-25 12:36:30 +00:00
|
|
|
identifier = slip39.generate_random_identifier()
|
2019-12-12 15:08:16 +00:00
|
|
|
mnemonics = slip39.split_ems(
|
2023-06-28 10:46:29 +00:00
|
|
|
group_threshold,
|
2024-04-30 18:26:46 +00:00
|
|
|
list(zip(member_thresholds, group_sizes)),
|
2023-06-28 10:46:29 +00:00
|
|
|
identifier,
|
2024-04-30 18:26:46 +00:00
|
|
|
extendable,
|
2023-06-28 10:46:29 +00:00
|
|
|
1,
|
|
|
|
self.EMS,
|
2019-06-11 09:26:29 +00:00
|
|
|
)
|
2024-04-30 18:26:46 +00:00
|
|
|
|
|
|
|
# Test all valid combinations of mnemonics.
|
|
|
|
for group, threshold in zip(mnemonics, member_thresholds):
|
|
|
|
for group_subset in combinations(group, threshold):
|
|
|
|
mnemonic_subset = list(group_subset)
|
|
|
|
random.shuffle(mnemonic_subset)
|
|
|
|
identifier, _, _, ems = slip39.recover_ems(mnemonic_subset)
|
|
|
|
self.assertEqual(ems, self.EMS)
|
|
|
|
|
|
|
|
def test_all_groups_exist(self):
|
|
|
|
for extendable in (False, True):
|
|
|
|
for group_threshold in (1, 2, 5):
|
|
|
|
identifier = slip39.generate_random_identifier()
|
|
|
|
mnemonics = slip39.split_ems(
|
|
|
|
group_threshold,
|
|
|
|
[(3, 5), (1, 1), (2, 3), (2, 5), (3, 5)],
|
|
|
|
identifier,
|
|
|
|
extendable,
|
|
|
|
1,
|
|
|
|
self.EMS,
|
|
|
|
)
|
|
|
|
self.assertEqual(len(mnemonics), 5)
|
|
|
|
self.assertEqual(len(sum(mnemonics, [])), 19)
|
2019-06-11 09:26:29 +00:00
|
|
|
|
2019-05-02 13:00:04 +00:00
|
|
|
def test_invalid_sharing(self):
|
2024-04-30 18:26:46 +00:00
|
|
|
for extendable in (False, True):
|
|
|
|
identifier = slip39.generate_random_identifier()
|
2019-05-02 13:00:04 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Group threshold exceeds number of groups.
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
slip39.split_ems(3, [(3, 5), (2, 5)], identifier, extendable, 1, self.EMS)
|
2019-05-02 13:00:04 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Invalid group threshold.
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
slip39.split_ems(0, [(3, 5), (2, 5)], identifier, extendable, 1, self.EMS)
|
2019-05-03 14:09:40 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Member threshold exceeds number of members.
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
slip39.split_ems(2, [(3, 2), (2, 5)], identifier, extendable, 1, self.EMS)
|
2019-05-03 14:09:40 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Invalid member threshold.
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
slip39.split_ems(2, [(0, 2), (2, 5)], identifier, extendable, 1, self.EMS)
|
2019-05-03 14:09:40 +00:00
|
|
|
|
2024-04-30 18:26:46 +00:00
|
|
|
# Group with multiple members and threshold 1.
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
slip39.split_ems(2, [(3, 5), (1, 3), (2, 5)], identifier, extendable, 1, self.EMS)
|
2019-05-02 13:00:04 +00:00
|
|
|
|
2019-04-12 19:17:47 +00:00
|
|
|
def test_vectors(self):
|
2019-04-12 09:41:32 +00:00
|
|
|
for mnemonics, secret in vectors:
|
|
|
|
if secret:
|
2024-04-30 18:26:46 +00:00
|
|
|
identifier, extendable, exponent, ems = slip39.recover_ems(mnemonics)
|
2023-06-28 10:46:29 +00:00
|
|
|
self.assertEqual(
|
2024-04-30 18:26:46 +00:00
|
|
|
slip39.decrypt(ems, b"TREZOR", exponent, identifier, extendable),
|
2023-06-28 10:46:29 +00:00
|
|
|
unhexlify(secret),
|
|
|
|
)
|
2019-04-12 09:41:32 +00:00
|
|
|
else:
|
|
|
|
with self.assertRaises(slip39.MnemonicError):
|
2019-12-12 15:08:16 +00:00
|
|
|
slip39.recover_ems(mnemonics)
|
2019-04-12 19:17:47 +00:00
|
|
|
|
|
|
|
|
2023-06-28 10:46:29 +00:00
|
|
|
if __name__ == "__main__":
|
2019-04-12 09:41:32 +00:00
|
|
|
unittest.main()
|