From 3e157dfe819008adf9f0f7c3910132f189f037e2 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Mon, 19 Aug 2024 20:50:10 -0700 Subject: [PATCH 1/8] feat: Add session apis in core Co-authored-by: Vaibhav Sethia Co-authored-by: Parnika Gupta --- apps/manager_app/device_authentication_api.c | 3 - apps/manager_app/device_authentication_api.h | 3 + common/core/core_api.c | 56 ++- common/core/core_api.h | 10 +- common/core/core_session.c | 377 ++++++++++++++++++ common/core/core_session.h | 83 ++++ common/interfaces/card_interface/apdu.c | 1 - common/interfaces/card_interface/nfc.c | 16 +- .../desktop_app_interface/usb_event.c | 27 +- common/libraries/util/atecc_utils.c | 306 ++++++++++++++ common/libraries/util/atecc_utils.h | 77 ++++ src/card_operations/card_fetch_data.h | 55 +++ 12 files changed, 997 insertions(+), 17 deletions(-) create mode 100644 common/core/core_session.c create mode 100644 common/core/core_session.h create mode 100644 common/libraries/util/atecc_utils.c create mode 100644 common/libraries/util/atecc_utils.h create mode 100644 src/card_operations/card_fetch_data.h diff --git a/apps/manager_app/device_authentication_api.c b/apps/manager_app/device_authentication_api.c index aebd73dc9..02f8d8cd0 100644 --- a/apps/manager_app/device_authentication_api.c +++ b/apps/manager_app/device_authentication_api.c @@ -70,9 +70,6 @@ #include "atca_basic.h" #include "device_authentication_api.h" -#define SIGNATURE_SIZE 64 -#define POSTFIX1_SIZE 7 -#define POSTFIX2_SIZE 23 #define RANDOM_CHALLENGE_SIZE 32 atecc_data_t atecc_data = {0}; diff --git a/apps/manager_app/device_authentication_api.h b/apps/manager_app/device_authentication_api.h index 9aa627930..ad85f8eba 100644 --- a/apps/manager_app/device_authentication_api.h +++ b/apps/manager_app/device_authentication_api.h @@ -26,6 +26,9 @@ * MACROS AND DEFINES *****************************************************************************/ #define DEFAULT_ATECC_RETRIES 5 +#define SIGNATURE_SIZE 64 +#define POSTFIX1_SIZE 7 +#define POSTFIX2_SIZE 23 #define DEVICE_SERIAL_SIZE 32 /***************************************************************************** diff --git a/common/core/core_api.c b/common/core/core_api.c index f44102a44..20ea29eca 100644 --- a/common/core/core_api.c +++ b/common/core/core_api.c @@ -65,6 +65,7 @@ #include #include "assert_conf.h" +#include "core_session.h" #include "pb_encode.h" #include "status_api.h" #include "usb_api.h" @@ -130,7 +131,6 @@ void send_response_to_host(const uint8_t *msg, const uint32_t size) { core_msg.cmd.applet_id = get_applet_id(); send_core_msg(&core_msg, msg, size); - return; } void send_core_error_msg_to_host(uint32_t core_error_type) { @@ -139,7 +139,6 @@ void send_core_error_msg_to_host(uint32_t core_error_type) { core_msg.error.type = (core_error_type_t)core_error_type; send_core_msg(&core_msg, NULL, 0); - return; } void send_app_version_list_to_host( @@ -154,5 +153,54 @@ void send_app_version_list_to_host( sizeof(core_app_version_result_response_t)); send_core_msg(&core_msg, NULL, 0); - return; -} \ No newline at end of file +} + +void send_core_session_start_response_to_host(const uint8_t *payload) { + core_msg_t core_msg = CORE_MSG_INIT_ZERO; + core_msg.which_type = CORE_MSG_SESSION_START_TAG; + core_msg.session_start.which_cmd = CORE_SESSION_START_CMD_RESPONSE_TAG; + core_msg.session_start.response.which_response = + CORE_SESSION_START_RESPONSE_CONFIRMATION_INITIATE_TAG; + uint32_t offset = 0; + memcpy(core_msg.session_start.response.confirmation_initiate + .device_random_public, + payload + offset, + SESSION_PUB_KEY_SIZE); + offset += SESSION_PUB_KEY_SIZE; + memcpy(core_msg.session_start.response.confirmation_initiate.device_id, + payload + offset, + DEVICE_SERIAL_SIZE); + offset += DEVICE_SERIAL_SIZE; + memcpy(core_msg.session_start.response.confirmation_initiate.signature, + payload + offset, + SIGNATURE_SIZE); + offset += SIGNATURE_SIZE; + memcpy(core_msg.session_start.response.confirmation_initiate.postfix1, + payload + offset, + POSTFIX1_SIZE); + offset += POSTFIX1_SIZE; + memcpy(core_msg.session_start.response.confirmation_initiate.postfix2, + payload + offset, + POSTFIX2_SIZE); + + send_core_msg(&core_msg, NULL, 0); +} + +void send_core_session_start_ack_to_host() { + core_msg_t core_msg = CORE_MSG_INIT_ZERO; + core_msg.which_type = CORE_MSG_SESSION_START_TAG; + core_msg.session_start.which_cmd = CORE_SESSION_START_CMD_RESPONSE_TAG; + core_msg.session_start.response.which_response = + CORE_SESSION_START_RESPONSE_CONFIRMATION_START_TAG; + send_core_msg(&core_msg, NULL, 0); +} + +void send_core_session_close_response_to_host() { + core_msg_t core_msg = CORE_MSG_INIT_ZERO; + core_msg.which_type = CORE_MSG_SESSION_CLOSE_TAG; + core_msg.session_close.which_cmd = CORE_SESSION_CLOSE_CMD_RESPONSE_TAG; + core_msg.session_close.response.which_response = + CORE_SESSION_CLOSE_RESPONSE_CLEAR_TAG; + + send_core_msg(&core_msg, NULL, 0); +} diff --git a/common/core/core_api.h b/common/core/core_api.h index c41113378..d9b15ef20 100644 --- a/common/core/core_api.h +++ b/common/core/core_api.h @@ -49,7 +49,7 @@ void send_response_to_host(const uint8_t *msg, const uint32_t size); * * @param which_error The enum type of the error which needs to be sent */ -void send_core_error_msg_to_host(uint32_t which_error); +void send_core_error_msg_to_host(uint32_t core_error_type); /** * @brief Helper API for core to send a response containing a list of @@ -60,4 +60,10 @@ void send_core_error_msg_to_host(uint32_t which_error); */ void send_app_version_list_to_host( const core_app_version_result_response_t *version_resp); -#endif /* CORE_API_H */ \ No newline at end of file + +void send_core_session_start_response_to_host(const uint8_t *payload); + +void send_core_session_start_ack_to_host(); + +void send_core_session_close_response_to_host(); +#endif /* CORE_API_H */ diff --git a/common/core/core_session.c b/common/core/core_session.c new file mode 100644 index 000000000..58628ef6e --- /dev/null +++ b/common/core/core_session.c @@ -0,0 +1,377 @@ +/** + * @file session_utils.c + * @author Cypherock X1 Team + * @brief Definition of the session utility functions + * This file defines the functions used to create and manage the + * session, send authentication requests and verify the responses. + * + * @copyright Copyright (c) 2022 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2022 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include "core_session.h" + +#include "core.pb.h" +#include "inheritance_main.h" +#include "logger.h" +#include "options.h" +#include "pb_decode.h" +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ +const ecdsa_curve *curve; +const uint32_t session_key_rotation[2] = {6, 7}; +session_config_t CONFIDENTIAL session = {0}; + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Completes the session creation process + * @details It verifies the server response and stores the session id and + * session random. + * @param session_init_details The server response + * @param verification_details The buffer to store the details + * to be send back to the server to confirm + * /home/parnika/Documents/GitHub/x1_wallet_firmware/common/libraries/util/session_utils.c:394:6the + * verification. + * @return true if the session is created, false otherwise + * + * @see SESSION_ESTABLISH + * @since v1.0.0 + */ +static bool session_receive_server_key(uint8_t *server_message); + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ +static void session_curve_init() { + curve = get_curve_by_name(SECP256K1_NAME)->params; +} + +static bool derive_server_public_key() { + HDNode node; + char xpub[112] = {'\0'}; + + base58_encode_check(get_card_root_xpub(), + FS_KEYSTORE_XPUB_LEN, + nist256p1_info.hasher_base58, + xpub, + 112); + hdnode_deserialize_public( + (char *)xpub, 0x0488b21e, NIST256P1_NAME, &node, NULL); + + uint8_t index = 0; + hdnode_public_ckd(&node, session_key_rotation[index]); + + index += 1; + hdnode_public_ckd(&node, session_key_rotation[index]); + + memcpy( + session.derived_server_public_key, node.public_key, SESSION_PUB_KEY_SIZE); + + return true; +} + +static bool verify_session_signature(uint8_t *payload, + size_t payload_size, + uint8_t *signature) { + uint8_t hash[32] = {0}; + sha256_Raw(payload, payload_size, hash); + + uint8_t status = ecdsa_verify_digest( + &nist256p1, session.derived_server_public_key, signature, hash); + + return (status == 0); +} + +static bool derive_session_iv() { + curve_point server_random_public_point; + memcpy(&server_random_public_point, + &session.server_random_public_point, + sizeof(curve_point)); + point_add( + curve, &session.device_random_public_point, &server_random_public_point); + + uint8_t session_iv[2 * SESSION_PRIV_KEY_SIZE]; + bn_write_be(&server_random_public_point.x, session_iv); + bn_write_be(&server_random_public_point.y, + session_iv + SESSION_PRIV_KEY_SIZE); + + sha256_Raw(session_iv, 2 * SESSION_PUB_KEY_SIZE, session.session_iv); + + return true; +} + +static bool derive_session_key() { + if (ecdh_multiply(curve, + session.device_random, + session.server_random_public, + session.session_key) != 0) { + LOG_ERROR("ERROR: Session key not generated"); + return false; + } + return true; +} + +static void session_append_signature(uint8_t *payload, size_t payload_size) { + uint8_t hash[32] = {0}; // hash size reference taken from the atecc_sign + sha256_Raw(payload, payload_size, hash); + auth_data_t signed_data = atecc_sign(hash); + + uint8_t offset = payload_size; + memcpy(payload + offset, signed_data.signature, SIGNATURE_SIZE); + offset += SIGNATURE_SIZE; + memcpy(payload + offset, signed_data.postfix1, POSTFIX1_SIZE); + offset += POSTFIX1_SIZE; + memcpy(payload + offset, signed_data.postfix2, POSTFIX2_SIZE); + offset += POSTFIX2_SIZE; +} + +static bool session_get_random_keys(uint8_t *random, + uint8_t *random_public, + curve_point random_public_point) { + memzero(random, SESSION_PRIV_KEY_SIZE); + memzero(random_public, SESSION_PUB_KEY_SIZE); + memset(&random_public_point, 0, sizeof(random_public_point)); + +#if USE_SIMULATOR == 0 + random_generate(random, SESSION_PRIV_KEY_SIZE); +#else + uint8_t get_ec_random[32] = {0x0b, 0x78, 0x9a, 0x1e, 0xb8, 0x0b, 0x7a, 0xac, + 0x97, 0xa1, 0x54, 0xd7, 0x0c, 0x5a, 0x53, 0x95, + 0x6f, 0x9c, 0xed, 0x97, 0x6f, 0xc7, 0xed, 0x7f, + 0xf9, 0x10, 0x01, 0xc1, 0xa8, 0x30, 0xde, 0xb1}; + memcpy(random, get_ec_random, SESSION_PRIV_KEY_SIZE); +#endif + session_curve_init(); + ecdsa_get_public_key33(curve, random, random_public); + + if (!ecdsa_read_pubkey(curve, random_public, &random_public_point)) { + LOG_ERROR("\nERROR: Random public key point not read"); + return false; + } + + return; +} + +static bool session_send_device_key(uint8_t *payload) { + if (!session_get_random_keys(session.device_random, + session.device_random_public, + session.device_random_public_point)) { + LOG_ERROR("\nERROR: Device Random keys not generated"); + return false; + } + + // Get device_id +#if USE_SIMULATOR == 0 + if (get_device_serial() != 0) { + LOG_ERROR("\nERROR: Device Serial fetch failed"); + return false; + } + memcpy(session.device_id, atecc_data.device_serial, DEVICE_SERIAL_SIZE); +#else + memcpy(session.device_id, session.device_random, DEVICE_SERIAL_SIZE); +#endif + + uint32_t offset = 0; + memcpy(payload + offset, session.device_random_public, SESSION_PUB_KEY_SIZE); + offset += SESSION_PUB_KEY_SIZE; + memcpy(payload + offset, session.device_id, DEVICE_SERIAL_SIZE); + offset += DEVICE_SERIAL_SIZE; + session_append_signature(payload, offset); + + return true; +} + +static bool session_receive_server_key(uint8_t *server_message) { + // Input Payload: Server_Random_public + Session Age + Device Id + ASSERT(server_message != NULL); + + if (!derive_server_public_key()) { + LOG_ERROR("\nERROR: Server Randoms not read"); + return false; + } + + size_t server_message_size = + SESSION_PUB_KEY_SIZE + SESSION_AGE_SIZE + DEVICE_SERIAL_SIZE; + + if (!verify_session_signature(server_message, + server_message_size, + server_message + server_message_size)) { + LOG_ERROR("\nERROR: Message from server invalid"); + return false; + } + + uint8_t offset = 0; + memcpy(session.server_random_public, + server_message + offset, + SESSION_PUB_KEY_SIZE); + offset += SESSION_PUB_KEY_SIZE; + memcpy(session.session_age, server_message + offset, SESSION_AGE_SIZE); + offset += SESSION_AGE_SIZE; + // Verify Device ID + if (memcmp(session.device_id, server_message + offset, DEVICE_SERIAL_SIZE) != + 0) { + } + offset += DEVICE_SERIAL_SIZE; + + if (!ecdsa_read_pubkey(curve, + session.server_random_public, + &session.server_random_public_point)) { + LOG_ERROR("\nERROR: Server random public key point not read"); + return false; + } + + if (!derive_session_iv() || !derive_session_key()) { + LOG_ERROR("\nERROR: Generation session keys"); + return false; + } + + return true; +} + +// TODO: convert this into macro +static void uint32_to_uint8_array(uint32_t value, uint8_t arr[4]) { + arr[0] = (value >> 24) & 0xFF; // Extract the highest byte + arr[1] = (value >> 16) & 0xFF; // Extract the second highest byte + arr[2] = (value >> 8) & 0xFF; // Extract the second lowest byte + arr[3] = value & 0xFF; // Extract the lowest byte +} + +static void initiate_request(void) { + uint8_t payload[SESSION_BUFFER_SIZE] = {0}; + if (!session_send_device_key(payload)) { + // TODO: Error Handling + LOG_ERROR("xxec %d", __LINE__); + comm_reject_invalid_cmd(); + clear_message_received_data(); + return; + } + send_core_session_start_response_to_host(payload); +} + +static void start_request(const core_msg_t *core_msg) { + uint8_t server_message_payload[SESSION_PUB_KEY_SIZE + SESSION_AGE_SIZE + + DEVICE_SERIAL_SIZE]; + uint32_t offset = 0; + core_msg = NULL; + core_session_start_begin_request_t request = + core_msg->session_start.request.start; + memcpy(server_message_payload, + request.session_random_public, + SESSION_PUB_KEY_SIZE); + offset += SESSION_PUB_KEY_SIZE; + + uint32_to_uint8_array(core_msg->session_start.request.start.session_age, + server_message_payload + SESSION_PUB_KEY_SIZE); + offset += SESSION_AGE_SIZE; + memcpy(server_message_payload + offset, + core_msg->session_start.request.start.device_id, + DEVICE_SERIAL_SIZE); + + if (!session_receive_server_key(server_message_payload)) { + // TODO: Error Handling + LOG_ERROR("xxec %d", __LINE__); + comm_reject_invalid_cmd(); + clear_message_received_data(); + + return; + } + send_core_session_start_ack_to_host(); +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void core_session_clear_metadata() { + memzero(&session, sizeof(session_config_t)); +} + +void core_session_parse_message(const core_msg_t *core_msg) { + size_t request_type = core_msg->session_start.request.which_request; + switch (request_type) { + case CORE_SESSION_START_REQUEST_INITIATE_TAG: { + initiate_request(); + } break; + + case CORE_SESSION_START_REQUEST_START_TAG: { + start_request(core_msg); + } break; + + default: + // TODO: Error Handling + break; + } +} diff --git a/common/core/core_session.h b/common/core/core_session.h new file mode 100644 index 000000000..f487f6a56 --- /dev/null +++ b/common/core/core_session.h @@ -0,0 +1,83 @@ +/** + * @file core_session.h + * @author Cypherock X1 Team + * @brief Header file containing the session utility functions + * This file declares the functions used to create and manage the + * session, send authentication requests and verify the responses. + * + * @copyright Copyright (c) 2022 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ + +#ifndef CORE_SESSION +#define CORE_SESSION + +#pragma once + +#include +#include + +#include "application_startup.h" +#include "atecc_utils.h" +#include "base58.h" +#include "bip32.h" +#include "card_fetch_data.h" +#include "core_api.h" +#include "curves.h" +#include "inheritance_main.h" +#include "nfc.h" +#include "rand.h" + +#define SESSION_BUFFER_SIZE 1024 +#define SESSION_PUB_KEY_SIZE 33 +#define SESSION_PRIV_KEY_SIZE 32 +#define SESSION_AGE_SIZE 4 + +#define SESSION_IV_SIZE 16 +#define SESSION_KEY_SIZE 32 + +#define SESSION_MSG_MAX 5 +#define SESSION_PACKET_BUFFER 400 +#define SESSION_PACKET_SIZE \ + (ENCRYPTED_DATA_SIZE * SESSION_MSG_MAX) + \ + SESSION_PACKET_BUFFER // (112 * 10) * 5 + 400 = 6000 + +extern const uint32_t session_key_rotation[2]; + +/** + * @brief Stores the session information + * @since v1.0.0 + */ +#pragma pack(push, 1) +typedef struct { + uint8_t device_id[DEVICE_SERIAL_SIZE]; + uint8_t device_random[SESSION_PRIV_KEY_SIZE]; + uint8_t device_random_public[SESSION_PUB_KEY_SIZE]; + curve_point device_random_public_point; + + uint8_t derived_server_public_key[SESSION_PUB_KEY_SIZE]; + uint8_t server_random_public[SESSION_PUB_KEY_SIZE]; + curve_point server_random_public_point; + uint8_t session_age[SESSION_AGE_SIZE]; + uint8_t server_signature[64]; + + const char wallet_name[NAME_SIZE]; + secure_data_t session_msgs[SESSION_MSG_MAX]; + uint8_t msg_count; + + uint8_t session_iv[SESSION_IV_SIZE]; + uint8_t session_key[SESSION_PRIV_KEY_SIZE]; + + uint8_t packet[SESSION_PACKET_SIZE]; + +} session_config_t; +#pragma pack(pop) + +extern session_config_t session; + +void core_session_parse_message(const core_msg_t *core_msg); + +void core_session_clear_metadata(); + +#endif // CORE_SESSION diff --git a/common/interfaces/card_interface/apdu.c b/common/interfaces/card_interface/apdu.c index a0c1814f1..fd587cb32 100644 --- a/common/interfaces/card_interface/apdu.c +++ b/common/interfaces/card_interface/apdu.c @@ -428,7 +428,6 @@ uint16_t create_apdu_inheritance(const uint8_t name[NAME_SIZE], ASSERT(data != NULL); ASSERT(apdu != NULL); ASSERT(data_size != 0); - ASSERT(operation != 0); apdu[OFFSET_CLA] = CLA_ISO7816; apdu[OFFSET_INS] = APDU_INHERITANCE; diff --git a/common/interfaces/card_interface/nfc.c b/common/interfaces/card_interface/nfc.c index 4d05cea8c..1e078f912 100644 --- a/common/interfaces/card_interface/nfc.c +++ b/common/interfaces/card_interface/nfc.c @@ -545,8 +545,8 @@ ISO7816 nfc_encrypt_data(const uint8_t name[NAME_SIZE], uint16_t *encrypted_data_size) { ASSERT(name != NULL); ASSERT(plain_data != NULL); + ASSERT(plain_data_size != 0); ASSERT(encrypted_data != NULL); - ASSERT(encrypted_data_size != NULL); ISO7816 status_word = CLA_ISO7816; uint8_t send_apdu[600] = {0}, *recv_apdu = send_apdu; @@ -562,6 +562,9 @@ ISO7816 nfc_encrypt_data(const uint8_t name[NAME_SIZE], uint32_t system_clock = uwTick; ret_code_t err_code = nfc_exchange_apdu(send_apdu, send_len, recv_apdu, &recv_len); + + uint8_t tmp[236]; + memcpy(tmp, recv_apdu, 236); LOG_SWV("Encrypt data in %lums\n", uwTick - system_clock); if (err_code != STM_SUCCESS) { @@ -572,8 +575,8 @@ ISO7816 nfc_encrypt_data(const uint8_t name[NAME_SIZE], if (status_word == SW_NO_ERROR) { // Extracting Data from APDU - *encrypted_data_size = recv_apdu[1]; - memcpy(encrypted_data, recv_apdu + 2, recv_apdu[1]); + *encrypted_data_size = recv_len - 5; + memcpy(encrypted_data, recv_apdu + 3, recv_len - 5); } } @@ -605,6 +608,9 @@ ISO7816 nfc_decrypt_data(const uint8_t name[NAME_SIZE], uint32_t system_clock = uwTick; ret_code_t err_code = nfc_exchange_apdu(send_apdu, send_len, recv_apdu, &recv_len); + + uint8_t tmp[236]; + memcpy(tmp, recv_apdu, 236); LOG_SWV("Decrypt data in %lums\n", uwTick - system_clock); if (err_code != STM_SUCCESS) { return err_code; @@ -614,8 +620,8 @@ ISO7816 nfc_decrypt_data(const uint8_t name[NAME_SIZE], if (status_word == SW_NO_ERROR) { // Extracting Data from APDU - *plain_data_size = recv_apdu[1]; - memcpy(plain_data, recv_apdu + 2, recv_apdu[1]); + *plain_data_size = recv_len - 5; + memcpy(plain_data, recv_apdu + 3, recv_len - 5); } } diff --git a/common/interfaces/desktop_app_interface/usb_event.c b/common/interfaces/desktop_app_interface/usb_event.c index 354a2a639..332812f61 100644 --- a/common/interfaces/desktop_app_interface/usb_event.c +++ b/common/interfaces/desktop_app_interface/usb_event.c @@ -65,8 +65,10 @@ #include "app_registry.h" #include "core.pb.h" #include "core_api.h" +#include "core_session.h" #include "memzero.h" #include "pb_decode.h" +#include "ui_core_confirm.h" #include "usb_api.h" #include "usb_api_priv.h" @@ -96,6 +98,12 @@ static usb_core_msg_t core_msg; // TODO: Following will be replaced when core starts maintaining it static uint32_t applet_id = 0; +/** + * Decoded core_msg_t while parsing by @ref get_core_req_type, + * to be used for any further processing required. + */ +static core_msg_t core_msg_p; + /***************************************************************************** * GLOBAL VARIABLES *****************************************************************************/ @@ -161,12 +169,13 @@ static void clear_msg_context() { static core_error_type_t get_core_req_type(usb_core_msg_t msg, size_t *request_type) { - core_msg_t core_msg_p = CORE_MSG_INIT_ZERO; + memzero(&core_msg_p, sizeof(core_msg_t)); pb_istream_t stream = pb_istream_from_buffer(msg.buffer, msg.size); core_error_type_t status = CORE_INVALID_MSG; // invalid buffer ref, 0 size & decode failure are error situation if (false == pb_decode(&stream, CORE_MSG_FIELDS, &core_msg_p) || NULL == msg.buffer || 0 == msg.size) { + // TODO: ADD PBERROR LOG return status; } @@ -184,6 +193,11 @@ static core_error_type_t get_core_req_type(usb_core_msg_t msg, } } break; + case CORE_MSG_SESSION_CLOSE_TAG: + case CORE_MSG_SESSION_START_TAG: { + status = CORE_NO_ERROR; + } break; + default: break; } @@ -277,8 +291,17 @@ bool usb_get_event(usb_event_t *evt) { populate_version_list(&resp); send_app_version_list_to_host(&resp); reset_event_obj(&usb_event); + + } else if (CORE_MSG_SESSION_START_TAG == request_type) { + core_session_parse_message(&core_msg_p); + reset_event_obj(&usb_event); + + } else if (CORE_MSG_SESSION_CLOSE_TAG == request_type) { + core_session_clear_metadata(); + send_core_session_close_response_to_host(); + reset_event_obj(&usb_event); } } } return evt->flag; -} \ No newline at end of file +} diff --git a/common/libraries/util/atecc_utils.c b/common/libraries/util/atecc_utils.c new file mode 100644 index 000000000..28b83e3e9 --- /dev/null +++ b/common/libraries/util/atecc_utils.c @@ -0,0 +1,306 @@ +/** + * @file atecc_utils.c + * @author Cypherock X1 Team + * @brief Definition of the ATECC signature helper functions used for + * signing and verifying the messages using the ATECC608A chip. + * + * @copyright Copyright (c) 2022 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2022 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +#include "atecc_utils.h" + +#include "board.h" +#include "flash_api.h" +#include "sha2.h" +#if USE_SIMULATOR == 0 +#include "stm32l4xx_it.h" +#endif + +static ATCA_STATUS helper_config_to_sign_internal( + ATCADeviceType device_type, + struct atca_sign_internal_in_out *param, + const uint8_t *config) { + const uint8_t *value = NULL; + uint16_t slot_locked = 0; + + if (param == NULL || config == NULL || param->temp_key == NULL) { + return ATCA_BAD_PARAM; + } + + // SlotConfig[TempKeyFlags.keyId] + value = &config[20 + param->temp_key->key_id * 2]; + param->slot_config = (uint16_t)value[0] | ((uint16_t)value[1] << 8); + + // KeyConfig[TempKeyFlags.keyId] + value = &config[96 + param->temp_key->key_id * 2]; + param->key_config = (uint16_t)value[0] | ((uint16_t)value[1] << 8); + + if (device_type == ATECC108A && param->temp_key->key_id < 8) { + value = &config[52 + param->temp_key->key_id * 2]; + param->use_flag = value[0]; + param->update_count = value[0]; + } else { + param->use_flag = 0x00; + param->update_count = 0x00; + } + + // SlotLocked:TempKeyFlags.keyId + slot_locked = (uint16_t)config[88] | ((uint16_t)config[89] << 8); + param->is_slot_locked = + (slot_locked & (1 << param->temp_key->key_id)) ? false : true; + + return ATCA_SUCCESS; +} + +void helper_get_gendig_hash(atecc_slot_define_t slot, + uint8_t *data, + uint8_t *digest, + uint8_t *postfix, + atecc_data_t *atecc_value) { + if (digest == NULL || data == NULL || postfix == NULL) { + return; + } + + uint8_t tempkey_init[96] = {0}; + uint8_t atecc_serial[9]; + atecc_value->status = atcab_read_serial_number(atecc_serial); + memcpy(tempkey_init, data, 32); + postfix[0] = tempkey_init[32] = 0x15; + postfix[1] = tempkey_init[33] = 0x02; + postfix[2] = tempkey_init[34] = slot; + postfix[3] = tempkey_init[35] = 0x00; + postfix[4] = tempkey_init[36] = atecc_serial[8]; + postfix[5] = tempkey_init[37] = atecc_serial[0]; + postfix[6] = tempkey_init[38] = atecc_serial[1]; + + sha256_Raw(tempkey_init, 96, digest); +} + +ATCA_STATUS helper_sign_internal_msg(struct atca_sign_internal_in_out *param, + uint8_t mode, + uint8_t priv_key_id, + uint8_t data_key_id, + atecc_data_t *atecc_value) { + uint8_t msg[55]; + uint8_t cfg[128] = {0}, sn[9] = {0}; + atca_temp_key_t temp_key = {0}; + param->key_id = priv_key_id; + temp_key.key_id = data_key_id; + temp_key.gen_dig_data = 1; + temp_key.valid = 1; + temp_key.source_flag = 1; + + atecc_value->status = atcab_read_config_zone(cfg); + memcpy(temp_key.value, param->message, 32); + param->temp_key = &temp_key; + helper_config_to_sign_internal(ATECC608A, param, cfg); + atecc_value->status = atcab_read_serial_number(sn); + + if (param == NULL || param->temp_key == NULL) { + return ATCA_BAD_PARAM; + } + + memset(msg, 0, sizeof(msg)); + memcpy(&msg[0], param->temp_key->value, 32); + msg[32] = ATCA_SIGN; // Sign OpCode + msg[33] = param->mode; // Sign Mode + msg[34] = (uint8_t)(param->key_id >> 0); // Sign KeyID + msg[35] = (uint8_t)(param->key_id >> 8); + msg[36] = + (uint8_t)(param->slot_config >> 0); // SlotConfig[TempKeyFlags.keyId] + msg[37] = (uint8_t)(param->slot_config >> 8); + msg[38] = + (uint8_t)(param->key_config >> 0); // KeyConfig[TempKeyFlags.keyId] + msg[39] = (uint8_t)(param->key_config >> 8); + + // TempKeyFlags (b0-3: keyId, b4: sourceFlag, b5: GenDigData, b6: GenKeyData, + // b7: NoMacFlag) + msg[40] |= ((param->temp_key->key_id & 0x0F) << 0); + msg[40] |= ((param->temp_key->source_flag & 0x01) << 4); + msg[40] |= ((param->temp_key->gen_dig_data & 0x01) << 5); + msg[40] |= ((param->temp_key->gen_key_data & 0x01) << 6); + msg[40] |= ((param->temp_key->no_mac_flag & 0x01) << 7); + msg[41] = 0x00; + msg[42] = 0x00; + + // Serial Number + msg[43] = sn[8]; + memcpy(&msg[48], &sn[0], 2); + if (param->mode & SIGN_MODE_INCLUDE_SN) { + memcpy(&msg[44], &sn[4], 4); + memcpy(&msg[50], &sn[2], 2); + } + + // The bit within the SlotLocked field corresponding to the last key used in + // the TempKey computation is in the LSB + msg[52] = param->is_slot_locked ? 0x00 : 0x01; + + // If the slot contains a public key corresponding to a supported curve, and + // if PubInfo indicates this key must be validated before being used by + // Verify, and if the validity bits have a value of 0x05, then the PubKey + // Valid byte will be 0x01.In all other cases, it will be 0. + msg[53] = param->for_invalidate ? 0x01 : 0x00; + + msg[54] = 0x00; + + if (param->message) { + memcpy(param->message, msg, sizeof(msg)); + } + if (param->verify_other_data) { + memcpy(¶m->verify_other_data[0], &msg[33], 10); + memcpy(¶m->verify_other_data[10], &msg[44], 4); + memcpy(¶m->verify_other_data[14], &msg[50], 5); + } + if (param->digest) { + return atcac_sw_sha2_256(msg, sizeof(msg), param->digest); + } else { + return ATCA_SUCCESS; + } +} + +auth_data_t atecc_sign(uint8_t *hash) { + uint8_t challenge_no[32]; + uint8_t io_protection_key[32] = {0}; + uint8_t nonce[32] = {0}; + auth_data_t auth_challenge_packet = {0}; + uint8_t tempkey_hash[DEVICE_SERIAL_SIZE + POSTFIX2_SIZE] = {0}; + uint8_t final_hash[32] = {0}; + get_io_protection_key(io_protection_key); + + memset(challenge_no, 0, sizeof(challenge_no)); + for (int i = 0; i < 32; ++i) + challenge_no[i] = challenge_no[i] ^ hash[i]; + + atca_sign_internal_in_out_t sign_internal_param = {0}; + + atecc_data.retries = DEFAULT_ATECC_RETRIES; + bool usb_irq_enable_on_entry = NVIC_GetEnableIRQ(OTG_FS_IRQn); + NVIC_DisableIRQ(OTG_FS_IRQn); + do { + OTG_FS_IRQHandler(); + + if (atecc_data.status != ATCA_SUCCESS) + LOG_CRITICAL("AERR CH: %04x, count:%d", + atecc_data.status, + DEFAULT_ATECC_RETRIES - atecc_data.retries); + + if ((atecc_data.status = atcab_init(atecc_data.cfg_atecc608a_iface)) != + ATCA_SUCCESS) { + continue; + } + + atecc_data.status = atcab_write_enc( + slot_5_challenge, 0, challenge_no, io_protection_key, slot_6_io_key); + if (atecc_data.status != ATCA_SUCCESS) { + continue; + } + + atecc_data.status = atcab_nonce(nonce); + if (atecc_data.status != ATCA_SUCCESS) { + continue; + } + + atecc_data.status = atcab_gendig(ATCA_ZONE_DATA, slot_5_challenge, NULL, 0); + if (atecc_data.status != ATCA_SUCCESS) { + continue; + } + + atecc_data.status = atcab_sign_internal( + slot_2_auth_key, false, false, auth_challenge_packet.signature); + if (atecc_data.status != ATCA_SUCCESS) { + continue; + } + + helper_get_gendig_hash(slot_5_challenge, + challenge_no, + tempkey_hash, + auth_challenge_packet.postfix1, + &atecc_data); + + sign_internal_param.message = tempkey_hash; + sign_internal_param.digest = final_hash; + + helper_sign_internal_msg(&sign_internal_param, + SIGN_MODE_INTERNAL, + slot_2_auth_key, + slot_5_challenge, + &atecc_data); + + memset(challenge_no, 0, sizeof(challenge_no)); + atecc_data.status = atcab_write_enc( + slot_5_challenge, 0, challenge_no, io_protection_key, slot_6_io_key); + if (atecc_data.status != ATCA_SUCCESS) { + continue; + } + + { + uint8_t result = ecdsa_verify_digest(&nist256p1, + get_auth_public_key(), + auth_challenge_packet.signature, + sign_internal_param.digest); + if (atecc_data.status != ATCA_SUCCESS || result != 0) { + LOG_ERROR("err xxx33 fault %d verify %d", atecc_data.status, result); + } + } + + } while (--atecc_data.retries && atecc_data.status != ATCA_SUCCESS); + if (usb_irq_enable_on_entry == true) + NVIC_EnableIRQ(OTG_FS_IRQn); + + memcpy(auth_challenge_packet.postfix2, + &tempkey_hash[32], + POSTFIX2_SIZE); // postfix 2 (12bytes) + + return auth_challenge_packet; +} \ No newline at end of file diff --git a/common/libraries/util/atecc_utils.h b/common/libraries/util/atecc_utils.h new file mode 100644 index 000000000..93515b284 --- /dev/null +++ b/common/libraries/util/atecc_utils.h @@ -0,0 +1,77 @@ +/** + * @file atecc_utils.h + * @author Cypherock X1 Team + * @brief Util functions related to atecc signing + * @version 0.1 + * @date 2022-12-16 + * + * @copyright Copyright (c) 2022 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + */ + +#ifndef ATECC_UTILS_H +#define ATECC_UTILS_H + +#pragma once + +#include "atca_host.h" +// #include "stm32l4xx_it.h" +#include "device_authentication_api.h" +#include "nist256p1.h" + +/** + * @brief + * @details + * + * @see + * @since v1.0.0 + * + * @note + */ +typedef struct auth_data_struct { + uint8_t postfix1[POSTFIX1_SIZE], postfix2[POSTFIX2_SIZE], + signature[SIGNATURE_SIZE], serial[DEVICE_SERIAL_SIZE]; +} auth_data_t; + +/** + * @brief + * @details + * + * @see swap_transaction_controller(), device_authentication_controller() + * + * @param slot + * @param data + * @param digest + * @param postfix + * @param atecc_value + */ +void helper_get_gendig_hash(atecc_slot_define_t slot, + uint8_t *data, + uint8_t *digest, + uint8_t *postfix, + atecc_data_t *atecc_value); + +/** + * @brief + * @details + * + * @see swap_transaction_controller(), device_authentication_controller() + * + * @param param + * @param mode + * @param priv_key_id + * @param data_key_id + * @param atecc_value + * @return + */ +ATCA_STATUS helper_sign_internal_msg(struct atca_sign_internal_in_out *param, + uint8_t mode, + uint8_t priv_key_id, + uint8_t data_key_id, + atecc_data_t *atecc_value); + +auth_data_t atecc_sign(uint8_t *hash); + +#endif // ATECC_UTILS_H \ No newline at end of file diff --git a/src/card_operations/card_fetch_data.h b/src/card_operations/card_fetch_data.h new file mode 100644 index 000000000..3cdab2c7e --- /dev/null +++ b/src/card_operations/card_fetch_data.h @@ -0,0 +1,55 @@ +/** + * @file card_fetch_data.h + * @author Cypherock X1 Team + * @brief API for deleting wallet share from a card + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef CARD_FETCH_DATA_H +#define CARD_FETCH_DATA_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include + +#include "card_operation_typedefs.h" +#include "stdbool.h" +#include "stdint.h" +#include "wallet.h" + +#define PLAIN_DATA_BUFFER_SIZE 100 // Card data encryption limit <100 chars> +#define ENCRYPTED_DATA_BUFFER_SIZE 112 + +#define DATA_CHUNKS_MAX 10 + +#define PLAIN_DATA_SIZE (PLAIN_DATA_BUFFER_SIZE * DATA_CHUNKS_MAX) +#define ENCRYPTED_DATA_SIZE (ENCRYPTED_DATA_BUFFER_SIZE * DATA_CHUNKS_MAX) + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ +#pragma pack(push, 1) +typedef struct { + uint8_t plain_data[PLAIN_DATA_SIZE]; + uint16_t plain_data_size; + uint8_t encrypted_data[ENCRYPTED_DATA_SIZE]; + uint16_t encrypted_data_size; + // is private +} secure_data_t; +#pragma pack(pop) + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ + +#endif From fb62de65edd8cb1c57f6716e224cf63d8bf8795a Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Thu, 22 Aug 2024 23:33:15 -0700 Subject: [PATCH 2/8] chore: Update code according to review comments --- common/core/core_session.c | 163 ++++++++++++++---- common/core/core_session.h | 10 +- .../desktop_app_interface/usb_event.c | 2 +- common/libraries/util/atecc_utils.c | 2 +- src/card_operations/card_fetch_data.h | 4 +- 5 files changed, 142 insertions(+), 39 deletions(-) diff --git a/common/core/core_session.c b/common/core/core_session.c index 58628ef6e..9b1648a7f 100644 --- a/common/core/core_session.c +++ b/common/core/core_session.c @@ -1,7 +1,6 @@ /** - * @file session_utils.c * @author Cypherock X1 Team - * @brief Definition of the session utility functions + * @brief Definition of the session functions * This file defines the functions used to create and manage the * session, send authentication requests and verify the responses. * @@ -97,24 +96,121 @@ session_config_t CONFIDENTIAL session = {0}; *****************************************************************************/ /** - * @brief Completes the session creation process - * @details It verifies the server response and stores the session id and - * session random. - * @param session_init_details The server response - * @param verification_details The buffer to store the details - * to be send back to the server to confirm - * /home/parnika/Documents/GitHub/x1_wallet_firmware/common/libraries/util/session_utils.c:394:6the - * verification. - * @return true if the session is created, false otherwise + * @brief Receives and processes the server's key as part of the session + * initialization. * - * @see SESSION_ESTABLISH - * @since v1.0.0 + * @param server_message The buffer containing the server's message, which + * includes the server's random public key, session age, and device ID. + * @return true if the server's key is successfully received and processed. + * @return false otherwise. */ static bool session_receive_server_key(uint8_t *server_message); +/** + * @brief Initializes the curve parameters for the session. + */ +static void session_curve_init(); + +/** + * @brief Derives the server's public key from the extended public key. + * + * @return true if the server's public key is successfully derived. + * @return false otherwise. + */ +static bool derive_server_public_key(); + +/** + * @brief Verifies the session signature using the provided payload and + * signature. + * + * @param payload The payload data to verify. + * @param payload_size The size of the payload. + * @param signature The signature to verify. + * @return true if the signature is valid. + * @return false otherwise. + */ +static bool verify_session_signature(const uint8_t *payload, + const size_t payload_size, + const uint8_t *signature); + +/** + * @brief Derives the session initialization vector (IV) using the server's and + * device's random public points. + */ +static void derive_session_iv(); + +/** + * @brief Derives the session key using ECDH multiplication. + * + * @return true if the session key is successfully derived. + * @return false otherwise. + */ +static bool derive_session_key(); + +/** + * @brief Derives both the session IV and session key. + * + * @return true if both the session IV and session key are successfully derived. + * @return false otherwise. + */ +static bool derive_session_iv_and_session_key(); + +/** + * @brief Appends a signature to the payload. + * + * @param payload The payload to append the signature to. + * @param payload_size The size of the payload. + */ +static void session_append_signature(uint8_t *payload, + const size_t payload_size); + +/** + * @brief Generates random keys for the session. + * + * @param random The buffer to store the generated random key. + * @param random_public The buffer to store the generated random public key. + * @param random_public_point The curve point to store the random public key + * point. + * @return true if the random keys are successfully generated. + * @return false otherwise. + */ +static bool session_get_random_keys(uint8_t *random, + uint8_t *random_public, + curve_point random_public_point); + +/** + * @brief Sends the device key as part of the session initialization. + * + * @param payload The buffer to store the payload data. + * @return true if the device key is successfully sent. + * @return false otherwise. + */ +static bool session_send_device_key(uint8_t *payload); + +/** + * @brief Converts a 32-bit unsigned integer to a 4-byte array. + * + * @param value The 32-bit unsigned integer to convert. + * @param arr The array to store the converted bytes. + */ +static void uint32_to_uint8_array(uint32_t value, uint8_t arr[4]); + +/** + * @brief Initiates a request by sending the device key. + */ +static void initiate_request(void); + +/** + * @brief Starts a request by processing the core message. + * + * @param core_msg The core message containing the request data. + */ +static void start_request(const core_msg_t *core_msg); + /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ + static void session_curve_init() { curve = get_curve_by_name(SECP256K1_NAME)->params; } @@ -143,19 +239,20 @@ static bool derive_server_public_key() { return true; } -static bool verify_session_signature(uint8_t *payload, - size_t payload_size, - uint8_t *signature) { +static bool verify_session_signature(const uint8_t *payload, + const size_t payload_size, + const uint8_t *signature) { uint8_t hash[32] = {0}; sha256_Raw(payload, payload_size, hash); - uint8_t status = ecdsa_verify_digest( - &nist256p1, session.derived_server_public_key, signature, hash); + bool is_signature_valid = + ecdsa_verify_digest( + &nist256p1, session.derived_server_public_key, signature, hash) == 0; - return (status == 0); + return is_signature_valid; } -static bool derive_session_iv() { +static void derive_session_iv() { curve_point server_random_public_point; memcpy(&server_random_public_point, &session.server_random_public_point, @@ -169,8 +266,6 @@ static bool derive_session_iv() { session_iv + SESSION_PRIV_KEY_SIZE); sha256_Raw(session_iv, 2 * SESSION_PUB_KEY_SIZE, session.session_iv); - - return true; } static bool derive_session_key() { @@ -184,7 +279,13 @@ static bool derive_session_key() { return true; } -static void session_append_signature(uint8_t *payload, size_t payload_size) { +static bool derive_session_iv_and_session_key() { + derive_session_iv(); + return derive_session_key(); +} + +static void session_append_signature(uint8_t *payload, + const size_t payload_size) { uint8_t hash[32] = {0}; // hash size reference taken from the atecc_sign sha256_Raw(payload, payload_size, hash); auth_data_t signed_data = atecc_sign(hash); @@ -222,7 +323,7 @@ static bool session_get_random_keys(uint8_t *random, return false; } - return; + return true; } static bool session_send_device_key(uint8_t *payload) { @@ -233,7 +334,7 @@ static bool session_send_device_key(uint8_t *payload) { return false; } - // Get device_id +// TODO: standardize simulator handling for hardware specific functionality #if USE_SIMULATOR == 0 if (get_device_serial() != 0) { LOG_ERROR("\nERROR: Device Serial fetch failed"); @@ -256,7 +357,10 @@ static bool session_send_device_key(uint8_t *payload) { static bool session_receive_server_key(uint8_t *server_message) { // Input Payload: Server_Random_public + Session Age + Device Id - ASSERT(server_message != NULL); + if (server_message != NULL) { + LOG_ERROR("\nERROR: server_message not set"); + return false; + } if (!derive_server_public_key()) { LOG_ERROR("\nERROR: Server Randoms not read"); @@ -293,7 +397,7 @@ static bool session_receive_server_key(uint8_t *server_message) { return false; } - if (!derive_session_iv() || !derive_session_key()) { + if (!derive_session_iv_and_session_key()) { LOG_ERROR("\nERROR: Generation session keys"); return false; } @@ -325,7 +429,6 @@ static void start_request(const core_msg_t *core_msg) { uint8_t server_message_payload[SESSION_PUB_KEY_SIZE + SESSION_AGE_SIZE + DEVICE_SERIAL_SIZE]; uint32_t offset = 0; - core_msg = NULL; core_session_start_begin_request_t request = core_msg->session_start.request.start; memcpy(server_message_payload, @@ -359,7 +462,7 @@ void core_session_clear_metadata() { memzero(&session, sizeof(session_config_t)); } -void core_session_parse_message(const core_msg_t *core_msg) { +void core_session_parse_start_message(const core_msg_t *core_msg) { size_t request_type = core_msg->session_start.request.which_request; switch (request_type) { case CORE_SESSION_START_REQUEST_INITIATE_TAG: { @@ -372,6 +475,8 @@ void core_session_parse_message(const core_msg_t *core_msg) { default: // TODO: Error Handling + comm_reject_invalid_cmd(); + clear_message_received_data(); break; } } diff --git a/common/core/core_session.h b/common/core/core_session.h index f487f6a56..4be695e81 100644 --- a/common/core/core_session.h +++ b/common/core/core_session.h @@ -1,7 +1,6 @@ /** - * @file core_session.h * @author Cypherock X1 Team - * @brief Header file containing the session utility functions + * @brief Header file containing the session functions * This file declares the functions used to create and manage the * session, send authentication requests and verify the responses. * @@ -34,6 +33,8 @@ #define SESSION_PRIV_KEY_SIZE 32 #define SESSION_AGE_SIZE 4 +#define SESSION_SERVER_SIGNATURE_SIZE 64 + #define SESSION_IV_SIZE 16 #define SESSION_KEY_SIZE 32 @@ -47,7 +48,6 @@ extern const uint32_t session_key_rotation[2]; /** * @brief Stores the session information - * @since v1.0.0 */ #pragma pack(push, 1) typedef struct { @@ -60,7 +60,7 @@ typedef struct { uint8_t server_random_public[SESSION_PUB_KEY_SIZE]; curve_point server_random_public_point; uint8_t session_age[SESSION_AGE_SIZE]; - uint8_t server_signature[64]; + uint8_t server_signature[SESSION_SERVER_SIGNATURE_SIZE]; const char wallet_name[NAME_SIZE]; secure_data_t session_msgs[SESSION_MSG_MAX]; @@ -76,7 +76,7 @@ typedef struct { extern session_config_t session; -void core_session_parse_message(const core_msg_t *core_msg); +void core_session_parse_start_message(const core_msg_t *core_msg); void core_session_clear_metadata(); diff --git a/common/interfaces/desktop_app_interface/usb_event.c b/common/interfaces/desktop_app_interface/usb_event.c index 332812f61..c669db90d 100644 --- a/common/interfaces/desktop_app_interface/usb_event.c +++ b/common/interfaces/desktop_app_interface/usb_event.c @@ -293,7 +293,7 @@ bool usb_get_event(usb_event_t *evt) { reset_event_obj(&usb_event); } else if (CORE_MSG_SESSION_START_TAG == request_type) { - core_session_parse_message(&core_msg_p); + core_session_parse_start_message(&core_msg_p); reset_event_obj(&usb_event); } else if (CORE_MSG_SESSION_CLOSE_TAG == request_type) { diff --git a/common/libraries/util/atecc_utils.c b/common/libraries/util/atecc_utils.c index 28b83e3e9..d9cf1dee8 100644 --- a/common/libraries/util/atecc_utils.c +++ b/common/libraries/util/atecc_utils.c @@ -303,4 +303,4 @@ auth_data_t atecc_sign(uint8_t *hash) { POSTFIX2_SIZE); // postfix 2 (12bytes) return auth_challenge_packet; -} \ No newline at end of file +} diff --git a/src/card_operations/card_fetch_data.h b/src/card_operations/card_fetch_data.h index 3cdab2c7e..5847431ab 100644 --- a/src/card_operations/card_fetch_data.h +++ b/src/card_operations/card_fetch_data.h @@ -1,7 +1,6 @@ /** - * @file card_fetch_data.h * @author Cypherock X1 Team - * @brief API for deleting wallet share from a card + * @brief API for fetching data from card * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ @@ -40,7 +39,6 @@ typedef struct { uint16_t plain_data_size; uint8_t encrypted_data[ENCRYPTED_DATA_SIZE]; uint16_t encrypted_data_size; - // is private } secure_data_t; #pragma pack(pop) From e6f8b4f61cdf7459067dd234ca628f7b889c9a8c Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Tue, 27 Aug 2024 08:33:09 -0700 Subject: [PATCH 3/8] chore: Updated review changes --- common/core/core_api.h | 25 +++++++++++++++++ common/core/core_session.c | 55 ++++++++++++++------------------------ common/core/core_session.h | 21 +++++++++++++-- 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/common/core/core_api.h b/common/core/core_api.h index d9b15ef20..d7a3d15b3 100644 --- a/common/core/core_api.h +++ b/common/core/core_api.h @@ -61,9 +61,34 @@ void send_core_error_msg_to_host(uint32_t core_error_type); void send_app_version_list_to_host( const core_app_version_result_response_t *version_resp); +/** + * @brief Sends a response to the host after receiving a session start payload. + * + * This function constructs a core message containing the confirmation + * initiation response for a session start. It extracts relevant information + * from the payload, such as device random public key, device ID, signature, and + * postfix values. + * + * @param payload Pointer to the received payload data. + */ void send_core_session_start_response_to_host(const uint8_t *payload); +/** + * @brief Sends an acknowledgment to the host after successfully starting a + * session. + * + * This function constructs a core message with the confirmation start response + * for a session start. It indicates that the session has been initiated + * successfully. + */ void send_core_session_start_ack_to_host(); +/** + * @brief Sends a response to the host when closing a session. + * + * This function constructs a core message to acknowledge the session close + * request. It specifies that the session should be cleared. + */ void send_core_session_close_response_to_host(); + #endif /* CORE_API_H */ diff --git a/common/core/core_session.c b/common/core/core_session.c index 9b1648a7f..689bd4b84 100644 --- a/common/core/core_session.c +++ b/common/core/core_session.c @@ -63,6 +63,7 @@ *****************************************************************************/ #include "core_session.h" +#include "bignum.h" #include "core.pb.h" #include "inheritance_main.h" #include "logger.h" @@ -95,17 +96,6 @@ session_config_t CONFIDENTIAL session = {0}; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ -/** - * @brief Receives and processes the server's key as part of the session - * initialization. - * - * @param server_message The buffer containing the server's message, which - * includes the server's random public key, session age, and device ID. - * @return true if the server's key is successfully received and processed. - * @return false otherwise. - */ -static bool session_receive_server_key(uint8_t *server_message); - /** * @brief Initializes the curve parameters for the session. */ @@ -119,6 +109,17 @@ static void session_curve_init(); */ static bool derive_server_public_key(); +/** + * @brief Receives and processes the server's key as part of the session + * initialization. + * + * @param server_message The buffer containing the server's message, which + * includes the server's random public key, session age, and device ID. + * @return true if the server's key is successfully received and processed. + * @return false otherwise. + */ +static bool session_receive_server_key(const uint8_t *server_message); + /** * @brief Verifies the session signature using the provided payload and * signature. @@ -179,21 +180,13 @@ static bool session_get_random_keys(uint8_t *random, curve_point random_public_point); /** - * @brief Sends the device key as part of the session initialization. + * @brief Generates the device key as part of the session initialization. * * @param payload The buffer to store the payload data. - * @return true if the device key is successfully sent. + * @return true if the device key is successfully generated. * @return false otherwise. */ -static bool session_send_device_key(uint8_t *payload); - -/** - * @brief Converts a 32-bit unsigned integer to a 4-byte array. - * - * @param value The 32-bit unsigned integer to convert. - * @param arr The array to store the converted bytes. - */ -static void uint32_to_uint8_array(uint32_t value, uint8_t arr[4]); +static bool session_generate_device_key(uint8_t *payload); /** * @brief Initiates a request by sending the device key. @@ -326,7 +319,7 @@ static bool session_get_random_keys(uint8_t *random, return true; } -static bool session_send_device_key(uint8_t *payload) { +static bool session_generate_device_key(uint8_t *payload) { if (!session_get_random_keys(session.device_random, session.device_random_public, session.device_random_public_point)) { @@ -355,7 +348,7 @@ static bool session_send_device_key(uint8_t *payload) { return true; } -static bool session_receive_server_key(uint8_t *server_message) { +static bool session_receive_server_key(const uint8_t *server_message) { // Input Payload: Server_Random_public + Session Age + Device Id if (server_message != NULL) { LOG_ERROR("\nERROR: server_message not set"); @@ -405,17 +398,9 @@ static bool session_receive_server_key(uint8_t *server_message) { return true; } -// TODO: convert this into macro -static void uint32_to_uint8_array(uint32_t value, uint8_t arr[4]) { - arr[0] = (value >> 24) & 0xFF; // Extract the highest byte - arr[1] = (value >> 16) & 0xFF; // Extract the second highest byte - arr[2] = (value >> 8) & 0xFF; // Extract the second lowest byte - arr[3] = value & 0xFF; // Extract the lowest byte -} - static void initiate_request(void) { uint8_t payload[SESSION_BUFFER_SIZE] = {0}; - if (!session_send_device_key(payload)) { + if (!session_generate_device_key(payload)) { // TODO: Error Handling LOG_ERROR("xxec %d", __LINE__); comm_reject_invalid_cmd(); @@ -436,8 +421,8 @@ static void start_request(const core_msg_t *core_msg) { SESSION_PUB_KEY_SIZE); offset += SESSION_PUB_KEY_SIZE; - uint32_to_uint8_array(core_msg->session_start.request.start.session_age, - server_message_payload + SESSION_PUB_KEY_SIZE); + write_be(server_message_payload + SESSION_PUB_KEY_SIZE, + core_msg->session_start.request.start.session_age); offset += SESSION_AGE_SIZE; memcpy(server_message_payload + offset, core_msg->session_start.request.start.device_id, diff --git a/common/core/core_session.h b/common/core/core_session.h index 4be695e81..fdc2b97aa 100644 --- a/common/core/core_session.h +++ b/common/core/core_session.h @@ -76,8 +76,25 @@ typedef struct { extern session_config_t session; -void core_session_parse_start_message(const core_msg_t *core_msg); - +/** + * @brief Clears the metadata related to the session configuration. + * + * This function zeroes out the memory occupied by the session configuration + * structure (`session_config_t`). It is typically called when closing or + * resetting a session. + */ void core_session_clear_metadata(); +/** + * @brief Parses the start message received from the host. + * + * This function processes the session start message and determines the type of + * request (initiate or start). Depending on the request type, it invokes the + * corresponding handler functions (`initiate_request()` or `start_request()`). + * + * @param core_msg Pointer to the core message containing the session start + * data. + */ +void core_session_parse_start_message(const core_msg_t *core_msg); + #endif // CORE_SESSION From ff39d7f82d33165cc8758d02db21f959efd06f3c Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Thu, 15 Aug 2024 20:44:56 -0700 Subject: [PATCH 4/8] feat: Added basic encryption outline --- .../inheritance_encrypt_data.c | 280 ++++++++++++++++++ apps/inheritance_app/inheritance_main.c | 2 +- apps/inheritance_app/inheritance_priv.h | 12 + .../inheritance/encrypt_data_with_pin.options | 3 + 4 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 apps/inheritance_app/inheritance_encrypt_data.c create mode 100644 common/proto-options/inheritance/encrypt_data_with_pin.options diff --git a/apps/inheritance_app/inheritance_encrypt_data.c b/apps/inheritance_app/inheritance_encrypt_data.c new file mode 100644 index 000000000..c88e6dce4 --- /dev/null +++ b/apps/inheritance_app/inheritance_encrypt_data.c @@ -0,0 +1,280 @@ +/** + * @file inheritance_encrypt_data.c + * @author Cypherock X1 Team + * @brief Inheritance message encrytion login + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include + +#include "inheritance/core.pb.h" +#include "inheritance/encrypt_data_with_pin.pb.h" +#include "inheritance_api.h" +#include "inheritance_priv.h" +#include "reconstruct_wallet_flow.h" +#include "status_api.h" +#include "ui_core_confirm.h" +#include "ui_screens.h" +#include "wallet_list.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief Checks if the provided query contains expected request. + * @details The function performs the check on the request type and if the check + * fails, then it will send an error to the host inheritance app and return + * false. + * + * @param query Reference to an instance of inheritance_query_t containing query + * received from host app + * @param which_request The expected request type enum + * + * @return bool Indicating if the check succeeded or failed + * @retval true If the query contains the expected request + * @retval false If the query does not contain the expected request + */ +static bool check_which_request(const inheritance_query_t *query, + pb_size_t which_request); + +/** + * @brief Validates the derivation path received in the request from host + * @details The function validates the provided account derivation path in the + * request. If invalid path is detected, the function will send an error to the + * host and return false. + * + * @param request Reference to an instance of inheritance_encrypt_data_request_t + * @return bool Indicating if the verification passed or failed + * @retval true If all the derivation path entries are valid + * @retval false If any of the derivation path entries are invalid + */ +static bool validate_request_data( + const inheritance_encrypt_data_with_pin_request_t *request); + +/** + * @brief Takes already received and decoded query for the user confirmation. + * @details The function will verify if the query contains the + * INHERITANCE_ENCRYPT_DATA type of request. Additionally, the wallet-id is + * validated for sanity and the derivation path for the account is also + * validated. After the validations, user is prompted about the action for + * confirmation. The function returns true indicating all the validation and + * user confirmation was a success. The function also duplicates the data from + * query into the inheritance_txn_context for further processing. + * + * @param query Constant reference to the decoded query received from the host + * + * @return bool Indicating if the function actions succeeded or failed + * @retval true If all the validation and user confirmation was positive + * @retval false If any of the validation or user confirmation was negative + */ +STATIC bool inheritance_handle_initiate_query(const inheritance_query_t *query); + +/** + * @brief Aggregates user consent for the encrytion info + * @details The function displays the required messages for user to very + * + * + * @return bool Indicating if the user confirmed the messages + * @retval true If user confirmed the messages displayed + * @retval false Immediate return if any of the messages are disapproved + */ +STATIC bool inheritance_get_user_verification(); + +/** + * @brief Sends the encrypted data to the host + * @details The function encrypts the data and sends it to the host + * + * @param query Reference to an instance of inheritance_query_t to store + * transient request from the host + * @return bool Indicating if the encrypted data is sent to the host + * @retval true If the encrypted data was sent to host successfully + * @retval false If the host responded with unknown/wrong query + */ +static bool send_encrypted_data(inheritance_query_t *query); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +STATIC inheritance_encryption_context_t *inheritance_encryption_context = NULL; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +static bool check_which_request(const inheritance_query_t *query, + pb_size_t which_request) { + if (which_request != query->encrypt.which_request) { + inheritance_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + return false; + } + + return true; +} + +static bool validate_request_data( + const inheritance_encrypt_data_with_pin_request_t *request) { + bool status = true; + + // TODO: check the current request and session validity here + + return status; +} + +STATIC bool inheritance_handle_initiate_query( + const inheritance_query_t *query) { + char wallet_name[NAME_SIZE] = ""; + char msg[100] = ""; + + if (!check_which_request( + query, INHERITANCE_ENCRYPT_DATA_WITH_PIN_REQUEST_INITIATE_TAG) || + !validate_request_data(&query->encrypt) || + !get_wallet_name_by_id(query->encrypt.initiate.wallet_id, + (uint8_t *)wallet_name, + inheritance_send_error)) { + return false; + } + + snprintf(msg, sizeof(msg), "Test %s", wallet_name); // TODO: update message + // + // Take user consent to sign the transaction for the wallet + if (!core_confirmation(msg, inheritance_send_error)) { + return false; + } + + set_app_flow_status(INHERITANCE_ENCRYPT_DATA_STATUS_USER_CONFIRMED); + + // TODO: copy data to local context; + + // show processing screen for a minimum duration (additional time will add due + // to actual processing) + delay_scr_init(ui_text_processing, DELAY_SHORT); + return true; +} + +STATIC bool inheritance_get_user_verification() { + // TODO: Iterate through messages that needs to be verified + if (!core_scroll_page( + "Demo title", "message preview", inheritance_send_error)) { + return false; + } + + set_app_flow_status(INHERITANCE_ENCRYPT_DATA_STATUS_MESSAGE_VERIFIED); + return true; +} + +static bool send_encrypted_data(inheritance_query_t *query) { + inheritance_result_t result = + init_inheritance_result(INHERITANCE_RESULT_ENCRYPT_TAG); + result.encrypt.which_response = + INHERITANCE_ENCRYPT_DATA_WITH_PIN_RESPONSE_RESULT_TAG; + if (!inheritance_get_query(query, INHERITANCE_QUERY_ENCRYPT_TAG) || + !check_which_request( + query, INHERITANCE_ENCRYPT_DATA_WITH_PIN_REQUEST_INITIATE_TAG)) { + return false; + } + + inheritance_encrypt_data_with_pin_result_response_t dummy = {0}; + + dummy.encrypted_data.size = 32; + + memcpy(&result.encrypt.result, + &dummy, + sizeof(inheritance_encrypt_data_with_pin_result_response_t)); + + inheritance_send_result(&result); + return true; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +void inheritance_encrypt_data(inheritance_query_t *query) { + inheritance_encryption_context = (inheritance_encryption_context_t *)malloc( + sizeof(inheritance_encryption_context_t)); + memzero(inheritance_encryption_context, + sizeof(inheritance_encryption_context_t)); + + // TODO: add actual encryption and decrypiton function + if (inheritance_handle_initiate_query(query) && + inheritance_get_user_verification() && send_encrypted_data(query)) { + delay_scr_init(ui_text_check_cysync, DELAY_TIME); + } + + free(inheritance_encryption_context); + inheritance_encryption_context = NULL; +} diff --git a/apps/inheritance_app/inheritance_main.c b/apps/inheritance_app/inheritance_main.c index 122ac2154..04835ea94 100644 --- a/apps/inheritance_app/inheritance_main.c +++ b/apps/inheritance_app/inheritance_main.c @@ -130,7 +130,7 @@ void inheritance_main(usb_event_t usb_evt, const void *app_config) { break; } case INHERITANCE_QUERY_ENCRYPT_TAG: { - // TODO: Add Encryption functionality + inheritance_encrypt_data(&query); break; } case INHERITANCE_QUERY_DECRYPT_TAG: { diff --git a/apps/inheritance_app/inheritance_priv.h b/apps/inheritance_app/inheritance_priv.h index b96807a4d..39a1b3907 100644 --- a/apps/inheritance_app/inheritance_priv.h +++ b/apps/inheritance_app/inheritance_priv.h @@ -22,4 +22,16 @@ * TYPEDEFS *****************************************************************************/ +typedef struct { +} inheritance_encryption_context_t; + +/** + * @brief Handler for inheritance messages encrytion flow. + * @details The expected request type is INHERITANCE_QUERY_ENCRYPT_TAG. + * The function controls the complete data exchange with host, user prompts and + * confirmations for encrytion of inheritance messages. + * + * @param query Reference to the decoded query struct from the host app + */ +void inheritance_encrypt_data(inheritance_query_t *query); #endif /* INHERITANCE_PRIV_H */ diff --git a/common/proto-options/inheritance/encrypt_data_with_pin.options b/common/proto-options/inheritance/encrypt_data_with_pin.options new file mode 100644 index 000000000..ff24054c9 --- /dev/null +++ b/common/proto-options/inheritance/encrypt_data_with_pin.options @@ -0,0 +1,3 @@ +inheritance.EncryptDataWithPinResultResponse.encrypted_data type:FT_STATIC max_size:6000 fixed_length:false +inheritance.EncryptDataWithPinInitiateRequest.plain_data type:FT_STATIC max_count:5 fixed_length:false +inheritance.EncryptDataWithPinInitiateRequest.wallet_id type:FT_STATIC max_size:32 fixed_length:true From 9ce3422773836eb193916a1969cb430c1bd732dd Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Tue, 27 Aug 2024 07:25:02 -0700 Subject: [PATCH 5/8] feat: Added encryption logic and other functions --- apps/inheritance_app/inheritance_context.h | 3 + .../inheritance_encrypt_data.c | 237 ++++++++++++--- apps/inheritance_app/inheritance_priv.h | 8 + common/core/core_session.c | 52 ++++ common/core/core_session.h | 27 +- .../proto-options/inheritance/common.options | 1 + src/card_operations/card_fetch_data.h | 10 + src/card_operations/card_fetch_encrypt_data.c | 205 +++++++++++++ src/wallet/verify_pin_flow.c | 282 ++++++++++++++++++ src/wallet/verify_pin_flow.h | 57 ++++ 10 files changed, 836 insertions(+), 46 deletions(-) create mode 100644 common/proto-options/inheritance/common.options create mode 100644 src/card_operations/card_fetch_encrypt_data.c create mode 100644 src/wallet/verify_pin_flow.c create mode 100644 src/wallet/verify_pin_flow.h diff --git a/apps/inheritance_app/inheritance_context.h b/apps/inheritance_app/inheritance_context.h index 03b1b4087..4ad2c35ea 100644 --- a/apps/inheritance_app/inheritance_context.h +++ b/apps/inheritance_app/inheritance_context.h @@ -19,6 +19,9 @@ * MACROS AND DEFINES *****************************************************************************/ +#define INHERITANCE_MESSAGES_MAX_COUNT 5 +#define INHERITANCE_PACKET_MAX_SIZE 6000 + /***************************************************************************** * TYPEDEFS *****************************************************************************/ diff --git a/apps/inheritance_app/inheritance_encrypt_data.c b/apps/inheritance_app/inheritance_encrypt_data.c index c88e6dce4..1c0b6229b 100644 --- a/apps/inheritance_app/inheritance_encrypt_data.c +++ b/apps/inheritance_app/inheritance_encrypt_data.c @@ -1,7 +1,6 @@ /** - * @file inheritance_encrypt_data.c * @author Cypherock X1 Team - * @brief Inheritance message encrytion login + * @brief Data encryption flow for inheritance * @copyright Copyright (c) 2023 HODL TECH PTE LTD *
You may obtain a copy of license at https://mitcc.org/ @@ -62,14 +61,20 @@ #include +#include "card_fetch_data.h" +#include "core_session.h" +#include "inheritance/common.pb.h" #include "inheritance/core.pb.h" #include "inheritance/encrypt_data_with_pin.pb.h" #include "inheritance_api.h" #include "inheritance_priv.h" -#include "reconstruct_wallet_flow.h" +#include "pb.h" #include "status_api.h" #include "ui_core_confirm.h" -#include "ui_screens.h" +#include "ui_delay.h" +#include "ui_input_text.h" +#include "verify_pin_flow.h" +#include "wallet.h" #include "wallet_list.h" /***************************************************************************** @@ -135,7 +140,7 @@ static bool validate_request_data( * @retval true If all the validation and user confirmation was positive * @retval false If any of the validation or user confirmation was negative */ -STATIC bool inheritance_handle_initiate_query(const inheritance_query_t *query); +STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query); /** * @brief Aggregates user consent for the encrytion info @@ -146,7 +151,7 @@ STATIC bool inheritance_handle_initiate_query(const inheritance_query_t *query); * @retval true If user confirmed the messages displayed * @retval false Immediate return if any of the messages are disapproved */ -STATIC bool inheritance_get_user_verification(); +STATIC bool inheritance_get_user_verification(void); /** * @brief Sends the encrypted data to the host @@ -164,7 +169,7 @@ static bool send_encrypted_data(inheritance_query_t *query); * STATIC VARIABLES *****************************************************************************/ -STATIC inheritance_encryption_context_t *inheritance_encryption_context = NULL; +STATIC inheritance_encryption_context_t *context = NULL; /***************************************************************************** * GLOBAL VARIABLES @@ -188,14 +193,29 @@ static bool check_which_request(const inheritance_query_t *query, static bool validate_request_data( const inheritance_encrypt_data_with_pin_request_t *request) { bool status = true; + Wallet wallet = {0}; - // TODO: check the current request and session validity here + do { + if (!get_wallet_data_by_id( + request->initiate.wallet_id, &wallet, inheritance_send_error)) { + status = false; + break; + } + + if (!WALLET_IS_PIN_SET(wallet.wallet_info)) { + status = false; + + inheritance_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_REQUEST); + break; + } + + } while (0); return status; } -STATIC bool inheritance_handle_initiate_query( - const inheritance_query_t *query) { +STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query) { char wallet_name[NAME_SIZE] = ""; char msg[100] = ""; @@ -208,34 +228,180 @@ STATIC bool inheritance_handle_initiate_query( return false; } - snprintf(msg, sizeof(msg), "Test %s", wallet_name); // TODO: update message - // - // Take user consent to sign the transaction for the wallet + snprintf(msg, + sizeof(msg), + "Proceed to encrypt data for %s", + wallet_name); // TODO: update message + if (!core_confirmation(msg, inheritance_send_error)) { return false; } set_app_flow_status(INHERITANCE_ENCRYPT_DATA_STATUS_USER_CONFIRMED); - // TODO: copy data to local context; + context->request_pointer = &(query->encrypt.initiate); - // show processing screen for a minimum duration (additional time will add due - // to actual processing) - delay_scr_init(ui_text_processing, DELAY_SHORT); return true; } -STATIC bool inheritance_get_user_verification() { - // TODO: Iterate through messages that needs to be verified - if (!core_scroll_page( - "Demo title", "message preview", inheritance_send_error)) { - return false; +STATIC bool inheritance_get_user_verification(void) { + for (int i = 0; i < context->request_pointer->plain_data_count; i++) { + const inheritance_plain_data_t *data = + &context->request_pointer->plain_data[i]; + + if (data->has_is_verified_on_device && data->is_verified_on_device) { + if (!core_scroll_page("Verify message", + (const char *)&data->message.bytes, + inheritance_send_error)) { + return false; + } + } } set_app_flow_status(INHERITANCE_ENCRYPT_DATA_STATUS_MESSAGE_VERIFIED); return true; } +static bool inheritance_verify_pin(void) { + return verify_pin(context->request_pointer->wallet_id, + context->pin_value, + inheritance_send_error); +} + +static void inheritance_fill_tlv(uint8_t *destination, + uint16_t *starting_index, + uint8_t tag, + uint16_t length, + const uint8_t *value) { + destination[(*starting_index)++] = tag; + destination[(*starting_index)++] = length; + destination[(*starting_index)++] = (length >> 8); + + memcpy(destination + *starting_index, value, length); + *starting_index = *starting_index + length; +} + +static bool serialize_message_data(void) { + if (context->request_pointer->plain_data_count >= + INHERITANCE_MESSAGES_MAX_COUNT) { + // TODO: Throw invalid message count error; + return false; + } + pb_size_t index = 0; + uint16_t zero_index = 0; + for (index = 0; index < context->request_pointer->plain_data_count; index++) { + uint16_t length = context->request_pointer->plain_data[index].message.size; + if (length > (PLAIN_DATA_SIZE - 3)) { + length = PLAIN_DATA_SIZE - 3; + } + zero_index = 0; + inheritance_fill_tlv( + context->data[index].plain_data, + &zero_index, + 0x00, ///< Default tag for every message + length, + context->request_pointer->plain_data[index].message.bytes); + context->data[index].plain_data_size = length; + } + zero_index = 0; + inheritance_fill_tlv(context->data[index].plain_data, + &zero_index, + 0x01, ///< Special tag for private messages + MAX_PIN_SIZE, + context->pin_value); + context->data[index].plain_data_size = MAX_PIN_SIZE; + + context->data_count = context->request_pointer->plain_data_count + 1; + memzero(context->pin_value, sizeof(context->pin_value)); + return true; +} + +static bool encrypt_message_data(void) { + card_error_type_e status = card_fetch_encrypt_data( + context->request_pointer->wallet_id, context->data, context->data_count); + + if (status != CARD_OPERATION_SUCCESS) { + // TODO: throw encryption failed error + return false; + } + set_app_flow_status(INHERITANCE_AUTH_WALLET_STATUS_CARD_TAPPED); + return true; +} + +static bool serialize_packet(void) { + context->packet_size = 0; + context->packet[context->packet_size++] = context->data_count; + pb_size_t index = 0; + + for (index = 0; index < context->data_count - 1; index++) { + inheritance_fill_tlv(context->packet, + &context->packet_size, + 0x00, + context->data[index].encrypted_data_size, + context->data[index].encrypted_data); + } + + // The last encrypted message is the PIN + inheritance_fill_tlv(context->packet, + &context->packet_size, + 0x50, + context->data[index].encrypted_data_size, + context->data[index].encrypted_data); + + return true; +} + +static bool encrypt_packet(void) { + if (SESSION_ENCRYPT_PACKET_SUCCESS != + session_aes_encrypt(context->packet, &context->packet_size)) { + return false; + } + + return true; +} + +static bool encrypt_data(void) { + bool status = true; + + do { + if (!inheritance_verify_pin()) { + // TODO: Throw user rejceted + core_confirmation("pin verification failed", inheritance_send_error); + status = false; + break; + } + if (!serialize_message_data()) { + // TODO: Throw serialization failed + core_confirmation("serialization failed", inheritance_send_error); + status = false; + break; + } + + if (!encrypt_message_data()) { + // TODO: Throw encryption failed + core_confirmation("encryption failed", inheritance_send_error); + status = false; + break; + } + + if (!serialize_packet()) { + // TODO: Throw packet serialization error + core_confirmation("packet serialization failed", inheritance_send_error); + status = false; + break; + } + + if (!encrypt_packet()) { + // TODO: Throw packet encryption error + core_confirmation("packet encryption failed", inheritance_send_error); + status = false; + break; + } + } while (0); + + return status; +} + static bool send_encrypted_data(inheritance_query_t *query) { inheritance_result_t result = init_inheritance_result(INHERITANCE_RESULT_ENCRYPT_TAG); @@ -247,13 +413,10 @@ static bool send_encrypted_data(inheritance_query_t *query) { return false; } - inheritance_encrypt_data_with_pin_result_response_t dummy = {0}; - - dummy.encrypted_data.size = 32; - - memcpy(&result.encrypt.result, - &dummy, - sizeof(inheritance_encrypt_data_with_pin_result_response_t)); + result.encrypt.result.encrypted_data.size = context->packet_size; + memcpy(&result.encrypt.result.encrypted_data.bytes, + context->packet, + context->packet_size); inheritance_send_result(&result); return true; @@ -264,17 +427,19 @@ static bool send_encrypted_data(inheritance_query_t *query) { *****************************************************************************/ void inheritance_encrypt_data(inheritance_query_t *query) { - inheritance_encryption_context = (inheritance_encryption_context_t *)malloc( + context = (inheritance_encryption_context_t *)malloc( sizeof(inheritance_encryption_context_t)); - memzero(inheritance_encryption_context, - sizeof(inheritance_encryption_context_t)); + ASSERT(context != NULL); + memzero(context, sizeof(inheritance_encryption_context_t)); - // TODO: add actual encryption and decrypiton function if (inheritance_handle_initiate_query(query) && - inheritance_get_user_verification() && send_encrypted_data(query)) { + inheritance_get_user_verification() && encrypt_data() && + send_encrypted_data(query)) { delay_scr_init(ui_text_check_cysync, DELAY_TIME); } - free(inheritance_encryption_context); - inheritance_encryption_context = NULL; + core_confirmation("flow over", inheritance_send_error); + + free(context); + context = NULL; } diff --git a/apps/inheritance_app/inheritance_priv.h b/apps/inheritance_app/inheritance_priv.h index 39a1b3907..09952734d 100644 --- a/apps/inheritance_app/inheritance_priv.h +++ b/apps/inheritance_app/inheritance_priv.h @@ -16,13 +16,21 @@ #include #include +#include "card_fetch_data.h" #include "inheritance_context.h" +#include "ui_input_text.h" /***************************************************************************** * TYPEDEFS *****************************************************************************/ typedef struct { + secure_data_t data[INHERITANCE_MESSAGES_MAX_COUNT]; + uint8_t data_count; + inheritance_encrypt_data_with_pin_initiate_request_t *request_pointer; + uint8_t pin_value[MAX_PIN_SIZE]; + uint8_t packet[INHERITANCE_PACKET_MAX_SIZE]; + uint16_t packet_size; } inheritance_encryption_context_t; /** diff --git a/common/core/core_session.c b/common/core/core_session.c index 689bd4b84..683dc113a 100644 --- a/common/core/core_session.c +++ b/common/core/core_session.c @@ -63,6 +63,8 @@ *****************************************************************************/ #include "core_session.h" +#include + #include "bignum.h" #include "core.pb.h" #include "inheritance_main.h" @@ -465,3 +467,53 @@ void core_session_parse_start_message(const core_msg_t *core_msg) { break; } } + +session_error_type_e session_aes_encrypt(uint8_t *InOut_data, uint16_t *len) { + ASSERT(InOut_data != NULL); + ASSERT(len != NULL); + + uint16_t size = *len; + uint8_t payload[size]; + + memcpy(payload, InOut_data, size); + memzero(InOut_data, size); + + uint8_t last_block[AES_BLOCK_SIZE] = {0}; + uint8_t remainder = size % AES_BLOCK_SIZE; + + // Round down to last whole block + size -= remainder; + // Copy old last block + memcpy(last_block, payload + size, remainder); + // Pad new last block with number of missing bytes + memset(last_block + remainder, + AES_BLOCK_SIZE - remainder, + AES_BLOCK_SIZE - remainder); + + // the IV gets mutated, so we make a copy not to touch the original + uint8_t iv[AES_BLOCK_SIZE] = {0}; + memcpy(iv, session.session_iv, AES_BLOCK_SIZE); + + aes_encrypt_ctx ctx = {0}; + + if (aes_encrypt_key256(session.session_key, &ctx) != EXIT_SUCCESS) { + return SESSION_ENCRYPT_PACKET_KEY_ERR; + } + + if (aes_cbc_encrypt(payload, InOut_data, size, iv, &ctx) != EXIT_SUCCESS) { + return SESSION_ENCRYPT_PACKET_ERR; + } + + if (aes_cbc_encrypt( + last_block, InOut_data + size, sizeof(last_block), iv, &ctx) != + EXIT_SUCCESS) { + return SESSION_ENCRYPT_PACKET_ERR; + } + + size += sizeof(last_block); + *len = size; + + memzero(&ctx, sizeof(ctx)); + + return SESSION_ENCRYPT_PACKET_SUCCESS; +} diff --git a/common/core/core_session.h b/common/core/core_session.h index fdc2b97aa..38ef077e4 100644 --- a/common/core/core_session.h +++ b/common/core/core_session.h @@ -38,14 +38,23 @@ #define SESSION_IV_SIZE 16 #define SESSION_KEY_SIZE 32 -#define SESSION_MSG_MAX 5 -#define SESSION_PACKET_BUFFER 400 -#define SESSION_PACKET_SIZE \ - (ENCRYPTED_DATA_SIZE * SESSION_MSG_MAX) + \ - SESSION_PACKET_BUFFER // (112 * 10) * 5 + 400 = 6000 - extern const uint32_t session_key_rotation[2]; +typedef enum { + SESSION_OK = 0, + SESSION_ERR_INVALID, + SESSION_ERR_DEVICE_KEY, + SESSION_ERR_SERVER_KEY, + SESSION_ERR_ENCRYPT, + SESSION_ERR_DECRYPT, + SESSION_ENCRYPT_PACKET_SUCCESS, + SESSION_DECRYPT_PACKET_SUCCESS, + SESSION_ENCRYPT_PACKET_KEY_ERR, + SESSION_ENCRYPT_PACKET_ERR, + SESSION_DECRYPT_PACKET_KEY_ERR, + SESSION_DECRYPT_PACKET_ERR +} session_error_type_e; + /** * @brief Stores the session information */ @@ -63,14 +72,10 @@ typedef struct { uint8_t server_signature[SESSION_SERVER_SIGNATURE_SIZE]; const char wallet_name[NAME_SIZE]; - secure_data_t session_msgs[SESSION_MSG_MAX]; - uint8_t msg_count; uint8_t session_iv[SESSION_IV_SIZE]; uint8_t session_key[SESSION_PRIV_KEY_SIZE]; - uint8_t packet[SESSION_PACKET_SIZE]; - } session_config_t; #pragma pack(pop) @@ -97,4 +102,6 @@ void core_session_clear_metadata(); */ void core_session_parse_start_message(const core_msg_t *core_msg); +session_error_type_e session_aes_encrypt(uint8_t *InOut_data, uint16_t *len); + #endif // CORE_SESSION diff --git a/common/proto-options/inheritance/common.options b/common/proto-options/inheritance/common.options new file mode 100644 index 000000000..29b800a77 --- /dev/null +++ b/common/proto-options/inheritance/common.options @@ -0,0 +1 @@ +inheritance.PlainData.message type:FT_STATIC max_size:1000 fixed_length:false diff --git a/src/card_operations/card_fetch_data.h b/src/card_operations/card_fetch_data.h index 5847431ab..d34be0c5c 100644 --- a/src/card_operations/card_fetch_data.h +++ b/src/card_operations/card_fetch_data.h @@ -50,4 +50,14 @@ typedef struct { * GLOBAL FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Encrypts given secure_data_t with card and stores the encrypted data + * in the same struct + * + * @return A card_error_type_e value representing the result of the operation. + */ +card_error_type_e card_fetch_encrypt_data(uint8_t *wallet_id, + secure_data_t *msgs, + size_t msg_count); + #endif diff --git a/src/card_operations/card_fetch_encrypt_data.c b/src/card_operations/card_fetch_encrypt_data.c new file mode 100644 index 000000000..1bc14e033 --- /dev/null +++ b/src/card_operations/card_fetch_encrypt_data.c @@ -0,0 +1,205 @@ +/** + * @author Cypherock X1 Team + * @brief Card encryption data implementation + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "buzzer.h" +#include "card_fetch_data.h" +#include "card_internal.h" +#include "card_utils.h" +#include "nfc.h" +#include "pow_utilities.h" +#include "ui_instruction.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +card_error_type_e card_fetch_encrypt_data(uint8_t *wallet_id, + secure_data_t *msgs, + size_t msg_count) { + card_error_type_e result = CARD_OPERATION_DEFAULT_INVALID; + card_operation_data_t card_data = {0}; + + char wallet_name[NAME_SIZE] = ""; +#if USE_SIMULATOR == 0 + // Confirm NULL use for rejection_cb + ASSERT(get_wallet_name_by_id(wallet_id, (uint8_t *)wallet_name, NULL)); +#endif + + instruction_scr_init(ui_text_place_card_below, ui_text_tap_1_2_cards); + + card_data.nfc_data.retries = 5; + card_data.nfc_data.init_session_keys = true; + + uint8_t plain_data_buffer[PLAIN_DATA_BUFFER_SIZE]; + uint8_t encrypted_data_buffer[ENCRYPTED_DATA_BUFFER_SIZE]; + uint16_t encrypted_data_buffer_size = 0; + uint16_t plain_data_buffer_size = 0; + size_t index = 0; + + while (1) { + card_data.nfc_data.acceptable_cards = ACCEPTABLE_CARDS_ALL; +#if USE_SIMULATOR == 0 + memcpy(card_data.nfc_data.family_id, get_family_id(), FAMILY_ID_SIZE); + result = card_initialize_applet(&card_data); +#endif + + if (CARD_OPERATION_SUCCESS == card_data.error_type) { + for (int i = 0; i < msg_count; i++) { + memzero(plain_data_buffer, PLAIN_DATA_BUFFER_SIZE); + memzero(encrypted_data_buffer, ENCRYPTED_DATA_BUFFER_SIZE); + + index = 0; + while (index < msgs[i].plain_data_size) { + plain_data_buffer_size = + (msgs[i].plain_data_size - index) < PLAIN_DATA_BUFFER_SIZE + ? (msgs[i].plain_data_size - index) + : PLAIN_DATA_BUFFER_SIZE; + memcpy(plain_data_buffer, + msgs[i].plain_data + index, + plain_data_buffer_size); + +#if USE_SIMULATOR == 0 + card_data.nfc_data.status = + nfc_encrypt_data((const uint8_t *)wallet_name, + plain_data_buffer, + plain_data_buffer_size, + encrypted_data_buffer, + &encrypted_data_buffer_size); +#else + card_data.nfc_data.status = SW_NO_ERROR; + result = CARD_OPERATION_SUCCESS; +#endif + if (card_data.nfc_data.status == SW_NO_ERROR) { + msgs[i].encrypted_data[msgs[i].encrypted_data_size] = + encrypted_data_buffer_size; + msgs[i].encrypted_data_size += 1; + + memcpy(msgs[i].encrypted_data + msgs[i].encrypted_data_size, + encrypted_data_buffer, + encrypted_data_buffer_size); + msgs[i].encrypted_data_size += encrypted_data_buffer_size; + } else { + card_handle_errors(&card_data); + } + index += plain_data_buffer_size; + } + } + } + + if (card_data.nfc_data.status == SW_NO_ERROR) { + buzzer_start(BUZZER_DURATION); + break; + } + + card_handle_errors(&card_data); + + if (CARD_OPERATION_CARD_REMOVED == card_data.error_type || + CARD_OPERATION_RETAP_BY_USER_REQUIRED == card_data.error_type) { + const char *error_msg = card_data.error_message; + if (CARD_OPERATION_SUCCESS == indicate_card_error(error_msg)) { + // Re-render the instruction screen + instruction_scr_init(ui_text_place_card_below, ui_text_tap_1_2_cards); + continue; + } + } + + result = handle_wallet_errors(&card_data, &wallet); + if (CARD_OPERATION_SUCCESS != result) { + break; + } + + // If control reached here, it is an unrecoverable error, so break + result = card_data.error_type; + break; + } +#if USE_SIMULATOR == 0 + nfc_deselect_card(); +#endif + return result; +} diff --git a/src/wallet/verify_pin_flow.c b/src/wallet/verify_pin_flow.c new file mode 100644 index 000000000..ee4211e8d --- /dev/null +++ b/src/wallet/verify_pin_flow.c @@ -0,0 +1,282 @@ +/** + * @author Cypherock X1 Team + * @brief Source file containing logic for pin verification using X1 card + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include "verify_pin_flow.h" + +#include +#include + +#include "bip39.h" +#include "card_flow_reconstruct_wallet.h" +#include "common_error.h" +#include "constant_texts.h" +#include "controller_main.h" +#include "core_error.h" +#include "sha2.h" +#include "shamir_wrapper.h" +#include "status_api.h" +#include "ui_input_text.h" +#include "ui_screens.h" +#include "ui_state_machine.h" +#include "wallet_list.h" +#include "wallet_utilities.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ +// TODO: Add these pointers in a common header file +extern char *ALPHABET; +extern char *ALPHA_NUMERIC; +extern char *NUMBERS; +extern char *PASSPHRASE; + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ +typedef enum { + PIN_INPUT, + TAP_CARD_FLOW, + COMPLETED, /** You may obtain a copy of license at https://mitcc.org/ + */ +#ifndef VERIFY_PIN_FLOW_H +#define VERIFY_PIN_FLOW_H + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ +#include +#include + +#include "ui_input_text.h" +#include "wallet.h" +#include "wallet_list.h" + +/***************************************************************************** + * MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * EXPORTED VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * GLOBAL FUNCTION PROTOTYPES + *****************************************************************************/ +/** + * @brief This API executes the wallet pin verification flow on the device + * and returns the pin. + * @details This function takes user inputs based wallet configuration + * corresponding to wallet with wallet_id, verifies the pin against any 1 X1 + * card, and returns it. The function informs the host in case of any early exit + * or card abort errors + * + * @param wallet_id The wallet_id of the wallet which needs to be verify the pin + * @param pin_out Pointer to buffer where the pin will be copied after + * verification + * @param reject_cb Callback to execute if there is any rejection during PIN + * input occurs, or a card abort error occurs + * @return true If the verification flow completed successfully and buffer + * pointed by pin_out is filled with the seed + * @return false If the verification flow could not be completed + */ +bool verify_pin(const uint8_t *wallet_id, + uint8_t pin_out[MAX_PIN_SIZE], + rejection_cb *reject_cb); + +#endif /* VERIFY_PIN_FLOW_H */ From 06c3efcf326ee0473296393e666519d5aab20c0b Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Tue, 27 Aug 2024 09:05:03 -0700 Subject: [PATCH 6/8] chore: Added docs and ui texts --- apps/inheritance_app/inheritance_context.h | 4 - .../inheritance_encrypt_data.c | 184 +++++++++++------- common/core/core_session.h | 12 ++ src/constant_texts.c | 3 + src/constant_texts.h | 2 + 5 files changed, 135 insertions(+), 70 deletions(-) diff --git a/apps/inheritance_app/inheritance_context.h b/apps/inheritance_app/inheritance_context.h index 4ad2c35ea..8c443664e 100644 --- a/apps/inheritance_app/inheritance_context.h +++ b/apps/inheritance_app/inheritance_context.h @@ -26,10 +26,6 @@ * TYPEDEFS *****************************************************************************/ -// TODO: Populate structure for INHERITANCE -typedef struct { -} inheritance_config_t; - /***************************************************************************** * EXPORTED VARIABLES *****************************************************************************/ diff --git a/apps/inheritance_app/inheritance_encrypt_data.c b/apps/inheritance_app/inheritance_encrypt_data.c index 1c0b6229b..2819eae81 100644 --- a/apps/inheritance_app/inheritance_encrypt_data.c +++ b/apps/inheritance_app/inheritance_encrypt_data.c @@ -62,6 +62,7 @@ #include #include "card_fetch_data.h" +#include "constant_texts.h" #include "core_session.h" #include "inheritance/common.pb.h" #include "inheritance/core.pb.h" @@ -94,74 +95,133 @@ *****************************************************************************/ /** - * @brief Checks if the provided query contains expected request. - * @details The function performs the check on the request type and if the check - * fails, then it will send an error to the host inheritance app and return - * false. - * - * @param query Reference to an instance of inheritance_query_t containing query - * received from host app - * @param which_request The expected request type enum - * - * @return bool Indicating if the check succeeded or failed - * @retval true If the query contains the expected request - * @retval false If the query does not contain the expected request + * @brief Checks the type of request in the inheritance query. + * + * This function verifies whether the specified request type matches the one + * contained in the query. If not, it sends an error message and returns false. + * + * @param query Pointer to the inheritance query. + * @param which_request The expected request type. + * @return True if the request type matches; false otherwise. */ static bool check_which_request(const inheritance_query_t *query, pb_size_t which_request); /** - * @brief Validates the derivation path received in the request from host - * @details The function validates the provided account derivation path in the - * request. If invalid path is detected, the function will send an error to the - * host and return false. - * - * @param request Reference to an instance of inheritance_encrypt_data_request_t - * @return bool Indicating if the verification passed or failed - * @retval true If all the derivation path entries are valid - * @retval false If any of the derivation path entries are invalid + * @brief Validates the request data for encrypting data with a PIN. + * + * This function ensures that the provided request data is valid. It checks if + * the wallet associated with the request has a set PIN. If not, it sends an + * error message and returns false. + * + * @param request Pointer to the encrypt data request. + * @return True if the request data is valid; false otherwise. */ static bool validate_request_data( const inheritance_encrypt_data_with_pin_request_t *request); /** - * @brief Takes already received and decoded query for the user confirmation. - * @details The function will verify if the query contains the - * INHERITANCE_ENCRYPT_DATA type of request. Additionally, the wallet-id is - * validated for sanity and the derivation path for the account is also - * validated. After the validations, user is prompted about the action for - * confirmation. The function returns true indicating all the validation and - * user confirmation was a success. The function also duplicates the data from - * query into the inheritance_txn_context for further processing. - * - * @param query Constant reference to the decoded query received from the host - * - * @return bool Indicating if the function actions succeeded or failed - * @retval true If all the validation and user confirmation was positive - * @retval false If any of the validation or user confirmation was negative + * @brief Handles the initiate query for encrypting data with a PIN. + * + * This function processes the initiate query, including verifying the request + * type, validating the request data, and obtaining the wallet name. It prompts + * the user for confirmation and sets the application flow status accordingly. + * + * @param query Pointer to the inheritance query. + * @return True if the query is successfully handled; false otherwise. + */ +STATIC bool inheritance_encryption_handle_inititate_query( + inheritance_query_t *query); + +/** + * @brief Obtains user verification for specific plain data messages. + * + * This function prompts the user to verify specific plain data messages. If the + * messages are verified, it updates the application flow status accordingly. + * + * @return True if user verification is successful; false otherwise. + */ +STATIC bool inheritance_encryption_get_user_verification(void); + +/** + * @brief Verifies the PIN associated with the request. + * + * This function verifies the PIN for the specified wallet ID. It communicates + * with the PIN verification mechanism and returns the verification status. + * + * @return True if the PIN is verified; false otherwise. */ -STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query); +static bool inheritance_verify_pin(void); /** - * @brief Aggregates user consent for the encrytion info - * @details The function displays the required messages for user to very + * @brief Fills a TLV (Tag-Length-Value) structure. * + * This function constructs a TLV structure by populating the destination buffer + * with the specified tag, length, and value. It updates the starting index + * accordingly. * - * @return bool Indicating if the user confirmed the messages - * @retval true If user confirmed the messages displayed - * @retval false Immediate return if any of the messages are disapproved + * @param destination Pointer to the destination buffer. + * @param starting_index Pointer to the starting index within the buffer. + * @param tag The TLV tag. + * @param length The length of the value. + * @param value Pointer to the value data. */ -STATIC bool inheritance_get_user_verification(void); +static void inheritance_fill_tlv(uint8_t *destination, + uint16_t *starting_index, + uint8_t tag, + uint16_t length, + const uint8_t *value); /** - * @brief Sends the encrypted data to the host - * @details The function encrypts the data and sends it to the host - * - * @param query Reference to an instance of inheritance_query_t to store - * transient request from the host - * @return bool Indicating if the encrypted data is sent to the host - * @retval true If the encrypted data was sent to host successfully - * @retval false If the host responded with unknown/wrong query + * @brief Serializes the message data for encryption. + * + * This function prepares the plain data messages for encryption. It constructs + * TLV (Tag-Length-Value) structures for each message, including a special tag + * for the PIN. The resulting data is stored in the context data array. + * + * @return True if serialization is successful; false otherwise. + */ +static bool serialize_message_data(void); + +/** + * @brief Encrypts the prepared message data. + * + * This function fetches and encrypts the data associated with the wallet ID. + * If successful, it updates the application flow status accordingly. + * + * @return True if encryption is successful; false otherwise. + */ +static bool encrypt_message_data(void); + +/** + * @brief Serializes the encrypted data into a packet. + * + * This function constructs a packet containing the serialized TLV structures + * for the encrypted data messages. The last entry in the packet corresponds + * to the encrypted PIN. + * + * @return True if serialization is successful; false otherwise. + */ +static bool serialize_packet(void); + +/** + * @brief Encrypts the entire packet. + * + * This function performs AES encryption on the packet data. If successful, + * it returns true; otherwise, false. + * + * @return True if packet encryption is successful; false otherwise. + */ +static bool encrypt_packet(void); + +/** + * @brief Encrypts the data and sends the result. + * + * This function orchestrates the entire process of verifying the PIN, + * serializing and encrypting the data, and sending the result back to the user. + * + * @param query Pointer to the inheritance query. + * @return True if the process completes successfully; false otherwise. */ static bool send_encrypted_data(inheritance_query_t *query); @@ -215,7 +275,8 @@ static bool validate_request_data( return status; } -STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query) { +STATIC bool inheritance_encryption_handle_inititate_query( + inheritance_query_t *query) { char wallet_name[NAME_SIZE] = ""; char msg[100] = ""; @@ -228,10 +289,8 @@ STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query) { return false; } - snprintf(msg, - sizeof(msg), - "Proceed to encrypt data for %s", - wallet_name); // TODO: update message + snprintf( + msg, sizeof(msg), ui_text_inheritance_flow_confirmation, wallet_name); if (!core_confirmation(msg, inheritance_send_error)) { return false; @@ -244,13 +303,13 @@ STATIC bool inheritance_handle_initiate_query(inheritance_query_t *query) { return true; } -STATIC bool inheritance_get_user_verification(void) { +STATIC bool inheritance_encryption_get_user_verification(void) { for (int i = 0; i < context->request_pointer->plain_data_count; i++) { const inheritance_plain_data_t *data = &context->request_pointer->plain_data[i]; if (data->has_is_verified_on_device && data->is_verified_on_device) { - if (!core_scroll_page("Verify message", + if (!core_scroll_page(UI_TEXT_VERIFY_MESSAGE, (const char *)&data->message.bytes, inheritance_send_error)) { return false; @@ -366,34 +425,29 @@ static bool encrypt_data(void) { do { if (!inheritance_verify_pin()) { // TODO: Throw user rejceted - core_confirmation("pin verification failed", inheritance_send_error); status = false; break; } if (!serialize_message_data()) { // TODO: Throw serialization failed - core_confirmation("serialization failed", inheritance_send_error); status = false; break; } if (!encrypt_message_data()) { // TODO: Throw encryption failed - core_confirmation("encryption failed", inheritance_send_error); status = false; break; } if (!serialize_packet()) { // TODO: Throw packet serialization error - core_confirmation("packet serialization failed", inheritance_send_error); status = false; break; } if (!encrypt_packet()) { // TODO: Throw packet encryption error - core_confirmation("packet encryption failed", inheritance_send_error); status = false; break; } @@ -432,14 +486,12 @@ void inheritance_encrypt_data(inheritance_query_t *query) { ASSERT(context != NULL); memzero(context, sizeof(inheritance_encryption_context_t)); - if (inheritance_handle_initiate_query(query) && - inheritance_get_user_verification() && encrypt_data() && + if (inheritance_encryption_handle_inititate_query(query) && + inheritance_encryption_get_user_verification() && encrypt_data() && send_encrypted_data(query)) { delay_scr_init(ui_text_check_cysync, DELAY_TIME); } - core_confirmation("flow over", inheritance_send_error); - free(context); context = NULL; } diff --git a/common/core/core_session.h b/common/core/core_session.h index 38ef077e4..cfb9581cd 100644 --- a/common/core/core_session.h +++ b/common/core/core_session.h @@ -102,6 +102,18 @@ void core_session_clear_metadata(); */ void core_session_parse_start_message(const core_msg_t *core_msg); +/** + * @brief Encrypts data using AES-CBC mode. + * + * This function encrypts the input data using AES-CBC (Cipher Block Chaining) + * mode. It operates on the provided data in-place, updating the `InOut_data` + * buffer with the encrypted result. The function also handles padding and IV + * (Initialization Vector). + * + * @param InOut_data Pointer to the data to be encrypted (input and output). + * @param len Pointer to the length of the data (input and output). + * @return The encryption status (success or error code). + */ session_error_type_e session_aes_encrypt(uint8_t *InOut_data, uint16_t *len); #endif // CORE_SESSION diff --git a/src/constant_texts.c b/src/constant_texts.c index a88b9ac35..d067fc753 100644 --- a/src/constant_texts.c +++ b/src/constant_texts.c @@ -527,6 +527,9 @@ const char *ui_text_unreliable_cards = const char *ui_critical_card_health_migrate_data = "Card health is critical! Migrate to new set of cards"; +const char *ui_text_inheritance_flow_confirmation = + "Proceed to encrypt data for %s?"; + #ifdef ALLOW_LOG_EXPORT const char *ui_text_send_logs_prompt = "Send logs to the cySync app?"; #endif diff --git a/src/constant_texts.h b/src/constant_texts.h index af5be9c8d..c050b4db6 100644 --- a/src/constant_texts.h +++ b/src/constant_texts.h @@ -370,6 +370,8 @@ extern const char *ui_text_nfc_hardware_fault_detected; extern const char *ui_text_unreliable_cards; extern const char *ui_critical_card_health_migrate_data; +extern const char *ui_text_inheritance_flow_confirmation; + #ifdef ALLOW_LOG_EXPORT extern const char *ui_text_send_logs_prompt; #endif From 495a7801cef16772bb6697d4d92111fa45932fa6 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 28 Aug 2024 01:56:05 -0700 Subject: [PATCH 7/8] fix: Reformat code after merge from github --- apps/inheritance_app/inheritance_priv.h | 4 ++-- src/card_operations/card_fetch_data.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/inheritance_app/inheritance_priv.h b/apps/inheritance_app/inheritance_priv.h index 9400bc7f5..f781784a4 100644 --- a/apps/inheritance_app/inheritance_priv.h +++ b/apps/inheritance_app/inheritance_priv.h @@ -16,10 +16,10 @@ #include #include "card_fetch_data.h" -#include "inheritance_context.h" -#include "ui_input_text.h" #include "ed25519.h" #include "inheritance/core.pb.h" +#include "inheritance_context.h" +#include "ui_input_text.h" #include "wallet.h" /***************************************************************************** diff --git a/src/card_operations/card_fetch_data.h b/src/card_operations/card_fetch_data.h index d49db8244..d85f6e20a 100644 --- a/src/card_operations/card_fetch_data.h +++ b/src/card_operations/card_fetch_data.h @@ -13,6 +13,7 @@ *****************************************************************************/ #include + #include "card_operation_typedefs.h" #include "stdbool.h" #include "stdint.h" From 0a64cf181ea409f80885db75118f4d1128da8e42 Mon Sep 17 00:00:00 2001 From: TejasvOnly Date: Wed, 28 Aug 2024 04:48:10 -0700 Subject: [PATCH 8/8] fix: Review changes --- apps/inheritance_app/inheritance_encrypt_data.c | 12 +++++++++--- common/proto-options/inheritance/common.options | 2 +- src/constant_texts.c | 4 +++- src/constant_texts.h | 5 ++++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/inheritance_app/inheritance_encrypt_data.c b/apps/inheritance_app/inheritance_encrypt_data.c index 2819eae81..f4c984d62 100644 --- a/apps/inheritance_app/inheritance_encrypt_data.c +++ b/apps/inheritance_app/inheritance_encrypt_data.c @@ -289,8 +289,10 @@ STATIC bool inheritance_encryption_handle_inititate_query( return false; } - snprintf( - msg, sizeof(msg), ui_text_inheritance_flow_confirmation, wallet_name); + snprintf(msg, + sizeof(msg), + ui_text_inheritance_encryption_flow_confirmation, + wallet_name); if (!core_confirmation(msg, inheritance_send_error)) { return false; @@ -489,9 +491,13 @@ void inheritance_encrypt_data(inheritance_query_t *query) { if (inheritance_encryption_handle_inititate_query(query) && inheritance_encryption_get_user_verification() && encrypt_data() && send_encrypted_data(query)) { - delay_scr_init(ui_text_check_cysync, DELAY_TIME); + delay_scr_init(ui_text_inheritance_encryption_flow_success, DELAY_TIME); + } else { + delay_scr_init(ui_text_inheritance_encryption_flow_failure, DELAY_TIME); } + delay_scr_init(ui_text_check_cysync, DELAY_TIME); + memzero(context, sizeof(inheritance_encryption_context_t)); free(context); context = NULL; } diff --git a/common/proto-options/inheritance/common.options b/common/proto-options/inheritance/common.options index 29b800a77..6d898d21c 100644 --- a/common/proto-options/inheritance/common.options +++ b/common/proto-options/inheritance/common.options @@ -1 +1 @@ -inheritance.PlainData.message type:FT_STATIC max_size:1000 fixed_length:false +inheritance.PlainData.message type:FT_STATIC max_size:900 fixed_length:false diff --git a/src/constant_texts.c b/src/constant_texts.c index a76c13f74..b7253806a 100644 --- a/src/constant_texts.c +++ b/src/constant_texts.c @@ -537,8 +537,10 @@ const char *ui_text_inheritance_wallet_auth_success = const char *ui_text_inheritance_wallet_auth_fail = "Wallet\nauthentication\nfailed"; -const char *ui_text_inheritance_flow_confirmation = +const char *ui_text_inheritance_encryption_flow_confirmation = "Proceed to encrypt data for %s?"; +const char *ui_text_inheritance_encryption_flow_success = "Encryption Success"; +const char *ui_text_inheritance_encryption_flow_failure = "Encryption Failed"; #ifdef ALLOW_LOG_EXPORT const char *ui_text_send_logs_prompt = "Send logs to the cySync app?"; diff --git a/src/constant_texts.h b/src/constant_texts.h index fb150edc9..1fc0c95f4 100644 --- a/src/constant_texts.h +++ b/src/constant_texts.h @@ -376,7 +376,10 @@ extern const char *ui_critical_card_health_migrate_data; extern const char *ui_text_inheritance_wallet_authenticating; extern const char *ui_text_inheritance_wallet_auth_success; extern const char *ui_text_inheritance_wallet_auth_fail; -extern const char *ui_text_inheritance_flow_confirmation; + +extern const char *ui_text_inheritance_encryption_flow_confirmation; +extern const char *ui_text_inheritance_encryption_flow_success; +extern const char *ui_text_inheritance_encryption_flow_failure; #ifdef ALLOW_LOG_EXPORT extern const char *ui_text_send_logs_prompt;