WhisperNet / lib /crypto.ts
bonesmasher's picture
Upload 56 files
abc1805 verified
export async function generateKeyPair() {
return await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
}
export async function exportKey(key: CryptoKey) {
const exported = await crypto.subtle.exportKey("jwk", key);
return exported;
}
export async function importKey(jwk: JsonWebKey, usage: KeyUsage[]) {
return await crypto.subtle.importKey(
"jwk",
jwk,
{
name: "RSA-OAEP",
hash: "SHA-256",
},
true,
usage
);
}
export async function encryptMessage(publicKey: CryptoKey, message: string) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const encrypted = await crypto.subtle.encrypt(
{
name: "RSA-OAEP",
},
publicKey,
data
);
return arrayBufferToBase64(encrypted);
}
export async function decryptMessage(privateKey: CryptoKey, encryptedMessage: string) {
const data = base64ToArrayBuffer(encryptedMessage);
const decrypted = await crypto.subtle.decrypt(
{
name: "RSA-OAEP",
},
privateKey,
data
);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
function arrayBufferToBase64(buffer: ArrayBuffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
function base64ToArrayBuffer(base64: string) {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
// ========== AES-GCM Functions for Hybrid Encryption ==========
export async function generateSymKey() {
return await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"]
);
}
export async function encryptSymMessage(key: CryptoKey, message: string) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
data
);
// Combine IV and ciphertext
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encrypted), iv.length);
return arrayBufferToBase64(combined.buffer);
}
export async function decryptSymMessage(key: CryptoKey, encryptedMessage: string) {
const combined = base64ToArrayBuffer(encryptedMessage);
const combinedArray = new Uint8Array(combined);
const iv = combinedArray.slice(0, 12);
const data = combinedArray.slice(12);
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
data
);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
export async function exportSymKey(key: CryptoKey) {
const exported = await crypto.subtle.exportKey("raw", key);
return arrayBufferToBase64(exported);
}
export async function importSymKey(rawKey: string) {
const buffer = base64ToArrayBuffer(rawKey);
return await crypto.subtle.importKey(
"raw",
buffer,
{
name: "AES-GCM",
},
true,
["encrypt", "decrypt"]
);
}
// ========== FILE ENCRYPTION FUNCTIONS ==========
/**
* Encrypt a file (ArrayBuffer) with AES-GCM
*/
export async function encryptFile(aesKey: CryptoKey, fileData: ArrayBuffer): Promise<ArrayBuffer> {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
aesKey,
fileData
);
// Combine IV + encrypted data
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv, 0);
combined.set(new Uint8Array(encrypted), iv.length);
return combined.buffer;
}
/**
* Decrypt a file (ArrayBuffer) with AES-GCM
*/
export async function decryptFile(aesKey: CryptoKey, encryptedData: ArrayBuffer): Promise<ArrayBuffer> {
const combined = new Uint8Array(encryptedData);
const iv = combined.slice(0, 12);
const data = combined.slice(12);
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv,
},
aesKey,
data
);
return decrypted;
}