What changed, and why it matters
This firmware update changes how the Keystone hardware wallet stores and checks device passwords. Previously, the device kept a separate password hash page in its secure element (SE) storage. After this update, password verification is done by trying to decrypt the actual account secret using the password, and the old password hash pages are wiped. The change also removes hard assertions on SE operations so that failures return error codes instead of crashing, and it maps a specific secure-element authentication failure to a wrong-password error. The version number is bumped from 12.4.6 to 12.5.0.
Treat this as a security-hardening and potential security-fix release. Users should upgrade to firmware 12.5.0 when available. Developers should verify that the legacy password hash wipe does not brick devices with partially written SE pages and that the new authentication error mapping does not leak information through timing or error counts.
Security signals we found
Removal of stored password hash page and migration to secret-decryption password verification
Addition of legacy password hash wipe routine on boot
SE error codes propagated instead of asserted, improving fault handling
ATCA_CHECKMAC_VERIFY_FAILED mapped to authentication error
UI loading overlay added during password verification to prevent repeated UI-driven attempts
Evidence from the diff
The commit refactors password verification to use the SE KDF/decryption path as the password oracle rather than a stored salted hash. It renames PAGE_INDEX_PASSWORD_HASH to PAGE_INDEX_LEGACY_PASSWORD_HASH, adds WipeLegacyPasswordHashPages() called during AccountManagerInit(), and introduces FindAccountByPassword()/VerifyAccountPassword() which call LoadAccountSecret() and translate ERR_KEYSTORE_AUTH to ERR_KEYSTORE_PASSWORD_ERR. SE_Interface.c now returns underlying error codes instead of asserting success. se_manager.c maps ATCA_CHECKMAC_VERIFY_FAILED to ERR_KEYSTORE_AUTH. UI changes add a verification loading overlay during PIN/password checks and ensure it is hidden on various lock-screen state transitions.
Changed components
src/managers/account_manager.csrc/managers/keystore.csrc/managers/se_manager.csrc/hardware_interface/se_interface.csrc/ui/gui_widgets/gui_lock_widgets.csrc/ui/gui_widgets/gui_enter_passcode.csrc/ui/gui_views/gui_lock_view.cInspect captured patch +279 / −125
diff --git a/src/config/version.h b/src/config/version.h
index 4458ec5..8e122b0 100644
--- a/src/config/version.h
+++ b/src/config/version.h
@@ -6,8 +6,8 @@
#define SOFTWARE_VERSION_MAX_LEN (32)
#define SOFTWARE_VERSION_MAJOR 12
#define SOFTWARE_VERSION_MAJOR_OFFSET 10
-#define SOFTWARE_VERSION_MINOR 4
-#define SOFTWARE_VERSION_BUILD 6
+#define SOFTWARE_VERSION_MINOR 5
+#define SOFTWARE_VERSION_BUILD 0
#define SOFTWARE_VERSION_BETA 1
#define SOFTWARE_VERSION (SOFTWARE_VERSION_MAJOR * 10000 + SOFTWARE_VERSION_MINOR * 100 + SOFTWARE_VERSION_BUILD)
#ifdef WEB3_VERSION
@@ -36,4 +36,3 @@ void GetBootVersionNumber(char *version);
bool NeedUpdateBoot(void);
#endif
-
diff --git a/src/crypto/utils/pbkdf2.h b/src/crypto/utils/pbkdf2.h
index 87ce04f..5120c06 100644
--- a/src/crypto/utils/pbkdf2.h
+++ b/src/crypto/utils/pbkdf2.h
@@ -4,6 +4,19 @@
#include "stdlib.h"
#include "stdint.h"
+/**
+ * Derive a pseudorandom key from inputs using HMAC SHA-256.
+ */
+int pbkdf2_hmac_sha256(
+ const unsigned char *pass,
+ size_t pass_len,
+ const unsigned char *salt,
+ size_t salt_len,
+ uint32_t flags,
+ uint32_t cost,
+ unsigned char *bytes_out,
+ size_t len);
+
/**
* Derive a pseudorandom key from inputs using HMAC SHA-512.
*
diff --git a/src/hardware_interface/se_interface.c b/src/hardware_interface/se_interface.c
index 6c9da3b..c846189 100644
--- a/src/hardware_interface/se_interface.c
+++ b/src/hardware_interface/se_interface.c
@@ -11,22 +11,19 @@
int32_t SE_EncryptWrite(uint8_t slot, uint8_t block, const uint8_t *data)
{
int32_t ret = Atecc608bEncryptWrite(slot, block, data);
- ASSERT(ret == ATCA_SUCCESS);
- return SUCCESS_CODE;
+ return ret;
}
int32_t SE_Kdf(uint8_t slot, const uint8_t *authKey, const uint8_t *inData, uint32_t inLen, uint8_t *outData)
{
int32_t ret = Atecc608bKdf(slot, authKey, inData, inLen, outData);
- ASSERT(ret == ATCA_SUCCESS);
- return SUCCESS_CODE;
+ return ret;
}
int32_t SE_DeriveKey(uint8_t slot, const uint8_t *authKey)
{
int32_t ret = Atecc608bDeriveKey(slot, authKey);
- ASSERT(ret == ATCA_SUCCESS);
- return SUCCESS_CODE;
+ return ret;
}
//END
@@ -34,8 +31,7 @@ int32_t SE_DeriveKey(uint8_t slot, const uint8_t *authKey)
int32_t SE_HmacEncryptRead(uint8_t *data, uint8_t page)
{
int32_t ret = DS28S60_HmacEncryptRead(data, page);
- ASSERT(ret == DS28S60_SUCCESS);
- return SUCCESS_CODE;
+ return ret;
}
int32_t SE_GetDS28S60Rng(uint8_t *rngArray, uint32_t num)
@@ -60,8 +56,7 @@ int32_t SE_GetAtecc608bRng(uint8_t *rngArray, uint32_t num)
int32_t SE_HmacEncryptWrite(const uint8_t *data, uint8_t page)
{
int32_t ret = DS28S60_HmacEncryptWrite(data, page);
- ASSERT(ret == DS28S60_SUCCESS);
- return SUCCESS_CODE;
+ return ret;
}
//END
diff --git a/src/managers/account_manager.c b/src/managers/account_manager.c
index f11caad..5da84b7 100644
--- a/src/managers/account_manager.c
+++ b/src/managers/account_manager.c
@@ -6,7 +6,6 @@
#include "user_utils.h"
#include "account_public_info.h"
#include "assert.h"
-#include "hash_and_salt.h"
#include "secret_cache.h"
#include "log_print.h"
#include "user_memory.h"
@@ -19,12 +18,15 @@
#include "safe_str_lib.h"
#endif
+#define PIN_HASH_WIPED_MAGIC 0xA5
+
typedef struct {
uint8_t loginPasswordErrorCount;
uint8_t currentPasswordErrorCount;
uint8_t reserved1[2];
uint32_t lastLockDeviceTime;
- uint8_t reserved2[24]; //byte 1~31 reserved.
+ uint8_t pinHashWiped; //byte 8, PIN_HASH_WIPED_MAGIC when legacy hash pages are wiped.
+ uint8_t reserved2[23]; //byte 9~31 reserved.
} PublicInfo_t;
static uint8_t g_currentAccountIndex = ACCOUNT_INDEX_LOGOUT;
@@ -37,6 +39,27 @@ static ZcashUFVKCache_t g_zcashUFVKcache = {0};
static void ClearZcashUFVK();
#endif
+static int32_t WipeLegacyPasswordHashPages(void)
+{
+ uint8_t data[32] = {0};
+ int32_t ret = SUCCESS_CODE;
+
+ if (IsPinHashWiped()) {
+ return SUCCESS_CODE;
+ }
+
+ // Erase old PIN verifiers. Do not add normal read/write users for this page.
+ for (uint8_t accountIndex = 0; accountIndex < 3; accountIndex++) {
+ ret = SE_HmacEncryptWrite(data, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_LEGACY_PASSWORD_HASH);
+ CHECK_ERRCODE_BREAK("wipe legacy password hash", ret);
+ }
+ if (ret == SUCCESS_CODE) {
+ ret = SetPinHashWiped(true);
+ }
+ CLEAR_ARRAY(data);
+ return ret;
+}
+
/// @brief Get current account info from SE, and copy info to g_currentAccountInfo.
/// @return err code.
static int32_t ReadCurrentAccountInfo(void)
@@ -62,6 +85,8 @@ int32_t AccountManagerInit(void)
ASSERT(sizeof(AccountInfo_t) == 32);
ASSERT(sizeof(PublicInfo_t) == 32);
ret = SE_HmacEncryptRead((uint8_t *)&g_publicInfo, PAGE_PUBLIC_INFO);
+ CHECK_ERRCODE_RETURN_INT(ret);
+ ret = WipeLegacyPasswordHashPages();
return ret;
}
@@ -186,7 +211,7 @@ int32_t CreateNewSlip39Account(uint8_t accountIndex, const uint8_t *ems, const u
/// @return err code.
int32_t VerifyCurrentAccountPassword(const char *password)
{
- uint8_t accountIndex, passwordHashClac[32], passwordHashStore[32];
+ uint8_t accountIndex;
int32_t ret;
do {
@@ -195,26 +220,22 @@ int32_t VerifyCurrentAccountPassword(const char *password)
ret = ERR_KEYSTORE_NOT_LOGIN;
break;
}
-#ifdef COMPILE_SIMULATOR
+ #ifdef COMPILE_SIMULATOR
ret = SimulatorVerifyCurrentPassword(accountIndex, password);
-#else
- ret = SE_HmacEncryptRead(passwordHashStore, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("read password hash", ret);
- HashWithSalt(passwordHashClac, (const uint8_t *)password, strlen(password), "password hash");
- ret = memcmp(passwordHashStore, passwordHashClac, 32);
-#endif
+ #else
+ ret = VerifyAccountPassword(accountIndex, password);
+ #endif
if (ret == SUCCESS_CODE) {
g_publicInfo.currentPasswordErrorCount = 0;
- } else {
+ } else if (ret == ERR_KEYSTORE_PASSWORD_ERR) {
g_publicInfo.currentPasswordErrorCount++;
printf("password error count=%d\r\n", g_publicInfo.currentPasswordErrorCount);
- ret = ERR_KEYSTORE_PASSWORD_ERR;
+ } else {
+ break;
}
SE_HmacEncryptWrite((uint8_t *)&g_publicInfo, PAGE_PUBLIC_INFO);
} while (0);
- CLEAR_ARRAY(passwordHashStore);
- CLEAR_ARRAY(passwordHashClac);
return ret;
}
@@ -227,8 +248,8 @@ int32_t ClearCurrentPasswordErrorCount(void)
return SUCCESS_CODE;
}
-/// @brief Verify password, if password verify success, set current account id. PasswordErrorCount++ if err.
-/// @param[out] accountIndex If password verify success, account index would be set here. Can be NULL if not needed.
+/// @brief Find account by password, if success set current account id. PasswordErrorCount++ if err.
+/// @param[out] accountIndex If password verify success, matched account index would be set here. Can be NULL if not needed.
/// @param password Password string.
/// @return err code.
int32_t VerifyPasswordAndLogin(uint8_t *accountIndex, const char *password)
@@ -236,7 +257,7 @@ int32_t VerifyPasswordAndLogin(uint8_t *accountIndex, const char *password)
int32_t ret;
uint8_t tempIndex;
- ret = VerifyPassword(&tempIndex, password);
+ ret = FindAccountByPassword(&tempIndex, password);
if (ret == SUCCESS_CODE) {
g_currentAccountIndex = tempIndex;
g_lastAccountIndex = tempIndex;
@@ -281,6 +302,11 @@ uint8_t GetCurrentAccountIndex(void)
return g_currentAccountIndex;
}
+uint8_t GetLastAccountIndex(void)
+{
+ return g_lastAccountIndex;
+}
+
/// @brief Set last account index.
void SetCurrentAccountIndex(void)
{
@@ -337,10 +363,8 @@ void LogoutCurrentAccount(void)
/// @return err code.
int32_t GetAccountInfo(uint8_t accountIndex, AccountInfo_t *pInfo)
{
- int32_t ret;
ASSERT(accountIndex <= 2);
- ret = SE_HmacEncryptRead((uint8_t *)pInfo, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PARAM);
- return ret;
+ return SE_HmacEncryptRead((uint8_t *)pInfo, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PARAM);
}
/// @brief Erase public info in SE.
@@ -351,6 +375,24 @@ int32_t ErasePublicInfo(void)
return SE_HmacEncryptWrite((uint8_t *)&g_publicInfo, PAGE_PUBLIC_INFO);
}
+bool IsPinHashWiped(void)
+{
+ return g_publicInfo.pinHashWiped == PIN_HASH_WIPED_MAGIC;
+}
+
+int32_t SetPinHashWiped(bool wiped)
+{
+ uint8_t oldPinHashWiped = g_publicInfo.pinHashWiped;
+ int32_t ret;
+
+ g_publicInfo.pinHashWiped = wiped ? PIN_HASH_WIPED_MAGIC : 0;
+ ret = SE_HmacEncryptWrite((uint8_t *)&g_publicInfo, PAGE_PUBLIC_INFO);
+ if (ret != SUCCESS_CODE) {
+ g_publicInfo.pinHashWiped = oldPinHashWiped;
+ }
+ return ret;
+}
+
/// @brief Get slip39 idrandom identifier of the current account.
/// @return idrandom identifier.
uint16_t GetSlip39Id(void)
@@ -559,30 +601,13 @@ int32_t DestroyAccount(uint8_t accountIndex)
void AccountsDataCheck(void)
{
int32_t ret;
- uint8_t data[32], accountIndex, validCount, i;
+ uint8_t data[32], accountIndex;
for (accountIndex = 0; accountIndex < 3; accountIndex++) {
- validCount = 0;
ret = SE_HmacEncryptRead(data, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_IV);
CHECK_ERRCODE_BREAK("read iv", ret);
- if (CheckEntropy(data, 32)) {
- validCount++;
- }
- ret = SE_HmacEncryptRead(data, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("read pwd hash", ret);
- if (CheckEntropy(data, 32)) {
- validCount++;
- }
- if (validCount == 1) {
- printf("illegal data:%d\n", accountIndex);
- memset_s(data, sizeof(data), 0, sizeof(data));
- for (i = 0; i < PAGE_NUM_PER_ACCOUNT; i++) {
- printf("erase index=%d\n", i);
- ret = SE_HmacEncryptWrite(data, accountIndex * PAGE_NUM_PER_ACCOUNT + i);
- CHECK_ERRCODE_BREAK("ds28s60 write", ret);
- }
- }
}
+ CLEAR_ARRAY(data);
}
#ifndef BTC_ONLY
diff --git a/src/managers/account_manager.h b/src/managers/account_manager.h
index aaac443..97bb87b 100644
--- a/src/managers/account_manager.h
+++ b/src/managers/account_manager.h
@@ -67,6 +67,7 @@ int32_t VerifyCurrentAccountPassword(const char *password);
int32_t VerifyPasswordAndLogin(uint8_t *accountIndex, const char *password);
void LogoutCurrentAccount(void);
uint8_t GetCurrentAccountIndex(void);
+uint8_t GetLastAccountIndex(void);
void SetCurrentAccountIndex(void);
int32_t GetExistAccountNum(uint8_t *accountNum);
int32_t GetBlankAccountIndex(uint8_t *accountIndex);
@@ -88,6 +89,8 @@ uint32_t GetLastLockDeviceTime(void);
void SetLastLockDeviceTime(uint32_t timeStamp);
uint32_t GetCurrentAccountEntropyLen(void);
uint32_t GetCurrentAccountSeedLen(void);
+bool IsPinHashWiped(void);
+int32_t SetPinHashWiped(bool wiped);
uint8_t *GetCurrentAccountMfp(void);
int32_t GetAccountInfo(uint8_t accountIndex, AccountInfo_t *pInfo);
@@ -110,4 +113,4 @@ int32_t SetupZcashSFP(uint8_t accountIndex, const char* password);
int32_t SetupZcashCache(uint8_t accountIndex, const char* password);
#endif
#endif
-#endif
\ No newline at end of file
+#endif
diff --git a/src/managers/keystore.c b/src/managers/keystore.c
index 8f46123..94cf7be 100644
--- a/src/managers/keystore.c
+++ b/src/managers/keystore.c
@@ -15,6 +15,7 @@
#include "log_print.h"
#include "bip39.h"
#include "slip39.h"
+#include "memzero.h"
#include "user_memory.h"
#include "drv_otp.h"
#include "librust_c.h"
@@ -44,10 +45,28 @@ static PassphraseInfo_t g_passphraseInfo[3] = {0};
static int32_t SaveAccountSecret(uint8_t accountIndex, const AccountSecret_t *accountSecret, const char *password, bool newAccount);
static int32_t LoadAccountSecret(uint8_t accountIndex, AccountSecret_t *accountSecret, const char *password);
+#ifndef COMPILE_SIMULATOR
+static int32_t LoadAccountSecretFromSE(uint8_t accountIndex, AccountSecret_t *accountSecret, const char *password);
+#endif
+static int32_t AccountExists(uint8_t accountIndex, bool *exists);
static void CombineInnerAesKey(uint8_t *aesKey);
static int32_t GetPassphraseSeed(uint8_t accountIndex, uint8_t *seed, const char *passphrase, const char *password);
+static int32_t AccountExists(uint8_t accountIndex, bool *exists)
+{
+ uint8_t iv[32];
+ int32_t ret;
+
+ ASSERT(accountIndex <= 2);
+ ret = SE_HmacEncryptRead(iv, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_IV);
+ if (ret == SUCCESS_CODE) {
+ *exists = CheckEntropy(iv, sizeof(iv));
+ }
+ CLEAR_ARRAY(iv);
+ return ret;
+}
+
/// @brief Generate 32 byte entropy from SE and mcu TRNG.
/// @param[out] entropy
/// @param[in] entropyLen
@@ -103,7 +122,6 @@ int32_t SaveNewBip39Entropy(uint8_t accountIndex, const uint8_t *entropy, uint8_
int32_t ret;
AccountSecret_t accountSecret = {0};
char *mnemonic = NULL;
- uint8_t passwordHash[32];
ASSERT(accountIndex <= 2);
do {
@@ -122,9 +140,6 @@ int32_t SaveNewBip39Entropy(uint8_t accountIndex, const uint8_t *entropy, uint8_
ret = SaveAccountSecret(accountIndex, &accountSecret, password, true);
CHECK_ERRCODE_BREAK("SaveAccountSecret", ret);
- HashWithSalt(passwordHash, (const uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "password hash");
- ret = SE_HmacEncryptWrite(passwordHash, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("write password hash", ret);
} while (0);
@@ -133,7 +148,6 @@ int32_t SaveNewBip39Entropy(uint8_t accountIndex, const uint8_t *entropy, uint8_
SRAM_FREE(mnemonic);
}
- CLEAR_ARRAY(passwordHash);
CLEAR_OBJECT(accountSecret);
ASSERT(ret == SUCCESS_CODE);
return ret;
@@ -151,7 +165,6 @@ int32_t SaveNewSlip39Entropy(uint8_t accountIndex, const uint8_t *ems, const uin
{
int32_t ret;
AccountSecret_t accountSecret = {0};
- uint8_t passwordHash[32];
ASSERT(accountIndex <= 2);
do {
@@ -165,13 +178,9 @@ int32_t SaveNewSlip39Entropy(uint8_t accountIndex, const uint8_t *ems, const uin
memcpy_s(accountSecret.slip39EmsOrTonEntropyL32, sizeof(accountSecret.slip39EmsOrTonEntropyL32), ems, entropyLen);
ret = SaveAccountSecret(accountIndex, &accountSecret, password, true);
CHECK_ERRCODE_BREAK("SaveAccountSecret", ret);
- HashWithSalt(passwordHash, (const uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "password hash");
- ret = SE_HmacEncryptWrite(passwordHash, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("write password hash", ret);
} while (0);
- CLEAR_ARRAY(passwordHash);
CLEAR_OBJECT(accountSecret);
ASSERT(ret == SUCCESS_CODE);
return ret;
@@ -266,7 +275,6 @@ int32_t ChangePassword(uint8_t accountIndex, const char *newPassword, const char
{
int32_t ret;
AccountSecret_t accountSecret;
- uint8_t passwordHash[32];
ASSERT(accountIndex <= 2);
do {
@@ -276,61 +284,106 @@ int32_t ChangePassword(uint8_t accountIndex, const char *newPassword, const char
CHECK_ERRCODE_BREAK("load account secret", ret);
ret = SaveAccountSecret(accountIndex, &accountSecret, newPassword, false);
CHECK_ERRCODE_BREAK("save account secret", ret);
- HashWithSalt(passwordHash, (const uint8_t *)newPassword, strnlen_s(newPassword, PASSWORD_MAX_LEN), "password hash");
- ret = SE_HmacEncryptWrite(passwordHash, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("write password hash", ret);
} while (0);
CLEAR_OBJECT(accountSecret);
return ret;
}
-/// @brief Verify password.
-/// @param[out] accountIndex If password verify success, account index would be set here. Can be NULL if not needed.
+/// @brief Find the existing account that can be unlocked by password.
+/// @param[out] matchedAccountIndex If password verify success, matched account index would be set here. Can be NULL if not needed.
/// @param password Password string.
/// @return err code.
-int32_t VerifyPassword(uint8_t *accountIndex, const char *password)
+int32_t FindAccountByPassword(uint8_t *matchedAccountIndex, const char *password)
{
- uint8_t passwordHashClac[32], passwordHashStore[32];
- int32_t ret, i;
+ AccountSecret_t accountSecret;
+ uint8_t tryOrder[3];
+ uint8_t lastAccountIndex;
+ bool exists;
+ int32_t ret = ERR_KEYSTORE_PASSWORD_ERR;
+ uint8_t tryCount = 0;
#ifdef COMPILE_SIMULATOR
- return SimulatorVerifyPassword(accountIndex, password);
+ return SimulatorVerifyPassword(matchedAccountIndex, password);
#endif
- for (i = 0; i < 3; i++) {
- ret = SE_HmacEncryptRead(passwordHashStore, i * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH);
- CHECK_ERRCODE_BREAK("read password hash", ret);
- HashWithSalt(passwordHashClac, (const uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "password hash");
- if (memcmp(passwordHashStore, passwordHashClac, 32) == 0) {
- if (accountIndex != NULL) {
- *accountIndex = i;
+ lastAccountIndex = GetLastAccountIndex();
+ if (lastAccountIndex <= 2) {
+ tryOrder[tryCount++] = lastAccountIndex;
+ }
+ for (uint8_t i = 0; i < 3; i++) {
+ if (i != lastAccountIndex) {
+ tryOrder[tryCount++] = i;
+ }
+ }
+
+ for (uint8_t i = 0; i < tryCount; i++) {
+ exists = false;
+ ret = AccountExists(tryOrder[i], &exists);
+ CHECK_ERRCODE_BREAK("check account exists", ret);
+ if (!exists) {
+ ret = ERR_KEYSTORE_PASSWORD_ERR;
+ continue;
+ }
+
+ ret = LoadAccountSecret(tryOrder[i], &accountSecret, password);
+ CLEAR_OBJECT(accountSecret);
+ if (ret == SUCCESS_CODE) {
+ if (matchedAccountIndex != NULL) {
+ *matchedAccountIndex = tryOrder[i];
}
- ret = SUCCESS_CODE;
break;
- } else {
+ }
+ if (ret == ERR_KEYSTORE_AUTH) {
ret = ERR_KEYSTORE_PASSWORD_ERR;
+ continue;
}
+ break;
}
- CLEAR_ARRAY(passwordHashStore);
- CLEAR_ARRAY(passwordHashClac);
return ret;
}
+int32_t VerifyAccountPassword(uint8_t accountIndex, const char *password)
+{
+ AccountSecret_t accountSecret;
+ int32_t ret;
+
+ ASSERT(accountIndex <= 2);
+ ret = LoadAccountSecret(accountIndex, &accountSecret, password);
+ CLEAR_OBJECT(accountSecret);
+ return (ret == ERR_KEYSTORE_AUTH) ? ERR_KEYSTORE_PASSWORD_ERR : ret;
+}
+
/// @brief Check if password repeat with existing others.
/// @param[in] password Password string.
/// @param[in] excludeIndex exclude account index, if do not need exclude any account, set excludeIndex to 255.
/// @return err code.
int32_t CheckPasswordExisted(const char *password, uint8_t excludeIndex)
{
- int32_t ret;
- uint8_t accountIndex;
-
- ret = VerifyPassword(&accountIndex, password);
- if (ret == SUCCESS_CODE && excludeIndex != accountIndex) {
- // password existed
- ret = ERR_KEYSTORE_REPEAT_PASSWORD;
- } else if (ret == ERR_KEYSTORE_PASSWORD_ERR) {
- ret = SUCCESS_CODE;
+ int32_t ret = SUCCESS_CODE;
+ bool exists;
+
+ for (uint8_t accountIndex = 0; accountIndex < 3; accountIndex++) {
+ if (excludeIndex == accountIndex) {
+ continue;
+ }
+
+ exists = false;
+ ret = AccountExists(accountIndex, &exists);
+ CHECK_ERRCODE_BREAK("check account exists", ret);
+ if (!exists) {
+ continue;
+ }
+
+ ret = VerifyAccountPassword(accountIndex, password);
+ if (ret == SUCCESS_CODE) {
+ ret = ERR_KEYSTORE_REPEAT_PASSWORD;
+ break;
+ }
+ if (ret == ERR_KEYSTORE_PASSWORD_ERR) {
+ ret = SUCCESS_CODE;
+ continue;
+ }
+ break;
}
return ret;
}
@@ -550,22 +603,19 @@ static int32_t SaveAccountSecret(uint8_t accountIndex, const AccountSecret_t *ac
return ret;
}
-/// @brief Load account secret, including entropy/seed/reservedData.
+/// @brief Load account secret with the SE PIN gate.
/// @param[in] accountIndex Account index, 0~2.
/// @param[out] accountSecret Account secret data.
/// @param[in] password Password string.
/// @return err code.
-static int32_t LoadAccountSecret(uint8_t accountIndex, AccountSecret_t *accountSecret, const char *password)
+#ifndef COMPILE_SIMULATOR
+static int32_t LoadAccountSecretFromSE(uint8_t accountIndex, AccountSecret_t *accountSecret, const char *password)
{
-#ifdef COMPILE_SIMULATOR
- return SimulatorLoadAccountSecret(accountIndex, accountSecret, password);
-#endif
uint8_t pieces[KEY_PIECE_LEN * 2], hash[32], sha512Hash[64], hmacCalc[32];
uint8_t *enKey, *authKey;
uint8_t *iv, *encryptEntropy, *encryptSeed, *slip39EmsOrTonEntropyL32, *encryptReservedData, *hmac;
uint8_t accountEncryptData[ACCOUNT_TOTAL_LEN], param[32];
AccountInfo_t *pAccountInfo = (AccountInfo_t *)param;
- GetAccountInfo(accountIndex, pAccountInfo);
int32_t ret;
AES256_CBC_ctx ctx;
@@ -580,6 +630,7 @@ static int32_t LoadAccountSecret(uint8_t accountIndex, AccountSecret_t *accountS
hmac = encryptReservedData + SE_DATA_RESERVED_LEN;
do {
ret = GetKeyPieceFromSE(accountIndex, pieces, password);
+ CHECK_ERRCODE_BREAK("get key piece", ret);
HashWithSalt(hash, pieces, sizeof(pieces), "combine two pieces");
KEYSTORE_PRINT_ARRAY("pieces hash", hash, sizeof(hash));
sha512((struct sha512 *)sha512Hash, hash, sizeof(hash));
@@ -629,6 +680,22 @@ static int32_t LoadAccountSecret(uint8_t accountIndex, AccountSecret_t *accountS
CLEAR_ARRAY(hmacCalc);
return ret;
}
+#endif
+
+/// @brief Load account secret, including entropy/seed/reservedData.
+/// @param[in] accountIndex Account index, 0~2.
+/// @param[out] accountSecret Account secret data.
+/// @param[in] password Password string.
+/// @return err code.
+static int32_t LoadAccountSecret(uint8_t accountIndex, AccountSecret_t *accountSecret, const char *password)
+{
+#ifdef COMPILE_SIMULATOR
+ return SimulatorLoadAccountSecret(accountIndex, accountSecret, password);
+#else
+ ASSERT(accountIndex <= 2);
+ return LoadAccountSecretFromSE(accountIndex, accountSecret, password);
+#endif
+}
/// @brief Combine with the internal AES KEY of MCU.
/// @param[inout] aesKey
@@ -821,7 +888,7 @@ void KeyStoreTest(int argc, char *argv[])
} else {
printf("VerifyCurrentAccountPassword err=%d\r\n", ret);
}
- ret = VerifyPassword(&accountIndex, argv[1]);
+ ret = FindAccountByPassword(&accountIndex, argv[1]);
if (ret == SUCCESS_CODE) {
printf("password verify ok,accountIndex=%d\r\n", accountIndex);
} else {
diff --git a/src/managers/keystore.h b/src/managers/keystore.h
index 5296d31..bf038bb 100644
--- a/src/managers/keystore.h
+++ b/src/managers/keystore.h
@@ -52,7 +52,8 @@ int32_t GetAccountEntropy(uint8_t accountIndex, uint8_t *entropy, uint8_t *entro
int32_t GetAccountSeed(uint8_t accountIndex, uint8_t *seed, const char *password);
int32_t GetAccountSlip39Ems(uint8_t accountIndex, uint8_t *slip39Ems, const char *password);
int32_t ChangePassword(uint8_t accountIndex, const char *newPassword, const char *password);
-int32_t VerifyPassword(uint8_t *accountIndex, const char *password);
+int32_t FindAccountByPassword(uint8_t *matchedAccountIndex, const char *password);
+int32_t VerifyAccountPassword(uint8_t accountIndex, const char *password);
int32_t GenerateTRNGRandomness(uint8_t *randomness, uint8_t len);
bool CheckPassphraseSame(uint8_t accountIndex, const char *passphrase);
char* GetPassphrase(uint8_t accountIndex);
diff --git a/src/managers/se_manager.c b/src/managers/se_manager.c
index b3e67f8..b519588 100644
--- a/src/managers/se_manager.c
+++ b/src/managers/se_manager.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include "stdlib.h"
+#include "string.h"
#include "se_manager.h"
#include "se_interface.h"
#include "user_utils.h"
@@ -9,21 +10,23 @@
#include "err_code.h"
#include "drv_trng.h"
#include "drv_atecc608b.h"
+#include "cryptoauthlib.h"
#include "log_print.h"
#include "hash_and_salt.h"
#include "secret_cache.h"
-#ifndef COMPILE_SIMULATOR
-#include "drv_mpu.h"
-#endif
#define SHA256_COUNT 3
static int32_t SetNewKeyPieceToAtecc608b(uint8_t accountIndex, uint8_t *piece, const char *password);
static int32_t SetNewKeyPieceToDs28s60(uint8_t accountIndex, uint8_t *piece, const char *password);
-
static int32_t GetKeyPieceFromAtecc608b(uint8_t accountIndex, uint8_t *piece, const char *password);
static int32_t GetKeyPieceFromDs28s60(uint8_t accountIndex, uint8_t *piece, const char *password);
+static int32_t NormalizeAteccAuthError(int32_t ret)
+{
+ return (ret == ATCA_CHECKMAC_VERIFY_FAILED) ? ERR_KEYSTORE_AUTH : ret;
+}
+
static int32_t SetNewKeyPieceToAtecc608b(uint8_t accountIndex, uint8_t *piece, const char *password)
{
uint8_t authKey[32], hostRandom[32], inData[32], outData[32];
@@ -34,7 +37,6 @@ static int32_t SetNewKeyPieceToAtecc608b(uint8_t accountIndex, uint8_t *piece, c
do {
HashWithSalt(authKey, (uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "auth_key");
GetAccountSlot(&accountSlot, accountIndex);
- //new kdf
ret = SE_EncryptWrite(accountSlot.auth, 0, authKey);
CHECK_ERRCODE_BREAK("write auth", ret);
ret = SE_DeriveKey(accountSlot.rollKdf, authKey);
@@ -89,7 +91,7 @@ static int32_t SetNewKeyPieceToDs28s60(uint8_t accountIndex, uint8_t *piece, con
static int32_t GetKeyPieceFromAtecc608b(uint8_t accountIndex, uint8_t *piece, const char *password)
{
- uint8_t authKey[32], hostRandom[32], inData[32], outData[32];
+ uint8_t authKey[32], inData[32], outData[32];
int32_t ret;
AccountSlot_t accountSlot;
@@ -101,9 +103,11 @@ static int32_t GetKeyPieceFromAtecc608b(uint8_t accountIndex, uint8_t *piece, co
GetAccountSlot(&accountSlot, accountIndex);
ret = SE_Kdf(accountSlot.rollKdf, authKey, inData, 32, outData);
+ ret = NormalizeAteccAuthError(ret);
CHECK_ERRCODE_BREAK("kdf", ret);
memcpy(inData, outData, 32);
ret = SE_Kdf(accountSlot.hostKdf, authKey, inData, 32, outData);
+ ret = NormalizeAteccAuthError(ret);
CHECK_ERRCODE_BREAK("kdf", ret);
for (uint32_t i = 0; i < SHA256_COUNT; i++) {
memcpy(inData, outData, 32);
@@ -112,7 +116,6 @@ static int32_t GetKeyPieceFromAtecc608b(uint8_t accountIndex, uint8_t *piece, co
memcpy(piece, outData, 32);
} while (0);
CLEAR_ARRAY(authKey);
- CLEAR_ARRAY(hostRandom);
CLEAR_ARRAY(inData);
CLEAR_ARRAY(outData);
@@ -125,10 +128,10 @@ static int32_t GetKeyPieceFromDs28s60(uint8_t accountIndex, uint8_t *piece, cons
int32_t ret;
ASSERT(accountIndex <= 2);
- HashWithSalt(passwordHash, (uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "ds28s60 digest");
do {
+ HashWithSalt(passwordHash, (uint8_t *)password, strnlen_s(password, PASSWORD_MAX_LEN), "ds28s60 digest");
ret = SE_HmacEncryptRead(xData, accountIndex * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_KEY_PIECE);
- CHECK_ERRCODE_BREAK("write xData", ret);
+ CHECK_ERRCODE_BREAK("read xData", ret);
for (uint32_t i = 0; i < 32; i++) {
piece[i] = passwordHash[i] ^ xData[i];
}
@@ -168,12 +171,17 @@ void GetAccountSlot(AccountSlot_t *accountSlot, uint8_t accountIndex)
int32_t GetKeyPieceFromSE(uint8_t accountIndex, uint8_t *pieces, const char *password)
{
- int32_t ret = GetKeyPieceFromAtecc608b(accountIndex, pieces, password);
- CHECK_ERRCODE_RETURN_INT(ret);
- // KEYSTORE_PRINT_ARRAY("608 piece", pieces, 32);
- ret = GetKeyPieceFromDs28s60(accountIndex, pieces + KEY_PIECE_LEN, password);
- CHECK_ERRCODE_RETURN_INT(ret);
- // KEYSTORE_PRINT_ARRAY("ds28s60 piece", pieces + KEY_PIECE_LEN, 32);
+ int32_t ret;
+
+ do {
+ ret = GetKeyPieceFromAtecc608b(accountIndex, pieces, password);
+ CHECK_ERRCODE_BREAK("atecc piece", ret);
+ // KEYSTORE_PRINT_ARRAY("608 piece", pieces, 32);
+ ret = GetKeyPieceFromDs28s60(accountIndex, pieces + KEY_PIECE_LEN, password);
+ CHECK_ERRCODE_BREAK("ds28s60 piece", ret);
+ // KEYSTORE_PRINT_ARRAY("ds28s60 piece", pieces + KEY_PIECE_LEN, 32);
+ } while (0);
+
return ret;
}
diff --git a/src/managers/se_manager.h b/src/managers/se_manager.h
index 49ba8c6..a063240 100644
--- a/src/managers/se_manager.h
+++ b/src/managers/se_manager.h
@@ -17,7 +17,8 @@
#define PAGE_INDEX_RESERVED 5
#define PAGE_INDEX_HMAC 6
#define PAGE_INDEX_KEY_PIECE 7
-#define PAGE_INDEX_PASSWORD_HASH 8
+// Don't use this page in the future usage, it is only for legacy password hash
+#define PAGE_INDEX_LEGACY_PASSWORD_HASH 8
#define PAGE_INDEX_PARAM 9
#define PAGE_INDEX_MULTISIG_CONFIG_HASH 10
//page 76~85 encrypted password
@@ -56,4 +57,4 @@ bool VerifyWalletDataHash(uint8_t index, uint8_t *info);
int32_t SetMultisigDataHash(uint8_t index, uint8_t *info);
bool VerifyMultisigWalletDataHash(uint8_t index, uint8_t *info);
-#endif
\ No newline at end of file
+#endif
diff --git a/src/ui/gui_views/gui_lock_view.c b/src/ui/gui_views/gui_lock_view.c
index 0a08b16..952e46c 100644
--- a/src/ui/gui_views/gui_lock_view.c
+++ b/src/ui/gui_views/gui_lock_view.c
@@ -39,6 +39,7 @@ int32_t GuiLockViewEventProcess(void *self, uint16_t usEvent, void *param, uint1
GuiLockScreenInit(param);
break;
case GUI_EVENT_OBJ_DEINIT:
+ GuiLockScreenHideVerifyLoading();
break;
case SIG_INIT_SDCARD_CHANGE:
rcvValue = *(uint32_t *)param;
@@ -97,6 +98,7 @@ int32_t GuiLockViewEventProcess(void *self, uint16_t usEvent, void *param, uint1
GuiLockScreenPasscodeSwitch(false);
break;
case SIG_EXTENDED_PUBLIC_KEY_NOT_MATCH:
+ GuiLockScreenHideVerifyLoading();
GuiLockScreenWipeDevice();
break;
case SIG_START_GENERATE_XPUB:
@@ -127,4 +129,3 @@ GUI_VIEW g_lockView = {
.optimization = false,
.pEvtHandler = GuiLockViewEventProcess,
};
-
diff --git a/src/ui/gui_widgets/gui_enter_passcode.c b/src/ui/gui_widgets/gui_enter_passcode.c
index 11230df..67f3dd9 100644
--- a/src/ui/gui_widgets/gui_enter_passcode.c
+++ b/src/ui/gui_widgets/gui_enter_passcode.c
@@ -129,6 +129,7 @@ static void SetPinEventHandler(lv_event_t *e)
switch (item->mode) {
case ENTER_PASSCODE_VERIFY_PIN:
SecretCacheSetPassword(g_pinBuf);
+ GuiLockScreenShowVerifyLoading(g_userParam);
GuiModelVerifyAccountPassWord(g_userParam);
break;
case ENTER_PASSCODE_SET_PIN:
@@ -196,10 +197,11 @@ static void SetPassWordHandler(lv_event_t *e)
}
} else if (item->mode == ENTER_PASSCODE_REPEAT_PASSWORD) {
GuiEmitSignal(SIG_SETTING_REPEAT_PIN, (char *)currText, strnlen_s(currText, CREATE_PIN_NUM));
- } else if ((item->mode == ENTER_PASSCODE_VERIFY_PASSWORD)) {
+ } else if (item->mode == ENTER_PASSCODE_VERIFY_PASSWORD) {
g_userParam = g_passParam.userParam;
if (strnlen_s(currText, PASSWORD_MAX_LEN) > 0) {
SecretCacheSetPassword((char *)currText);
+ GuiLockScreenShowVerifyLoading(g_userParam);
GuiModelVerifyAccountPassWord(g_userParam);
}
}
@@ -802,4 +804,4 @@ uint8_t GetPassWordStrength(const char *password, uint8_t len)
}
return totalScore;
-}
\ No newline at end of file
+}
diff --git a/src/ui/gui_widgets/gui_lock_widgets.c b/src/ui/gui_widgets/gui_lock_widgets.c
index 2266e94..e50470e 100644
--- a/src/ui/gui_widgets/gui_lock_widgets.c
+++ b/src/ui/gui_widgets/gui_lock_widgets.c
@@ -55,6 +55,7 @@ static uint8_t g_fpErrorCount = 0;
static LOCK_SCREEN_PURPOSE_ENUM g_purpose = LOCK_SCREEN_PURPOSE_UNLOCK;
static PageWidget_t *g_pageWidget;
static lv_obj_t *g_LoadingView = NULL;
+static lv_obj_t *g_verifyLoadingCont = NULL;
static lv_timer_t *g_countDownTimer;
static int8_t g_countDown = 0;
static bool g_canDismissLoading = false;
@@ -190,8 +191,41 @@ bool GuiLockScreenIsTop(void)
return false;
}
+static bool GuiLockScreenIsVerifyLoadingParam(void *param)
+{
+ if (param == NULL) {
+ return false;
+ }
+ uint16_t signal = *(uint16_t *)param;
+ return signal == SIG_LOCK_VIEW_VERIFY_PIN || signal == SIG_LOCK_VIEW_SCREEN_GO_HOME_PASS;
+}
+
+void GuiLockScreenShowVerifyLoading(void *param)
+{
+ if (!GuiLockScreenIsVerifyLoadingParam(param) || !GuiLockScreenIsTop()) {
+ return;
+ }
+ if (g_verifyLoadingCont != NULL && lv_obj_is_valid(g_verifyLoadingCont)) {
+ return;
+ }
+
+ g_verifyLoadingCont = GuiCreateAnimHintBox(480, 278, 82);
+ lv_obj_t *title = GuiCreateTextLabel(g_verifyLoadingCont, _("seed_check_wait_verify"));
+ lv_obj_align(title, LV_ALIGN_BOTTOM_MID, 0, -76);
+ lv_obj_add_flag(g_verifyLoadingCont, LV_OBJ_FLAG_CLICKABLE);
+}
+
+void GuiLockScreenHideVerifyLoading(void)
+{
+ if (g_verifyLoadingCont != NULL && lv_obj_is_valid(g_verifyLoadingCont)) {
+ GuiDeleteAnimHintBox();
+ }
+ g_verifyLoadingCont = NULL;
+}
+
void GuiLockScreenHidden(void)
{
+ GuiLockScreenHideVerifyLoading();
if (g_pageWidget->page != NULL) {
lv_obj_add_flag(g_pageWidget->page, LV_OBJ_FLAG_HIDDEN);
}
@@ -207,6 +241,7 @@ void OpenForgetPasswordHandler(lv_event_t *e)
void GuiLockScreenTurnOn(void *param)
{
+ GuiLockScreenHideVerifyLoading();
uint16_t *single = param;
if (*single == SIG_LOCK_VIEW_VERIFY_PIN || *single == SIG_LOCK_VIEW_SCREEN_GO_HOME_PASS) {
GuiNvsBarSetWalletIcon(NULL);
@@ -236,6 +271,7 @@ void GuiLockScreenShuffleNumKeyBoardMap(void)
void GuiLockScreenTurnOff(void)
{
static uint16_t single = SIG_LOCK_VIEW_VERIFY_PIN;
+ GuiLockScreenHideVerifyLoading();
lv_obj_add_flag(g_pageWidget->page, LV_OBJ_FLAG_HIDDEN);
GuiEnterPassCodeStatus(g_verifyLock, true);
@@ -263,6 +299,7 @@ void GuiUpdateOldAccountIndex(void)
void GuiLockScreenToHome(void)
{
+ GuiLockScreenHideVerifyLoading();
lv_obj_add_flag(g_pageWidget->page, LV_OBJ_FLAG_HIDDEN);
GuiModeGetWalletDesc();
GuiEnterPassCodeStatus(g_verifyLock, true);
@@ -277,6 +314,7 @@ void GuiLockScreenTurnOffHandler(lv_event_t *e)
void GuiLockScreenPassCode(bool en)
{
+ GuiLockScreenHideVerifyLoading();
GuiEnterPassCodeStatus(g_verifyLock, en);
if (en) {
g_fpErrorCount = 0;
diff --git a/src/ui/gui_widgets/gui_lock_widgets.h b/src/ui/gui_widgets/gui_lock_widgets.h
index aa0ce9e..c8ab8d6 100644
--- a/src/ui/gui_widgets/gui_lock_widgets.h
+++ b/src/ui/gui_widgets/gui_lock_widgets.h
@@ -31,6 +31,8 @@ void GuiLockScreenSetFirstUnlock(void);
void GuiLockViewRefreshLanguage(void);
void GuiLockScreenSetNumKeyBoardMapDefault(void);
void GuiLockScreenShuffleNumKeyBoardMap(void);
+void GuiLockScreenShowVerifyLoading(void *param);
+void GuiLockScreenHideVerifyLoading(void);
void GuiLockScreenErrorCount(void *param);
void GuiLockScreenToHome(void);
@@ -43,4 +45,3 @@ void GuiHideGenerateXPubLoading(void);
void GuiLockViewRefreshLanguage(void);
#endif /* _GUI_LOCK_WIDGETS_H */
-
diff --git a/ui_simulator/simulator_storage.c b/ui_simulator/simulator_storage.c
index 449030b..f95422e 100644
--- a/ui_simulator/simulator_storage.c
+++ b/ui_simulator/simulator_storage.c
@@ -396,7 +396,7 @@ int32_t SE_HmacEncryptRead(uint8_t *data, uint8_t page)
GetJsonArrayData(rootJson, data, 32, "hmac");
} else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_KEY_PIECE) {
GetJsonArrayData(rootJson, data, 32, "key_piece");
- } else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH) {
+ } else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_LEGACY_PASSWORD_HASH) {
GetJsonArrayData(rootJson, data, 32, "password_hash");
} else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PARAM) {
GetJsonArrayData(rootJson, data, sizeof(AccountInfo_t), "param");
@@ -454,7 +454,7 @@ int32_t SE_HmacEncryptWrite(const uint8_t *data, uint8_t page)
// ModifyJsonArrayData(rootJson, data, 32, "hmac");
} else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_KEY_PIECE) {
// ModifyJsonArrayData(rootJson, data, 32, "key_piece");
- } else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PASSWORD_HASH) {
+ } else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_LEGACY_PASSWORD_HASH) {
// ModifyJsonArrayData(rootJson, data, 32, "password_hash");
} else if (page == account * PAGE_NUM_PER_ACCOUNT + PAGE_INDEX_PARAM) {
ModifyJsonArrayData(rootJson, data, sizeof(AccountInfo_t), "param");
@@ -577,4 +577,4 @@ void FatfsGetFileName(const char *path, char *fileName[], uint32_t maxLen, uint3
}
lv_fs_dir_close(&dir);
*number = count;
-}
\ No newline at end of file
+}
Why this scored 57/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.