ESP32: убрать кэш генерации секрета
This commit is contained in:
parent
66986b804c
commit
d2426c473c
@ -1,25 +0,0 @@
|
|||||||
## Кратко
|
|
||||||
|
|
||||||
Добавлен локальный кэш последних 5 успешных генераций мастер-секрета на ESP32.
|
|
||||||
|
|
||||||
## Что сделано
|
|
||||||
|
|
||||||
- Генерация секретов теперь сначала проверяет кэш по `SHA-256(login + 0x00 + password)`.
|
|
||||||
- При совпадении ESP32 сразу подставляет готовый секрет и не запускает долгую генерацию.
|
|
||||||
- Успешные результаты складываются в NVS как последние 5 записей.
|
|
||||||
|
|
||||||
## Что проверять
|
|
||||||
|
|
||||||
- Сгенерировать секрет для `login + password`.
|
|
||||||
- Повторить ту же пару ещё раз.
|
|
||||||
- Убедиться, что вторая попытка не запускает долгую генерацию и сразу показывает готовый секрет.
|
|
||||||
- Проверить, что после 6 различных успешных генераций самая старая запись вытесняется.
|
|
||||||
|
|
||||||
## Ожидаемый результат
|
|
||||||
|
|
||||||
- Повторная генерация той же пары `login + password` берётся из кэша.
|
|
||||||
- Кэш хранит только последние 5 успешных результатов.
|
|
||||||
|
|
||||||
## Статус
|
|
||||||
|
|
||||||
`pending`
|
|
||||||
@ -347,6 +347,9 @@ static unsigned long gLastAccountCheckMs = 0;
|
|||||||
static bool gShowRegisterAccountButton = false;
|
static bool gShowRegisterAccountButton = false;
|
||||||
static bool gShowHomeserverPdaActionButton = false;
|
static bool gShowHomeserverPdaActionButton = false;
|
||||||
static String gHomeserverPdaActionReason;
|
static String gHomeserverPdaActionReason;
|
||||||
|
static bool gCachedAccountPdaValid = false;
|
||||||
|
static String gCachedAccountPdaLogin;
|
||||||
|
static ShinePdaUserState gCachedAccountPdaState;
|
||||||
static String gUserPdaAddress;
|
static String gUserPdaAddress;
|
||||||
static String gRegistrationSignature;
|
static String gRegistrationSignature;
|
||||||
static String gShineStatusLine = "SHiNE: account not configured";
|
static String gShineStatusLine = "SHiNE: account not configured";
|
||||||
@ -1453,6 +1456,9 @@ static void markAccountStateDirty() {
|
|||||||
gHomeserverPdaResultMessage = "";
|
gHomeserverPdaResultMessage = "";
|
||||||
gHomeserverPdaResultDetails = "";
|
gHomeserverPdaResultDetails = "";
|
||||||
gHomeserverPdaResultSuccess = false;
|
gHomeserverPdaResultSuccess = false;
|
||||||
|
gCachedAccountPdaValid = false;
|
||||||
|
gCachedAccountPdaLogin = "";
|
||||||
|
gCachedAccountPdaState = ShinePdaUserState{};
|
||||||
gUserPdaAddress = "";
|
gUserPdaAddress = "";
|
||||||
gRegistrationSignature = "";
|
gRegistrationSignature = "";
|
||||||
gRegisterConfirmMessage = "";
|
gRegisterConfirmMessage = "";
|
||||||
@ -3078,6 +3084,12 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag
|
|||||||
|
|
||||||
ShinePdaUserState currentState;
|
ShinePdaUserState currentState;
|
||||||
String stateError;
|
String 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)) {
|
if (!readShineUserPda(cleanLogin, currentState, stateError)) {
|
||||||
diagDetails += String("read_pda_error=") + stateError + "\n";
|
diagDetails += String("read_pda_error=") + stateError + "\n";
|
||||||
return failWithDiag(stateError.isEmpty() ? "Failed to read user PDA" : stateError);
|
return failWithDiag(stateError.isEmpty() ? "Failed to read user PDA" : stateError);
|
||||||
@ -3085,7 +3097,9 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag
|
|||||||
if (!currentState.found) {
|
if (!currentState.found) {
|
||||||
return failWithDiag("User PDA does not exist yet");
|
return failWithDiag("User PDA does not exist yet");
|
||||||
}
|
}
|
||||||
saveRegisterDiagCheckpoint("Homeserver PDA read", diagDetails);
|
diagDetails += "read_pda_source=rpc\n";
|
||||||
|
}
|
||||||
|
saveRegisterDiagCheckpoint(usedCachedPda ? "Homeserver PDA read from cache" : "Homeserver PDA read", diagDetails);
|
||||||
|
|
||||||
uint8_t recoverySeed[32] = {};
|
uint8_t recoverySeed[32] = {};
|
||||||
uint8_t recoveryPub[32] = {};
|
uint8_t recoveryPub[32] = {};
|
||||||
@ -3685,6 +3699,9 @@ static void refreshAccountPdaStatus() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gCachedAccountPdaState = pdaState;
|
||||||
|
gCachedAccountPdaLogin = gLoginValue;
|
||||||
|
gCachedAccountPdaValid = true;
|
||||||
gAccountPdaStatus = ACCOUNT_PDA_OK;
|
gAccountPdaStatus = ACCOUNT_PDA_OK;
|
||||||
gAccountPdaStatusMessage = "ok";
|
gAccountPdaStatusMessage = "ok";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "shine_secret_generation.h"
|
#include "shine_secret_generation.h"
|
||||||
|
|
||||||
#include <SD_MMC.h>
|
#include <SD_MMC.h>
|
||||||
#include <Preferences.h>
|
|
||||||
#include <mbedtls/sha256.h>
|
#include <mbedtls/sha256.h>
|
||||||
#include <mbedtls/base64.h>
|
#include <mbedtls/base64.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -37,10 +36,6 @@ static bool gError = false;
|
|||||||
static char gMessage[96] = {};
|
static char gMessage[96] = {};
|
||||||
static uint8_t gSecret[32] = {};
|
static uint8_t gSecret[32] = {};
|
||||||
static char gSecretB58[64] = {};
|
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 gDoneBlocks = 0;
|
||||||
static uint32_t gStartMs = 0;
|
static uint32_t gStartMs = 0;
|
||||||
static uint32_t gCurPass = 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 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<uint8_t> 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) {
|
static void finishSecretFromBytes(const uint8_t secret32[32], const char *message) {
|
||||||
memcpy(gSecret, secret32, 32);
|
memcpy(gSecret, secret32, 32);
|
||||||
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
||||||
@ -596,18 +474,6 @@ bool shineSecretStart(const char *normalizedLogin, const char *password, String
|
|||||||
return false;
|
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 (!shineSecretInitSd(error)) return false;
|
||||||
|
|
||||||
if (gSdFile) gSdFile.close();
|
if (gSdFile) gSdFile.close();
|
||||||
@ -629,7 +495,6 @@ bool shineSecretStart(const char *normalizedLogin, const char *password, String
|
|||||||
if (!password || !password[0]) {
|
if (!password || !password[0]) {
|
||||||
deriveLegacyMasterSecret(password ? password : "", gSecret);
|
deriveLegacyMasterSecret(password ? password : "", gSecret);
|
||||||
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
||||||
rememberSecret(loginBuf, gSecret);
|
|
||||||
gDone = true;
|
gDone = true;
|
||||||
gRunning = false;
|
gRunning = false;
|
||||||
setMessage("Secret generated");
|
setMessage("Secret generated");
|
||||||
@ -684,7 +549,6 @@ bool shineSecretStep(uint32_t blocksPerTick) {
|
|||||||
gSdFile.read(gBufOut, A2_BLKSZ);
|
gSdFile.read(gBufOut, A2_BLKSZ);
|
||||||
b2long(gBufOut, A2_BLKSZ, gSecret, A2_DKLEN);
|
b2long(gBufOut, A2_BLKSZ, gSecret, A2_DKLEN);
|
||||||
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
||||||
rememberSecret(gCacheLogin, gSecret);
|
|
||||||
gDone = true;
|
gDone = true;
|
||||||
gRunning = false;
|
gRunning = false;
|
||||||
setMessage("Secret generated");
|
setMessage("Secret generated");
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.241
|
client.version=1.2.243
|
||||||
server.version=1.2.226
|
server.version=1.2.228
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user