What changed, and why it matters
This commit is a security-focused code review and hardening pass on account and secret handling in the Keystone 3 hardware wallet firmware. It replaces unsafe string and memory functions (strcpy, sprintf, memcpy) with safer bounded versions, removes debug printf leaks of sensitive data, fixes a bug where a new-password cache overwrote the password cache, adds compile-time guards against shipping test keys in production, and hardens JSON parsing against malformed flash data. These are defensive improvements that reduce the risk of buffer overflows, information leaks, and accidental weakening of secure-element protections.
Treat this commit as a security hardening patch. Review the new helper functions for correct bounds handling, verify that production builds do not define ATECC608B_TEST_MODE or DS28S60_TEST_MODE, and ensure the removed debug prints do not leave other logging sites that leak secrets. Consider running static analysis and targeted fuzzing on the JSON parsing and xpub derivation paths.
Security signals we found
Replacement of strcpy/sprintf/memcpy with bounded strncpy_s/snprintf_s/memcpy_s across secret and account code
Removal of printf debug statements that printed xpubs, wallet config, mnemonics, and JSON secrets to UART/log
Secure zeroization of cached passwords, passphrases, entropy, EMS, and mnemonics before freeing
Fix of SecretCacheSetNewPassword assigning to g_passwordCache instead of g_newPasswordCache
Compile-time #error preventing ATECC608B/DS28S60 test-mode keys in PRODUCTION_BUILD
Null/length checks added to JSON parsing and flash read/write paths
Bounds check added to SLIP39 mnemonic cache index
Fix of GetPassphrase returning bool false instead of NULL for invalid account index
Refactoring of xpub derivation to reduce duplicated conditional code paths
Evidence from the diff
The patch refactors account_public_info.c to centralize xpub derivation (DeriveChainXpub, DeriveMasterKeysIfNeeded, FillPublicInfoForAllChains), adds null/length checks around JSON and flash operations, and replaces strcpy/sprintf with strncpy_s/snprintf_s. secret_cache.c now securely wipes old secrets before freeing, bounds-checks SLIP39 mnemonic indices, and fixes a copy-paste bug where SecretCacheSetNewPassword assigned to g_passwordCache instead of g_newPasswordCache. drv_atecc608b.c and drv_ds28s60.c add #error/#warning guards around test-mode secure-element keys. drv_ds28s60.c also removes a redundant GetMasterSecret call and fixes a typo in DS28S60_SetProtection_From_Index. keystore.c corrects an invalid return type (returning false from a char* function). user_utils.c/h add helper functions for safe JSON manipulation. gui_btc.c contains only cosmetic formatting changes.
Changed components
src/crypto/account_public_info.csrc/crypto/secret_cache.csrc/driver/drv_atecc608b.csrc/driver/drv_ds28s60.csrc/managers/keystore.csrc/ui/gui_chain/gui_btc.csrc/utils/user_utils.csrc/utils/user_utils.hInspect captured patch +368 / −238
diff --git a/src/crypto/account_public_info.c b/src/crypto/account_public_info.c
index 4615519..050b56d 100644
--- a/src/crypto/account_public_info.c
+++ b/src/crypto/account_public_info.c
@@ -62,6 +62,11 @@ static void CleanupJson(cJSON* json);
static void FreePublicKeyRam(void);
static void PrintInfo(void);
static void SetIsTempAccount(bool isTemp);
+static SimpleResponse_c_char *ProcessKeyType(uint8_t *seed, int len, int cryptoKey, const char *path, void *icarusMasterKey, void *ledgerBitbox02MasterKey);
+static int32_t DeriveMasterKeysIfNeeded(bool isBip39, uint8_t *entropy, uint8_t entropyLen, uint8_t accountIndex,
+ SimpleResponse_c_char **outCip3, SimpleResponse_c_char **outLedger);
+static SimpleResponse_c_char* DeriveChainXpub(int chainIndex, const uint8_t *seed, int seedLen, const char *password,
+ const char *icarusMasterKey, const char *ledgerBitbox02Key, bool isSlip39);
#ifdef BTC_ONLY
static void LoadCurrentAccountMultiReceiveIndex(void);
@@ -82,7 +87,8 @@ static void LoadCurrentAccountMultiReceiveIndex(void)
if (GetCurrenMultisigWalletByIndex(i) == NULL) {
continue;
}
- strcpy(g_multiSigReceiveIndex[i].verifyCode, GetCurrenMultisigWalletByIndex(i)->verifyCode);
+ strncpy_s(g_multiSigReceiveIndex[i].verifyCode, sizeof(g_multiSigReceiveIndex[i].verifyCode),
+ GetCurrenMultisigWalletByIndex(i)->verifyCode, sizeof(g_multiSigReceiveIndex[i].verifyCode) - 1);
}
}
@@ -112,7 +118,7 @@ void ExportMultiSigXpub(ChainType chainType)
ByteArrayToHexStr(mfp, sizeof(mfp), mfpHexStr);
char path[64] = {0};
- strcpy(path, GetXPubPath(chainType));
+ strncpy_s(path, sizeof(path), GetXPubPath(chainType), sizeof(path) - 1);
replace(path, "M", "m");
char xpub[128] = {0};
@@ -132,15 +138,15 @@ void ExportMultiSigXpub(ChainType chainType)
switch (chainType) {
case XPUB_TYPE_BTC_MULTI_SIG_P2SH:
case XPUB_TYPE_BTC_MULTI_SIG_P2SH_TEST:
- sprintf(exportFileName, "0:%s_%s.json", mfpHexStr, "P2SH");
+ snprintf_s(exportFileName, sizeof(exportFileName), "0:%s_%s.json", mfpHexStr, "P2SH");
break;
case XPUB_TYPE_BTC_MULTI_SIG_P2WSH_P2SH:
case XPUB_TYPE_BTC_MULTI_SIG_P2WSH_P2SH_TEST:
- sprintf(exportFileName, "0:%s_%s.json", mfpHexStr, "P2SH-P2WSH");
+ snprintf_s(exportFileName, sizeof(exportFileName), "0:%s_%s.json", mfpHexStr, "P2SH-P2WSH");
break;
case XPUB_TYPE_BTC_MULTI_SIG_P2WSH:
case XPUB_TYPE_BTC_MULTI_SIG_P2WSH_TEST:
- sprintf(exportFileName, "0:%s_%s.json", mfpHexStr, "P2WSH");
+ snprintf_s(exportFileName, sizeof(exportFileName), "0:%s_%s.json", mfpHexStr, "P2WSH");
break;
default:
break;
@@ -148,8 +154,6 @@ void ExportMultiSigXpub(ChainType chainType)
int res = FatfsFileWrite(exportFileName, (uint8_t *)jsonString, strlen(jsonString));
- printf("export data is %s\r\n", jsonString);
-
if (res == RES_OK) {
printf("multi sig write to sdcard success\r\n");
} else {
@@ -168,7 +172,6 @@ static void ConvertXPub(char *dest, ChainType chainType)
char head[] = "xpub";
switch (chainType) {
case XPUB_TYPE_BTC_MULTI_SIG_P2SH:
- sprintf(dest, "%s", xpub);
break;
case XPUB_TYPE_BTC_MULTI_SIG_P2WSH_P2SH:
head[0] = 'Y';
@@ -190,7 +193,7 @@ static void ConvertXPub(char *dest, ChainType chainType)
}
result = xpub_convert_version(xpub, head);
ASSERT(result);
- sprintf(dest, "%s", result->data);
+ snprintf_s(dest, 128, "%s", result->data);
free_simple_response_c_char(result);
}
@@ -206,10 +209,9 @@ void ExportMultiSigWallet(char *verifyCode, uint8_t accountIndex)
}
char exportFileName[32] = {0};
- sprintf(exportFileName, "0:exprot-%s.txt", multiSigWalletItem->name);
+ snprintf_s(exportFileName, sizeof(exportFileName), "0:export-%s.txt", multiSigWalletItem->name);
int res = FatfsFileWrite(exportFileName, (uint8_t *)multiSigWalletItem->walletConfig, strlen(multiSigWalletItem->walletConfig));
- printf("export file name is %s\r\n", exportFileName);
- printf("export data is %s\r\n", multiSigWalletItem->walletConfig);
+
if (res == RES_OK) {
printf("multi sig write to sdcard success\r\n");
} else {
@@ -230,20 +232,19 @@ uint32_t GetAccountMultiReceiveIndex(char *verifyCode)
uint32_t GetAccountMultiReceiveIndexFromFlash(char *verifyCode)
{
char key[BUFFER_SIZE_64] = {0};
- sprintf(key, "multiRecvIndex_%s", verifyCode);
- printf("key = %s.\n", key);
+ snprintf_s(key, sizeof(key), "multiRecvIndex_%s", verifyCode);
return GetTemplateWalletValue("BTC", key);
}
void SetAccountMultiReceiveIndex(uint32_t index, char *verifyCode)
{
char key[BUFFER_SIZE_64] = {0};
- sprintf(key, "multiRecvIndex_%s", verifyCode);
- printf("key = %s.\n", key);
+ snprintf_s(key, sizeof(key), "multiRecvIndex_%s", verifyCode);
for (int i = 0; i < MAX_MULTI_SIG_WALLET_NUMBER; i++) {
if (strlen(g_multiSigReceiveIndex[i].verifyCode) == 0) {
g_multiSigReceiveIndex[i].index = index;
- strcpy(g_multiSigReceiveIndex[i].verifyCode, verifyCode);
+ strncpy_s(g_multiSigReceiveIndex[i].verifyCode, sizeof(g_multiSigReceiveIndex[i].verifyCode),
+ verifyCode, sizeof(g_multiSigReceiveIndex[i].verifyCode) - 1);
break;
} else if (strcmp(g_multiSigReceiveIndex[i].verifyCode, verifyCode) == 0) {
g_multiSigReceiveIndex[i].index = index;
@@ -257,8 +258,7 @@ void DeleteAccountMultiReceiveIndex(const char* chainName, char *verifyCode)
{
uint32_t addr;
char key[BUFFER_SIZE_64] = {0};
- sprintf(key, "multiRecvIndex_%s", verifyCode);
- printf("key = %s.\n", key);
+ snprintf_s(key, sizeof(key), "multiRecvIndex_%s", verifyCode);
cJSON* rootJson = ReadAndParseAccountJson(&addr, NULL);
cJSON* item = cJSON_GetObjectItem(rootJson, chainName);
@@ -558,6 +558,125 @@ static const ChainItem_t g_chainTable[] = {
#endif
};
+static void EraseFlashRange(uint32_t startAddr, uint32_t totalSize, uint32_t sectorSize)
+{
+ for (uint32_t eraseAddr = startAddr; eraseAddr < startAddr + totalSize; eraseAddr += sectorSize) {
+ Gd25FlashSectorErase(eraseAddr);
+ }
+}
+
+static int32_t WritePublicJsonAndHash(uint8_t accountIndex, uint32_t addr, const char *jsonString)
+{
+ uint8_t hash[32];
+ uint32_t size = strlen(jsonString);
+ sha256((struct sha256 *)hash, jsonString, size);
+ SetWalletDataHash(accountIndex, hash);
+ CLEAR_ARRAY(hash);
+ int len = Gd25FlashWriteBuffer(addr, (uint8_t *)&size, 4);
+ ASSERT(len == 4);
+ len = Gd25FlashWriteBuffer(addr + 4, (uint8_t *)jsonString, size);
+ ASSERT(len == size);
+ return SUCCESS_CODE;
+}
+
+// enumerate and fill all non-TON chains
+static int32_t FillPublicInfoForAllChains(const uint8_t *seed, int seedLen, const char *password,
+ const char *icarusMasterKey, const char *ledgerBitbox02Key,
+ bool isSlip39)
+{
+ int32_t ret = SUCCESS_CODE;
+ for (int i = 0; i < NUMBER_OF_ARRAYS(g_chainTable); i++) {
+ // skip unsupported for slip39 and non-public entries
+ if (isSlip39 && (g_chainTable[i].cryptoKey == LEDGER_BITBOX02 || g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED)) {
+ continue;
+ }
+#ifdef WEB3_VERSION
+ if (g_chainTable[i].cryptoKey == TON_CHECKSUM || g_chainTable[i].cryptoKey == TON_NATIVE) {
+ continue;
+ }
+#endif
+ SimpleResponse_c_char* xPubResult = DeriveChainXpub(i, seed, seedLen, password, icarusMasterKey, ledgerBitbox02Key, isSlip39);
+ if (g_chainTable[i].cryptoKey == RSA_KEY && xPubResult == NULL) {
+ continue;
+ }
+ ASSERT(xPubResult);
+ if (xPubResult->error_code != 0) {
+ printf("get_extended_pubkey error\r\n");
+ if (xPubResult->error_message != NULL) {
+ printf("error code = %d\r\nerror msg is: %s\r\n", xPubResult->error_code, xPubResult->error_message);
+ }
+ ret = xPubResult->error_code;
+ free_simple_response_c_char(xPubResult);
+ break;
+ }
+ ASSERT(xPubResult->data);
+ g_accountPublicInfo[i].value = SRAM_MALLOC(strnlen_s(xPubResult->data, SIMPLERESPONSE_C_CHAR_MAX_LEN) + 1);
+ strcpy_s(g_accountPublicInfo[i].value, strnlen_s(xPubResult->data, SIMPLERESPONSE_C_CHAR_MAX_LEN) + 1, xPubResult->data);
+ free_simple_response_c_char(xPubResult);
+ }
+ return ret;
+}
+
+static int32_t DeriveMasterKeysIfNeeded(bool isBip39, uint8_t *entropy, uint8_t entropyLen, uint8_t accountIndex,
+ SimpleResponse_c_char **outCip3, SimpleResponse_c_char **outLedger)
+{
+ int32_t ret = SUCCESS_CODE;
+ *outCip3 = NULL;
+ *outLedger = NULL;
+ if (!isBip39) {
+ return SUCCESS_CODE;
+ }
+ char *mnemonic = NULL;
+ ret = bip39_mnemonic_from_bytes(NULL, entropy, entropyLen, &mnemonic);
+ if (ret != SUCCESS_CODE) {
+ printf("get mnemonic error\r\n");
+ if (mnemonic != NULL) {
+ memset_s(mnemonic, MNEMONIC_MAX_LEN, 0, strnlen_s(mnemonic, MNEMONIC_MAX_LEN));
+ SRAM_FREE(mnemonic);
+ }
+ return ERR_GENERAL_FAIL;
+ }
+
+ char *passphrase = GetPassphrase(accountIndex);
+ *outCip3 = get_icarus_master_key(entropy, entropyLen, passphrase);
+ *outLedger = get_ledger_bitbox02_master_key(mnemonic, passphrase);
+ if (mnemonic != NULL) {
+ size_t mnemonic_len = strnlen_s(mnemonic, MNEMONIC_MAX_LEN);
+ memset_s(mnemonic, MNEMONIC_MAX_LEN, 0, mnemonic_len);
+ SRAM_FREE(mnemonic);
+ }
+ return SUCCESS_CODE;
+}
+
+static SimpleResponse_c_char* DeriveChainXpub(int chainIndex, const uint8_t *seed, int seedLen, const char *password,
+ const char *icarusMasterKey, const char *ledgerBitbox02Key, bool isSlip39)
+{
+ const int cryptoKey = g_chainTable[chainIndex].cryptoKey;
+ const char *path = g_chainTable[chainIndex].path;
+#ifdef CYPHERPUNK_VERSION
+ if (cryptoKey == ZCASH_UFVK_ENCRYPTED) {
+ SimpleResponse_c_char *zcash_ufvk_response = derive_zcash_ufvk((uint8_t*)seed, seedLen, path);
+ if (zcash_ufvk_response == NULL || zcash_ufvk_response->error_code != 0) {
+ return zcash_ufvk_response;
+ }
+ char* zcashUfvk = zcash_ufvk_response->data;
+ SimpleResponse_u8 *iv_response = rust_derive_iv_from_seed((uint8_t*)seed, seedLen);
+ uint8_t iv_bytes[16];
+ memcpy_s(iv_bytes, 16, iv_response->data, 16);
+ free_simple_response_u8(iv_response);
+ SimpleResponse_c_char* enc = rust_aes256_cbc_encrypt(zcashUfvk, password, iv_bytes, 16);
+ free_simple_response_c_char(zcash_ufvk_response);
+ return enc;
+ }
+#endif
+#ifdef WEB3_VERSION
+ if (cryptoKey == BIP32_ED25519 && isSlip39) {
+ return cardano_get_pubkey_by_slip23((uint8_t*)seed, seedLen, path);
+ }
+#endif
+ return ProcessKeyType((uint8_t*)seed, seedLen, cryptoKey, path, (void*)icarusMasterKey, (void*)ledgerBitbox02Key);
+}
+
#ifdef WEB3_VERSION
ChainType CheckSolPathSupport(char *path)
{
@@ -799,13 +918,16 @@ void AccountPublicHomeCoinSet(WalletState_t *walletList, uint8_t count)
if (needUpdate) {
for (eraseAddr = addr; eraseAddr < addr + SPI_FLASH_SIZE_USER1_MUTABLE_DATA; eraseAddr += GD25QXX_SECTOR_SIZE) {
- Gd25FlashSectorErase(eraseAddr);
+ if (Gd25FlashSectorErase(eraseAddr) != SUCCESS_CODE) {
+ printf("Gd25FlashSectorErase error\r\n");
+ return;
+ }
}
jsonString = cJSON_PrintBuffered(rootJson, SPI_FLASH_SIZE_USER1_MUTABLE_DATA - 4, false);
RemoveFormatChar(jsonString);
size = strlen(jsonString);
- Gd25FlashWriteBuffer(addr, (uint8_t *)&size, 4);
- Gd25FlashWriteBuffer(addr + 4, (uint8_t *)jsonString, size);
+ ASSERT(Gd25FlashWriteBuffer(addr, (uint8_t *)&size, 4) == 4);
+ ASSERT(Gd25FlashWriteBuffer(addr + 4, (uint8_t *)jsonString, size) == size);
EXT_FREE(jsonString);
}
cJSON_Delete(rootJson);
@@ -842,7 +964,6 @@ int32_t AccountPublicInfoReadFromFlash(uint8_t accountIndex, uint32_t addr)
CLEAR_ARRAY(hash);
if (GetPublicKeyFromJsonString(jsonString) == false) {
printf("GetPublicKeyFromJsonString false, need regenerate\r\n");
- printf("err jsonString=%s\r\n", jsonString);
ret = ERR_GENERAL_FAIL;
}
@@ -884,22 +1005,11 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
CHECK_ERRCODE_BREAK("get entropy", ret);
SimpleResponse_c_char* cip3_response = NULL;
SimpleResponse_c_char *ledger_bitbox02_response = NULL;
- // should setup ADA for bip39 wallet;
if (isBip39) {
- char *mnemonic = NULL;
- ret = bip39_mnemonic_from_bytes(NULL, entropy, entropyLen, &mnemonic);
+ ret = DeriveMasterKeysIfNeeded(isBip39, entropy, entropyLen, accountIndex, &cip3_response, &ledger_bitbox02_response);
if (ret != SUCCESS_CODE) {
- printf("get mnemonic error\r\n");
- if (mnemonic != NULL) {
- memset_s(mnemonic, MNEMONIC_MAX_LEN, 0, strnlen_s(mnemonic, MNEMONIC_MAX_LEN));
- SRAM_FREE(mnemonic);
- }
- ret = ERR_GENERAL_FAIL;
break;
}
- cip3_response = get_icarus_master_key(entropy, entropyLen, GetPassphrase(accountIndex));
- ledger_bitbox02_response = get_ledger_bitbox02_master_key(mnemonic, GetPassphrase(accountIndex));
- SRAM_FREE(mnemonic);
CHECK_AND_FREE_XPUB(cip3_response);
CHECK_AND_FREE_XPUB(ledger_bitbox02_response);
icarusMasterKey = cip3_response->data;
@@ -918,12 +1028,11 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
//store a checksum of entropy for quick compare;
uint8_t checksum[32] = {'\0'};
CalculateTonChecksum(entropy, (char *)checksum);
- printf("ton checksum: %s\r\n", checksum);
g_accountPublicInfo[PUBLIC_INFO_TON_CHECKSUM].value = SRAM_MALLOC(65);
char* ptr = g_accountPublicInfo[PUBLIC_INFO_TON_CHECKSUM].value;
memset_s(ptr, 65, 0, 65);
for (size_t i = 0; i < 32; i++) {
- snprintf_s(ptr, 65, "%s%02x", ptr, checksum[i]);
+ snprintf_s(ptr + i * 2, 65 - i * 2, "%02x", checksum[i]);
}
} else {
#endif
@@ -938,34 +1047,7 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
if (g_chainTable[i].cryptoKey == TON_CHECKSUM || g_chainTable[i].cryptoKey == TON_NATIVE) {
continue;
}
-#ifdef CYPHERPUNK_VERSION
- //encrypt zcash ufvk
- if (g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED) {
- char* zcashUfvk = NULL;
- SimpleResponse_c_char *zcash_ufvk_response = NULL;
- zcash_ufvk_response = derive_zcash_ufvk(seed, seedLen, g_chainTable[i].path);
- CHECK_AND_FREE_XPUB(zcash_ufvk_response)
- zcashUfvk = zcash_ufvk_response->data;
- SimpleResponse_u8 *iv_response = rust_derive_iv_from_seed(seed, seedLen);
- //iv_response won't fail
- uint8_t iv_bytes[16];
- memcpy_s(iv_bytes, 16, iv_response->data, 16);
- free_simple_response_u8(iv_response);
- xPubResult = rust_aes256_cbc_encrypt(zcashUfvk, password, iv_bytes, 16);
- } else {
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
- }
-#endif
-#ifdef WEB3_VERSION
- if (g_chainTable[i].cryptoKey == BIP32_ED25519 && isSlip39) {
- xPubResult = cardano_get_pubkey_by_slip23(seed, seedLen, g_chainTable[i].path);
- } else {
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
- }
-#endif
-#ifdef BTC_ONLY
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
-#endif
+ xPubResult = DeriveChainXpub(i, seed, seedLen, password, icarusMasterKey, ledgerBitbox02Key, isSlip39);
if (g_chainTable[i].cryptoKey == RSA_KEY && xPubResult == NULL) {
continue;
}
@@ -980,23 +1062,15 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
#ifdef WEB3_VERSION
}
#endif
- printf("erase user data:0x%X\n", addr);
- for (uint32_t eraseAddr = addr; eraseAddr < addr + SPI_FLASH_SIZE_USER1_DATA; eraseAddr += GD25QXX_SECTOR_SIZE) {
- Gd25FlashSectorErase(eraseAddr);
- }
- printf("erase done\n");
+ EraseFlashRange(addr, SPI_FLASH_SIZE_USER1_DATA, GD25QXX_SECTOR_SIZE);
jsonString = GetJsonStringFromPublicKey();
+ if (jsonString == NULL) {
+ ret = ERR_GENERAL_FAIL;
+ break;
+ }
- printf("save jsonString = \r\n%s\n", jsonString);
- sha256((struct sha256 *)hash, jsonString, strlen(jsonString));
- SetWalletDataHash(accountIndex, hash);
- CLEAR_ARRAY(hash);
- uint32_t size = strlen(jsonString);
- int len = Gd25FlashWriteBuffer(addr, (uint8_t *)&size, 4);
- ASSERT(len == 4);
- len = Gd25FlashWriteBuffer(addr + 4, (uint8_t *)jsonString, size);
- ASSERT(len == size);
- printf("regenerate jsonString=%s\r\n", jsonString);
+ WritePublicJsonAndHash(accountIndex, addr, jsonString);
+ // printf("regenerate jsonString=%s\r\n", jsonString);
if (!isSlip39) {
memset_s(cip3_response->data, strlen(cip3_response->data), 0, strlen(cip3_response->data));
memset_s(ledger_bitbox02_response->data, strlen(ledger_bitbox02_response->data), 0, strlen(ledger_bitbox02_response->data));
@@ -1092,65 +1166,36 @@ int32_t TempAccountPublicInfo(uint8_t accountIndex, const char *password, bool s
SimpleResponse_c_char *ledger_bitbox02_response = NULL;
if (!isSlip39) {
- do {
- char *mnemonic = NULL;
- ret = bip39_mnemonic_from_bytes(NULL, entropy, entropyLen, &mnemonic);
- if (ret != SUCCESS_CODE) {
- printf("get mnemonic error\r\n");
- if (mnemonic != NULL) {
- memset_s(mnemonic, MNEMONIC_MAX_LEN, 0, strnlen_s(mnemonic, MNEMONIC_MAX_LEN));
- SRAM_FREE(mnemonic);
+ ret = DeriveMasterKeysIfNeeded(true, entropy, entropyLen, accountIndex, &cip3_response, &ledger_bitbox02_response);
+ if (ret == SUCCESS_CODE && cip3_response && ledger_bitbox02_response) {
+ if (cip3_response->error_code != 0) {
+ printf("get_extended_pubkey error\r\n");
+ free_simple_response_c_char(cip3_response);
+ if (ledger_bitbox02_response) free_simple_response_c_char(ledger_bitbox02_response);
+ return cip3_response->error_code;
+ }
+ if (ledger_bitbox02_response->error_code != 0) {
+ printf("get_extended_pubkey error\r\n");
+ if (ledger_bitbox02_response->error_message != NULL) {
+ printf("error code = %d\r\nerror msg is: %s\r\n", ledger_bitbox02_response->error_code, ledger_bitbox02_response->error_message);
}
- break;
+ free_simple_response_c_char(ledger_bitbox02_response);
+ free_simple_response_c_char(cip3_response);
+ return ledger_bitbox02_response->error_code;
}
- cip3_response = get_icarus_master_key(entropy, entropyLen, GetPassphrase(accountIndex));
- ledger_bitbox02_response = get_ledger_bitbox02_master_key(mnemonic, GetPassphrase(accountIndex));
- SRAM_FREE(mnemonic);
- CHECK_AND_FREE_XPUB(cip3_response);
- CHECK_AND_FREE_XPUB(ledger_bitbox02_response);
icarusMasterKey = cip3_response->data;
ledgerBitbox02Key = ledger_bitbox02_response->data;
- } while (0);
+ }
}
for (i = 0; i < NUMBER_OF_ARRAYS(g_chainTable); i++) {
- // SLIP32 wallet does not support ADA
- // slip23 for ada
if (isSlip39 && (g_chainTable[i].cryptoKey == LEDGER_BITBOX02 || g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED)) {
continue;
}
if (g_chainTable[i].cryptoKey == TON_CHECKSUM || g_chainTable[i].cryptoKey == TON_NATIVE) {
continue;
}
-#ifdef CYPHERPUNK_VERSION
- //encrypt zcash ufvk
- if (g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED) {
- char* zcashUfvk = NULL;
- SimpleResponse_c_char *zcash_ufvk_response = NULL;
- zcash_ufvk_response = derive_zcash_ufvk(seed, seedLen, g_chainTable[i].path);
- CHECK_AND_FREE_XPUB(zcash_ufvk_response)
- zcashUfvk = zcash_ufvk_response->data;
- SimpleResponse_u8 *iv_response = rust_derive_iv_from_seed(seed, seedLen);
- //iv_response won't fail
- uint8_t iv_bytes[16];
- memcpy_s(iv_bytes, 16, iv_response->data, 16);
- free_simple_response_u8(iv_response);
- xPubResult = rust_aes256_cbc_encrypt(zcashUfvk, password, iv_bytes, 16);
- } else {
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
- }
-#endif
-#ifdef WEB3_VERSION
- if (g_chainTable[i].cryptoKey == BIP32_ED25519 && isSlip39) {
- // ada slip23
- xPubResult = cardano_get_pubkey_by_slip23(seed, seedLen, g_chainTable[i].path);
- } else {
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
- }
-#endif
-#ifdef BTC_ONLY
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
-#endif
+ xPubResult = DeriveChainXpub(i, seed, seedLen, password, icarusMasterKey, ledgerBitbox02Key, isSlip39);
if (g_chainTable[i].cryptoKey == RSA_KEY && xPubResult == NULL) {
continue;
}
@@ -1163,11 +1208,9 @@ int32_t TempAccountPublicInfo(uint8_t accountIndex, const char *password, bool s
free_simple_response_c_char(xPubResult);
break;
}
- printf("index=%d,path=%s,pub=%s\r\n", accountIndex, g_chainTable[i].path, xPubResult->data);
ASSERT(xPubResult->data);
g_accountPublicInfo[i].value = SRAM_MALLOC(strnlen_s(xPubResult->data, SIMPLERESPONSE_C_CHAR_MAX_LEN) + 1);
- strcpy(g_accountPublicInfo[i].value, xPubResult->data);
- printf("xPubResult=%s\r\n", xPubResult->data);
+ strcpy_s(g_accountPublicInfo[i].value, strnlen_s(xPubResult->data, SIMPLERESPONSE_C_CHAR_MAX_LEN) + 1, xPubResult->data);
free_simple_response_c_char(xPubResult);
}
if (!isSlip39) {
@@ -1239,6 +1282,7 @@ uint8_t SpecifiedXPubExist(const char *value, bool isTon)
uint8_t accountIndex = 255;
for (index = 0; index < 3; index++) {
+ rootJson = NULL;
addr = SPI_FLASH_ADDR_USER1_DATA + index * SPI_FLASH_ADDR_EACH_SIZE;
ret = Gd25FlashReadBuffer(addr, (uint8_t *)&size, sizeof(size));
ASSERT(ret == 4);
@@ -1375,8 +1419,9 @@ static bool GetPublicKeyFromJsonString(const char *string)
} else {
GetStringValue(chainJson, "value", pubKeyString, PUB_KEY_MAX_LENGTH);
//printf("%s pub key=%s\r\n", g_chainTable[i].name, pubKeyString);
- g_accountPublicInfo[i].value = SRAM_MALLOC(strnlen_s(pubKeyString, PUB_KEY_MAX_LENGTH) + 1);
- strcpy(g_accountPublicInfo[i].value, pubKeyString);
+ size_t pubKeyLen = strnlen_s(pubKeyString, PUB_KEY_MAX_LENGTH);
+ g_accountPublicInfo[i].value = SRAM_MALLOC(pubKeyLen + 1);
+ strcpy_s(g_accountPublicInfo[i].value, pubKeyLen + 1, pubKeyString);
}
}
} while (0);
@@ -1395,14 +1440,18 @@ static char *GetJsonStringFromPublicKey(void)
chainsJson = cJSON_CreateObject();
for (i = 0; i < NUMBER_OF_ARRAYS(g_chainTable); i++) {
jsonItem = cJSON_CreateObject();
- cJSON_AddItemToObject(jsonItem, "value", cJSON_CreateString(g_accountPublicInfo[i].value));
- //printf("g_accountPublicInfo[%d].value=%s\r\n", i, g_accountPublicInfo[i].value);
+ const char* value = g_accountPublicInfo[i].value ? g_accountPublicInfo[i].value : "";
+ cJSON_AddItemToObject(jsonItem, "value", cJSON_CreateString(value));
cJSON_AddItemToObject(jsonItem, "current", cJSON_CreateNumber(g_accountPublicInfo[i].current));
cJSON_AddItemToObject(chainsJson, g_chainTable[i].name, jsonItem);
}
cJSON_AddItemToObject(rootJson, "version", cJSON_CreateString(g_xpubInfoVersion));
cJSON_AddItemToObject(rootJson, "key", chainsJson);
retStr = cJSON_PrintBuffered(rootJson, SPI_FLASH_SIZE_USER1_DATA - 4, 0);
+ if (retStr == NULL) {
+ cJSON_Delete(rootJson);
+ return NULL;
+ }
RemoveFormatChar(retStr);
cJSON_Delete(rootJson);
return retStr;
@@ -1449,13 +1498,16 @@ bool GetFirstReceive(const char* chainName)
cJSON *rootJson = cJSON_Parse(jsonString);
SRAM_FREE(jsonString);
+ if (rootJson == NULL) {
+ return false;
+ }
cJSON *item = cJSON_GetObjectItem(rootJson, chainName);
bool state = false;
if (item == NULL) {
printf("GetFirstReceive cannot get %s\r\n", chainName);
} else {
cJSON *firstRecv = cJSON_GetObjectItem(item, "firstRecv");
- state = firstRecv->valueint;
+ state = firstRecv ? firstRecv->valueint : false;
}
cJSON_Delete(rootJson);
return state;
@@ -1480,17 +1532,19 @@ void SetFirstReceive(const char* chainName, bool isFirst)
cJSON *rootJson = cJSON_Parse(jsonString);
SRAM_FREE(jsonString);
+ if (rootJson == NULL) {
+ rootJson = cJSON_CreateObject();
+ }
cJSON *item = cJSON_GetObjectItem(rootJson, chainName);
if (item == NULL) {
printf("SetFirstReceive cannot get %s\r\n", chainName);
- cJSON *jsonItem = cJSON_CreateObject();
- cJSON_AddItemToObject(jsonItem, "recvIndex", cJSON_CreateNumber(0));
- cJSON_AddItemToObject(jsonItem, "recvPath", cJSON_CreateNumber(0));
- cJSON_AddItemToObject(jsonItem, "firstRecv", cJSON_CreateBool(isFirst));
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(true));
- cJSON_AddItemToObject(rootJson, chainName, jsonItem);
+ cJSON *jsonItem = GetOrCreateObjectItem(rootJson, chainName);
+ SetUintValue(jsonItem, "recvIndex", 0);
+ SetUintValue(jsonItem, "recvPath", 0);
+ SetBoolValue(jsonItem, "firstRecv", isFirst);
+ SetBoolValue(jsonItem, "manage", true);
} else {
- cJSON_ReplaceItemInObject(item, "firstRecv", cJSON_CreateBool(isFirst));
+ SetBoolValue(item, "firstRecv", isFirst);
}
for (eraseAddr = addr; eraseAddr < addr + SPI_FLASH_SIZE_USER1_MUTABLE_DATA; eraseAddr += GD25QXX_SECTOR_SIZE) {
@@ -1635,28 +1689,33 @@ int32_t MultiSigWalletGet(uint8_t accountIndex, const char *password, MultiSigWa
cJSON *order = cJSON_GetObjectItem(wallet, "order");
multiSigWalletItem->order = order->valueint;
GetStringValue(wallet, "name", strCache, MULTI_SIG_STR_CACHE_LENGTH);
- multiSigWalletItem->name = MULTI_SIG_MALLOC(strlen(strCache) + 1);
- strcpy(multiSigWalletItem->name, strCache);
+ size_t nameLen = strnlen_s(strCache, MULTI_SIG_STR_CACHE_LENGTH);
+ multiSigWalletItem->name = MULTI_SIG_MALLOC(nameLen + 1);
+ strcpy_s(multiSigWalletItem->name, nameLen + 1, strCache);
GetStringValue(wallet, "verify_code", strCache, MULTI_SIG_STR_CACHE_LENGTH);
- multiSigWalletItem->verifyCode = MULTI_SIG_MALLOC(strlen(strCache) + 1);
- strcpy(multiSigWalletItem->verifyCode, strCache);
+ size_t verifyCodeLen = strnlen_s(strCache, MULTI_SIG_STR_CACHE_LENGTH);
+ multiSigWalletItem->verifyCode = MULTI_SIG_MALLOC(verifyCodeLen + 1);
+ strcpy_s(multiSigWalletItem->verifyCode, verifyCodeLen + 1, strCache);
GetStringValue(wallet, "verify_without_mfp", strCache, MULTI_SIG_STR_CACHE_LENGTH);
- multiSigWalletItem->verifyWithoutMfp = MULTI_SIG_MALLOC(strlen(strCache) + 1);
- strcpy(multiSigWalletItem->verifyWithoutMfp, strCache);
+ size_t verifyWithoutMfpLen = strnlen_s(strCache, MULTI_SIG_STR_CACHE_LENGTH);
+ multiSigWalletItem->verifyWithoutMfp = MULTI_SIG_MALLOC(verifyWithoutMfpLen + 1);
+ strcpy_s(multiSigWalletItem->verifyWithoutMfp, verifyWithoutMfpLen + 1, strCache);
cJSON *network = cJSON_GetObjectItem(wallet, "network");
multiSigWalletItem->network = network->valueint;
GetStringValue(wallet, "wallet_config", strCache, MULTI_SIG_STR_CACHE_LENGTH);
- multiSigWalletItem->walletConfig = MULTI_SIG_MALLOC(strlen(strCache) + 1);
- strcpy(multiSigWalletItem->walletConfig, strCache);
+ size_t walletConfigLen = strnlen_s(strCache, MULTI_SIG_STR_CACHE_LENGTH);
+ multiSigWalletItem->walletConfig = MULTI_SIG_MALLOC(walletConfigLen + 1);
+ strcpy_s(multiSigWalletItem->walletConfig, walletConfigLen + 1, strCache);
GetStringValue(wallet, "format", strCache, MULTI_SIG_STR_CACHE_LENGTH);
- multiSigWalletItem->format = MULTI_SIG_MALLOC(strlen(strCache) + 1);
- strcpy(multiSigWalletItem->format, strCache);
+ size_t formatLen = strnlen_s(strCache, MULTI_SIG_STR_CACHE_LENGTH);
+ multiSigWalletItem->format = MULTI_SIG_MALLOC(formatLen + 1);
+ strcpy_s(multiSigWalletItem->format, formatLen + 1, strCache);
cJSON *passphrase = cJSON_GetObjectItem(wallet, "passphrase");
multiSigWalletItem->passphrase = passphrase ? passphrase->valueint : 0;
@@ -1677,21 +1736,14 @@ uint32_t GetAccountReceiveIndex(const char* chainName)
cJSON *item = cJSON_GetObjectItem(rootJson, chainName);
if (item == NULL) {
printf("receive index cannot get %s\r\n", chainName);
- cJSON *jsonItem = cJSON_CreateObject();
- cJSON_AddItemToObject(jsonItem, "recvIndex", cJSON_CreateNumber(0)); // recvIndex is the address index
- cJSON_AddItemToObject(jsonItem, "recvPath", cJSON_CreateNumber(0)); // recvPath is the derivation path type
- cJSON_AddItemToObject(jsonItem, "firstRecv", cJSON_CreateBool(true)); // firstRecv is the first receive address
- if (!strcmp(chainName, "TON")) {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(true));
- } else if ((!strcmp(chainName, "BTC") || !strcmp(chainName, "ETH"))) {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(true));
- } else {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(false));
- }
- cJSON_AddItemToObject(rootJson, chainName, jsonItem);
+ cJSON *jsonItem = GetOrCreateObjectItem(rootJson, chainName);
+ SetUintValue(jsonItem, "recvIndex", 0); // recvIndex is the address index
+ SetUintValue(jsonItem, "recvPath", 0); // recvPath is the derivation path type
+ SetBoolValue(jsonItem, "firstRecv", true); // firstRecv is the first receive address
+ bool manage = (!strcmp(chainName, "TON")) || (!strcmp(chainName, "BTC")) || (!strcmp(chainName, "ETH"));
+ SetBoolValue(jsonItem, "manage", manage);
} else {
- cJSON *recvIndex = cJSON_GetObjectItem(item, "recvIndex");
- index = recvIndex ? recvIndex->valueint : 0;
+ index = GetUintValue(item, "recvIndex", 0);
}
if (!PassphraseExist(GetCurrentAccountIndex())) {
@@ -1705,17 +1757,8 @@ void SetAccountReceiveIndex(const char* chainName, uint32_t index)
uint32_t addr;
cJSON *rootJson = ReadAndParseAccountJson(&addr, NULL);
- cJSON *item = cJSON_GetObjectItem(rootJson, chainName);
- if (item == NULL) {
- printf("SetAccountReceiveIndex cannot get %s\r\n", chainName);
- }
-
- cJSON *recvIndex = cJSON_GetObjectItem(item, "recvIndex");
- if (recvIndex != NULL) {
- cJSON_ReplaceItemInObject(item, "recvIndex", cJSON_CreateNumber(index));
- } else {
- cJSON_AddItemToObject(item, "recvIndex", cJSON_CreateNumber(index));
- }
+ cJSON *item = GetOrCreateObjectItem(rootJson, chainName);
+ SetUintValue(item, "recvIndex", index);
if (!PassphraseExist(GetCurrentAccountIndex())) {
WriteJsonToFlash(addr, rootJson);
@@ -1731,22 +1774,14 @@ uint32_t GetAccountReceivePath(const char* chainName)
cJSON *item = cJSON_GetObjectItem(rootJson, chainName);
if (item == NULL) {
printf("GetAccountReceivePath index cannot get %s\r\n", chainName);
- printf("receive index cannot get %s\r\n", chainName);
- cJSON *jsonItem = cJSON_CreateObject();
- cJSON_AddItemToObject(jsonItem, "recvIndex", cJSON_CreateNumber(0)); // recvIndex is the address index
- cJSON_AddItemToObject(jsonItem, "recvPath", cJSON_CreateNumber(0)); // recvPath is the derivation path type
- cJSON_AddItemToObject(jsonItem, "firstRecv", cJSON_CreateBool(true)); // firstRecv is the first receive address
- if (!strcmp(chainName, "TON")) {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(true));
- } else if ((!strcmp(chainName, "BTC") || !strcmp(chainName, "ETH"))) {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(true));
- } else {
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(false));
- }
- cJSON_AddItemToObject(rootJson, chainName, jsonItem);
+ cJSON *jsonItem = GetOrCreateObjectItem(rootJson, chainName);
+ SetUintValue(jsonItem, "recvIndex", 0); // address index
+ SetUintValue(jsonItem, "recvPath", 0); // derivation path type
+ SetBoolValue(jsonItem, "firstRecv", true);
+ bool manage = (!strcmp(chainName, "TON")) || (!strcmp(chainName, "BTC")) || (!strcmp(chainName, "ETH"));
+ SetBoolValue(jsonItem, "manage", manage);
} else {
- cJSON *recvPath = cJSON_GetObjectItem(item, "recvPath");
- index = recvPath ? recvPath->valueint : 0;
+ index = GetUintValue(item, "recvPath", 0);
}
if (!PassphraseExist(GetCurrentAccountIndex())) {
cJSON_Delete(rootJson);
@@ -1765,21 +1800,15 @@ void SetAccountReceivePath(const char* chainName, uint32_t index)
if (!PassphraseExist(GetCurrentAccountIndex())) {
cJSON_Delete(rootJson);
} else {
- cJSON *jsonItem = cJSON_CreateObject();
- cJSON_AddItemToObject(jsonItem, "recvIndex", cJSON_CreateNumber(0)); // recvIndex is the address index
- cJSON_AddItemToObject(jsonItem, "recvPath", cJSON_CreateNumber(index)); // recvPath is the derivation path type
- cJSON_AddItemToObject(jsonItem, "firstRecv", cJSON_CreateBool(false)); // firstRecv is the first receive address
- cJSON_AddItemToObject(jsonItem, "manage", cJSON_CreateBool(false));
- cJSON_AddItemToObject(rootJson, chainName, jsonItem);
+ cJSON *jsonItem = GetOrCreateObjectItem(rootJson, chainName);
+ SetUintValue(jsonItem, "recvIndex", 0); // address index
+ SetUintValue(jsonItem, "recvPath", index); // derivation path type
+ SetBoolValue(jsonItem, "firstRecv", false);
+ SetBoolValue(jsonItem, "manage", false);
}
return;
}
- cJSON *recvPath = cJSON_GetObjectItem(item, "recvPath");
- if (recvPath != NULL) {
- cJSON_ReplaceItemInObject(item, "recvPath", cJSON_CreateNumber(index));
- } else {
- cJSON_AddItemToObject(item, "recvPath", cJSON_CreateNumber(index));
- }
+ SetUintValue(item, "recvPath", index);
WriteJsonToFlash(addr, rootJson);
if (!PassphraseExist(GetCurrentAccountIndex())) {
@@ -2011,6 +2040,9 @@ static cJSON* ReadAndParseAccountJson(uint32_t *outAddr, uint32_t *outSize)
rootJson = cJSON_Parse(jsonString);
SRAM_FREE(jsonString);
+ if (rootJson == NULL) {
+ rootJson = cJSON_CreateObject();
+ }
if (outAddr) *outAddr = addr;
if (outSize) *outSize = size;
@@ -2033,6 +2065,9 @@ static void WriteJsonToFlash(uint32_t addr, cJSON *rootJson)
Gd25FlashSectorErase(eraseAddr);
}
jsonString = cJSON_PrintBuffered(rootJson, SPI_FLASH_SIZE_USER1_MUTABLE_DATA - 4, false);
+ if (jsonString == NULL) {
+ return;
+ }
RemoveFormatChar(jsonString);
size = strlen(jsonString);
Gd25FlashWriteBuffer(addr, (uint8_t *)&size, 4);
diff --git a/src/crypto/secret_cache.c b/src/crypto/secret_cache.c
index fccd0c0..68ec8a6 100644
--- a/src/crypto/secret_cache.c
+++ b/src/crypto/secret_cache.c
@@ -6,6 +6,7 @@
#include "keystore.h"
#include "log_print.h"
#include "stdio.h"
+#include "account_manager.h"
static char *g_passwordCache = NULL;
static char *g_newPasswordCache = NULL;
@@ -26,7 +27,7 @@ static bool g_extendable;
void SecretCacheSetChecksum(uint8_t *checksum)
{
- memcpy(g_checksumCache, checksum, sizeof(g_checksumCache));
+ memcpy_s(g_checksumCache, sizeof(g_checksumCache), checksum, sizeof(g_checksumCache));
}
void SecretCacheGetChecksum(char *checksum)
@@ -47,10 +48,12 @@ uint8_t SecretCacheGetWalletIconIndex()
void SecretCacheSetWalletName(const char* walletName)
{
if (g_walletName) {
+ size_t oldLen = strnlen_s(g_walletName, WALLET_NAME_MAX_LEN + 1);
+ memset_s(g_walletName, WALLET_NAME_MAX_LEN + 1, 0, oldLen);
SRAM_FREE(g_walletName);
}
- g_walletName = SRAM_MALLOC(17);
- strcpy_s(g_walletName, 17, walletName);
+ g_walletName = SRAM_MALLOC(WALLET_NAME_MAX_LEN + 1);
+ strcpy_s(g_walletName, WALLET_NAME_MAX_LEN + 1, walletName);
}
char *SecretCacheGetWalletName()
@@ -61,6 +64,8 @@ char *SecretCacheGetWalletName()
void SecretCacheSetPassword(char *password)
{
if (g_passwordCache) {
+ size_t oldLen = strnlen_s(g_passwordCache, PASSWORD_MAX_LEN);
+ memset_s(g_passwordCache, PASSWORD_MAX_LEN, 0, oldLen);
SRAM_FREE(g_passwordCache);
}
size_t len = strnlen_s(password, PASSWORD_MAX_LEN) + 1;
@@ -76,6 +81,8 @@ char *SecretCacheGetPassword(void)
void SecretCacheSetPassphrase(const char *passPhrase)
{
if (g_passphraseCache) {
+ size_t oldLen = strnlen_s(g_passphraseCache, PASSPHRASE_MAX_LEN);
+ memset_s(g_passphraseCache, PASSPHRASE_MAX_LEN, 0, oldLen);
SRAM_FREE(g_passphraseCache);
}
size_t len = strnlen_s(passPhrase, PASSPHRASE_MAX_LEN) + 1;
@@ -91,10 +98,12 @@ char *SecretCacheGetPassphrase(void)
void SecretCacheSetNewPassword(char *password)
{
if (g_newPasswordCache) {
+ size_t oldLen = strnlen_s(g_newPasswordCache, PASSWORD_MAX_LEN);
+ memset_s(g_newPasswordCache, PASSWORD_MAX_LEN, 0, oldLen);
SRAM_FREE(g_newPasswordCache);
}
size_t len = strnlen_s(password, PASSWORD_MAX_LEN) + 1;
- g_passwordCache = SRAM_MALLOC(len);
+ g_newPasswordCache = SRAM_MALLOC(len);
strcpy_s(g_newPasswordCache, len, password);
}
@@ -136,11 +145,12 @@ bool SecretCacheGetExtendable(void)
void SecretCacheSetEntropy(uint8_t *entropy, uint32_t len)
{
if (g_entropyCache) {
+ memset_s(g_entropyCache, g_entropyLen, 0, g_entropyLen);
SRAM_FREE(g_entropyCache);
}
g_entropyCache = SRAM_MALLOC(len);
g_entropyLen = len;
- memcpy(g_entropyCache, entropy, len);
+ memcpy_s(g_entropyCache, len, entropy, len);
}
uint8_t *SecretCacheGetEntropy(uint32_t *len)
@@ -152,11 +162,12 @@ uint8_t *SecretCacheGetEntropy(uint32_t *len)
void SecretCacheSetEms(uint8_t *ems, uint32_t len)
{
if (g_emsCache) {
+ memset_s(g_emsCache, g_emsLen, 0, g_emsLen);
SRAM_FREE(g_emsCache);
}
g_emsCache = SRAM_MALLOC(len);
g_emsLen = len;
- memcpy(g_emsCache, ems, len);
+ memcpy_s(g_emsCache, len, ems, len);
}
uint8_t *SecretCacheGetEms(uint32_t *len)
@@ -168,6 +179,8 @@ uint8_t *SecretCacheGetEms(uint32_t *len)
void SecretCacheSetMnemonic(char *mnemonic)
{
if (g_mnemonicCache) {
+ size_t oldLen = strnlen_s(g_mnemonicCache, MNEMONIC_MAX_LEN);
+ memset_s(g_mnemonicCache, MNEMONIC_MAX_LEN, 0, oldLen);
SRAM_FREE(g_mnemonicCache);
}
g_mnemonicCache = SRAM_MALLOC(strnlen_s(mnemonic, MNEMONIC_MAX_LEN) + 1);
@@ -181,21 +194,30 @@ char *SecretCacheGetMnemonic(void)
void SecretCacheSetSlip39Mnemonic(char *mnemonic, int index)
{
+ if (index < 0 || index >= SLIP39_MAX_MEMBER) {
+ return;
+ }
if (g_slip39MnemonicCache[index] != NULL) {
+ size_t oldLen = strnlen_s(g_slip39MnemonicCache[index], MNEMONIC_MAX_LEN);
+ memset_s(g_slip39MnemonicCache[index], MNEMONIC_MAX_LEN, 0, oldLen);
EXT_FREE(g_slip39MnemonicCache[index]);
}
- g_slip39MnemonicCache[index] = EXT_MALLOC(strnlen_s(mnemonic, MNEMONIC_MAX_LEN) + 1);
- strcpy(g_slip39MnemonicCache[index], mnemonic);
+ size_t len = strnlen_s(mnemonic, MNEMONIC_MAX_LEN) + 1;
+ g_slip39MnemonicCache[index] = EXT_MALLOC(len);
+ strcpy_s(g_slip39MnemonicCache[index], len, mnemonic);
}
char *SecretCacheGetSlip39Mnemonic(int index)
{
+ if (index < 0 || index >= SLIP39_MAX_MEMBER) {
+ return NULL;
+ }
return g_slip39MnemonicCache[index];
}
void SecretCacheSetDiceRollHash(uint8_t *hash)
{
- memcpy(g_diceRollHashCache, hash, 32);
+ memcpy_s(g_diceRollHashCache, sizeof(g_diceRollHashCache), hash, 32);
}
uint8_t *SecretCacheGetDiceRollHash()
@@ -253,6 +275,8 @@ void ClearSecretCache(void)
}
if (g_walletName != NULL) {
+ size_t len = strnlen_s(g_walletName, WALLET_NAME_MAX_LEN + 1);
+ memset_s(g_walletName, WALLET_NAME_MAX_LEN + 1, 0, len);
SRAM_FREE(g_walletName);
g_walletName = NULL;
}
diff --git a/src/driver/drv_atecc608b.c b/src/driver/drv_atecc608b.c
index 409a36e..90e4227 100644
--- a/src/driver/drv_atecc608b.c
+++ b/src/driver/drv_atecc608b.c
@@ -14,6 +14,15 @@
//#define ATECC608B_TEST_MODE
+// SECURITY: Compile-time check to prevent test mode in production builds
+#if defined(ATECC608B_TEST_MODE) && defined(PRODUCTION_BUILD)
+#error "SECURITY ERROR: ATECC608B_TEST_MODE must be disabled in production builds! Hardcoded test keys pose a critical security risk."
+#endif
+
+#ifdef ATECC608B_TEST_MODE
+#warning "ATECC608B_TEST_MODE is enabled - This should ONLY be used for development/testing!"
+#endif
+
#define CHECK_ATECC608B_RET(content, ret) {if (ret != ATCA_SUCCESS) {printf("%s err,0x%X\r\n", content, ret); break; }}
static int32_t Atecc608bBinding(void);
diff --git a/src/driver/drv_ds28s60.c b/src/driver/drv_ds28s60.c
index 24676b4..040d720 100644
--- a/src/driver/drv_ds28s60.c
+++ b/src/driver/drv_ds28s60.c
@@ -21,6 +21,15 @@
//#define DS28S60_TEST_MODE
//#define DS28S60_FORCE_BINDING
+// SECURITY: Compile-time check to prevent test mode in production builds
+#if defined(DS28S60_TEST_MODE) && defined(PRODUCTION_BUILD)
+#error "SECURITY ERROR: DS28S60_TEST_MODE must be disabled in production builds! Hardcoded test keys pose a critical security risk."
+#endif
+
+#ifdef DS28S60_TEST_MODE
+#warning "DS28S60_TEST_MODE is enabled - This should ONLY be used for development/testing!"
+#endif
+
#define DS28S60_HARDWARE_EVB 0
#define DS28S60_HARDWARE_EVT0 1
@@ -117,7 +126,7 @@ static int32_t ConfirmBlockSettings(void);
static int32_t DS28S60_SendCmdAndGetResult(uint8_t cmd, uint8_t *para, uint8_t paraLen, uint8_t expectedLen, uint8_t *resultArray);
static int32_t DS28S60_TrySendCmdAndGetResult(uint8_t cmd, uint8_t *para, uint8_t paraLen, uint8_t expectedLen, uint8_t *resultArray);
static int32_t DS28S60_Binding(void);
-static int32_t DS28S60_SetProctection_From_Index(uint8_t index);
+static int32_t DS28S60_SetProtection_From_Index(uint8_t index);
static void DS28S60_PrintInfo(void);
static void GetMasterSecret(uint8_t *masterSecret);
static void GetBindingPageData(uint8_t *bindingPageData);
@@ -212,7 +221,6 @@ static void DS28S60_GetHmacKey(uint8_t *key, const DS28S60_Info_t *info, uint8_t
{
uint8_t msg[76], masterSecret[32], bindingPageData[32], partialSecret[32];
- GetMasterSecret(masterSecret);
GetBindingPageData(bindingPageData);
GetPartialSecret(partialSecret);
memcpy(&msg[0], info->ROMID, 8);
@@ -258,7 +266,7 @@ static int32_t DS28S60_WriteSecret(void)
blockProtection.Prot.b.WP = 1;
blockProtection.Prot.b.RP = 1;
ret = DS28S60_SetBlockProtection(&blockProtection, SECRET_A_BLOCK);
- CHECK_ERRCODE_BREAK("set serect block wp", ret);
+ CHECK_ERRCODE_BREAK("set secret block wp", ret);
} while (0);
CLEAR_ARRAY(sendBuf);
CLEAR_ARRAY(masterSecret);
@@ -268,7 +276,7 @@ static int32_t DS28S60_WriteSecret(void)
return ret;
}
-static int32_t DS28S60_SetProctection_From_Index(uint8_t index)
+static int32_t DS28S60_SetProtection_From_Index(uint8_t index)
{
int32_t ret;
uint8_t block;
@@ -293,7 +301,7 @@ static int32_t DS28S60_SetProctection_From_Index(uint8_t index)
static int32_t DS28S60_Setup(void)
{
- return DS28S60_SetProctection_From_Index(0);
+ return DS28S60_SetProtection_From_Index(0);
}
static int32_t ConfirmBlockSettings(void)
@@ -330,7 +338,7 @@ static int32_t ConfirmBlockSettings(void)
right = mid;
}
}
- ret = DS28S60_SetProctection_From_Index(left);
+ ret = DS28S60_SetProtection_From_Index(left);
}
} while (0);
diff --git a/src/managers/keystore.c b/src/managers/keystore.c
index 2425679..fab66b7 100644
--- a/src/managers/keystore.c
+++ b/src/managers/keystore.c
@@ -435,7 +435,7 @@ bool PassphraseExist(uint8_t accountIndex)
char *GetPassphrase(uint8_t accountIndex)
{
if (accountIndex > 2) {
- return false;
+ return NULL;
}
return g_passphraseInfo[accountIndex].passphrase;
diff --git a/src/ui/gui_chain/gui_btc.c b/src/ui/gui_chain/gui_btc.c
index 40370f2..f20c79d 100644
--- a/src/ui/gui_chain/gui_btc.c
+++ b/src/ui/gui_chain/gui_btc.c
@@ -154,7 +154,8 @@ UREncodeResult *GuiGetBtcSignUrDataUnlimited(void)
return GetBtcSignDataDynamic(true);
}
-static UREncodeResult *BtcSignPsbt(void *data, uint8_t *seed, int len, uint8_t *mfp, bool unLimit) {
+static UREncodeResult *BtcSignPsbt(void *data, uint8_t *seed, int len, uint8_t *mfp, bool unLimit)
+{
UREncodeResult *encodeResult = NULL;
if (GuiGetCurrentTransactionNeedSign()) {
if (unLimit) {
@@ -166,7 +167,8 @@ static UREncodeResult *BtcSignPsbt(void *data, uint8_t *seed, int len, uint8_t *
return encodeResult;
}
-static UREncodeResult *BtcSignPsbtMultisig(void *data, uint8_t *seed, int len, uint8_t *mfp) {
+static UREncodeResult *BtcSignPsbtMultisig(void *data, uint8_t *seed, int len, uint8_t *mfp)
+{
#ifdef BTC_ONLY
UREncodeResult *encodeResult = NULL;
if (!GuiGetCurrentTransactionNeedSign()) {
@@ -183,7 +185,8 @@ static UREncodeResult *BtcSignPsbtMultisig(void *data, uint8_t *seed, int len, u
#endif
}
-static bool SupportSignPsbtFromSDCard(void) {
+static bool SupportSignPsbtFromSDCard(void)
+{
#ifdef BTC_ONLY
return true;
#else
@@ -191,7 +194,8 @@ static bool SupportSignPsbtFromSDCard(void) {
#endif
}
-static bool SupportSignLegacyKeystoneTransactions(QRCodeType urType) {
+static bool SupportSignLegacyKeystoneTransactions(QRCodeType urType)
+{
#ifdef WEB3_VERSION
return (urType == Bytes || urType == KeystoneSignRequest);
#else
@@ -199,7 +203,8 @@ static bool SupportSignLegacyKeystoneTransactions(QRCodeType urType) {
#endif
}
-static bool SupportSignPsbtExtend(QRCodeType urType) {
+static bool SupportSignPsbtExtend(QRCodeType urType)
+{
#ifdef WEB3_VERSION
return (urType == CryptoPSBTExtend);
#else
@@ -241,16 +246,14 @@ static UREncodeResult *GetBtcSignDataDynamic(bool unLimit)
} else {
encodeResult = BtcSignPsbt(data, seed, len, mfp, unLimit);
}
- }
- else if (SupportSignLegacyKeystoneTransactions(urType)) {
+ } else if (SupportSignLegacyKeystoneTransactions(urType)) {
char *hdPath = NULL;
char *xPub = NULL;
if (0 != GuiGetUtxoPubKeyAndHdPath(viewType, &xPub, &hdPath)) {
return NULL;
}
encodeResult = utxo_sign_keystone(data, urType, mfp, sizeof(mfp), xPub, SOFTWARE_VERSION, seed, len);
- }
- else if (urType == BtcSignRequest) {
+ } else if (urType == BtcSignRequest) {
encodeResult = btc_sign_msg(data, seed, len, mfp, sizeof(mfp));
} else if (urType == SeedSignerMessage) {
encodeResult = sign_seed_signer_message(data, seed, len);
@@ -330,7 +333,8 @@ static void *GuiGetParsedPsbtStrData(void)
#endif
-static void PreparePublicKeys(PtrT_CSliceFFI_ExtendedPublicKey public_keys) {
+static void PreparePublicKeys(PtrT_CSliceFFI_ExtendedPublicKey public_keys)
+{
#ifdef BTC_ONLY
ExtendedPublicKey keys[14];
public_keys->data = keys;
@@ -393,7 +397,8 @@ static void PreparePublicKeys(PtrT_CSliceFFI_ExtendedPublicKey public_keys) {
#endif
}
-static void *ParsePsbt(void *crypto, uint8_t *mfp, PtrT_CSliceFFI_ExtendedPublicKey public_keys) {
+static void *ParsePsbt(void *crypto, uint8_t *mfp, PtrT_CSliceFFI_ExtendedPublicKey public_keys)
+{
g_parseResult = NULL;
#ifdef BTC_ONLY
char *wallet_config = NULL;
@@ -443,8 +448,7 @@ void *GuiGetParsedQrData(void)
GuiSetCurrentTransactionType(TRANSACTION_TYPE_BTC_MULTISIG);
}
return g_parseResult;
- }
- else if (SupportSignLegacyKeystoneTransactions(urType)) {
+ } else if (SupportSignLegacyKeystoneTransactions(urType)) {
char *hdPath = NULL;
char *xPub = NULL;
if (0 != GuiGetUtxoPubKeyAndHdPath(viewType, &xPub, &hdPath)) {
@@ -519,7 +523,8 @@ static PtrT_TransactionCheckResult GuiGetPsbtStrCheckResult(void)
}
#endif
-static PtrT_TransactionCheckResult CheckPsbt(void *crypto, uint8_t *mfp, PtrT_CSliceFFI_ExtendedPublicKey public_keys) {
+static PtrT_TransactionCheckResult CheckPsbt(void *crypto, uint8_t *mfp, PtrT_CSliceFFI_ExtendedPublicKey public_keys)
+{
PtrT_TransactionCheckResult result = NULL;
#ifdef BTC_ONLY
char *verify_without_mfp = NULL;
@@ -579,16 +584,14 @@ PtrT_TransactionCheckResult GuiGetPsbtCheckResult(void)
PreparePublicKeys(public_keys);
result = CheckPsbt(crypto, mfp, public_keys);
SRAM_FREE(public_keys);
- }
- else if (SupportSignLegacyKeystoneTransactions(urType)) {
+ } else if (SupportSignLegacyKeystoneTransactions(urType)) {
char *hdPath = NULL;
char *xPub = NULL;
if (0 != GuiGetUtxoPubKeyAndHdPath(viewType, &xPub, &hdPath)) {
return NULL;
}
result = utxo_check_keystone(crypto, urType, mfp, sizeof(mfp), xPub);
- }
- else if (urType == BtcSignRequest) {
+ } else if (urType == BtcSignRequest) {
result = btc_check_msg(crypto, mfp, sizeof(mfp));
} else if (urType == SeedSignerMessage) {
result = tx_check_pass();
diff --git a/src/utils/user_utils.c b/src/utils/user_utils.c
index 052ca9b..b24ed63 100644
--- a/src/utils/user_utils.c
+++ b/src/utils/user_utils.c
@@ -247,6 +247,52 @@ bool GetBoolValue(const cJSON *obj, const char *key, bool defaultValue)
return defaultValue;
}
+uint32_t GetUintValue(const cJSON *obj, const char *key, uint32_t defaultValue)
+{
+ cJSON *numJson = cJSON_GetObjectItem((cJSON *)obj, key);
+ if (numJson != NULL) {
+ return (uint32_t)numJson->valuedouble;
+ }
+ printf("key:%s does not exist\r\n", key);
+ return defaultValue;
+}
+
+void SetUintValue(cJSON *obj, const char *key, uint32_t value)
+{
+ if (obj == NULL) {
+ return;
+ }
+ cJSON *numJson = cJSON_GetObjectItem(obj, key);
+ if (numJson != NULL) {
+ cJSON_SetNumberValue(numJson, value);
+ } else {
+ cJSON_AddNumberToObject(obj, key, value);
+ }
+}
+
+void SetBoolValue(cJSON *obj, const char *key, bool value)
+{
+ if (obj == NULL) {
+ return;
+ }
+ cJSON *boolJson = cJSON_GetObjectItem(obj, key);
+ if (boolJson != NULL) {
+ cJSON_ReplaceItemInObject(obj, key, cJSON_CreateBool(value));
+ } else {
+ cJSON_AddItemToObject(obj, key, cJSON_CreateBool(value));
+ }
+}
+
+cJSON* GetOrCreateObjectItem(cJSON *root, const char *key)
+{
+ cJSON *item = cJSON_GetObjectItem(root, key);
+ if (item == NULL) {
+ item = cJSON_CreateObject();
+ cJSON_AddItemToObject(root, key, item);
+ }
+ return item;
+}
+
void CutAndFormatFileName(char *out, uint32_t maxLen, const char *fileName, const char *contain)
{
if (strlen(fileName) >= 20 + strlen(contain)) {
@@ -283,4 +329,4 @@ void insert_16bit_value(uint8_t *frame, int offset, uint16_t value)
{
frame[offset] = (uint8_t)(value >> 8);
frame[offset + 1] = (uint8_t)(value & 0xFF);
-}
+}
\ No newline at end of file
diff --git a/src/utils/user_utils.h b/src/utils/user_utils.h
index 0fed85e..74d0f2d 100644
--- a/src/utils/user_utils.h
+++ b/src/utils/user_utils.h
@@ -27,9 +27,14 @@ int FindStringCharPosition(const char *str, const char destChar, int index);
int32_t GetIntValue(const cJSON *obj, const char *key, int32_t defaultValue);
void GetStringValue(const cJSON *obj, const char *key, char *value, uint32_t maxLen);
bool GetBoolValue(const cJSON *obj, const char *key, bool defaultValue);
+uint32_t GetUintValue(const cJSON *obj, const char *key, uint32_t defaultValue);
+void SetUintValue(cJSON *obj, const char *key, uint32_t value);
+void SetBoolValue(cJSON *obj, const char *key, bool value);
+cJSON* GetOrCreateObjectItem(cJSON *root, const char *key);
void CutAndFormatString(char *out, uint32_t maxLen, const char *string, uint32_t targetLen);
void CutAndFormatFileName(char *out, uint32_t maxLen, const char *fileName, const char *contain);
uint16_t extract_16bit_value(const uint8_t *frame, int offset);
void insert_16bit_value(uint8_t *frame, int offset, uint16_t value);
+void ReplaceStringInBuffer(char *str, const char *old_str, const char *new_str);
#endif /* _USER_UTILS_H */
Why this scored 61/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.