streaming, markdown, model selecting, and lots more
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
export async function deriveKey(password: string, userId: string) {
|
||||
const iterations = 1_000_000;
|
||||
const saltBuffer = new TextEncoder().encode(userId);
|
||||
const passwordBuffer = new TextEncoder().encode(password);
|
||||
|
||||
const baseKey = await window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
passwordBuffer,
|
||||
{ name: "PBKDF2" },
|
||||
false,
|
||||
["deriveKey"]
|
||||
);
|
||||
|
||||
const derivedKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: saltBuffer,
|
||||
iterations: iterations,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
baseKey,
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256
|
||||
},
|
||||
true,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
|
||||
const encryptionKey = await crypto.subtle.exportKey(
|
||||
'jwk',
|
||||
derivedKey
|
||||
);
|
||||
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
function generateIv() {
|
||||
return window.crypto.getRandomValues(new Uint8Array(12));
|
||||
}
|
||||
|
||||
export function uint8ArrayToBase64(bytes: Uint8Array<ArrayBuffer>) {
|
||||
let binaryString = '';
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binaryString += String.fromCharCode(bytes[i]!);
|
||||
}
|
||||
return window.btoa(binaryString);
|
||||
}
|
||||
|
||||
export function base64ToUint8Array(base64: string) {
|
||||
const binaryString = window.atob(base64);
|
||||
const len = binaryString.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export async function encryptData(key: CryptoKey, plaintext: string) {
|
||||
const iv = generateIv();
|
||||
const data = new TextEncoder().encode(plaintext);
|
||||
|
||||
const encryptedContent = await window.crypto.subtle.encrypt(
|
||||
{ name: "AES-GCM", iv: iv },
|
||||
key,
|
||||
data
|
||||
);
|
||||
|
||||
// Combine IV and Ciphertext into one Uint8Array for easy storage
|
||||
const combined = new Uint8Array(iv.length + encryptedContent.byteLength);
|
||||
combined.set(iv);
|
||||
combined.set(new Uint8Array(encryptedContent), iv.length);
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
export async function decrypt(key: CryptoKey, combinedData: Uint8Array) {
|
||||
try {
|
||||
const iv = combinedData.slice(0, 12);
|
||||
const ciphertext = combinedData.slice(12);
|
||||
|
||||
const decryptedBuffer = await window.crypto.subtle.decrypt(
|
||||
{ name: "AES-GCM", iv: iv },
|
||||
key,
|
||||
ciphertext
|
||||
);
|
||||
|
||||
return new TextDecoder().decode(decryptedBuffer);
|
||||
} catch (error) {
|
||||
throw new Error("Decryption failed. Incorrect password or corrupted data.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user