Skip to content

Commit

Permalink
fix(xmr): add missing view_tags to hf15
Browse files Browse the repository at this point in the history
  • Loading branch information
ph4r05 committed Jun 20, 2022
1 parent 2aa427a commit 6678255
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 9 deletions.
54 changes: 45 additions & 9 deletions core/src/apps/monero/signing/step_06_set_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -192,25 +198,35 @@ 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 TxoutToKey or txout_to_tagged_key
crypto.encodepoint_into(tx_out_bin, tx_out_key, 2)
if use_tags:
view_tag = _derive_view_tags(derivation, state.current_output_index)
tx_out_bin[-1] = view_tag[0]

state.mem_trace(8)

# Tx header prefix hashing
Expand Down Expand Up @@ -569,6 +585,26 @@ 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]
"""
from apps.monero.xmr.serialize.int_serialize import dump_uvarint_b_into

buff = bytearray(32)
ctx = crypto_helpers.get_keccak()
ctx.update(b"view_tag")
crypto.encodepoint_into(buff, derivation)
ctx.update(buff)

buff[0] = 0
dump_uvarint_b_into(output_index, buff)
ctx.update(memoryview(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
Expand Down
12 changes: 12 additions & 0 deletions core/tests/test_apps.monero.crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,18 @@ def test_public_spend(self):
)
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()

0 comments on commit 6678255

Please sign in to comment.