feat(admin): decrypt mnemonics + balance column in seed phrases
- lib/mnemonic.ts: decryptMnemonic (same AES-256-CBC+HKDF algo as bot, uses ENCRYPTION_KEY) - wallets/seeds API: decrypts mnemonic per user, adds balance field - wallets/export-seeds (CSV): decrypts mnemonic, adds Balance column - wallets-page: Balance column added to seed phrases table - compose: ENCRYPTION_KEY passed to tg_shop_admin (same key as bot)
This commit is contained in:
49
admin-next/src/lib/mnemonic.ts
Normal file
49
admin-next/src/lib/mnemonic.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
// Дешифровка мнемоник — тот же алгоритм, что в боте (src/utils/encryption.js)
|
||||
const HKDF_SALT_LENGTH = 32;
|
||||
const IV_LENGTH = 16;
|
||||
const KEY_LENGTH = 32;
|
||||
const HKDF_INFO = 'telegram-shop-mnemonic-encryption';
|
||||
|
||||
function deriveKeyHKDF(masterKey: string, salt: Buffer, userId: number) {
|
||||
return crypto.hkdfSync(
|
||||
'sha256',
|
||||
Buffer.from(masterKey, 'utf8'),
|
||||
salt,
|
||||
HKDF_INFO + ':' + userId.toString(),
|
||||
KEY_LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
function deriveKeyLegacy(masterKey: string, userId: number) {
|
||||
return crypto.createHash('sha256').update(masterKey + userId.toString()).digest();
|
||||
}
|
||||
|
||||
export function decryptMnemonic(encryptedData: string, userId: number): string {
|
||||
const parts = encryptedData.split(':');
|
||||
const masterKey = process.env.ENCRYPTION_KEY || '';
|
||||
|
||||
if (parts.length === 3) {
|
||||
const salt = Buffer.from(parts[0], 'hex');
|
||||
const key = deriveKeyHKDF(masterKey, salt, userId);
|
||||
const iv = Buffer.from(parts[1], 'hex');
|
||||
const ciphertext = parts[2];
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||||
let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
if (parts.length === 2) {
|
||||
const key = deriveKeyLegacy(masterKey, userId);
|
||||
const iv = Buffer.from(parts[0], 'hex');
|
||||
const ciphertext = parts[1];
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||||
let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
throw new Error('Invalid encrypted data format');
|
||||
}
|
||||
Reference in New Issue
Block a user