144 lines
3.4 KiB
TypeScript
144 lines
3.4 KiB
TypeScript
import { ICryptoEncrypted, ICryptoData } from "./types";
|
|
|
|
const importKey = async (key: Uint8Array<ArrayBuffer>): Promise<CryptoKey> => {
|
|
return await crypto.subtle.importKey(
|
|
"raw",
|
|
key,
|
|
{ name: "AES-GCM" },
|
|
false,
|
|
["encrypt", "decrypt"],
|
|
);
|
|
};
|
|
|
|
const deriveKey = async (password: string): Promise<CryptoKey> => {
|
|
const salt = "NEXLINK_FIXED_SALT";
|
|
const encoder = new TextEncoder();
|
|
|
|
const passwordKey = await crypto.subtle.importKey(
|
|
"raw",
|
|
encoder.encode(password),
|
|
"PBKDF2",
|
|
false,
|
|
["deriveKey"],
|
|
);
|
|
|
|
return crypto.subtle.deriveKey(
|
|
{
|
|
name: "PBKDF2",
|
|
salt: encoder.encode(salt),
|
|
iterations: 100000,
|
|
hash: "SHA-256",
|
|
},
|
|
passwordKey,
|
|
{ name: "AES-GCM", length: 256 },
|
|
true,
|
|
["encrypt", "decrypt"],
|
|
);
|
|
};
|
|
|
|
const encryptData = async <T>(
|
|
cryptoData: ICryptoData<T>,
|
|
): Promise<ArrayBuffer> => {
|
|
const encoded = new TextEncoder().encode(JSON.stringify(cryptoData.data));
|
|
const encrypted = await crypto.subtle.encrypt(
|
|
{ name: "AES-GCM", iv: cryptoData.iv },
|
|
cryptoData.key,
|
|
encoded,
|
|
);
|
|
|
|
return encrypted;
|
|
};
|
|
|
|
const decryptData = async <T>(cryptoData: ICryptoEncrypted): Promise<T> => {
|
|
const decrypted = await crypto.subtle.decrypt(
|
|
{ name: "AES-GCM", iv: cryptoData.iv },
|
|
cryptoData.key,
|
|
cryptoData.encryptedData,
|
|
);
|
|
const decoded = JSON.parse(new TextDecoder().decode(decrypted));
|
|
|
|
return decoded as T;
|
|
};
|
|
|
|
const encryptBytes = async (
|
|
cryptoData: ICryptoData<ArrayBuffer>,
|
|
): Promise<ArrayBuffer> => {
|
|
const encrypted = await crypto.subtle.encrypt(
|
|
{ name: "AES-GCM", iv: cryptoData.iv },
|
|
cryptoData.key,
|
|
cryptoData.data,
|
|
);
|
|
|
|
return encrypted;
|
|
};
|
|
|
|
const decryptBytes = async (
|
|
cryptoData: ICryptoEncrypted,
|
|
): Promise<ArrayBuffer> => {
|
|
return await crypto.subtle.decrypt(
|
|
{ name: "AES-GCM", iv: cryptoData.iv },
|
|
cryptoData.key,
|
|
cryptoData.encryptedData,
|
|
);
|
|
};
|
|
|
|
const generateIv = (): Uint8Array<ArrayBuffer> => {
|
|
return crypto.getRandomValues(new Uint8Array(12));
|
|
};
|
|
|
|
const hexToBytes = (hex: string): ArrayBuffer | undefined => {
|
|
if (hex.length % 2 !== 0) {
|
|
return;
|
|
}
|
|
const bytes = new Uint8Array(hex.length / 2);
|
|
for (let i = 0; i < hex.length; i++) {
|
|
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
|
}
|
|
return bytes.buffer;
|
|
};
|
|
|
|
const bytesToHex = (bytes: ArrayBuffer): string => {
|
|
const bytesUint8 = new Uint8Array(bytes);
|
|
let hex = "";
|
|
for (const byte of bytesUint8) {
|
|
hex += byte.toString(16).padStart(2, "0");
|
|
}
|
|
return hex;
|
|
};
|
|
|
|
const bufferToBase64 = (buffer: ArrayBuffer): string => {
|
|
const bytes = new Uint8Array(buffer);
|
|
let binary = "";
|
|
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
|
|
return btoa(binary);
|
|
};
|
|
|
|
const base64ToBuffer = (base64: string): ArrayBuffer => {
|
|
const binary = atob(base64);
|
|
const len = binary.length;
|
|
const bytes = new Uint8Array(len);
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
bytes[i] = binary.charCodeAt(i);
|
|
}
|
|
|
|
return bytes.buffer;
|
|
};
|
|
|
|
export {
|
|
importKey,
|
|
deriveKey,
|
|
encryptData,
|
|
decryptData,
|
|
encryptBytes,
|
|
decryptBytes,
|
|
generateIv,
|
|
hexToBytes,
|
|
bytesToHex,
|
|
bufferToBase64,
|
|
base64ToBuffer,
|
|
};
|