Files
CT/src/Model/Utility/crypto.ts
T

123 lines
3.0 KiB
TypeScript
Raw Normal View History

2026-09-30 17:33:53 +02:00
import cryptoRandomString from "crypto-random-string";
2024-07-21 02:58:59 +02:00
const IV_SIZE = 12;
const ENCRYPTION_ALG = "AES-GCM";
2026-09-30 17:33:53 +02:00
// random
export function random(length: number): string {
2026-09-30 18:57:09 +02:00
return cryptoRandomString({ length });
2026-09-30 17:33:53 +02:00
}
2026-09-30 18:57:09 +02:00
// encrypt
2024-07-21 02:58:59 +02:00
export async function encryptString(
2026-04-01 08:29:39 +00:00
plaintext: string,
passphrase: string,
2024-07-21 02:58:59 +02:00
): Promise<string> {
2026-04-01 08:29:39 +00:00
if (!window.crypto.subtle) return plaintext;
2024-07-21 02:58:59 +02:00
2026-04-01 08:29:39 +00:00
const iv = generateIV();
const key = await importKey(passphrase, "encrypt");
const encryptedArray = await encrypt(iv, key, plaintext);
2024-07-21 02:58:59 +02:00
2026-04-01 08:29:39 +00:00
const encryptionData = {
iv: uInt8ToArray(iv),
encryptedArray: uInt8ToArray(encryptedArray),
};
return btoa(JSON.stringify(encryptionData));
2024-07-21 02:58:59 +02:00
}
export async function decryptString(
2026-04-01 08:29:39 +00:00
cyphertext: string,
passphrase: string,
2024-07-21 02:58:59 +02:00
): Promise<string> {
2026-04-01 08:29:39 +00:00
try {
const encrypionData = JSON.parse(atob(cyphertext));
const iv = arrayToUint8(encrypionData.iv);
const encryptedArray = arrayToUint8(encrypionData.encryptedArray);
const key = await importKey(passphrase, "decrypt");
return await decrypt(iv, key, encryptedArray);
} catch {
return cyphertext;
}
2024-07-21 02:58:59 +02:00
}
// process
2026-04-18 11:55:01 +00:00
function encode(string: string): Uint8Array {
2026-04-01 08:29:39 +00:00
return new TextEncoder().encode(string);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
function decode(array: Uint8Array): string {
2026-04-01 08:29:39 +00:00
return new TextDecoder("utf-8").decode(array);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
async function encrypt(
2026-04-01 08:29:39 +00:00
iv: Uint8Array,
key: CryptoKey,
message: string,
2024-07-21 02:58:59 +02:00
): Promise<Uint8Array> {
2026-04-01 08:29:39 +00:00
const arrayBuffer = await window.crypto.subtle.encrypt(
{ name: ENCRYPTION_ALG, iv },
key,
encode(message),
);
return new Uint8Array(arrayBuffer);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
async function decrypt(
2026-04-01 08:29:39 +00:00
iv: Uint8Array,
key: CryptoKey,
cyphertext: Uint8Array,
2024-07-21 02:58:59 +02:00
): Promise<string> {
2026-04-01 08:29:39 +00:00
const arrayBuffer = await crypto.subtle.decrypt(
{ name: ENCRYPTION_ALG, iv },
key,
cyphertext,
);
return arrayBufferToString(arrayBuffer);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
async function hash(encoded: Uint8Array): Promise<ArrayBuffer> {
2026-04-01 08:29:39 +00:00
return await crypto.subtle.digest("SHA-256", encoded);
2024-07-21 02:58:59 +02:00
}
// iv
2026-04-18 11:55:01 +00:00
function generateIV(): Uint8Array {
2026-04-01 08:29:39 +00:00
return crypto.getRandomValues(new Uint8Array(IV_SIZE));
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
function splitMergedString(mergedString: string): [Uint8Array, string] {
2026-04-01 08:29:39 +00:00
const iv = mergedString.slice(0, IV_SIZE);
const encryptedString = mergedString.slice(IV_SIZE);
2024-07-21 02:58:59 +02:00
2026-04-01 08:29:39 +00:00
return [JSON.parse(iv), encryptedString];
2024-07-21 02:58:59 +02:00
}
// key
2026-04-18 11:55:01 +00:00
async function importKey(
2026-04-01 08:29:39 +00:00
passphrase: string,
purpose: "encrypt" | "decrypt",
2024-07-21 02:58:59 +02:00
): Promise<CryptoKey> {
2026-04-01 08:29:39 +00:00
return await crypto.subtle.importKey(
"raw",
await hash(encode(passphrase)),
{ name: ENCRYPTION_ALG },
false,
[purpose],
);
2024-07-21 02:58:59 +02:00
}
// conversion
2026-04-18 11:55:01 +00:00
function arrayBufferToString(arrayBuffer: ArrayBuffer): string {
2026-04-01 08:29:39 +00:00
const uInt8Array = new Uint8Array(arrayBuffer);
return decode(uInt8Array);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
function uInt8ToArray(uInt8Array: Uint8Array): number[] {
2026-04-01 08:29:39 +00:00
return Array.from(uInt8Array);
2024-07-21 02:58:59 +02:00
}
2026-04-18 11:55:01 +00:00
function arrayToUint8(array: number[]): Uint8Array {
2026-04-01 08:29:39 +00:00
return new Uint8Array(array);
2024-07-21 02:58:59 +02:00
}