2024-01-26 11:16:55 +00:00
|
|
|
from common import * # isort:skip
|
2020-07-23 14:23:24 +00:00
|
|
|
|
2024-01-26 11:16:55 +00:00
|
|
|
from mock_storage import mock_storage
|
2024-11-15 20:46:22 +00:00
|
|
|
from storage import cache, cache_common
|
2024-11-15 16:31:22 +00:00
|
|
|
from trezor import utils, wire
|
2020-07-23 14:23:24 +00:00
|
|
|
from trezor.crypto import bip39
|
2021-03-23 12:35:27 +00:00
|
|
|
from trezor.enums import SafetyCheckLevel
|
2024-11-15 20:46:22 +00:00
|
|
|
from trezor.wire import context
|
2020-07-23 14:23:24 +00:00
|
|
|
|
2023-06-28 10:58:54 +00:00
|
|
|
from apps.common import safety_checks
|
|
|
|
from apps.common.keychain import Keychain, LRUCache, get_keychain, with_slip44_keychain
|
|
|
|
from apps.common.paths import PATTERN_SEP5, PathSchema
|
2024-11-15 20:46:22 +00:00
|
|
|
from trezor.wire.codec.codec_context import CodecContext
|
2024-11-15 16:31:22 +00:00
|
|
|
|
|
|
|
if utils.USE_THP:
|
|
|
|
import thp_common
|
|
|
|
if not utils.USE_THP:
|
|
|
|
from storage import cache_codec
|
2023-06-28 10:58:54 +00:00
|
|
|
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
class TestKeychain(unittest.TestCase):
|
2024-11-15 20:46:22 +00:00
|
|
|
|
2024-11-19 11:08:10 +00:00
|
|
|
def setUpClass(self):
|
2024-11-15 20:46:22 +00:00
|
|
|
context.CURRENT_CONTEXT = CodecContext(None, bytearray(64))
|
2024-11-19 11:08:10 +00:00
|
|
|
|
|
|
|
def tearDownClass(self):
|
|
|
|
context.CURRENT_CONTEXT = None
|
2024-11-15 20:46:22 +00:00
|
|
|
|
2024-11-15 16:31:22 +00:00
|
|
|
def __init__(self):
|
|
|
|
if __debug__:
|
|
|
|
thp_common.suppres_debug_log()
|
|
|
|
thp_common.prepare_context()
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
context.CURRENT_CONTEXT = CodecContext(None, bytearray(64))
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
cache_codec.start_session()
|
2020-07-14 10:15:18 +00:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
cache.clear_all()
|
|
|
|
|
2020-07-23 14:23:24 +00:00
|
|
|
@mock_storage
|
|
|
|
def test_verify_path(self):
|
2020-07-14 10:15:18 +00:00
|
|
|
schemas = (
|
2021-03-19 15:32:55 +00:00
|
|
|
PathSchema.parse("m/44'/coin_type'", slip44_id=134),
|
|
|
|
PathSchema.parse("m/44'/coin_type'", slip44_id=11),
|
2020-07-14 10:15:18 +00:00
|
|
|
)
|
|
|
|
keychain = Keychain(b"", "secp256k1", schemas)
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
correct = (
|
2020-07-14 10:15:18 +00:00
|
|
|
[H_(44), H_(134)],
|
|
|
|
[H_(44), H_(11)],
|
2020-07-23 14:23:24 +00:00
|
|
|
)
|
|
|
|
for path in correct:
|
|
|
|
keychain.verify_path(path)
|
|
|
|
|
|
|
|
fails = (
|
2020-07-14 10:15:18 +00:00
|
|
|
[H_(44), 134], # path does not match
|
2020-07-23 14:23:24 +00:00
|
|
|
[44, 134], # path does not match (non-hardened items)
|
2020-07-14 10:15:18 +00:00
|
|
|
[H_(44), H_(13)], # invalid second item
|
2020-07-23 14:23:24 +00:00
|
|
|
)
|
|
|
|
for f in fails:
|
|
|
|
with self.assertRaises(wire.DataError):
|
|
|
|
keychain.verify_path(f)
|
|
|
|
|
|
|
|
# turn off restrictions
|
2020-09-17 12:27:04 +00:00
|
|
|
safety_checks.apply_setting(SafetyCheckLevel.PromptTemporarily)
|
2020-07-23 14:23:24 +00:00
|
|
|
for path in correct + fails:
|
|
|
|
keychain.verify_path(path)
|
|
|
|
|
|
|
|
def test_verify_path_special_ed25519(self):
|
2021-03-19 15:32:55 +00:00
|
|
|
schema = PathSchema.parse("m/44'/coin_type'/*", slip44_id=134)
|
2020-07-14 10:15:18 +00:00
|
|
|
k = Keychain(b"", "ed25519-keccak", [schema])
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
# OK case
|
2020-07-14 10:15:18 +00:00
|
|
|
k.verify_path([H_(44), H_(134)])
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
# failing case: non-hardened component with ed25519-like derivation
|
|
|
|
with self.assertRaises(wire.DataError):
|
2020-07-14 10:15:18 +00:00
|
|
|
k.verify_path([H_(44), H_(134), 1])
|
2020-07-23 14:23:24 +00:00
|
|
|
|
2020-07-14 10:15:18 +00:00
|
|
|
def test_no_schemas(self):
|
|
|
|
k = Keychain(b"", "secp256k1", [])
|
|
|
|
paths = (
|
2020-07-23 14:23:24 +00:00
|
|
|
[],
|
|
|
|
[1, 2, 3, 4],
|
2020-07-14 10:15:18 +00:00
|
|
|
[H_(44), H_(11)],
|
|
|
|
[H_(44), H_(11), 12],
|
2020-07-23 14:23:24 +00:00
|
|
|
)
|
2020-07-14 10:15:18 +00:00
|
|
|
for path in paths:
|
|
|
|
self.assertRaises(wire.DataError, k.verify_path, path)
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
def test_get_keychain(self):
|
|
|
|
seed = bip39.seed(" ".join(["all"] * 12), "")
|
2024-11-15 20:46:22 +00:00
|
|
|
context.cache_set(cache_common.APP_COMMON_SEED, seed)
|
2020-07-23 14:23:24 +00:00
|
|
|
|
2021-03-19 15:32:55 +00:00
|
|
|
schema = PathSchema.parse("m/44'/1'", 0)
|
2023-06-26 11:42:10 +00:00
|
|
|
keychain = await_result(get_keychain("secp256k1", [schema]))
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
# valid path:
|
2020-07-14 10:15:18 +00:00
|
|
|
self.assertIsNotNone(keychain.derive([H_(44), H_(1)]))
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
# invalid path:
|
|
|
|
with self.assertRaises(wire.DataError):
|
|
|
|
keychain.derive([44])
|
|
|
|
|
|
|
|
def test_with_slip44(self):
|
|
|
|
seed = bip39.seed(" ".join(["all"] * 12), "")
|
2024-11-15 20:46:22 +00:00
|
|
|
context.cache_set(cache_common.APP_COMMON_SEED, seed)
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
slip44_id = 42
|
2020-07-14 10:15:18 +00:00
|
|
|
valid_path = [H_(44), H_(slip44_id), H_(0)]
|
|
|
|
invalid_path = [H_(44), H_(99), H_(0)]
|
|
|
|
testnet_path = [H_(44), H_(1), H_(0)]
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
def check_valid_paths(keychain, *paths):
|
|
|
|
for path in paths:
|
|
|
|
self.assertIsNotNone(keychain.derive(path))
|
|
|
|
|
|
|
|
def check_invalid_paths(keychain, *paths):
|
|
|
|
for path in paths:
|
|
|
|
self.assertRaises(wire.DataError, keychain.derive, path)
|
|
|
|
|
2020-07-14 10:15:18 +00:00
|
|
|
@with_slip44_keychain(PATTERN_SEP5, slip44_id=slip44_id)
|
2023-06-26 11:42:10 +00:00
|
|
|
async def func_id_only(msg, keychain):
|
2020-07-23 14:23:24 +00:00
|
|
|
check_valid_paths(keychain, valid_path, testnet_path)
|
|
|
|
check_invalid_paths(keychain, invalid_path)
|
|
|
|
|
2020-07-14 10:15:18 +00:00
|
|
|
@with_slip44_keychain(PATTERN_SEP5, slip44_id=slip44_id, allow_testnet=False)
|
2023-06-26 11:42:10 +00:00
|
|
|
async def func_disallow_testnet(msg, keychain):
|
2020-07-23 14:23:24 +00:00
|
|
|
check_valid_paths(keychain, valid_path)
|
|
|
|
check_invalid_paths(keychain, testnet_path, invalid_path)
|
|
|
|
|
2020-07-14 10:15:18 +00:00
|
|
|
@with_slip44_keychain(PATTERN_SEP5, slip44_id=slip44_id, curve="ed25519")
|
2023-06-26 11:42:10 +00:00
|
|
|
async def func_with_curve(msg, keychain):
|
2020-07-14 10:15:18 +00:00
|
|
|
self.assertEqual(keychain.curve, "ed25519")
|
|
|
|
check_valid_paths(keychain, valid_path, testnet_path)
|
|
|
|
check_invalid_paths(keychain, invalid_path)
|
|
|
|
|
2023-06-26 11:42:10 +00:00
|
|
|
await_result(func_id_only(None))
|
|
|
|
await_result(func_disallow_testnet(None))
|
|
|
|
await_result(func_with_curve(None))
|
2020-07-23 14:23:24 +00:00
|
|
|
|
|
|
|
def test_lru_cache(self):
|
|
|
|
class Deletable:
|
|
|
|
def __init__(self):
|
|
|
|
self.deleted = False
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
self.deleted = True
|
|
|
|
|
|
|
|
cache = LRUCache(10)
|
|
|
|
|
|
|
|
obj_a = Deletable()
|
|
|
|
self.assertIsNone(cache.get("a"))
|
|
|
|
cache.insert("a", obj_a)
|
|
|
|
|
|
|
|
self.assertIs(cache.get("a"), obj_a)
|
|
|
|
|
|
|
|
# test eviction
|
|
|
|
objects = [(i, Deletable()) for i in range(10)]
|
|
|
|
for key, obj in objects:
|
|
|
|
cache.insert(key, obj)
|
|
|
|
|
|
|
|
# object A should have been evicted
|
|
|
|
self.assertIsNone(cache.get("a"))
|
|
|
|
self.assertTrue(obj_a.deleted)
|
|
|
|
|
|
|
|
cache.insert("a", obj_a)
|
|
|
|
for key, obj in objects[:-1]:
|
|
|
|
# objects should have been evicted in insertion order
|
|
|
|
self.assertIsNone(cache.get(key))
|
|
|
|
self.assertTrue(obj.deleted)
|
|
|
|
cache.insert(key, obj)
|
|
|
|
|
|
|
|
# use "a" object
|
|
|
|
self.assertIs(cache.get("a"), obj_a)
|
|
|
|
# insert last object
|
|
|
|
key, obj = objects[-1]
|
|
|
|
cache.insert(key, obj)
|
|
|
|
|
|
|
|
# "a" is recently used so should not be evicted now
|
|
|
|
self.assertIs(cache.get("a"), obj_a)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|