UNPKG

@jbrowse/core

Version:

JBrowse 2 core libraries used by plugins

537 lines (536 loc) 20.6 kB
const SALT_PREFIX = 'Salted__'; function isWebCryptoAvailable() { return typeof crypto !== 'undefined' && crypto.subtle !== undefined; } const SBOX = new Uint8Array([ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ]); const SBOX_INV = new Uint8Array([ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, ]); const RCON = new Uint8Array([ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, ]); function gmul(a, b) { let p = 0; for (let i = 0; i < 8; i++) { if (b & 1) { p ^= a; } const hiBit = a & 0x80; a = (a << 1) & 0xff; if (hiBit) { a ^= 0x1b; } b >>= 1; } return p; } function aesKeyExpansion(key) { const Nk = 8; const Nr = 14; const expandedKey = new Uint8Array(16 * (Nr + 1)); expandedKey.set(key); for (let i = Nk; i < 4 * (Nr + 1); i++) { const temp = expandedKey.slice((i - 1) * 4, i * 4); if (i % Nk === 0) { const t = temp[0]; temp[0] = SBOX[temp[1]] ^ RCON[i / Nk - 1]; temp[1] = SBOX[temp[2]]; temp[2] = SBOX[temp[3]]; temp[3] = SBOX[t]; } else if (Nk > 6 && i % Nk === 4) { for (let j = 0; j < 4; j++) { temp[j] = SBOX[temp[j]]; } } for (let j = 0; j < 4; j++) { expandedKey[i * 4 + j] = expandedKey[(i - Nk) * 4 + j] ^ temp[j]; } } return expandedKey; } function aesEncryptBlock(block, expandedKey) { const Nr = 14; const state = new Uint8Array(16); state.set(block); for (let i = 0; i < 16; i++) { state[i] ^= expandedKey[i]; } for (let round = 1; round <= Nr; round++) { for (let i = 0; i < 16; i++) { state[i] = SBOX[state[i]]; } const t1 = state[1]; state[1] = state[5]; state[5] = state[9]; state[9] = state[13]; state[13] = t1; const t2 = state[2]; const t6 = state[6]; state[2] = state[10]; state[6] = state[14]; state[10] = t2; state[14] = t6; const t3 = state[15]; state[15] = state[11]; state[11] = state[7]; state[7] = state[3]; state[3] = t3; if (round < Nr) { for (let c = 0; c < 4; c++) { const i = c * 4; const s0 = state[i]; const s1 = state[i + 1]; const s2 = state[i + 2]; const s3 = state[i + 3]; state[i] = gmul(s0, 2) ^ gmul(s1, 3) ^ s2 ^ s3; state[i + 1] = s0 ^ gmul(s1, 2) ^ gmul(s2, 3) ^ s3; state[i + 2] = s0 ^ s1 ^ gmul(s2, 2) ^ gmul(s3, 3); state[i + 3] = gmul(s0, 3) ^ s1 ^ s2 ^ gmul(s3, 2); } } const roundKey = expandedKey.subarray(round * 16, (round + 1) * 16); for (let i = 0; i < 16; i++) { state[i] ^= roundKey[i]; } } return state; } function aesDecryptBlock(block, expandedKey) { const Nr = 14; const state = new Uint8Array(16); state.set(block); for (let i = 0; i < 16; i++) { state[i] ^= expandedKey[Nr * 16 + i]; } for (let round = Nr - 1; round >= 0; round--) { const t1 = state[13]; state[13] = state[9]; state[9] = state[5]; state[5] = state[1]; state[1] = t1; const t2 = state[10]; const t6 = state[14]; state[10] = state[2]; state[14] = state[6]; state[2] = t2; state[6] = t6; const t3 = state[3]; state[3] = state[7]; state[7] = state[11]; state[11] = state[15]; state[15] = t3; for (let i = 0; i < 16; i++) { state[i] = SBOX_INV[state[i]]; } const roundKey = expandedKey.subarray(round * 16, (round + 1) * 16); for (let i = 0; i < 16; i++) { state[i] ^= roundKey[i]; } if (round > 0) { for (let c = 0; c < 4; c++) { const i = c * 4; const s0 = state[i]; const s1 = state[i + 1]; const s2 = state[i + 2]; const s3 = state[i + 3]; state[i] = gmul(s0, 14) ^ gmul(s1, 11) ^ gmul(s2, 13) ^ gmul(s3, 9); state[i + 1] = gmul(s0, 9) ^ gmul(s1, 14) ^ gmul(s2, 11) ^ gmul(s3, 13); state[i + 2] = gmul(s0, 13) ^ gmul(s1, 9) ^ gmul(s2, 14) ^ gmul(s3, 11); state[i + 3] = gmul(s0, 11) ^ gmul(s1, 13) ^ gmul(s2, 9) ^ gmul(s3, 14); } } } return state; } function pkcs7Pad(data, blockSize) { const padding = blockSize - (data.length % blockSize); const padded = new Uint8Array(data.length + padding); padded.set(data); padded.fill(padding, data.length); return padded; } function pkcs7Unpad(data) { const padding = data[data.length - 1]; if (padding > 16 || padding === 0) { throw new Error('Invalid padding'); } for (let i = data.length - padding; i < data.length; i++) { if (data[i] !== padding) { throw new Error('Invalid padding'); } } return data.subarray(0, data.length - padding); } function aesCbcEncryptFallback(plaintext, key, iv) { const expandedKey = aesKeyExpansion(key); const padded = pkcs7Pad(plaintext, 16); const ciphertext = new Uint8Array(padded.length); let prevBlock = iv; for (let i = 0; i < padded.length; i += 16) { const block = padded.slice(i, i + 16); for (let j = 0; j < 16; j++) { block[j] ^= prevBlock[j]; } const encrypted = aesEncryptBlock(block, expandedKey); ciphertext.set(encrypted, i); prevBlock = encrypted; } return ciphertext; } function aesCbcDecryptFallback(ciphertext, key, iv) { const expandedKey = aesKeyExpansion(key); const plaintext = new Uint8Array(ciphertext.length); let prevBlock = iv; for (let i = 0; i < ciphertext.length; i += 16) { const block = ciphertext.slice(i, i + 16); const decrypted = aesDecryptBlock(block, expandedKey); for (let j = 0; j < 16; j++) { decrypted[j] ^= prevBlock[j]; } plaintext.set(decrypted, i); prevBlock = block; } return pkcs7Unpad(plaintext); } function getRandomBytes(length) { if (typeof crypto !== 'undefined' && crypto.getRandomValues) { return crypto.getRandomValues(new Uint8Array(length)); } const bytes = new Uint8Array(length); for (let i = 0; i < length; i++) { bytes[i] = Math.floor(Math.random() * 256); } return bytes; } const SHA256_K = new Uint32Array([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ]); function sha256Fallback(data) { function rotr(x, n) { return (x >>> n) | (x << (32 - n)); } function ch(x, y, z) { return (x & y) ^ (~x & z); } function maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } function sigma0(x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } function sigma1(x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); } function gamma0(x) { return rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3); } function gamma1(x) { return rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10); } const originalLength = data.length; const bitLength = originalLength * 8; const paddingLength = (64 - ((originalLength + 9) % 64)) % 64; const padded = new Uint8Array(originalLength + 1 + paddingLength + 8); padded.set(data); padded[originalLength] = 0x80; const view = new DataView(padded.buffer); view.setUint32(padded.length - 4, bitLength >>> 0, false); view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000), false); let h0 = 0x6a09e667; let h1 = 0xbb67ae85; let h2 = 0x3c6ef372; let h3 = 0xa54ff53a; let h4 = 0x510e527f; let h5 = 0x9b05688c; let h6 = 0x1f83d9ab; let h7 = 0x5be0cd19; const W = new Uint32Array(64); for (let i = 0; i < padded.length; i += 64) { for (let t = 0; t < 16; t++) { W[t] = view.getUint32(i + t * 4, false); } for (let t = 16; t < 64; t++) { W[t] = (gamma1(W[t - 2]) + W[t - 7] + gamma0(W[t - 15]) + W[t - 16]) >>> 0; } let a = h0; let b = h1; let c = h2; let d = h3; let e = h4; let f = h5; let g = h6; let h = h7; for (let t = 0; t < 64; t++) { const T1 = (h + sigma1(e) + ch(e, f, g) + SHA256_K[t] + W[t]) >>> 0; const T2 = (sigma0(a) + maj(a, b, c)) >>> 0; h = g; g = f; f = e; e = (d + T1) >>> 0; d = c; c = b; b = a; a = (T1 + T2) >>> 0; } h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0; h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0; h4 = (h4 + e) >>> 0; h5 = (h5 + f) >>> 0; h6 = (h6 + g) >>> 0; h7 = (h7 + h) >>> 0; } const result = new Uint8Array(32); const resultView = new DataView(result.buffer); resultView.setUint32(0, h0, false); resultView.setUint32(4, h1, false); resultView.setUint32(8, h2, false); resultView.setUint32(12, h3, false); resultView.setUint32(16, h4, false); resultView.setUint32(20, h5, false); resultView.setUint32(24, h6, false); resultView.setUint32(28, h7, false); return result; } function md5(data) { const S = [ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, ]; const K = [ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, ]; function leftRotate(x, c) { return (x << c) | (x >>> (32 - c)); } const originalLength = data.length; const bitLength = originalLength * 8; const paddingLength = (56 - ((originalLength + 1) % 64) + 64) % 64; const padded = new Uint8Array(originalLength + 1 + paddingLength + 8); padded.set(data); padded[originalLength] = 0x80; const view = new DataView(padded.buffer); view.setUint32(padded.length - 8, bitLength >>> 0, true); view.setUint32(padded.length - 4, Math.floor(bitLength / 0x100000000), true); let a0 = 0x67452301; let b0 = 0xefcdab89; let c0 = 0x98badcfe; let d0 = 0x10325476; for (let i = 0; i < padded.length; i += 64) { const M = new Uint32Array(16); for (let j = 0; j < 16; j++) { M[j] = view.getUint32(i + j * 4, true); } let A = a0; let B = b0; let C = c0; let D = d0; for (let j = 0; j < 64; j++) { let F; let g; if (j < 16) { F = (B & C) | (~B & D); g = j; } else if (j < 32) { F = (D & B) | (~D & C); g = (5 * j + 1) % 16; } else if (j < 48) { F = B ^ C ^ D; g = (3 * j + 5) % 16; } else { F = C ^ (B | ~D); g = (7 * j) % 16; } F = (F + A + K[j] + M[g]) >>> 0; A = D; D = C; C = B; B = (B + leftRotate(F, S[j])) >>> 0; } a0 = (a0 + A) >>> 0; b0 = (b0 + B) >>> 0; c0 = (c0 + C) >>> 0; d0 = (d0 + D) >>> 0; } const result = new Uint8Array(16); const resultView = new DataView(result.buffer); resultView.setUint32(0, a0, true); resultView.setUint32(4, b0, true); resultView.setUint32(8, c0, true); resultView.setUint32(12, d0, true); return result; } function evpBytesToKey(password, salt, keyLen, ivLen) { const result = new Uint8Array(keyLen + ivLen); let offset = 0; let prevHash = new Uint8Array(0); while (offset < keyLen + ivLen) { const data = new Uint8Array(prevHash.length + password.length + salt.length); data.set(prevHash); data.set(password, prevHash.length); data.set(salt, prevHash.length + password.length); prevHash = md5(data); result.set(prevHash.subarray(0, Math.min(16, keyLen + ivLen - offset)), offset); offset += 16; } return { key: result.slice(0, keyLen), iv: result.slice(keyLen, keyLen + ivLen), }; } function stringToBytes(str) { const encoded = new TextEncoder().encode(str); return encoded.slice(); } function bytesToString(bytes) { return new TextDecoder().decode(bytes); } function base64Encode(data) { let binary = ''; for (const byte of data) { binary += String.fromCharCode(byte); } return btoa(binary); } function base64Decode(str) { const binary = atob(str); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; } export async function aesEncrypt(plaintext, password) { const salt = getRandomBytes(8); const { key, iv } = evpBytesToKey(stringToBytes(password), salt, 32, 16); let encrypted; if (isWebCryptoAvailable()) { const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']); const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, stringToBytes(plaintext)); encrypted = new Uint8Array(encryptedBuffer); } else { encrypted = aesCbcEncryptFallback(stringToBytes(plaintext), key, iv); } const saltPrefix = stringToBytes(SALT_PREFIX); const result = new Uint8Array(saltPrefix.length + salt.length + encrypted.byteLength); result.set(saltPrefix); result.set(salt, saltPrefix.length); result.set(encrypted, saltPrefix.length + salt.length); return base64Encode(result); } export async function aesDecrypt(ciphertext, password) { const data = base64Decode(ciphertext); const saltPrefix = stringToBytes(SALT_PREFIX); const hasSalt = data.length > saltPrefix.length && bytesToString(data.subarray(0, saltPrefix.length)) === SALT_PREFIX; if (!hasSalt) { throw new Error('Invalid encrypted data format'); } const salt = data.slice(saltPrefix.length, saltPrefix.length + 8); const encrypted = data.slice(saltPrefix.length + 8); const { key, iv } = evpBytesToKey(stringToBytes(password), salt, 32, 16); let decrypted; if (isWebCryptoAvailable()) { const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['decrypt']); const decryptedBuffer = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, encrypted); decrypted = new Uint8Array(decryptedBuffer); } else { decrypted = aesCbcDecryptFallback(encrypted, key, iv); } return bytesToString(decrypted); } export async function sha256(data) { const encoded = stringToBytes(data); if (isWebCryptoAvailable()) { const hashBuffer = await crypto.subtle.digest('SHA-256', encoded); return new Uint8Array(hashBuffer); } return sha256Fallback(encoded); } export function toBase64(data) { return base64Encode(data); } export function toBase64Url(data) { return base64Encode(data) .replaceAll('+', '-') .replaceAll('/', '_') .replaceAll('=', ''); } export async function sha256Base64(data) { const hash = await sha256(data); return toBase64(hash); } export async function sha256Base64Url(data) { const hash = await sha256(data); return toBase64Url(hash); }