What changed, and why it matters
This commit adds Solana CLI support to the Keystone 3 hardware wallet firmware. It introduces a new USB command that lets a connected computer request public keys for specific Solana derivation paths, and it reworks how USB responses are sent so they happen asynchronously rather than blocking the user interface. Most changes are feature additions and defensive hardening (for example, checking for NULL pointers and ensuring password checks are complete before approving key-derivation requests). There is no explicit security bug fix or vendor security disclosure in the commit message or diff.
Review the new GetDeviceUsbPubkeyService command for authorization requirements (it currently appears to require only an unlocked device via CheckURAcceptable/CheckSolPathSupport, but confirm whether user confirmation is required). Audit the async message path for lifetime and race conditions, especially the fallback to the synchronous handler when queue allocation fails. Verify that the version-string change does not break version parsing in companion software.
Security signals we found
New USB command exposes public-key export for a specific coin type and derivation path
Added NULL/empty checks before returning generated UR data and before using cached password
Moved several UR result sends from synchronous to asynchronous (task-queue based) dispatch
Added a check that a key-derivation request view is already opened before allowing hardware-call processing
No explicit security fix language or CVE reference in commit title/message
Evidence from the diff
The patch adds CMD_GET_DEVICE_USB_PUBKEY to the EAPDU protocol and a new service, service_trans_usb_pubkey.c, that parses a 4-byte big-endian coin type plus a Solana BIP44-style derivation path, validates the path via CheckSolPathSupport(), and returns the current account public key as JSON over USB. It also introduces HandleURResultViaUSBAsyncFunc() to marshal UR results through the USB task queue instead of calling the synchronous handler directly from UI code, and it tightens state checks in the key-derivation request flow (GuiKeyDerivationRequestIsUsbPasswordReady). A version-string formatting change is included but appears cosmetic.
Changed components
src/webusb_protocol/general/eapdu_protocol_parser.csrc/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.csrc/webusb_protocol/general/eapdu_services/service_resolve_ur.csrc/tasks/usb_task.csrc/ui/gui_widgets/multi/gui_key_derivation_request_widgets.csrc/ui/gui_widgets/gui_transaction_detail_widgets.cInspect captured patch +282 / −20
diff --git a/src/config/version.c b/src/config/version.c
index 71def31..303f956 100644
--- a/src/config/version.c
+++ b/src/config/version.c
@@ -26,8 +26,7 @@ void GetSoftWareVersion(char *version)
if (SOFTWARE_VERSION_BUILD % 2 == 0) {
snprintf(version, SOFTWARE_VERSION_MAX_LEN, "%s v%d.%d.%d%s", _("about_info_firmware_version_head"), SOFTWARE_VERSION_MAJOR - SOFTWARE_VERSION_MAJOR_OFFSET, SOFTWARE_VERSION_MINOR, SOFTWARE_VERSION_BUILD, SOFTWARE_VERSION_SUFFIX);
} else {
- snprintf(version, SOFTWARE_VERSION_MAX_LEN, "%s v%d.%d.%d(beta%d)%s",
- _("about_info_firmware_version_head"),
+ snprintf(version, SOFTWARE_VERSION_MAX_LEN, "v%d.%d.%d(beta%d)",
SOFTWARE_VERSION_MAJOR - SOFTWARE_VERSION_MAJOR_OFFSET,
SOFTWARE_VERSION_MINOR,
SOFTWARE_VERSION_BUILD,
diff --git a/src/msg/user_msg.c b/src/msg/user_msg.c
index 1320300..6738b3d 100644
--- a/src/msg/user_msg.c
+++ b/src/msg/user_msg.c
@@ -67,6 +67,7 @@ void UserMsgInit(void)
SubMessageID(USB_MSG_SET_STATE, g_usbQueue);
SubMessageID(USB_MSG_INIT, g_usbQueue);
SubMessageID(USB_MSG_DEINIT, g_usbQueue);
+ SubMessageID(USB_MSG_HANDLE_UR_RESULT, g_usbQueue);
SubMessageID(LOW_POWER_ENTER, g_lowPowerQueue);
SubMessageID(LOW_POWER_QUIT, g_lowPowerQueue);
diff --git a/src/msg/user_msg.h b/src/msg/user_msg.h
index e8b2ef2..5d1822b 100644
--- a/src/msg/user_msg.h
+++ b/src/msg/user_msg.h
@@ -73,6 +73,7 @@ enum {
USB_MSG_SET_STATE,
USB_MSG_INIT,
USB_MSG_DEINIT,
+ USB_MSG_HANDLE_UR_RESULT,
};
enum {
diff --git a/src/tasks/ui_display_task.c b/src/tasks/ui_display_task.c
index 9ee3a01..91b8886 100644
--- a/src/tasks/ui_display_task.c
+++ b/src/tasks/ui_display_task.c
@@ -139,6 +139,8 @@ static void UiDisplayTask(void *argument)
case UI_MSG_USB_TRANSPORT_NEXT_VIEW: {
if (GuiCheckIfTopView(&g_USBTransportView)) {
GuiEmitSignal(SIG_CLOSE_USB_TRANSPORT, NULL, 0);
+ } else if (GuiCheckIfTopView(&g_keyDerivationRequestView)) {
+ GuiEmitSignal(SIG_CLOSE_KEY_DERIVATION_REQUEST, NULL, 0);
}
}
break;
diff --git a/src/tasks/usb_task.c b/src/tasks/usb_task.c
index 54b5721..2ca59da 100644
--- a/src/tasks/usb_task.c
+++ b/src/tasks/usb_task.c
@@ -16,6 +16,7 @@
#include "gui_setup_widgets.h"
#include "low_power.h"
#include "account_manager.h"
+#include "general/eapdu_services/service_resolve_ur.h"
static void UsbTask(void *argument);
void ClearUSBRequestId(void);
@@ -89,6 +90,24 @@ static void UsbTask(void *argument)
SetUsbState(false);
}
break;
+ case USB_MSG_HANDLE_UR_RESULT: {
+ if ((rcvMsg.buffer != NULL) && (rcvMsg.length >= sizeof(USBURResultMsg_t))) {
+ USBURResultMsg_t *msg = (USBURResultMsg_t *)rcvMsg.buffer;
+ uint32_t payloadLen = rcvMsg.length - sizeof(USBURResultMsg_t);
+ uint32_t dataLen = msg->dataLen;
+
+ if (payloadLen == 0) {
+ break;
+ }
+ msg->data[payloadLen - 1] = '\0';
+ if (dataLen >= payloadLen) {
+ dataLen = payloadLen - 1;
+ }
+
+ HandleURResultViaUSBFunc(msg->data, dataLen, msg->requestID, msg->status);
+ }
+ }
+ break;
default:
break;
}
diff --git a/src/ui/gui_views/gui_views.h b/src/ui/gui_views/gui_views.h
index 98e233c..695b59c 100644
--- a/src/ui/gui_views/gui_views.h
+++ b/src/ui/gui_views/gui_views.h
@@ -121,6 +121,7 @@ typedef enum {
SIG_SETTING_CHANGE_RECOVERY_MODE_SWITCH,
SIG_USB_HARDWARE_CALL_PARSE_UR,
SIG_CLOSE_USB_TRANSPORT,
+ SIG_CLOSE_KEY_DERIVATION_REQUEST,
SIG_SETTING_BUTT,
SIG_FINGER_REGISTER_STEP_SUCCESS = SIG_SETTING_BUTT + 50,
diff --git a/src/ui/gui_views/multi/gui_key_derivation_request_view.c b/src/ui/gui_views/multi/gui_key_derivation_request_view.c
index e35ff4c..a999d2a 100644
--- a/src/ui/gui_views/multi/gui_key_derivation_request_view.c
+++ b/src/ui/gui_views/multi/gui_key_derivation_request_view.c
@@ -55,6 +55,9 @@ int32_t GuiKeyDerivationRequestViewEventProcess(void *self, uint16_t usEvent, vo
case SIG_INIT_PULLOUT_USB:
GuiKeyDeriveUsbPullout();
break;
+ case SIG_CLOSE_KEY_DERIVATION_REQUEST:
+ UsbGoToHomeView();
+ break;
default:
return ERR_GUI_UNHANDLED;
}
diff --git a/src/ui/gui_widgets/gui_transaction_detail_widgets.c b/src/ui/gui_widgets/gui_transaction_detail_widgets.c
index 581ab43..ee62fdf 100644
--- a/src/ui/gui_widgets/gui_transaction_detail_widgets.c
+++ b/src/ui/gui_widgets/gui_transaction_detail_widgets.c
@@ -115,7 +115,7 @@ static void TransactionGoToHomeViewHandler(lv_event_t *e)
#ifndef BTC_ONLY
if (GetCurrentTransactionMode() == TRANSACTION_MODE_USB) {
const char *data = "UR parsing rejected";
- HandleURResultViaUSBFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_REJECTED);
+ HandleURResultViaUSBAsyncFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_REJECTED);
}
#endif
CloseQRTimer();
@@ -235,9 +235,9 @@ void GuiTransactionDetailVerifyPasswordSuccess(void)
}
UREncodeResult *urResult = func();
if (urResult->error_code == 0) {
- HandleURResultViaUSBFunc(urResult->data, strlen(urResult->data), GetCurrentUSParsingRequestID(), RSP_SUCCESS_CODE);
+ HandleURResultViaUSBAsyncFunc(urResult->data, strlen(urResult->data), GetCurrentUSParsingRequestID(), RSP_SUCCESS_CODE);
} else {
- HandleURResultViaUSBFunc(urResult->error_message, strlen(urResult->error_message), GetCurrentUSParsingRequestID(), PRS_PARSING_ERROR);
+ HandleURResultViaUSBAsyncFunc(urResult->error_message, strlen(urResult->error_message), GetCurrentUSParsingRequestID(), PRS_PARSING_ERROR);
}
return;
}
@@ -254,7 +254,7 @@ void GuiSignVerifyPasswordErrorCount(void *param)
#ifndef BTC_ONLY
if (GetCurrentTransactionMode() == TRANSACTION_MODE_USB) {
const char *data = "Please try again after unlocking";
- HandleURResultViaUSBFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_VERIFY_PASSWORD_ERROR);
+ HandleURResultViaUSBAsyncFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_VERIFY_PASSWORD_ERROR);
}
#endif
}
@@ -423,4 +423,4 @@ bool supportBlindSigning(uint8_t viewType)
#else
return false;
#endif
-}
\ No newline at end of file
+}
diff --git a/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.c b/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.c
index 9bd7d86..086935c 100644
--- a/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.c
+++ b/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.c
@@ -368,10 +368,17 @@ void GuiKeyDerivationRequestPrevTile()
lv_obj_set_tile_id(g_keyDerivationTileView.tileView, g_keyDerivationTileView.currentTile, 0, LV_ANIM_OFF);
}
+bool GuiKeyDerivationRequestIsUsbPasswordReady(void)
+{
+ char *password = SecretCacheGetPassword();
+ return g_isUsb && g_isUsbPassWordCheck && password != NULL &&
+ strnlen_s(password, PASSWORD_MAX_LEN) != 0;
+}
+
void UpdateAndParseHardwareCall(void)
{
GuiModelURClear();
- if (strnlen_s(SecretCacheGetPassword(), PASSWORD_MAX_LEN) != 0 && g_isUsbPassWordCheck) {
+ if (GuiKeyDerivationRequestIsUsbPasswordReady()) {
if (g_response != NULL) {
free_Response_QRHardwareCallData(g_response);
g_response = NULL;
@@ -840,7 +847,7 @@ static void GuiShowKeyBoardDialog(lv_obj_t *parent)
static void OnApproveHandler(lv_event_t *e)
{
// click approve button and check the hardware call params
- HardwareCallResult_t res = g_hardwareCallParamsCheckResult;
+ HardwareCallResult_t res = g_hardwareCallParamsCheckResult;
if (!res.isLegal) {
GuiCreateHardwareCallInvaildParamHintbox(res.title, res.message);
return;
@@ -892,7 +899,15 @@ void HiddenKeyboardAndShowAnimateQR()
}
g_isUsbPassWordCheck = true;
UREncodeResult *urResult = ModelGenerateSyncUR();
- HandleURResultViaUSBFunc(urResult->data, strlen(urResult->data), GetCurrentUSParsingRequestID(), PRS_EXPORT_HARDWARE_CALL_SUCCESS);
+ if (urResult == NULL || urResult->data == NULL) {
+ const char *data = "Generate sync ur failed";
+ HandleURResultViaUSBAsyncFunc(data, strlen(data), GetCurrentUSParsingRequestID(), RSP_FAILURE_CODE);
+ if (urResult != NULL) {
+ free_ur_encode_result(urResult);
+ }
+ return;
+ }
+ HandleURResultViaUSBAsyncFunc(urResult->data, strlen(urResult->data), GetCurrentUSParsingRequestID(), PRS_EXPORT_HARDWARE_CALL_SUCCESS);
free_ur_encode_result(urResult);
} else {
GuiDeleteKeyboardWidget(g_keyboardWidget);
@@ -1228,7 +1243,7 @@ static void ApproveButtonHandler(lv_event_t *e)
static void RejectButtonHandler(lv_event_t *e)
{
const char *data = "UR parsing rejected";
- HandleURResultViaUSBFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_REJECTED);
+ HandleURResultViaUSBAsyncFunc(data, strlen(data), GetCurrentUSParsingRequestID(), PRS_PARSING_REJECTED);
GuiCloseCurrentWorkingView();
}
diff --git a/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.h b/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.h
index e4f12f0..bf1740d 100644
--- a/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.h
+++ b/src/ui/gui_widgets/multi/gui_key_derivation_request_widgets.h
@@ -14,6 +14,7 @@ void GuiSetKeyDerivationRequestData(void *data, void *multiResult, bool is_multi
void GuiKeyDerivePasswordErrorCount(void *param);
void UpdateAndParseHardwareCall(void);
void GuiKeyDeriveUsbPullout(void);
+bool GuiKeyDerivationRequestIsUsbPasswordReady(void);
void HiddenKeyboardAndShowAnimateQR();
#endif
\ No newline at end of file
diff --git a/src/webusb_protocol/general/eapdu_protocol_parser.c b/src/webusb_protocol/general/eapdu_protocol_parser.c
index 186ad4e..31c26a5 100644
--- a/src/webusb_protocol/general/eapdu_protocol_parser.c
+++ b/src/webusb_protocol/general/eapdu_protocol_parser.c
@@ -14,6 +14,7 @@
#include "eapdu_services/service_echo_test.h"
#include "eapdu_services/service_export_address.h"
#include "eapdu_services/service_get_device_info.h"
+#include "eapdu_services/service_trans_usb_pubkey.h"
static ProtocolSendCallbackFunc_t g_sendFunc = NULL;
static uint32_t g_eapduRcvCount = 0;
@@ -148,6 +149,9 @@ static void EApduRequestHandler(EAPDURequestPayload_t *request)
case CMD_GET_DEVICE_INFO:
GetDeviceInfoService(request);
break;
+ case CMD_GET_DEVICE_USB_PUBKEY:
+ GetDeviceUsbPubkeyService(request);
+ break;
default:
printf("Invalid command: %u\n", request->commandType);
break;
diff --git a/src/webusb_protocol/general/eapdu_protocol_parser.h b/src/webusb_protocol/general/eapdu_protocol_parser.h
index 0cd14cd..5be957a 100644
--- a/src/webusb_protocol/general/eapdu_protocol_parser.h
+++ b/src/webusb_protocol/general/eapdu_protocol_parser.h
@@ -20,6 +20,7 @@ typedef enum {
CMD_CHECK_LOCK_STATUS, // Command to check lock status
CMD_EXPORT_ADDRESS, // Command to export address
CMD_GET_DEVICE_INFO, // Command to get device info
+ CMD_GET_DEVICE_USB_PUBKEY, // Command to get device public key
CMD_MAX_VALUE = 0xFFFFFFFF, // The maximum value for command
} CommandType;
diff --git a/src/webusb_protocol/general/eapdu_services/service_resolve_ur.c b/src/webusb_protocol/general/eapdu_services/service_resolve_ur.c
index 300aa32..959c162 100644
--- a/src/webusb_protocol/general/eapdu_services/service_resolve_ur.c
+++ b/src/webusb_protocol/general/eapdu_services/service_resolve_ur.c
@@ -1,4 +1,6 @@
#include "service_resolve_ur.h"
+#include <string.h>
+#include <stdio.h>
#include "user_delay.h"
#include "gui_chain.h"
#include "user_msg.h"
@@ -6,6 +8,7 @@
#include "gui_lock_widgets.h"
#include "gui_resolve_ur.h"
#include "gui_views.h"
+#include "gui_framework.h"
#include "general_msg.h"
#include "gui_home_widgets.h"
#include "gui_key_derivation_request_widgets.h"
@@ -63,6 +66,31 @@ void HandleURResultViaUSBFunc(const void *data, uint32_t data_len, uint16_t requ
SRAM_FREE(resultPage);
};
+void HandleURResultViaUSBAsyncFunc(const void *data, uint32_t data_len, uint16_t requestID, StatusEnum status)
+{
+ if (data == NULL) {
+ return;
+ }
+
+ uint32_t msgLen = sizeof(USBURResultMsg_t) + data_len + 1;
+ USBURResultMsg_t *msg = (USBURResultMsg_t *)SRAM_MALLOC(msgLen);
+ if (msg == NULL) {
+ HandleURResultViaUSBFunc(data, data_len, requestID, status);
+ return;
+ }
+
+ msg->dataLen = data_len;
+ msg->requestID = requestID;
+ msg->status = status;
+ memcpy(msg->data, data, data_len);
+ msg->data[data_len] = '\0';
+
+ if (PubBufferMsg(USB_MSG_HANDLE_UR_RESULT, msg, msgLen) != MSG_SUCCESS) {
+ HandleURResultViaUSBFunc(data, data_len, requestID, status);
+ }
+ SRAM_FREE(msg);
+}
+
uint16_t GetCurrentUSParsingRequestID()
{
return g_requestID;
@@ -107,11 +135,11 @@ static bool IsRequestAllowed(uint32_t requestID)
{
if (g_requestID != REQUEST_ID_IDLE) {
const char *data = "Previous request is not finished";
- HandleURResultViaUSBFunc(data, strlen(data), requestID, PRS_PARSING_DISALLOWED);
- return false;
- }
-
- if (!CheckURAcceptable()) {
+ StatusEnum status = PRS_PARSING_DISALLOWED;
+ if (GuiCheckIfViewOpened(&g_keyDerivationRequestView)) {
+ data = "Waiting for user approval";
+ }
+ HandleURResultViaUSBFunc(data, strlen(data), requestID, status);
return false;
}
@@ -120,7 +148,20 @@ static bool IsRequestAllowed(uint32_t requestID)
static void HandleHardwareCall(struct URParseResult *urResult)
{
- if (GuiCheckIfTopView(&g_keyDerivationRequestView) || GuiHomePageIsTop()) {
+ if (GuiCheckIfViewOpened(&g_keyDerivationRequestView)) {
+ if (!GuiKeyDerivationRequestIsUsbPasswordReady()) {
+ const char *data = "Waiting for user approval";
+ printf("[USB ResolveUR] hardware_call source=view_open wait_ui req=%u\r\n", (unsigned int)g_requestID);
+ HandleURResultViaUSBFunc(data, strlen(data), g_requestID, PRS_PARSING_DISALLOWED);
+ free_ur_parse_result(urResult);
+ return;
+ }
+ GuiSetKeyDerivationRequestData(urResult, NULL, false);
+ PubValueMsg(UI_MSG_USB_HARDWARE_VIEW, 0);
+ return;
+ }
+
+ if (GuiHomePageIsTop()) {
GuiSetKeyDerivationRequestData(urResult, NULL, false);
PubValueMsg(UI_MSG_USB_HARDWARE_VIEW, 0);
return;
@@ -137,7 +178,7 @@ static bool HandleNormalCall(void)
return true;
}
- if (GuiCheckIfTopView(&g_USBTransportView)) {
+ if (GuiCheckIfTopView(&g_USBTransportView) || GuiCheckIfTopView(&g_keyDerivationRequestView)) {
PubValueMsg(UI_MSG_USB_TRANSPORT_NEXT_VIEW, 0);
UserDelay(200);
return true;
@@ -189,6 +230,9 @@ void ProcessURService(EAPDURequestPayload_t *payload)
HandleHardwareCall(urResult);
break;
}
+ if (!CheckURAcceptable()) {
+ break;
+ }
if (!HandleNormalCall()) {
break;
}
@@ -208,4 +252,4 @@ void ProcessURService(EAPDURequestPayload_t *payload)
free_TransactionCheckResult(checkResult);
}
#endif
-}
\ No newline at end of file
+}
diff --git a/src/webusb_protocol/general/eapdu_services/service_resolve_ur.h b/src/webusb_protocol/general/eapdu_services/service_resolve_ur.h
index c71214d..a373383 100644
--- a/src/webusb_protocol/general/eapdu_services/service_resolve_ur.h
+++ b/src/webusb_protocol/general/eapdu_services/service_resolve_ur.h
@@ -6,5 +6,13 @@
void ProcessURService(EAPDURequestPayload_t *payload);
void HandleURResultViaUSBFunc(const void *data, uint32_t data_len, uint16_t requestID, StatusEnum status);
+void HandleURResultViaUSBAsyncFunc(const void *data, uint32_t data_len, uint16_t requestID, StatusEnum status);
uint16_t GetCurrentUSParsingRequestID();
-void ClearUSBRequestId(void);
\ No newline at end of file
+void ClearUSBRequestId(void);
+
+typedef struct {
+ uint32_t dataLen;
+ uint16_t requestID;
+ StatusEnum status;
+ uint8_t data[];
+} USBURResultMsg_t;
diff --git a/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.c b/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.c
new file mode 100644
index 0000000..cd81095
--- /dev/null
+++ b/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.c
@@ -0,0 +1,155 @@
+#include "service_echo_test.h"
+#include "eapdu_protocol_parser.h"
+#include "utils/define.h"
+#include "cJSON.h"
+#include "account_public_info.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "user_memory.h"
+
+#define COIN_TYPE_SIZE 4
+#define SOLANA_COIN_TYPE 501U
+
+static bool ParseCoinType(const uint8_t *data, uint32_t len, uint32_t *coinType)
+{
+ if (data == NULL || coinType == NULL || len < COIN_TYPE_SIZE) {
+ return false;
+ }
+
+ *coinType = ((uint32_t)data[0] << 24) |
+ ((uint32_t)data[1] << 16) |
+ ((uint32_t)data[2] << 8) |
+ ((uint32_t)data[3]);
+ return true;
+}
+
+static char *ParseSolDerivationPath(const uint8_t *data, uint32_t len)
+{
+ if (data == NULL || len < 1) {
+ return NULL;
+ }
+
+ uint8_t depth = data[0];
+ uint32_t expectedLen = 1 + (depth * 4);
+
+ if (len < expectedLen) {
+ return NULL;
+ }
+
+ char *path = (char *)SRAM_MALLOC(BUFFER_SIZE_32);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ path[0] = '\0';
+
+ for (uint8_t i = 0; i < depth; i++) {
+ uint32_t offset = 1 + (i * 4);
+
+ uint32_t component = ((uint32_t)data[offset] << 24) |
+ ((uint32_t)data[offset + 1] << 16) |
+ ((uint32_t)data[offset + 2] << 8) |
+ ((uint32_t)data[offset + 3]);
+
+ bool isHardened = (component & 0x80000000) != 0;
+ if (!isHardened) {
+ SRAM_FREE(path);
+ return NULL;
+ }
+
+ component &= 0x7FFFFFFF;
+ if (strlen(path) == 0) {
+ snprintf(path, BUFFER_SIZE_32, "%u'", component);
+ } else {
+ snprintf(path + strlen(path), BUFFER_SIZE_32 - strlen(path), "/%u'", component);
+ }
+ }
+
+ return path;
+}
+
+void GetDeviceUsbPubkeyService(EAPDURequestPayload_t *payload)
+{
+ EAPDUResponsePayload_t *result = NULL;
+ cJSON *root = NULL;
+ char *json_str = NULL;
+ uint32_t coinType = 0;
+ char *path = NULL;
+ char *pubKey = NULL;
+
+ result = (EAPDUResponsePayload_t *)SRAM_MALLOC(sizeof(EAPDUResponsePayload_t));
+ if (result == NULL) {
+ printf("Failed to allocate result structure\n");
+ goto cleanup;
+ }
+
+ root = cJSON_CreateObject();
+ if (root == NULL) {
+ printf("Failed to create JSON object\n");
+ goto cleanup;
+ }
+
+ if (!ParseCoinType(payload->data, payload->dataLen, &coinType)) {
+ cJSON_AddStringToObject(root, "error", "Invalid payload");
+ goto create_response;
+ }
+
+ if (coinType != SOLANA_COIN_TYPE) {
+ cJSON_AddStringToObject(root, "error", "Unsupported coin type");
+ goto create_response;
+ }
+
+ path = ParseSolDerivationPath(payload->data + COIN_TYPE_SIZE, payload->dataLen - COIN_TYPE_SIZE);
+ if (path == NULL) {
+ cJSON_AddStringToObject(root, "error", "Failed to parse derivation path");
+ goto create_response;
+ }
+
+ ChainType pubkeyIndex = CheckSolPathSupport(path);
+ if (pubkeyIndex == XPUB_TYPE_NUM) {
+ cJSON_AddStringToObject(root, "error", "Unsupported derivation path");
+ goto create_response;
+ }
+
+ pubKey = GetCurrentAccountPublicKey(pubkeyIndex);
+ if (pubKey == NULL) {
+ cJSON_AddStringToObject(root, "error", "Failed to get public key");
+ goto create_response;
+ }
+
+ cJSON_AddStringToObject(root, "pubkey", pubKey);
+ cJSON_AddStringToObject(root, "derivationPath", path);
+ cJSON_AddNumberToObject(root, "coinType", coinType);
+
+create_response:
+ json_str = cJSON_PrintBuffered(root, BUFFER_SIZE_1024, false);
+ if (json_str == NULL) {
+ printf("Failed to generate JSON string\n");
+ goto cleanup;
+ }
+
+ result->data = (uint8_t *)json_str;
+ result->dataLen = strlen((char *)result->data);
+ result->status = RSP_SUCCESS_CODE;
+ result->cla = EAPDU_PROTOCOL_HEADER;
+ result->commandType = CMD_GET_DEVICE_USB_PUBKEY;
+ result->requestID = payload->requestID;
+
+ SendEApduResponse(result);
+
+cleanup:
+ if (json_str != NULL) {
+ EXT_FREE(json_str);
+ }
+ if (root != NULL) {
+ cJSON_Delete(root);
+ }
+ if (path != NULL) {
+ SRAM_FREE(path);
+ }
+ if (result != NULL) {
+ SRAM_FREE(result);
+ }
+}
diff --git a/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.h b/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.h
new file mode 100644
index 0000000..e100651
--- /dev/null
+++ b/src/webusb_protocol/general/eapdu_services/service_trans_usb_pubkey.h
@@ -0,0 +1,8 @@
+#ifndef _SERVICE_TRANS_USB_PUBKEY_H
+#define _SERVICE_TRANS_USB_PUBKEY_H
+
+#include "eapdu_protocol_parser.h"
+
+void GetDeviceUsbPubkeyService(EAPDURequestPayload_t *payload);
+
+#endif
\ No newline at end of file
Why this scored 36/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.