1function ensureWebCrypto() { 2 if (!globalThis.crypto || !globalThis.crypto.subtle) { 3 throw new Error("Web Crypto API is unavailable. Password Manager requires a modern browser over HTTPS."); 4 } 5 6 return globalThis.crypto; 7} 8 9function fromBase64(base64) { 10 const binary = globalThis.atob(base64); 11 const bytes = new Uint8Array(binary.length); 12 13 for (let index = 0; index < binary.length; index += 1) { 14 bytes[index] = binary.charCodeAt(index); 15 } 16 17 return bytes; 18} 19 20function toBase64(bytes) { 21 const chunkSize = 0x8000; 22 let binary = ""; 23 24 for (let index = 0; index < bytes.length; index += chunkSize) { 25 const chunk = bytes.subarray(index, index + chunkSize); 26 binary += String.fromCharCode(...chunk); 27 } 28 29 return globalThis.btoa(binary); 30} 31 32export function getRandomBytes(length) { 33 const crypto = ensureWebCrypto(); 34 const bytes = new Uint8Array(length); 35 crypto.getRandomValues(bytes); 36 return toBase64(bytes); 37} 38 39export async function derivePbkdf2Sha256(passwordBase64, saltBase64, iterations, outputLengthBytes) { 40 const crypto = ensureWebCrypto(); 41 const password = fromBase64(passwordBase64); 42 const salt = fromBase64(saltBase64); 43 const keyMaterial = await crypto.subtle.importKey("raw", password, "PBKDF2", false, ["deriveBits"]); 44 const derivedBits = await crypto.subtle.deriveBits( 45 { 46 name: "PBKDF2", 47 hash: "SHA-256", 48 salt, 49 iterations 50 }, 51 keyMaterial, 52 outputLengthBytes * 8); 53 54 return toBase64(new Uint8Array(derivedBits)); 55} 56 57export async function sha256(dataBase64) { 58 const crypto = ensureWebCrypto(); 59 const digest = await crypto.subtle.digest("SHA-256", fromBase64(dataBase64)); 60 return toBase64(new Uint8Array(digest)); 61} 62 63export async function aesCbcEncrypt(keyBase64, ivBase64, dataBase64) { 64 const crypto = ensureWebCrypto(); 65 const key = await crypto.subtle.importKey("raw", fromBase64(keyBase64), { name: "AES-CBC" }, false, ["encrypt"]); 66 const ciphertext = await crypto.subtle.encrypt( 67 { 68 name: "AES-CBC", 69 iv: fromBase64(ivBase64) 70 }, 71 key, 72 fromBase64(dataBase64)); 73 74 return toBase64(new Uint8Array(ciphertext)); 75} 76 77export async function aesCbcDecrypt(keyBase64, ivBase64, dataBase64) { 78 const crypto = ensureWebCrypto(); 79 const key = await crypto.subtle.importKey("raw", fromBase64(keyBase64), { name: "AES-CBC" }, false, ["decrypt"]); 80 const plaintext = await crypto.subtle.decrypt( 81 { 82 name: "AES-CBC", 83 iv: fromBase64(ivBase64) 84 }, 85 key, 86 fromBase64(dataBase64)); 87 88 return toBase64(new Uint8Array(plaintext)); 89}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.