1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-16 17:42:02 +00:00

tests: expand backup device tests

This commit is contained in:
ciny 2019-09-23 14:49:54 +02:00 committed by matejcik
parent 2217b680e3
commit a8f2f7b1e3
7 changed files with 837 additions and 0 deletions

View File

@ -19,10 +19,12 @@ import pytest
import shamir_mnemonic as shamir import shamir_mnemonic as shamir
from trezorlib import device, messages from trezorlib import device, messages
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import ButtonRequestType as B from trezorlib.messages import ButtonRequestType as B
from ..common import ( from ..common import (
MNEMONIC12, MNEMONIC12,
MNEMONIC_SLIP39_ADVANCED_20,
MNEMONIC_SLIP39_BASIC_20_3of6, MNEMONIC_SLIP39_BASIC_20_3of6,
click_through, click_through,
read_and_confirm_mnemonic, read_and_confirm_mnemonic,
@ -61,7 +63,11 @@ def test_backup_bip39(client):
assert mnemonic == MNEMONIC12 assert mnemonic == MNEMONIC12
client.init_device() client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is messages.BackupType.Bip39
@pytest.mark.skip_t1 @pytest.mark.skip_t1
@ -118,8 +124,214 @@ def test_backup_slip39_basic(client):
device.backup(client) device.backup(client)
client.init_device() client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is messages.BackupType.Slip39_Basic
expected_ms = shamir.combine_mnemonics(MNEMONIC_SLIP39_BASIC_20_3of6) expected_ms = shamir.combine_mnemonics(MNEMONIC_SLIP39_BASIC_20_3of6)
actual_ms = shamir.combine_mnemonics(mnemonics[:3]) actual_ms = shamir.combine_mnemonics(mnemonics[:3])
assert expected_ms == actual_ms assert expected_ms == actual_ms
@pytest.mark.skip_t1
@pytest.mark.setup_client(mnemonic=MNEMONIC_SLIP39_ADVANCED_20)
def test_backup_slip39_advanced(client):
assert client.features.needs_backup is True
mnemonics = []
def input_flow():
# 1. Checklist
# 2. Set and confirm group count
# 3. Checklist
# 4. Set and confirm group threshold
# 5. Checklist
# 6-15: for each of 5 groups:
# 1. Set & Confirm number of shares
# 2. Set & confirm share threshold value
# 16. Confirm show seeds
yield from click_through(client.debug, screens=16, code=B.ResetDevice)
# Mnemonic phrases
for _ in range(5):
for _ in range(5):
yield # Phrase screen
mnemonic = read_and_confirm_mnemonic(client.debug, words=20)
mnemonics.append(mnemonic)
yield # Confirm continue to next
client.debug.press_yes()
# Confirm backup
yield
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #1 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #2 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #3 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #4 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #5 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # show seeds
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success), # show seeds ends here
messages.ButtonRequest(code=B.Success),
messages.Success(),
]
)
device.backup(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is messages.BackupType.Slip39_Advanced
expected_ms = shamir.combine_mnemonics(MNEMONIC_SLIP39_ADVANCED_20)
actual_ms = shamir.combine_mnemonics(
mnemonics[:3] + mnemonics[5:8] + mnemonics[10:13]
)
assert expected_ms == actual_ms
# we only test this with bip39 because the code path is always the same
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_no_backup_fails(client):
device.reset(
client,
display_random=False,
strength=128,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
no_backup=True,
)
assert client.features.initialized is True
assert client.features.no_backup is True
assert client.features.needs_backup is False
# backup attempt should fail because no_backup=True
with pytest.raises(TrezorFailure, match="ProcessError: Seed already backed up"):
device.backup(client)
# we only test this with bip39 because the code path is always the same
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_interupt_backup_fails(client):
device.reset(
client,
display_random=False,
strength=128,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
skip_backup=True,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
# start backup
client.call_raw(messages.BackupDevice())
# interupt backup by sending initialize
client.init_device()
# check that device state is as expected
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is True
assert client.features.no_backup is False
# Second attempt at backup should fail
with pytest.raises(TrezorFailure, match="ProcessError: Seed already backed up"):
device.backup(client)
# we only test this with bip39 because the code path is always the same
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_no_backup_show_entropy_fails(client):
with pytest.raises(
TrezorFailure,
match="ProcessError: Can't show internal entropy when backup is skipped",
):
device.reset(
client,
display_random=True,
strength=128,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
no_backup=True,
)

View File

@ -20,6 +20,7 @@ import pytest
from mnemonic import Mnemonic from mnemonic import Mnemonic
from trezorlib import device, messages as proto from trezorlib import device, messages as proto
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import ButtonRequestType as B from trezorlib.messages import ButtonRequestType as B
from ..common import ( from ..common import (
@ -105,6 +106,10 @@ class TestMsgResetDeviceT2:
assert resp.passphrase_protection is False assert resp.passphrase_protection is False
assert resp.backup_type is proto.BackupType.Bip39 assert resp.backup_type is proto.BackupType.Bip39
# backup attempt fails because backup was done in reset
with pytest.raises(TrezorFailure, match="ProcessError: Seed already backed up"):
device.backup(client)
@pytest.mark.setup_client(uninitialized=True) @pytest.mark.setup_client(uninitialized=True)
def test_reset_device_pin(self, client): def test_reset_device_pin(self, client):
mnemonic = None mnemonic = None

View File

@ -20,6 +20,7 @@ import pytest
import shamir_mnemonic as shamir import shamir_mnemonic as shamir
from trezorlib import device, messages as proto from trezorlib import device, messages as proto
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B from trezorlib.messages import BackupType, ButtonRequestType as B
from ..common import click_through, generate_entropy, read_and_confirm_mnemonic from ..common import click_through, generate_entropy, read_and_confirm_mnemonic
@ -176,6 +177,10 @@ class TestMsgResetDeviceT2:
assert client.features.passphrase_protection is False assert client.features.passphrase_protection is False
assert client.features.backup_type is BackupType.Slip39_Advanced assert client.features.backup_type is BackupType.Slip39_Advanced
# backup attempt fails because backup was done in reset
with pytest.raises(TrezorFailure, match="ProcessError: Seed already backed up"):
device.backup(client)
def validate_mnemonics(mnemonics, threshold, expected_ems): def validate_mnemonics(mnemonics, threshold, expected_ems):
# 3of5 shares 3of5 groups # 3of5 shares 3of5 groups

View File

@ -22,6 +22,7 @@ import shamir_mnemonic as shamir
from shamir_mnemonic import MnemonicError from shamir_mnemonic import MnemonicError
from trezorlib import device, messages as proto from trezorlib import device, messages as proto
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B from trezorlib.messages import BackupType, ButtonRequestType as B
from ..common import click_through, generate_entropy, read_and_confirm_mnemonic from ..common import click_through, generate_entropy, read_and_confirm_mnemonic
@ -123,6 +124,10 @@ class TestMsgResetDeviceT2:
assert client.features.passphrase_protection is False assert client.features.passphrase_protection is False
assert client.features.backup_type is BackupType.Slip39_Basic assert client.features.backup_type is BackupType.Slip39_Basic
# backup attempt fails because backup was done in reset
with pytest.raises(TrezorFailure, match="ProcessError: Seed already backed up"):
device.backup(client)
def validate_mnemonics(mnemonics, threshold, expected_ems): def validate_mnemonics(mnemonics, threshold, expected_ems):
# We expect these combinations to recreate the secret properly # We expect these combinations to recreate the secret properly

View File

@ -0,0 +1,167 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from unittest import mock
import pytest
from mnemonic import Mnemonic
from trezorlib import device, messages
from trezorlib.messages import BackupType, ButtonRequestType as B
from ..common import click_through, generate_entropy, read_and_confirm_mnemonic
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
OS_URANDOM = mock.Mock(return_value=EXTERNAL_ENTROPY)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_msg(client):
with mock.patch("os.urandom", OS_URANDOM), client:
device.reset(
client,
display_random=False,
strength=128,
skip_backup=True,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
backup_type=BackupType.Bip39,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Bip39
mnemonic = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Bip39
assert mnemonic is not None
# generate mnemonic locally
internal_entropy = client.debug.state().reset_entropy
entropy = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
# Compare that device generated proper mnemonic for given entropies
assert mnemonic == expected_mnemonic
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_manual(client):
def reset_skip_input_flow():
yield # Confirm Recovery
client.debug.press_yes()
yield # Skip Backup
client.debug.press_no()
yield # Confirm skip backup
client.debug.press_no()
with mock.patch("os.urandom", OS_URANDOM), client:
client.set_input_flow(reset_skip_input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.Success(),
messages.Features(),
]
)
device.reset(
client,
pin_protection=False,
passphrase_protection=False,
backup_type=BackupType.Bip39,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Bip39
mnemonic = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Bip39
assert mnemonic is not None
# generate mnemonic locally
internal_entropy = client.debug.state().reset_entropy
entropy = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
# Compare that device generated proper mnemonic for given entropies
assert mnemonic == expected_mnemonic
def backup_flow(client):
mnemonic = None
def input_flow():
nonlocal mnemonic
# 1. Confirm Reset
yield from click_through(client.debug, screens=1, code=B.ResetDevice)
# mnemonic phrases
btn_code = yield
assert btn_code == B.ResetDevice
mnemonic = read_and_confirm_mnemonic(client.debug, words=12)
# confirm recovery seed check
btn_code = yield
assert btn_code == B.Success
client.debug.press_yes()
# confirm success
btn_code = yield
assert btn_code == B.Success
client.debug.press_yes()
with client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success(),
]
)
client.set_input_flow(input_flow)
device.backup(client)
return mnemonic

View File

@ -0,0 +1,250 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from unittest import mock
import pytest
import shamir_mnemonic as shamir
from trezorlib import device, messages
from trezorlib.messages import BackupType, ButtonRequestType as B
from ..common import click_through, generate_entropy, read_and_confirm_mnemonic
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
OS_URANDOM = mock.Mock(return_value=EXTERNAL_ENTROPY)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_msg(client):
with mock.patch("os.urandom", OS_URANDOM), client:
device.reset(
client,
display_random=False,
strength=128,
skip_backup=True,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
backup_type=BackupType.Slip39_Advanced,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Slip39_Advanced
# generate secret locally
internal_entropy = client.debug.state().reset_entropy
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
mnemonics = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Slip39_Advanced
assert mnemonics is not []
validate_mnemonics(mnemonics, secret)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_manual(client):
def reset_skip_input_flow():
yield # Confirm Recovery
client.debug.press_yes()
yield # Skip Backup
client.debug.press_no()
yield # Confirm skip backup
client.debug.press_no()
with mock.patch("os.urandom", OS_URANDOM), client:
client.set_input_flow(reset_skip_input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.Success(),
messages.Features(),
]
)
device.reset(
client,
strength=128,
pin_protection=False,
passphrase_protection=False,
backup_type=BackupType.Slip39_Advanced,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Slip39_Advanced
# generate secret locally
internal_entropy = client.debug.state().reset_entropy
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
mnemonics = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Slip39_Advanced
assert mnemonics is not []
# generate secret locally
internal_entropy = client.debug.state().reset_entropy
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
validate_mnemonics(mnemonics, secret)
def backup_flow(client):
all_mnemonics = []
def input_flow():
# 1. Confirm Reset
# 2. shares info
# 3. Set & Confirm number of groups
# 4. threshold info
# 5. Set & confirm group threshold value
# 6-15: for each of 5 groups:
# 1. Set & Confirm number of shares
# 2. Set & confirm share threshold value
# 16. Confirm show seeds
yield from click_through(client.debug, screens=16, code=B.ResetDevice)
# show & confirm shares for all groups
for g in range(5):
for h in range(5):
# mnemonic phrases
btn_code = yield
assert btn_code == B.ResetDevice
mnemonic = read_and_confirm_mnemonic(client.debug, words=20)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
btn_code = yield
assert btn_code == B.Success
client.debug.press_yes()
# safety warning
btn_code = yield
assert btn_code == B.Success
client.debug.press_yes()
with client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #1 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #2 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #3 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #4 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #5 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # show seeds
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success), # show seeds ends here
messages.ButtonRequest(code=B.Success),
messages.Success(),
]
)
client.set_input_flow(input_flow)
device.backup(client)
return all_mnemonics
def validate_mnemonics(mnemonics, expected_ems):
# 3of5 shares 3of5 groups
test_combination = mnemonics[0:3] + mnemonics[5:8] + mnemonics[10:13]
ms = shamir.combine_mnemonics(test_combination)
identifier, iteration_exponent, _, _, _ = shamir._decode_mnemonics(test_combination)
ems = shamir._encrypt(ms, b"", iteration_exponent, identifier)
assert ems == expected_ems

View File

@ -0,0 +1,193 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from itertools import combinations
from unittest import mock
import pytest
import shamir_mnemonic as shamir
from shamir_mnemonic import MnemonicError
from trezorlib import device, messages
from trezorlib.messages import BackupType, ButtonRequestType as B
from ..common import click_through, generate_entropy, read_and_confirm_mnemonic
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
OS_URANDOM = mock.Mock(return_value=EXTERNAL_ENTROPY)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_msg(client):
with mock.patch("os.urandom", OS_URANDOM), client:
device.reset(
client,
display_random=False,
strength=128,
skip_backup=True,
passphrase_protection=False,
pin_protection=False,
label="test",
language="english",
backup_type=BackupType.Slip39_Basic,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Slip39_Basic
# generate secret locally
internal_entropy = client.debug.state().reset_entropy
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
mnemonics = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Slip39_Basic
assert mnemonics is not []
validate_mnemonics(mnemonics, 3, secret)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_manual(client):
def reset_skip_input_flow():
yield # Confirm Recovery
client.debug.press_yes()
yield # Skip Backup
client.debug.press_no()
yield # Confirm skip backup
client.debug.press_no()
with mock.patch("os.urandom", OS_URANDOM), client:
client.set_input_flow(reset_skip_input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.Success(),
messages.Features(),
]
)
device.reset(
client,
pin_protection=False,
passphrase_protection=False,
backup_type=BackupType.Slip39_Basic,
)
assert client.features.initialized is True
assert client.features.needs_backup is True
assert client.features.unfinished_backup is False
assert client.features.no_backup is False
assert client.features.backup_type is BackupType.Slip39_Basic
# generate secret locally
internal_entropy = client.debug.state().reset_entropy
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
mnemonics = backup_flow(client)
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.unfinished_backup is False
assert client.features.backup_type is BackupType.Slip39_Basic
assert mnemonics is not []
validate_mnemonics(mnemonics, 3, secret)
def backup_flow(client):
mnemonics = []
def input_flow():
# 1. Checklist
# 2. Number of shares (5)
# 3. Checklist
# 4. Threshold (3)
# 5. Checklist
# 6. Confirm show seeds
yield from click_through(client.debug, screens=6, code=B.ResetDevice)
# Mnemonic phrases
for _ in range(5):
yield # Phrase screen
mnemonic = read_and_confirm_mnemonic(client.debug, words=20)
mnemonics.append(mnemonic)
yield # Confirm continue to next
client.debug.press_yes()
# Confirm backup
yield
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success(),
]
)
device.backup(client)
return mnemonics
def validate_mnemonics(mnemonics, threshold, expected_ems):
# We expect these combinations to recreate the secret properly
for test_group in combinations(mnemonics, threshold):
# TODO: HOTFIX, we should fix this properly by modifying and unifying the python-shamir-mnemonic API
ms = shamir.combine_mnemonics(test_group)
identifier, iteration_exponent, _, _, _ = shamir._decode_mnemonics(test_group)
ems = shamir._encrypt(ms, b"", iteration_exponent, identifier)
assert ems == expected_ems
# We expect these combinations to raise MnemonicError
for test_group in combinations(mnemonics, threshold - 1):
with pytest.raises(
MnemonicError, match=r".*Expected {} mnemonics.*".format(threshold)
):
shamir.combine_mnemonics(test_group)