Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
2.1 kB · 66 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667import * as Effect from "effect/Effect";export const encode = (bytes: Uint8Array) => btoa(String.fromCharCode(...bytes)) .replaceAll("+", "-") .replaceAll("/", "_") .replaceAll("=", "");export const decode = (value: string) => Uint8Array.from(atob(value.replaceAll("-", "+").replaceAll("_", "/")), (c) => c.charCodeAt(0), );export const random = () => encode(crypto.getRandomValues(new Uint8Array(32)));export const hash = (value: string) => Effect.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)), ).pipe(Effect.map((bytes) => encode(new Uint8Array(bytes))));export const opaque = (value: unknown): value is string => typeof value === "string" && /^[A-Za-z0-9_-]{43}$/.test(value);function key(secret: string) { return Effect.gen(function* () { return yield* Effect.promise(() => crypto.subtle.importKey("raw", decode(secret), "AES-GCM", false, [ "encrypt", "decrypt", ]), ); });}export function encrypt(secret: string, value: unknown, binding: string) { return Effect.gen(function* () { const iv = crypto.getRandomValues(new Uint8Array(12)); const cryptoKey = yield* key(secret); const ciphertext = yield* Effect.promise(() => crypto.subtle.encrypt( { name: "AES-GCM", iv, additionalData: new TextEncoder().encode(binding), }, cryptoKey, new TextEncoder().encode(JSON.stringify(value)), ), ); return { iv: encode(iv), ciphertext: encode(new Uint8Array(ciphertext)) }; });}export function decrypt<T>( secret: string, sealed: { iv: string; ciphertext: string }, binding: string,) { return Effect.gen(function* () { const cryptoKey = yield* key(secret); const bytes = yield* Effect.promise(() => crypto.subtle.decrypt( { name: "AES-GCM", iv: decode(sealed.iv), additionalData: new TextEncoder().encode(binding), }, cryptoKey, decode(sealed.ciphertext), ), ); return JSON.parse(new TextDecoder().decode(bytes)) as T; });}