LFM2-WebGPU / src /utils /storage.ts
shreyask's picture
add mcp support
c211e0e verified
raw
history blame
2.98 kB
/**
* Secure storage utilities for sensitive data like OAuth tokens
*/
const ENCRYPTION_KEY_NAME = 'mcp-encryption-key';
// Generate or retrieve encryption key
async function getEncryptionKey(): Promise<CryptoKey> {
const keyData = localStorage.getItem(ENCRYPTION_KEY_NAME);
if (keyData) {
try {
const keyBuffer = new Uint8Array(JSON.parse(keyData));
return await crypto.subtle.importKey(
'raw',
keyBuffer,
{ name: 'AES-GCM' },
false,
['encrypt', 'decrypt']
);
} catch {
// Key corrupted, generate new one
}
}
// Generate new key
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
// Store key for future use
const keyBuffer = await crypto.subtle.exportKey('raw', key);
localStorage.setItem(ENCRYPTION_KEY_NAME, JSON.stringify(Array.from(new Uint8Array(keyBuffer))));
return key;
}
// Encrypt sensitive data
export async function encryptData(data: string): Promise<string> {
try {
const key = await getEncryptionKey();
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(data);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedBuffer = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
dataBuffer
);
// Combine IV and encrypted data
const result = new Uint8Array(iv.length + encryptedBuffer.byteLength);
result.set(iv);
result.set(new Uint8Array(encryptedBuffer), iv.length);
return btoa(String.fromCharCode(...result));
} catch (error) {
console.warn('Encryption failed, storing data unencrypted:', error);
return data;
}
}
// Decrypt sensitive data
export async function decryptData(encryptedData: string): Promise<string> {
try {
const key = await getEncryptionKey();
const dataBuffer = new Uint8Array(
atob(encryptedData).split('').map(char => char.charCodeAt(0))
);
const iv = dataBuffer.slice(0, 12);
const encrypted = dataBuffer.slice(12);
const decryptedBuffer = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
key,
encrypted
);
const decoder = new TextDecoder();
return decoder.decode(decryptedBuffer);
} catch (error) {
console.warn('Decryption failed, returning data as-is:', error);
return encryptedData;
}
}
// Secure storage wrapper for sensitive data
export const secureStorage = {
async setItem(key: string, value: string): Promise<void> {
const encrypted = await encryptData(value);
localStorage.setItem(`secure_${key}`, encrypted);
},
async getItem(key: string): Promise<string | null> {
const encrypted = localStorage.getItem(`secure_${key}`);
if (!encrypted) return null;
return await decryptData(encrypted);
},
removeItem(key: string): void {
localStorage.removeItem(`secure_${key}`);
}
};