Skip to content

Commit

Permalink
feat(core): implement a new Trezor-Host protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
M1nd3r committed Oct 10, 2024
1 parent 4d7c3bc commit 98c4f11
Show file tree
Hide file tree
Showing 298 changed files with 18,200 additions and 6,063 deletions.
1 change: 1 addition & 0 deletions ci/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ core unix frozen debug build:
needs: []
variables:
PYOPT: "0"
THP: "1"
script:
- $NIX_SHELL --run "poetry run make -C core build_unix_frozen"
artifacts:
Expand Down
2 changes: 2 additions & 0 deletions common/protob/messages-common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ message Failure {
Failure_PinMismatch = 12;
Failure_WipeCodeMismatch = 13;
Failure_InvalidSession = 14;
Failure_ThpUnallocatedSession=15;
Failure_InvalidProtocol=16;
Failure_FirmwareError = 99;
}
}
Expand Down
5 changes: 5 additions & 0 deletions common/protob/messages-debug.proto
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ message DebugLinkGetState {
// trezor-core only - wait until current layout changes
// changed in 2.6.4: multiple wait types instead of true/false.
optional DebugWaitType wait_layout = 3 [default=IMMEDIATE];

optional bytes thp_channel_id=4; // THP only - used to get information from particular channel
}

/**
Expand All @@ -130,6 +132,9 @@ message DebugLinkState {
optional uint32 reset_word_pos = 11; // index of mnemonic word the device is expecting during ResetDevice workflow
optional management.BackupType mnemonic_type = 12; // current mnemonic type (BIP-39/SLIP-39)
repeated string tokens = 13; // current layout represented as a list of string tokens
optional uint32 thp_pairing_code_entry_code = 14;
optional bytes thp_pairing_code_qr_code = 15;
optional bytes thp_pairing_code_nfc_unidirectional = 16;
}

/**
Expand Down
183 changes: 183 additions & 0 deletions common/protob/messages-thp.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,189 @@ option (include_in_bitcoin_only) = true;

import "messages.proto";

/**
* Numeric identifiers of pairing methods.
* @embed
*/
enum ThpPairingMethod {
NoMethod = 1; // Trust without MITM protection.
CodeEntry = 2; // User types code diplayed on Trezor into the host application.
QrCode = 3; // User scans code displayed on Trezor into host application.
NFC_Unidirectional = 4; // Trezor transmits an authentication key to the host device via NFC.
}

/**
* @embed
*/
message ThpDeviceProperties {
optional string internal_model = 1; // Internal model name e.g. "T2B1".
optional uint32 model_variant = 2; // Encodes the device properties such as color.
optional bool bootloader_mode = 3; // Indicates whether the device is in bootloader or firmware mode.
optional uint32 protocol_version = 4; // The communication protocol version supported by the firmware.
repeated ThpPairingMethod pairing_methods = 5; // The pairing methods supported by the Trezor.
}

/**
* @embed
*/
message ThpHandshakeCompletionReqNoisePayload {
optional bytes host_pairing_credential = 1; // Host's pairing credential
repeated ThpPairingMethod pairing_methods = 2; // The pairing methods chosen by the host
}

/**
* Request: Ask device for a new session with given passphrase.
* @start
* @next ThpNewSession
*/
message ThpCreateNewSession{
optional string passphrase = 1;
optional bool on_device = 2; // User wants to enter passphrase on the device
optional bool derive_cardano = 3; // If True, Cardano keys will be derived. Ignored with BTC-only
}

/**
* Response: Contains session_id of the newly created session.
* @end
*/
message ThpNewSession{
optional uint32 new_session_id = 1;
}

/**
* Request: Start pairing process.
* @start
* @next ThpCodeEntryCommitment
* @next ThpPairingPreparationsFinished
*/
message ThpStartPairingRequest{
optional string host_name = 1; // Human-readable host name
}

/**
* Response: Pairing is ready for user input / OOB communication.
* @next ThpCodeEntryCpace
* @next ThpQrCodeTag
* @next ThpNfcUnidirectionalTag
*/
message ThpPairingPreparationsFinished{
}

/**
* Response: If Code Entry is an allowed pairing option, Trezor responds with a commitment.
* @next ThpCodeEntryChallenge
*/
message ThpCodeEntryCommitment {
optional bytes commitment = 1; // SHA-256 of Trezor's random 32-byte secret
}

/**
* Response: Host responds to Trezor's Code Entry commitment with a challenge.
* @next ThpPairingPreparationsFinished
*/
message ThpCodeEntryChallenge {
optional bytes challenge = 1; // host's random 32-byte challenge
}

/**
* Request: User selected Code Entry option in Host. Host starts CPACE protocol with Trezor.
* @next ThpCodeEntryCpaceTrezor
*/
message ThpCodeEntryCpaceHost {
optional bytes cpace_host_public_key = 1; // Host's ephemeral CPace public key
}

/**
* Response: Trezor continues with the CPACE protocol.
* @next ThpCodeEntryTag
*/
message ThpCodeEntryCpaceTrezor {
optional bytes cpace_trezor_public_key = 1; // Trezor's ephemeral CPace public key
}

/**
* Response: Host continues with the CPACE protocol.
* @next ThpCodeEntrySecret
*/
message ThpCodeEntryTag {
optional bytes tag = 2; // SHA-256 of shared secret
}

/**
* Response: Trezor finishes the CPACE protocol.
* @next ThpCredentialRequest
* @next ThpEndRequest
*/
message ThpCodeEntrySecret {
optional bytes secret = 1; // Trezor's secret
}

/**
* Request: User selected QR Code pairing option. Host sends a QR Tag.
* @next ThpQrCodeSecret
*/
message ThpQrCodeTag {
optional bytes tag = 1; // SHA-256 of shared secret
}

/**
* Response: Trezor sends the QR secret.
* @next ThpCredentialRequest
* @next ThpEndRequest
*/
message ThpQrCodeSecret {
optional bytes secret = 1; // Trezor's secret
}

/**
* Request: User selected Unidirectional NFC pairing option. Host sends an Unidirectional NFC Tag.
* @next ThpNfcUnidirectionalSecret
*/
message ThpNfcUnidirectionalTag {
optional bytes tag = 1; // SHA-256 of shared secret
}

/**
* Response: Trezor sends the Unidirectioal NFC secret.
* @next ThpCredentialRequest
* @next ThpEndRequest
*/
message ThpNfcUnidirectionalSecret {
optional bytes secret = 1; // Trezor's secret
}

/**
* Request: Host requests issuance of a new pairing credential.
* @start
* @next ThpCredentialResponse
*/
message ThpCredentialRequest {
optional bytes host_static_pubkey = 1; // Host's static public key used in the handshake.
}

/**
* Response: Trezor issues a new pairing credential.
* @next ThpCredentialRequest
* @next ThpEndRequest
*/
message ThpCredentialResponse {
optional bytes trezor_static_pubkey = 1; // Trezor's static public key used in the handshake.
optional bytes credential = 2; // The pairing credential issued by the Trezor to the host.
}

/**
* Request: Host requests transition to the encrypted traffic phase.
* @start
* @next ThpEndResponse
*/
message ThpEndRequest {}

/**
* Response: Trezor approves transition to the encrypted traffic phase
* @end
*/
message ThpEndResponse {}

/**
* Only for internal use.
* @embed
Expand Down
26 changes: 26 additions & 0 deletions common/protob/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ extend google.protobuf.EnumValueOptions {
optional bool wire_tiny = 50006; // message is handled by Trezor when the USB stack is in tiny mode
optional bool wire_bootloader = 50007; // message is only handled by Trezor Bootloader
optional bool wire_no_fsm = 50008; // message is not handled by Trezor unless the USB stack is in tiny mode
optional bool channel_in = 50009;
optional bool channel_out = 50010;
optional bool pairing_in = 50011;
optional bool pairing_out = 50012;

optional bool bitcoin_only = 60000; // enum value is available on BITCOIN_ONLY build
// (messages not marked bitcoin_only will be EXCLUDED)
Expand Down Expand Up @@ -376,4 +380,26 @@ enum MessageType {
MessageType_SolanaAddress = 903 [(wire_out) = true];
MessageType_SolanaSignTx = 904 [(wire_in) = true];
MessageType_SolanaTxSignature = 905 [(wire_out) = true];

// THP
MessageType_ThpCreateNewSession = 1000[(bitcoin_only)=true, (channel_in) = true];
MessageType_ThpNewSession = 1001[(bitcoin_only)=true, (channel_out) = true];
MessageType_ThpStartPairingRequest = 1008 [(bitcoin_only) = true, (pairing_in) = true];
MessageType_ThpPairingPreparationsFinished = 1009 [(bitcoin_only) = true, (pairing_out) = true];
MessageType_ThpCredentialRequest = 1010 [(bitcoin_only) = true, (pairing_in) = true];
MessageType_ThpCredentialResponse = 1011 [(bitcoin_only) = true, (pairing_out) = true];
MessageType_ThpEndRequest = 1012 [(bitcoin_only) = true, (pairing_in) = true];
MessageType_ThpEndResponse = 1013[(bitcoin_only) = true, (pairing_out) = true];
MessageType_ThpCodeEntryCommitment = 1016[(bitcoin_only)=true, (pairing_out) = true];
MessageType_ThpCodeEntryChallenge = 1017[(bitcoin_only)=true, (pairing_in) = true];
MessageType_ThpCodeEntryCpaceHost = 1018[(bitcoin_only)=true, (pairing_in) = true];
MessageType_ThpCodeEntryCpaceTrezor = 1019[(bitcoin_only)=true, (pairing_out) = true];
MessageType_ThpCodeEntryTag = 1020[(bitcoin_only)=true, (pairing_in) = true];
MessageType_ThpCodeEntrySecret = 1021[(bitcoin_only)=true, (pairing_out) = true];
MessageType_ThpQrCodeTag = 1024[(bitcoin_only)=true, (pairing_in) = true];
MessageType_ThpQrCodeSecret = 1025[(bitcoin_only)=true, (pairing_out) = true];
MessageType_ThpNfcUnidirectionalTag = 1032[(bitcoin_only)=true, (pairing_in) = true];
MessageType_ThpNfcUnidirectionalSecret = 1033[(bitcoin_only)=true, (pairing_in) = true];
}


2 changes: 2 additions & 0 deletions common/protob/pb2py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ class RustBlobRenderer:
enums = []
cursor = 0
for enum in sorted(self.descriptor.enums, key=lambda e: e.name):
if enum.name == "MessageType":
continue
self.enum_map[enum.name] = cursor
enum_blob = ENUM_ENTRY.build(sorted(v.number for v in enum.value))
enums.append(enum_blob)
Expand Down
10 changes: 8 additions & 2 deletions core/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,20 @@ build_unix: templates ## build unix port
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \
TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" THP="$(THP)" \
PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \
NEW_RENDERING="$(NEW_RENDERING)"
NEW_RENDERING="$(NEW_RENDERING)" TREZOR_MEMPERF="$(TREZOR_MEMPERF)"

build_unix_frozen: templates build_cross ## build unix port with frozen modules
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \
TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \
TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" THP="$(THP)"\
PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \
TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 NEW_RENDERING="$(NEW_RENDERING)"

build_unix_frozen_debug: templates build_cross ## build unix port with frozen modules and DEBUG (PYOPT="0")
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \
TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" THP="$(THP)"\
PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \
TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1

build_unix_debug: templates ## build unix port
$(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \
TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \
Expand Down
19 changes: 18 additions & 1 deletion core/SConscript.firmware
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ FEATURE_FLAGS = {
"AES_GCM": False,
}


if THP:
FEATURE_FLAGS = {
"RDI": True,
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
"AES_GCM": True, # Required for THP encryption
}
else:
FEATURE_FLAGS = {
"RDI": True,
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
"AES_GCM": False,
}
FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"]
if DISABLE_OPTIGA and PYOPT == '0':
FEATURES_WANTED.remove("optiga")
Expand Down Expand Up @@ -567,6 +580,8 @@ if FROZEN:
] if not EVERYTHING else []
))

if THP:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/wire/thp/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/wire/*.py'))

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'storage/*.py',
Expand Down Expand Up @@ -616,6 +631,8 @@ if FROZEN:
SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py',
])
)
if THP:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/thp/*.py'))

if EVERYTHING:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/binance/*.py'))
Expand Down Expand Up @@ -696,7 +713,7 @@ if FROZEN:
source_files = SOURCE_MOD + SOURCE_MOD_CRYPTO + SOURCE_FIRMWARE + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED + SOURCE_HAL
obj_program = []
obj_program.extend(env.Object(source=SOURCE_MOD))
obj_program.extend(env.Object(source=SOURCE_MOD_CRYPTO, CCFLAGS='$CCFLAGS -ftrivial-auto-var-init=zero'))
obj_program.extend(env.Object(source=SOURCE_MOD_CRYPTO))
if FEATURE_FLAGS["SECP256K1_ZKP"]:
obj_program.extend(env.Object(source=SOURCE_MOD_SECP256K1_ZKP, CCFLAGS='$CCFLAGS -Wno-unused-function'))
source_files.extend(SOURCE_MOD_SECP256K1_ZKP)
Expand Down
4 changes: 3 additions & 1 deletion core/SConscript.unix
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ if ARGUMENTS.get('TREZOR_EMULATOR_DEBUGGABLE', '0') == '1':

if ARGUMENTS.get('TREZOR_MEMPERF', '0') == '1':
CPPDEFINES_MOD += [
('MICROPY_TREZOR_MEMPERF', '\(1\)')
('MICROPY_TREZOR_MEMPERF', '1')
]

env.Replace(
Expand Down Expand Up @@ -633,6 +633,8 @@ if FROZEN:
] if not EVERYTHING else []
))

if THP:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/wire/thp/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/wire/*.py'))

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'storage/*.py',
Expand Down
12 changes: 6 additions & 6 deletions core/embed/extmod/modtrezorcrypto/modtrezorcrypto-aesgcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_AesGcm_encrypt_obj,
mod_trezorcrypto_AesGcm_encrypt);

/// def encrypt_in_place(self, data: bytearray | memoryview) -> int:
/// """
/// Encrypt data chunk in place. Returns the length of the encrypted data.
/// """
/// """
/// Encrypt data chunk in place. Returns the length of the encrypted data.
/// """
STATIC mp_obj_t mod_trezorcrypto_AesGcm_encrypt_in_place(mp_obj_t self,
mp_obj_t data) {
mp_obj_AesGcm_t *o = MP_OBJ_TO_PTR(self);
Expand Down Expand Up @@ -158,9 +158,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_AesGcm_decrypt_obj,
mod_trezorcrypto_AesGcm_decrypt);

/// def decrypt_in_place(self, data: bytearray | memoryview) -> int:
/// """
/// Decrypt data chunk in place. Returns the length of the decrypted data.
/// """
/// """
/// Decrypt data chunk in place. Returns the length of the decrypted data.
/// """
STATIC mp_obj_t mod_trezorcrypto_AesGcm_decrypt_in_place(mp_obj_t self,
mp_obj_t data) {
mp_obj_AesGcm_t *o = MP_OBJ_TO_PTR(self);
Expand Down
2 changes: 1 addition & 1 deletion core/embed/extmod/modtrezorutils/modtrezorutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ STATIC mp_obj_tuple_t mod_trezorutils_version_obj = {
/// UI_LAYOUT: str
/// """UI layout identifier ("tt" for model T, "tr" for models One and R)."""
/// USE_THP: bool
/// """Whether the firmware supports Trezor-Host Protocol (version 3)."""
/// """Whether the firmware supports the Trezor-Host Protocol."""

STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorutils)},
Expand Down
Loading

0 comments on commit 98c4f11

Please sign in to comment.