From ed8ff9c10396d9003557b74bcefefde0da1bb5fb Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 8 Sep 2026 13:15:54 -0600 Subject: [PATCH 1/3] test: load consented runtime provider for metadata vectors --- tests/runtime_provider.py | 31 ++++++++++++++++++++++++ tests/test_msg_ethereum_clear_signing.py | 6 +++++ 2 files changed, 37 insertions(+) create mode 100644 tests/runtime_provider.py diff --git a/tests/runtime_provider.py b/tests/runtime_provider.py new file mode 100644 index 00000000..28038724 --- /dev/null +++ b/tests/runtime_provider.py @@ -0,0 +1,31 @@ +"""Explicit consent fixture for firmware with RAM-only provider identities.""" +from google.protobuf import descriptor_pb2, descriptor_pool, message_factory +from keepkeylib import mapping, messages_pb2 as proto + +# The audited host pin predates this message. Keep the fixture wire contract +# narrow rather than importing unrelated generated protocol changes. +spec = descriptor_pb2.FileDescriptorProto(name="runtime_provider_fixture.proto", syntax="proto2") +message = spec.message_type.add(name="FixtureLoadClearsignSigner") +for number, name, kind in [(1, "key_id", 13), (2, "pubkey", 12), + (3, "alias", 9), (7, "persist", 8)]: + message.field.add(name=name, number=number, type=kind, label=1) +pool = descriptor_pool.DescriptorPool() +pool.Add(spec) +LoadSigner = message_factory.MessageFactory(pool).GetPrototype( + pool.FindMessageTypeByName("FixtureLoadClearsignSigner")) +mapping.map_class_to_type[LoadSigner] = 117 +mapping.map_type_to_class[117] = LoadSigner + + +def load_test_provider(client): + from ecdsa import SigningKey, SECP256k1 + from keepkeylib.signed_metadata import TEST_PRIVATE_KEY + key = SigningKey.from_string(TEST_PRIVATE_KEY, curve=SECP256k1) + public_key = key.get_verifying_key().to_string() + compressed_key = bytes([2 | (public_key[-1] & 1)]) + public_key[:32] + request = LoadSigner( + key_id=3, alias="Integration provider", persist=False, + pubkey=compressed_key) + with client: + client.set_expected_responses([proto.ButtonRequest(), proto.Success()]) + client.call(request) diff --git a/tests/test_msg_ethereum_clear_signing.py b/tests/test_msg_ethereum_clear_signing.py index 5d9e661a..7204f6b8 100644 --- a/tests/test_msg_ethereum_clear_signing.py +++ b/tests/test_msg_ethereum_clear_signing.py @@ -15,6 +15,7 @@ """ import unittest +import os import hashlib import struct @@ -414,6 +415,11 @@ def setUp(self): self.requires_firmware("7.14.0") self.requires_message("EthereumTxMetadata") self.setup_mnemonic_nopin_nopassphrase() + if os.environ.get("KEEPKEY_RUNTIME_PROVIDER") == "1": + self.client.apply_policy("AdvancedMode", True) + from runtime_provider import load_test_provider + load_test_provider(self.client) + common.reset_screenshot_capture(self.client) def test_valid_metadata_returns_verified(self): """Send valid signed metadata → device returns VERIFIED.""" From 6dfd3a5a7a5d45920e97a30103f7f9f1fdb98a73 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 8 Sep 2026 13:21:07 -0600 Subject: [PATCH 2/3] test: guard provider wire registration and name protobuf types --- tests/runtime_provider.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/runtime_provider.py b/tests/runtime_provider.py index 28038724..7a736699 100644 --- a/tests/runtime_provider.py +++ b/tests/runtime_provider.py @@ -2,19 +2,27 @@ from google.protobuf import descriptor_pb2, descriptor_pool, message_factory from keepkeylib import mapping, messages_pb2 as proto +LOAD_CLEARSIGN_SIGNER_WIRE_ID = 117 +FIELD = descriptor_pb2.FieldDescriptorProto + # The audited host pin predates this message. Keep the fixture wire contract # narrow rather than importing unrelated generated protocol changes. spec = descriptor_pb2.FileDescriptorProto(name="runtime_provider_fixture.proto", syntax="proto2") message = spec.message_type.add(name="FixtureLoadClearsignSigner") -for number, name, kind in [(1, "key_id", 13), (2, "pubkey", 12), - (3, "alias", 9), (7, "persist", 8)]: - message.field.add(name=name, number=number, type=kind, label=1) +for number, name, kind in [(1, "key_id", FIELD.TYPE_UINT32), + (2, "pubkey", FIELD.TYPE_BYTES), + (3, "alias", FIELD.TYPE_STRING), + (7, "persist", FIELD.TYPE_BOOL)]: + message.field.add(name=name, number=number, type=kind, label=FIELD.LABEL_OPTIONAL) pool = descriptor_pool.DescriptorPool() pool.Add(spec) LoadSigner = message_factory.MessageFactory(pool).GetPrototype( pool.FindMessageTypeByName("FixtureLoadClearsignSigner")) -mapping.map_class_to_type[LoadSigner] = 117 -mapping.map_type_to_class[117] = LoadSigner +if (LOAD_CLEARSIGN_SIGNER_WIRE_ID in mapping.map_type_to_class or + LOAD_CLEARSIGN_SIGNER_WIRE_ID in mapping.map_class_to_type.values()): + raise RuntimeError("Runtime provider fixture wire ID is already registered") +mapping.map_class_to_type[LoadSigner] = LOAD_CLEARSIGN_SIGNER_WIRE_ID +mapping.map_type_to_class[LOAD_CLEARSIGN_SIGNER_WIRE_ID] = LoadSigner def load_test_provider(client): From 4da83a26a270cae2b58d102ba5ca03e54a732070 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 8 Sep 2026 13:28:06 -0600 Subject: [PATCH 3/3] test: clarify required runtime-provider fixture dependencies --- tests/test_msg_ethereum_clear_signing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_msg_ethereum_clear_signing.py b/tests/test_msg_ethereum_clear_signing.py index 7204f6b8..e55d01ca 100644 --- a/tests/test_msg_ethereum_clear_signing.py +++ b/tests/test_msg_ethereum_clear_signing.py @@ -22,7 +22,7 @@ try: import common except ImportError: - import sys, os + import sys sys.path.insert(0, os.path.dirname(__file__)) import common @@ -416,6 +416,8 @@ def setUp(self): self.requires_message("EthereumTxMetadata") self.setup_mnemonic_nopin_nopassphrase() if os.environ.get("KEEPKEY_RUNTIME_PROVIDER") == "1": + # Explicit CI mode requires the fixture and its dependencies. + # Missing ecdsa must fail rather than skip signature validation. self.client.apply_policy("AdvancedMode", True) from runtime_provider import load_test_provider load_test_provider(self.client)