diff --git a/Dev_Docs/Pending_Features/2026-06-23_1325_esp32_secret_cache.md b/Dev_Docs/Pending_Features/2026-06-23_1325_esp32_secret_cache.md deleted file mode 100644 index 9b8fd63..0000000 --- a/Dev_Docs/Pending_Features/2026-06-23_1325_esp32_secret_cache.md +++ /dev/null @@ -1,25 +0,0 @@ -## Кратко - -Добавлен локальный кэш последних 5 успешных генераций мастер-секрета на ESP32. - -## Что сделано - -- Генерация секретов теперь сначала проверяет кэш по `SHA-256(login + 0x00 + password)`. -- При совпадении ESP32 сразу подставляет готовый секрет и не запускает долгую генерацию. -- Успешные результаты складываются в NVS как последние 5 записей. - -## Что проверять - -- Сгенерировать секрет для `login + password`. -- Повторить ту же пару ещё раз. -- Убедиться, что вторая попытка не запускает долгую генерацию и сразу показывает готовый секрет. -- Проверить, что после 6 различных успешных генераций самая старая запись вытесняется. - -## Ожидаемый результат - -- Повторная генерация той же пары `login + password` берётся из кэша. -- Кэш хранит только последние 5 успешных результатов. - -## Статус - -`pending` diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino index 62dbe61..68057b4 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino @@ -347,6 +347,9 @@ static unsigned long gLastAccountCheckMs = 0; static bool gShowRegisterAccountButton = false; static bool gShowHomeserverPdaActionButton = false; static String gHomeserverPdaActionReason; +static bool gCachedAccountPdaValid = false; +static String gCachedAccountPdaLogin; +static ShinePdaUserState gCachedAccountPdaState; static String gUserPdaAddress; static String gRegistrationSignature; static String gShineStatusLine = "SHiNE: account not configured"; @@ -1453,6 +1456,9 @@ static void markAccountStateDirty() { gHomeserverPdaResultMessage = ""; gHomeserverPdaResultDetails = ""; gHomeserverPdaResultSuccess = false; + gCachedAccountPdaValid = false; + gCachedAccountPdaLogin = ""; + gCachedAccountPdaState = ShinePdaUserState{}; gUserPdaAddress = ""; gRegistrationSignature = ""; gRegisterConfirmMessage = ""; @@ -3078,14 +3084,22 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag ShinePdaUserState currentState; String stateError; - if (!readShineUserPda(cleanLogin, currentState, stateError)) { - diagDetails += String("read_pda_error=") + stateError + "\n"; - return failWithDiag(stateError.isEmpty() ? "Failed to read user PDA" : stateError); + bool usedCachedPda = false; + if (gCachedAccountPdaValid && gCachedAccountPdaLogin == cleanLogin && gCachedAccountPdaState.found) { + currentState = gCachedAccountPdaState; + usedCachedPda = true; + diagDetails += "read_pda_source=cache\n"; + } else { + if (!readShineUserPda(cleanLogin, currentState, stateError)) { + diagDetails += String("read_pda_error=") + stateError + "\n"; + return failWithDiag(stateError.isEmpty() ? "Failed to read user PDA" : stateError); + } + if (!currentState.found) { + return failWithDiag("User PDA does not exist yet"); + } + diagDetails += "read_pda_source=rpc\n"; } - if (!currentState.found) { - return failWithDiag("User PDA does not exist yet"); - } - saveRegisterDiagCheckpoint("Homeserver PDA read", diagDetails); + saveRegisterDiagCheckpoint(usedCachedPda ? "Homeserver PDA read from cache" : "Homeserver PDA read", diagDetails); uint8_t recoverySeed[32] = {}; uint8_t recoveryPub[32] = {}; @@ -3685,6 +3699,9 @@ static void refreshAccountPdaStatus() { return; } + gCachedAccountPdaState = pdaState; + gCachedAccountPdaLogin = gLoginValue; + gCachedAccountPdaValid = true; gAccountPdaStatus = ACCOUNT_PDA_OK; gAccountPdaStatusMessage = "ok"; } diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_secret_generation.cpp b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_secret_generation.cpp index d2a7129..3193265 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_secret_generation.cpp +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_secret_generation.cpp @@ -1,7 +1,6 @@ #include "shine_secret_generation.h" #include -#include #include #include #include @@ -37,10 +36,6 @@ static bool gError = false; static char gMessage[96] = {}; static uint8_t gSecret[32] = {}; static char gSecretB58[64] = {}; -static Preferences gCachePrefs; -static bool gCachePrefsReady = false; -static char gCacheLogin[32] = {}; -static uint8_t gCacheLookupHash[32] = {}; static uint32_t gDoneBlocks = 0; static uint32_t gStartMs = 0; static uint32_t gCurPass = 0; @@ -86,123 +81,6 @@ static void setMessage(const char *message) { static void sha256calc(const uint8_t *in, size_t len, uint8_t *out32); -static const char *CACHE_NS = "secret_cache"; -static const char *CACHE_VERSION_KEY = "ver"; -static const char *CACHE_HEAD_KEY = "head"; -static const char *CACHE_COUNT_KEY = "count"; -static const uint8_t CACHE_VERSION = 1; -static const uint8_t CACHE_MAX_ENTRIES = 5; - -struct SecretCacheEntry { - char login[32]; - uint8_t lookupHash[32]; - uint8_t secret[32]; -}; - -static void computeLookupHash(const char *normalizedLogin, const char *password, uint8_t out32[32]) { - std::vector material; - const char *login = normalizedLogin ? normalizedLogin : ""; - const char *pwd = password ? password : ""; - size_t loginLen = strlen(login); - size_t pwdLen = strlen(pwd); - material.reserve(loginLen + 1 + pwdLen); - material.insert(material.end(), login, login + loginLen); - material.push_back(0); - material.insert(material.end(), pwd, pwd + pwdLen); - sha256calc(material.data(), material.size(), out32); -} - -static bool cacheOpen(String &error) { - error = ""; - if (gCachePrefsReady) return true; - if (!gCachePrefs.begin(CACHE_NS, false)) { - error = "Cache NVS open failed"; - return false; - } - if (!gCachePrefs.isKey(CACHE_VERSION_KEY)) { - gCachePrefs.putUChar(CACHE_VERSION_KEY, CACHE_VERSION); - gCachePrefs.putUChar(CACHE_HEAD_KEY, 0); - gCachePrefs.putUChar(CACHE_COUNT_KEY, 0); - } - gCachePrefsReady = true; - return true; -} - -static void cacheClose() { - if (gCachePrefsReady) { - gCachePrefs.end(); - gCachePrefsReady = false; - } -} - -static String cacheSlotKey(uint8_t slot) { - return String("s") + String(slot); -} - -static bool loadCacheEntry(uint8_t slot, SecretCacheEntry &entry) { - memset(&entry, 0, sizeof(entry)); - String key = cacheSlotKey(slot); - if (!gCachePrefs.isKey(key.c_str())) { - return false; - } - size_t len = gCachePrefs.getBytesLength(key.c_str()); - if (len != sizeof(entry)) { - return false; - } - return gCachePrefs.getBytes(key.c_str(), &entry, sizeof(entry)) == sizeof(entry); -} - -static void storeCacheEntry(uint8_t slot, const SecretCacheEntry &entry) { - String key = cacheSlotKey(slot); - gCachePrefs.putBytes(key.c_str(), &entry, sizeof(entry)); -} - -static bool findCachedSecret(const char *normalizedLogin, const char *password, uint8_t secretOut[32]) { - String error; - if (!cacheOpen(error)) { - return false; - } - uint8_t lookupHash[32]; - computeLookupHash(normalizedLogin, password, lookupHash); - uint8_t head = gCachePrefs.getUChar(CACHE_HEAD_KEY, 0); - uint8_t count = gCachePrefs.getUChar(CACHE_COUNT_KEY, 0); - for (uint8_t i = 0; i < count && i < CACHE_MAX_ENTRIES; ++i) { - uint8_t slot = (uint8_t)((head + CACHE_MAX_ENTRIES - 1 - i) % CACHE_MAX_ENTRIES); - SecretCacheEntry entry; - if (!loadCacheEntry(slot, entry)) { - continue; - } - if (memcmp(entry.lookupHash, lookupHash, 32) == 0) { - memcpy(secretOut, entry.secret, 32); - snprintf(gCacheLogin, sizeof(gCacheLogin), "%s", entry.login); - memcpy(gCacheLookupHash, lookupHash, 32); - return true; - } - } - return false; -} - -static void rememberSecret(const char *normalizedLogin, const uint8_t secret32[32]) { - String error; - if (!cacheOpen(error)) { - return; - } - SecretCacheEntry entry = {}; - snprintf(entry.login, sizeof(entry.login), "%s", normalizedLogin ? normalizedLogin : ""); - memcpy(entry.lookupHash, gCacheLookupHash, 32); - memcpy(entry.secret, secret32, 32); - uint8_t head = gCachePrefs.getUChar(CACHE_HEAD_KEY, 0); - uint8_t count = gCachePrefs.getUChar(CACHE_COUNT_KEY, 0); - storeCacheEntry(head, entry); - head = (uint8_t)((head + 1) % CACHE_MAX_ENTRIES); - if (count < CACHE_MAX_ENTRIES) { - count++; - } - gCachePrefs.putUChar(CACHE_HEAD_KEY, head); - gCachePrefs.putUChar(CACHE_COUNT_KEY, count); - gCachePrefs.putUChar(CACHE_VERSION_KEY, CACHE_VERSION); -} - static void finishSecretFromBytes(const uint8_t secret32[32], const char *message) { memcpy(gSecret, secret32, 32); shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58)); @@ -596,18 +474,6 @@ bool shineSecretStart(const char *normalizedLogin, const char *password, String return false; } - uint8_t cachedSecret[32] = {}; - if (findCachedSecret(loginBuf, password ? password : "", cachedSecret)) { - if (gSdFile) gSdFile.close(); - snprintf(gCacheLogin, sizeof(gCacheLogin), "%s", loginBuf); - computeLookupHash(loginBuf, password ? password : "", gCacheLookupHash); - finishSecretFromBytes(cachedSecret, "Secret loaded from cache"); - return true; - } - - snprintf(gCacheLogin, sizeof(gCacheLogin), "%s", loginBuf); - computeLookupHash(loginBuf, password ? password : "", gCacheLookupHash); - if (!shineSecretInitSd(error)) return false; if (gSdFile) gSdFile.close(); @@ -629,7 +495,6 @@ bool shineSecretStart(const char *normalizedLogin, const char *password, String if (!password || !password[0]) { deriveLegacyMasterSecret(password ? password : "", gSecret); shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58)); - rememberSecret(loginBuf, gSecret); gDone = true; gRunning = false; setMessage("Secret generated"); @@ -684,7 +549,6 @@ bool shineSecretStep(uint32_t blocksPerTick) { gSdFile.read(gBufOut, A2_BLKSZ); b2long(gBufOut, A2_BLKSZ, gSecret, A2_DKLEN); shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58)); - rememberSecret(gCacheLogin, gSecret); gDone = true; gRunning = false; setMessage("Secret generated"); diff --git a/VERSION.properties b/VERSION.properties index eaa4f8e..4045847 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.241 -server.version=1.2.226 +client.version=1.2.243 +server.version=1.2.228