From 1bf927c943722f10fe9cc6dd8e29a75c655a8ffc Mon Sep 17 00:00:00 2001 From: M1nd3r Date: Mon, 22 Jul 2024 13:38:43 +0200 Subject: [PATCH] feat(core): incorporate handshake hash into pairing --- core/src/apps/thp/pairing.py | 36 ++++++++++++++--------------- core/src/trezor/wire/thp/channel.py | 7 ++++-- core/src/trezor/wire/thp/cpace.py | 13 +++++++---- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/core/src/apps/thp/pairing.py b/core/src/apps/thp/pairing.py index ddd07fadca..07783cfc2a 100644 --- a/core/src/apps/thp/pairing.py +++ b/core/src/apps/thp/pairing.py @@ -132,7 +132,7 @@ async def show_display_data(ctx: PairingContext, expected_types: Container[int] if cancel_task in race.finished: raise ActionCancelled else: - return Exception("Should not happen") # TODO + return Exception("Should not happen") # TODO what to do here? @check_state_and_log(ChannelState.TP1) @@ -149,12 +149,11 @@ async def _handle_code_entry_is_included(ctx: PairingContext) -> None: if challenge_message.challenge is None: raise Exception("Invalid message") - - code_code_entry_hash = sha256( - challenge_message.challenge - + ctx.secret - + bytes("PairingMethod_CodeEntry", "utf-8") - ).digest() # TODO add handshake hash + sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash()) + sha_ctx.update(ctx.secret) + sha_ctx.update(challenge_message.challenge) + sha_ctx.update(bytes("PairingMethod_CodeEntry", "utf-8")) + code_code_entry_hash = sha_ctx.digest() ctx.display_data.code_code_entry = ( int.from_bytes(code_code_entry_hash, "big") % 1000000 ) @@ -163,21 +162,19 @@ async def _handle_code_entry_is_included(ctx: PairingContext) -> None: @check_state_and_log(ChannelState.TP1, ChannelState.TP2) def _handle_qr_code_is_included(ctx: PairingContext) -> None: - ctx.display_data.code_qr_code = sha256( - ctx.secret + bytes("PairingMethod_QrCode", "utf-8") - ).digest()[ - :16 - ] # TODO add handshake hash + sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash()) + sha_ctx.update(ctx.secret) + sha_ctx.update(bytes("PairingMethod_QrCode", "utf-8")) + ctx.display_data.code_qr_code = sha_ctx.digest()[:16] ctx.display_data.display_qr_code = True @check_state_and_log(ChannelState.TP1, ChannelState.TP2) def _handle_nfc_unidirectional_is_included(ctx: PairingContext) -> None: - ctx.display_data.code_nfc_unidirectional = sha256( - ctx.secret + bytes("PairingMethod_NfcUnidirectional", "utf-8") - ).digest()[ - :16 - ] # TODO add handshake hash + sha_ctx = sha256(ctx.channel_ctx.get_handshake_hash()) + sha_ctx.update(ctx.secret) + sha_ctx.update(bytes("PairingMethod_NfcUnidirectional", "utf-8")) + ctx.display_data.code_nfc_unidirectional = sha_ctx.digest()[:16] ctx.display_data.display_nfc_unidirectional = True @@ -208,7 +205,10 @@ async def _handle_code_entry_cpace( if message.cpace_host_public_key is None: raise ThpError("Message ThpCodeEntryCpaceHost has no public key") - ctx.cpace = Cpace(message.cpace_host_public_key) + ctx.cpace = Cpace( + message.cpace_host_public_key, + ctx.channel_ctx.get_handshake_hash(), + ) assert ctx.display_data.code_code_entry is not None ctx.cpace.generate_keys_and_secret( ctx.display_data.code_code_entry.to_bytes(6, "big") diff --git a/core/src/trezor/wire/thp/channel.py b/core/src/trezor/wire/thp/channel.py index bcdb2ffb0e..51eb1897c5 100644 --- a/core/src/trezor/wire/thp/channel.py +++ b/core/src/trezor/wire/thp/channel.py @@ -76,6 +76,9 @@ class Channel: log.debug(__name__, "get_channel_state: %s", state_to_str(state)) return state + def get_handshake_hash(self) -> bytes: + return self.channel_cache.get(CHANNEL_HANDSHAKE_HASH) or b"" + def set_channel_state(self, state: ChannelState) -> None: self.channel_cache.state = bytearray(state.to_bytes(1, "big")) if __debug__: @@ -178,7 +181,7 @@ class Channel: else: key_receive = self.channel_cache.get(CHANNEL_KEY_RECEIVE) nonce_receive = self.channel_cache.get_int(CHANNEL_NONCE_RECEIVE) - auth_data = self.channel_cache.get(CHANNEL_HANDSHAKE_HASH) + auth_data = self.get_handshake_hash() assert key_receive is not None assert nonce_receive is not None @@ -212,7 +215,7 @@ class Channel: else: key_send = self.channel_cache.get(CHANNEL_KEY_SEND) nonce_send = self.channel_cache.get_int(CHANNEL_NONCE_SEND) - auth_data = self.channel_cache.get(CHANNEL_HANDSHAKE_HASH) + auth_data = self.get_handshake_hash() assert key_send is not None assert nonce_send is not None diff --git a/core/src/trezor/wire/thp/cpace.py b/core/src/trezor/wire/thp/cpace.py index c1cc08f0e2..f5e90ab8c1 100644 --- a/core/src/trezor/wire/thp/cpace.py +++ b/core/src/trezor/wire/thp/cpace.py @@ -11,19 +11,22 @@ class Cpace: CPace, a balanced composable PAKE: https://datatracker.ietf.org/doc/draft-irtf-cfrg-cpace/ """ - def __init__(self, cpace_host_public_key: bytes) -> None: + def __init__(self, cpace_host_public_key: bytes, handshake_hash: bytes) -> None: + self.handshake_hash: bytes = handshake_hash self.host_public_key: bytes = cpace_host_public_key + self.shared_secret: bytes self.trezor_private_key: bytes self.trezor_public_key: bytes - self.shared_secret: bytes def generate_keys_and_secret(self, code_code_entry: bytes) -> None: """ Generate ephemeral key pair and a shared secret using Elligator2 with X25519. """ - pregenerator = sha512(_PREFIX + code_code_entry + _PADDING).digest()[ - :32 - ] # TODO add handshake hash + sha_ctx = sha512(_PREFIX) + sha_ctx.update(code_code_entry) + sha_ctx.update(_PADDING) + sha_ctx.update(self.handshake_hash) + pregenerator = sha_ctx.digest()[:32] generator = elligator2.map_to_curve25519(pregenerator) self.trezor_private_key = random.bytes(32) if __debug__: