1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-23 14:58:09 +00:00

nem2: Canonicalize mosaics in transfer transactions

NIS deserializes then serializes transactions in order to verify the
signature. This means that transactions must be serialized canonically,
otherwise the signature will not match. Due to [1], mosaics are sorted
and deduplicated in transfer transactions.

[1]: 4231550ddf
This commit is contained in:
Saleem Rashid 2017-07-28 16:29:31 +01:00
parent 1f20625bbc
commit 1d83eee3b3
3 changed files with 95 additions and 36 deletions

View File

@ -1206,6 +1206,10 @@ void fsm_msgNEMSignTx(NEMSignTx *msg) {
char address[NEM_ADDRESS_SIZE + 1];
hdnode_get_nem_address(node, common->network, address);
if (msg->has_transfer) {
msg->transfer.mosaics_count = nem_canonicalizeMosaics(msg->transfer.mosaics, msg->transfer.mosaics_count);
}
if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user"));
layoutHome();

View File

@ -157,43 +157,21 @@ const char *nem_validate_aggregate_modification(const NEMAggregateModification *
bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc) {
if (transfer->mosaics_count) {
struct {
bool skip;
uint64_t quantity;
const NEMMosaicDefinition *definition;
} mosaics[transfer->mosaics_count], *xem = NULL;
memset(mosaics, 0, sizeof(mosaics));
const NEMMosaic *xem = NULL;
bool unknownMosaic = false;
for (size_t i = 0; i < transfer->mosaics_count; i++) {
// Skip duplicate mosaics
if (mosaics[i].skip) continue;
const NEMMosaicDefinition *definitions[transfer->mosaics_count];
for (size_t i = 0; i < transfer->mosaics_count; i++) {
const NEMMosaic *mosaic = &transfer->mosaics[i];
if ((mosaics[i].definition = nem_mosaicByName(mosaic->namespace, mosaic->mosaic, common->network))) {
// XEM is displayed separately
if (mosaics[i].definition == NEM_MOSAIC_DEFINITION_XEM) {
// Do not display as a mosaic
mosaics[i].skip = true;
xem = &mosaics[i];
}
} else {
definitions[i] = nem_mosaicByName(mosaic->namespace, mosaic->mosaic, common->network);
if (definitions[i] == NEM_MOSAIC_DEFINITION_XEM) {
xem = mosaic;
} else if (definitions[i] == NULL) {
unknownMosaic = true;
}
mosaics[i].quantity = mosaic->quantity;
for (size_t j = i + 1; j < transfer->mosaics_count; j++) {
const NEMMosaic *new_mosaic = &transfer->mosaics[j];
if (nem_mosaicMatches(mosaics[i].definition, new_mosaic->namespace, new_mosaic->mosaic, common->network)) {
// Merge duplicate mosaics
mosaics[j].skip = true;
mosaics[i].quantity += new_mosaic->quantity;
}
}
}
bignum256 multiplier;
@ -221,15 +199,16 @@ bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *tran
}
for (size_t i = 0; i < transfer->mosaics_count; i++) {
// Skip duplicate mosaics or XEM
if (mosaics[i].skip) continue;
const NEMMosaic *mosaic = &transfer->mosaics[i];
if (mosaics[i].definition) {
layoutNEMTransferMosaic(mosaics[i].definition, mosaics[i].quantity, &multiplier, common->network);
if (mosaic == xem) {
continue;
}
if (definitions[i]) {
layoutNEMTransferMosaic(definitions[i], mosaic->quantity, &multiplier, common->network);
} else {
layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic, mosaics[i].quantity, &multiplier);
layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic, mosaic->quantity, &multiplier);
}
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
@ -707,6 +686,58 @@ static inline size_t format_amount(const NEMMosaicDefinition *definition, const
size);
}
size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count) {
if (mosaics_count <= 1) {
return mosaics_count;
}
size_t actual_count = 0;
bool skip[mosaics_count];
memset(skip, 0, sizeof(skip));
// Merge duplicates
for (size_t i = 0; i < mosaics_count; i++) {
if (skip[i]) continue;
NEMMosaic *mosaic = &mosaics[actual_count];
if (actual_count++ != i) {
memcpy(mosaic, &mosaics[i], sizeof(NEMMosaic));
}
for (size_t j = i + 1; j < mosaics_count; j++) {
if (skip[j]) continue;
const NEMMosaic *new_mosaic = &mosaics[j];
if (nem_mosaicCompare(mosaic, new_mosaic) == 0) {
skip[j] = true;
mosaic->quantity += new_mosaic->quantity;
}
}
}
NEMMosaic temp;
// Sort mosaics
for (size_t i = 0; i < actual_count - 1; i++) {
NEMMosaic *a = &mosaics[i];
for (size_t j = i + 1; j < actual_count; j++) {
NEMMosaic *b = &mosaics[j];
if (nem_mosaicCompare(a, b) > 0) {
memcpy(&temp, a, sizeof(NEMMosaic));
memcpy(a, b, sizeof(NEMMosaic));
memcpy(b, &temp, sizeof(NEMMosaic));
}
}
}
return actual_count;
}
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size) {
bignum256 amnt;
bn_read_uint64(quantity, &amnt);

View File

@ -55,6 +55,7 @@ bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *c
const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, const char *mosaic, uint8_t network);
size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count);
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size);
bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, uint8_t network, char *str_out, size_t size);
@ -80,4 +81,27 @@ static inline bool nem_mosaicMatches(const NEMMosaicDefinition *definition, cons
return false;
}
static inline int nem_mosaicCompare(const NEMMosaic *a, const NEMMosaic *b) {
size_t namespace_length = strlen(a->namespace);
// Ensure that strlen(a->namespace) <= strlen(b->namespace)
if (namespace_length > strlen(b->namespace)) {
return -nem_mosaicCompare(b, a);
}
int r = strncmp(a->namespace, b->namespace, namespace_length);
if (r == 0 && b->namespace[namespace_length] != '\0') {
// The next character would be the separator
r = (':' - b->namespace[namespace_length]);
}
if (r == 0) {
// Finally compare the mosaic
r = strcmp(a->mosaic, b->mosaic);
}
return r;
}
#endif