| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- import { ENCRYPTION_KEY_BITS } from "../constants";
- import { blobToArrayBuffer } from "./blob";
- export const IV_LENGTH_BYTES = 12;
- export const createIV = () => {
- const arr = new Uint8Array(IV_LENGTH_BYTES);
- return window.crypto.getRandomValues(arr);
- };
- export const generateEncryptionKey = async <
- T extends "string" | "cryptoKey" = "string",
- >(
- returnAs?: T,
- ): Promise<T extends "cryptoKey" ? CryptoKey : string> => {
- const key = await window.crypto.subtle.generateKey(
- {
- name: "AES-GCM",
- length: ENCRYPTION_KEY_BITS,
- },
- true, // extractable
- ["encrypt", "decrypt"],
- );
- return (
- returnAs === "cryptoKey"
- ? key
- : (await window.crypto.subtle.exportKey("jwk", key)).k
- ) as T extends "cryptoKey" ? CryptoKey : string;
- };
- export const getCryptoKey = (key: string, usage: KeyUsage) =>
- window.crypto.subtle.importKey(
- "jwk",
- {
- alg: "A128GCM",
- ext: true,
- k: key,
- key_ops: ["encrypt", "decrypt"],
- kty: "oct",
- },
- {
- name: "AES-GCM",
- length: ENCRYPTION_KEY_BITS,
- },
- false, // extractable
- [usage],
- );
- export const encryptData = async (
- key: string | CryptoKey,
- data: Uint8Array | ArrayBuffer | Blob | File | string,
- ): Promise<{ encryptedBuffer: ArrayBuffer; iv: Uint8Array }> => {
- const importedKey =
- typeof key === "string" ? await getCryptoKey(key, "encrypt") : key;
- const iv = createIV();
- const buffer: ArrayBuffer | Uint8Array =
- typeof data === "string"
- ? new TextEncoder().encode(data)
- : data instanceof Uint8Array
- ? data
- : data instanceof Blob
- ? await blobToArrayBuffer(data)
- : data;
- // We use symmetric encryption. AES-GCM is the recommended algorithm and
- // includes checks that the ciphertext has not been modified by an attacker.
- const encryptedBuffer = await window.crypto.subtle.encrypt(
- {
- name: "AES-GCM",
- iv,
- },
- importedKey,
- buffer as ArrayBuffer | Uint8Array,
- );
- return { encryptedBuffer, iv };
- };
- export const decryptData = async (
- iv: Uint8Array,
- encrypted: Uint8Array | ArrayBuffer,
- privateKey: string,
- ): Promise<ArrayBuffer> => {
- const key = await getCryptoKey(privateKey, "decrypt");
- return window.crypto.subtle.decrypt(
- {
- name: "AES-GCM",
- iv,
- },
- key,
- encrypted,
- );
- };
|