SHA256
Обновить Telegram-бота, документацию и связанные доработки
This commit is contained in:
+134
-11
@@ -4,9 +4,12 @@
|
||||
* Результат сохраняется в NVS (внутренняя flash ESP32).
|
||||
*
|
||||
* Алгоритм совпадает с JS crypto-utils.js:
|
||||
* salt = SHA256("shine-auth-v2|login=<login>|suffix=master.secret")[0:16]
|
||||
* passBytes = UTF8("<login>\n<password>")
|
||||
* loginNorm = trim(lowercase(login))
|
||||
* salt = SHA256("shine-auth-v2|login=<loginNorm>|suffix=master.secret")[0:16]
|
||||
* passBytes = UTF8("<loginNorm>\n<password>")
|
||||
* secret = Argon2id(passBytes, salt, t=2, m=65536 KB, p=1, dkLen=32)
|
||||
* legacy(empty password):
|
||||
* secret = SHA256(base64(SHA256(password)) + "master.secret")
|
||||
* keyPair_i = Ed25519(SHA256(base64(secret) + "|" + suffix_i))
|
||||
* suffixes = ["root.key", "bch.key", "dev.key"]
|
||||
*
|
||||
@@ -105,6 +108,7 @@ static char gLogin[64] = {};
|
||||
static char gPass[64] = {};
|
||||
static uint8_t gSecret[32];
|
||||
static char gSecretHex[65];
|
||||
static char gInputStatus[96] = {};
|
||||
static bool gKbNums = false;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -356,6 +360,61 @@ static void sha256calc(const uint8_t *in,size_t len,uint8_t *out32){
|
||||
mbedtls_sha256_free(&ctx);
|
||||
}
|
||||
|
||||
static void trimInPlace(char *text){
|
||||
if(!text)return;
|
||||
size_t len=strlen(text);
|
||||
size_t start=0;
|
||||
while(start<len&&(text[start]==' '||text[start]=='\t'||text[start]=='\n'||text[start]=='\r'))start++;
|
||||
size_t end=len;
|
||||
while(end>start&&(text[end-1]==' '||text[end-1]=='\t'||text[end-1]=='\n'||text[end-1]=='\r'))end--;
|
||||
if(start>0&&end>start)memmove(text,text+start,end-start);
|
||||
if(end<=start){text[0]='\0';return;}
|
||||
text[end-start]='\0';
|
||||
}
|
||||
|
||||
static void lowercaseAsciiInPlace(char *text){
|
||||
if(!text)return;
|
||||
for(int i=0;text[i];i++){
|
||||
if(text[i]>='A'&&text[i]<='Z')text[i]+=32;
|
||||
}
|
||||
}
|
||||
|
||||
static void normalizeLoginInPlace(char *text){
|
||||
trimInPlace(text);
|
||||
lowercaseAsciiInPlace(text);
|
||||
}
|
||||
|
||||
static void setInputStatus(const char *message){
|
||||
snprintf(gInputStatus,sizeof(gInputStatus),"%s",message?message:"");
|
||||
}
|
||||
|
||||
static void clearInputStatus(){
|
||||
gInputStatus[0]='\0';
|
||||
}
|
||||
|
||||
static void bytesToBase64Std(const uint8_t *data,size_t len,char *out,size_t outSz){
|
||||
size_t b64len=0;
|
||||
if(outSz==0)return;
|
||||
if(mbedtls_base64_encode((uint8_t*)out,outSz,&b64len,data,len)!=0){
|
||||
out[0]='\0';
|
||||
return;
|
||||
}
|
||||
if(b64len>=outSz)b64len=outSz-1;
|
||||
out[b64len]='\0';
|
||||
}
|
||||
|
||||
static void deriveLegacyMasterSecret(const char *password,uint8_t *out32){
|
||||
uint8_t baseHash[32];
|
||||
sha256calc((const uint8_t*)password,strlen(password),baseHash);
|
||||
|
||||
char baseB64[64];
|
||||
bytesToBase64Std(baseHash,32,baseB64,sizeof(baseB64));
|
||||
|
||||
char material[96];
|
||||
snprintf(material,sizeof(material),"%smaster.secret",baseB64);
|
||||
sha256calc((const uint8_t*)material,strlen(material),out32);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// BASE58
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -473,6 +532,13 @@ static void argon2Init(const char *login,const char *password){
|
||||
#undef B2LE32
|
||||
b2_final(&gB2S,gH0);
|
||||
|
||||
// Debug: print inputs and H0 to Serial for cross-check with reference
|
||||
Serial.printf("[DBG] login='%s' passLen=%u saltLen=%u\n",login,passLen,saltLen);
|
||||
Serial.print("[DBG] salt: ");
|
||||
for(int i=0;i<16;i++)Serial.printf("%02x",salt[i]);Serial.println();
|
||||
Serial.print("[DBG] H0: ");
|
||||
for(int i=0;i<64;i++)Serial.printf("%02x",gH0[i]);Serial.println();
|
||||
|
||||
uint8_t input[72];memcpy(input,gH0,64);
|
||||
for(uint32_t i=0;i<2;i++){
|
||||
input[64]=i;input[65]=0;input[66]=0;input[67]=0;
|
||||
@@ -541,6 +607,13 @@ static void argon2Finalize(){
|
||||
Serial.println("=== END ===");
|
||||
}
|
||||
|
||||
static void completeResultFromSecret(){
|
||||
for(int i=0;i<32;i++)snprintf(gSecretHex+i*2,3,"%02x",gSecret[i]);
|
||||
gSecretHex[64]='\0';
|
||||
deriveKeyPairs();
|
||||
nvsSave();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// UI — HELPERS
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -592,6 +665,9 @@ static void drawInputScreen(const char *title,const char *buf,bool isMasked){
|
||||
else{strncpy(show,buf,67);show[67]='\0';}
|
||||
gfx->setTextSize(2);gfx->setTextColor(C_TEXT);gfx->setCursor(30,78);gfx->print(show);
|
||||
int cx=30+strlen(show)*12;gfx->drawFastVLine(cx,78,20,C_GREEN);
|
||||
if(gInputStatus[0]){
|
||||
gfx->setTextSize(2);gfx->setTextColor(C_RED);gfx->setCursor(20,132);gfx->print(gInputStatus);
|
||||
}
|
||||
drawKeyboard();
|
||||
}
|
||||
|
||||
@@ -628,6 +704,7 @@ static void drawProgressHeader(){
|
||||
// нарисовать пустую полосу один раз
|
||||
int bx=20,by=90,bw=DISP_W-40,bh=36;
|
||||
uiRect(bx,by,bw,bh,C_BAR_BG,C_SEP,4);
|
||||
gfx->fillRect(bx+1,by+1,bw-2,bh-2,C_BG);
|
||||
}
|
||||
|
||||
static void drawProgress(){
|
||||
@@ -639,7 +716,7 @@ static void drawProgress(){
|
||||
int filled=(int)((uint64_t)bw*gDone/TOTAL_FILLS);
|
||||
if(gDone>0&&filled==0)filled=1; // at least 1px once started
|
||||
if(filled>gBarFilledPx){
|
||||
gfx->fillRect(bx+gBarFilledPx,by+1,filled-gBarFilledPx,bh-2,C_BAR_FG);
|
||||
gfx->fillRect(bx+1+gBarFilledPx,by+1,filled-gBarFilledPx,bh-2,C_GREEN);
|
||||
gBarFilledPx=filled;
|
||||
}
|
||||
|
||||
@@ -722,9 +799,11 @@ static void drawResultContent(){
|
||||
char buf[96];
|
||||
|
||||
// — заголовок —
|
||||
d.line("RESULT",C_GREEN,2);
|
||||
d.line(" RESULT",C_GREEN,2);
|
||||
snprintf(buf,sizeof(buf),"Login: %s",gLogin);
|
||||
d.line(buf,C_TEXT,2);
|
||||
snprintf(buf,sizeof(buf),"Password: %s",gPass[0]?gPass:"(empty)");
|
||||
d.line(buf,C_TEXT,2);
|
||||
d.gap(4);
|
||||
d.line("Master secret (base58):",C_HINT,2);
|
||||
d.longval(gSecretB58,C_GREEN);
|
||||
@@ -758,7 +837,6 @@ static void drawResultBtnBar(){
|
||||
}
|
||||
|
||||
static void drawResultFull(){
|
||||
gScrollY=0;
|
||||
drawResultContent();
|
||||
drawResultBtnBar();
|
||||
}
|
||||
@@ -790,27 +868,53 @@ static void handleTap(int tx,int ty){
|
||||
char *buf=isPass?gPass:gLogin;
|
||||
char c=kbTouch(tx,ty);
|
||||
if(!c)return;
|
||||
if(c=='\x03'){gKbNums=!gKbNums;drawInputScreen(isPass?"Password:":"Login:",buf,isPass);return;}
|
||||
if(c=='\x08'){int n=strlen(buf);if(n>0)buf[n-1]='\0';}
|
||||
if(c=='\x03'){gKbNums=!gKbNums;clearInputStatus();drawInputScreen(isPass?"Password:":"Login:",buf,isPass);return;}
|
||||
if(c=='\x08'){int n=strlen(buf);if(n>0)buf[n-1]='\0';clearInputStatus();}
|
||||
else if(c=='\x0D'){
|
||||
if(!strlen(buf))return;
|
||||
if(gState==ST_LOGIN){
|
||||
for(int i=0;buf[i];i++)if(buf[i]>='A'&&buf[i]<='Z')buf[i]+=32;
|
||||
normalizeLoginInPlace(buf);
|
||||
if(!strlen(buf)){
|
||||
setInputStatus("Логин обязателен");
|
||||
drawInputScreen("Login:",gLogin,false);
|
||||
return;
|
||||
}
|
||||
clearInputStatus();
|
||||
gState=ST_PASS;drawInputScreen("Password:",gPass,true);return;
|
||||
} else {
|
||||
clearInputStatus();
|
||||
gState=ST_RUNNING;
|
||||
gfx->fillScreen(C_BG);drawProgressHeader();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
gSdFile=SD_MMC.open(SD_MEM_FILE,FILE_WRITE);
|
||||
gSdFile=SD_MMC.open(SD_MEM_FILE,"w+");
|
||||
if(!gSdFile){
|
||||
gfx->setTextSize(2);gfx->setTextColor(C_RED);
|
||||
gfx->setCursor(20,200);gfx->println("SD open failed");
|
||||
gState=ST_ERROR;return;
|
||||
}
|
||||
normalizeLoginInPlace(gLogin);
|
||||
if(!gLogin[0]){
|
||||
gSdFile.close();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
gState=ST_LOGIN;
|
||||
setInputStatus("Логин обязателен");
|
||||
drawInputScreen("Login:",gLogin,false);
|
||||
return;
|
||||
}
|
||||
if(!gPass[0]){
|
||||
deriveLegacyMasterSecret(gPass,gSecret);
|
||||
gElapsedSec=0;
|
||||
completeResultFromSecret();
|
||||
gSdFile.close();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
gState=ST_DONE;
|
||||
gScrollY=0;
|
||||
drawResultFull();
|
||||
return;
|
||||
}
|
||||
argon2Init(gLogin,gPass);
|
||||
drawProgress();return;
|
||||
}
|
||||
} else {int n=strlen(buf);if(n<63){buf[n]=c;buf[n+1]='\0';}}
|
||||
} else {int n=strlen(buf);if(n<63){buf[n]=c;buf[n+1]='\0';}clearInputStatus();}
|
||||
drawInputScreen(isPass?"Password:":"Login:",buf,isPass);
|
||||
return;
|
||||
}
|
||||
@@ -875,6 +979,25 @@ void setup(){
|
||||
while(true)delay(1000);
|
||||
}
|
||||
|
||||
// BLAKE2b self-test: BLAKE2b-512("") = 786a02f7...
|
||||
{
|
||||
static const uint8_t EXP[64]={
|
||||
0x78,0x6a,0x02,0xf7,0x42,0x01,0x59,0x03,0xc6,0xc6,0xfd,0x85,0x25,0x52,0xd2,0x72,
|
||||
0x91,0x2f,0x47,0x40,0xe1,0x58,0x47,0x61,0x8a,0x86,0xe2,0x17,0xf7,0x1f,0x54,0x19,
|
||||
0xd2,0x5e,0x10,0x31,0xaf,0xee,0x58,0x53,0x13,0x89,0x64,0x44,0x93,0x4e,0xb0,0x4b,
|
||||
0x90,0x3a,0x68,0x5b,0x14,0x48,0xb7,0x55,0xd5,0x6f,0x70,0x1a,0xfe,0x9b,0xe2,0xce
|
||||
};
|
||||
uint8_t got[64];
|
||||
b2_init(&gB2S,64);b2_final(&gB2S,got);
|
||||
bool ok=(memcmp(got,EXP,64)==0);
|
||||
Serial.printf("BLAKE2b-512('') : %s\n",ok?"PASS":"FAIL");
|
||||
if(!ok){
|
||||
Serial.print("got: ");
|
||||
for(int i=0;i<64;i++)Serial.printf("%02x",got[i]);
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
// PSRAM буферы
|
||||
gBufPrev=(uint8_t*)ps_malloc(A2_BLKSZ);gBufRef=(uint8_t*)ps_malloc(A2_BLKSZ);
|
||||
gBufOut=(uint8_t*)ps_malloc(A2_BLKSZ);gBufZero=(uint8_t*)ps_calloc(1,A2_BLKSZ);
|
||||
|
||||
Reference in New Issue
Block a user