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

80 lines
2.9 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 trezor import utils
from trezor.messages.MoneroGetTxKeyAck import MoneroGetTxKeyAck
from trezor.messages.MoneroGetTxKeyRequest import MoneroGetTxKeyRequest
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero.xmr import crypto
from apps.monero.xmr.crypto import chacha_poly
_GET_TX_KEY_REASON_TX_KEY = 0
_GET_TX_KEY_REASON_TX_DERIVATION = 1
@auto_keychain(__name__)
async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
await paths.validate_path(ctx, keychain, msg.address_n)
do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
await confirms.require_confirm_tx_key(ctx, 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.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")
del msg.tx_enc_keys
# If return only derivations do tx_priv * view_pub
if do_deriv:
plain_buff = bytearray(plain_buff)
view_pub = crypto.decodepoint(msg.view_public_key)
tx_priv = crypto.new_scalar()
derivation = crypto.new_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