nem: validators

This commit introduces a lot of boundary checking validating if the NEM
transaction has all required fields. It is based solely on the T1 mcu code.
pull/25/head
Tomas Susanka 7 years ago committed by Jan Pochyla
parent 1f7ab29613
commit d30d6859ba

@ -1,11 +1,11 @@
from apps.wallet.get_address import _show_address
from apps.common import seed
from trezor.messages.NEMAddress import NEMAddress
from .helpers import *
from .validators import *
async def nem_get_address(ctx, msg):
network = nem_validate_network(msg.network)
network = validate_network(msg.network)
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE)
address = node.nem_address(network)
@ -15,4 +15,3 @@ async def nem_get_address(ctx, msg):
break
return NEMAddress(address=address)

@ -15,14 +15,10 @@ NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE = const(0x2001)
NEM_TRANSACTION_TYPE_MOSAIC_CREATION = const(0x4001)
NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE = const(0x4002)
NEM_MAX_DIVISIBILITY = const(6)
NEM_MAX_SUPPLY = const(9000000000)
NEM_SALT_SIZE = const(32)
AES_BLOCK_SIZE = const(16)
NEM_HASH_ALG = 'keccak'
def nem_validate_network(network):
if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN):
return network
if network is None:
return NEM_NETWORK_MAINNET
raise ValueError('Invalid NEM network')
NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key

@ -1,5 +1,6 @@
from apps.nem.layout import *
from apps.nem.transaction import *
from apps.nem.validators import validate
from apps.nem import helpers
from apps.common import seed
from trezor.messages.NEMSignTx import NEMSignTx
@ -10,13 +11,12 @@ from trezor.crypto import random
async def nem_sign_tx(ctx, msg: NEMSignTx):
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
validate(msg)
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
payload, encrypted = _get_payload(msg, node)
public_key = _get_public_key(node)
_validate_network(msg.transaction.network)
tx = nem_transaction_create_transfer(
msg.transaction.network,
msg.transaction.timestamp,
@ -69,8 +69,3 @@ def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes:
iv = random.bytes(helpers.AES_BLOCK_SIZE)
encrypted = node.nem_encrypt(public_key, iv, salt, payload)
return iv + salt + encrypted
def _validate_network(network):
if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]:
raise ValueError('Invalid NEM network')

@ -0,0 +1,210 @@
from apps.nem.helpers import *
from trezor.messages.NEMModificationType import CosignatoryModification_Delete
from trezor.messages.NEMSignTx import NEMAggregateModification
from trezor.messages.NEMSignTx import NEMImportanceTransfer
from trezor.messages.NEMSignTx import NEMMosaicCreation
from trezor.messages.NEMSignTx import NEMMosaicSupplyChange
from trezor.messages.NEMSignTx import NEMProvisionNamespace
from trezor.messages.NEMSignTx import NEMSignTx
from trezor.messages.NEMSignTx import NEMTransactionCommon
from trezor.messages.NEMSignTx import NEMTransfer
from trezor.crypto import nem
def validate(msg: NEMSignTx):
if msg.transaction is None:
raise ValueError('No common provided')
_validate_single_tx(msg)
_validate_common(msg.transaction)
if msg.transfer:
_validate_transfer(msg.transfer, msg.transaction.network)
if msg.provision_namespace:
_validate_provision_namespace(msg.provision_namespace, msg.transaction.network)
if msg.mosaic_creation:
_validate_mosaic_creation(msg.mosaic_creation, msg.transaction.network)
if msg.supply_change:
_validate_supply_change(msg.supply_change)
if msg.aggregate_modification:
_validate_aggregate_modification(msg.aggregate_modification, not len(msg.multisig))
if msg.importance_transfer:
_validate_importance_transfer(msg.importance_transfer)
def validate_network(network: int) -> int:
if network is None:
return NEM_NETWORK_MAINNET
_validate_network(network)
return network
def _validate_network(network: int):
if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]:
raise ValueError('Invalid NEM network')
def _validate_single_tx(msg: NEMSignTx):
# ensure exactly one transaction is provided
tx_count = bool(msg.transfer) + \
bool(msg.provision_namespace) + \
bool(msg.mosaic_creation) + \
bool(msg.supply_change) + \
bool(msg.aggregate_modification) + \
bool(msg.importance_transfer)
if tx_count == 0:
raise ValueError('No transaction provided')
if tx_count > 1:
raise ValueError('More than one transaction provided')
def _validate_common(common: NEMTransactionCommon, inner: bool=False):
common.network = validate_network(common.network)
err = None
if not common.timestamp:
err = 'timestamp'
if not common.fee:
err = 'fee'
if not common.deadline:
err = 'deadline'
is_signer = common.signer is not None
if inner != is_signer:
if not inner:
raise ValueError('Signer not allowed in outer transaction')
err = 'signer'
if err:
if inner:
raise ValueError('No ' + err + ' provided in inner transaction')
else:
raise ValueError('No ' + err + ' provided')
if common.signer:
_validate_public_key(common.signer, 'Invalid signer public key in inner transaction')
def _validate_public_key(public_key: bytes, err_msg):
if not public_key:
raise ValueError(err_msg + ' (none provided)')
if len(public_key) != NEM_PUBLIC_KEY_SIZE:
raise ValueError(err_msg + ' (invalid length)')
def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer):
if not importance_transfer.mode:
raise ValueError('No mode provided')
_validate_public_key(importance_transfer.public_key, 'Invalid remote account public key provided')
def _validate_aggregate_modification(aggregate_modification: NEMAggregateModification, creation: bool=False):
if creation and len(aggregate_modification.modifications) == 0:
raise ValueError('No modifications provided')
for m in aggregate_modification.modifications:
if not m.type:
raise ValueError('No modification type provided')
if creation and m.type == CosignatoryModification_Delete:
raise ValueError('Cannot remove cosignatory when converting account')
_validate_public_key(m.public_key, 'Invalid cosignatory public key provided')
def _validate_supply_change(supply_change: NEMMosaicSupplyChange):
if not supply_change.namespace:
raise ValueError('No namespace provided')
if not supply_change.mosaic:
raise ValueError('No mosaic provided')
if not supply_change.type:
raise ValueError('No type provided')
if not supply_change.delta:
raise ValueError('No delta provided')
def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int):
if not mosaic_creation.definition:
raise ValueError('No mosaic definition provided')
if not mosaic_creation.sink:
raise ValueError('No creation sink provided')
if not mosaic_creation.fee:
raise ValueError('No creation sink fee provided')
if not nem.validate_address(mosaic_creation.sink, network):
raise ValueError('Invalid creation sink address')
if mosaic_creation.definition.name:
raise ValueError('Name not allowed in mosaic creation transactions')
if mosaic_creation.definition.ticker:
raise ValueError('Ticker not allowed in mosaic creation transactions')
if len(mosaic_creation.definition.networks):
raise ValueError('Networks not allowed in mosaic creation transactions')
if not mosaic_creation.definition.namespace:
raise ValueError('No mosaic namespace provided')
if not mosaic_creation.definition.mosaic:
raise ValueError('No mosaic name provided')
if mosaic_creation.definition.levy:
if not mosaic_creation.definition.fee:
raise ValueError('No levy fee provided')
if not mosaic_creation.definition.levy_address:
raise ValueError('No levy address provided')
if not mosaic_creation.definition.levy_namespace:
raise ValueError('No levy namespace provided')
if not mosaic_creation.definition.levy_mosaic:
raise ValueError('No levy mosaic name provided')
if not mosaic_creation.definition.divisibility:
raise ValueError('No divisibility provided')
if not mosaic_creation.definition.supply:
raise ValueError('No supply provided')
if not mosaic_creation.definition.mutable_supply:
raise ValueError('No supply mutability provided')
if not mosaic_creation.definition.transferable:
raise ValueError('No mosaic transferability provided')
if not mosaic_creation.definition.description:
raise ValueError('No description provided')
if mosaic_creation.definition.divisibility > NEM_MAX_DIVISIBILITY:
raise ValueError('Invalid divisibility provided')
if mosaic_creation.definition.supply > NEM_MAX_SUPPLY:
raise ValueError('Invalid supply provided')
if not nem.validate_address(mosaic_creation.definition.levy_address, network):
raise ValueError('Invalid levy address')
def _validate_provision_namespace(provision_namespace: NEMProvisionNamespace, network: int):
if not provision_namespace.namespace:
raise ValueError('No namespace provided')
if not provision_namespace.sink:
raise ValueError('No rental sink provided')
if not provision_namespace.fee:
raise ValueError('No rental sink fee provided')
if not nem.validate_address(provision_namespace.sink, network):
raise ValueError('Invalid rental sink address')
def _validate_transfer(transfer: NEMTransfer, network: int):
if transfer.recipient is None:
raise ValueError('No recipient provided')
if transfer.amount is None:
raise ValueError('No amount provided')
if transfer.public_key:
_validate_public_key(transfer.public_key, 'Invalid recipient public key')
if not nem.validate_address(transfer.recipient, network):
raise ValueError('Invalid recipient address')
for m in transfer.mosaics:
if not m.namespace:
raise ValueError('No mosaic namespace provided')
if not m.mosaic:
raise ValueError('No mosaic name provided')
if not m.quantity:
raise ValueError('No mosaic quantity provided')
Loading…
Cancel
Save