diff --git a/APP_SPECIFICATION.md b/APP_SPECIFICATION.md deleted file mode 100644 index 12e2f59..0000000 --- a/APP_SPECIFICATION.md +++ /dev/null @@ -1,166 +0,0 @@ -# Technical Specification - -> **Warning** -This documentation is a template and shall be updated with your own APDUs. - -## About - -This documentation describes the APDU messages interface to communicate with the Ontology application. - -The application covers the following functionalities : - -- Get a public Ontology address given a BIP 32 path -- Sign a basic Ontology transaction given a BIP 32 path and raw transaction -- Retrieve the Ontology app version -- Retrieve the Ontology app name - -The application interface can be accessed over HID or BLE - -## APDUs - -### GET ONTOLOGY PUBLIC ADDRESS - -#### Description - -This command returns the public key for the given BIP 32 path. - -The address can be optionally checked on the device before being returned. - -#### Coding - -##### `Command` - -| CLA | INS | P1 | P2 | Lc | Le | -| --- | --- | --- | --- | --- | --- | -| E0 | 05 | 00 : return address | 00 | variable | variable | -| | | 01 : display address and confirm before returning | | | | - -##### `Input data` - -| Description | Length | -| --- | --- | -| Number of BIP 32 derivations to perform (max 10) | 1 | -| First derivation index (big endian) | 4 | -| ... | 4 | -| Last derivation index (big endian) | 4 | - -##### `Output data` - -| Description | Length | -| --- | --- | -| Public Key length | 1 | -| Public Key | var | -| Chain code length | 1 | -| Chain code | var | - -### SIGN ONTOLOGY TRANSACTION - -#### Description - -This command signs a Ontology transaction after having the user validate the transactions parameters. - -The input data is the RLP encoded transaction streamed to the device in 255 bytes maximum data chunks. - -#### Coding - -##### `Command` - -| CLA | INS | P1 | P2 | Lc | Le | -| --- | --- | --- | --- | --- | --- | -| E0 | 06 | 00-FF : chunk index | 00 : last transaction data block | variable | variable | -| | | | 80 : subsequent transaction data block | | | - -##### `Input data (first transaction data block)` - -| Description | Length | -| --- | --- | -| Number of BIP 32 derivations to perform (max 10) | 1 | -| First derivation index (big endian) | 4 | -| ... | 4 | -| Last derivation index (big endian) | 4 | - -##### `Input data (other transaction data block)` - -| Description | Length | -| --- | --- | -| Transaction chunk | variable | - -##### `Output data` - -| Description | Length | -| --- | --- | -| Signature length | 1 | -| Signature | variable | -| v | 1 | - -### GET APP VERSION - -#### Description - -This command returns Ontology application version - -#### Coding - -##### `Command` - -| CLA | INS | P1 | P2 | Lc | Le | -| --- | --- | --- | --- | --- | ---| -| E0 | 03 | 00 | 00 | 00 | 04 | - -##### `Input data` - -None - -##### `Output data` - -| Description | Length | -| --- | --- | -| Application major version | 01 | -| Application minor version | 01 | -| Application patch version | 01 | - -### GET APP NAME - -#### Description - -This command returns ontology application name - -#### Coding - -##### `Command` - -| CLA | INS | P1 | P2 | Lc | Le | -| --- | --- | --- | --- | --- | ---| -| E0 | 04 | 00 | 00 | 00 | 04 | - -##### `Input data` - -None - -##### `Output data` - -| Description | Length | -| --- | --- | -| Application name | variable | - -## Status Words - -The following standard Status Words are returned for all APDUs. - -| SW | SW name | Description | -| --- | --- | --- | -| 6985 | SW_DENY | Rejected by user | -| 6A86 | SW_WRONG_P1P2 | Either P1 or P2 is incorrect | -| 6A87 | SW_WRONG_DATA_LENGTH | Lc or minimum APDU length is incorrect | -| 6D00 | SW_INS_NOT_SUPPORTED | No command exists with INS | -| 6E00 | SW_CLA_NOT_SUPPORTED | Bad CLA used for this application | -| B000 | SW_WRONG_RESPONSE_LENGTH | Wrong response length (buffer size problem) | -| B001 | SW_DISPLAY_BIP32_PATH_FAIL | BIP32 path conversion to string failed | -| B002 | SW_DISPLAY_ADDRESS_FAIL | Address conversion to string failed | -| B003 | SW_DISPLAY_AMOUNT_FAIL | Amount conversion to string failed | -| B004 | SW_WRONG_TX_LENGTH | Wrong raw transaction length | -| B005 | SW_TX_PARSING_FAIL | Failed to parse raw transaction | -| B006 | SW_TX_HASH_FAIL | Failed to compute hash digest of raw transaction | -| B007 | SW_BAD_STATE | Security issue with bad state | -| B008 | SW_SIGNATURE_FAIL | Signature of raw transaction failed | -| 9000 | OK | Success | diff --git a/doc/APP_SPECIFICATION.md b/doc/APP_SPECIFICATION.md index f300389..9598a60 100644 --- a/doc/APP_SPECIFICATION.md +++ b/doc/APP_SPECIFICATION.md @@ -195,9 +195,10 @@ The following standard Status Words are returned for all APDUs. | B003 | SW_DISPLAY_AMOUNT_FAIL | Amount conversion to string failed | | B004 | SW_WRONG_TX_LENGTH | Wrong raw transaction length | | B005 | SW_TX_PARSING_FAIL | Failed to parse raw transaction | -| B006 | SW_TX_HASH_FAIL | Failed to compute hash digest of raw transaction | +| B006 | SW_HASH_FAIL | Failed to compute hash digest of raw transaction or msg | | B007 | SW_BAD_STATE | Security issue with bad state | | B008 | SW_SIGNATURE_FAIL | Signature of data(tx or personal msg) failed | | B009 | SW_PERSONAL_MSG_PARSING_FAIL | Failed to parse personal msg | -| B00A | SW_PERSONAL_MSG_HASH_FAIL | Failed to compute hash digest of personal msg | -| B00B | SW_INVALID_TRANSACTION | Invalid transaction | +| B00A | SW_INVALID_TRANSACTION | Invalid transaction | +| B00B | SW_HASH_FAILED | Msg hash failed | +| B00C | SW_INVALID_PATH | Invalid path | diff --git a/src/handler/get_public_key.c b/src/handler/get_public_key.c index c9296bf..0471781 100644 --- a/src/handler/get_public_key.c +++ b/src/handler/get_public_key.c @@ -32,6 +32,7 @@ #include "sw.h" #include "display.h" #include "send_response.h" +#include "../transaction/utils.h" int handler_get_public_key(buffer_t *cdata, bool display) { explicit_bzero(&G_context, sizeof(G_context)); @@ -43,6 +44,10 @@ int handler_get_public_key(buffer_t *cdata, bool display) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + if (!is_valid_bip44_prefix(G_context.bip32_path, G_context.bip32_path_len)) { + return io_send_sw(SW_INVALID_PATH); + } + cx_err_t error = bip32_derive_get_pubkey_256(CX_CURVE_256R1, G_context.bip32_path, G_context.bip32_path_len, diff --git a/src/handler/sign_msg.c b/src/handler/sign_msg.c index 836d488..56d4bd7 100644 --- a/src/handler/sign_msg.c +++ b/src/handler/sign_msg.c @@ -43,13 +43,16 @@ int handler_sign_message(buffer_t *cdata, uint8_t chunk, bool more) { (size_t) G_context.bip32_path_len)) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + if (!is_valid_bip44_prefix(G_context.bip32_path, G_context.bip32_path_len)) { + return io_send_sw(SW_INVALID_PATH); + } return io_send_sw(SW_OK); } else { if (G_context.req_type != CONFIRM_MESSAGE) { return io_send_sw(SW_BAD_STATE); } if (G_context.msg_info.raw_msg_len + cdata->size > sizeof(G_context.msg_info.raw_msg)) { - return io_send_sw(SW_WRONG_PERSONAL_MSG_LENGTH); + return io_send_sw(SW_WRONG_DATA_LENGTH); } if (!buffer_move(cdata, G_context.msg_info.raw_msg + G_context.msg_info.raw_msg_len, @@ -79,7 +82,7 @@ int handler_sign_message(buffer_t *cdata, uint8_t chunk, bool more) { G_context.msg_info.raw_msg, G_context.msg_info.raw_msg_len) != CX_OK || cx_hash_final((cx_hash_t *) &cx_sha256, G_context.msg_info.m_hash) != CX_OK) { - return io_send_sw(SW_WRONG_PERSONAL_MSG_LENGTH); // todo, another sw + return io_send_sw(SW_HASH_FAIL); } PRINTF("Hash: %.*H\n", sizeof(G_context.msg_info.m_hash), G_context.msg_info.m_hash); diff --git a/src/handler/sign_tx.c b/src/handler/sign_tx.c index 864b23b..6412db5 100644 --- a/src/handler/sign_tx.c +++ b/src/handler/sign_tx.c @@ -47,6 +47,9 @@ int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more) { (size_t) G_context.bip32_path_len)) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + if (!is_valid_bip44_prefix(G_context.bip32_path, G_context.bip32_path_len)) { + return io_send_sw(SW_INVALID_PATH); + } return io_send_sw(SW_OK); } else { // parse transaction @@ -96,7 +99,7 @@ static int handler_hash_tx_and_display_tx(bool is_blind_signing) { explicit_bzero(&second_hash, sizeof(second_hash)); if (!res) { - return io_send_sw(SW_TX_HASH_FAIL); + return io_send_sw(SW_HASH_FAIL); } else { G_context.state = STATE_PARSED; return ui_display_transaction(is_blind_signing); diff --git a/src/sw.h b/src/sw.h index e253648..632eff6 100644 --- a/src/sw.h +++ b/src/sw.h @@ -49,9 +49,9 @@ */ #define SW_TX_PARSING_FAIL 0xB005 /** - * Status word for fail of transaction hash. + * Status word for fail of hash. */ -#define SW_TX_HASH_FAIL 0xB006 +#define SW_HASH_FAIL 0xB006 /** * Status word for bad state. */ @@ -65,10 +65,10 @@ */ #define SW_PERSONAL_MSG_PARSING_FAIL 0xB009 /** - * Status word for wrong personal msg length. + * Status word for invalid transaction. */ -#define SW_WRONG_PERSONAL_MSG_LENGTH 0xB00A +#define SW_INVALID_TRANSACTION 0xB00A /** - * Status word for invalid transaction. + * Status word for invalid path. */ -#define SW_INVALID_TRANSACTION 0xB00B \ No newline at end of file +#define SW_INVALID_PATH 0xB00B \ No newline at end of file diff --git a/src/transaction/utils.c b/src/transaction/utils.c index c017e76..9d6238e 100644 --- a/src/transaction/utils.c +++ b/src/transaction/utils.c @@ -22,6 +22,9 @@ #define P64_R 6 // 2^64 % 10 #define P64_Q 1844674407370955161ULL // 2^64 / 10 +#define BIP44_COIN_TYPE_1024 0x80000400 // 1024' (hardened) +#define BIP44_COIN_TYPE_888 0x80000378 // 888' (hardened) +#define BIP44_PURPOSE 0x8000002C // 44' (hardened) static bool convert_bytes_to_uint64_le(const uint8_t *amount, const size_t len, uint64_t *out) { if (amount == NULL || out == NULL) { return false; @@ -228,3 +231,12 @@ bool convert_param_amount_to_chars(tx_parameter_t *param, convert_params_to_uint128_le(param, has_prefix, &low, &high) && format_fpu128_trimmed(amount, amount_len, low, high, decimals); } + +bool is_valid_bip44_prefix(uint32_t *path, uint8_t path_len) { + if (path_len < 2) { // Need at least purpose and coin type + return false; + } + // Check for 44' (purpose) followed by 1024' or 888' (coin type) + return path[0] == BIP44_PURPOSE && + (path[1] == BIP44_COIN_TYPE_1024 || path[1] == BIP44_COIN_TYPE_888); +} \ No newline at end of file diff --git a/src/transaction/utils.h b/src/transaction/utils.h index c2d0477..d30c169 100644 --- a/src/transaction/utils.h +++ b/src/transaction/utils.h @@ -56,4 +56,16 @@ bool convert_param_amount_to_chars(tx_parameter_t *param, */ static inline bool methodcmp(const tx_parameter_t *param, const char *method_name) { return param->len == strlen(method_name) && memcmp(param->data, method_name, param->len) == 0; -} \ No newline at end of file +} + +/** + * Validate a BIP-32 path prefix (44'/1024' or 44'/888'). + * + * @param[in] path + * Pointer to the array representing the BIP-32 path. + * @param[in] path_len + * Length of the path array. + * + * @return true if the path has a valid BIP-44 prefix, false otherwise. + */ +bool is_valid_bip44_prefix(uint32_t *path, uint8_t path_len); \ No newline at end of file diff --git a/tests/application_client/boilerplate_command_sender.py b/tests/application_client/boilerplate_command_sender.py index fe62ea7..886b8f1 100644 --- a/tests/application_client/boilerplate_command_sender.py +++ b/tests/application_client/boilerplate_command_sender.py @@ -43,12 +43,12 @@ class Errors(IntEnum): SW_DISPLAY_AMOUNT_FAIL = 0xB003 SW_WRONG_TX_LENGTH = 0xB004 SW_TX_PARSING_FAIL = 0xB005 - SW_TX_HASH_FAIL = 0xB006 + SW_HASH_FAIL = 0xB006 SW_BAD_STATE = 0xB007 SW_SIGNATURE_FAIL = 0xB008 SW_PERSONAL_MSG_PARSING_FAIL = 0xB009 - SW_WRONG_PERSONAL_MSG_LENGTH = 0xB00A - SW_INVALID_TRANSACTION = 0xB00B + SW_INVALID_TRANSACTION = 0xB00A + SW_INVALID_PATH = 0xB00B def split_message(message: bytes, max_size: int) -> List[bytes]: