web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

keys.ts (3060B)


      1 import { as_array_buffer } from "@radroots/utils";
      2 import { cl_crypto_error } from "./error.js";
      3 
      4 const KEY_ID_BYTES_LENGTH = 16;
      5 const WRAP_IV_LENGTH = 12;
      6 
      7 const bytes_to_hex = (bytes: Uint8Array): string => {
      8     let out = "";
      9     for (let i = 0; i < bytes.length; i++) {
     10         const part = bytes[i].toString(16).padStart(2, "0");
     11         out += part;
     12     }
     13     return out;
     14 };
     15 
     16 export const crypto_key_id_create = (): string => {
     17     if (!globalThis.crypto) throw new Error(cl_crypto_error.crypto_undefined);
     18     const bytes = new Uint8Array(KEY_ID_BYTES_LENGTH);
     19     crypto.getRandomValues(bytes);
     20     return bytes_to_hex(bytes);
     21 };
     22 
     23 export const crypto_key_generate = async (): Promise<CryptoKey> => {
     24     if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_crypto_error.crypto_undefined);
     25     return await crypto.subtle.generateKey(
     26         {
     27             name: "AES-GCM",
     28             length: 256
     29         },
     30         true,
     31         ["encrypt", "decrypt"]
     32     );
     33 };
     34 
     35 export const crypto_key_export_raw = async (key: CryptoKey): Promise<Uint8Array> => {
     36     if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_crypto_error.crypto_undefined);
     37     const raw = await crypto.subtle.exportKey("raw", key);
     38     return new Uint8Array(raw);
     39 };
     40 
     41 export const crypto_key_import_raw = async (raw: Uint8Array): Promise<CryptoKey> => {
     42     if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_crypto_error.crypto_undefined);
     43     return await crypto.subtle.importKey(
     44         "raw",
     45         as_array_buffer(raw),
     46         "AES-GCM",
     47         false,
     48         ["encrypt", "decrypt"]
     49     );
     50 };
     51 
     52 export const crypto_key_wrap = async (
     53     kek: CryptoKey,
     54     raw_key: Uint8Array
     55 ): Promise<{ wrapped_key: Uint8Array; wrap_iv: Uint8Array; }> => {
     56     if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_crypto_error.crypto_undefined);
     57     try {
     58         const wrap_iv = new Uint8Array(WRAP_IV_LENGTH);
     59         crypto.getRandomValues(wrap_iv);
     60         const cipher_buf = await crypto.subtle.encrypt(
     61             {
     62                 name: "AES-GCM",
     63                 iv: as_array_buffer(wrap_iv)
     64             },
     65             kek,
     66             as_array_buffer(raw_key)
     67         );
     68         const wrapped_key = new Uint8Array(cipher_buf);
     69         raw_key.fill(0);
     70         return { wrapped_key, wrap_iv };
     71     } catch {
     72         throw new Error(cl_crypto_error.wrap_failure);
     73     }
     74 };
     75 
     76 export const crypto_key_unwrap = async (
     77     kek: CryptoKey,
     78     wrapped_key: Uint8Array,
     79     wrap_iv: Uint8Array
     80 ): Promise<CryptoKey> => {
     81     if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_crypto_error.crypto_undefined);
     82     try {
     83         const raw = await crypto.subtle.decrypt(
     84             {
     85                 name: "AES-GCM",
     86                 iv: as_array_buffer(wrap_iv)
     87             },
     88             kek,
     89             as_array_buffer(wrapped_key)
     90         );
     91         return await crypto_key_import_raw(new Uint8Array(raw));
     92     } catch {
     93         throw new Error(cl_crypto_error.unwrap_failure);
     94     }
     95 };