From 75dfcacc210e117ff96d23e82a80aa71c726db0f Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 3 Oct 2019 10:51:26 +0200 Subject: [PATCH 1/2] core/tests: add unit test for process_slip39 function updates #542 --- .../management/recovery_device/recover.py | 4 + .../test_apps.management.recovery_device.py | 111 +++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/core/src/apps/management/recovery_device/recover.py b/core/src/apps/management/recovery_device/recover.py index ac3c67e3fa..83975270c7 100644 --- a/core/src/apps/management/recovery_device/recover.py +++ b/core/src/apps/management/recovery_device/recover.py @@ -56,8 +56,12 @@ def process_slip39(words: str) -> Tuple[Optional[bytes], slip39.Share]: # These should be checked by UI before so it's a Runtime exception otherwise if share.identifier != storage.recovery.get_slip39_identifier(): raise RuntimeError("Slip39: Share identifiers do not match") + if share.iteration_exponent != storage.recovery.get_slip39_iteration_exponent(): + raise RuntimeError("Slip39: Share exponents do not match") if storage.recovery_shares.get(share.index, share.group_index): raise RuntimeError("Slip39: This mnemonic was already entered") + if share.group_count != storage.recovery.get_slip39_group_count(): + raise RuntimeError("Slip39: Group count does not match") remaining_for_share = ( storage.recovery.get_slip39_remaining_shares(share.group_index) diff --git a/core/tests/test_apps.management.recovery_device.py b/core/tests/test_apps.management.recovery_device.py index 86db4dbc58..d175efb8dc 100644 --- a/core/tests/test_apps.management.recovery_device.py +++ b/core/tests/test_apps.management.recovery_device.py @@ -25,13 +25,120 @@ MNEMONIC_SLIP39_ADVANCED_33 = [ class TestSlip39(unittest.TestCase): + @mock_storage - def test_process_slip39(self): + def test_process_slip39_basic(self): storage.recovery.set_in_progress(True) - words = MNEMONIC_SLIP39_BASIC_20_3of6[0] + + # first share (member index 5) + first = MNEMONIC_SLIP39_BASIC_20_3of6[0] + secret, share = process_slip39(first) + self.assertIsNone(secret) + self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) + self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) + self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier()) + self.assertEqual(storage.recovery.get_slip39_remaining_shares(0), 2) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), first) + + # second share (member index 0) + second = MNEMONIC_SLIP39_BASIC_20_3of6[1] + secret, share = process_slip39(second) + self.assertIsNone(secret) + self.assertEqual(storage.recovery.get_slip39_remaining_shares(0), 1) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), second) + self.assertEqual(storage.recovery_shares.fetch_group(share.group_index), [second, first]) # ordered by index + + # third share (member index 3) + third = MNEMONIC_SLIP39_BASIC_20_3of6[2] + secret, share = process_slip39(third) + self.assertIsNotNone(secret) + self.assertEqual(storage.recovery.get_slip39_remaining_shares(0), 0) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), third) + self.assertEqual(storage.recovery_shares.fetch_group(share.group_index), [second, third, first]) # ordered by index + + @mock_storage + def test_process_slip39_advanced(self): + storage.recovery.set_in_progress(True) + + # complete group 1 (1of1) + words = MNEMONIC_SLIP39_ADVANCED_20[0] secret, share = process_slip39(words) self.assertIsNone(secret) self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) + self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) + self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier()) + self.assertEqual(storage.recovery.fetch_slip39_remaining_shares(), [16, 0, 16, 16]) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), words) + + # member index 4 from group 2 (3of5) + words = MNEMONIC_SLIP39_ADVANCED_20[1] + secret, share = process_slip39(words) + self.assertIsNone(secret) + self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) + self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) + self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier()) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), words) + self.assertEqual(storage.recovery_shares.fetch_group(1), [MNEMONIC_SLIP39_ADVANCED_20[0]]) + self.assertEqual(storage.recovery_shares.fetch_group(2), [MNEMONIC_SLIP39_ADVANCED_20[1]]) + self.assertEqual(storage.recovery.fetch_slip39_remaining_shares(), [16, 0, 2, 16]) + + # member index 2 from group 2 + words = MNEMONIC_SLIP39_ADVANCED_20[2] + secret, share = process_slip39(words) + self.assertIsNone(secret) + self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) + self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) + self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier()) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), words) + self.assertEqual(storage.recovery_shares.fetch_group(1), [MNEMONIC_SLIP39_ADVANCED_20[0]]) + self.assertEqual(storage.recovery_shares.fetch_group(2), [MNEMONIC_SLIP39_ADVANCED_20[2], MNEMONIC_SLIP39_ADVANCED_20[1]]) + self.assertEqual(storage.recovery.fetch_slip39_remaining_shares(), [16, 0, 1, 16]) + + # last member index 0 from group 2 + # now group 2 is complete => the whole Shamir recovery is completed + words = MNEMONIC_SLIP39_ADVANCED_20[3] + secret, share = process_slip39(words) + self.assertIsNotNone(secret) + self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) + self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) + self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier()) + self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), words) + self.assertEqual(storage.recovery_shares.fetch_group(1), [MNEMONIC_SLIP39_ADVANCED_20[0]]) + self.assertEqual(storage.recovery_shares.fetch_group(2), [MNEMONIC_SLIP39_ADVANCED_20[3], MNEMONIC_SLIP39_ADVANCED_20[2], MNEMONIC_SLIP39_ADVANCED_20[1]]) + self.assertEqual(storage.recovery.fetch_slip39_remaining_shares(), [16, 0, 0, 16]) + + @mock_storage + def test_exceptions(self): + storage.recovery.set_in_progress(True) + + words = MNEMONIC_SLIP39_BASIC_20_3of6[0] + secret, share = process_slip39(words) + self.assertIsNone(secret) + + # same mnemonic + words = MNEMONIC_SLIP39_BASIC_20_3of6[0] + with self.assertRaises(RuntimeError): + secret, share = process_slip39(words) + self.assertIsNone(secret) + + # identifier mismatch + words = MNEMONIC_SLIP39_ADVANCED_20[0] + with self.assertRaises(RuntimeError): + secret, share = process_slip39(words) + self.assertIsNone(secret) + + # same identifier but different group settings + words = MNEMONIC_SLIP39_BASIC_20_3of6[0] + w = words.split() + w[2] = "check" # change the group settings + w[3] = "mortgage" + w[17] = "garden" # modify checksum accordingly + w[18] = "merchant" + w[19] = "merchant" + words = " ".join(w) + with self.assertRaises(RuntimeError): + secret, share = process_slip39(words) + self.assertIsNone(secret) if __name__ == "__main__": From 1068acae40400e5badce4efa28f53a38fe32a95d Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 4 Oct 2019 13:47:52 +0000 Subject: [PATCH 2/2] core/tests: assert secret in process_slip39 test --- core/tests/test_apps.management.recovery_device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/test_apps.management.recovery_device.py b/core/tests/test_apps.management.recovery_device.py index d175efb8dc..a49fa92d8c 100644 --- a/core/tests/test_apps.management.recovery_device.py +++ b/core/tests/test_apps.management.recovery_device.py @@ -51,7 +51,7 @@ class TestSlip39(unittest.TestCase): # third share (member index 3) third = MNEMONIC_SLIP39_BASIC_20_3of6[2] secret, share = process_slip39(third) - self.assertIsNotNone(secret) + self.assertEqual(secret, b'I\x1by[\x80\xfc!\xcc\xdfFl\x0f\xbc\x98\xc8\xfc') self.assertEqual(storage.recovery.get_slip39_remaining_shares(0), 0) self.assertEqual(storage.recovery_shares.get(share.index, share.group_index), third) self.assertEqual(storage.recovery_shares.fetch_group(share.group_index), [second, third, first]) # ordered by index @@ -98,7 +98,7 @@ class TestSlip39(unittest.TestCase): # now group 2 is complete => the whole Shamir recovery is completed words = MNEMONIC_SLIP39_ADVANCED_20[3] secret, share = process_slip39(words) - self.assertIsNotNone(secret) + self.assertEqual(secret, b'\xc2\xd2\xe2j\xd0`#\xc6\x01E\xf1P\xab\xe2\xdd+') self.assertEqual(share.group_count, storage.recovery.get_slip39_group_count()) self.assertEqual(share.iteration_exponent, storage.recovery.get_slip39_iteration_exponent()) self.assertEqual(share.identifier, storage.recovery.get_slip39_identifier())