From b6949f61537dc599ea641ef0af570a947c32cd08 Mon Sep 17 00:00:00 2001 From: Aleksey Popov Date: Mon, 16 Apr 2018 23:14:31 +0300 Subject: [PATCH] app.lisk: Add lisk_sign_tx and layouts --- src/apps/lisk/__init__.py | 8 +- src/apps/lisk/layout.py | 57 +++++++++++++++ src/apps/lisk/sign_tx.py | 126 ++++++++++++++++++++++++++++++++ src/apps/lisk/verify_message.py | 2 +- 4 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/apps/lisk/layout.py create mode 100644 src/apps/lisk/sign_tx.py diff --git a/src/apps/lisk/__init__.py b/src/apps/lisk/__init__.py index e2fcbdb389..7c2835e48e 100644 --- a/src/apps/lisk/__init__.py +++ b/src/apps/lisk/__init__.py @@ -1,6 +1,6 @@ from trezor.wire import register, protobuf_workflow from trezor.messages.wire_types import \ - LiskGetAddress, LiskSignMessage, LiskVerifyMessage, LiskGetPublicKey + LiskGetAddress, LiskSignMessage, LiskVerifyMessage, LiskSignTx, LiskGetPublicKey def dispatch_LiskGetAddress(*args, **kwargs): @@ -23,8 +23,14 @@ def dispatch_LiskVerifyMessage(*args, **kwargs): return lisk_verify_message(*args, **kwargs) +def dispatch_LiskSignTx(*args, **kwargs): + from .sign_tx import lisk_sign_tx + return lisk_sign_tx(*args, **kwargs) + + def boot(): register(LiskGetPublicKey, protobuf_workflow, dispatch_LiskGetPublicKey) register(LiskGetAddress, protobuf_workflow, dispatch_LiskGetAddress) register(LiskSignMessage, protobuf_workflow, dispatch_LiskSignMessage) register(LiskVerifyMessage, protobuf_workflow, dispatch_LiskVerifyMessage) + register(LiskSignTx, protobuf_workflow, dispatch_LiskSignTx) diff --git a/src/apps/lisk/layout.py b/src/apps/lisk/layout.py new file mode 100644 index 0000000000..002622e92d --- /dev/null +++ b/src/apps/lisk/layout.py @@ -0,0 +1,57 @@ +from apps.common.confirm import * +from trezor import ui +from trezor.utils import chunks +from trezor.messages import ButtonRequestType +from trezor.ui.text import Text +from .helpers import get_vote_tx_text + + +async def require_confirm_tx(ctx, to, value): + content = Text('Confirm sending', ui.ICON_SEND, + ui.BOLD, format_amount(value), + ui.NORMAL, 'to', + ui.MONO, *split_address(to), + icon_color=ui.GREEN) + return await require_confirm(ctx, content, ButtonRequestType.SignTx) + +async def require_confirm_delegate_registration(ctx, delegate_name): + content = Text('Confirm transaction', ui.ICON_SEND, + 'Do you really want to', + 'register a delegate?', + ui.BOLD, *chunks(delegate_name, 20), + icon_color=ui.GREEN) + return await require_confirm(ctx, content, ButtonRequestType.SignTx) + +async def require_confirm_vote_tx(ctx, votes): + content = Text('Confirm transaction', ui.ICON_SEND, + *get_vote_tx_text(votes), + icon_color=ui.GREEN) + + return await require_confirm(ctx, content, ButtonRequestType.SignTx) + +async def require_confirm_public_key(ctx, public_key): + from apps.wallet.get_public_key import _show_pubkey + return await _show_pubkey(ctx, public_key) + +async def require_confirm_multisig(ctx, multisignature): + content = Text('Confirm transaction', ui.ICON_SEND, + ('Keys group length: %s' % len(multisignature.keys_group)), + ('Life time: %s' % multisignature.life_time), + ('Min: %s' % multisignature.min), + icon_color=ui.GREEN) + + return await require_confirm(ctx, content, ButtonRequestType.SignTx) + +async def require_confirm_fee(ctx, value, fee): + content = Text('Confirm transaction', ui.ICON_SEND, + ui.BOLD, format_amount(value), + ui.NORMAL, 'fee:', + ui.BOLD, format_amount(fee), + icon_color=ui.GREEN) + await require_hold_to_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + +def format_amount(value): + return '%s LSK' % (int(value) / 100000000) + +def split_address(address): + return chunks(address, 16) diff --git a/src/apps/lisk/sign_tx.py b/src/apps/lisk/sign_tx.py new file mode 100644 index 0000000000..8ef130639d --- /dev/null +++ b/src/apps/lisk/sign_tx.py @@ -0,0 +1,126 @@ +from trezor import wire, ui +from trezor.messages.LiskSignedTx import LiskSignedTx +from trezor.messages.LiskTransactionType import * +from trezor.messages import FailureType +from trezor.utils import HashWriter +from apps.lisk.layout import * +from ubinascii import unhexlify, hexlify + + +async def lisk_sign_tx(ctx, msg): + from trezor.crypto.hashlib import sha256 + + public_key, seckey = await _get_keys(ctx, msg) + transaction = update_raw_tx(msg.transaction, public_key) + + # throw ValueError if transaction has not valid structure + try: + await require_confirm_by_type(ctx, transaction) + except AttributeError: + raise ValueError(FailureType.DataError, 'The transaction has invalid asset data field') + + await require_confirm_fee(ctx, transaction.amount, transaction.fee) + + sha = HashWriter(sha256) + transactionBytes = _get_transaction_bytes(transaction) + + for field in transactionBytes: + sha.extend(field) + + digest = sha.get_digest() + + signature = await get_signature(seckey, digest) + + return LiskSignedTx(signature=signature) + +async def require_confirm_by_type(ctx, transaction): + if transaction.type is Transfer: + return await require_confirm_tx(ctx, transaction.recipient_id, transaction.amount) + if transaction.type is RegisterDelegate: + return await require_confirm_delegate_registration(ctx, transaction.asset.delegate.username) + if transaction.type is CastVotes: + return await require_confirm_vote_tx(ctx, transaction.asset.votes) + if transaction.type is RegisterSecondPassphrase: + return await require_confirm_public_key(ctx, transaction.asset.signature.public_key) + if transaction.type is RegisterMultisignatureAccount: + return await require_confirm_multisig(ctx, transaction.asset.multisignature) + +async def get_signature(seckey, digest): + from trezor.crypto.curve import ed25519 + signature = ed25519.sign(seckey, digest) + + return signature + +def _get_transaction_bytes(msg): + from ustruct import pack + + # Required transaction parameters + t_type = pack('Q', 0) + else: + t_recipient_id = pack('>Q', int(msg.recipient_id[:-1])) + + if msg.signature is None: + t_signature = b'' + else: + t_signature = msg.signature + + t_asset = _get_asset_data_byttes(msg) + + return [t_type, t_timestamp, t_pubkey, t_requester_public_key, t_recipient_id, t_amount, t_asset, t_signature] + +def _get_asset_data_byttes(msg): + from ustruct import pack + data = b'' + + if msg.type is Transfer and getattr(msg.asset, "data"): + data = bytes(msg.asset.data, "utf8") + + if msg.type is RegisterDelegate: + data = bytes(msg.asset.delegate.username, "utf8") + + if msg.type is CastVotes: + data = bytes("".join(msg.asset.votes), "utf8") + + if msg.type is RegisterSecondPassphrase: + data = msg.asset.signature.public_key + + if msg.type is RegisterMultisignatureAccount: + data += pack('