Skip to content

Commit

Permalink
Add message signing (may not work yet)
Browse files Browse the repository at this point in the history
No tests for message signing, but everything else works as before.
  • Loading branch information
relatko committed Jan 3, 2024
1 parent 3be8e7a commit 45b3c7b
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 34 deletions.
76 changes: 51 additions & 25 deletions src/apdu_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "zxformat.h"
#include "hdpath.h"
#include "parser_impl.h"
#include "message.h"
#include "script_parser.h"

__Z_INLINE void handleGetPubkey(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
hasPubkey = false;
Expand Down Expand Up @@ -88,33 +90,57 @@ __Z_INLINE void handleGetPubkey(volatile uint32_t *flags, volatile uint32_t *tx,
}

__Z_INLINE void handleSign(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
if (!process_chunk(tx, rx)) {
THROW(APDU_CODE_OK);
}

const char *error_msg = tx_parse();

if (error_msg != NULL) {
int error_msg_length = strlen(error_msg);
MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length);
*tx += (error_msg_length);
THROW(APDU_CODE_DATA_INVALID);
}

show_address = SHOW_ADDRESS_NONE;
loadAddressCompareHdPathFromSlot();
zemu_log("HandleSign.\n");
process_chunk_response_t callType = process_chunk(tx, rx);

switch (callType) {
case PROCESS_CHUNK_NOT_FINISHED:
zemu_log("Continue processing chunks.\n");
THROW(APDU_CODE_OK);
case PROCESS_CHUNK_FINISHED_MESSAGE:;
zemu_log("Processing chunks finished, sign message.\n");
zxerr_t err = message_parse();
if (err != zxerr_ok) {
const char *error_msg = "Invalid message";
int error_msg_length = strlen(error_msg);
MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length);
*tx += (error_msg_length);
THROW(APDU_CODE_DATA_INVALID);
}
CHECK_APP_CANARY()
view_review_init(message_getItem, message_getNumItems, app_sign_message);
view_review_show(REVIEW_TXN);
*flags |= IO_ASYNCH_REPLY;
break;
case PROCESS_CHUNK_FINISHED_NO_METADATA:;
zemu_log("Processing chunks finished, no metadata.\n");
const char *error_msg = tx_parse();

if (error_msg != NULL) {
int error_msg_length = strlen(error_msg);
MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length);
*tx += (error_msg_length);
THROW(APDU_CODE_DATA_INVALID);
}
show_address = SHOW_ADDRESS_NONE;
loadAddressCompareHdPathFromSlot();

// if we found matching hdPath on slot 0
if (show_address == SHOW_ADDRESS_YES ||
show_address == SHOW_ADDRESS_YES_HASH_MISMATCH) {
checkAddressUsedInTx();
} else {
addressUsedInTx = 0;
}

// if we found matching hdPath on slot 0
if (show_address == SHOW_ADDRESS_YES || show_address == SHOW_ADDRESS_YES_HASH_MISMATCH) {
checkAddressUsedInTx();
} else {
addressUsedInTx = 0;
CHECK_APP_CANARY()
view_review_init(tx_getItem, tx_getNumItems, app_sign);
view_review_show(REVIEW_TXN);
*flags |= IO_ASYNCH_REPLY;
break;
default:
THROW(APDU_CODE_INVALIDP1P2);
}

CHECK_APP_CANARY()
view_review_init(tx_getItem, tx_getNumItems, app_sign);
view_review_show(REVIEW_TXN);
*flags |= IO_ASYNCH_REPLY;
}

__Z_INLINE void handleSlotStatus(__Z_UNUSED volatile uint32_t *flags,
Expand Down
54 changes: 46 additions & 8 deletions src/common/app_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "coin.h"
#include "zxmacros.h"
#include "hdpath.h"
#include "tx_metadata.h"

void extractHDPathAndCryptoOptions(uint32_t rx, uint32_t offset) {
if ((rx - offset) < sizeof(hdPath.data) + sizeof(cryptoOptions)) {
Expand All @@ -47,10 +48,11 @@ void extractHDPathAndCryptoOptions(uint32_t rx, uint32_t offset) {
MEMCPY(&cryptoOptions, G_io_apdu_buffer + offset + sizeof(hdPath.data), sizeof(cryptoOptions));
}

bool process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx) {
process_chunk_response_t process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx) {
const uint8_t payloadType = G_io_apdu_buffer[OFFSET_PAYLOAD_TYPE];
const uint8_t p2 = G_io_apdu_buffer[OFFSET_P2];

if (G_io_apdu_buffer[OFFSET_P2] != 0) {
if (p2 != 0 && payloadType != 0x02) {
THROW(APDU_CODE_INVALIDP1P2);
}

Expand All @@ -60,23 +62,59 @@ bool process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx) {

uint32_t added;
switch (payloadType) {
case 0:
case 0x00:
tx_initialize();
tx_reset();
extractHDPathAndCryptoOptions(rx, OFFSET_DATA);
return false;
case 1:
initStoredTxMetadata();
return PROCESS_CHUNK_NOT_FINISHED;
case 0x01:
added = tx_append(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA);
if (added != rx - OFFSET_DATA) {
THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL);
}
return false;
case 2:
return PROCESS_CHUNK_NOT_FINISHED;
case 0x02:
added = tx_append(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA);
if (added != rx - OFFSET_DATA) {
THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL);
}
return true;
switch (p2) {
case 0x01:
return PROCESS_CHUNK_FINISHED_NO_METADATA;
case 0x02:
return PROCESS_CHUNK_FINISHED_NFT1;
case 0x03:
return PROCESS_CHUNK_FINISHED_NFT2;
default:
THROW(APDU_CODE_INVALIDP1P2);
}
case 0x03:
if (storeTxMetadata(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA) != PARSER_OK) {
initStoredTxMetadata(); // delete merkle tree proof on error for redundant security
THROW(APDU_CODE_DATA_INVALID);
}
return PROCESS_CHUNK_NOT_FINISHED;
case 0x04:
if (validateStoredTxMetadataMerkleTreeLevel(&(G_io_apdu_buffer[OFFSET_DATA]),
rx - OFFSET_DATA) != PARSER_OK) {
initStoredTxMetadata(); // delete merkle tree proof on error for redundant security
THROW(APDU_CODE_DATA_INVALID);
}
return PROCESS_CHUNK_NOT_FINISHED;
case 0x05:
if (validateStoredTxMetadataMerkleTreeLevel(&(G_io_apdu_buffer[OFFSET_DATA]),
rx - OFFSET_DATA) != PARSER_OK) {
initStoredTxMetadata(); // delete merkle tree proof on error for redundant security
THROW(APDU_CODE_DATA_INVALID);
}
return PROCESS_CHUNK_FINISHED_WITH_METADATA;
case 0x10:
added = tx_append(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA);
if (added != rx - OFFSET_DATA) {
THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL);
}
return PROCESS_CHUNK_FINISHED_MESSAGE;
}

THROW(APDU_CODE_INVALIDP1P2);
Expand Down
11 changes: 10 additions & 1 deletion src/common/app_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@

void extractHDPathAndCryptoOptions(uint32_t rx, uint32_t offset);

bool process_chunk(volatile uint32_t *tx, uint32_t rx);
typedef enum {
PROCESS_CHUNK_NOT_FINISHED = 0,
PROCESS_CHUNK_FINISHED_WITH_METADATA,
PROCESS_CHUNK_FINISHED_NO_METADATA,
PROCESS_CHUNK_FINISHED_NFT1,
PROCESS_CHUNK_FINISHED_NFT2,
PROCESS_CHUNK_FINISHED_MESSAGE,
} process_chunk_response_t;

process_chunk_response_t process_chunk(volatile uint32_t *tx, uint32_t rx);

__Z_INLINE void handle_getversion(__Z_UNUSED volatile uint32_t *flags,
volatile uint32_t *tx,
Expand Down
123 changes: 123 additions & 0 deletions src/message.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*******************************************************************************
* (c) 2022 Vacuumlabs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/
#include "message.h"
#include "zxformat.h"
#include "app_mode.h"
#include "hdpath.h"
#include "crypto.h"
#include "buffering.h"

#define MAX_MESSAGE_SHOW_LENGTH 34 * 5
#define MAX_MESSAGE_LENGTH 0x7FFF

struct {
uint8_t hash[CX_SHA256_SIZE];
uint16_t length;
uint8_t *message;
bool canBeDisplayed;

} messageData;

zxerr_t message_parse() {
messageData.message = buffering_get_buffer()->data;
messageData.length = buffering_get_buffer()->pos;

if (messageData.length > MAX_MESSAGE_LENGTH) {
return zxerr_out_of_bounds;
}

for (size_t j = 0; j < messageData.length; j++) {
if (messageData.message[j] < 32 || messageData.message[j] > 127) {
return zxerr_out_of_bounds;
}
}

sha256(messageData.message, messageData.length, messageData.hash);
messageData.canBeDisplayed = false;

if (messageData.length <= MAX_MESSAGE_SHOW_LENGTH) {
messageData.canBeDisplayed = true;
}

return zxerr_ok;
}

zxerr_t message_getNumItems(uint8_t *num_items) {
*num_items = 2 + (!messageData.canBeDisplayed ? 1 : 0) + (app_mode_expert() ? 1 : 0);
return zxerr_ok;
}

// retrieves a readable output for each field / page
zxerr_t message_getItem(int8_t displayIdx,
char *outKey,
uint16_t outKeyLen,
char *outVal,
uint16_t outValLen,
uint8_t pageIdx,
uint8_t *pageCount) {
switch (displayIdx) {
case 0:
snprintf(outKey, outKeyLen, "Review");
snprintf(outVal, outValLen, "the message to sign");
return zxerr_ok;
case 1:
if (messageData.canBeDisplayed) {
snprintf(outKey, outKeyLen, "Message");
pageStringExt(outVal,
outValLen,
(char *) messageData.message,
messageData.length,
pageIdx,
pageCount);
return zxerr_ok;
} else {
snprintf(outKey, outKeyLen, "Message too long,");
snprintf(outVal, outValLen, "validate hash on a secure device.");
return zxerr_ok;
}
}

displayIdx -= 2;

if (displayIdx == 0 && !messageData.canBeDisplayed) {
snprintf(outKey, outKeyLen, "Message hash");
pageStringHex(outVal,
outValLen,
(const char *) messageData.hash,
sizeof(messageData.hash),
pageIdx,
pageCount);
return zxerr_ok;
}

if (!messageData.canBeDisplayed) {
displayIdx -= 1;
}

if (displayIdx == 0 && app_mode_expert()) {
snprintf(outKey, outKeyLen, "Your Path");
char buffer[100];
path_options_to_string(buffer,
sizeof(buffer),
hdPath.data,
HDPATH_LEN_DEFAULT,
cryptoOptions);
pageString(outVal, outValLen, buffer, pageIdx, pageCount);
return zxerr_ok;
}

return zxerr_no_data;
}
30 changes: 30 additions & 0 deletions src/message.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* (c) 2022 Vacuumlabs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/
#pragma once

#include "zxerror.h"

zxerr_t message_parse();

zxerr_t message_getNumItems(uint8_t *num_items);

zxerr_t message_getItem(int8_t displayIdx,
char *outKey,
uint16_t outKeyLen,
char *outVal,
uint16_t outValLen,
uint8_t pageIdx,
uint8_t *pageCount);
4 changes: 4 additions & 0 deletions tests/application_client/flow_command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class P1(IntEnum):

class P2(IntEnum):
""" Parameter 2 definitions """
# Parameter 2 for SIGN P1_LAST
P2_NO_METADATA = 0x01
# Parameter 2 P2_MORE.
P2_MORE = 0x80

class InsType(IntEnum):
Expand Down Expand Up @@ -247,6 +250,7 @@ def sign_tx(
with self.backend.exchange_async(cla=ClaType.CLA_APP,
ins=InsType.SIGN,
p1=P1.P1_LAST,
p2=P2.P2_NO_METADATA,
data=messages[-1]) as response:
yield response

Expand Down

0 comments on commit 45b3c7b

Please sign in to comment.