p-sdk-wallet
Version:
A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.
179 lines (178 loc) • 7.38 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EncryptionService = void 0;
const CryptoJS = __importStar(require("crypto-js"));
const bip39 = __importStar(require("bip39"));
const bip32_1 = require("bip32");
const ecc = __importStar(require("@bitcoinerlab/secp256k1"));
const buffer_1 = require("buffer");
const chains_1 = require("../config/chains");
const constants_1 = require("../config/constants");
const bip32 = (0, bip32_1.BIP32Factory)(ecc);
const SALT_LENGTH = constants_1.SECURITY_CONFIG.SALT_LENGTH;
const KEY_LENGTH = constants_1.SECURITY_CONFIG.AES_KEY_SIZE / 8; // Convert bits to bytes
const IV_LENGTH = constants_1.SECURITY_CONFIG.IV_LENGTH;
/**
* Service providing cryptographic operations including mnemonic generation,
* encryption/decryption, and key derivation for the wallet SDK.
*/
class EncryptionService {
/**
* Generates a cryptographically secure mnemonic phrase.
* @returns A 24-word mnemonic phrase following BIP-39 standard
*/
static generateMnemonic() {
return bip39.generateMnemonic(256);
}
/**
* Validates a mnemonic phrase to ensure it's properly formatted and contains valid words.
* @param mnemonic - The mnemonic phrase to validate (12, 15, 18, 21, or 24 words)
* @returns True if the mnemonic is valid, false otherwise
*/
static validateMnemonic(mnemonic) {
return bip39.validateMnemonic(mnemonic);
}
/**
* Encrypts data using AES-256-CBC with PBKDF2 key derivation.
* @param data - The plaintext data to encrypt
* @param password - The password to use for key derivation
* @returns Promise resolving to the encrypted data structure
* @throws Error if encryption fails
*/
static async encrypt(data, password) {
const salt = CryptoJS.lib.WordArray.random(SALT_LENGTH);
const iv = CryptoJS.lib.WordArray.random(IV_LENGTH);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: KEY_LENGTH,
iterations: (0, constants_1.getPBKDF2Iterations)(),
hasher: CryptoJS.algo.SHA256,
});
const encrypted = CryptoJS.AES.encrypt(data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
return {
encryptedData: encrypted.toString(),
iv: CryptoJS.enc.Base64.stringify(iv),
salt: CryptoJS.enc.Base64.stringify(salt),
};
}
/**
* Decrypts data that was encrypted using the encrypt method.
* @param encryptedData - The encrypted data structure containing data, iv, and salt
* @param password - The password used for the original encryption
* @returns Promise resolving to the decrypted plaintext data
* @throws Error if decryption fails due to incorrect password or corrupted data
*/
static async decrypt(encryptedData, password) {
const salt = CryptoJS.enc.Base64.parse(encryptedData.salt);
const iv = CryptoJS.enc.Base64.parse(encryptedData.iv);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: KEY_LENGTH,
iterations: (0, constants_1.getPBKDF2Iterations)(),
hasher: CryptoJS.algo.SHA256,
});
const decrypted = CryptoJS.AES.decrypt(encryptedData.encryptedData, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
if (!decryptedText) {
throw new Error("Decryption failed. Invalid password or corrupted data.");
}
return decryptedText;
}
/**
* Converts a mnemonic phrase to a seed buffer using PBKDF2.
* @param mnemonic - The mnemonic phrase to convert
* @returns Promise resolving to the seed buffer
*/
static async mnemonicToSeed(mnemonic) {
return bip39.mnemonicToSeed(mnemonic);
}
/**
* Derives a private key from a seed using BIP-32 derivation.
* @param seed - The seed buffer to derive from
* @param path - The derivation path to use (defaults to EVM path)
* @returns Promise resolving to the derived private key buffer
* @throws Error if private key derivation fails
*/
static async derivePrivateKey(seed, path = chains_1.DERIVATION_PATHS.EVM) {
const root = bip32.fromSeed(seed);
const child = root.derivePath(path);
if (!child.privateKey) {
throw new Error('Could not derive private key.');
}
return buffer_1.Buffer.from(child.privateKey);
}
/**
* Attempts to clear sensitive data from memory.
* Note: JavaScript doesn't guarantee memory clearing, but this helps reduce exposure.
* @param data - The sensitive data to clear (string or buffer)
*/
static clearSensitiveData(data) {
if (typeof data === 'string') {
// For strings, we can't directly clear memory, but we can help GC
// by removing references and using setTimeout to delay cleanup
setTimeout(() => {
// This helps reduce the time sensitive data stays in memory
}, 0);
}
else if (buffer_1.Buffer.isBuffer(data)) {
// For buffers, we can fill with zeros
data.fill(0);
}
}
/**
* Creates a temporary copy of sensitive data that gets cleared after use.
* This is a security measure to minimize the time sensitive data stays in memory.
* @param data - The sensitive data to use temporarily
* @param callback - Function to execute with the temporary data
* @returns The result of the callback function
*/
static withTemporaryData(data, callback) {
try {
const result = callback(data);
return result;
}
finally {
this.clearSensitiveData(data);
}
}
}
exports.EncryptionService = EncryptionService;