diff --git a/apps/cast/src/constants/locale.ts b/apps/cast/src/constants/locale.ts deleted file mode 100644 index 201800480..000000000 --- a/apps/cast/src/constants/locale.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** Enums of supported locale */ -export enum Language { - en = 'en', - fr = 'fr', - zh = 'zh', - nl = 'nl', - es = 'es', -} diff --git a/apps/cast/src/utils/crypto/index.ts b/apps/cast/src/utils/crypto/index.ts deleted file mode 100644 index dfae5f78a..000000000 --- a/apps/cast/src/utils/crypto/index.ts +++ /dev/null @@ -1,366 +0,0 @@ -import { KeyAttributes, SRPSetupAttributes } from 'types/user'; -import { SESSION_KEYS, setKey } from 'utils/storage/sessionStorage'; -import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; -import { setRecoveryKey } from 'services/userService'; -import { logError } from '@ente/shared/sentry'; -import isElectron from 'is-electron'; -// import safeStorageService from 'services/electron/safeStorage'; -import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker'; -import { PasswordStrength } from 'constants/crypto'; -import zxcvbn from 'zxcvbn'; -import { SRP, SrpClient } from 'fast-srp-hap'; -import { convertBase64ToBuffer, convertBufferToBase64 } from 'utils/user'; -import { v4 as uuidv4 } from 'uuid'; -import { addLocalLog } from 'utils/logging'; -import { getActualKey } from '@ente/shared/user'; -import { getToken } from '@ente/shared/storage/localStorage/helpers'; - -const SRP_PARAMS = SRP.params['4096']; - -const LOGIN_SUB_KEY_LENGTH = 32; -const LOGIN_SUB_KEY_ID = 1; -const LOGIN_SUB_KEY_CONTEXT = 'loginctx'; -const LOGIN_SUB_KEY_BYTE_LENGTH = 16; - -export async function generateKeyAndSRPAttributes(passphrase: string): Promise<{ - keyAttributes: KeyAttributes; - masterKey: string; - srpSetupAttributes: SRPSetupAttributes; -}> { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const masterKey = await cryptoWorker.generateEncryptionKey(); - const recoveryKey = await cryptoWorker.generateEncryptionKey(); - const kekSalt = await cryptoWorker.generateSaltToDeriveKey(); - const kek = await cryptoWorker.deriveSensitiveKey(passphrase, kekSalt); - - const masterKeyEncryptedWithKek = await cryptoWorker.encryptToB64( - masterKey, - kek.key - ); - const masterKeyEncryptedWithRecoveryKey = await cryptoWorker.encryptToB64( - masterKey, - recoveryKey - ); - const recoveryKeyEncryptedWithMasterKey = await cryptoWorker.encryptToB64( - recoveryKey, - masterKey - ); - - const keyPair = await cryptoWorker.generateKeyPair(); - const encryptedKeyPairAttributes = await cryptoWorker.encryptToB64( - keyPair.privateKey, - masterKey - ); - - const loginSubKey = await generateLoginSubKey(kek.key); - - const srpSetupAttributes = await generateSRPSetupAttributes(loginSubKey); - - const keyAttributes: KeyAttributes = { - kekSalt, - encryptedKey: masterKeyEncryptedWithKek.encryptedData, - keyDecryptionNonce: masterKeyEncryptedWithKek.nonce, - publicKey: keyPair.publicKey, - encryptedSecretKey: encryptedKeyPairAttributes.encryptedData, - secretKeyDecryptionNonce: encryptedKeyPairAttributes.nonce, - opsLimit: kek.opsLimit, - memLimit: kek.memLimit, - masterKeyEncryptedWithRecoveryKey: - masterKeyEncryptedWithRecoveryKey.encryptedData, - masterKeyDecryptionNonce: masterKeyEncryptedWithRecoveryKey.nonce, - recoveryKeyEncryptedWithMasterKey: - recoveryKeyEncryptedWithMasterKey.encryptedData, - recoveryKeyDecryptionNonce: recoveryKeyEncryptedWithMasterKey.nonce, - }; - - return { - keyAttributes, - masterKey, - srpSetupAttributes, - }; -} - -// We encrypt the masterKey, with an intermediate key derived from the -// passphrase (with Interactive mem and ops limits) to avoid saving it to local -// storage in plain text. This means that on the web user will always have to -// enter their passphrase to access their masterKey. -export async function generateAndSaveIntermediateKeyAttributes( - passphrase: string, - existingKeyAttributes: KeyAttributes, - key: string -): Promise { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const intermediateKekSalt = await cryptoWorker.generateSaltToDeriveKey(); - const intermediateKek = await cryptoWorker.deriveInteractiveKey( - passphrase, - intermediateKekSalt - ); - const encryptedKeyAttributes = await cryptoWorker.encryptToB64( - key, - intermediateKek.key - ); - - const intermediateKeyAttributes = Object.assign(existingKeyAttributes, { - kekSalt: intermediateKekSalt, - encryptedKey: encryptedKeyAttributes.encryptedData, - keyDecryptionNonce: encryptedKeyAttributes.nonce, - opsLimit: intermediateKek.opsLimit, - memLimit: intermediateKek.memLimit, - }); - setData(LS_KEYS.KEY_ATTRIBUTES, intermediateKeyAttributes); - return intermediateKeyAttributes; -} - -export const saveKeyInSessionStore = async ( - keyType: SESSION_KEYS, - key: string, - fromDesktop?: boolean -) => { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const sessionKeyAttributes = await cryptoWorker.generateKeyAndEncryptToB64( - key - ); - setKey(keyType, sessionKeyAttributes); - if ( - isElectron() && - !fromDesktop && - keyType === SESSION_KEYS.ENCRYPTION_KEY - ) { - // safeStorageService.setEncryptionKey(key); - } -}; - -export const getRecoveryKey = async () => { - let recoveryKey: string = null; - try { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - - const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); - const { - recoveryKeyEncryptedWithMasterKey, - recoveryKeyDecryptionNonce, - } = keyAttributes; - const masterKey = await getActualKey(); - if (recoveryKeyEncryptedWithMasterKey) { - recoveryKey = await cryptoWorker.decryptB64( - recoveryKeyEncryptedWithMasterKey, - recoveryKeyDecryptionNonce, - masterKey - ); - } else { - recoveryKey = await createNewRecoveryKey(); - } - recoveryKey = await cryptoWorker.toHex(recoveryKey); - return recoveryKey; - } catch (e) { - logError(e, 'getRecoveryKey failed'); - throw e; - } -}; - -// Used only for legacy users for whom we did not generate recovery keys during -// sign up -async function createNewRecoveryKey() { - const masterKey = await getActualKey(); - const existingAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); - - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - - const recoveryKey = await cryptoWorker.generateEncryptionKey(); - const encryptedMasterKey = await cryptoWorker.encryptToB64( - masterKey, - recoveryKey - ); - const encryptedRecoveryKey = await cryptoWorker.encryptToB64( - recoveryKey, - masterKey - ); - const recoveryKeyAttributes = { - masterKeyEncryptedWithRecoveryKey: encryptedMasterKey.encryptedData, - masterKeyDecryptionNonce: encryptedMasterKey.nonce, - recoveryKeyEncryptedWithMasterKey: encryptedRecoveryKey.encryptedData, - recoveryKeyDecryptionNonce: encryptedRecoveryKey.nonce, - }; - await setRecoveryKey(getToken(), recoveryKeyAttributes); - - const updatedKeyAttributes = Object.assign( - existingAttributes, - recoveryKeyAttributes - ); - setData(LS_KEYS.KEY_ATTRIBUTES, updatedKeyAttributes); - - return recoveryKey; -} - -export async function decryptAndStoreToken( - keyAttributes: KeyAttributes, - masterKey: string -) { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const user = getData(LS_KEYS.USER); - let decryptedToken = null; - const { encryptedToken } = user; - if (encryptedToken && encryptedToken.length > 0) { - const secretKey = await cryptoWorker.decryptB64( - keyAttributes.encryptedSecretKey, - keyAttributes.secretKeyDecryptionNonce, - masterKey - ); - const urlUnsafeB64DecryptedToken = await cryptoWorker.boxSealOpen( - encryptedToken, - keyAttributes.publicKey, - secretKey - ); - const decryptedTokenBytes = await cryptoWorker.fromB64( - urlUnsafeB64DecryptedToken - ); - decryptedToken = await cryptoWorker.toURLSafeB64(decryptedTokenBytes); - setData(LS_KEYS.USER, { - ...user, - token: decryptedToken, - encryptedToken: null, - }); - } -} - -export async function encryptWithRecoveryKey(key: string) { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const hexRecoveryKey = await getRecoveryKey(); - const recoveryKey = await cryptoWorker.fromHex(hexRecoveryKey); - const encryptedKey = await cryptoWorker.encryptToB64(key, recoveryKey); - return encryptedKey; -} - -export async function decryptDeleteAccountChallenge( - encryptedChallenge: string -) { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const masterKey = await getActualKey(); - const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); - const secretKey = await cryptoWorker.decryptB64( - keyAttributes.encryptedSecretKey, - keyAttributes.secretKeyDecryptionNonce, - masterKey - ); - const b64DecryptedChallenge = await cryptoWorker.boxSealOpen( - encryptedChallenge, - keyAttributes.publicKey, - secretKey - ); - const utf8DecryptedChallenge = atob(b64DecryptedChallenge); - return utf8DecryptedChallenge; -} - -export function estimatePasswordStrength(password: string): PasswordStrength { - if (!password) { - return PasswordStrength.WEAK; - } - - const zxcvbnResult = zxcvbn(password); - if (zxcvbnResult.score < 2) { - return PasswordStrength.WEAK; - } else if (zxcvbnResult.score < 3) { - return PasswordStrength.MODERATE; - } else { - return PasswordStrength.STRONG; - } -} - -export const isWeakPassword = (password: string) => { - return estimatePasswordStrength(password) === PasswordStrength.WEAK; -}; - -export const generateLoginSubKey = async (kek: string) => { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const kekSubKeyString = await cryptoWorker.generateSubKey( - kek, - LOGIN_SUB_KEY_LENGTH, - LOGIN_SUB_KEY_ID, - LOGIN_SUB_KEY_CONTEXT - ); - const kekSubKey = await cryptoWorker.fromB64(kekSubKeyString); - - // use first 16 bytes of generated kekSubKey as loginSubKey - const loginSubKey = await cryptoWorker.toB64( - kekSubKey.slice(0, LOGIN_SUB_KEY_BYTE_LENGTH) - ); - - return loginSubKey; -}; - -export const generateSRPSetupAttributes = async ( - loginSubKey: string -): Promise => { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - - const srpSalt = await cryptoWorker.generateSaltToDeriveKey(); - - const srpUserID = uuidv4(); - - const srpVerifierBuffer = SRP.computeVerifier( - SRP_PARAMS, - convertBase64ToBuffer(srpSalt), - Buffer.from(srpUserID), - convertBase64ToBuffer(loginSubKey) - ); - - const srpVerifier = convertBufferToBase64(srpVerifierBuffer); - - addLocalLog( - () => `SRP setup attributes generated', - ${JSON.stringify({ - srpSalt, - srpUserID, - srpVerifier, - loginSubKey, - })}` - ); - - return { - srpUserID, - srpSalt, - srpVerifier, - loginSubKey, - }; -}; - -export const computeVerifierHelper = ( - srpSalt: string, - srpUserID: string, - loginSubKey: string -) => { - const srpVerifierBuffer = SRP.computeVerifier( - SRP_PARAMS, - convertBase64ToBuffer(srpSalt), - Buffer.from(srpUserID), - convertBase64ToBuffer(loginSubKey) - ); - return convertBufferToBase64(srpVerifierBuffer); -}; -export const generateSRPClient = async ( - srpSalt: string, - srpUserID: string, - loginSubKey: string -) => { - return new Promise((resolve, reject) => { - SRP.genKey(function (err, secret1) { - try { - if (err) { - reject(err); - } - const srpClient = new SrpClient( - SRP_PARAMS, - convertBase64ToBuffer(srpSalt), - Buffer.from(srpUserID), - convertBase64ToBuffer(loginSubKey), - secret1, - false - ); - - resolve(srpClient); - } catch (e) { - reject(e); - } - }); - }); -}; diff --git a/apps/cast/src/utils/crypto/libsodium.ts b/apps/cast/src/utils/crypto/libsodium.ts deleted file mode 100644 index 02c12c8df..000000000 --- a/apps/cast/src/utils/crypto/libsodium.ts +++ /dev/null @@ -1,422 +0,0 @@ -import sodium, { StateAddress } from 'libsodium-wrappers'; -import { ENCRYPTION_CHUNK_SIZE } from 'constants/crypto'; -import { B64EncryptionResult } from 'types/crypto'; -import { CustomError } from '@ente/shared/error'; - -export async function decryptChaChaOneShot( - data: Uint8Array, - header: Uint8Array, - key: string -) { - await sodium.ready; - const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull( - header, - await fromB64(key) - ); - const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull( - pullState, - data, - null - ); - return pullResult.message; -} - -export async function decryptChaCha( - data: Uint8Array, - header: Uint8Array, - key: string -) { - await sodium.ready; - const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull( - header, - await fromB64(key) - ); - const decryptionChunkSize = - ENCRYPTION_CHUNK_SIZE + - sodium.crypto_secretstream_xchacha20poly1305_ABYTES; - let bytesRead = 0; - const decryptedData = []; - let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE; - while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) { - let chunkSize = decryptionChunkSize; - if (bytesRead + chunkSize > data.length) { - chunkSize = data.length - bytesRead; - } - const buffer = data.slice(bytesRead, bytesRead + chunkSize); - const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull( - pullState, - buffer - ); - if (!pullResult.message) { - throw new Error(CustomError.PROCESSING_FAILED); - } - for (let index = 0; index < pullResult.message.length; index++) { - decryptedData.push(pullResult.message[index]); - } - tag = pullResult.tag; - bytesRead += chunkSize; - } - return Uint8Array.from(decryptedData); -} - -export async function initChunkDecryption(header: Uint8Array, key: Uint8Array) { - await sodium.ready; - const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull( - header, - key - ); - const decryptionChunkSize = - ENCRYPTION_CHUNK_SIZE + - sodium.crypto_secretstream_xchacha20poly1305_ABYTES; - const tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE; - return { pullState, decryptionChunkSize, tag }; -} - -export async function decryptFileChunk( - data: Uint8Array, - pullState: StateAddress -) { - await sodium.ready; - const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull( - pullState, - data - ); - if (!pullResult.message) { - throw new Error(CustomError.PROCESSING_FAILED); - } - const newTag = pullResult.tag; - return { decryptedData: pullResult.message, newTag }; -} - -export async function encryptChaChaOneShot(data: Uint8Array, key: string) { - await sodium.ready; - - const uintkey: Uint8Array = await fromB64(key); - const initPushResult = - sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey); - const [pushState, header] = [initPushResult.state, initPushResult.header]; - - const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push( - pushState, - data, - null, - sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL - ); - return { - key: await toB64(uintkey), - file: { - encryptedData: pushResult, - decryptionHeader: await toB64(header), - }, - }; -} - -export async function encryptChaCha(data: Uint8Array) { - await sodium.ready; - - const uintkey: Uint8Array = - sodium.crypto_secretstream_xchacha20poly1305_keygen(); - - const initPushResult = - sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey); - const [pushState, header] = [initPushResult.state, initPushResult.header]; - let bytesRead = 0; - let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE; - - const encryptedData = []; - - while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) { - let chunkSize = ENCRYPTION_CHUNK_SIZE; - if (bytesRead + chunkSize >= data.length) { - chunkSize = data.length - bytesRead; - tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL; - } - - const buffer = data.slice(bytesRead, bytesRead + chunkSize); - bytesRead += chunkSize; - const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push( - pushState, - buffer, - null, - tag - ); - for (let index = 0; index < pushResult.length; index++) { - encryptedData.push(pushResult[index]); - } - } - return { - key: await toB64(uintkey), - file: { - encryptedData: new Uint8Array(encryptedData), - decryptionHeader: await toB64(header), - }, - }; -} - -export async function initChunkEncryption() { - await sodium.ready; - const key = sodium.crypto_secretstream_xchacha20poly1305_keygen(); - const initPushResult = - sodium.crypto_secretstream_xchacha20poly1305_init_push(key); - const [pushState, header] = [initPushResult.state, initPushResult.header]; - return { - key: await toB64(key), - decryptionHeader: await toB64(header), - pushState, - }; -} - -export async function encryptFileChunk( - data: Uint8Array, - pushState: sodium.StateAddress, - isFinalChunk: boolean -) { - await sodium.ready; - const tag = isFinalChunk - ? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL - : sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE; - const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push( - pushState, - data, - null, - tag - ); - - return pushResult; -} -export async function encryptToB64(data: string, key: string) { - await sodium.ready; - const encrypted = await encrypt(await fromB64(data), await fromB64(key)); - - return { - encryptedData: await toB64(encrypted.encryptedData), - key: await toB64(encrypted.key), - nonce: await toB64(encrypted.nonce), - } as B64EncryptionResult; -} - -export async function generateKeyAndEncryptToB64(data: string) { - await sodium.ready; - const key = sodium.crypto_secretbox_keygen(); - return await encryptToB64(data, await toB64(key)); -} - -export async function encryptUTF8(data: string, key: string) { - const b64Data = await toB64(await fromUTF8(data)); - return await encryptToB64(b64Data, key); -} - -export async function decryptB64(data: string, nonce: string, key: string) { - await sodium.ready; - const decrypted = await decrypt( - await fromB64(data), - await fromB64(nonce), - await fromB64(key) - ); - - return await toB64(decrypted); -} - -export async function decryptToUTF8(data: string, nonce: string, key: string) { - await sodium.ready; - const decrypted = await decrypt( - await fromB64(data), - await fromB64(nonce), - await fromB64(key) - ); - - return sodium.to_string(decrypted); -} - -async function encrypt(data: Uint8Array, key: Uint8Array) { - await sodium.ready; - const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES); - const encryptedData = sodium.crypto_secretbox_easy(data, nonce, key); - return { - encryptedData, - key, - nonce, - }; -} - -async function decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) { - await sodium.ready; - return sodium.crypto_secretbox_open_easy(data, nonce, key); -} - -export async function initChunkHashing() { - await sodium.ready; - const hashState = sodium.crypto_generichash_init( - null, - sodium.crypto_generichash_BYTES_MAX - ); - return hashState; -} - -export async function hashFileChunk( - hashState: sodium.StateAddress, - chunk: Uint8Array -) { - await sodium.ready; - sodium.crypto_generichash_update(hashState, chunk); -} - -export async function completeChunkHashing(hashState: sodium.StateAddress) { - await sodium.ready; - const hash = sodium.crypto_generichash_final( - hashState, - sodium.crypto_generichash_BYTES_MAX - ); - const hashString = toB64(hash); - return hashString; -} - -export async function deriveKey( - passphrase: string, - salt: string, - opsLimit: number, - memLimit: number -) { - await sodium.ready; - return await toB64( - sodium.crypto_pwhash( - sodium.crypto_secretbox_KEYBYTES, - await fromUTF8(passphrase), - await fromB64(salt), - opsLimit, - memLimit, - sodium.crypto_pwhash_ALG_ARGON2ID13 - ) - ); -} - -export async function deriveSensitiveKey(passphrase: string, salt: string) { - await sodium.ready; - const minMemLimit = sodium.crypto_pwhash_MEMLIMIT_MIN; - let opsLimit = sodium.crypto_pwhash_OPSLIMIT_SENSITIVE; - let memLimit = sodium.crypto_pwhash_MEMLIMIT_SENSITIVE; - while (memLimit > minMemLimit) { - try { - const key = await deriveKey(passphrase, salt, opsLimit, memLimit); - return { - key, - opsLimit, - memLimit, - }; - } catch (e) { - opsLimit *= 2; - memLimit /= 2; - } - } -} - -export async function deriveInteractiveKey(passphrase: string, salt: string) { - await sodium.ready; - const key = await toB64( - sodium.crypto_pwhash( - sodium.crypto_secretbox_KEYBYTES, - await fromUTF8(passphrase), - await fromB64(salt), - sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, - sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE, - sodium.crypto_pwhash_ALG_ARGON2ID13 - ) - ); - return { - key, - opsLimit: sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, - memLimit: sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE, - }; -} - -export async function generateEncryptionKey() { - await sodium.ready; - return await toB64(sodium.crypto_kdf_keygen()); -} - -export async function generateSaltToDeriveKey() { - await sodium.ready; - return await toB64(sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES)); -} - -export async function generateKeyPair() { - await sodium.ready; - const keyPair: sodium.KeyPair = sodium.crypto_box_keypair(); - return { - privateKey: await toB64(keyPair.privateKey), - publicKey: await toB64(keyPair.publicKey), - }; -} - -export async function boxSealOpen( - input: string, - publicKey: string, - secretKey: string -) { - await sodium.ready; - return await toB64( - sodium.crypto_box_seal_open( - await fromB64(input), - await fromB64(publicKey), - await fromB64(secretKey) - ) - ); -} - -export async function boxSeal(input: string, publicKey: string) { - await sodium.ready; - return await toB64( - sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey)) - ); -} - -export async function generateSubKey( - key: string, - subKeyLength: number, - subKeyID: number, - context: string -) { - await sodium.ready; - return await toB64( - sodium.crypto_kdf_derive_from_key( - subKeyLength, - subKeyID, - context, - await fromB64(key) - ) - ); -} - -export async function fromB64(input: string) { - await sodium.ready; - return sodium.from_base64(input, sodium.base64_variants.ORIGINAL); -} - -export async function toB64(input: Uint8Array) { - await sodium.ready; - return sodium.to_base64(input, sodium.base64_variants.ORIGINAL); -} - -export async function toURLSafeB64(input: Uint8Array) { - await sodium.ready; - return sodium.to_base64(input, sodium.base64_variants.URLSAFE); -} - -export async function fromUTF8(input: string) { - await sodium.ready; - return sodium.from_string(input); -} - -export async function toUTF8(input: string) { - await sodium.ready; - return sodium.to_string(await fromB64(input)); -} -export async function toHex(input: string) { - await sodium.ready; - return sodium.to_hex(await fromB64(input)); -} - -export async function fromHex(input: string) { - await sodium.ready; - return await toB64(sodium.from_hex(input)); -}