You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/monero/get_tx_keys.py

90 lines
3.1 KiB

"""
This `get_tx_key` command supports retrieval of private tx keys (not spend keys,
just random transaction privates `r` and additional private keys if applicable)
required by users to check the transaction or when resolving disputes with
the recipient.
It supports returning transaction derivations = private tx key * public view key.
This enables to compute the tx_proof for outgoing transactions which are also
a nice tool when resolving disputes, provides better protection as tx private
key are not exported in this case.
This is related to singing/step10 where we send `tx_enc_keys` to the host
encrypted using the private spend key. Here the host sends it back
in `MoneroGetTxKeyRequest.tx_enc_keys` to be decrypted and yet again encrypted
using the view key, which the host possess.
"""
from micropython import const
from typing import TYPE_CHECKING
from apps.common.keychain import auto_keychain
_GET_TX_KEY_REASON_TX_DERIVATION = const(1)
if TYPE_CHECKING:
from trezor.messages import MoneroGetTxKeyAck, MoneroGetTxKeyRequest
from apps.common.keychain import Keychain
@auto_keychain(__name__)
async def get_tx_keys(
msg: MoneroGetTxKeyRequest, keychain: Keychain
) -> MoneroGetTxKeyAck:
from trezor import utils, wire
from trezor.messages import MoneroGetTxKeyAck
from apps.common import paths
from apps.monero import layout, misc
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers
await paths.validate_path(keychain, msg.address_n)
do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
await layout.require_confirm_tx_key(export_key=not do_deriv)
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
tx_enc_key = misc.compute_tx_key(
creds.spend_key_private,
msg.tx_prefix_hash,
msg.salt1,
crypto_helpers.decodeint(msg.salt2),
)
# the plain_buff first stores the tx_priv_keys as decrypted here
# and then is used to store the derivations if applicable
plain_buff = chacha_poly.decrypt_pack(tx_enc_key, msg.tx_enc_keys)
utils.ensure(len(plain_buff) % 32 == 0, "Tx key buffer has invalid size")
msg.tx_enc_keys = b""
# If return only derivations do tx_priv * view_pub
if do_deriv:
if msg.view_public_key is None:
raise wire.DataError("Missing view public key")
plain_buff = bytearray(plain_buff)
view_pub = crypto_helpers.decodepoint(msg.view_public_key)
tx_priv = crypto.Scalar()
derivation = crypto.Point()
n_keys = len(plain_buff) // 32
for c in range(n_keys):
crypto.decodeint_into(tx_priv, plain_buff, 32 * c)
crypto.scalarmult_into(derivation, view_pub, tx_priv)
crypto.encodepoint_into(plain_buff, derivation, 32 * c)
# Encrypt by view-key based password.
tx_enc_key_host, salt = misc.compute_enc_key_host(
creds.view_key_private, msg.tx_prefix_hash
)
res = chacha_poly.encrypt_pack(tx_enc_key_host, plain_buff)
res_msg = MoneroGetTxKeyAck(salt=salt)
if do_deriv:
res_msg.tx_derivations = res
return res_msg
res_msg.tx_keys = res
return res_msg