UNPKG

chaingate

Version:

Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO

78 lines (77 loc) 3.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Web3Keystore = void 0; const scrypt_js_1 = require("@noble/hashes/scrypt.js"); const sha3_js_1 = require("@noble/hashes/sha3.js"); const encoding_1 = require("../utils/encoding"); const errors_1 = require("../errors"); /** Web3 (V3) keystore. */ class Web3Keystore { constructor(keystoreData) { this.keystoreData = keystoreData; } async checkPassword(password) { try { const derivedKey = await this.deriveKey(password); return this.verifyMac(derivedKey); } catch { return false; } } async decrypt(password) { const derivedKey = await this.deriveKey(password); if (!this.verifyMac(derivedKey)) { throw new errors_1.IncorrectKeystorePasswordError(); } const iv = (0, encoding_1.hexToBytes)(this.keystoreData.crypto.cipherparams.iv); const ciphertext = (0, encoding_1.hexToBytes)(this.keystoreData.crypto.ciphertext); const encryptionKey = derivedKey.slice(0, 16); const cryptoKey = await crypto.subtle.importKey('raw', encryptionKey.buffer, 'AES-CTR', false, ['decrypt']); const decrypted = await crypto.subtle.decrypt({ name: 'AES-CTR', counter: iv.buffer, length: 128 }, cryptoKey, ciphertext.buffer); return new Uint8Array(decrypted); } async deriveKey(password) { const kdfparams = this.keystoreData.crypto.kdfparams; const salt = (0, encoding_1.hexToBytes)(kdfparams.salt); const dkLen = kdfparams.dklen; const passwordBytes = new TextEncoder().encode(password); if (this.keystoreData.crypto.kdf === 'scrypt') { const N = kdfparams.n; const r = kdfparams.r; const p = kdfparams.p; if (!N || !r || !p) { throw new errors_1.InvalidKeystoreError('Missing scrypt parameters'); } return (0, scrypt_js_1.scryptAsync)(passwordBytes, salt, { N, r, p, dkLen }); } if (this.keystoreData.crypto.kdf === 'pbkdf2') { const iterations = kdfparams.c; if (!iterations) { throw new errors_1.InvalidKeystoreError('Missing PBKDF2 iterations'); } const prf = kdfparams.prf ?? 'hmac-sha256'; const hashAlgo = prf === 'hmac-sha256' ? 'SHA-256' : 'SHA-512'; const keyMaterial = await crypto.subtle.importKey('raw', passwordBytes, 'PBKDF2', false, [ 'deriveBits', ]); const bits = await crypto.subtle.deriveBits({ name: 'PBKDF2', salt: salt.buffer, iterations, hash: hashAlgo }, keyMaterial, dkLen * 8); return new Uint8Array(bits); } throw new errors_1.InvalidKeystoreError(`Unsupported KDF: ${this.keystoreData.crypto.kdf}`); } verifyMac(derivedKey) { const ciphertext = (0, encoding_1.hexToBytes)(this.keystoreData.crypto.ciphertext); // MAC = keccak256(derivedKey[16:32] + ciphertext) const macInput = new Uint8Array(16 + ciphertext.length); macInput.set(derivedKey.slice(16, 32)); macInput.set(ciphertext, 16); const computedMac = (0, encoding_1.bytesToHex)((0, sha3_js_1.keccak_256)(macInput)); return computedMac === this.keystoreData.crypto.mac; } /** Checks whether a parsed JSON object looks like a V3 keystore. */ static isKeystore(obj) { return 'version' in obj && obj.version == 3; } } exports.Web3Keystore = Web3Keystore;