From 2d36c4e8f393df6ef552e814b0dcdf386e7f7b77 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 20 Jun 2022 19:45:52 +0200 Subject: [PATCH] fix(core/monero): add missing view_tags to hf15 --- ci/shell.nix | 4 +- .../apps/monero/signing/step_06_set_output.py | 58 +++++++++++++++---- core/tests/run_tests_device_emu_monero.sh | 4 +- core/tests/test_apps.monero.crypto.py | 12 ++++ 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/ci/shell.nix b/ci/shell.nix index 1d33e40e6..40e99e397 100644 --- a/ci/shell.nix +++ b/ci/shell.nix @@ -19,8 +19,8 @@ let sha256 = "02s3qkb6kz3ndyx7rfndjbvp4vlwiqc42fxypn3g6jnc0v5jyz95"; }) { }; moneroTests = nixpkgs.fetchurl { - url = "https://github.com/ph4r05/monero/releases/download/v0.17.3.2-dev-tests-u18.04-02/trezor_tests"; - sha256 = "f790ad7485cbbad92fbea673a75935028f61d42f64627a7c55f850f6742cf93a"; + url = "https://github.com/ph4r05/monero/releases/download/v0.17.3.2-dev-tests-u18.04-03/trezor_tests"; + sha256 = "3280aeef795baf2fc46687c07ac4131e5a18767ecdd3af83cf17823ebb2d1007"; }; moneroTestsPatched = nixpkgs.runCommandCC "monero_trezor_tests" {} '' cp ${moneroTests} $out diff --git a/core/src/apps/monero/signing/step_06_set_output.py b/core/src/apps/monero/signing/step_06_set_output.py index 955fdfc64..60740ad17 100644 --- a/core/src/apps/monero/signing/step_06_set_output.py +++ b/core/src/apps/monero/signing/step_06_set_output.py @@ -60,7 +60,7 @@ async def set_output( state.mem_trace(5, True) # Compute tx keys and masks if applicable - tx_out_key, amount_key = _compute_tx_keys(state, dst_entr) + tx_out_key, amount_key, derivation = _compute_tx_keys(state, dst_entr) utils.unimport_end(mods) state.mem_trace(6, True) @@ -76,7 +76,13 @@ async def set_output( return MoneroTransactionSetOutputAck() # Tx header prefix hashing, hmac dst_entr - tx_out_bin, hmac_vouti = _set_out_tx_out(state, dst_entr, tx_out_key) + tx_out_bin, hmac_vouti = _set_out_tx_out( + state, + dst_entr, + tx_out_key, + derivation, + ) + del (derivation,) state.mem_trace(11, True) out_pk_dest, out_pk_commitment, ecdh_info_bin = _get_ecdh_info_and_out_pk( @@ -172,11 +178,11 @@ def _validate( def _compute_tx_keys( state: State, dst_entr: MoneroTransactionDestinationEntry -) -> tuple[crypto.Point, crypto.Scalar]: +) -> tuple[crypto.Point, crypto.Scalar, crypto.Point]: """Computes tx_out_key, amount_key""" if state.is_processing_offloaded: - return None, None # no need to recompute + return None, None, None # no need to recompute # additional tx key if applicable additional_txkey_priv = _set_out_additional_keys(state, dst_entr) @@ -192,26 +198,36 @@ def _compute_tx_keys( state.current_output_index, crypto_helpers.decodepoint(dst_entr.addr.spend_public_key), ) - del (derivation, additional_txkey_priv) + del (additional_txkey_priv,) from apps.monero.xmr import monero mask = monero.commitment_mask(crypto_helpers.encodeint(amount_key)) state.output_masks.append(mask) - return tx_out_key, amount_key + return tx_out_key, amount_key, derivation def _set_out_tx_out( - state: State, dst_entr: MoneroTransactionDestinationEntry, tx_out_key: crypto.Point + state: State, + dst_entr: MoneroTransactionDestinationEntry, + tx_out_key: crypto.Point, + derivation: crypto.Point, ) -> tuple[bytes, bytes]: """ Manually serializes TxOut(0, TxoutToKey(key)) and calculates hmac. """ - tx_out_bin = bytearray(34) + use_tags = state.hard_fork >= 15 + tx_out_bin = bytearray(35 if use_tags else 34) tx_out_bin[0] = 0 # amount varint - tx_out_bin[1] = 2 # variant code TxoutToKey + tx_out_bin[1] = ( + 3 if use_tags else 2 + ) # variant code txout_to_tagged_key or txout_to_key crypto.encodepoint_into(tx_out_bin, tx_out_key, 2) - state.mem_trace(8) + if use_tags: + view_tag = _derive_view_tags(derivation, state.current_output_index) + tx_out_bin[-1] = view_tag[0] + + state.mem_trace(8, True) # Tx header prefix hashing state.tx_prefix_hasher.buffer(tx_out_bin) @@ -569,6 +585,28 @@ def _set_out_derivation( return derivation +def _derive_view_tags(derivation: crypto.Point, output_index: int) -> bytes: + """ + Computes view tags for tx output - speeds up blockchain scanning for wallets + view_tag_full = H["view_tag"|derivation|output_index] + """ + buff = bytearray(32) + ctx = crypto_helpers.get_keccak() + ctx.update(b"view_tag") + crypto.encodepoint_into(buff, derivation) + ctx.update(buff) + + # Correct way of serializing output_index is calling dump_uvarint_b_into, but we + # know that output_index is always lower than 127 so we save imports and calls. + if output_index > 127: + raise ValueError("Output index too big") + + buff[0] = output_index + ctx.update(buff[:1]) + full_tag = ctx.digest() + return full_tag[:1] + + def _is_last_in_batch(state: State, idx: int, bidx: int) -> bool: """ Returns true if the current output is last in the rsig batch diff --git a/core/tests/run_tests_device_emu_monero.sh b/core/tests/run_tests_device_emu_monero.sh index a1f06fc5d..dae62ce0a 100755 --- a/core/tests/run_tests_device_emu_monero.sh +++ b/core/tests/run_tests_device_emu_monero.sh @@ -23,8 +23,8 @@ fi # When updating URL and sha256sum also update the URL in ci/shell.nix. error=1 -: "${TREZOR_MONERO_TESTS_URL:=https://github.com/ph4r05/monero/releases/download/v0.17.3.2-dev-tests-u18.04-02/trezor_tests}" -: "${TREZOR_MONERO_TESTS_SHA256SUM:=f790ad7485cbbad92fbea673a75935028f61d42f64627a7c55f850f6742cf93a}" +: "${TREZOR_MONERO_TESTS_URL:=https://github.com/ph4r05/monero/releases/download/v0.17.3.2-dev-tests-u18.04-03/trezor_tests}" +: "${TREZOR_MONERO_TESTS_SHA256SUM:=3280aeef795baf2fc46687c07ac4131e5a18767ecdd3af83cf17823ebb2d1007}" : "${TREZOR_MONERO_TESTS_PATH:=$CORE_DIR/tests/trezor_monero_tests}" : "${TREZOR_MONERO_TESTS_LOG:=$CORE_DIR/tests/trezor_monero_tests.log}" : "${TREZOR_MONERO_TESTS_CHAIN:=$CORE_DIR/tests/trezor_monero_tests.chain}" diff --git a/core/tests/test_apps.monero.crypto.py b/core/tests/test_apps.monero.crypto.py index 3a4adff1b..f3eff8aec 100644 --- a/core/tests/test_apps.monero.crypto.py +++ b/core/tests/test_apps.monero.crypto.py @@ -236,6 +236,18 @@ class TestMoneroCrypto(unittest.TestCase): ) self.assertEqual(pkey_ex, crypto_helpers.encodepoint(pkey_comp)) + def test_view_tags(self): + from apps.monero.signing.step_06_set_output import _derive_view_tags + + test_vectors = [ + (b'0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97', 0, b'\x76'), + (b'fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0', 15, b'\xeb'), + (b'ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d', 13, b'\x42'), + ] + + for key, idx, exp in test_vectors: + self.assertEqual(_derive_view_tags(crypto_helpers.decodepoint(unhexlify(key)), idx), exp) + if __name__ == "__main__": unittest.main()