Download apps/web/src/lib/crypto.ts from Atmint/cpns: direct link, hf CLI and curl.
- Browser
- Download file 3.37 kB
-
https://huggingface.co/spaces/Atmint/cpns/resolve/main/apps/web/src/lib/crypto.ts
- Command line
-
hf download hf://spaces/Atmint/cpns/apps/web/src/lib/crypto.ts
-
curl -L -o crypto.ts https://huggingface.co/spaces/Atmint/cpns/resolve/main/apps/web/src/lib/crypto.ts
3.37 kB
| const DB_NAME = "pram-crypto"; | |
| const STORE_NAME = "keys"; | |
| const KEY_NAME = "pram-master-key"; | |
| function openDB(): Promise<IDBDatabase> { | |
| return new Promise((resolve, reject) => { | |
| const request = indexedDB.open(DB_NAME, 1); | |
| request.onerror = () => reject(request.error); | |
| request.onsuccess = () => resolve(request.result); | |
| request.onupgradeneeded = () => { | |
| const db = request.result; | |
| if (!db.objectStoreNames.contains(STORE_NAME)) { | |
| db.createObjectStore(STORE_NAME); | |
| } | |
| }; | |
| }); | |
| } | |
| async function getFromStore(db: IDBDatabase, key: string): Promise<Uint8Array | undefined> { | |
| return new Promise((resolve, reject) => { | |
| const tx = db.transaction(STORE_NAME, "readonly"); | |
| const store = tx.objectStore(STORE_NAME); | |
| const request = store.get(key); | |
| request.onsuccess = () => resolve(request.result); | |
| request.onerror = () => reject(request.error); | |
| }); | |
| } | |
| async function putToStore(db: IDBDatabase, key: string, value: Uint8Array): Promise<void> { | |
| return new Promise((resolve, reject) => { | |
| const tx = db.transaction(STORE_NAME, "readwrite"); | |
| const store = tx.objectStore(STORE_NAME); | |
| const request = store.put(value, key); | |
| request.onsuccess = () => resolve(); | |
| request.onerror = () => reject(request.error); | |
| }); | |
| } | |
| async function getOrCreateMasterKey(): Promise<CryptoKey> { | |
| const db = await openDB(); | |
| const stored = await getFromStore(db, KEY_NAME); | |
| if (stored) { | |
| return crypto.subtle.importKey( | |
| "raw", | |
| stored.buffer as ArrayBuffer, | |
| { name: "AES-GCM" }, | |
| false, | |
| ["encrypt", "decrypt"], | |
| ); | |
| } | |
| const key = await crypto.subtle.generateKey( | |
| { name: "AES-GCM", length: 256 }, | |
| true, | |
| ["encrypt", "decrypt"], | |
| ); | |
| const exported = await crypto.subtle.exportKey("raw", key); | |
| await putToStore(db, KEY_NAME, new Uint8Array(exported)); | |
| return key; | |
| } | |
| interface EncryptedData { | |
| iv: string; | |
| data: string; | |
| } | |
| export async function encryptText(plaintext: string): Promise<string> { | |
| const key = await getOrCreateMasterKey(); | |
| const iv = crypto.getRandomValues(new Uint8Array(12)); | |
| const encoded = new TextEncoder().encode(plaintext); | |
| const ciphertext = await crypto.subtle.encrypt( | |
| { name: "AES-GCM", iv }, | |
| key, | |
| encoded, | |
| ); | |
| const combined: EncryptedData = { | |
| iv: arrayBufferToBase64(iv), | |
| data: arrayBufferToBase64(ciphertext), | |
| }; | |
| return JSON.stringify(combined); | |
| } | |
| export async function decryptText(encrypted: string): Promise<string> { | |
| const key = await getOrCreateMasterKey(); | |
| const parsed: EncryptedData = JSON.parse(encrypted); | |
| const iv = base64ToArrayBuffer(parsed.iv); | |
| const ciphertext = base64ToArrayBuffer(parsed.data); | |
| const decrypted = await crypto.subtle.decrypt( | |
| { name: "AES-GCM", iv }, | |
| key, | |
| ciphertext, | |
| ); | |
| return new TextDecoder().decode(decrypted); | |
| } | |
| function arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string { | |
| const bytes = new Uint8Array(buffer); | |
| let binary = ""; | |
| for (let i = 0; i < bytes.byteLength; i++) { | |
| binary += String.fromCharCode(bytes[i]); | |
| } | |
| return btoa(binary); | |
| } | |
| function base64ToArrayBuffer(base64: string): ArrayBuffer { | |
| const binary = atob(base64); | |
| const bytes = new Uint8Array(binary.length); | |
| for (let i = 0; i < binary.length; i++) { | |
| bytes[i] = binary.charCodeAt(i); | |
| } | |
| return bytes.buffer; | |
| } | |