Spaces:
Running
Running
File size: 2,977 Bytes
c211e0e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
/**
* 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}`);
}
}; |