1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-13 16:12:18 +00:00
trezor-firmware/src/apps/ripple/sign_tx.py
2018-12-13 15:47:05 +01:00

79 lines
2.6 KiB
Python

from trezor.crypto import der
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha512
from trezor.messages.RippleSignedTx import RippleSignedTx
from trezor.messages.RippleSignTx import RippleSignTx
from trezor.wire import ProcessError
from . import helpers, layout
from .serialize import serialize
from apps.common import paths, seed
async def sign_tx(ctx, msg: RippleSignTx):
keychain = await seed.get_keychain(ctx)
validate(msg)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = keychain.derive(msg.address_n)
source_address = helpers.address_from_public_key(node.public_key())
set_canonical_flag(msg)
tx = serialize(msg, source_address, pubkey=node.public_key())
to_sign = get_network_prefix() + tx
check_fee(msg.fee)
await layout.require_confirm_fee(ctx, msg.fee)
await layout.require_confirm_tx(ctx, msg.payment.destination, msg.payment.amount)
signature = ecdsa_sign(node.private_key(), first_half_of_sha512(to_sign))
tx = serialize(msg, source_address, pubkey=node.public_key(), signature=signature)
return RippleSignedTx(signature, tx)
def check_fee(fee: int):
if fee < helpers.MIN_FEE or fee > helpers.MAX_FEE:
raise ProcessError("Fee must be in the range of 10 to 10,000 drops")
def get_network_prefix():
"""Network prefix is prepended before the transaction and public key is included"""
return helpers.HASH_TX_SIGN.to_bytes(4, "big")
def first_half_of_sha512(b):
"""First half of SHA512, which Ripple uses"""
hash = sha512(b)
return hash.digest()[:32]
def ecdsa_sign(private_key: bytes, digest: bytes) -> bytes:
"""Signs and encodes signature into DER format"""
signature = secp256k1.sign(private_key, digest)
sig_der = der.encode_seq((signature[1:33], signature[33:65]))
return sig_der
def set_canonical_flag(msg: RippleSignTx):
"""
Our ECDSA implementation already returns fully-canonical signatures,
so we're enforcing it in the transaction using the designated flag
- see https://wiki.ripple.com/Transaction_Malleability#Using_Fully-Canonical_Signatures
- see https://github.com/trezor/trezor-crypto/blob/3e8974ff8871263a70b7fbb9a27a1da5b0d810f7/ecdsa.c#L791
"""
if msg.flags is None:
msg.flags = 0
msg.flags |= helpers.FLAG_FULLY_CANONICAL
def validate(msg: RippleSignTx):
if None in (msg.fee, msg.sequence, msg.payment) or (
msg.payment and None in (msg.payment.amount, msg.payment.destination)
):
raise ProcessError(
"Some of the required fields are missing (fee, sequence, payment.amount, payment.destination)"
)