UNPKG

ndwallet-core

Version:

Core cryptographic library for NDWallet browser environments

106 lines (105 loc) 3.95 kB
/** * Generate a random salt * @returns Uint8Array containing the salt */ export function generateRandomSalt() { return crypto.getRandomValues(new Uint8Array(32)); } /** * Derive a key from password and salt using PBKDF2 * @param password - The password * @param salt - The salt to use for PBKDF2 * @returns Promise resolving to a CryptoKey */ export async function derivePbkdf2EncryptionKey(password, salt) { // Convert to an ArrayBuffer const encoder = new TextEncoder(); const passwordBuffer = encoder.encode(password); const saltBuffer = encoder.encode(salt); // Derive a key using PBKDF2 const baseKey = await window.crypto.subtle.importKey('raw', passwordBuffer, { name: 'PBKDF2' }, false, ['deriveBits', 'deriveKey']); // Use PBKDF2 to derive a key const derivedKey = await window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt: saltBuffer, iterations: 100000, hash: 'SHA-256' }, baseKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']); return derivedKey; } /** * Derive a specific encryption key from the master key using HKDF * @param masterKey - Master key derived from passkey * @param context - Context string to derive different keys for different purposes * @returns Promise resolving to a CryptoKey that can be used for encryption/decryption */ export async function deriveHkdfEncryptionKey(masterKey, context) { try { // Create a salt from the context const encoder = new TextEncoder(); const contextBytes = encoder.encode(context); const salt = await crypto.subtle.digest('SHA-256', contextBytes); // Derive key using HKDF return await crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt: salt, info: new Uint8Array(0) }, masterKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']); } catch (error) { console.error('Error deriving encryption key:', error); if (error instanceof Error) { throw new Error(`Encryption key derivation failed: ${error.message}`); } else { throw new Error(`Encryption key derivation failed: ${String(error)}`); } } } // Encrypt data using AES-GCM export async function encryptData(data, key) { try { // Generate a random initialization vector const iv = window.crypto.getRandomValues(new Uint8Array(12)); // Convert the data to an ArrayBuffer const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); // Encrypt the data const encryptedBuffer = await window.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, dataBuffer); // Convert the encrypted data to an array of numbers const encryptedArray = Array.from(new Uint8Array(encryptedBuffer)); const ivArray = Array.from(iv); return { iv: ivArray, data: encryptedArray }; } catch (error) { console.error('Encryption error:', error); throw new Error('Failed to encrypt data'); } } // Decrypt data using AES-GCM export async function decryptData(encryptedData, key) { try { // Convert the encrypted data and IV back to Uint8Arrays const encryptedBuffer = new Uint8Array(encryptedData.data); const ivBuffer = new Uint8Array(encryptedData.iv); // Decrypt the data const decryptedBuffer = await window.crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuffer }, key, encryptedBuffer); // Convert the decrypted data back to a string const decoder = new TextDecoder(); return decoder.decode(decryptedBuffer); } catch (error) { console.error('Decryption error:', error); throw new Error('Failed to decrypt data'); } }