refactor(python): extract model definition into single file
What changed, and why it matters
This is a code cleanup (refactor) in the Python trezorlib library. It moves Trezor hardware model definitions (names, USB IDs, firmware verification keys, hash settings, UI layout, BLE capability) from several scattered files into one shared module. The actual data values appear unchanged; only the organization of the code changed. There is no indication this fixes a security bug or introduces a vulnerability.
No security action required. Treat as normal code-review/QA for a large refactor; verify downstream tests pass and that no model constants were accidentally changed during the move.
Security signals we found
No security-relevant behavioral change observed
No new attack surface introduced
No change to keys, signature thresholds, hash parameters, or trust model
Refactor only: consolidation of existing model metadata
Evidence from the diff
The commit creates python/src/trezorlib/_modeldata/ as a single-source-of-truth for model metadata and verification keys. It removes duplicated per-model ModelKeys and FirmwareHashParameters definitions from firmware/models.py and per-model TrezorModel constructors from models.py, replacing them with generated mappings from the registry. It updates cli/firmware.py, debuglink.py, and transport/ble.py to consume the registry. No cryptographic keys, thresholds, hash algorithms, chunk sizes, or padding bytes were altered in the visible diff; the values are copied verbatim. The change is purely architectural.
Changed components
python/src/trezorlib/_modeldata/*python/src/trezorlib/models.pypython/src/trezorlib/firmware/models.pypython/src/trezorlib/cli/firmware.pypython/src/trezorlib/debuglink.pypython/src/trezorlib/transport/ble.pyInspect captured patch +875 / −383
diff --git a/python/.changelog.d/7212.added b/python/.changelog.d/7212.added
new file mode 100644
index 00000000..e609c538
--- /dev/null
+++ b/python/.changelog.d/7212.added
@@ -0,0 +1 @@
+Refactored Trezor model definitions into a single-source module shared by `trezorlib.models` and `trezorlib.firmware.models`.
diff --git a/python/src/trezorlib/_modeldata/D001.py b/python/src/trezorlib/_modeldata/D001.py
new file mode 100644
index 00000000..24bb183c
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/D001.py
@@ -0,0 +1,43 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Discovery board DISC1 (D001) — single-file model definition.
+
+Dev board: it has no dedicated production keys, so its "production" slot reuses
+the shared core dev keys (production=False), exactly like firmware/models.py."""
+
+import hashlib
+
+from . import HashParams, Layout, ModelClass, ModelData
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="D001",
+ name="DISC1",
+ hw_model=b"D001",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.BOLT,
+ ble_capable=False,
+ prod_keys=TREZOR_CORE_DEV,
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.blake2s,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/D002.py b/python/src/trezorlib/_modeldata/D002.py
new file mode 100644
index 00000000..c0bd9bbe
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/D002.py
@@ -0,0 +1,43 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Discovery board DISC2 (D002) — single-file model definition.
+
+Dev board: reuses the shared core dev keys for its "production" slot
+(production=False), exactly like firmware/models.py."""
+
+import hashlib
+
+from . import HashParams, Layout, ModelClass, ModelData
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="D002",
+ name="DISC2",
+ hw_model=b"D002",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.BOLT,
+ ble_capable=False,
+ prod_keys=TREZOR_CORE_DEV,
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 256,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/D003.py b/python/src/trezorlib/_modeldata/D003.py
new file mode 100644
index 00000000..41a9a48b
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/D003.py
@@ -0,0 +1,43 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Discovery board DISC3 (D003) — single-file model definition.
+
+Dev board: reuses the shared core dev keys for its "production" slot
+(production=False), exactly like firmware/models.py."""
+
+import hashlib
+
+from . import HashParams, Layout, ModelClass, ModelData
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="D003",
+ name="DISC3",
+ hw_model=b"D003",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.BOLT,
+ ble_capable=False,
+ prod_keys=TREZOR_CORE_DEV,
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 256,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T1B1.py b/python/src/trezorlib/_modeldata/T1B1.py
new file mode 100644
index 00000000..1f716f6f
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T1B1.py
@@ -0,0 +1,48 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor One (T1B1) — single-file model definition."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import LEGACY_V3_DEV
+
+MODEL = ModelData(
+ internal_name="T1B1",
+ name="1",
+ hw_model=b"T1B1",
+ minimum_version=(1, 8, 0),
+ aliases=("1", "one"),
+ model_class=ModelClass.LEGACY,
+ layout=Layout.T1,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ firmware_keys=keys(
+ "032300c1bb4539fcbfca2590bda3dd2093826f4ae437bddecc1a2e72520764ff7a",
+ "0233baeaebc94a2a3e8b11f39a7133dbf427be292fcbceb887d71ef51e85395a19",
+ "0357091fa254b55233d0bb4c48e106c91b92fd0788ebed9d3a916719f44c76c015",
+ ),
+ firmware_sigs_needed=2,
+ ),
+ dev_keys=LEGACY_V3_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 64,
+ padding_byte=b"\xff",
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T2B1.py b/python/src/trezorlib/_modeldata/T2B1.py
new file mode 100644
index 00000000..3ec2b080
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T2B1.py
@@ -0,0 +1,54 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor Safe 3 (T2B1) — single-file model definition."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T2B1",
+ name="Safe 3",
+ hw_model=b"T2B1",
+ minimum_version=(2, 3, 0),
+ aliases=("r", "safe3", "s3"),
+ model_class=ModelClass.CORE,
+ layout=Layout.CAESAR,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ boardloader_keys=keys(
+ "549a45557008d5518a9a151dc6a3568cf73830a7fe419f2626d9f30d024b2bec",
+ "c16c7027f8a3962607bf24cdec2e3cd2344e1f6071e8260b3dda52b1a5107cb7",
+ "87180f933178b2832bee2d7046c7f4b98300ca7d7fb2e4567169c8730a1c4020",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "bf4e6f004fcb32cec683f22c88c1a86c1518c6de8ac97002d84a63bea3e375dd",
+ "d2def691c1e9d809d8190cf7af935c10688f68983479b4ee9abac19104878ec1",
+ "07c85134946bf89fa19bdc2c5e5ff9ce01296508ee0863d0ff6d63331d1a2516",
+ ),
+ bootloader_sigs_needed=2,
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.blake2s,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T2T1.py b/python/src/trezorlib/_modeldata/T2T1.py
new file mode 100644
index 00000000..420f2d0c
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T2T1.py
@@ -0,0 +1,54 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor T (T2T1) — single-file model definition."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T2T1",
+ name="T",
+ hw_model=b"T2T1",
+ minimum_version=(2, 3, 0),
+ aliases=("t",),
+ model_class=ModelClass.CORE,
+ layout=Layout.BOLT,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ boardloader_keys=keys(
+ "0eb9856be9ba7e972c7f34eac1ed9b6fd0efd172ec00faf0c589759da4ddfba0",
+ "ac8ab40b32c98655798fd5da5e192be27a22306ea05c6d277cdff4a3f4125cd8",
+ "ce0fcd12543ef5936cf2804982136707863d17295faced72af171d6e6513ff06",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "c2c87a49c5a3460977fbb2ec9dfe60f06bd694db8244bd4981fe3b7a26307f3f",
+ "80d036b08739b846f4cb77593078deb25dc9487aedcf52e30b4fb7cd7024178a",
+ "b8307a71f552c60a4cbb317ff48b82cdbf6b6bb5f04c920fec7badf017883751",
+ ),
+ bootloader_sigs_needed=2,
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.blake2s,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T3B1.py b/python/src/trezorlib/_modeldata/T3B1.py
new file mode 100644
index 00000000..ea0d61a0
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T3B1.py
@@ -0,0 +1,54 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor Safe 3 / rev2 (T3B1) — single-file model definition."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T3B1",
+ name="Safe 3",
+ hw_model=b"T3B1",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.CAESAR,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ boardloader_keys=keys(
+ "bbc21adbc1b44d6bfe10c5223de33c28429e52680707d3249007ed42dcc5be13",
+ "22e42a301f3b6ff4f2e6926bce4359e83fc83f0f4a84a7338959c1fd0e29dc13",
+ "7f478f5fb78d8c054b720d8104bf2f6487e452402458979b5525c290dc344d32",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "41d9884801377cff04b0b459fc9b56af1b51f47343a3a6e4fdc1eacabcad7756",
+ "23ec4ec4674d68ac5431e8ba84d7ac24cb5a66702ec565014d164a72182a66c7",
+ "8a7dac53e1be46607231920b0c71056a27be16b67a2fc0d8644d5f8708a28dd1",
+ ),
+ bootloader_sigs_needed=2,
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T3T1.py b/python/src/trezorlib/_modeldata/T3T1.py
new file mode 100644
index 00000000..de270231
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T3T1.py
@@ -0,0 +1,54 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor Safe 5 (T3T1) — single-file model definition."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T3T1",
+ name="Safe 5",
+ hw_model=b"T3T1",
+ minimum_version=(2, 3, 0),
+ aliases=("safe5", "s5"),
+ model_class=ModelClass.CORE,
+ layout=Layout.DELIZIA,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ boardloader_keys=keys(
+ "76af426e61406bad7c077b409c66fde39fb817919313ae1e4c02535c80beed96",
+ "619751dc8d2d09d7e5dfb99e41f606debdf419f85a8143e8e5399ea67a3988c7",
+ "abf94b6615a7dde2a871f7d62c38efc7d9d8f6010d8846bee636e4f3e658a38c",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "338b949b7e3b26470d4fe3696fd6fff28757265d14cca48ebf2db97b4f5bc039",
+ "28682027730b783201b05a8c9d11685447c17297db71b8a60dc693a44610751d",
+ "9fbf31b4e351a4cc81c75995b2257f0a7169268da5a44e94b6a5590d434e32da",
+ ),
+ bootloader_sigs_needed=2,
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T3T2.py b/python/src/trezorlib/_modeldata/T3T2.py
new file mode 100644
index 00000000..f80d1bd5
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T3T2.py
@@ -0,0 +1,58 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor T3T2 — single-file model definition.
+
+Mirrors core/embed/models/T3T2/: model.toml (layout_delizia, no BLE) and
+model_T3T2.h (keys, IMAGE_CHUNK_SIZE 128K, IMAGE_HASH_SHA256)."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T3T2",
+ name="T3T2",
+ hw_model=b"T3T2",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.DELIZIA,
+ ble_capable=False,
+ prod_keys=KeySet(
+ production=True,
+ # TODO replace with production keys (matches firmware/models.py today)
+ boardloader_keys=keys(
+ "76af426e61406bad7c077b409c66fde39fb817919313ae1e4c02535c80beed96",
+ "619751dc8d2d09d7e5dfb99e41f606debdf419f85a8143e8e5399ea67a3988c7",
+ "abf94b6615a7dde2a871f7d62c38efc7d9d8f6010d8846bee636e4f3e658a38c",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "338b949b7e3b26470d4fe3696fd6fff28757265d14cca48ebf2db97b4f5bc039",
+ "28682027730b783201b05a8c9d11685447c17297db71b8a60dc693a44610751d",
+ "9fbf31b4e351a4cc81c75995b2257f0a7169268da5a44e94b6a5590d434e32da",
+ ),
+ bootloader_sigs_needed=2,
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 128,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/T3W1.py b/python/src/trezorlib/_modeldata/T3W1.py
new file mode 100644
index 00000000..c97e8395
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/T3W1.py
@@ -0,0 +1,68 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Trezor Safe 7 (T3W1) — single-file model definition.
+
+Exercises every axis: secmon keys, NRF/BLE keys, BLE capability, Eckhart
+layout, 256K hash chunk. Mirrors core/embed/models/T3W1/."""
+
+import hashlib
+
+from . import HashParams, KeySet, Layout, ModelClass, ModelData, keys
+from ._shared import TREZOR_CORE_DEV
+
+MODEL = ModelData(
+ internal_name="T3W1",
+ name="Safe 7",
+ hw_model=b"T3W1",
+ minimum_version=(2, 3, 0),
+ aliases=(),
+ model_class=ModelClass.CORE,
+ layout=Layout.ECKHART,
+ ble_capable=True,
+ prod_keys=KeySet(
+ production=True,
+ boardloader_keys=keys(
+ "e8912f81b3e780ee650ed3856db5326e0b9eff10364b339193e7a8f10f7621b9",
+ "bde70a38eee633d26f434eee2f536df457b8deb8bd988294f4a0c8d9054903d2",
+ "a85b601dfbda1d22ccb5dd492d26034d87f67f2a0b8584b7774439461fc471a9",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "320e111e9dded5fe7f5d41fd372ef0e91b2dfa4c6cdc9fe5221bfb16aaf91775",
+ "2e349f8d06b2334262ecb603ed04cb5a7cc0b660ebe3cd5c2972b5cd1f38ef85",
+ "ab0d3f91a4adf744719dba661783ec549f73a4e45457cb6d02752a40fb63d3bf",
+ ),
+ bootloader_sigs_needed=2,
+ secmon_keys=keys(
+ "7da3dd4769fef0f9489d5ff7fba8be122aef0f60778302557ba2cc67ff2a6d9e",
+ "4ae3bf88b0e5226322d867432940265b4bef46e5c45b64730e26ca32ee653e0b",
+ "6c1640f38d037c57e86960863505ef70ff60f98157440cf25f1c133b4a15960e",
+ ),
+ secmon_sigs_needed=2,
+ nrf_keys=keys(
+ "d1bad5e8c73dfe183ba1bd5464b2c96f1d1de66d53c95026d17169148d096f3e",
+ "585f0635efc6518c490228a72ae1f5d0808ebe77f1c12516eb6d525821eb1e21",
+ "065ee19b0de4eec3be70938935313ca2949cc3808b2bf3ad7ef0ac419a974191",
+ ),
+ ),
+ dev_keys=TREZOR_CORE_DEV,
+ hash_params=HashParams(
+ hash_function=hashlib.sha256,
+ chunk_size=1024 * 256,
+ padding_byte=None,
+ ),
+)
diff --git a/python/src/trezorlib/_modeldata/__init__.py b/python/src/trezorlib/_modeldata/__init__.py
new file mode 100644
index 00000000..ad56fed2
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/__init__.py
@@ -0,0 +1,119 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Unified, single-source-of-truth model data.
+
+This package holds one file per Trezor model. Each file declares a single
+``MODEL`` of type :class:`ModelData` carrying *everything* trezorlib needs to
+know about that model: identity, release metadata, UI layout, BLE capability,
+and firmware-verification keys / hash parameters.
+
+Design constraints that shaped this layout:
+
+* **No protobuf dependency.** This module deliberately imports only the stdlib,
+ so it sits *below* both ``trezorlib.models`` (high level, pulls in protobuf
+ ``mapping``) and ``trezorlib.firmware.models`` (verification layer). Both can
+ consume it without creating an import cycle or dragging protobuf into the
+ firmware-parsing path.
+* **Constant fields are not stored per model.** ``vendors``, ``usb_ids`` and
+ ``default_mapping`` are identical across a model *class* (legacy vs. core), so
+ the aggregator injects them; they don't belong in per-model files.
+* **Shared dev keys are shared.** Every core model verifies against the same
+ ``TREZOR_CORE_DEV`` dev keys, so those live here once, not copied into nine
+ files.
+
+Fields are annotated below with their eventual generation source once the
+firmware-side codegen exists. Fields marked "sidecar" are NOT present anywhere
+in ``core/embed/models`` today and would need a ``[trezorlib]`` block in
+``model.toml`` (or a parallel metadata file) to feed the generator.
+"""
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Callable, Optional, Tuple
+
+
+def keys(*hexes: str) -> Tuple[bytes, ...]:
+ """Helper: turn hex strings into a tuple of key bytes."""
+ return tuple(bytes.fromhex(h) for h in hexes)
+
+
+class ModelClass(Enum):
+ """Coarse hardware family. Drives the constant fields the aggregator fills
+ in (USB ids, image padding byte, which shared dev key set applies)."""
+
+ LEGACY = "legacy" # Trezor One
+ CORE = "core" # everything STM32-based
+
+
+class Layout(Enum):
+ """UI layout. Source: the ``layout_*`` feature in ``model.toml``."""
+
+ T1 = "T1"
+ BOLT = "Bolt"
+ CAESAR = "Caesar"
+ DELIZIA = "Delizia"
+ ECKHART = "Eckhart"
+
+
+@dataclass(frozen=True)
+class KeySet:
+ """Firmware-verification keys for one model.
+
+ Production keys come from ``MODEL_*_KEYS`` in ``model_<NAME>.h``. The
+ ``*_sigs_needed`` thresholds and ``nrf_keys`` are NOT in any model file
+ today (sidecar). ``production`` is intrinsic to the key set (dev key sets,
+ and the dev keys that discovery boards reuse, are not production)."""
+
+ production: bool = False
+ boardloader_keys: Tuple[bytes, ...] = ()
+ boardloader_sigs_needed: int = -1
+ bootloader_keys: Tuple[bytes, ...] = ()
+ bootloader_sigs_needed: int = -1
+ firmware_keys: Tuple[bytes, ...] = ()
+ firmware_sigs_needed: int = -1
+ secmon_keys: Tuple[bytes, ...] = ()
+ secmon_sigs_needed: int = -1
+ nrf_keys: Tuple[bytes, ...] = ()
+
+
+@dataclass(frozen=True)
+class HashParams:
+ """Firmware image hashing. Source: ``IMAGE_HASH_*`` / ``IMAGE_CHUNK_SIZE``
+ in ``model_<NAME>.h``; ``padding_byte`` follows from ``model_class``."""
+
+ hash_function: Callable
+ chunk_size: int
+ padding_byte: Optional[bytes]
+
+
+@dataclass(frozen=True)
+class ModelData:
+ # --- identity (source: model.toml dir name / MODEL_NAME in header) ---
+ internal_name: str
+ name: str
+ hw_model: bytes
+ # --- release / protocol metadata (sidecar: not in firmware files) ---
+ minimum_version: Tuple[int, int, int]
+ aliases: Tuple[str, ...] = ()
+ # --- hardware / UI (source: model.toml features) ---
+ model_class: ModelClass = ModelClass.CORE
+ layout: Layout = Layout.BOLT
+ ble_capable: bool = False
+ # --- firmware verification ---
+ prod_keys: KeySet = field(default_factory=KeySet)
+ dev_keys: KeySet = field(default_factory=KeySet)
+ hash_params: Optional[HashParams] = None
diff --git a/python/src/trezorlib/_modeldata/_shared.py b/python/src/trezorlib/_modeldata/_shared.py
new file mode 100644
index 00000000..fbf5a28a
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/_shared.py
@@ -0,0 +1,59 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Key sets shared across multiple models.
+
+Dev keys are identical for every core model, and the legacy dev set is shared
+by the One. Keeping them here avoids copying the same blob into every per-model
+file. Mirrors ``TREZOR_CORE_DEV`` / ``LEGACY_V3_DEV`` in firmware/models.py."""
+
+from . import KeySet, keys
+
+# Shared dev keys for all STM32-based (core) models.
+TREZOR_CORE_DEV = KeySet(
+ boardloader_keys=keys(
+ "db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d",
+ "2152f8d19b791d24453242e15f2eab6cb7cffa7b6a5ed30097960e069881db12",
+ "22fc297792f0b6ffc0bfcfdb7edb0c0aa14e025a365ec0e342e86e3829cb74b6",
+ ),
+ boardloader_sigs_needed=2,
+ bootloader_keys=keys(
+ "d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48",
+ "6355691c178a8ff91007a7478afb955ef7352c63e7b25703984cf78b26e21a56",
+ "ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148",
+ ),
+ bootloader_sigs_needed=2,
+ secmon_keys=keys(
+ "db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d",
+ "2152f8d19b791d24453242e15f2eab6cb7cffa7b6a5ed30097960e069881db12",
+ "22fc297792f0b6ffc0bfcfdb7edb0c0aa14e025a365ec0e342e86e3829cb74b6",
+ ),
+ secmon_sigs_needed=2,
+ nrf_keys=keys(
+ "d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48",
+ "6355691c178a8ff91007a7478afb955ef7352c63e7b25703984cf78b26e21a56",
+ ),
+)
+
+# Shared dev keys for the legacy Trezor One (firmware-only signing).
+LEGACY_V3_DEV = KeySet(
+ firmware_keys=keys(
+ "037308e14077161c365dea0f5c80aa6c5dba34719e825bd23ae5f7e7d2988adb0f",
+ "039c1b2460e343712e982e0732e7ed17f60de4c933065b7170d99c6e7fe7cc7f4b",
+ "03152b37fdf126111274c894c348dcc975b57c115ee24ceb19b5190ac7f7b65173",
+ ),
+ firmware_sigs_needed=2,
+)
diff --git a/python/src/trezorlib/_modeldata/registry.py b/python/src/trezorlib/_modeldata/registry.py
new file mode 100644
index 00000000..8d0c9178
--- /dev/null
+++ b/python/src/trezorlib/_modeldata/registry.py
@@ -0,0 +1,43 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 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>.
+
+"""Aggregated registry of all model definitions.
+
+Once the firmware-side generator exists, the import list below is the only
+thing it needs to emit/maintain (one line per ``core/embed/models/<NAME>``)."""
+
+from typing import Dict, Optional, Tuple
+
+from . import D001, D002, D003, T1B1, T2B1, T2T1, T3B1, T3T1, T3T2, T3W1, ModelData
+
+ALL: Tuple[ModelData, ...] = (
+ T1B1.MODEL,
+ T2T1.MODEL,
+ T2B1.MODEL,
+ T3T1.MODEL,
+ T3T2.MODEL,
+ T3B1.MODEL,
+ T3W1.MODEL,
+ D001.MODEL,
+ D002.MODEL,
+ D003.MODEL,
+)
+
+BY_INTERNAL_NAME: Dict[str, ModelData] = {m.internal_name: m for m in ALL}
+
+
+def by_internal_name(internal_name: str) -> Optional[ModelData]:
+ return BY_INTERNAL_NAME.get(internal_name)
diff --git a/python/src/trezorlib/cli/firmware.py b/python/src/trezorlib/cli/firmware.py
index f5fe1f6c..1109eebb 100644
--- a/python/src/trezorlib/cli/firmware.py
+++ b/python/src/trezorlib/cli/firmware.py
@@ -42,24 +42,13 @@ if TYPE_CHECKING:
from ..client import Session, TrezorClient
from . import TrezorConnection
+# Built from the single-source model registry: every model is selectable by its
+# internal name, plus any aliases it declares. Adding a model (or alias) in
+# trezorlib._modeldata makes it available here automatically.
MODEL_CHOICE = ChoiceType(
{
- "T1B1": models.T1B1,
- "T2T1": models.T2T1,
- "T2B1": models.T2B1,
- "T3T1": models.T3T1,
- "T3T2": models.T3T2,
- "T3B1": models.T3B1,
- "T3W1": models.T3W1,
- # aliases
- "1": models.T1B1,
- "one": models.T1B1,
- "t": models.T2T1,
- "r": models.T2B1,
- "safe3": models.T2B1,
- "s3": models.T2B1,
- "safe5": models.T3T1,
- "s5": models.T3T1,
+ **{model.internal_name: model for model in models.ALL_MODELS},
+ **{alias: model for model in models.ALL_MODELS for alias in model.aliases},
},
case_sensitive=False,
)
diff --git a/python/src/trezorlib/debuglink.py b/python/src/trezorlib/debuglink.py
index 4bf6df1e..0f15064d 100644
--- a/python/src/trezorlib/debuglink.py
+++ b/python/src/trezorlib/debuglink.py
@@ -82,33 +82,22 @@ class LayoutType(Enum):
@classmethod
def from_model(cls, model: models.TrezorModel) -> "LayoutType":
- if model in (models.T2T1,):
- return cls.Bolt
- if model in (models.T2B1, models.T3B1):
- return cls.Caesar
- if model in (models.T3T1, models.T3T2):
- return cls.Delizia
- if model in (models.T3W1,):
- return cls.Eckhart
- if model in (models.T1B1,):
- return cls.T1
- internal_name = getattr(model, "internal_name", None)
+ # The model's UI layout is sourced from the single-source model registry
+ # (trezorlib._modeldata); LayoutType member names match those layout
+ # names, so a new model is handled without touching this function.
+ layout = model.layout
+ if layout:
+ return cls[layout]
+ internal_name = model.internal_name
if internal_name:
return cls.from_internal_name(internal_name)
raise ValueError(f"Unknown model: {model}")
@classmethod
def from_internal_name(cls, internal_name: str) -> "LayoutType":
- if internal_name in (models.T2T1.internal_name,):
- return cls.Bolt
- if internal_name in (models.T2B1.internal_name, models.T3B1.internal_name):
- return cls.Caesar
- if internal_name in (models.T3T1.internal_name, models.T3T2.internal_name):
- return cls.Delizia
- if internal_name in (models.T3W1.internal_name,):
- return cls.Eckhart
- if internal_name in (models.T1B1.internal_name,):
- return cls.T1
+ model = models.by_internal_name(internal_name)
+ if model is not None and model.layout:
+ return cls[model.layout]
raise ValueError(f"Unknown internal name: {internal_name}")
def __str__(self) -> str:
diff --git a/python/src/trezorlib/firmware/models.py b/python/src/trezorlib/firmware/models.py
index 744e11b0..d800045d 100644
--- a/python/src/trezorlib/firmware/models.py
+++ b/python/src/trezorlib/firmware/models.py
@@ -16,7 +16,6 @@
from __future__ import annotations
-import hashlib
import typing as t
from dataclasses import dataclass
from enum import Enum
@@ -183,33 +182,6 @@ LEGACY_V3_DEV = ModelKeys(
nrf_keys=(),
)
-T2T1 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- "0eb9856be9ba7e972c7f34eac1ed9b6fd0efd172ec00faf0c589759da4ddfba0",
- "ac8ab40b32c98655798fd5da5e192be27a22306ea05c6d277cdff4a3f4125cd8",
- "ce0fcd12543ef5936cf2804982136707863d17295faced72af171d6e6513ff06",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- "c2c87a49c5a3460977fbb2ec9dfe60f06bd694db8244bd4981fe3b7a26307f3f",
- "80d036b08739b846f4cb77593078deb25dc9487aedcf52e30b4fb7cd7024178a",
- "b8307a71f552c60a4cbb317ff48b82cdbf6b6bb5f04c920fec7badf017883751",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=(),
- secmon_sigs_needed=-1,
- nrf_keys=(),
-)
-
TREZOR_CORE_DEV = ModelKeys(
production=False,
boardloader_keys=[
@@ -250,157 +222,6 @@ TREZOR_CORE_DEV = ModelKeys(
],
)
-T2B1 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- "549a45557008d5518a9a151dc6a3568cf73830a7fe419f2626d9f30d024b2bec",
- "c16c7027f8a3962607bf24cdec2e3cd2344e1f6071e8260b3dda52b1a5107cb7",
- "87180f933178b2832bee2d7046c7f4b98300ca7d7fb2e4567169c8730a1c4020",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- "bf4e6f004fcb32cec683f22c88c1a86c1518c6de8ac97002d84a63bea3e375dd",
- "d2def691c1e9d809d8190cf7af935c10688f68983479b4ee9abac19104878ec1",
- "07c85134946bf89fa19bdc2c5e5ff9ce01296508ee0863d0ff6d63331d1a2516",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=(),
- secmon_sigs_needed=-1,
- nrf_keys=(),
-)
-
-T3T1 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- "76af426e61406bad7c077b409c66fde39fb817919313ae1e4c02535c80beed96",
- "619751dc8d2d09d7e5dfb99e41f606debdf419f85a8143e8e5399ea67a3988c7",
- "abf94b6615a7dde2a871f7d62c38efc7d9d8f6010d8846bee636e4f3e658a38c",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- "338b949b7e3b26470d4fe3696fd6fff28757265d14cca48ebf2db97b4f5bc039",
- "28682027730b783201b05a8c9d11685447c17297db71b8a60dc693a44610751d",
- "9fbf31b4e351a4cc81c75995b2257f0a7169268da5a44e94b6a5590d434e32da",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=(),
- secmon_sigs_needed=-1,
- nrf_keys=(),
-)
-
-T3T2 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- # todo replace with production keys
- "76af426e61406bad7c077b409c66fde39fb817919313ae1e4c02535c80beed96",
- "619751dc8d2d09d7e5dfb99e41f606debdf419f85a8143e8e5399ea67a3988c7",
- "abf94b6615a7dde2a871f7d62c38efc7d9d8f6010d8846bee636e4f3e658a38c",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- # todo replace with production keys
- "338b949b7e3b26470d4fe3696fd6fff28757265d14cca48ebf2db97b4f5bc039",
- "28682027730b783201b05a8c9d11685447c17297db71b8a60dc693a44610751d",
- "9fbf31b4e351a4cc81c75995b2257f0a7169268da5a44e94b6a5590d434e32da",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=(),
- secmon_sigs_needed=-1,
- nrf_keys=(),
-)
-
-T3B1 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- "bbc21adbc1b44d6bfe10c5223de33c28429e52680707d3249007ed42dcc5be13",
- "22e42a301f3b6ff4f2e6926bce4359e83fc83f0f4a84a7338959c1fd0e29dc13",
- "7f478f5fb78d8c054b720d8104bf2f6487e452402458979b5525c290dc344d32",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- "41d9884801377cff04b0b459fc9b56af1b51f47343a3a6e4fdc1eacabcad7756",
- "23ec4ec4674d68ac5431e8ba84d7ac24cb5a66702ec565014d164a72182a66c7",
- "8a7dac53e1be46607231920b0c71056a27be16b67a2fc0d8644d5f8708a28dd1",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=(),
- secmon_sigs_needed=-1,
- nrf_keys=(),
-)
-
-T3W1 = ModelKeys(
- production=True,
- boardloader_keys=[
- bytes.fromhex(key)
- for key in (
- "e8912f81b3e780ee650ed3856db5326e0b9eff10364b339193e7a8f10f7621b9",
- "bde70a38eee633d26f434eee2f536df457b8deb8bd988294f4a0c8d9054903d2",
- "a85b601dfbda1d22ccb5dd492d26034d87f67f2a0b8584b7774439461fc471a9",
- )
- ],
- boardloader_sigs_needed=2,
- bootloader_keys=[
- bytes.fromhex(key)
- for key in (
- "320e111e9dded5fe7f5d41fd372ef0e91b2dfa4c6cdc9fe5221bfb16aaf91775",
- "2e349f8d06b2334262ecb603ed04cb5a7cc0b660ebe3cd5c2972b5cd1f38ef85",
- "ab0d3f91a4adf744719dba661783ec549f73a4e45457cb6d02752a40fb63d3bf",
- )
- ],
- bootloader_sigs_needed=2,
- firmware_keys=(),
- firmware_sigs_needed=-1,
- secmon_keys=[
- bytes.fromhex(key)
- for key in (
- "7da3dd4769fef0f9489d5ff7fba8be122aef0f60778302557ba2cc67ff2a6d9e",
- "4ae3bf88b0e5226322d867432940265b4bef46e5c45b64730e26ca32ee653e0b",
- "6c1640f38d037c57e86960863505ef70ff60f98157440cf25f1c133b4a15960e",
- )
- ],
- secmon_sigs_needed=2,
- nrf_keys=[
- bytes.fromhex(k)
- for k in (
- "d1bad5e8c73dfe183ba1bd5464b2c96f1d1de66d53c95026d17169148d096f3e",
- "585f0635efc6518c490228a72ae1f5d0808ebe77f1c12516eb6d525821eb1e21",
- "065ee19b0de4eec3be70938935313ca2949cc3808b2bf3ad7ef0ac419a974191",
- )
- ],
-)
-
ROOT_ED25519_KEYS = [
bytes.fromhex(key)
for key in (
@@ -442,87 +263,59 @@ ROOT_SLH_DSA_KEYS_DEV_PUBLIC = [
]
-LEGACY_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 64,
- padding_byte=b"\xff",
-)
+# ---- Model dispatch tables, built from the single-source model registry ----
+# trezorlib._modeldata holds one file per model (keys, hash params, layout, ...).
+# Adding a model there makes it appear in all three maps automatically, so these
+# tables no longer need to be hand-maintained.
+
+from .._modeldata import HashParams, KeySet
+from .._modeldata import registry as _registry # noqa: E402
+
+
+def _to_modelkeys(ks: KeySet) -> ModelKeys:
+ """Convert a _modeldata.KeySet into the verification-layer ModelKeys."""
+ return ModelKeys(
+ production=ks.production,
+ boardloader_keys=list(ks.boardloader_keys),
+ boardloader_sigs_needed=ks.boardloader_sigs_needed,
+ bootloader_keys=list(ks.bootloader_keys),
+ bootloader_sigs_needed=ks.bootloader_sigs_needed,
+ firmware_keys=list(ks.firmware_keys),
+ firmware_sigs_needed=ks.firmware_sigs_needed,
+ secmon_keys=list(ks.secmon_keys),
+ secmon_sigs_needed=ks.secmon_sigs_needed,
+ nrf_keys=list(ks.nrf_keys),
+ )
-T2T1_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.blake2s,
- chunk_size=1024 * 128,
- padding_byte=None,
-)
-T3T1_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 128,
- padding_byte=None,
-)
+MODEL_MAP = {Model(d.hw_model): _to_modelkeys(d.prod_keys) for d in _registry.ALL}
-T3T2_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 128,
- padding_byte=None,
-)
+MODEL_MAP_DEV = {Model(d.hw_model): _to_modelkeys(d.dev_keys) for d in _registry.ALL}
-T3B1_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 128,
- padding_byte=None,
-)
-T3W1_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 256,
- padding_byte=None,
-)
-
-D002_HASH_PARAMS = FirmwareHashParameters(
- hash_function=hashlib.sha256,
- chunk_size=1024 * 256,
- padding_byte=None,
-)
-
-MODEL_MAP = {
- Model.T1B1: LEGACY_V3,
- Model.T2T1: T2T1,
- Model.T2B1: T2B1,
- Model.T3T1: T3T1,
- Model.T3T2: T3T2,
- Model.T3B1: T3B1,
- Model.T3W1: T3W1,
- Model.D001: TREZOR_CORE_DEV,
- Model.D002: TREZOR_CORE_DEV,
- Model.D003: TREZOR_CORE_DEV,
-}
+def _to_hashparams(hp: HashParams | None) -> FirmwareHashParameters:
+ assert hp is not None
+ return FirmwareHashParameters(
+ hash_function=hp.hash_function,
+ chunk_size=hp.chunk_size,
+ padding_byte=hp.padding_byte,
+ )
-MODEL_MAP_DEV = {
- Model.T1B1: LEGACY_V3_DEV,
- Model.T2T1: TREZOR_CORE_DEV,
- Model.T2B1: TREZOR_CORE_DEV,
- Model.T3T1: TREZOR_CORE_DEV,
- Model.T3T2: TREZOR_CORE_DEV,
- Model.T3B1: TREZOR_CORE_DEV,
- Model.T3W1: TREZOR_CORE_DEV,
- Model.D001: TREZOR_CORE_DEV,
- Model.D002: TREZOR_CORE_DEV,
- Model.D003: TREZOR_CORE_DEV,
-}
MODEL_HASH_PARAMS_MAP = {
- Model.T1B1: LEGACY_HASH_PARAMS,
- Model.T2T1: T2T1_HASH_PARAMS,
- Model.T2B1: T2T1_HASH_PARAMS,
- Model.T3T1: T3T1_HASH_PARAMS,
- Model.T3T2: T3T2_HASH_PARAMS,
- Model.T3B1: T3B1_HASH_PARAMS,
- Model.T3W1: T3W1_HASH_PARAMS,
- Model.D001: T2T1_HASH_PARAMS,
- Model.D002: D002_HASH_PARAMS,
- Model.D003: D002_HASH_PARAMS,
+ Model(d.hw_model): _to_hashparams(d.hash_params) for d in _registry.ALL
}
+# Per-model key sets, kept as module-level names for backwards compatibility
+# (e.g. firmware.consts, _internal.firmware_headers). The source of truth is the
+# model registry above; these are just convenient handles into MODEL_MAP.
+T2T1 = MODEL_MAP[Model.T2T1]
+T2B1 = MODEL_MAP[Model.T2B1]
+T3T1 = MODEL_MAP[Model.T3T1]
+T3T2 = MODEL_MAP[Model.T3T2]
+T3B1 = MODEL_MAP[Model.T3B1]
+T3W1 = MODEL_MAP[Model.T3W1]
+
# deprecated aliases -- don't add more
TREZOR_ONE_V1V2 = LEGACY_V1V2
@@ -530,10 +323,10 @@ TREZOR_ONE_V1V2_DEV = LEGACY_V1V2_DEV
TREZOR_ONE_V3 = LEGACY_V3
TREZOR_ONE_V3_DEV = LEGACY_V3_DEV
-TREZOR_T = T2T1
-TREZOR_R = T2B1
-TREZOR_T3T1 = T3T1
-TREZOR_T3B1 = T3B1
+TREZOR_T = MODEL_MAP[Model.T2T1]
+TREZOR_R = MODEL_MAP[Model.T2B1]
+TREZOR_T3T1 = MODEL_MAP[Model.T3T1]
+TREZOR_T3B1 = MODEL_MAP[Model.T3B1]
TREZOR_T_DEV = TREZOR_CORE_DEV
TREZOR_R_DEV = TREZOR_CORE_DEV
diff --git a/python/src/trezorlib/models.py b/python/src/trezorlib/models.py
index 9fffb94d..bb9d7447 100644
--- a/python/src/trezorlib/models.py
+++ b/python/src/trezorlib/models.py
@@ -19,7 +19,9 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import Collection, Tuple
+from . import _modeldata as _md
from . import mapping, messages
+from ._modeldata import registry as _registry
UsbId = Tuple[int, int]
@@ -28,6 +30,16 @@ VENDORS = ("bitcointrezor.com", "trezor.io")
@dataclass(eq=True, frozen=True)
class TrezorModel:
+ """Runtime/wire view of a model, built from a single-source
+ :class:`._modeldata.ModelData` via :func:`_build`.
+
+ ``ModelData`` is the protobuf-free source of truth (it also carries the
+ firmware-verification keys/hashes) and deliberately sits *below* this module
+ so ``trezorlib.firmware.models`` can consume it without pulling in protobuf.
+ ``TrezorModel`` is the derived wrapper that adds the protobuf mapping and
+ transport fields (``default_mapping``, ``usb_ids``, ``vendors``); the fields
+ shared with ``ModelData`` are populated from it, not duplicated."""
+
name: str
internal_name: str
minimum_version: tuple[int, int, int]
@@ -36,6 +48,12 @@ class TrezorModel:
default_mapping: mapping.ProtobufMapping
is_unknown: bool = False
+ # UI layout name (matches debuglink.LayoutType members), BLE capability, and
+ # CLI aliases. Sourced from the single-source model registry; None/empty for
+ # ad-hoc unknown models.
+ layout: str | None = None
+ ble_capable: bool = False
+ aliases: tuple[str, ...] = ()
# ==== internal names ====
@@ -45,95 +63,41 @@ USBID_TREZOR_CORE = (0x1209, 0x53C1)
USBID_TREZOR_CORE_BOOTLOADER = (0x1209, 0x53C0)
-T1B1 = TrezorModel(
- name="1",
- internal_name="T1B1",
- minimum_version=(1, 8, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_ONE,),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-
-T2T1 = TrezorModel(
- name="T",
- internal_name="T2T1",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-
-T2B1 = TrezorModel(
- name="Safe 3",
- internal_name="T2B1",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-
-T3T1 = TrezorModel(
- name="Safe 5",
- internal_name="T3T1",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-
-T3T2 = TrezorModel(
- name="T3T2",
- internal_name="T3T2",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
+def _usb_ids(model_class: _md.ModelClass) -> tuple[UsbId, ...]:
+ if model_class is _md.ModelClass.LEGACY:
+ return (USBID_TREZOR_ONE,)
+ return (USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER)
-T3B1 = TrezorModel(
- name="Safe 3",
- internal_name="T3B1",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-T3W1 = TrezorModel(
- name="Safe 7",
- internal_name="T3W1",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
+def _build(data: _md.ModelData) -> TrezorModel:
+ """Build a TrezorModel from a single-source model definition. Constant
+ fields (vendors, usb_ids, default_mapping) are injected here rather than
+ stored per model."""
+ return TrezorModel(
+ name=data.name,
+ internal_name=data.internal_name,
+ minimum_version=data.minimum_version,
+ vendors=VENDORS,
+ usb_ids=_usb_ids(data.model_class),
+ default_mapping=mapping.DEFAULT_MAPPING,
+ layout=data.layout.value,
+ ble_capable=data.ble_capable,
+ aliases=data.aliases,
+ )
-DISC1 = TrezorModel(
- name="DISC1",
- internal_name="D001",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
-DISC2 = TrezorModel(
- name="DISC2",
- internal_name="D002",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
+_BY_INTERNAL = {d.internal_name: _build(d) for d in _registry.ALL}
-DISC3 = TrezorModel(
- name="DISC3",
- internal_name="D003",
- minimum_version=(2, 3, 0),
- vendors=VENDORS,
- usb_ids=(USBID_TREZOR_CORE, USBID_TREZOR_CORE_BOOTLOADER),
- default_mapping=mapping.DEFAULT_MAPPING,
-)
+T1B1 = _BY_INTERNAL["T1B1"]
+T2T1 = _BY_INTERNAL["T2T1"]
+T2B1 = _BY_INTERNAL["T2B1"]
+T3T1 = _BY_INTERNAL["T3T1"]
+T3T2 = _BY_INTERNAL["T3T2"]
+T3B1 = _BY_INTERNAL["T3B1"]
+T3W1 = _BY_INTERNAL["T3W1"]
+D001 = _BY_INTERNAL["D001"]
+D002 = _BY_INTERNAL["D002"]
+D003 = _BY_INTERNAL["D003"]
# ==== model based names ====
@@ -142,12 +106,27 @@ TREZOR_T = T2T1
TREZOR_R = T2B1
TREZOR_SAFE3 = T2B1
TREZOR_SAFE5 = T3T1
-TREZOR_DISC1 = DISC1
-TREZOR_DISC2 = DISC2
-TREZOR_DISC3 = DISC3
-
-LEGACY_MODELS = frozenset({T1B1})
-CORE_MODELS = frozenset({T2T1, T2B1, T3T1, T3T2, T3B1, T3W1, DISC1, DISC2, DISC3})
+TREZOR_DISC1 = D001
+TREZOR_DISC2 = D002
+TREZOR_DISC3 = D003
+
+# deprecated aliases: the discovery boards used to be exposed under DISC1/DISC2,
+# but the canonical handle is now the internal name (D001/D002), like every
+# other model. Kept for backwards compatibility.
+DISC1 = D001
+DISC2 = D002
+DISC3 = D003
+
+LEGACY_MODELS = frozenset(
+ _BY_INTERNAL[d.internal_name]
+ for d in _registry.ALL
+ if d.model_class is _md.ModelClass.LEGACY
+)
+CORE_MODELS = frozenset(
+ _BY_INTERNAL[d.internal_name]
+ for d in _registry.ALL
+ if d.model_class is _md.ModelClass.CORE
+)
ALL_MODELS = LEGACY_MODELS | CORE_MODELS
diff --git a/python/src/trezorlib/transport/ble.py b/python/src/trezorlib/transport/ble.py
index a603940e..62880d6c 100644
--- a/python/src/trezorlib/transport/ble.py
+++ b/python/src/trezorlib/transport/ble.py
@@ -25,7 +25,6 @@ from multiprocessing import Pipe, Process
from multiprocessing.connection import Connection
from ..log import DUMP_PACKETS
-from ..models import T3W1
from . import Timeout, Transport, TransportException
from .udp import UdpTransport
@@ -78,7 +77,9 @@ class BleTransport(Transport):
cls, models: t.Iterable[TrezorModel] | None = None
) -> t.Iterable[BleTransport]:
# TODO use manufacturer_data
- if models and T3W1 not in models:
+ # Skip BLE enumeration unless at least one requested model is BLE-capable
+ # (capability comes from the single-source model registry).
+ if models and not any(model.ble_capable for model in models):
return []
devices = cls.ble_proxy().scan()
return [BleTransport(device[0]) for device in devices]
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.