Auth/UI: Argon2id derivation для login/register + блок Расширенные

This commit is contained in:
AidarKC
2026-05-13 02:10:42 +03:00
parent 8de4e95c6a
commit b55fd1571e
6 changed files with 107 additions and 12 deletions
+39 -2
View File
@@ -1,5 +1,6 @@
const encoder = new TextEncoder();
const WEB_CRYPTO_REQUIRED_MESSAGE = 'Регистрация и подпись блоков требуют WebCrypto (crypto.subtle). Откройте приложение через HTTPS или localhost в современном браузере и повторите попытку.';
import { argon2idAsync } from 'https://esm.sh/@noble/hashes@1.8.0/argon2.js';
function getCryptoApi() {
const api = globalThis.crypto;
@@ -66,6 +67,33 @@ export async function derivePasswordSeed(password, suffix) {
return sha256Text(concat);
}
function normalizeLoginForKdf(login) {
return String(login || '').trim().toLowerCase();
}
async function makeArgon2Salt(login, suffix) {
const normalizedLogin = normalizeLoginForKdf(login);
const normalizedSuffix = String(suffix || '').trim();
const saltSource = `shine-auth-v2|login=${normalizedLogin}|suffix=${normalizedSuffix}`;
const digest = await sha256Text(saltSource);
return digest.slice(0, 16);
}
async function derivePasswordSeedArgon2id({ login, password, suffix }) {
const normalizedLogin = normalizeLoginForKdf(login);
const normalizedPassword = String(password ?? '');
const normalizedSuffix = String(suffix || '').trim();
const salt = await makeArgon2Salt(normalizedLogin, normalizedSuffix);
const passBytes = utf8Bytes(`${normalizedLogin}\n${normalizedPassword}`);
const out = await argon2idAsync(passBytes, salt, {
t: 3,
m: 262144,
p: 1,
dkLen: 32,
});
return new Uint8Array(out);
}
function ed25519Pkcs8FromSeed(seed32) {
if (seed32.length !== 32) {
throw new Error('Для Ed25519 нужен seed длиной 32 байта');
@@ -79,8 +107,17 @@ function ed25519Pkcs8FromSeed(seed32) {
return out;
}
export async function deriveEd25519FromPassword(password, suffix) {
const seed = await derivePasswordSeed(password, suffix);
export async function deriveEd25519FromPassword(password, suffix, options = {}) {
const normalizedPassword = String(password ?? '');
const normalizedLogin = String(options?.login ?? '');
const useLegacyEmptyPassword = normalizedPassword.length === 0;
const seed = useLegacyEmptyPassword
? await derivePasswordSeed(normalizedPassword, suffix)
: await derivePasswordSeedArgon2id({
login: normalizedLogin,
password: normalizedPassword,
suffix,
});
const pkcs8 = ed25519Pkcs8FromSeed(seed);
const subtle = getSubtleApi();
const privateKey = await subtle.importKey('pkcs8', pkcs8, { name: 'Ed25519' }, true, ['sign']);