{"version":3,"file":"worker.js","sources":["../../packages/tutanota-crypto/dist/hashes/MurmurHash.js","../../../src/api/worker/utils/DbUtils.ts","../../packages/tutanota-crypto/dist/encryption/KeyEncryption.js","../../packages/tutanota-crypto/dist/encryption/Rsa.js","../../../src/api/worker/EventQueue.ts","../../../src/api/worker/ProgressMonitorDelegate.ts","../../../src/api/worker/EventBusClient.ts","../../../src/api/worker/rest/DefaultEntityRestCache.ts","../../../src/api/worker/rest/EntityRestClient.ts","../../packages/tutanota-crypto/dist/internal/sjcl.js","../../packages/tutanota-crypto/dist/misc/CryptoError.js","../../packages/tutanota-crypto/dist/random/Randomizer.js","../../packages/tutanota-crypto/dist/hashes/Sha256.js","../../packages/tutanota-crypto/dist/misc/Utils.js","../../packages/tutanota-crypto/dist/hashes/Sha512.js","../../packages/tutanota-crypto/dist/encryption/Aes.js","../../packages/tutanota-crypto/dist/internal/bCrypt.js","../../packages/tutanota-crypto/dist/misc/Constants.js","../../packages/tutanota-crypto/dist/hashes/Bcrypt.js","../../packages/tutanota-crypto/dist/random/SecureRandom.js","../../packages/tutanota-crypto/dist/internal/crypto-jsbn-2012-08-09_1.js","../../packages/tutanota-crypto/dist/hashes/Sha1.js","../../packages/tutanota-crypto/dist/misc/TotpVerifier.js","../../../src/api/worker/crypto/CryptoFacade.ts","../../../src/api/worker/facades/LoginFacade.ts","../../../src/api/worker/rest/RestClient.ts","../../../src/api/worker/SuspensionHandler.ts","../../../src/api/worker/facades/DeviceEncryptionFacade.ts","../../../src/native/worker/AesApp.ts","../../../src/api/worker/crypto/RsaImplementation.ts","../../../src/api/worker/Compression.ts","../../../src/api/worker/crypto/InstanceMapper.ts","../../../src/api/worker/rest/AdminClientDummyEntityRestCache.ts","../../../src/api/worker/utils/SleepDetector.ts","../../../src/api/worker/rest/CustomCacheHandler.ts","../../../src/api/worker/rest/EphemeralCacheStorage.ts","../../../src/api/worker/rest/CacheStorageProxy.ts","../../../src/api/worker/rest/ServiceExecutor.ts","../../../src/api/worker/facades/UserFacade.ts","../../../src/api/worker/offline/SqlValue.ts","../../../src/api/worker/offline/OfflineStorage.ts","../../../src/api/worker/offline/StandardMigrations.ts","../../../src/api/worker/offline/migrations/tutanota-v60.ts","../../../src/api/worker/offline/OfflineStorageMigrator.ts","../../../src/api/worker/offline/migrations/offline-v1.ts","../../../src/api/worker/offline/migrations/sys-v75.ts","../../../src/api/worker/offline/migrations/sys-v76.ts","../../../src/api/worker/offline/migrations/sys-v79.ts","../../../src/api/worker/offline/migrations/sys-v80.ts","../../../src/api/worker/offline/migrations/tutanota-v54.ts","../../../src/api/worker/offline/migrations/tutanota-v56.ts","../../../src/api/worker/offline/migrations/tutanota-v57.ts","../../../src/api/worker/offline/migrations/tutanota-v58.ts","../../../src/api/worker/offline/migrations/storage-v6.ts","../../../src/api/worker/offline/migrations/sys-v83.ts","../../../src/api/worker/offline/migrations/accounting-v5.ts","../../../src/api/worker/offline/migrations/sys-v84.ts","../../../src/api/worker/offline/migrations/tutanota-v61.ts","../../../src/api/worker/offline/migrations/sys-v85.ts","../../../src/api/worker/offline/migrations/sys-v86.ts","../../../src/api/worker/offline/migrations/sys-v87.ts","../../../src/api/worker/offline/migrations/sys-v88.ts","../../../src/api/worker/facades/EntropyFacade.ts","../../../src/api/worker/facades/BlobAccessTokenFacade.ts","../../../src/api/worker/crypto/OwnerEncSessionKeysUpdateQueue.ts","../../../src/api/worker/EventBusEventCoordinator.ts","../../../src/api/worker/facades/WorkerFacade.ts","../../../src/api/worker/WorkerLocator.ts","../../../src/api/worker/WorkerImpl.ts","../../../src/api/worker/worker.ts","../../../src/native/worker/RsaApp.ts","../../../src/api/worker/DateProvider.ts"],"sourcesContent":["/*!\n * +----------------------------------------------------------------------------------+\n * | murmurHash3.js v3.0.0 (http://github.com/karanlyons/murmurHash3.js)              |\n * | A TypeScript/JavaScript implementation of MurmurHash3's hashing algorithms.      |\n * |----------------------------------------------------------------------------------|\n * | Copyright (c) 2012-2020 Karan Lyons. Freely distributable under the MIT license. |\n * +----------------------------------------------------------------------------------+\n *\n * tutao: heavily stripped down to only take x86hash32, removed types for now.\n * This implementation should handle non-ascii characters.\n */\nimport { stringToUtf8Uint8Array } from \"@tutao/tutanota-utils\";\nfunction x86fmix32(h) {\n    h ^= h >>> 16;\n    h = mul32(h, 0x85ebca6b);\n    h ^= h >>> 13;\n    h = mul32(h, 0xc2b2ae35);\n    h ^= h >>> 16;\n    return h;\n}\nconst x86hash32c1 = 0xcc9e2d51;\nconst x86hash32c2 = 0x1b873593;\nfunction x86mix32(h, k) {\n    k = mul32(k, x86hash32c1);\n    k = rol32(k, 15);\n    k = mul32(k, x86hash32c2);\n    h ^= k;\n    h = rol32(h, 13);\n    h = mul32(h, 5) + 0xe6546b64;\n    return h;\n}\nfunction mul32(m, n) {\n    return (m & 0xffff) * n + ((((m >>> 16) * n) & 0xffff) << 16);\n}\nfunction rol32(n, r) {\n    return (n << r) | (n >>> (32 - r));\n}\nexport function murmurHash(value) {\n    let state = 0;\n    const buf = stringToUtf8Uint8Array(value);\n    let h1;\n    let i;\n    let len;\n    h1 = state;\n    i = 0;\n    len = 0;\n    const dtv = new DataView(buf.buffer, buf.byteOffset);\n    const remainder = (buf.byteLength - i) % 4;\n    const bytes = buf.byteLength - i - remainder;\n    len += bytes;\n    for (; i < bytes; i += 4) {\n        h1 = x86mix32(h1, dtv.getUint32(i, true));\n    }\n    len += remainder;\n    let k1 = 0x0;\n    // noinspection FallThroughInSwitchStatementJS\n    switch (remainder) {\n        case 3:\n            k1 ^= buf[i + 2] << 16;\n        case 2:\n            k1 ^= buf[i + 1] << 8;\n        case 1:\n            k1 ^= buf[i];\n            k1 = mul32(k1, x86hash32c1);\n            k1 = rol32(k1, 15);\n            k1 = mul32(k1, x86hash32c2);\n            h1 ^= k1;\n    }\n    h1 ^= len & 0xffffffff;\n    h1 = x86fmix32(h1);\n    return h1 >>> 0;\n}\n","import { assertWorkerOrNode } from \"../../common/Env\"\n\nassertWorkerOrNode()\n\nexport function deleteObjectStores(db: IDBDatabase, ...oss: string[]) {\n\tfor (let os of oss) {\n\t\ttry {\n\t\t\tdb.deleteObjectStore(os)\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"Error while deleting old os\", os, \"ignoring\", e)\n\t\t}\n\t}\n}\n","import { aes128Decrypt, aes128Encrypt, aes256Decrypt, aes256Encrypt, IV_BYTE_LENGTH } from \"./Aes.js\";\nimport { bitArrayToUint8Array, fixedIv, uint8ArrayToBitArray } from \"../misc/Utils.js\";\nimport { concat, hexToUint8Array, uint8ArrayToHex } from \"@tutao/tutanota-utils\";\nimport { hexToPrivateKey, privateKeyToHex } from \"./Rsa.js\";\nimport { random } from \"../random/Randomizer.js\";\nexport function encryptKey(encryptionKey, key) {\n    return aes128Encrypt(encryptionKey, bitArrayToUint8Array(key), fixedIv, false, false).slice(fixedIv.length);\n}\nexport function decryptKey(encryptionKey, key) {\n    return uint8ArrayToBitArray(aes128Decrypt(encryptionKey, concat(fixedIv, key), false));\n}\nexport function encrypt256Key(encryptionKey, key) {\n    return aes128Encrypt(encryptionKey, bitArrayToUint8Array(key), fixedIv, false, false).slice(fixedIv.length);\n}\nexport function decrypt256Key(encryptionKey, key) {\n    return uint8ArrayToBitArray(aes128Decrypt(encryptionKey, concat(fixedIv, key), false));\n}\nexport function aes256EncryptKey(encryptionKey, key) {\n    return aes256Encrypt(encryptionKey, bitArrayToUint8Array(key), fixedIv, false, false).slice(fixedIv.length);\n}\nexport function aes256DecryptKey(encryptionKey, key) {\n    return uint8ArrayToBitArray(aes256Decrypt(encryptionKey, concat(fixedIv, key), false, false));\n}\nexport function aes256Encrypt256Key(encryptionKey, keyToEncrypt) {\n    return aes256Encrypt(encryptionKey, bitArrayToUint8Array(keyToEncrypt), fixedIv, false, false).slice(fixedIv.length);\n}\nexport function aes256Decrypt256Key(encryptionKey, keyToDecrypt) {\n    return uint8ArrayToBitArray(aes256Decrypt(encryptionKey, concat(fixedIv, keyToDecrypt), false, false));\n}\nexport function encryptRsaKey(encryptionKey, privateKey, iv) {\n    return aes128Encrypt(encryptionKey, hexToUint8Array(privateKeyToHex(privateKey)), iv ? iv : random.generateRandomData(IV_BYTE_LENGTH), true, false);\n}\nexport function decryptRsaKey(encryptionKey, encryptedPrivateKey) {\n    return hexToPrivateKey(uint8ArrayToHex(aes128Decrypt(encryptionKey, encryptedPrivateKey, true)));\n}\n","// @ts-ignore[untyped-import]\nimport { BigInteger, parseBigInt, RSAKey } from \"../internal/crypto-jsbn-2012-08-09_1.js\";\nimport { base64ToHex, base64ToUint8Array, concat, int8ArrayToBase64, uint8ArrayToBase64, uint8ArrayToHex } from \"@tutao/tutanota-utils\";\nimport { CryptoError } from \"../misc/CryptoError.js\";\nimport { sha256Hash } from \"../hashes/Sha256.js\";\nconst RSA_KEY_LENGTH_BITS = 2048;\nconst RSA_PUBLIC_EXPONENT = 65537;\nexport function generateRsaKey() {\n    // jsbn is seeded inside, see SecureRandom.js\n    try {\n        let rsa = new RSAKey();\n        rsa.generate(RSA_KEY_LENGTH_BITS, RSA_PUBLIC_EXPONENT.toString(16)); // must be hex for JSBN\n        return {\n            publicKey: {\n                version: 0,\n                keyLength: RSA_KEY_LENGTH_BITS,\n                modulus: uint8ArrayToBase64(new Uint8Array(rsa.n.toByteArray())),\n                publicExponent: RSA_PUBLIC_EXPONENT,\n            },\n            privateKey: {\n                version: 0,\n                keyLength: RSA_KEY_LENGTH_BITS,\n                modulus: uint8ArrayToBase64(new Uint8Array(rsa.n.toByteArray())),\n                privateExponent: uint8ArrayToBase64(new Uint8Array(rsa.d.toByteArray())),\n                primeP: uint8ArrayToBase64(new Uint8Array(rsa.p.toByteArray())),\n                primeQ: uint8ArrayToBase64(new Uint8Array(rsa.q.toByteArray())),\n                primeExponentP: uint8ArrayToBase64(new Uint8Array(rsa.dmp1.toByteArray())),\n                primeExponentQ: uint8ArrayToBase64(new Uint8Array(rsa.dmq1.toByteArray())),\n                crtCoefficient: uint8ArrayToBase64(new Uint8Array(rsa.coeff.toByteArray())),\n            },\n        };\n    }\n    catch (e) {\n        throw new CryptoError(\"failed RSA key generation\", e);\n    }\n}\nexport function rsaEncrypt(publicKey, bytes, seed) {\n    const rsa = new RSAKey();\n    // we have double conversion from bytes to hex to big int because there is no direct conversion from bytes to big int\n    // BigInteger of JSBN uses a signed byte array and we convert to it by using Int8Array\n    rsa.n = new BigInteger(new Int8Array(base64ToUint8Array(publicKey.modulus)));\n    rsa.e = publicKey.publicExponent;\n    const paddedBytes = oaepPad(bytes, publicKey.keyLength, seed);\n    const paddedHex = uint8ArrayToHex(paddedBytes);\n    const bigInt = parseBigInt(paddedHex, 16);\n    let encrypted;\n    try {\n        // toByteArray() produces Array so we convert it to buffer.\n        encrypted = new Uint8Array(rsa.doPublic(bigInt).toByteArray());\n    }\n    catch (e) {\n        throw new CryptoError(\"failed RSA encryption\", e);\n    }\n    // the encrypted value might have leading zeros or needs to be padded with zeros\n    return _padAndUnpadLeadingZeros(publicKey.keyLength / 8, encrypted);\n}\nexport function rsaDecrypt(privateKey, bytes) {\n    try {\n        const rsa = new RSAKey();\n        // we have double conversion from bytes to hex to big int because there is no direct conversion from bytes to big int\n        // BigInteger of JSBN uses a signed byte array and we convert to it by using Int8Array\n        rsa.n = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.modulus)));\n        rsa.d = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.privateExponent)));\n        rsa.p = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.primeP)));\n        rsa.q = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.primeQ)));\n        rsa.dmp1 = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.primeExponentP)));\n        rsa.dmq1 = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.primeExponentQ)));\n        rsa.coeff = new BigInteger(new Int8Array(base64ToUint8Array(privateKey.crtCoefficient)));\n        const hex = uint8ArrayToHex(bytes);\n        const bigInt = parseBigInt(hex, 16);\n        const decrypted = new Uint8Array(rsa.doPrivate(bigInt).toByteArray());\n        // the decrypted value might have leading zeros or needs to be padded with zeros\n        const paddedDecrypted = _padAndUnpadLeadingZeros(privateKey.keyLength / 8 - 1, decrypted);\n        return oaepUnpad(paddedDecrypted, privateKey.keyLength);\n    }\n    catch (e) {\n        throw new CryptoError(\"failed RSA decryption\", e);\n    }\n}\n/**\n * Adds leading 0's to the given byte array until targeByteLength bytes are reached. Removes leading 0's if byteArray is longer than targetByteLength.\n */\nexport function _padAndUnpadLeadingZeros(targetByteLength, byteArray) {\n    const result = new Uint8Array(targetByteLength);\n    // JSBN produces results which are not always exact length.\n    // The byteArray might have leading 0 that make it larger than the actual result array length.\n    // Here we cut them off\n    // byteArray [0, 0, 1, 1, 1]\n    // target       [0, 0, 0, 0]\n    // result       [0, 1, 1, 1]\n    if (byteArray.length > result.length) {\n        const lastExtraByte = byteArray[byteArray.length - result.length - 1];\n        if (lastExtraByte !== 0) {\n            throw new CryptoError(`leading byte is not 0 but ${lastExtraByte}, encrypted length: ${byteArray.length}`);\n        }\n        byteArray = byteArray.slice(byteArray.length - result.length);\n    }\n    // If the byteArray is not as long as the result array we add leading 0's\n    // byteArray     [1, 1, 1]\n    // target     [0, 0, 0, 0]\n    // result     [0, 1, 1, 1]\n    result.set(byteArray, result.length - byteArray.length);\n    return result;\n}\n/********************************* OAEP *********************************/\n/**\n * Optimal Asymmetric Encryption Padding (OAEP) / RSA padding\n * @see https://tools.ietf.org/html/rfc3447#section-7.1\n *\n * @param value The byte array to encode.\n * @param keyLength The length of the RSA key in bit.\n * @param seed An array of 32 random bytes.\n * @return The padded byte array.\n */\nexport function oaepPad(value, keyLength, seed) {\n    let hashLength = 32; // bytes sha256\n    if (seed.length !== hashLength) {\n        throw new CryptoError(\"invalid seed length: \" + seed.length + \". expected: \" + hashLength + \" bytes!\");\n    }\n    if (value.length > keyLength / 8 - hashLength - 1) {\n        throw new CryptoError(\"invalid value length: \" + value.length + \". expected: max. \" + (keyLength / 8 - hashLength - 1));\n    }\n    let block = _getPSBlock(value, keyLength);\n    let dbMask = mgf1(seed, block.length - hashLength);\n    for (let i = hashLength; i < block.length; i++) {\n        block[i] ^= dbMask[i - hashLength];\n    }\n    // same as invoking sha256 directly because only one block is hashed\n    let seedMask = mgf1(block.slice(hashLength, block.length), hashLength);\n    for (let i = 0; i < seedMask.length; i++) {\n        block[i] = seed[i] ^ seedMask[i];\n    }\n    return block;\n}\n/**\n * @param value The byte array to unpad.\n * @param keyLength The length of the RSA key in bit.\n * @return The unpadded byte array.\n */\nexport function oaepUnpad(value, keyLength) {\n    let hashLength = 32; // bytes sha256\n    if (value.length !== keyLength / 8 - 1) {\n        throw new CryptoError(\"invalid value length: \" + value.length + \". expected: \" + (keyLength / 8 - 1) + \" bytes!\");\n    }\n    let seedMask = mgf1(value.slice(hashLength, value.length), hashLength);\n    let seed = new Uint8Array(hashLength);\n    for (let i = 0; i < seedMask.length; i++) {\n        seed[i] = value[i] ^ seedMask[i];\n    }\n    let dbMask = mgf1(seed, value.length - hashLength);\n    for (let i = hashLength; i < value.length; i++) {\n        value[i] ^= dbMask[i - hashLength];\n    }\n    // check that the zeros and the one is there\n    for (var index = 2 * hashLength; index < value.length; index++) {\n        if (value[index] === 1) {\n            // found the 0x01\n            break;\n        }\n        else if (value[index] !== 0 || index === value.length) {\n            throw new CryptoError(\"invalid padding\");\n        }\n    }\n    return value.slice(index + 1, value.length);\n}\n/**\n * Provides a block of keyLength / 8 - 1 bytes with the following format:\n * [ zeros ] [ label hash ] [ zeros ] [ 1 ] [ value ]\n *    32           32    keyLen-2*32-2  1  value.length\n * The label is the hash of an empty string like defined in PKCS#1 v2.1\n */\nexport function _getPSBlock(value, keyLength) {\n    let hashLength = 32; // bytes sha256\n    let blockLength = keyLength / 8 - 1; // the leading byte shall be 0 to make the resulting value in any case smaller than the modulus, so we just leave the byte off\n    let block = new Uint8Array(blockLength);\n    let defHash = sha256Hash(new Uint8Array([])); // empty label\n    let nbrOfZeros = block.length - (1 + value.length);\n    for (let i = 0; i < block.length; i++) {\n        if (i >= hashLength && i < 2 * hashLength) {\n            block[i] = defHash[i - hashLength];\n        }\n        else if (i < nbrOfZeros) {\n            block[i] = 0;\n        }\n        else if (i === nbrOfZeros) {\n            block[i] = 1;\n        }\n        else {\n            block[i] = value[i - nbrOfZeros - 1];\n        }\n    }\n    return block;\n}\n/********************************* PSS *********************************/\n/**\n * @param message The byte array to encode.\n * @param keyLength The length of the RSA key in bit.\n * @param salt An array of random bytes.\n * @return The padded byte array.\n */\nexport function encode(message, keyLength, salt) {\n    let hashLength = 32; // bytes sha256\n    let emLen = Math.ceil(keyLength / 8);\n    if (salt.length !== hashLength) {\n        throw new Error(\"invalid _salt length: \" + salt.length + \". expected: \" + hashLength + \" bytes!\");\n    }\n    let length = hashLength + salt.length + 2;\n    if (emLen < length) {\n        throw new Error(\"invalid hash/_salt length: \" + length + \". expected: max. \" + emLen);\n    }\n    let emBits = keyLength - 1;\n    let minEmBitsLength = 8 * hashLength + 8 * salt.length + 9;\n    if (emBits < minEmBitsLength) {\n        throw new Error(\"invalid maximum emBits length. Was \" + emBits + \", expected: \" + minEmBitsLength);\n    }\n    let messageHash = sha256Hash(message);\n    //  M' = (0x)00 00 00 00 00 00 00 00 || mHash || _salt\n    let message2 = concat(new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]), messageHash, salt);\n    let message2Hash = sha256Hash(message2);\n    let ps = new Uint8Array(emLen - salt.length - hashLength - 2);\n    for (let i = 0; i < ps.length; i++) {\n        ps[i] = 0;\n    }\n    let db = concat(ps, new Uint8Array([1]), salt);\n    _clear(ps);\n    let expectedDbLength = emLen - hashLength - 1;\n    if (db.length !== expectedDbLength) {\n        throw new Error(\"unexpected length of block: \" + db.length + \". Expected: \" + expectedDbLength);\n    }\n    let dbMask = mgf1(message2Hash, emLen - message2Hash.length - 1);\n    let maskedDb = new Uint8Array(dbMask.length);\n    for (let i = 0; i < dbMask.length; i++) {\n        maskedDb[i] = db[i] ^ dbMask[i];\n    }\n    _clear(db);\n    maskedDb[0] &= 0xff >> (8 * emLen - emBits);\n    let em = concat(maskedDb, message2Hash, new Uint8Array([188])); // 0xbc\n    _clear(maskedDb);\n    return em;\n}\n/**\n * clears an array to contain only zeros (0)\n */\nfunction _clear(array) {\n    if (!array) {\n        return;\n    }\n    array.fill(0);\n}\n/********************************* RSA utils *********************************/\n/**\n * @param seed An array of byte values.\n * @param length The length of the return value in bytes.\n */\nexport function mgf1(seed, length) {\n    let C = null;\n    let counter = 0;\n    let T = new Uint8Array(0);\n    do {\n        C = i2osp(counter);\n        T = concat(T, sha256Hash(concat(seed, C)));\n    } while (++counter < Math.ceil(length / (256 / 8)));\n    return T.slice(0, length);\n}\n/**\n * converts an integer to a 4 byte array\n */\nexport function i2osp(i) {\n    return new Uint8Array([(i >> 24) & 255, (i >> 16) & 255, (i >> 8) & 255, (i >> 0) & 255]);\n}\n/********************************* Key conversion *********************************/\n/**\n * @param publicKey\n * @returns The public key in a persistable array format\n * @private\n */\nfunction _publicKeyToArray(publicKey) {\n    return [_base64ToBigInt(publicKey.modulus)];\n}\n/**\n * @param privateKey\n * @returns The private key in a persistable array format\n * @private\n */\nfunction _privateKeyToArray(privateKey) {\n    return [\n        _base64ToBigInt(privateKey.modulus),\n        _base64ToBigInt(privateKey.privateExponent),\n        _base64ToBigInt(privateKey.primeP),\n        _base64ToBigInt(privateKey.primeQ),\n        _base64ToBigInt(privateKey.primeExponentP),\n        _base64ToBigInt(privateKey.primeExponentQ),\n        _base64ToBigInt(privateKey.crtCoefficient),\n    ];\n}\nfunction _arrayToPublicKey(publicKey) {\n    return {\n        version: 0,\n        keyLength: RSA_KEY_LENGTH_BITS,\n        modulus: int8ArrayToBase64(new Int8Array(publicKey[0].toByteArray())),\n        publicExponent: RSA_PUBLIC_EXPONENT,\n    };\n}\nfunction _arrayToPrivateKey(privateKey) {\n    return {\n        version: 0,\n        keyLength: RSA_KEY_LENGTH_BITS,\n        modulus: int8ArrayToBase64(new Int8Array(privateKey[0].toByteArray())),\n        privateExponent: int8ArrayToBase64(new Int8Array(privateKey[1].toByteArray())),\n        primeP: int8ArrayToBase64(new Int8Array(privateKey[2].toByteArray())),\n        primeQ: int8ArrayToBase64(new Int8Array(privateKey[3].toByteArray())),\n        primeExponentP: int8ArrayToBase64(new Int8Array(privateKey[4].toByteArray())),\n        primeExponentQ: int8ArrayToBase64(new Int8Array(privateKey[5].toByteArray())),\n        crtCoefficient: int8ArrayToBase64(new Int8Array(privateKey[6].toByteArray())),\n    };\n}\nfunction _base64ToBigInt(base64) {\n    return parseBigInt(base64ToHex(base64), 16);\n}\n/**\n * Provides the length of the given string as hex string of 4 characters length. Padding to 4 characters is done with '0'.\n * @param {string} string A string to get the length of.\n * @return {string} A hex string containing the length of string.\n */\nfunction _hexLen(string) {\n    var hexLen = string.length.toString(16);\n    while (hexLen.length < 4) {\n        hexLen = \"0\" + hexLen;\n    }\n    return hexLen;\n}\nexport function _keyArrayToHex(key) {\n    var hex = \"\";\n    for (var i = 0; i < key.length; i++) {\n        var param = key[i].toString(16);\n        if (param.length % 2 === 1) {\n            param = \"0\" + param;\n        }\n        hex += _hexLen(param) + param;\n    }\n    return hex;\n}\nfunction _hexToKeyArray(hex) {\n    try {\n        var key = [];\n        var pos = 0;\n        while (pos < hex.length) {\n            var nextParamLen = parseInt(hex.substring(pos, pos + 4), 16);\n            pos += 4;\n            key.push(parseBigInt(hex.substring(pos, pos + nextParamLen), 16));\n            pos += nextParamLen;\n        }\n        _validateKeyLength(key);\n        return key;\n    }\n    catch (e) {\n        throw new CryptoError(\"hex to rsa key failed\", e);\n    }\n}\nfunction _validateKeyLength(key) {\n    if (key.length !== 1 && key.length !== 7) {\n        throw new Error(\"invalid key params\");\n    }\n    if (key[0].bitLength() < RSA_KEY_LENGTH_BITS - 1 || key[0].bitLength() > RSA_KEY_LENGTH_BITS) {\n        throw new Error(\"invalid key length, expected: around \" + RSA_KEY_LENGTH_BITS + \", but was: \" + key[0].bitLength());\n    }\n}\nexport function privateKeyToHex(privateKey) {\n    return _keyArrayToHex(_privateKeyToArray(privateKey));\n}\nexport function publicKeyToHex(publicKey) {\n    return _keyArrayToHex(_publicKeyToArray(publicKey));\n}\nexport function hexToPrivateKey(privateKeyHex) {\n    return _arrayToPrivateKey(_hexToKeyArray(privateKeyHex));\n}\nexport function hexToPublicKey(publicKeyHex) {\n    return _arrayToPublicKey(_hexToKeyArray(publicKeyHex));\n}\n","import { OperationType } from \"../common/TutanotaConstants.js\"\nimport { containsEventOfType, getEventOfType } from \"../common/utils/Utils.js\"\nimport { assertNotNull, findAllAndRemove, isSameTypeRefByAttr, remove } from \"@tutao/tutanota-utils\"\nimport { ConnectionError, ServiceUnavailableError } from \"../common/error/RestError.js\"\nimport type { EntityUpdate } from \"../entities/sys/TypeRefs.js\"\nimport { ProgrammingError } from \"../common/error/ProgrammingError.js\"\nimport { MailTypeRef } from \"../entities/tutanota/TypeRefs.js\"\nimport { isSameId } from \"../common/utils/EntityUtils.js\"\nimport { CustomerInfoTypeRef } from \"../entities/sys/TypeRefs.js\"\nimport { EntityUpdateData } from \"../main/EventController.js\"\n\nexport type QueuedBatch = {\n\tevents: EntityUpdate[]\n\tgroupId: Id\n\tbatchId: Id\n}\n\nexport const enum EntityModificationType {\n\tCREATE = \"CREATE\",\n\tUPDATE = \"UPDATE\",\n\tMOVE = \"MOVE\",\n\tDELETE = \"DELETE\",\n}\n\ntype QueueAction = (nextElement: QueuedBatch) => Promise<void>\nconst MOVABLE_EVENT_TYPE_REFS = [\n\t// moved in MoveMailService\n\tMailTypeRef, // moved in SwitchAccountTypeService\n\tCustomerInfoTypeRef,\n]\n\n/**\n * Whether the entity of the event supports MOVE operation. MOVE is supposed to be immutable so we cannot apply it to all instances.\n */\nfunction isMovableEventType(event: EntityUpdate): boolean {\n\treturn MOVABLE_EVENT_TYPE_REFS.some((typeRef) => isSameTypeRefByAttr(typeRef, event.application, event.type))\n}\n\n/**\n * Checks which modification is applied in the given batch for the entity id.\n * @param batch entity updates of the batch.\n * @param entityId\n */\nexport function batchMod(batch: ReadonlyArray<EntityUpdate>, entityId: Id): EntityModificationType {\n\tconst batchAsUpdateData = batch as readonly EntityUpdateData[]\n\tfor (const event of batch) {\n\t\tif (isSameId(event.instanceId, entityId)) {\n\t\t\tswitch (event.operation) {\n\t\t\t\tcase OperationType.CREATE:\n\t\t\t\t\treturn isMovableEventType(event) && containsEventOfType(batchAsUpdateData, OperationType.DELETE, entityId)\n\t\t\t\t\t\t? EntityModificationType.MOVE\n\t\t\t\t\t\t: EntityModificationType.CREATE\n\n\t\t\t\tcase OperationType.UPDATE:\n\t\t\t\t\treturn EntityModificationType.UPDATE\n\n\t\t\t\tcase OperationType.DELETE:\n\t\t\t\t\treturn isMovableEventType(event) && containsEventOfType(batchAsUpdateData, OperationType.CREATE, entityId)\n\t\t\t\t\t\t? EntityModificationType.MOVE\n\t\t\t\t\t\t: EntityModificationType.DELETE\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new ProgrammingError(`Unknown operation: ${event.operation}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new ProgrammingError(`Batch does not have events for ${entityId}`)\n}\n\nexport class EventQueue {\n\t/** Batches to process. Oldest first. */\n\treadonly _eventQueue: Array<QueuedBatch>\n\treadonly _lastOperationForEntity: Map<Id, QueuedBatch>\n\treadonly _queueAction: QueueAction\n\treadonly _optimizationEnabled: boolean\n\t_processingBatch: QueuedBatch | null\n\t_paused: boolean\n\n\t/**\n\t * @param queueAction which is executed for each batch. Must *never* throw.\n\t */\n\tconstructor(optimizationEnabled: boolean, queueAction: QueueAction) {\n\t\tthis._eventQueue = []\n\t\tthis._lastOperationForEntity = new Map()\n\t\tthis._queueAction = queueAction\n\t\tthis._optimizationEnabled = optimizationEnabled\n\t\tthis._processingBatch = null\n\t\tthis._paused = false\n\t}\n\n\taddBatches(batches: ReadonlyArray<QueuedBatch>) {\n\t\tfor (const batch of batches) {\n\t\t\tthis.add(batch.batchId, batch.groupId, batch.events)\n\t\t}\n\t}\n\n\t/**\n\t * @return whether the batch was added (not optimized away)\n\t */\n\tadd(batchId: Id, groupId: Id, newEvents: ReadonlyArray<EntityUpdate>): boolean {\n\t\tconst newBatch: QueuedBatch = {\n\t\t\tevents: [],\n\t\t\tgroupId,\n\t\t\tbatchId,\n\t\t}\n\n\t\tif (!this._optimizationEnabled) {\n\t\t\tnewBatch.events.push(...newEvents)\n\t\t} else {\n\t\t\tthis._optimizingAddEvents(newBatch, batchId, groupId, newEvents)\n\t\t}\n\n\t\tif (newBatch.events.length !== 0) {\n\t\t\tthis._eventQueue.push(newBatch)\n\n\t\t\tfor (const update of newBatch.events) {\n\t\t\t\tthis._lastOperationForEntity.set(update.instanceId, newBatch)\n\t\t\t}\n\t\t}\n\n\t\t// ensures that events are processed when not paused\n\t\tthis.start()\n\t\treturn newBatch.events.length > 0\n\t}\n\n\t_optimizingAddEvents(newBatch: QueuedBatch, batchId: Id, groupId: Id, newEvents: ReadonlyArray<EntityUpdate>): void {\n\t\tfor (const newEvent of newEvents) {\n\t\t\tconst elementId = newEvent.instanceId\n\n\t\t\tconst lastBatchForEntity = this._lastOperationForEntity.get(elementId)\n\n\t\t\tif (\n\t\t\t\tlastBatchForEntity == null ||\n\t\t\t\t(this._processingBatch != null && this._processingBatch === lastBatchForEntity) ||\n\t\t\t\tgroupId !== lastBatchForEntity.groupId\n\t\t\t) {\n\t\t\t\t// If there's no current operation, there's nothing to merge, just add\n\t\t\t\t// If current operation is already being processed, don't modify it, we cannot merge anymore and should just append.\n\t\t\t\tnewBatch.events.push(newEvent)\n\t\t\t} else {\n\t\t\t\tconst newEntityModification = batchMod(newEvents, elementId)\n\t\t\t\tconst lastEntityModification = batchMod(lastBatchForEntity.events, elementId)\n\n\t\t\t\tif (newEntityModification === EntityModificationType.UPDATE) {\n\t\t\t\t\tswitch (lastEntityModification) {\n\t\t\t\t\t\tcase EntityModificationType.CREATE:\n\t\t\t\t\t\t// Skip create because the create was not processed yet and we will download the updated version already\n\t\t\t\t\t\tcase EntityModificationType.UPDATE:\n\t\t\t\t\t\t\t// Skip update because the previous update was not processed yet and we will download the updated version already\n\t\t\t\t\t\t\tbreak\n\n\t\t\t\t\t\tcase EntityModificationType.MOVE:\n\t\t\t\t\t\t\t// Leave both, as we expect MOVE to not mutate the entity\n\t\t\t\t\t\t\t// We will execute this twice for DELETE and CREATE but it's fine, we need both\n\t\t\t\t\t\t\tnewBatch.events.push(newEvent)\n\t\t\t\t\t\t\tbreak\n\n\t\t\t\t\t\tcase EntityModificationType.DELETE:\n\t\t\t\t\t\t\tthrow new ProgrammingError(\"UPDATE not allowed after DELETE\")\n\t\t\t\t\t}\n\t\t\t\t} else if (newEntityModification === EntityModificationType.MOVE) {\n\t\t\t\t\tif (newEvent.operation === OperationType.DELETE) {\n\t\t\t\t\t\t// We only want to process the CREAT event of the move operation\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (lastEntityModification) {\n\t\t\t\t\t\tcase EntityModificationType.CREATE:\n\t\t\t\t\t\t\t// Replace old create with new create of the move event\n\t\t\t\t\t\t\tthis._replace(lastBatchForEntity, newEvent)\n\n\t\t\t\t\t\t\t// ignore DELETE of move operation\n\t\t\t\t\t\t\tbreak\n\n\t\t\t\t\t\tcase EntityModificationType.UPDATE:\n\t\t\t\t\t\t\t// The instance is not at the original location anymore so we cannot leave update in because we won't be able to download\n\t\t\t\t\t\t\t// it but we also cannot say that it just moved so we need to actually delete and create it again\n\t\t\t\t\t\t\tconst deleteEvent = assertNotNull(getEventOfType(newEvents, OperationType.DELETE, newEvent.instanceId))\n\n\t\t\t\t\t\t\t// Replace update with delete the old location\n\t\t\t\t\t\t\tthis._replace(lastBatchForEntity, deleteEvent)\n\n\t\t\t\t\t\t\tnewBatch.events.push(newEvent)\n\t\t\t\t\t\t\tbreak\n\n\t\t\t\t\t\tcase EntityModificationType.MOVE:\n\t\t\t\t\t\t\t// Replace move with a move from original location to the final destination\n\t\t\t\t\t\t\tconst oldDelete = assertNotNull(getEventOfType(lastBatchForEntity.events, OperationType.DELETE, newEvent.instanceId))\n\n\t\t\t\t\t\t\tthis._replace(lastBatchForEntity, newEvent)\n\n\t\t\t\t\t\t\t// replace removes all events so we need to add the old delete again\n\t\t\t\t\t\t\tlastBatchForEntity.events.unshift(oldDelete)\n\t\t\t\t\t\t\tbreak\n\n\t\t\t\t\t\tcase EntityModificationType.DELETE:\n\t\t\t\t\t\t\tthrow new ProgrammingError(\"MOVE not allowed after DELETE\")\n\t\t\t\t\t} // skip delete in favor of create so that we don't run the same conditions twice\n\t\t\t\t} else if (newEntityModification === EntityModificationType.DELETE) {\n\t\t\t\t\t// find first move or delete (at different list) operation\n\t\t\t\t\tconst firstMoveIndex = this._eventQueue.findIndex(\n\t\t\t\t\t\t(queuedBatch) =>\n\t\t\t\t\t\t\tthis._processingBatch !== queuedBatch &&\n\t\t\t\t\t\t\tcontainsEventOfType(queuedBatch.events as readonly EntityUpdateData[], OperationType.DELETE, elementId),\n\t\t\t\t\t)\n\n\t\t\t\t\tif (firstMoveIndex !== -1) {\n\t\t\t\t\t\t// delete CREATE of first move and keep the DELETE event\n\t\t\t\t\t\tconst firstMoveBatch = this._eventQueue[firstMoveIndex]\n\t\t\t\t\t\tconst createEvent = getEventOfType(firstMoveBatch.events, OperationType.CREATE, elementId)\n\t\t\t\t\t\tcreateEvent && remove(firstMoveBatch.events, createEvent)\n\n\t\t\t\t\t\t// We removed empty batches from the list but the one in the map will still stay\n\t\t\t\t\t\t// so we need to manually clean it up.\n\t\t\t\t\t\tthis._lastOperationForEntity.set(elementId, this._eventQueue[firstMoveIndex])\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// add delete event\n\t\t\t\t\t\tnewBatch.events.push(newEvent) // _lastOperationForEntity will be set after the batch is prepared as it's non-empty\n\t\t\t\t\t}\n\n\t\t\t\t\t// delete all other events\n\t\t\t\t\tthis.removeEventsForInstance(elementId, firstMoveIndex + 1)\n\t\t\t\t} else if (newEntityModification === EntityModificationType.CREATE) {\n\t\t\t\t\tif (lastEntityModification === EntityModificationType.DELETE || lastEntityModification === EntityModificationType.CREATE) {\n\t\t\t\t\t\t// It is likely custom id instance which got re-created\n\t\t\t\t\t\tnewBatch.events.push(newEvent)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ProgrammingError(\n\t\t\t\t\t\t\t`Impossible modification combination ${lastEntityModification} ${newEntityModification} ${JSON.stringify(newEvent)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new ProgrammingError(\n\t\t\t\t\t\t`Impossible modification combination ${lastEntityModification} ${newEntityModification} ${JSON.stringify(newEvent)}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tremoveEventsForInstance(elementId: Id, startIndex: number = 0): void {\n\t\t// this will remove batches with an empty event list\n\t\tfindAllAndRemove(\n\t\t\tthis._eventQueue,\n\t\t\t(batchInThePast) => {\n\t\t\t\tif (this._processingBatch === batchInThePast) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\t// this will remove all events for the element id from the batch\n\t\t\t\tfindAllAndRemove(batchInThePast.events, (event) => isSameId(event.instanceId, elementId))\n\t\t\t\treturn batchInThePast.events.length === 0\n\t\t\t},\n\t\t\tstartIndex,\n\t\t)\n\t}\n\n\tstart() {\n\t\tif (this._processingBatch) {\n\t\t\treturn\n\t\t}\n\n\t\tthis._processNext()\n\t}\n\n\tqueueSize(): number {\n\t\treturn this._eventQueue.length\n\t}\n\n\t_processNext() {\n\t\tif (this._paused) {\n\t\t\treturn\n\t\t}\n\n\t\tconst next = this._eventQueue[0]\n\n\t\tif (next) {\n\t\t\tthis._processingBatch = next\n\n\t\t\tthis._queueAction(next)\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis._eventQueue.shift()\n\n\t\t\t\t\tthis._processingBatch = null\n\n\t\t\t\t\t// When we are done with the batch, we don't want to merge with it anymore\n\t\t\t\t\tfor (const event of next.events) {\n\t\t\t\t\t\tif (this._lastOperationForEntity.get(event.instanceId) === next) {\n\t\t\t\t\t\t\tthis._lastOperationForEntity.delete(event.instanceId)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._processNext()\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\t// processing continues if the event bus receives a new event\n\t\t\t\t\tthis._processingBatch = null\n\n\t\t\t\t\tif (!(e instanceof ServiceUnavailableError || e instanceof ConnectionError)) {\n\t\t\t\t\t\tconsole.error(\"Uncaught EventQueue error!\", e)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}\n\t}\n\n\tclear() {\n\t\tthis._eventQueue.splice(0)\n\n\t\tthis._processingBatch = null\n\n\t\tfor (const k of this._lastOperationForEntity.keys()) {\n\t\t\tthis._lastOperationForEntity.delete(k)\n\t\t}\n\t}\n\n\tpause() {\n\t\tthis._paused = true\n\t}\n\n\tresume() {\n\t\tthis._paused = false\n\t\tthis.start()\n\t}\n\n\t_replace(batch: QueuedBatch, newMod: EntityUpdate) {\n\t\tbatch.events = batch.events.filter((e) => e.instanceId !== newMod.instanceId)\n\t\tbatch.events.push(newMod)\n\t}\n}\n","import type { IProgressMonitor, ProgressMonitorId } from \"../common/utils/ProgressMonitor\"\nimport { ExposedProgressTracker } from \"../main/ProgressTracker.js\"\n\n/** A wrapper that will send completed work remotely */\nexport class ProgressMonitorDelegate implements IProgressMonitor {\n\tprivate readonly ref: Promise<ProgressMonitorId>\n\n\tconstructor(private readonly progressTracker: ExposedProgressTracker, readonly totalAmount: number) {\n\t\tthis.ref = progressTracker.registerMonitor(totalAmount)\n\t}\n\n\tasync workDone(amount: number) {\n\t\tawait this.progressTracker.workDoneForMonitor(await this.ref, amount)\n\t}\n\n\tasync completed() {\n\t\tawait this.progressTracker.workDoneForMonitor(await this.ref, this.totalAmount)\n\t}\n}\n","import { assertWorkerOrNode } from \"../common/Env\"\nimport {\n\tAccessBlockedError,\n\tAccessDeactivatedError,\n\tConnectionError,\n\thandleRestError,\n\tNotAuthorizedError,\n\tServiceUnavailableError,\n\tSessionExpiredError,\n} from \"../common/error/RestError\"\nimport {\n\tcreateWebsocketLeaderStatus,\n\tEntityEventBatch,\n\tEntityEventBatchTypeRef,\n\tEntityUpdate,\n\tWebsocketCounterData,\n\tWebsocketCounterDataTypeRef,\n\tWebsocketEntityData,\n\tWebsocketEntityDataTypeRef,\n\tWebsocketLeaderStatus,\n\tWebsocketLeaderStatusTypeRef,\n} from \"../entities/sys/TypeRefs.js\"\nimport { assertNotNull, binarySearch, delay, identity, lastThrow, ofClass, randomIntFromInterval } from \"@tutao/tutanota-utils\"\nimport { OutOfSyncError } from \"../common/error/OutOfSyncError\"\nimport { CloseEventBusOption, GroupType, SECOND_MS } from \"../common/TutanotaConstants\"\nimport { CancelledError } from \"../common/error/CancelledError\"\nimport { EntityClient } from \"../common/EntityClient\"\nimport type { QueuedBatch } from \"./EventQueue.js\"\nimport { EventQueue } from \"./EventQueue.js\"\nimport { ProgressMonitorDelegate } from \"./ProgressMonitorDelegate\"\nimport type { IProgressMonitor } from \"../common/utils/ProgressMonitor\"\nimport { NoopProgressMonitor } from \"../common/utils/ProgressMonitor\"\nimport { compareOldestFirst, firstBiggerThanSecond, GENERATED_MAX_ID, GENERATED_MIN_ID, getElementId, isSameId } from \"../common/utils/EntityUtils\"\nimport { InstanceMapper } from \"./crypto/InstanceMapper\"\nimport { WsConnectionState } from \"../main/WorkerClient\"\nimport { EntityRestCache } from \"./rest/DefaultEntityRestCache.js\"\nimport { SleepDetector } from \"./utils/SleepDetector.js\"\nimport sysModelInfo from \"../entities/sys/ModelInfo.js\"\nimport tutanotaModelInfo from \"../entities/tutanota/ModelInfo.js\"\nimport { resolveTypeReference } from \"../common/EntityFunctions.js\"\nimport { PhishingMarker, PhishingMarkerWebsocketData, PhishingMarkerWebsocketDataTypeRef } from \"../entities/tutanota/TypeRefs\"\nimport { UserFacade } from \"./facades/UserFacade\"\nimport { ExposedProgressTracker } from \"../main/ProgressTracker.js\"\n\nassertWorkerOrNode()\n\nexport const enum EventBusState {\n\tAutomatic = \"automatic\",\n\t// automatic reconnection is enabled\n\tSuspended = \"suspended\",\n\t// automatic reconnection is suspended but can be enabled again\n\tTerminated = \"terminated\", // automatic reconnection is disabled and websocket is closed but can be opened again by calling connect explicit\n}\n\n// EntityEventBatches expire after 45 days. keep a time diff security of one day.\nexport const ENTITY_EVENT_BATCH_EXPIRE_MS = 44 * 24 * 60 * 60 * 1000\nconst RETRY_AFTER_SERVICE_UNAVAILABLE_ERROR_MS = 30000\nconst NORMAL_SHUTDOWN_CLOSE_CODE = 1\n/**\n * Reconnection interval bounds. When we reconnect we pick a random number of seconds in a range to prevent that all the clients connect at the same time which\n * would put unnecessary load on the server.\n * The range depends on the number of attempts and the server response.\n * */\nconst RECONNECT_INTERVAL = Object.freeze({\n\tSMALL: [5, 10],\n\tMEDIUM: [20, 40],\n\tLARGE: [60, 120],\n} as const)\n// we store the last 1000 event ids per group, so we know if an event was already processed.\n// it is not sufficient to check the last event id because a smaller event id may arrive later\n// than a bigger one if the requests are processed in parallel on the server\nconst MAX_EVENT_IDS_QUEUE_LENGTH = 1000\n\n/** Known types of messages that can be received over websocket. */\nconst enum MessageType {\n\tEntityUpdate = \"entityUpdate\",\n\tUnreadCounterUpdate = \"unreadCounterUpdate\",\n\tPhishingMarkers = \"phishingMarkers\",\n\tLeaderStatus = \"leaderStatus\",\n}\n\nexport const enum ConnectMode {\n\tInitial,\n\tReconnect,\n}\n\nexport interface EventBusListener {\n\tonWebsocketStateChanged(state: WsConnectionState): unknown\n\n\tonCounterChanged(counter: WebsocketCounterData): unknown\n\n\tonLeaderStatusChanged(leaderStatus: WebsocketLeaderStatus): unknown\n\n\tonEntityEventsReceived(events: EntityUpdate[], batchId: Id, groupId: Id): Promise<void>\n\n\tonPhishingMarkersReceived(markers: PhishingMarker[]): unknown\n\n\tonError(tutanotaError: Error): void\n}\n\nexport class EventBusClient {\n\tprivate state: EventBusState\n\tprivate socket: WebSocket | null\n\tprivate immediateReconnect: boolean = false // if true tries to reconnect immediately after the websocket is closed\n\n\t/**\n\t * Map from group id to last event ids (max. _MAX_EVENT_IDS_QUEUE_LENGTH). We keep them to avoid processing the same event twice if\n\t * it comes out of order from the server) and for requesting missed entity events on reconnect.\n\t *\n\t * We do not have to update these event ids if the groups of the user change because we always take the current users groups from the\n\t * LoginFacade.\n\t */\n\tprivate lastEntityEventIds: Map<Id, Array<Id>>\n\n\t/**\n\t * Last batch which was actually added to the queue. We need it to find out when the group is processed\n\t */\n\tprivate lastAddedBatchForGroup: Map<Id, Id>\n\n\tprivate lastAntiphishingMarkersId: Id | null = null\n\n\t/** Queue to process all events. */\n\tprivate readonly eventQueue: EventQueue\n\n\t/** Queue that handles incoming websocket messages only. Caches them until we process downloaded ones and then adds them to eventQueue. */\n\tprivate readonly entityUpdateMessageQueue: EventQueue\n\tprivate reconnectTimer: TimeoutID | null\n\tprivate connectTimer: TimeoutID | null\n\n\t/**\n\t * Represents a currently retried executing due to a ServiceUnavailableError\n\t */\n\tprivate serviceUnavailableRetry: Promise<void> | null = null\n\tprivate failedConnectionAttempts: number = 0\n\tprivate progressMonitor: IProgressMonitor\n\n\tconstructor(\n\t\tprivate readonly listener: EventBusListener,\n\t\tprivate readonly cache: EntityRestCache,\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly entity: EntityClient,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly socketFactory: (path: string) => WebSocket,\n\t\tprivate readonly sleepDetector: SleepDetector,\n\t\tprivate readonly progressTracker: ExposedProgressTracker,\n\t) {\n\t\t// We are not connected by default and will not try to unless connect() is called\n\t\tthis.state = EventBusState.Terminated\n\t\tthis.lastEntityEventIds = new Map()\n\t\tthis.lastAddedBatchForGroup = new Map()\n\t\tthis.socket = null\n\t\tthis.reconnectTimer = null\n\t\tthis.connectTimer = null\n\t\tthis.progressMonitor = new NoopProgressMonitor()\n\t\tthis.eventQueue = new EventQueue(true, (modification) => this.eventQueueCallback(modification))\n\t\tthis.entityUpdateMessageQueue = new EventQueue(false, (batch) => this.entityUpdateMessageQueueCallback(batch))\n\t\tthis.reset()\n\t}\n\n\tprivate reset() {\n\t\tthis.immediateReconnect = false\n\n\t\tthis.lastEntityEventIds.clear()\n\n\t\tthis.lastAddedBatchForGroup.clear()\n\n\t\tthis.eventQueue.pause()\n\n\t\tthis.eventQueue.clear()\n\n\t\tthis.serviceUnavailableRetry = null\n\t}\n\n\t/**\n\t * Opens a WebSocket connection to receive server events.\n\t * @param connectMode\n\t */\n\tconnect(connectMode: ConnectMode) {\n\t\tconsole.log(\"ws connect reconnect:\", connectMode === ConnectMode.Reconnect, \"state:\", this.state)\n\t\t// make sure a retry will be cancelled by setting _serviceUnavailableRetry to null\n\t\tthis.serviceUnavailableRetry = null\n\n\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connecting)\n\n\t\t// Task for updating events are number of groups + 2. Use 2 as base for reconnect state.\n\t\tif (this.progressMonitor) {\n\t\t\t// Say that the old monitor is completed so that we don't calculate its amount as still to do.\n\t\t\tthis.progressMonitor.completed()\n\t\t}\n\n\t\tthis.progressMonitor = new ProgressMonitorDelegate(this.progressTracker, this.eventGroups().length + 2)\n\t\tthis.progressMonitor.workDone(1)\n\n\t\tthis.state = EventBusState.Automatic\n\t\tthis.connectTimer = null\n\n\t\tconst authHeaders = this.userFacade.createAuthHeaders()\n\n\t\t// Native query building is not supported in old browser, mithril is not available in the worker\n\t\tconst authQuery =\n\t\t\t\"modelVersions=\" +\n\t\t\tsysModelInfo.version +\n\t\t\t\".\" +\n\t\t\ttutanotaModelInfo.version +\n\t\t\t\"&clientVersion=\" +\n\t\t\tenv.versionNumber +\n\t\t\t\"&userId=\" +\n\t\t\tthis.userFacade.getLoggedInUser()._id +\n\t\t\t\"&accessToken=\" +\n\t\t\tauthHeaders.accessToken +\n\t\t\t(this.lastAntiphishingMarkersId ? \"&lastPhishingMarkersId=\" + this.lastAntiphishingMarkersId : \"\")\n\t\tconst path = \"/event?\" + authQuery\n\n\t\tthis.unsubscribeFromOldWebsocket()\n\n\t\tthis.socket = this.socketFactory(path)\n\t\tthis.socket.onopen = () => this.onOpen(connectMode)\n\t\tthis.socket.onclose = (event: CloseEvent) => this.onClose(event)\n\t\tthis.socket.onerror = (error: any) => this.onError(error)\n\t\tthis.socket.onmessage = (message: MessageEvent<string>) => this.onMessage(message)\n\n\t\tthis.sleepDetector.start(() => {\n\t\t\tconsole.log(\"ws sleep detected, reconnecting...\")\n\t\t\tthis.tryReconnect(true, true)\n\t\t})\n\t}\n\n\t/**\n\t * Sends a close event to the server and finally closes the connection.\n\t * The state of this event bus client is reset and the client is terminated (does not automatically reconnect) except reconnect == true\n\t */\n\tasync close(closeOption: CloseEventBusOption): Promise<void> {\n\t\tconsole.log(\"ws close closeOption: \", closeOption, \"state:\", this.state)\n\n\t\tswitch (closeOption) {\n\t\t\tcase CloseEventBusOption.Terminate:\n\t\t\t\tthis.terminate()\n\n\t\t\t\tbreak\n\n\t\t\tcase CloseEventBusOption.Pause:\n\t\t\t\tthis.state = EventBusState.Suspended\n\n\t\t\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connecting)\n\n\t\t\t\tbreak\n\n\t\t\tcase CloseEventBusOption.Reconnect:\n\t\t\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connecting)\n\n\t\t\t\tbreak\n\t\t}\n\n\t\tthis.socket?.close()\n\t}\n\n\tasync tryReconnect(closeIfOpen: boolean, enableAutomaticState: boolean, delay: number | null = null): Promise<void> {\n\t\tconsole.log(\"ws tryReconnect closeIfOpen:\", closeIfOpen, \"enableAutomaticState:\", enableAutomaticState, \"delay:\", delay)\n\n\t\tif (this.reconnectTimer) {\n\t\t\t// prevent reconnect race-condition\n\t\t\tclearTimeout(this.reconnectTimer)\n\t\t\tthis.reconnectTimer = null\n\t\t}\n\n\t\tif (!delay) {\n\t\t\tthis.reconnect(closeIfOpen, enableAutomaticState)\n\t\t} else {\n\t\t\tthis.reconnectTimer = setTimeout(() => this.reconnect(closeIfOpen, enableAutomaticState), delay)\n\t\t}\n\t}\n\n\t// Returning promise for tests\n\tprivate onOpen(connectMode: ConnectMode): Promise<void> {\n\t\tthis.failedConnectionAttempts = 0\n\t\tconsole.log(\"ws open state:\", this.state)\n\n\t\t// Indicate some progress right away\n\t\tthis.progressMonitor.workDone(1)\n\n\t\tconst p = this.initEntityEvents(connectMode)\n\n\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connected)\n\n\t\treturn p\n\t}\n\n\tprivate onError(error: any) {\n\t\tconsole.log(\"ws error:\", error, JSON.stringify(error), \"state:\", this.state)\n\t}\n\n\tprivate async onMessage(message: MessageEvent<string>): Promise<void> {\n\t\tconst [type, value] = message.data.split(\";\")\n\n\t\tswitch (type) {\n\t\t\tcase MessageType.EntityUpdate: {\n\t\t\t\tconst data: WebsocketEntityData = await this.instanceMapper.decryptAndMapToInstance(\n\t\t\t\t\tawait resolveTypeReference(WebsocketEntityDataTypeRef),\n\t\t\t\t\tJSON.parse(value),\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t\tthis.entityUpdateMessageQueue.add(data.eventBatchId, data.eventBatchOwner, data.eventBatch)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase MessageType.UnreadCounterUpdate: {\n\t\t\t\tconst counterData: WebsocketCounterData = await this.instanceMapper.decryptAndMapToInstance(\n\t\t\t\t\tawait resolveTypeReference(WebsocketCounterDataTypeRef),\n\t\t\t\t\tJSON.parse(value),\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t\tthis.listener.onCounterChanged(counterData)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase MessageType.PhishingMarkers: {\n\t\t\t\tconst data: PhishingMarkerWebsocketData = await this.instanceMapper.decryptAndMapToInstance(\n\t\t\t\t\tawait resolveTypeReference(PhishingMarkerWebsocketDataTypeRef),\n\t\t\t\t\tJSON.parse(value),\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t\tthis.lastAntiphishingMarkersId = data.lastId\n\t\t\t\tthis.listener.onPhishingMarkersReceived(data.markers)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase MessageType.LeaderStatus:\n\t\t\t\tconst data: WebsocketLeaderStatus = await this.instanceMapper.decryptAndMapToInstance(\n\t\t\t\t\tawait resolveTypeReference(WebsocketLeaderStatusTypeRef),\n\t\t\t\t\tJSON.parse(value),\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t\tawait this.userFacade.setLeaderStatus(data)\n\t\t\t\tawait this.listener.onLeaderStatusChanged(data)\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tconsole.log(\"ws message with unknown type\", type)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate onClose(event: CloseEvent) {\n\t\tthis.failedConnectionAttempts++\n\t\tconsole.log(\"ws close event:\", event, \"state:\", this.state)\n\n\t\tthis.userFacade.setLeaderStatus(\n\t\t\tcreateWebsocketLeaderStatus({\n\t\t\t\tleaderStatus: false,\n\t\t\t}),\n\t\t)\n\n\t\tthis.sleepDetector.stop()\n\n\t\t// Avoid running into penalties when trying to authenticate with an invalid session\n\t\t// NotAuthenticatedException 401, AccessDeactivatedException 470, AccessBlocked 472\n\t\t// do not catch session expired here because websocket will be reused when we authenticate again\n\t\tconst serverCode = event.code - 4000\n\n\t\tif ([NotAuthorizedError.CODE, AccessDeactivatedError.CODE, AccessBlockedError.CODE].includes(serverCode)) {\n\t\t\tthis.terminate()\n\t\t\tthis.listener.onError(handleRestError(serverCode, \"web socket error\", null, null))\n\t\t} else if (serverCode === SessionExpiredError.CODE) {\n\t\t\t// session is expired. do not try to reconnect until the user creates a new session\n\t\t\tthis.state = EventBusState.Suspended\n\t\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connecting)\n\t\t} else if (this.state === EventBusState.Automatic && this.userFacade.isFullyLoggedIn()) {\n\t\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.connecting)\n\n\t\t\tif (this.immediateReconnect) {\n\t\t\t\tthis.immediateReconnect = false\n\t\t\t\tthis.tryReconnect(false, false)\n\t\t\t} else {\n\t\t\t\tlet reconnectionInterval: readonly [number, number]\n\n\t\t\t\tif (serverCode === NORMAL_SHUTDOWN_CLOSE_CODE) {\n\t\t\t\t\treconnectionInterval = RECONNECT_INTERVAL.LARGE\n\t\t\t\t} else if (this.failedConnectionAttempts === 1) {\n\t\t\t\t\treconnectionInterval = RECONNECT_INTERVAL.SMALL\n\t\t\t\t} else if (this.failedConnectionAttempts === 2) {\n\t\t\t\t\treconnectionInterval = RECONNECT_INTERVAL.MEDIUM\n\t\t\t\t} else {\n\t\t\t\t\treconnectionInterval = RECONNECT_INTERVAL.LARGE\n\t\t\t\t}\n\n\t\t\t\tthis.tryReconnect(false, false, SECOND_MS * randomIntFromInterval(reconnectionInterval[0], reconnectionInterval[1]))\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async initEntityEvents(connectMode: ConnectMode): Promise<void> {\n\t\t// pause processing entity update message while initializing event queue\n\t\tthis.entityUpdateMessageQueue.pause()\n\n\t\t// pause event queue and add all missed entity events first\n\t\tthis.eventQueue.pause()\n\n\t\tconst existingConnection = connectMode == ConnectMode.Reconnect && this.lastEntityEventIds.size > 0\n\t\tconst p = existingConnection ? this.loadMissedEntityEvents() : this.initOnNewConnection()\n\n\t\treturn p\n\t\t\t.then(() => {\n\t\t\t\tthis.entityUpdateMessageQueue.resume()\n\t\t\t\tthis.eventQueue.resume()\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(ConnectionError, (e) => {\n\t\t\t\t\tconsole.log(\"ws not connected in connect(), close websocket\", e)\n\t\t\t\t\tthis.close(CloseEventBusOption.Reconnect)\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(CancelledError, () => {\n\t\t\t\t\t// the processing was aborted due to a reconnect. do not reset any attributes because they might already be in use since reconnection\n\t\t\t\t\tconsole.log(\"ws cancelled retry process entity events after reconnect\")\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(ServiceUnavailableError, async (e) => {\n\t\t\t\t\t// a ServiceUnavailableError is a temporary error and we have to retry to avoid data inconsistencies\n\t\t\t\t\t// some EventBatches/missed events are processed already now\n\t\t\t\t\t// for an existing connection we just keep the current state and continue loading missed events for the other groups\n\t\t\t\t\t// for a new connection we reset the last entity event ids because otherwise this would not be completed in the next try\n\t\t\t\t\tif (!existingConnection) {\n\t\t\t\t\t\tthis.lastEntityEventIds.clear()\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.log(\"ws retry init entity events in \", RETRY_AFTER_SERVICE_UNAVAILABLE_ERROR_MS, e)\n\t\t\t\t\tlet promise = delay(RETRY_AFTER_SERVICE_UNAVAILABLE_ERROR_MS).then(() => {\n\t\t\t\t\t\t// if we have a websocket reconnect we have to stop retrying\n\t\t\t\t\t\tif (this.serviceUnavailableRetry === promise) {\n\t\t\t\t\t\t\tconsole.log(\"ws retry initializing entity events\")\n\t\t\t\t\t\t\treturn this.initEntityEvents(connectMode)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.log(\"ws cancel initializing entity events\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tthis.serviceUnavailableRetry = promise\n\t\t\t\t\treturn promise\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(OutOfSyncError, async (e) => {\n\t\t\t\t\t// we did not check for updates for too long, so some missed EntityEventBatches can not be loaded any more\n\t\t\t\t\t// purge cache if out of sync\n\t\t\t\t\tawait this.cache.purgeStorage()\n\t\t\t\t\t// We want users to re-login. By the time we get here they probably already have loaded some entities which we cannot update\n\t\t\t\t\tthrow e\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch((e) => {\n\t\t\t\tthis.entityUpdateMessageQueue.resume()\n\n\t\t\t\tthis.eventQueue.resume()\n\n\t\t\t\tthis.listener.onError(e)\n\t\t\t})\n\t}\n\n\tprivate async initOnNewConnection() {\n\t\tconst { lastIds, someIdsWereCached } = await this.retrieveLastEntityEventIds()\n\t\t// First, we record lastEntityEventIds. We need this to know what we need to re-fetch.\n\t\t// This is not the same as the cache because we might have already downloaded them but cache might not have processed them yet.\n\t\t// Important: do it in one step so that we don't have partial IDs in the map in case an error occurs.\n\t\tthis.lastEntityEventIds = lastIds\n\n\t\t// Second, we need to initialize the cache too.\n\t\tif (someIdsWereCached) {\n\t\t\t// If some of the last IDs were retrieved from the cache then we want to load from that point to bring cache up-to-date. This is mostly important for\n\t\t\t// persistent cache.\n\t\t\tawait this.loadMissedEntityEvents()\n\t\t} else {\n\t\t\t// If the cache is clean then this is a clean cache (either ephemeral after first connect or persistent with empty DB).\n\t\t\t// We need to record the time even if we don't process anything to later know if we are out of sync or not.\n\t\t\tawait this.cache.recordSyncTime()\n\t\t}\n\t}\n\n\t/**\n\t * Gets the latest event batch ids for each of the users groups or min id if there is no event batch yet.\n\t * This is needed to know from where to start loading missed events when we connect.\n\t */\n\tprivate async retrieveLastEntityEventIds(): Promise<{ lastIds: Map<Id, Array<Id>>; someIdsWereCached: boolean }> {\n\t\t// set all last event ids in one step to avoid that we have just set them for a few groups when a ServiceUnavailableError occurs\n\t\tconst lastIds: Map<Id, Array<Id>> = new Map()\n\t\tlet someIdsWereCached = false\n\t\tfor (const groupId of this.eventGroups()) {\n\t\t\tconst cachedBatchId = await this.cache.getLastEntityEventBatchForGroup(groupId)\n\t\t\tif (cachedBatchId != null) {\n\t\t\t\tlastIds.set(groupId, [cachedBatchId])\n\t\t\t\tsomeIdsWereCached = true\n\t\t\t} else {\n\t\t\t\tconst batches = await this.entity.loadRange(EntityEventBatchTypeRef, groupId, GENERATED_MAX_ID, 1, true)\n\t\t\t\tconst batchId = batches.length === 1 ? getElementId(batches[0]) : GENERATED_MIN_ID\n\t\t\t\tlastIds.set(groupId, [batchId])\n\t\t\t\t// In case we don't receive any events for the group this time we want to still download from this point next time.\n\t\t\t\tawait this.cache.setLastEntityEventBatchForGroup(groupId, batchId)\n\t\t\t\t// We will not process any entities for this group so we consider this group \"done\"\n\t\t\t\tthis.progressMonitor.workDone(1)\n\t\t\t}\n\t\t}\n\n\t\treturn { lastIds, someIdsWereCached }\n\t}\n\n\t/** Load event batches since the last time we were connected to bring cache and other things up-to-date. */\n\tprivate async loadMissedEntityEvents(): Promise<void> {\n\t\tif (!this.userFacade.isFullyLoggedIn()) {\n\t\t\treturn\n\t\t}\n\n\t\tawait this.checkOutOfSync()\n\n\t\tfor (let groupId of this.eventGroups()) {\n\t\t\tconst eventBatches = await this.loadEntityEventsForGroup(groupId)\n\t\t\tif (eventBatches.length === 0) {\n\t\t\t\t// There won't be a callback from the queue to process the event so we mark this group as\n\t\t\t\t// completed right away\n\t\t\t\tthis.progressMonitor.workDone(1)\n\t\t\t} else {\n\t\t\t\tfor (const batch of eventBatches) {\n\t\t\t\t\tthis.addBatch(getElementId(batch), groupId, batch.events)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// We've loaded all the batches, we've added them to the queue, we can let the cache remember sync point for us to detect out of sync now.\n\t\t// It is possible that we will record the time before the batch will be processed but the risk is low.\n\t\tawait this.cache.recordSyncTime()\n\t}\n\n\tprivate async loadEntityEventsForGroup(groupId: Id): Promise<EntityEventBatch[]> {\n\t\ttry {\n\t\t\treturn await this.entity.loadAll(EntityEventBatchTypeRef, groupId, this.getLastEventBatchIdOrMinIdForGroup(groupId))\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotAuthorizedError) {\n\t\t\t\tconsole.log(\"ws could not download entity updates, lost permission\")\n\t\t\t\treturn []\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async checkOutOfSync() {\n\t\t// We try to detect whether event batches have already expired.\n\t\t// If this happened we don't need to download anything, we need to purge the cache and start all over.\n\t\tif (await this.cache.isOutOfSync()) {\n\t\t\t// Allow the progress bar to complete\n\t\t\tthis.progressMonitor.completed()\n\n\t\t\t// We handle it where we initialize the connection and purge the cache there.\n\t\t\tthrow new OutOfSyncError(\"some missed EntityEventBatches cannot be loaded any more\")\n\t\t}\n\t}\n\n\tprivate async eventQueueCallback(modification: QueuedBatch): Promise<void> {\n\t\ttry {\n\t\t\tawait this.processEventBatch(modification)\n\t\t} catch (e) {\n\t\t\tconsole.log(\"ws error while processing event batches\", e)\n\t\t\tthis.listener.onError(e)\n\t\t\tthrow e\n\t\t}\n\n\t\t// If we completed the event, it was added before\n\t\tconst lastForGroup = assertNotNull(this.lastAddedBatchForGroup.get(modification.groupId))\n\n\t\tif (isSameId(modification.batchId, lastForGroup) || firstBiggerThanSecond(modification.batchId, lastForGroup)) {\n\t\t\tthis.progressMonitor && this.progressMonitor.workDone(1)\n\t\t}\n\t}\n\n\tprivate async entityUpdateMessageQueueCallback(batch: QueuedBatch): Promise<void> {\n\t\tthis.addBatch(batch.batchId, batch.groupId, batch.events)\n\t\tthis.eventQueue.resume()\n\t}\n\n\tprivate unsubscribeFromOldWebsocket() {\n\t\tif (this.socket) {\n\t\t\t// Remove listeners. We don't want old socket to mess our state\n\t\t\tthis.socket.onopen = this.socket.onclose = this.socket.onerror = this.socket.onmessage = identity\n\t\t}\n\t}\n\n\tprivate async terminate(): Promise<void> {\n\t\tthis.state = EventBusState.Terminated\n\n\t\tthis.reset()\n\n\t\tthis.listener.onWebsocketStateChanged(WsConnectionState.terminated)\n\t}\n\n\t/**\n\t * Tries to reconnect the websocket if it is not connected.\n\t */\n\tprivate reconnect(closeIfOpen: boolean, enableAutomaticState: boolean) {\n\t\tconsole.log(\n\t\t\t\"ws reconnect socket.readyState: (CONNECTING=0, OPEN=1, CLOSING=2, CLOSED=3): \" + (this.socket ? this.socket.readyState : \"null\"),\n\t\t\t\"state:\",\n\t\t\tthis.state,\n\t\t\t\"closeIfOpen:\",\n\t\t\tcloseIfOpen,\n\t\t\t\"enableAutomaticState:\",\n\t\t\tenableAutomaticState,\n\t\t)\n\n\t\tif (this.state !== EventBusState.Terminated && enableAutomaticState) {\n\t\t\tthis.state = EventBusState.Automatic\n\t\t}\n\n\t\tif (closeIfOpen && this.socket && this.socket.readyState === WebSocket.OPEN) {\n\t\t\tthis.immediateReconnect = true\n\t\t\tthis.socket.close()\n\t\t} else if (\n\t\t\t(this.socket == null || this.socket.readyState === WebSocket.CLOSED || this.socket.readyState === WebSocket.CLOSING) &&\n\t\t\tthis.state !== EventBusState.Terminated &&\n\t\t\tthis.userFacade.isFullyLoggedIn()\n\t\t) {\n\t\t\t// Don't try to connect right away because connection may not be actually there\n\t\t\t// see #1165\n\t\t\tif (this.connectTimer) {\n\t\t\t\tclearTimeout(this.connectTimer)\n\t\t\t}\n\n\t\t\tthis.connectTimer = setTimeout(() => this.connect(ConnectMode.Reconnect), 100)\n\t\t}\n\t}\n\n\tprivate addBatch(batchId: Id, groupId: Id, events: ReadonlyArray<EntityUpdate>) {\n\t\tconst lastForGroup = this.lastEntityEventIds.get(groupId) || []\n\t\t// find the position for inserting into last entity events (negative value is considered as not present in the array)\n\t\tconst index = binarySearch(lastForGroup, batchId, compareOldestFirst)\n\t\tlet wasAdded\n\n\t\tif (index < 0) {\n\t\t\tlastForGroup.splice(-index, 0, batchId)\n\t\t\t// only add the batch if it was not process before\n\t\t\twasAdded = this.eventQueue.add(batchId, groupId, events)\n\t\t} else {\n\t\t\twasAdded = false\n\t\t}\n\n\t\tif (lastForGroup.length > MAX_EVENT_IDS_QUEUE_LENGTH) {\n\t\t\tlastForGroup.shift()\n\t\t}\n\n\t\tthis.lastEntityEventIds.set(batchId, lastForGroup)\n\n\t\tif (wasAdded) {\n\t\t\tthis.lastAddedBatchForGroup.set(groupId, batchId)\n\t\t}\n\t}\n\n\tprivate async processEventBatch(batch: QueuedBatch): Promise<void> {\n\t\ttry {\n\t\t\tif (this.isTerminated()) return\n\t\t\tconst filteredEvents = await this.cache.entityEventsReceived(batch)\n\t\t\tif (!this.isTerminated()) await this.listener.onEntityEventsReceived(filteredEvents, batch.batchId, batch.groupId)\n\t\t} catch (e) {\n\t\t\tif (e instanceof ServiceUnavailableError) {\n\t\t\t\t// a ServiceUnavailableError is a temporary error and we have to retry to avoid data inconsistencies\n\t\t\t\tconsole.log(\"ws retry processing event in 30s\", e)\n\t\t\t\tconst retryPromise = delay(RETRY_AFTER_SERVICE_UNAVAILABLE_ERROR_MS).then(() => {\n\t\t\t\t\t// if we have a websocket reconnect we have to stop retrying\n\t\t\t\t\tif (this.serviceUnavailableRetry === retryPromise) {\n\t\t\t\t\t\treturn this.processEventBatch(batch)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new CancelledError(\"stop retry processing after service unavailable due to reconnect\")\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tthis.serviceUnavailableRetry = retryPromise\n\t\t\t\treturn retryPromise\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getLastEventBatchIdOrMinIdForGroup(groupId: Id): Id {\n\t\tconst lastIds = this.lastEntityEventIds.get(groupId)\n\n\t\treturn lastIds && lastIds.length > 0 ? lastThrow(lastIds) : GENERATED_MIN_ID\n\t}\n\n\tprivate isTerminated() {\n\t\treturn this.state === EventBusState.Terminated\n\t}\n\n\tprivate eventGroups(): Id[] {\n\t\treturn this.userFacade\n\t\t\t.getLoggedInUser()\n\t\t\t.memberships.filter((membership) => membership.groupType !== GroupType.MailingList)\n\t\t\t.map((membership) => membership.group)\n\t\t\t.concat(this.userFacade.getLoggedInUser().userGroup.group)\n\t}\n}\n","import type { EntityRestInterface } from \"./EntityRestClient\"\nimport { EntityRestClient, EntityRestClientSetupOptions } from \"./EntityRestClient\"\nimport { resolveTypeReference } from \"../../common/EntityFunctions\"\nimport { OperationType } from \"../../common/TutanotaConstants\"\nimport { assertNotNull, difference, flat, getFirstOrThrow, groupBy, isSameTypeRef, lastThrow, TypeRef } from \"@tutao/tutanota-utils\"\nimport { containsEventOfType, getEventOfType } from \"../../common/utils/Utils\"\nimport type { EntityUpdate, User } from \"../../entities/sys/TypeRefs.js\"\nimport {\n\tBucketPermissionTypeRef,\n\tEntityEventBatchTypeRef,\n\tPermissionTypeRef,\n\tRecoverCodeTypeRef,\n\tRejectedSenderTypeRef,\n\tSecondFactorTypeRef,\n\tSessionTypeRef,\n\tUserTypeRef,\n} from \"../../entities/sys/TypeRefs.js\"\nimport { ValueType } from \"../../common/EntityConstants\"\nimport { NotAuthorizedError, NotFoundError } from \"../../common/error/RestError\"\nimport { MailDetailsBlobTypeRef, MailTypeRef } from \"../../entities/tutanota/TypeRefs.js\"\nimport { firstBiggerThanSecond, GENERATED_MAX_ID, GENERATED_MIN_ID, getElementId } from \"../../common/utils/EntityUtils\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport type { ListElementEntity, SomeEntity, TypeModel } from \"../../common/EntityTypes\"\nimport { ElementEntity } from \"../../common/EntityTypes\"\nimport { EntityUpdateData } from \"../../main/EventController\"\nimport { QueuedBatch } from \"../EventQueue.js\"\nimport { ENTITY_EVENT_BATCH_EXPIRE_MS } from \"../EventBusClient\"\nimport { CustomCacheHandlerMap } from \"./CustomCacheHandler.js\"\n\nassertWorkerOrNode()\n\n/**\n *\n * The minimum size of a range request when extending an existing range\n * Because we extend by making (potentially) many range requests until we reach the startId\n * We want to avoid that the requests are too small\n */\nexport const EXTEND_RANGE_MIN_CHUNK_SIZE = 40\nconst IGNORED_TYPES = [\n\tEntityEventBatchTypeRef,\n\tPermissionTypeRef,\n\tBucketPermissionTypeRef,\n\tSessionTypeRef,\n\tSecondFactorTypeRef,\n\tRecoverCodeTypeRef,\n\tRejectedSenderTypeRef,\n] as const\n\nexport interface EntityRestCache extends EntityRestInterface {\n\t/**\n\t * Clear out the contents of the cache.\n\t */\n\tpurgeStorage(): Promise<void>\n\n\t/**\n\t * Get the batch id of the most recently processed batch for the given group.\n\t */\n\tgetLastEntityEventBatchForGroup(groupId: Id): Promise<Id | null>\n\n\t/**\n\t * Saved tha batch id of the most recently processed batch manually.\n\t *\n\t * Is needed when the cache is new but we want to make sure that the next time we will download from this moment, even if we don't receive any events.\n\t */\n\tsetLastEntityEventBatchForGroup(groupId: Id, batchId: Id): Promise<void>\n\n\t/**\n\t * Persist the last time client downloaded event batches. This is not the last *processed* item, merely when things were *downloaded*. We use it to\n\t * detect out-of-sync.\n\t */\n\trecordSyncTime(): Promise<void>\n\n\t/**\n\t * Fetch the time since last time we downloaded event batches.\n\t */\n\ttimeSinceLastSyncMs(): Promise<number | null>\n\n\t/**\n\t * Detect if out of sync based on stored \"lastUpdateTime\" and the current server time\n\t */\n\tisOutOfSync(): Promise<boolean>\n}\n\nexport type Range = { lower: Id; upper: Id }\n\nexport type LastUpdateTime = { type: \"recorded\"; time: number } | { type: \"never\" } | { type: \"uninitialized\" }\n\n/**\n * Part of the cache storage only with subset of CacheStorage functionality\n *\n * Separate from the rest of the cache as a narrow interface to not expose the whole storage for cases where we want to only get the cached part of the list to\n * display it even if we can't load the full page from the server or need some metadata.\n *\n * also exposes functions to repair an outdated cache in case we can't access the server without getting a new version of a cached entity\n * (mainly password changes)\n */\nexport interface ExposedCacheStorage {\n\t/**\n\t * Load range of entities. Does not include {@param start}.\n\t * If {@param reverse} is false then returns entities newer than {@param start} in ascending order sorted by\n\t * elementId.\n\t * If {@param reverse} is true then returns entities older than {@param start} in descending order sorted by\n\t * elementId.\n\t */\n\tprovideFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]>\n\n\t/**\n\t * retrieve all list elements that are in the cache\n\t * @param typeRef\n\t * @param listId\n\t */\n\tgetWholeList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<T>>\n\n\tgetLastUpdateTime(): Promise<LastUpdateTime>\n\n\tclearExcludedData(): Promise<void>\n\n\t/**\n\t * remove an ElementEntity from the cache by typeRef and Id.\n\t * the exposed interface is intentionally more narrow than the internal cacheStorage because\n\t * we must maintain the integrity of our list ranges.\n\t * */\n\tdeleteIfExists<T extends ElementEntity>(typeRef: TypeRef<T>, listId: null, id: Id): Promise<void>\n}\n\nexport interface CacheStorage extends ExposedCacheStorage {\n\t/**\n\t * Get a given entity from the cache, expects that you have already checked for existence\n\t */\n\tget<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<T | null>\n\n\t/**\n\t * get a map with cache handlers for the customId types this storage implementation supports\n\t * customId types that don't have a custom handler don't get served from the cache\n\t */\n\tgetCustomCacheHandlerMap(entityRestClient: EntityRestClient): CustomCacheHandlerMap\n\n\tisElementIdInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<boolean>\n\n\tput(originalEntity: SomeEntity): Promise<void>\n\n\tgetRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Range | null>\n\n\tsetUpperRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void>\n\n\tsetLowerRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void>\n\n\t/**\n\t * Creates a new list cache if there is none. Resets everything but elements.\n\t * @param typeRef\n\t * @param listId\n\t * @param lower\n\t * @param upper\n\t */\n\tsetNewRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, lower: Id, upper: Id): Promise<void>\n\n\tgetIdsInRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<Id>>\n\n\t/**\n\t * Persist the last processed batch for a given group id.\n\t */\n\tputLastBatchIdForGroup(groupId: Id, batchId: Id): Promise<void>\n\n\t/**\n\t * Retrieve the least processed batch id for a given group.\n\t */\n\tgetLastBatchIdForGroup(groupId: Id): Promise<Id | null>\n\n\tdeleteIfExists<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<void>\n\n\tpurgeStorage(): Promise<void>\n\n\tputLastUpdateTime(value: number): Promise<void>\n\n\tgetUserId(): Id\n\n\tdeleteAllOwnedBy(owner: Id): Promise<void>\n\n\t/**\n\t * We want to lock the access to the \"ranges\" db when updating / reading the\n\t * offline available mail list ranges for each mail list (referenced using the listId)\n\t * @param listId the mail list that we want to lock\n\t */\n\tlockRangesDbAccess(listId: Id): Promise<void>\n\n\t/**\n\t * This is the counterpart to the function \"lockRangesDbAccess(listId)\"\n\t * @param listId the mail list that we want to unlock\n\t */\n\tunlockRangesDbAccess(listId: Id): Promise<void>\n}\n\n/**\n * This implementation provides a caching mechanism to the rest chain.\n * It forwards requests to the entity rest client.\n * The cache works as follows:\n * If a read from the target fails, the request fails.\n * If a read from the target is successful, the cache is written and the element returned.\n * For LETs the cache stores one range per list id. if a range is requested starting in the stored range or at the range ends the missing elements are loaded from the server.\n * Only ranges with elements with generated ids are stored in the cache. Custom id elements are only stored as single element currently. If needed this has to be extended for ranges.\n * Range requests starting outside the stored range are only allowed if the direction is away from the stored range. In this case we load from the range end to avoid gaps in the stored range.\n * Requests for creating or updating elements are always forwarded and not directly stored in the cache.\n * On EventBusClient notifications updated elements are stored in the cache if the element already exists in the cache.\n * On EventBusClient notifications new elements are only stored in the cache if they are LETs and in the stored range.\n * On EventBusClient notifications deleted elements are removed from the cache.\n *\n * Range handling:\n * |          <|>        c d e f g h i j k      <|>             |\n * MIN_ID  lowerRangeId     ids in range    upperRangeId    MAX_ID\n * lowerRangeId may be anything from MIN_ID to c, upperRangeId may be anything from k to MAX_ID\n */\nexport class DefaultEntityRestCache implements EntityRestCache {\n\tconstructor(readonly entityRestClient: EntityRestClient, private readonly storage: CacheStorage) {}\n\n\tasync load<T extends SomeEntity>(typeRef: TypeRef<T>, id: PropertyType<T, \"_id\">, queryParameters?: Dict, extraHeaders?: Dict): Promise<T> {\n\t\tconst { listId, elementId } = expandId(id)\n\n\t\tconst cachedEntity = await this.storage.get(typeRef, listId, elementId)\n\t\tif (\n\t\t\tqueryParameters?.version != null || //if a specific version is requested we have to load again\n\t\t\tcachedEntity == null\n\t\t) {\n\t\t\tconst entity = await this.entityRestClient.load(typeRef, id, queryParameters, extraHeaders)\n\t\t\tif (queryParameters?.version == null && !isIgnoredType(typeRef)) {\n\t\t\t\tawait this.storage.put(entity)\n\t\t\t}\n\t\t\treturn entity\n\t\t}\n\t\treturn cachedEntity\n\t}\n\n\tloadMultiple<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementIds: Array<Id>): Promise<Array<T>> {\n\t\tif (isIgnoredType(typeRef)) {\n\t\t\treturn this.entityRestClient.loadMultiple(typeRef, listId, elementIds)\n\t\t}\n\n\t\treturn this._loadMultiple(typeRef, listId, elementIds)\n\t}\n\n\tsetup<T extends SomeEntity>(listId: Id | null, instance: T, extraHeaders?: Dict, options?: EntityRestClientSetupOptions): Promise<Id> {\n\t\treturn this.entityRestClient.setup(listId, instance, extraHeaders, options)\n\t}\n\n\tsetupMultiple<T extends SomeEntity>(listId: Id | null, instances: Array<T>): Promise<Array<Id>> {\n\t\treturn this.entityRestClient.setupMultiple(listId, instances)\n\t}\n\n\tupdate<T extends SomeEntity>(instance: T): Promise<void> {\n\t\treturn this.entityRestClient.update(instance)\n\t}\n\n\terase<T extends SomeEntity>(instance: T): Promise<void> {\n\t\treturn this.entityRestClient.erase(instance)\n\t}\n\n\tgetLastEntityEventBatchForGroup(groupId: Id): Promise<Id | null> {\n\t\treturn this.storage.getLastBatchIdForGroup(groupId)\n\t}\n\n\tsetLastEntityEventBatchForGroup(groupId: Id, batchId: Id): Promise<void> {\n\t\treturn this.storage.putLastBatchIdForGroup(groupId, batchId)\n\t}\n\n\tpurgeStorage(): Promise<void> {\n\t\tconsole.log(\"Purging the user's offline database\")\n\t\treturn this.storage.purgeStorage()\n\t}\n\n\tasync isOutOfSync(): Promise<boolean> {\n\t\tconst timeSinceLastSync = await this.timeSinceLastSyncMs()\n\t\treturn timeSinceLastSync != null && timeSinceLastSync > ENTITY_EVENT_BATCH_EXPIRE_MS\n\t}\n\n\tasync recordSyncTime(): Promise<void> {\n\t\tconst timestamp = this.getServerTimestampMs()\n\t\tawait this.storage.putLastUpdateTime(timestamp)\n\t}\n\n\tasync timeSinceLastSyncMs(): Promise<number | null> {\n\t\tconst lastUpdate = await this.storage.getLastUpdateTime()\n\t\tlet lastUpdateTime: number\n\t\tswitch (lastUpdate.type) {\n\t\t\tcase \"recorded\":\n\t\t\t\tlastUpdateTime = lastUpdate.time\n\t\t\t\tbreak\n\t\t\tcase \"never\":\n\t\t\t\treturn null\n\t\t\tcase \"uninitialized\":\n\t\t\t\tthrow new ProgrammingError(\"Offline storage is not initialized\")\n\t\t}\n\t\tconst now = this.getServerTimestampMs()\n\t\treturn now - lastUpdateTime\n\t}\n\n\tprivate getServerTimestampMs(): number {\n\t\treturn this.entityRestClient.getRestClient().getServerTimestampMs()\n\t}\n\n\t/**\n\t * Delete a cached entity. Sometimes this is necessary to do to ensure you always load the new version\n\t */\n\tdeleteFromCacheIfExists<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementId: Id): Promise<void> {\n\t\treturn this.storage.deleteIfExists(typeRef, listId, elementId)\n\t}\n\n\tprivate async _loadMultiple<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, ids: Array<Id>): Promise<Array<T>> {\n\t\tconst entitiesInCache: T[] = []\n\t\tconst idsToLoad: Id[] = []\n\t\tfor (let id of ids) {\n\t\t\tconst items = await this.storage.get(typeRef, listId, id)\n\t\t\tif (items != null) {\n\t\t\t\tentitiesInCache.push(items)\n\t\t\t} else {\n\t\t\t\tidsToLoad.push(id)\n\t\t\t}\n\t\t}\n\t\tconst entitiesFromServer: T[] = []\n\t\tif (idsToLoad.length > 0) {\n\t\t\tconst entities = await this.entityRestClient.loadMultiple(typeRef, listId, idsToLoad)\n\t\t\tfor (let entity of entities) {\n\t\t\t\tawait this.storage.put(entity)\n\t\t\t\tentitiesFromServer.push(entity)\n\t\t\t}\n\t\t}\n\n\t\treturn entitiesFromServer.concat(entitiesInCache)\n\t}\n\n\tasync loadRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\tif (this.storage.getCustomCacheHandlerMap(this.entityRestClient).has(typeRef)) {\n\t\t\treturn await this.storage.getCustomCacheHandlerMap(this.entityRestClient).get(typeRef)!.loadRange(this.storage, listId, start, count, reverse)\n\t\t}\n\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\tif (!isCachedType(typeModel, typeRef)) {\n\t\t\treturn this.entityRestClient.loadRange(typeRef, listId, start, count, reverse)\n\t\t}\n\n\t\t// We lock access to the \"ranges\" db here in order to prevent race conditions when accessing the ranges database.\n\t\tawait this.storage.lockRangesDbAccess(listId)\n\n\t\ttry {\n\t\t\tconst range = await this.storage.getRangeForList(typeRef, listId)\n\n\t\t\tif (range == null) {\n\t\t\t\tawait this.populateNewListWithRange(typeRef, listId, start, count, reverse)\n\t\t\t} else if (isStartIdWithinRange(range, start)) {\n\t\t\t\tawait this.extendFromWithinRange(typeRef, listId, start, count, reverse)\n\t\t\t} else if (isRangeRequestAwayFromExistingRange(range, reverse, start)) {\n\t\t\t\tawait this.extendAwayFromRange(typeRef, listId, start, count, reverse)\n\t\t\t} else {\n\t\t\t\tawait this.extendTowardsRange(typeRef, listId, start, count, reverse)\n\t\t\t}\n\n\t\t\treturn this.storage.provideFromRange(typeRef, listId, start, count, reverse)\n\t\t} finally {\n\t\t\t// We unlock access to the \"ranges\" db here. We lock it in order to prevent race conditions when accessing the \"ranges\" database.\n\t\t\tawait this.storage.unlockRangesDbAccess(listId)\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new list range, reading everything from the server that it can\n\t * range:         (none)\n\t * request:       *--------->\n\t * range becomes: |---------|\n\t * @private\n\t */\n\tprivate async populateNewListWithRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean) {\n\t\t// Create a new range and load everything\n\t\tconst entities = await this.entityRestClient.loadRange(typeRef, listId, start, count, reverse)\n\n\t\t// Initialize a new range for this list\n\t\tawait this.storage.setNewRangeForList(typeRef, listId, start, start)\n\n\t\t// The range bounds will be updated in here\n\t\tawait this.updateRangeInStorage(typeRef, listId, count, reverse, entities)\n\t}\n\n\t/**\n\t * Returns part of a request from the cache, and the remainder is loaded from the server\n\t * range:          |---------|\n\t * request:             *-------------->\n\t * range becomes: |--------------------|\n\t */\n\tprivate async extendFromWithinRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean) {\n\t\tconst { newStart, newCount } = await this.recalculateRangeRequest(typeRef, listId, start, count, reverse)\n\t\tif (newCount > 0) {\n\t\t\t// We will be able to provide some entities from the cache, so we just want to load the remaining entities from the server\n\t\t\tconst entities = await this.entityRestClient.loadRange(typeRef, listId, newStart, newCount, reverse)\n\t\t\tawait this.updateRangeInStorage(typeRef, listId, newCount, reverse, entities)\n\t\t}\n\t}\n\n\t/**\n\t * Start was outside the range, and we are loading away from the range\n\t * Keeps loading elements from the end of the range in the direction of the startId.\n\t * Returns once all available elements have been loaded or the requested number is in cache\n\t * range:          |---------|\n\t * request:                     *------->\n\t * range becomes:  |--------------------|\n\t */\n\tprivate async extendAwayFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean) {\n\t\t// Start is outside the range, and we are loading away from the range, so we grow until we are able to provide enough\n\t\t// entities starting at startId\n\t\twhile (true) {\n\t\t\tconst range = assertNotNull(await this.storage.getRangeForList(typeRef, listId))\n\n\t\t\t// Which end of the range to start loading from\n\t\t\tconst loadStartId = reverse ? range.lower : range.upper\n\n\t\t\tconst requestCount = Math.max(count, EXTEND_RANGE_MIN_CHUNK_SIZE)\n\n\t\t\t// Load some entities\n\t\t\tconst entities = await this.entityRestClient.loadRange(typeRef, listId, loadStartId, requestCount, reverse)\n\n\t\t\tawait this.updateRangeInStorage(typeRef, listId, requestCount, reverse, entities)\n\n\t\t\t// If we exhausted the entities from the server\n\t\t\tif (entities.length < requestCount) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Try to get enough entities from cache\n\t\t\tconst entitiesFromCache = await this.storage.provideFromRange(typeRef, listId, start, count, reverse)\n\n\t\t\t// If cache is now capable of providing the whole request\n\t\t\tif (entitiesFromCache.length === count) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Loads all elements from the startId in the direction of the range\n\t * Once complete, returns as many elements as it can from the original request\n\t * range:         |---------|\n\t * request:                     <------*\n\t * range becomes: |--------------------|\n\t * or\n\t * range:              |---------|\n\t * request:       <-------------------*\n\t * range becomes: |--------------------|\n\t */\n\tprivate async extendTowardsRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean) {\n\t\twhile (true) {\n\t\t\tconst range = assertNotNull(await this.storage.getRangeForList(typeRef, listId))\n\n\t\t\tconst loadStartId = reverse ? range.upper : range.lower\n\n\t\t\tconst requestCount = Math.max(count, EXTEND_RANGE_MIN_CHUNK_SIZE)\n\n\t\t\tconst entities = await this.entityRestClient.loadRange(typeRef, listId, loadStartId, requestCount, !reverse)\n\n\t\t\tawait this.updateRangeInStorage(typeRef, listId, requestCount, !reverse, entities)\n\n\t\t\t// The call to `updateRangeInStorage` will have set the range bounds to GENERATED_MIN_ID/GENERATED_MAX_ID\n\t\t\t// in the case that we have exhausted all elements from the server, so if that happens, we will also end up breaking here\n\t\t\tif (await this.storage.isElementIdInCacheRange(typeRef, listId, start)) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tawait this.extendFromWithinRange(typeRef, listId, start, count, reverse)\n\t}\n\n\t/**\n\t * Given the parameters and result of a range request,\n\t * Inserts the result into storage, and updates the range bounds\n\t * based on number of entities requested and the actual amount that were received\n\t */\n\tprivate async updateRangeInStorage<T extends ListElementEntity>(\n\t\ttypeRef: TypeRef<T>,\n\t\tlistId: Id,\n\t\tcountRequested: number,\n\t\twasReverseRequest: boolean,\n\t\treceivedEntities: T[],\n\t) {\n\t\tlet elementsToAdd = receivedEntities\n\t\tif (wasReverseRequest) {\n\t\t\t// Ensure that elements are cached in ascending (not reverse) order\n\t\t\telementsToAdd = receivedEntities.reverse()\n\t\t\tif (receivedEntities.length < countRequested) {\n\t\t\t\tawait this.storage.setLowerRangeForList(typeRef, listId, GENERATED_MIN_ID)\n\t\t\t} else {\n\t\t\t\t// After reversing the list the first element in the list is the lower range limit\n\t\t\t\tawait this.storage.setLowerRangeForList(typeRef, listId, getElementId(getFirstOrThrow(receivedEntities)))\n\t\t\t}\n\t\t} else {\n\t\t\t// Last element in the list is the upper range limit\n\t\t\tif (receivedEntities.length < countRequested) {\n\t\t\t\t// all elements have been loaded, so the upper range must be set to MAX_ID\n\t\t\t\tawait this.storage.setUpperRangeForList(typeRef, listId, GENERATED_MAX_ID)\n\t\t\t} else {\n\t\t\t\tawait this.storage.setUpperRangeForList(typeRef, listId, getElementId(lastThrow(receivedEntities)))\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all(elementsToAdd.map((element) => this.storage.put(element)))\n\t}\n\n\t/**\n\t * Calculates the new start value for the getElementRange request and the number of elements to read in\n\t * order to read no duplicate values.\n\t * @return returns the new start and count value.\n\t */\n\tprivate async recalculateRangeRequest<T extends ListElementEntity>(\n\t\ttypeRef: TypeRef<T>,\n\t\tlistId: Id,\n\t\tstart: Id,\n\t\tcount: number,\n\t\treverse: boolean,\n\t): Promise<{ newStart: string; newCount: number }> {\n\t\tlet allRangeList = await this.storage.getIdsInRange(typeRef, listId)\n\t\tlet elementsToRead = count\n\t\tlet startElementId = start\n\t\tconst range = await this.storage.getRangeForList(typeRef, listId)\n\t\tif (range == null) {\n\t\t\treturn { newStart: start, newCount: count }\n\t\t}\n\t\tconst { lower, upper } = range\n\t\tlet indexOfStart = allRangeList.indexOf(start)\n\t\tif ((!reverse && upper === GENERATED_MAX_ID) || (reverse && lower === GENERATED_MIN_ID)) {\n\t\t\t// we have already loaded the complete range in the desired direction, so we do not have to load from server\n\t\t\telementsToRead = 0\n\t\t} else if (allRangeList.length === 0) {\n\t\t\t// Element range is empty, so read all elements\n\t\t\telementsToRead = count\n\t\t} else if (indexOfStart !== -1) {\n\t\t\t// Start element is located in allRange read only elements that are not in allRange.\n\t\t\tif (reverse) {\n\t\t\t\telementsToRead = count - indexOfStart\n\t\t\t\tstartElementId = allRangeList[0] // use the lowest id in allRange as start element\n\t\t\t} else {\n\t\t\t\telementsToRead = count - (allRangeList.length - 1 - indexOfStart)\n\t\t\t\tstartElementId = allRangeList[allRangeList.length - 1] // use the  highest id in allRange as start element\n\t\t\t}\n\t\t} else if (lower === start || (firstBiggerThanSecond(start, lower) && firstBiggerThanSecond(allRangeList[0], start))) {\n\t\t\t// Start element is not in allRange but has been used has start element for a range request, eg. EntityRestInterface.GENERATED_MIN_ID, or start is between lower range id and lowest element in range\n\t\t\tif (!reverse) {\n\t\t\t\t// if not reverse read only elements that are not in allRange\n\t\t\t\tstartElementId = allRangeList[allRangeList.length - 1] // use the  highest id in allRange as start element\n\t\t\t\telementsToRead = count - allRangeList.length\n\t\t\t}\n\t\t\t// if reverse read all elements\n\t\t} else if (upper === start || (firstBiggerThanSecond(start, allRangeList[allRangeList.length - 1]) && firstBiggerThanSecond(upper, start))) {\n\t\t\t// Start element is not in allRange but has been used has start element for a range request, eg. EntityRestInterface.GENERATED_MAX_ID, or start is between upper range id and highest element in range\n\t\t\tif (reverse) {\n\t\t\t\t// if not reverse read only elements that are not in allRange\n\t\t\t\tstartElementId = allRangeList[0] // use the  highest id in allRange as start element\n\t\t\t\telementsToRead = count - allRangeList.length\n\t\t\t}\n\t\t\t// if not reverse read all elements\n\t\t}\n\t\treturn { newStart: startElementId, newCount: elementsToRead }\n\t}\n\n\t/**\n\t * Resolves when the entity is loaded from the server if necessary\n\t * @pre The last call of this function must be resolved. This is needed to avoid that e.g. while\n\t * loading a created instance from the server we receive an update of that instance and ignore it because the instance is not in the cache yet.\n\t *\n\t * @return Promise, which resolves to the array of valid events (if response is NotFound or NotAuthorized we filter it out)\n\t */\n\tasync entityEventsReceived(batch: QueuedBatch): Promise<Array<EntityUpdate>> {\n\t\tawait this.recordSyncTime()\n\n\t\t// we handle post multiple create operations separately to optimize the number of requests with getMultiple\n\t\tconst createUpdatesForLETs: EntityUpdate[] = []\n\t\tconst regularUpdates: EntityUpdate[] = [] // all updates not resulting from post multiple requests\n\t\tconst updatesArray = batch.events\n\t\tfor (const update of updatesArray) {\n\t\t\tif (update.application !== \"monitor\") {\n\t\t\t\t// monitor application is ignored\n\t\t\t\t// mails are ignored because move operations are handled as a special event (and no post multiple is possible)\n\t\t\t\tif (\n\t\t\t\t\tupdate.operation === OperationType.CREATE &&\n\t\t\t\t\tgetUpdateInstanceId(update).instanceListId != null &&\n\t\t\t\t\t!isSameTypeRef(new TypeRef(update.application, update.type), MailTypeRef)\n\t\t\t\t) {\n\t\t\t\t\tcreateUpdatesForLETs.push(update)\n\t\t\t\t} else {\n\t\t\t\t\tregularUpdates.push(update)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst createUpdatesForLETsPerList = groupBy(createUpdatesForLETs, (update) => update.instanceListId)\n\n\t\tconst postMultipleEventUpdates: EntityUpdate[][] = []\n\t\t// we first handle potential post multiple updates in get multiple requests\n\t\tfor (let [instanceListId, updates] of createUpdatesForLETsPerList) {\n\t\t\tconst firstUpdate = updates[0]\n\t\t\tconst typeRef = new TypeRef<ListElementEntity>(firstUpdate.application, firstUpdate.type)\n\t\t\tconst ids = updates.map((update) => update.instanceId)\n\n\t\t\t// We only want to load the instances that are in cache range\n\t\t\tconst customHandlers = this.storage.getCustomCacheHandlerMap(this.entityRestClient)\n\t\t\tconst idsInCacheRange = customHandlers.has(typeRef)\n\t\t\t\t? await customHandlers.get(typeRef)!.getElementIdsInCacheRange(this.storage, instanceListId, ids)\n\t\t\t\t: await this.getElementIdsInCacheRange(typeRef, instanceListId, ids)\n\n\t\t\tif (idsInCacheRange.length === 0) {\n\t\t\t\tpostMultipleEventUpdates.push(updates)\n\t\t\t} else {\n\t\t\t\tconst updatesNotInCacheRange =\n\t\t\t\t\tidsInCacheRange.length === updates.length ? [] : updates.filter((update) => !idsInCacheRange.includes(update.instanceId))\n\n\t\t\t\ttry {\n\t\t\t\t\t// loadMultiple is only called to cache the elements and check which ones return errors\n\t\t\t\t\tconst returnedInstances = await this._loadMultiple(typeRef, instanceListId, idsInCacheRange)\n\t\t\t\t\t//We do not want to pass updates that caused an error\n\t\t\t\t\tif (returnedInstances.length !== idsInCacheRange.length) {\n\t\t\t\t\t\tconst returnedIds = returnedInstances.map((instance) => getElementId(instance))\n\t\t\t\t\t\tpostMultipleEventUpdates.push(updates.filter((update) => returnedIds.includes(update.instanceId)).concat(updatesNotInCacheRange))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpostMultipleEventUpdates.push(updates)\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (e instanceof NotAuthorizedError) {\n\t\t\t\t\t\t// return updates that are not in cache Range if NotAuthorizedError (for those updates that are in cache range)\n\t\t\t\t\t\tpostMultipleEventUpdates.push(updatesNotInCacheRange)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst otherEventUpdates: EntityUpdate[] = []\n\t\tfor (let update of regularUpdates) {\n\t\t\tconst { operation, type, application } = update\n\t\t\tconst { instanceListId, instanceId } = getUpdateInstanceId(update)\n\t\t\tconst typeRef = new TypeRef<SomeEntity>(application, type)\n\n\t\t\tswitch (operation) {\n\t\t\t\tcase OperationType.UPDATE: {\n\t\t\t\t\tconst handledUpdate = await this.processUpdateEvent(typeRef, update)\n\t\t\t\t\tif (handledUpdate) {\n\t\t\t\t\t\totherEventUpdates.push(handledUpdate)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcase OperationType.DELETE: {\n\t\t\t\t\tif (\n\t\t\t\t\t\tisSameTypeRef(MailTypeRef, typeRef) &&\n\t\t\t\t\t\tcontainsEventOfType(updatesArray as Readonly<EntityUpdateData[]>, OperationType.CREATE, instanceId)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// move for mail is handled in create event.\n\t\t\t\t\t} else if (isSameTypeRef(MailTypeRef, typeRef)) {\n\t\t\t\t\t\t// delete mailDetails if they are available (as we don't send an event for this type)\n\t\t\t\t\t\tconst mail = await this.storage.get(MailTypeRef, instanceListId, instanceId)\n\t\t\t\t\t\tawait this.storage.deleteIfExists(typeRef, instanceListId, instanceId)\n\t\t\t\t\t\tif (mail?.mailDetails != null) {\n\t\t\t\t\t\t\tawait this.storage.deleteIfExists(MailDetailsBlobTypeRef, mail.mailDetails[0], mail.mailDetails[1])\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.storage.deleteIfExists(typeRef, instanceListId, instanceId)\n\t\t\t\t\t}\n\t\t\t\t\totherEventUpdates.push(update)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcase OperationType.CREATE: {\n\t\t\t\t\tconst handledUpdate = await this.processCreateEvent(typeRef, update, updatesArray)\n\t\t\t\t\tif (handledUpdate) {\n\t\t\t\t\t\totherEventUpdates.push(handledUpdate)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new ProgrammingError(\"Unknown operation type: \" + operation)\n\t\t\t}\n\t\t}\n\t\t// the whole batch has been written successfully\n\t\tawait this.storage.putLastBatchIdForGroup(batch.groupId, batch.batchId)\n\t\t// merge the results\n\t\treturn otherEventUpdates.concat(flat(postMultipleEventUpdates))\n\t}\n\n\t/** Returns {null} when the update should be skipped. */\n\tprivate async processCreateEvent(typeRef: TypeRef<any>, update: EntityUpdate, batch: ReadonlyArray<EntityUpdate>): Promise<EntityUpdate | null> {\n\t\t// do not return undefined to avoid implicit returns\n\t\tconst { instanceId, instanceListId } = getUpdateInstanceId(update)\n\n\t\t// We put new instances into cache only when it's a new instance in the cached range which is only for the list instances.\n\t\tif (instanceListId != null) {\n\t\t\tconst deleteEvent = getEventOfType(batch, OperationType.DELETE, instanceId)\n\n\t\t\tconst element = deleteEvent && isSameTypeRef(MailTypeRef, typeRef) ? await this.storage.get(typeRef, deleteEvent.instanceListId, instanceId) : null\n\t\t\tif (deleteEvent != null && element != null) {\n\t\t\t\t// It is a move event for cached mail\n\t\t\t\tawait this.storage.deleteIfExists(typeRef, deleteEvent.instanceListId, instanceId)\n\t\t\t\telement._id = [instanceListId, instanceId]\n\t\t\t\tawait this.storage.put(element)\n\t\t\t\treturn update\n\t\t\t} else if (await this.storage.isElementIdInCacheRange(typeRef, instanceListId, instanceId)) {\n\t\t\t\t// No need to try to download something that's not there anymore\n\t\t\t\t// We do not consult custom handlers here because they are only needed for list elements.\n\t\t\t\treturn this.entityRestClient\n\t\t\t\t\t.load(typeRef, [instanceListId, instanceId])\n\t\t\t\t\t.then((entity) => this.storage.put(entity))\n\t\t\t\t\t.then(() => update)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tif (isExpectedErrorForSynchronization(e)) {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow e\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t} else {\n\t\t\t\treturn update\n\t\t\t}\n\t\t} else {\n\t\t\treturn update\n\t\t}\n\t}\n\n\t/** Returns {null} when the update should be skipped. */\n\tprivate async processUpdateEvent(typeRef: TypeRef<SomeEntity>, update: EntityUpdate): Promise<EntityUpdate | null> {\n\t\tconst { instanceId, instanceListId } = getUpdateInstanceId(update)\n\t\tconst cached = await this.storage.get(typeRef, instanceListId, instanceId)\n\t\t// No need to try to download something that's not there anymore\n\t\tif (cached != null) {\n\t\t\ttry {\n\t\t\t\t// in case this is an update for the user instance: if the password changed we'll be logged out at this point\n\t\t\t\t// if we don't catch the expected NotAuthenticated Error that results from trying to load anything with\n\t\t\t\t// the old user.\n\t\t\t\t// Letting the NotAuthenticatedError propagate to the main thread instead of trying to handle it ourselves\n\t\t\t\t// or throwing out the update drops us onto the login page and into the session recovery flow if the user\n\t\t\t\t// clicks their saved credentials again, but lets them still use offline login if they try to use the\n\t\t\t\t// outdated credentials while not connected to the internet.\n\t\t\t\tconst newEntity = await this.entityRestClient.load(typeRef, collapseId(instanceListId, instanceId))\n\t\t\t\tif (isSameTypeRef(typeRef, UserTypeRef)) {\n\t\t\t\t\tawait this.handleUpdatedUser(cached, newEntity)\n\t\t\t\t}\n\t\t\t\tawait this.storage.put(newEntity)\n\t\t\t\treturn update\n\t\t\t} catch (e) {\n\t\t\t\t// If the entity is not there anymore we should evict it from the cache and not keep the outdated/nonexisting instance around.\n\t\t\t\t// Even for list elements this should be safe as the instance is not there anymore and is definitely not in this version\n\t\t\t\tif (isExpectedErrorForSynchronization(e)) {\n\t\t\t\t\tconsole.log(`Instance not found when processing update for ${JSON.stringify(update)}, deleting from the cache.`)\n\t\t\t\t\tawait this.storage.deleteIfExists(typeRef, instanceListId, instanceId)\n\t\t\t\t\treturn null\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn update\n\t}\n\n\tprivate async handleUpdatedUser(cached: SomeEntity, newEntity: SomeEntity) {\n\t\t// When we are removed from a group we just get an update for our user\n\t\t// with no membership on it. We need to clean up all the entities that\n\t\t// belong to that group since we shouldn't be able to access them anymore\n\t\t// and we won't get any update or another chance to clean them up.\n\t\tconst oldUser = cached as User\n\t\tif (oldUser._id !== this.storage.getUserId()) {\n\t\t\treturn\n\t\t}\n\t\tconst newUser = newEntity as User\n\t\tconst removedShips = difference(oldUser.memberships, newUser.memberships, (l, r) => l._id === r._id)\n\t\tfor (const ship of removedShips) {\n\t\t\tconsole.log(\"Lost membership on \", ship._id, ship.groupType)\n\t\t\tawait this.storage.deleteAllOwnedBy(ship.group)\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @returns {Array<Id>} the ids that are in cache range and therefore should be cached\n\t */\n\tprivate async getElementIdsInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, ids: Id[]): Promise<Id[]> {\n\t\tconst ret: Id[] = []\n\t\tfor (let i = 0; i < ids.length; i++) {\n\t\t\tif (await this.storage.isElementIdInCacheRange(typeRef, listId, ids[i])) {\n\t\t\t\tret.push(ids[i])\n\t\t\t}\n\t\t}\n\t\treturn ret\n\t}\n}\n\n/**\n * Returns whether the error is expected for the cases where our local state might not be up-to-date with the server yet. E.g. we might be processing an update\n * for the instance that was already deleted. Normally this would be optimized away but it might still happen due to timing.\n */\nfunction isExpectedErrorForSynchronization(e: Error): boolean {\n\treturn e instanceof NotFoundError || e instanceof NotAuthorizedError\n}\n\nexport function expandId(id: Id | IdTuple): { listId: Id | null; elementId: Id } {\n\tif (typeof id === \"string\") {\n\t\treturn {\n\t\t\tlistId: null,\n\t\t\telementId: id,\n\t\t}\n\t} else {\n\t\tconst [listId, elementId] = id\n\t\treturn {\n\t\t\tlistId,\n\t\t\telementId,\n\t\t}\n\t}\n}\n\nexport function collapseId(listId: Id | null, elementId: Id): Id | IdTuple {\n\tif (listId != null) {\n\t\treturn [listId, elementId]\n\t} else {\n\t\treturn elementId\n\t}\n}\n\nexport function getUpdateInstanceId(update: EntityUpdate): { instanceListId: Id | null; instanceId: Id } {\n\tlet instanceListId\n\tif (update.instanceListId === \"\") {\n\t\tinstanceListId = null\n\t} else {\n\t\tinstanceListId = update.instanceListId\n\t}\n\treturn { instanceListId, instanceId: update.instanceId }\n}\n\n/**\n * Check if a range request begins inside of an existing range\n */\nfunction isStartIdWithinRange(range: Range, startId: Id): boolean {\n\treturn !firstBiggerThanSecond(startId, range.upper) && !firstBiggerThanSecond(range.lower, startId)\n}\n\n/**\n * Check if a range request is going away from an existing range\n * Assumes that the range request doesn't start inside the range\n */\nfunction isRangeRequestAwayFromExistingRange(range: Range, reverse: boolean, start: string) {\n\treturn reverse ? firstBiggerThanSecond(range.lower, start) : firstBiggerThanSecond(start, range.upper)\n}\n\n/**\n * some types are completely ignored by the cache and always served from a request.\n * Note:\n * isCachedType(ref) ---> !isIgnoredType(ref) but\n * isIgnoredType(ref) -/-> !isCachedType(ref) because of opted-in CustomId types.\n */\nfunction isIgnoredType(typeRef: TypeRef<unknown>): boolean {\n\treturn typeRef.app === \"monitor\" || IGNORED_TYPES.some((ref) => isSameTypeRef(typeRef, ref))\n}\n\n/**\n * customId types are normally not cached, but some are opted in.\n * Note:\n * isCachedType(ref) ---> !isIgnoredType(ref) but\n * isIgnoredType(ref) -/-> !isCachedType(ref)\n */\nfunction isCachedType(typeModel: TypeModel, typeRef: TypeRef<unknown>): boolean {\n\treturn !isIgnoredType(typeRef) && typeModel.values._id.type === ValueType.GeneratedId\n}\n","import type { RestClient } from \"./RestClient\"\nimport type { CryptoFacade } from \"../crypto/CryptoFacade\"\nimport { _verifyType, HttpMethod, MediaType, resolveTypeReference } from \"../../common/EntityFunctions\"\nimport { SessionKeyNotFoundError } from \"../../common/error/SessionKeyNotFoundError\"\nimport type { EntityUpdate } from \"../../entities/sys/TypeRefs.js\"\nimport { PushIdentifierTypeRef } from \"../../entities/sys/TypeRefs.js\"\nimport {\n\tConnectionError,\n\tInternalServerError,\n\tNotAuthenticatedError,\n\tNotAuthorizedError,\n\tNotFoundError,\n\tPayloadTooLargeError,\n} from \"../../common/error/RestError\"\nimport type { lazy } from \"@tutao/tutanota-utils\"\nimport { flat, isSameTypeRef, Mapper, ofClass, promiseMap, splitInChunks, TypeRef } from \"@tutao/tutanota-utils\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport type { ListElementEntity, SomeEntity, TypeModel } from \"../../common/EntityTypes\"\nimport { LOAD_MULTIPLE_LIMIT, POST_MULTIPLE_LIMIT } from \"../../common/utils/EntityUtils\"\nimport { Type } from \"../../common/EntityConstants\"\nimport { SetupMultipleError } from \"../../common/error/SetupMultipleError\"\nimport { expandId } from \"./DefaultEntityRestCache.js\"\nimport { InstanceMapper } from \"../crypto/InstanceMapper\"\nimport { QueuedBatch } from \"../EventQueue.js\"\nimport { AuthDataProvider } from \"../facades/UserFacade\"\nimport { LoginIncompleteError } from \"../../common/error/LoginIncompleteError.js\"\nimport { BlobServerUrl } from \"../../entities/storage/TypeRefs.js\"\nimport { BlobAccessTokenFacade } from \"../facades/BlobAccessTokenFacade.js\"\nimport { isOfflineError } from \"../../common/utils/ErrorCheckUtils.js\"\n\nassertWorkerOrNode()\n\nexport function typeRefToPath(typeRef: TypeRef<any>): string {\n\treturn `/rest/${typeRef.app}/${typeRef.type.toLowerCase()}`\n}\n\nexport interface EntityRestClientSetupOptions {\n\tbaseUrl?: string\n\t/** Use this key to encrypt session key instead of trying to resolve the owner key based on the ownerGroup. */\n\townerKey?: Aes128Key\n}\n\n/**\n * The EntityRestInterface provides a convenient interface for invoking server side REST services.\n */\nexport interface EntityRestInterface {\n\t/**\n\t * Reads a single element from the server (or cache). Entities are decrypted before they are returned.\n\t * @param ownerKey Use this key to decrypt session key instead of trying to resolve the owner key based on the ownerGroup.\n\t */\n\tload<T extends SomeEntity>(typeRef: TypeRef<T>, id: PropertyType<T, \"_id\">, queryParameters?: Dict, extraHeaders?: Dict, ownerKey?: Aes128Key): Promise<T>\n\n\t/**\n\t * Reads a range of elements from the server (or cache). Entities are decrypted before they are returned.\n\t */\n\tloadRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]>\n\n\t/**\n\t * Reads multiple elements from the server (or cache). Entities are decrypted before they are returned.\n\t */\n\tloadMultiple<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementIds: Array<Id>): Promise<Array<T>>\n\n\t/**\n\t * Creates a single element on the server. Entities are encrypted before they are sent.\n\t */\n\tsetup<T extends SomeEntity>(listId: Id | null, instance: T, extraHeaders?: Dict, options?: EntityRestClientSetupOptions): Promise<Id>\n\n\t/**\n\t * Creates multiple elements on the server. Entities are encrypted before they are sent.\n\t */\n\tsetupMultiple<T extends SomeEntity>(listId: Id | null, instances: Array<T>): Promise<Array<Id>>\n\n\t/**\n\t * Modifies a single element on the server. Entities are encrypted before they are sent.\n\t * @param ownerKey Use this key to decrypt session key instead of trying to resolve the owner key based on the ownerGroup.\n\t */\n\tupdate<T extends SomeEntity>(instance: T, ownerKey?: Aes128Key): Promise<void>\n\n\t/**\n\t * Deletes a single element on the server.\n\t */\n\terase<T extends SomeEntity>(instance: T): Promise<void>\n\n\t/**\n\t * Must be called when entity events are received.\n\t * @param batch The entity events that were received.\n\t * @return Similar to the events in the data parameter, but reduced by the events which are obsolete.\n\t */\n\tentityEventsReceived(batch: QueuedBatch): Promise<Array<EntityUpdate>>\n}\n\n/**\n * Retrieves the instances from the backend (db) and converts them to entities.\n *\n * Part of this process is\n * * the decryption for the returned instances (GET) and the encryption of all instances before they are sent (POST, PUT)\n * * the injection of aggregate instances for the returned instances (GET)\n * * caching for retrieved instances (GET)\n *\n */\nexport class EntityRestClient implements EntityRestInterface {\n\tget _crypto(): CryptoFacade {\n\t\treturn this.lazyCrypto()\n\t}\n\n\tconstructor(\n\t\tprivate readonly authDataProvider: AuthDataProvider,\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly lazyCrypto: lazy<CryptoFacade>,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly blobAccessTokenFacade: BlobAccessTokenFacade,\n\t) {}\n\n\tasync load<T extends SomeEntity>(\n\t\ttypeRef: TypeRef<T>,\n\t\tid: PropertyType<T, \"_id\">,\n\t\tqueryParameters?: Dict,\n\t\textraHeaders?: Dict,\n\t\townerKey?: Aes128Key,\n\t): Promise<T> {\n\t\tconst { listId, elementId } = expandId(id)\n\t\tconst { path, queryParams, headers, typeModel } = await this._validateAndPrepareRestRequest(\n\t\t\ttypeRef,\n\t\t\tlistId,\n\t\t\telementId,\n\t\t\tqueryParameters,\n\t\t\textraHeaders,\n\t\t\townerKey,\n\t\t)\n\t\tconst json = await this.restClient.request(path, HttpMethod.GET, {\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t\tresponseType: MediaType.Json,\n\t\t})\n\t\tconst entity = JSON.parse(json)\n\t\tconst migratedEntity = await this._crypto.applyMigrations(typeRef, entity)\n\t\tconst sessionKey = ownerKey\n\t\t\t? this._crypto.resolveSessionKeyWithOwnerKey(migratedEntity, ownerKey)\n\t\t\t: await this._crypto.resolveSessionKey(typeModel, migratedEntity).catch(\n\t\t\t\t\tofClass(SessionKeyNotFoundError, (e) => {\n\t\t\t\t\t\tconsole.log(\"could not resolve session key\", e)\n\t\t\t\t\t\treturn null // will result in _errors being set on the instance\n\t\t\t\t\t}),\n\t\t\t  )\n\t\tconst instance = await this.instanceMapper.decryptAndMapToInstance<T>(typeModel, migratedEntity, sessionKey)\n\t\treturn this._crypto.applyMigrationsForInstance(instance)\n\t}\n\n\tasync loadRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\tconst rangeRequestParams = {\n\t\t\tstart: String(start),\n\t\t\tcount: String(count),\n\t\t\treverse: String(reverse),\n\t\t}\n\t\tconst { path, headers, typeModel, queryParams } = await this._validateAndPrepareRestRequest(\n\t\t\ttypeRef,\n\t\t\tlistId,\n\t\t\tnull,\n\t\t\trangeRequestParams,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t)\n\t\t// This should never happen if type checking is not bypassed with any\n\t\tif (typeModel.type !== Type.ListElement) throw new Error(\"only ListElement types are permitted\")\n\t\tconst json = await this.restClient.request(path, HttpMethod.GET, {\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t\tresponseType: MediaType.Json,\n\t\t})\n\t\treturn this._handleLoadMultipleResult(typeRef, JSON.parse(json))\n\t}\n\n\tasync loadMultiple<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementIds: Array<Id>): Promise<Array<T>> {\n\t\tconst { path, headers } = await this._validateAndPrepareRestRequest(typeRef, listId, null, undefined, undefined, undefined)\n\t\tconst idChunks = splitInChunks(LOAD_MULTIPLE_LIMIT, elementIds)\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\n\t\tconst loadedChunks = await promiseMap(idChunks, async (idChunk) => {\n\t\t\tlet queryParams = {\n\t\t\t\tids: idChunk.join(\",\"),\n\t\t\t}\n\t\t\tlet json: string\n\t\t\tif (typeModel.type === Type.BlobElement) {\n\t\t\t\tjson = await this.loadMultipleBlobElements(listId, queryParams, headers, path, typeRef)\n\t\t\t} else {\n\t\t\t\tjson = await this.restClient.request(path, HttpMethod.GET, {\n\t\t\t\t\tqueryParams,\n\t\t\t\t\theaders,\n\t\t\t\t\tresponseType: MediaType.Json,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn this._handleLoadMultipleResult(typeRef, JSON.parse(json))\n\t\t})\n\t\treturn flat(loadedChunks)\n\t}\n\n\tprivate async loadMultipleBlobElements(\n\t\tarchiveId: Id | null,\n\t\tqueryParams: { ids: string },\n\t\theaders: Dict | undefined,\n\t\tpath: string,\n\t\ttypeRef: TypeRef<any>,\n\t): Promise<string> {\n\t\tif (archiveId == null) {\n\t\t\tthrow new Error(\"archiveId must be set to load BlobElementTypes\")\n\t\t}\n\t\tconst doBlobRequest = async () => {\n\t\t\tconst blobServerAccessInfo = await this.blobAccessTokenFacade.requestReadTokenArchive(archiveId)\n\t\t\tconst additionalRequestParams = Object.assign(\n\t\t\t\t{},\n\t\t\t\theaders, // prevent CORS request due to non standard header usage\n\t\t\t\tqueryParams,\n\t\t\t)\n\t\t\tconst allParams = await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, additionalRequestParams, typeRef)\n\t\t\treturn tryServers(\n\t\t\t\tblobServerAccessInfo.servers,\n\t\t\t\tasync (serverUrl) =>\n\t\t\t\t\tthis.restClient.request(path, HttpMethod.GET, {\n\t\t\t\t\t\tqueryParams: allParams,\n\t\t\t\t\t\theaders: {}, // prevent CORS request due to non standard header usage\n\t\t\t\t\t\tresponseType: MediaType.Json,\n\t\t\t\t\t\tbaseUrl: serverUrl,\n\t\t\t\t\t\tnoCORS: true,\n\t\t\t\t\t}),\n\t\t\t\t`can't load instances from server `,\n\t\t\t)\n\t\t}\n\t\tconst doEvictToken = () => this.blobAccessTokenFacade.evictArchiveToken(archiveId)\n\n\t\treturn doBlobRequestWithRetry(doBlobRequest, doEvictToken)\n\t}\n\n\tasync _handleLoadMultipleResult<T extends SomeEntity>(typeRef: TypeRef<T>, loadedEntities: Array<any>): Promise<Array<T>> {\n\t\tconst model = await resolveTypeReference(typeRef)\n\n\t\t// PushIdentifier was changed in the system model v43 to encrypt the name.\n\t\t// We check here to check the type only once per array and not for each element.\n\t\tif (isSameTypeRef(typeRef, PushIdentifierTypeRef)) {\n\t\t\tawait promiseMap(loadedEntities, (instance) => this._crypto.applyMigrations(typeRef, instance), {\n\t\t\t\tconcurrency: 5,\n\t\t\t})\n\t\t}\n\n\t\treturn promiseMap(loadedEntities, (instance) => this._decryptMapAndMigrate(instance, model), { concurrency: 5 })\n\t}\n\n\tasync _decryptMapAndMigrate<T>(instance: any, model: TypeModel): Promise<T> {\n\t\tlet sessionKey\n\t\ttry {\n\t\t\tsessionKey = await this._crypto.resolveSessionKey(model, instance)\n\t\t} catch (e) {\n\t\t\tif (e instanceof SessionKeyNotFoundError) {\n\t\t\t\tconsole.log(\"could not resolve session key\", e)\n\t\t\t\tsessionKey = null // will result in _errors being set on the instance\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t\tconst decryptedInstance = await this.instanceMapper.decryptAndMapToInstance<T>(model, instance, sessionKey)\n\t\treturn this._crypto.applyMigrationsForInstance<T>(decryptedInstance)\n\t}\n\n\tasync setup<T extends SomeEntity>(listId: Id | null, instance: T, extraHeaders?: Dict, options?: EntityRestClientSetupOptions): Promise<Id> {\n\t\tconst typeRef = instance._type\n\t\tconst { typeModel, path, headers, queryParams } = await this._validateAndPrepareRestRequest(\n\t\t\ttypeRef,\n\t\t\tlistId,\n\t\t\tnull,\n\t\t\tundefined,\n\t\t\textraHeaders,\n\t\t\toptions?.ownerKey,\n\t\t)\n\n\t\tif (typeModel.type === Type.ListElement) {\n\t\t\tif (!listId) throw new Error(\"List id must be defined for LETs\")\n\t\t} else {\n\t\t\tif (listId) throw new Error(\"List id must not be defined for ETs\")\n\t\t}\n\n\t\tconst sk = this._crypto.setNewOwnerEncSessionKey(typeModel, instance, options?.ownerKey)\n\n\t\tconst encryptedEntity = await this.instanceMapper.encryptAndMapToLiteral(typeModel, instance, sk)\n\t\tconst persistencePostReturn = await this.restClient.request(path, HttpMethod.POST, {\n\t\t\tbaseUrl: options?.baseUrl,\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t\tbody: JSON.stringify(encryptedEntity),\n\t\t\tresponseType: MediaType.Json,\n\t\t})\n\t\treturn JSON.parse(persistencePostReturn).generatedId\n\t}\n\n\tasync setupMultiple<T extends SomeEntity>(listId: Id | null, instances: Array<T>): Promise<Array<Id>> {\n\t\tconst count = instances.length\n\n\t\tif (count < 1) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst instanceChunks = splitInChunks(POST_MULTIPLE_LIMIT, instances)\n\t\tconst typeRef = instances[0]._type\n\t\tconst { typeModel, path, headers } = await this._validateAndPrepareRestRequest(typeRef, listId, null, undefined, undefined, undefined)\n\n\t\tif (typeModel.type === Type.ListElement) {\n\t\t\tif (!listId) throw new Error(\"List id must be defined for LETs\")\n\t\t} else {\n\t\t\tif (listId) throw new Error(\"List id must not be defined for ETs\")\n\t\t}\n\n\t\tconst errors: Error[] = []\n\t\tconst failedInstances: T[] = []\n\t\tconst idChunks: Array<Array<Id>> = await promiseMap(instanceChunks, async (instanceChunk) => {\n\t\t\ttry {\n\t\t\t\tconst encryptedEntities = await promiseMap(instanceChunk, (e) => {\n\t\t\t\t\tconst sk = this._crypto.setNewOwnerEncSessionKey(typeModel, e)\n\n\t\t\t\t\treturn this.instanceMapper.encryptAndMapToLiteral(typeModel, e, sk)\n\t\t\t\t})\n\t\t\t\t// informs the server that this is a POST_MULTIPLE request\n\t\t\t\tconst queryParams = {\n\t\t\t\t\tcount: String(instanceChunk.length),\n\t\t\t\t}\n\t\t\t\tconst persistencePostReturn = await this.restClient.request(path, HttpMethod.POST, {\n\t\t\t\t\tqueryParams,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: JSON.stringify(encryptedEntities),\n\t\t\t\t\tresponseType: MediaType.Json,\n\t\t\t\t})\n\t\t\t\treturn this.parseSetupMultiple(persistencePostReturn)\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof PayloadTooLargeError) {\n\t\t\t\t\t// If we try to post too many large instances then we get PayloadTooLarge\n\t\t\t\t\t// So we fall back to posting single instances\n\t\t\t\t\tconst returnedIds = await promiseMap(instanceChunk, (instance) => {\n\t\t\t\t\t\treturn this.setup(listId, instance).catch((e) => {\n\t\t\t\t\t\t\terrors.push(e)\n\t\t\t\t\t\t\tfailedInstances.push(instance)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\treturn returnedIds.filter(Boolean) as Id[]\n\t\t\t\t} else {\n\t\t\t\t\terrors.push(e)\n\t\t\t\t\tfailedInstances.push(...instanceChunk)\n\t\t\t\t\treturn [] as Id[]\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tif (errors.length) {\n\t\t\tif (errors.some(isOfflineError)) {\n\t\t\t\tthrow new ConnectionError(\"Setup multiple entities failed\")\n\t\t\t}\n\t\t\tthrow new SetupMultipleError<T>(\"Setup multiple entities failed\", errors, failedInstances)\n\t\t} else {\n\t\t\treturn flat(idChunks)\n\t\t}\n\t}\n\n\tasync update<T extends SomeEntity>(instance: T, ownerKey?: Aes128Key): Promise<void> {\n\t\tif (!instance._id) throw new Error(\"Id must be defined\")\n\t\tconst { listId, elementId } = expandId(instance._id)\n\t\tconst { path, queryParams, headers, typeModel } = await this._validateAndPrepareRestRequest(\n\t\t\tinstance._type,\n\t\t\tlistId,\n\t\t\telementId,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\townerKey,\n\t\t)\n\t\tconst sessionKey = ownerKey ? this._crypto.resolveSessionKeyWithOwnerKey(instance, ownerKey) : await this._crypto.resolveSessionKey(typeModel, instance)\n\t\tconst encryptedEntity = await this.instanceMapper.encryptAndMapToLiteral(typeModel, instance, sessionKey)\n\t\tawait this.restClient.request(path, HttpMethod.PUT, {\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t\tbody: JSON.stringify(encryptedEntity),\n\t\t\tresponseType: MediaType.Json,\n\t\t})\n\t}\n\n\tasync erase<T extends SomeEntity>(instance: T): Promise<void> {\n\t\tconst { listId, elementId } = expandId(instance._id)\n\t\tconst { path, queryParams, headers } = await this._validateAndPrepareRestRequest(instance._type, listId, elementId, undefined, undefined, undefined)\n\t\tawait this.restClient.request(path, HttpMethod.DELETE, {\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t})\n\t}\n\n\tasync _validateAndPrepareRestRequest(\n\t\ttypeRef: TypeRef<any>,\n\t\tlistId: Id | null,\n\t\telementId: Id | null,\n\t\tqueryParams: Dict | undefined,\n\t\textraHeaders: Dict | undefined,\n\t\townerKey: Aes128Key | undefined,\n\t): Promise<{\n\t\tpath: string\n\t\tqueryParams: Dict | undefined\n\t\theaders: Dict | undefined\n\t\ttypeModel: TypeModel\n\t}> {\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\n\t\t_verifyType(typeModel)\n\n\t\tif (ownerKey == undefined && !this.authDataProvider.isFullyLoggedIn() && typeModel.encrypted) {\n\t\t\t// Short-circuit before we do an actual request which we can't decrypt\n\t\t\tthrow new LoginIncompleteError(`Trying to do a network request with encrypted entity but is not fully logged in yet, type: ${typeModel.name}`)\n\t\t}\n\n\t\tlet path = typeRefToPath(typeRef)\n\n\t\tif (listId) {\n\t\t\tpath += \"/\" + listId\n\t\t}\n\n\t\tif (elementId) {\n\t\t\tpath += \"/\" + elementId\n\t\t}\n\n\t\tconst headers = Object.assign({}, this.authDataProvider.createAuthHeaders(), extraHeaders)\n\n\t\tif (Object.keys(headers).length === 0) {\n\t\t\tthrow new NotAuthenticatedError(\"user must be authenticated for entity requests\")\n\t\t}\n\n\t\theaders.v = typeModel.version\n\t\treturn {\n\t\t\tpath,\n\t\t\tqueryParams,\n\t\t\theaders,\n\t\t\ttypeModel,\n\t\t}\n\t}\n\n\t/**\n\t * for the admin area (no cache available)\n\t */\n\tentityEventsReceived(batch: QueuedBatch): Promise<Array<EntityUpdate>> {\n\t\treturn Promise.resolve(batch.events)\n\t}\n\n\tgetRestClient(): RestClient {\n\t\treturn this.restClient\n\t}\n\n\tprivate parseSetupMultiple(result: any): Id[] {\n\t\ttry {\n\t\t\treturn JSON.parse(result).map((r: any) => r.generatedId)\n\t\t} catch (e) {\n\t\t\tthrow new Error(`Invalid response: ${result}, ${e}`)\n\t\t}\n\t}\n}\n\n/**\n * Tries to run the mapper action against a list of servers. If the action resolves\n * successfully, the result is returned. In case of an ConnectionError and errors\n * that might occur only for a single blob server, the next server is tried.\n * Throws in all other cases.\n */\nexport async function tryServers<T>(servers: BlobServerUrl[], mapper: Mapper<string, T>, errorMsg: string): Promise<T> {\n\tlet index = 0\n\tlet error: Error | null = null\n\tfor (const server of servers) {\n\t\ttry {\n\t\t\treturn await mapper(server.url, index)\n\t\t} catch (e) {\n\t\t\t// InternalServerError is returned when accessing a corrupted archive, so we retry\n\t\t\tif (e instanceof ConnectionError || e instanceof InternalServerError || e instanceof NotFoundError) {\n\t\t\t\tconsole.log(`${errorMsg} ${server.url}`, e)\n\t\t\t\terror = e\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t\tindex++\n\t}\n\tthrow error\n}\n\n/**\n * Do a blob request and retry it in case of a NotAuthorizedError, performing some cleanup before retrying.\n *\n * This is useful for blob requests to handle expired tokens, which cah occur if the requests take a long time, the client gets suspended or paused by the OS.\n * @param doBlobRequest\n * @param doEvictTokenBeforeRetry\n */\nexport async function doBlobRequestWithRetry<T>(doBlobRequest: () => Promise<T>, doEvictTokenBeforeRetry: () => void): Promise<T> {\n\treturn doBlobRequest().catch(\n\t\t// in case one of the chunks could not be uploaded because of an invalid/expired token we upload all chunks again in order to guarantee that they are uploaded to the same archive.\n\t\t// we don't have to take care of already uploaded chunks, as they are unreferenced and will be cleaned up by the server automatically.\n\t\tofClass(NotAuthorizedError, (e) => {\n\t\t\tdoEvictTokenBeforeRetry()\n\t\t\treturn doBlobRequest()\n\t\t}),\n\t)\n}\n\nexport function getIds(\n\tinstance: any,\n\ttypeModel: TypeModel,\n): {\n\tlistId: string | null\n\tid: string\n} {\n\tif (!instance._id) throw new Error(\"Id must be defined\")\n\tlet listId = null\n\tlet id\n\n\tif (typeModel.type === Type.ListElement) {\n\t\tlistId = instance._id[0]\n\t\tid = instance._id[1]\n\t} else {\n\t\tid = instance._id\n\t}\n\n\treturn {\n\t\tlistId,\n\t\tid,\n\t}\n}\n","/** @fileOverview Javascript cryptography implementation.\n *\n * Crush to remove comments, shorten variable names and\n * generally reduce transmission size.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n// FORKED from SJCL 1.0.7\n// CHANGED (tutao.arm)\n// - added option to not use padding in encrypt/decrypt in cbc mode\n// Configured with: ./configure --with-codecArrayBuffer --with-cbc --with-sha1 --with-sha512 --with-codecBytes --without-ccm --without-ocb2 --without-pbkdf2 --without-convenience --compress=none\n/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */\n/*global document, window, escape, unescape, module, require, Uint32Array */\n/**\n * The Stanford Javascript Crypto Library, top-level namespace.\n * @namespace\n * @type any\n */\nvar sjcl = {\n    /**\n     * Symmetric ciphers.\n     * @namespace\n     */\n    cipher: {},\n    /**\n     * Hash functions.  Right now only SHA256 is implemented.\n     * @namespace\n     */\n    hash: {},\n    /**\n     * Key exchange functions.  Right now only SRP is implemented.\n     * @namespace\n     */\n    keyexchange: {},\n    /**\n     * Cipher modes of operation.\n     * @namespace\n     */\n    mode: {},\n    /**\n     * Miscellaneous.  HMAC and PBKDF2.\n     * @namespace\n     */\n    misc: {},\n    /**\n     * Bit array encoders and decoders.\n     * @namespace\n     *\n     * @description\n     * The members of this namespace are functions which translate between\n     * SJCL's bitArrays and other objects (usually strings).  Because it\n     * isn't always clear which direction is encoding and which is decoding,\n     * the method names are \"fromBits\" and \"toBits\".\n     */\n    codec: {},\n    /**\n     * Exceptions.\n     * @namespace\n     */\n    exception: {\n        /**\n         * Ciphertext is corrupt.\n         * @constructor\n         */\n        corrupt: function (message) {\n            this.toString = function () {\n                return \"CORRUPT: \" + this.message;\n            };\n            this.message = message;\n        },\n        /**\n         * Invalid parameter.\n         * @constructor\n         */\n        invalid: function (message) {\n            this.toString = function () {\n                return \"INVALID: \" + this.message;\n            };\n            this.message = message;\n        },\n        /**\n         * Bug or missing feature in SJCL.\n         * @constructor\n         */\n        bug: function (message) {\n            this.toString = function () {\n                return \"BUG: \" + this.message;\n            };\n            this.message = message;\n        },\n        /**\n         * Something isn't ready.\n         * @constructor\n         */\n        notReady: function (message) {\n            this.toString = function () {\n                return \"NOT READY: \" + this.message;\n            };\n            this.message = message;\n        }\n    }\n};\n/** @fileOverview Low-level AES implementation.\n *\n * This file contains a low-level implementation of AES, optimized for\n * size and for efficiency on several browsers.  It is based on\n * OpenSSL's aes_core.c, a public-domain implementation by Vincent\n * Rijmen, Antoon Bosselaers and Paulo Barreto.\n *\n * An older version of this implementation is available in the public\n * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,\n * Stanford University 2008-2010 and BSD-licensed for liability\n * reasons.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * Schedule out an AES key for both encryption and decryption.  This\n * is a low-level class.  Use a cipher mode to do bulk encryption.\n *\n * @constructor\n * @param {Array} key The key as an array of 4, 6 or 8 words.\n */\nsjcl.cipher.aes = function (key) {\n    if (!this._tables[0][0][0]) {\n        this._precompute();\n    }\n    var i, j, tmp, encKey, decKey, sbox = this._tables[0][4], decTable = this._tables[1], keyLen = key.length, rcon = 1;\n    if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {\n        throw new sjcl.exception.invalid(\"invalid aes key size\");\n    }\n    this._key = [encKey = key.slice(0), decKey = []];\n    // schedule encryption keys\n    for (i = keyLen; i < 4 * keyLen + 28; i++) {\n        tmp = encKey[i - 1];\n        // apply sbox\n        if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) {\n            tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];\n            // shift rows and add rcon\n            if (i % keyLen === 0) {\n                tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;\n                rcon = rcon << 1 ^ (rcon >> 7) * 283;\n            }\n        }\n        encKey[i] = encKey[i - keyLen] ^ tmp;\n    }\n    // schedule decryption keys\n    for (j = 0; i; j++, i--) {\n        tmp = encKey[j & 3 ? i : i - 4];\n        if (i <= 4 || j < 4) {\n            decKey[j] = tmp;\n        }\n        else {\n            decKey[j] = decTable[0][sbox[tmp >>> 24]] ^\n                decTable[1][sbox[tmp >> 16 & 255]] ^\n                decTable[2][sbox[tmp >> 8 & 255]] ^\n                decTable[3][sbox[tmp & 255]];\n        }\n    }\n};\nsjcl.cipher.aes.prototype = {\n    // public\n    /* Something like this might appear here eventually\n     name: \"AES\",\n     blockSize: 4,\n     keySizes: [4,6,8],\n     */\n    /**\n     * Encrypt an array of 4 big-endian words.\n     * @param {Array} data The plaintext.\n     * @return {Array} The ciphertext.\n     */\n    encrypt: function (data) {\n        return this._crypt(data, 0);\n    },\n    /**\n     * Decrypt an array of 4 big-endian words.\n     * @param {Array} data The ciphertext.\n     * @return {Array} The plaintext.\n     */\n    decrypt: function (data) {\n        return this._crypt(data, 1);\n    },\n    /**\n     * The expanded S-box and inverse S-box tables.  These will be computed\n     * on the client so that we don't have to send them down the wire.\n     *\n     * There are two tables, _tables[0] is for encryption and\n     * _tables[1] is for decryption.\n     *\n     * The first 4 sub-tables are the expanded S-box with MixColumns.  The\n     * last (_tables[01][4]) is the S-box itself.\n     *\n     * @private\n     */\n    _tables: [[[], [], [], [], []], [[], [], [], [], []]],\n    /**\n     * Expand the S-box tables.\n     *\n     * @private\n     */\n    _precompute: function () {\n        var encTable = this._tables[0], decTable = this._tables[1], sbox = encTable[4], sboxInv = decTable[4], i, x, xInv, d = [], th = [], x2, x4, x8, s, tEnc, tDec;\n        // Compute double and third tables\n        for (i = 0; i < 256; i++) {\n            th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;\n        }\n        for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {\n            // Compute sbox\n            s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;\n            s = s >> 8 ^ s & 255 ^ 99;\n            sbox[x] = s;\n            sboxInv[s] = x;\n            // Compute MixColumns\n            x8 = d[x4 = d[x2 = d[x]]];\n            tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;\n            tEnc = d[s] * 0x101 ^ s * 0x1010100;\n            for (i = 0; i < 4; i++) {\n                encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;\n                decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;\n            }\n        }\n        // Compactify.  Considerable speedup on Firefox.\n        for (i = 0; i < 5; i++) {\n            encTable[i] = encTable[i].slice(0);\n            decTable[i] = decTable[i].slice(0);\n        }\n    },\n    /**\n     * Encryption and decryption core.\n     * @param {Array} input Four words to be encrypted or decrypted.\n     * @param dir The direction, 0 for encrypt and 1 for decrypt.\n     * @return {Array} The four encrypted or decrypted words.\n     * @private\n     */\n    _crypt: function (input, dir) {\n        if (input.length !== 4) {\n            throw new sjcl.exception.invalid(\"invalid aes block size\");\n        }\n        var key = this._key[dir], \n        // state variables a,b,c,d are loaded with pre-whitened data\n        a = input[0] ^ key[0], b = input[dir ? 3 : 1] ^ key[1], c = input[2] ^ key[2], d = input[dir ? 1 : 3] ^ key[3], a2, b2, c2, nInnerRounds = key.length / 4 - 2, i, kIndex = 4, out = [0, 0, 0, 0], table = this._tables[dir], \n        // load up the tables\n        t0 = table[0], t1 = table[1], t2 = table[2], t3 = table[3], sbox = table[4];\n        // Inner rounds.  Cribbed from OpenSSL.\n        for (i = 0; i < nInnerRounds; i++) {\n            a2 = t0[a >>> 24] ^ t1[b >> 16 & 255] ^ t2[c >> 8 & 255] ^ t3[d & 255] ^ key[kIndex];\n            b2 = t0[b >>> 24] ^ t1[c >> 16 & 255] ^ t2[d >> 8 & 255] ^ t3[a & 255] ^ key[kIndex + 1];\n            c2 = t0[c >>> 24] ^ t1[d >> 16 & 255] ^ t2[a >> 8 & 255] ^ t3[b & 255] ^ key[kIndex + 2];\n            d = t0[d >>> 24] ^ t1[a >> 16 & 255] ^ t2[b >> 8 & 255] ^ t3[c & 255] ^ key[kIndex + 3];\n            kIndex += 4;\n            a = a2;\n            b = b2;\n            c = c2;\n        }\n        // Last round.\n        for (i = 0; i < 4; i++) {\n            out[dir ? 3 & -i : i] =\n                sbox[a >>> 24] << 24 ^\n                    sbox[b >> 16 & 255] << 16 ^\n                    sbox[c >> 8 & 255] << 8 ^\n                    sbox[d & 255] ^\n                    key[kIndex++];\n            a2 = a;\n            a = b;\n            b = c;\n            c = d;\n            d = a2;\n        }\n        return out;\n    }\n};\n/** @fileOverview Arrays of bits, encoded as arrays of Numbers.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * Arrays of bits, encoded as arrays of Numbers.\n * @namespace\n * @description\n * <p>\n * These objects are the currency accepted by SJCL's crypto functions.\n * </p>\n *\n * <p>\n * Most of our crypto primitives operate on arrays of 4-byte words internally,\n * but many of them can take arguments that are not a multiple of 4 bytes.\n * This library encodes arrays of bits (whose size need not be a multiple of 8\n * bits) as arrays of 32-bit words.  The bits are packed, big-endian, into an\n * array of words, 32 bits at a time.  Since the words are double-precision\n * floating point numbers, they fit some extra data.  We use this (in a private,\n * possibly-changing manner) to encode the number of bits actually  present\n * in the last word of the array.\n * </p>\n *\n * <p>\n * Because bitwise ops clear this out-of-band data, these arrays can be passed\n * to ciphers like AES which want arrays of words.\n * </p>\n */\nsjcl.bitArray = {\n    /**\n     * Array slices in units of bits.\n     * @param {bitArray} a The array to slice.\n     * @param {Number} bstart The offset to the start of the slice, in bits.\n     * @param {Number} bend The offset to the end of the slice, in bits.  If this is undefined,\n     * slice until the end of the array.\n     * @return {bitArray} The requested slice.\n     */\n    bitSlice: function (a, bstart, bend) {\n        a = sjcl.bitArray._shiftRight(a.slice(bstart / 32), 32 - (bstart & 31)).slice(1);\n        return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend - bstart);\n    },\n    /**\n     * Extract a number packed into a bit array.\n     * @param {bitArray} a The array to slice.\n     * @param {Number} bstart The offset to the start of the slice, in bits.\n     * @param {Number} blength The length of the number to extract.\n     * @return {Number} The requested slice.\n     */\n    extract: function (a, bstart, blength) {\n        // FIXME: this Math.floor is not necessary at all, but for some reason\n        // seems to suppress a bug in the Chromium JIT.\n        var x, sh = Math.floor((-bstart - blength) & 31);\n        if ((bstart + blength - 1 ^ bstart) & -32) {\n            // it crosses a boundary\n            x = (a[bstart / 32 | 0] << (32 - sh)) ^ (a[bstart / 32 + 1 | 0] >>> sh);\n        }\n        else {\n            // within a single word\n            x = a[bstart / 32 | 0] >>> sh;\n        }\n        return x & ((1 << blength) - 1);\n    },\n    /**\n     * Concatenate two bit arrays.\n     * @param {bitArray} a1 The first array.\n     * @param {bitArray} a2 The second array.\n     * @return {bitArray} The concatenation of a1 and a2.\n     */\n    concat: function (a1, a2) {\n        if (a1.length === 0 || a2.length === 0) {\n            return a1.concat(a2);\n        }\n        var last = a1[a1.length - 1], shift = sjcl.bitArray.getPartial(last);\n        if (shift === 32) {\n            return a1.concat(a2);\n        }\n        else {\n            return sjcl.bitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));\n        }\n    },\n    /**\n     * Find the length of an array of bits.\n     * @param {bitArray} a The array.\n     * @return {Number} The length of a, in bits.\n     */\n    bitLength: function (a) {\n        var l = a.length, x;\n        if (l === 0) {\n            return 0;\n        }\n        x = a[l - 1];\n        return (l - 1) * 32 + sjcl.bitArray.getPartial(x);\n    },\n    /**\n     * Truncate an array.\n     * @param {bitArray} a The array.\n     * @param {Number} len The length to truncate to, in bits.\n     * @return {bitArray} A new array, truncated to len bits.\n     */\n    clamp: function (a, len) {\n        if (a.length * 32 < len) {\n            return a;\n        }\n        a = a.slice(0, Math.ceil(len / 32));\n        var l = a.length;\n        len = len & 31;\n        if (l > 0 && len) {\n            a[l - 1] = sjcl.bitArray.partial(len, a[l - 1] & 0x80000000 >> (len - 1), 1);\n        }\n        return a;\n    },\n    /**\n     * Make a partial word for a bit array.\n     * @param {Number} len The number of bits in the word.\n     * @param {Number} x The bits.\n     * @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side.\n     * @return {Number} The partial word.\n     */\n    partial: function (len, x, _end) {\n        if (len === 32) {\n            return x;\n        }\n        return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000;\n    },\n    /**\n     * Get the number of bits used by a partial word.\n     * @param {Number} x The partial word.\n     * @return {Number} The number of bits used by the partial word.\n     */\n    getPartial: function (x) {\n        return Math.round(x / 0x10000000000) || 32;\n    },\n    /**\n     * Compare two arrays for equality in a predictable amount of time.\n     * @param {bitArray} a The first array.\n     * @param {bitArray} b The second array.\n     * @return {boolean} true if a == b; false otherwise.\n     */\n    equal: function (a, b) {\n        if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) {\n            return false;\n        }\n        var x = 0, i;\n        for (i = 0; i < a.length; i++) {\n            x |= a[i] ^ b[i];\n        }\n        return (x === 0);\n    },\n    /** Shift an array right.\n     * @param {bitArray} a The array to shift.\n     * @param {Number} shift The number of bits to shift.\n     * @param {Number} [carry=0] A byte to carry in\n     * @param {bitArray} [out=[]] An array to prepend to the output.\n     * @private\n     */\n    _shiftRight: function (a, shift, carry, out) {\n        var i, last2 = 0, shift2;\n        if (out === undefined) {\n            out = [];\n        }\n        for (; shift >= 32; shift -= 32) {\n            out.push(carry);\n            carry = 0;\n        }\n        if (shift === 0) {\n            return out.concat(a);\n        }\n        for (i = 0; i < a.length; i++) {\n            out.push(carry | a[i] >>> shift);\n            carry = a[i] << (32 - shift);\n        }\n        last2 = a.length ? a[a.length - 1] : 0;\n        shift2 = sjcl.bitArray.getPartial(last2);\n        out.push(sjcl.bitArray.partial(shift + shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(), 1));\n        return out;\n    },\n    /** xor a block of 4 words together.\n     * @private\n     */\n    _xor4: function (x, y) {\n        return [x[0] ^ y[0], x[1] ^ y[1], x[2] ^ y[2], x[3] ^ y[3]];\n    },\n    /** byteswap a word array inplace.\n     * (does not handle partial words)\n     * @param {sjcl.bitArray} a word array\n     * @return {sjcl.bitArray} byteswapped array\n     */\n    byteswapM: function (a) {\n        var i, v, m = 0xff00;\n        for (i = 0; i < a.length; ++i) {\n            v = a[i];\n            a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);\n        }\n        return a;\n    }\n};\n/** @fileOverview Bit array codec implementations.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * UTF-8 strings\n * @namespace\n */\nsjcl.codec.utf8String = {\n    /** Convert from a bitArray to a UTF-8 string. */\n    fromBits: function (arr) {\n        var out = \"\", bl = sjcl.bitArray.bitLength(arr), i, tmp;\n        for (i = 0; i < bl / 8; i++) {\n            if ((i & 3) === 0) {\n                tmp = arr[i / 4];\n            }\n            out += String.fromCharCode(tmp >>> 8 >>> 8 >>> 8);\n            tmp <<= 8;\n        }\n        return decodeURIComponent(escape(out));\n    },\n    /** Convert from a UTF-8 string to a bitArray. */\n    toBits: function (str) {\n        str = unescape(encodeURIComponent(str));\n        var out = [], i, tmp = 0;\n        for (i = 0; i < str.length; i++) {\n            tmp = tmp << 8 | str.charCodeAt(i);\n            if ((i & 3) === 3) {\n                out.push(tmp);\n                tmp = 0;\n            }\n        }\n        if (i & 3) {\n            out.push(sjcl.bitArray.partial(8 * (i & 3), tmp));\n        }\n        return out;\n    }\n};\n/** @fileOverview Bit array codec implementations.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/** @fileOverview Bit array codec implementations.\n *\n * @author Nils Kenneweg\n */\n/**\n * Base32 encoding/decoding\n * @namespace\n */\nsjcl.codec.base32 = {\n    /** The base32 alphabet.\n     * @private\n     */\n    _chars: \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\",\n    _hexChars: \"0123456789ABCDEFGHIJKLMNOPQRSTUV\",\n    /* bits in an array */\n    BITS: 32,\n    /* base to encode at (2^x) */\n    BASE: 5,\n    /* bits - base */\n    REMAINING: 27,\n    /** Convert from a bitArray to a base32 string. */\n    fromBits: function (arr, _noEquals, _hex) {\n        var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;\n        var out = \"\", i, bits = 0, c = sjcl.codec.base32._chars, ta = 0, bl = sjcl.bitArray.bitLength(arr);\n        if (_hex) {\n            c = sjcl.codec.base32._hexChars;\n        }\n        for (i = 0; out.length * BASE < bl;) {\n            out += c.charAt((ta ^ arr[i] >>> bits) >>> REMAINING);\n            if (bits < BASE) {\n                ta = arr[i] << (BASE - bits);\n                bits += REMAINING;\n                i++;\n            }\n            else {\n                ta <<= BASE;\n                bits -= BASE;\n            }\n        }\n        while ((out.length & 7) && !_noEquals) {\n            out += \"=\";\n        }\n        return out;\n    },\n    /** Convert from a base32 string to a bitArray */\n    toBits: function (str, _hex) {\n        str = str.replace(/\\s|=/g, '').toUpperCase();\n        var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;\n        var out = [], i, bits = 0, c = sjcl.codec.base32._chars, ta = 0, x, format = \"base32\";\n        if (_hex) {\n            c = sjcl.codec.base32._hexChars;\n            format = \"base32hex\";\n        }\n        for (i = 0; i < str.length; i++) {\n            x = c.indexOf(str.charAt(i));\n            if (x < 0) {\n                // Invalid character, try hex format\n                if (!_hex) {\n                    try {\n                        return sjcl.codec.base32hex.toBits(str);\n                    }\n                    catch (e) {\n                    }\n                }\n                throw new sjcl.exception.invalid(\"this isn't \" + format + \"!\");\n            }\n            if (bits > REMAINING) {\n                bits -= REMAINING;\n                out.push(ta ^ x >>> bits);\n                ta = x << (BITS - bits);\n            }\n            else {\n                bits += BASE;\n                ta ^= x << (BITS - bits);\n            }\n        }\n        if (bits & 56) {\n            out.push(sjcl.bitArray.partial(bits & 56, ta, 1));\n        }\n        return out;\n    }\n};\nsjcl.codec.base32hex = {\n    fromBits: function (arr, _noEquals) {\n        return sjcl.codec.base32.fromBits(arr, _noEquals, 1);\n    },\n    toBits: function (str) {\n        return sjcl.codec.base32.toBits(str, 1);\n    }\n};\n/** @fileOverview Bit array codec implementations.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * Base64 encoding/decoding\n * @namespace\n */\nsjcl.codec.base64 = {\n    /** The base64 alphabet.\n     * @private\n     */\n    _chars: \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",\n    /** Convert from a bitArray to a base64 string. */\n    fromBits: function (arr, _noEquals, _url) {\n        var out = \"\", i, bits = 0, c = sjcl.codec.base64._chars, ta = 0, bl = sjcl.bitArray.bitLength(arr);\n        if (_url) {\n            c = c.substr(0, 62) + '-_';\n        }\n        for (i = 0; out.length * 6 < bl;) {\n            out += c.charAt((ta ^ arr[i] >>> bits) >>> 26);\n            if (bits < 6) {\n                ta = arr[i] << (6 - bits);\n                bits += 26;\n                i++;\n            }\n            else {\n                ta <<= 6;\n                bits -= 6;\n            }\n        }\n        while ((out.length & 3) && !_noEquals) {\n            out += \"=\";\n        }\n        return out;\n    },\n    /** Convert from a base64 string to a bitArray */\n    toBits: function (str, _url) {\n        str = str.replace(/\\s|=/g, '');\n        var out = [], i, bits = 0, c = sjcl.codec.base64._chars, ta = 0, x;\n        if (_url) {\n            c = c.substr(0, 62) + '-_';\n        }\n        for (i = 0; i < str.length; i++) {\n            x = c.indexOf(str.charAt(i));\n            if (x < 0) {\n                throw new sjcl.exception.invalid(\"this isn't base64!\");\n            }\n            if (bits > 26) {\n                bits -= 26;\n                out.push(ta ^ x >>> bits);\n                ta = x << (32 - bits);\n            }\n            else {\n                bits += 6;\n                ta ^= x << (32 - bits);\n            }\n        }\n        if (bits & 56) {\n            out.push(sjcl.bitArray.partial(bits & 56, ta, 1));\n        }\n        return out;\n    }\n};\n/** @fileOverview Javascript SHA-256 implementation.\n *\n * An older version of this implementation is available in the public\n * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,\n * Stanford University 2008-2010 and BSD-licensed for liability\n * reasons.\n *\n * Special thanks to Aldo Cortesi for pointing out several bugs in\n * this code.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * Context for a SHA-256 operation in progress.\n * @constructor\n */\nsjcl.hash.sha256 = function (hash) {\n    if (!this._key[0]) {\n        this._precompute();\n    }\n    if (hash) {\n        this._h = hash._h.slice(0);\n        this._buffer = hash._buffer.slice(0);\n        this._length = hash._length;\n    }\n    else {\n        this.reset();\n    }\n};\n/**\n * Hash a string or an array of words.\n * @static\n * @param {bitArray|String} data the data to hash.\n * @return {bitArray} The hash value, an array of 16 big-endian words.\n */\nsjcl.hash.sha256.hash = function (data) {\n    return (new sjcl.hash.sha256()).update(data).finalize();\n};\nsjcl.hash.sha256.prototype = {\n    /**\n     * The hash's block size, in bits.\n     * @constant\n     */\n    blockSize: 512,\n    /**\n     * Reset the hash state.\n     * @return this\n     */\n    reset: function () {\n        this._h = this._init.slice(0);\n        this._buffer = [];\n        this._length = 0;\n        return this;\n    },\n    /**\n     * Input several words to the hash.\n     * @param {bitArray|String} data the data to hash.\n     * @return this\n     */\n    update: function (data) {\n        if (typeof data === \"string\") {\n            data = sjcl.codec.utf8String.toBits(data);\n        }\n        var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data), ol = this._length, nl = this._length = ol + sjcl.bitArray.bitLength(data);\n        if (nl > 9007199254740991) {\n            throw new sjcl.exception.invalid(\"Cannot hash more than 2^53 - 1 bits\");\n        }\n        if (typeof Uint32Array !== 'undefined') {\n            var c = new Uint32Array(b);\n            var j = 0;\n            for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {\n                this._block(c.subarray(16 * j, 16 * (j + 1)));\n                j += 1;\n            }\n            b.splice(0, 16 * j);\n        }\n        else {\n            for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {\n                this._block(b.splice(0, 16));\n            }\n        }\n        return this;\n    },\n    /**\n     * Complete hashing and output the hash value.\n     * @return {bitArray} The hash value, an array of 8 big-endian words.\n     */\n    finalize: function () {\n        var i, b = this._buffer, h = this._h;\n        // Round out and push the buffer\n        b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]);\n        // Round out the buffer to a multiple of 16 words, less the 2 length words.\n        for (i = b.length + 2; i & 15; i++) {\n            b.push(0);\n        }\n        // append the length\n        b.push(Math.floor(this._length / 0x100000000));\n        b.push(this._length | 0);\n        while (b.length) {\n            this._block(b.splice(0, 16));\n        }\n        this.reset();\n        return h;\n    },\n    /**\n     * The SHA-256 initialization vector, to be precomputed.\n     * @private\n     */\n    _init: [],\n    /*\n     _init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],\n     */\n    /**\n     * The SHA-256 hash key, to be precomputed.\n     * @private\n     */\n    _key: [],\n    /*\n     _key:\n     [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n     0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n     0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n     0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n     0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n     0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],\n     */\n    /**\n     * Function to precompute _init and _key.\n     * @private\n     */\n    _precompute: function () {\n        var i = 0, prime = 2, factor, isPrime;\n        function frac(x) {\n            return (x - Math.floor(x)) * 0x100000000 | 0;\n        }\n        for (; i < 64; prime++) {\n            isPrime = true;\n            for (factor = 2; factor * factor <= prime; factor++) {\n                if (prime % factor === 0) {\n                    isPrime = false;\n                    break;\n                }\n            }\n            if (isPrime) {\n                if (i < 8) {\n                    this._init[i] = frac(Math.pow(prime, 1 / 2));\n                }\n                this._key[i] = frac(Math.pow(prime, 1 / 3));\n                i++;\n            }\n        }\n    },\n    /**\n     * Perform one cycle of SHA-256.\n     * @param {Uint32Array|bitArray} w one block of words.\n     * @private\n     */\n    _block: function (w) {\n        var i, tmp, a, b, h = this._h, k = this._key, h0 = h[0], h1 = h[1], h2 = h[2], h3 = h[3], h4 = h[4], h5 = h[5], h6 = h[6], h7 = h[7];\n        /* Rationale for placement of |0 :\n         * If a value can overflow is original 32 bits by a factor of more than a few\n         * million (2^23 ish), there is a possibility that it might overflow the\n         * 53-bit mantissa and lose precision.\n         *\n         * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that\n         * propagates around the loop, and on the hash state h[].  I don't believe\n         * that the clamps on h4 and on h0 are strictly necessary, but it's close\n         * (for h4 anyway), and better safe than sorry.\n         *\n         * The clamps on h[] are necessary for the output to be correct even in the\n         * common case and for short inputs.\n         */\n        for (i = 0; i < 64; i++) {\n            // load up the input word for this round\n            if (i < 16) {\n                tmp = w[i];\n            }\n            else {\n                a = w[(i + 1) & 15];\n                b = w[(i + 14) & 15];\n                tmp = w[i & 15] = ((a >>> 7 ^ a >>> 18 ^ a >>> 3 ^ a << 25 ^ a << 14) +\n                    (b >>> 17 ^ b >>> 19 ^ b >>> 10 ^ b << 15 ^ b << 13) +\n                    w[i & 15] + w[(i + 9) & 15]) | 0;\n            }\n            tmp = (tmp + h7 + (h4 >>> 6 ^ h4 >>> 11 ^ h4 >>> 25 ^ h4 << 26 ^ h4 << 21 ^ h4 << 7) + (h6 ^ h4 & (h5 ^ h6))\n                + k[i]); // | 0;\n            // shift register\n            h7 = h6;\n            h6 = h5;\n            h5 = h4;\n            h4 = h3 + tmp | 0;\n            h3 = h2;\n            h2 = h1;\n            h1 = h0;\n            h0 = (tmp + ((h1 & h2) ^ (h3 & (h1 ^ h2))) + (h1 >>> 2 ^ h1 >>> 13 ^ h1 >>> 22 ^ h1 << 30 ^ h1 << 19 ^ h1\n                << 10)) | 0;\n        }\n        h[0] = h[0] + h0 | 0;\n        h[1] = h[1] + h1 | 0;\n        h[2] = h[2] + h2 | 0;\n        h[3] = h[3] + h3 | 0;\n        h[4] = h[4] + h4 | 0;\n        h[5] = h[5] + h5 | 0;\n        h[6] = h[6] + h6 | 0;\n        h[7] = h[7] + h7 | 0;\n    }\n};\n/** @fileOverview Javascript SHA-512 implementation.\n *\n * This implementation was written for CryptoJS by Jeff Mott and adapted for\n * SJCL by Stefan Thomas.\n *\n * CryptoJS (c) 2009–2012 by Jeff Mott. All rights reserved.\n * Released with New BSD License\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n * @author Jeff Mott\n * @author Stefan Thomas\n */\n/**\n * Context for a SHA-512 operation in progress.\n * @constructor\n */\nsjcl.hash.sha512 = function (hash) {\n    if (!this._key[0]) {\n        this._precompute();\n    }\n    if (hash) {\n        this._h = hash._h.slice(0);\n        this._buffer = hash._buffer.slice(0);\n        this._length = hash._length;\n    }\n    else {\n        this.reset();\n    }\n};\n/**\n * Hash a string or an array of words.\n * @static\n * @param {bitArray|String} data the data to hash.\n * @return {bitArray} The hash value, an array of 16 big-endian words.\n */\nsjcl.hash.sha512.hash = function (data) {\n    return (new sjcl.hash.sha512()).update(data).finalize();\n};\nsjcl.hash.sha512.prototype = {\n    /**\n     * The hash's block size, in bits.\n     * @constant\n     */\n    blockSize: 1024,\n    /**\n     * Reset the hash state.\n     * @return this\n     */\n    reset: function () {\n        this._h = this._init.slice(0);\n        this._buffer = [];\n        this._length = 0;\n        return this;\n    },\n    /**\n     * Input several words to the hash.\n     * @param {bitArray|String} data the data to hash.\n     * @return this\n     */\n    update: function (data) {\n        if (typeof data === \"string\") {\n            data = sjcl.codec.utf8String.toBits(data);\n        }\n        var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data), ol = this._length, nl = this._length = ol + sjcl.bitArray.bitLength(data);\n        if (nl > 9007199254740991) {\n            throw new sjcl.exception.invalid(\"Cannot hash more than 2^53 - 1 bits\");\n        }\n        if (typeof Uint32Array !== 'undefined') {\n            var c = new Uint32Array(b);\n            var j = 0;\n            for (i = 1024 + ol - ((1024 + ol) & 1023); i <= nl; i += 1024) {\n                this._block(c.subarray(32 * j, 32 * (j + 1)));\n                j += 1;\n            }\n            b.splice(0, 32 * j);\n        }\n        else {\n            for (i = 1024 + ol - ((1024 + ol) & 1023); i <= nl; i += 1024) {\n                this._block(b.splice(0, 32));\n            }\n        }\n        return this;\n    },\n    /**\n     * Complete hashing and output the hash value.\n     * @return {bitArray} The hash value, an array of 16 big-endian words.\n     */\n    finalize: function () {\n        var i, b = this._buffer, h = this._h;\n        // Round out and push the buffer\n        b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]);\n        // Round out the buffer to a multiple of 32 words, less the 4 length words.\n        for (i = b.length + 4; i & 31; i++) {\n            b.push(0);\n        }\n        // append the length\n        b.push(0);\n        b.push(0);\n        b.push(Math.floor(this._length / 0x100000000));\n        b.push(this._length | 0);\n        while (b.length) {\n            this._block(b.splice(0, 32));\n        }\n        this.reset();\n        return h;\n    },\n    /**\n     * The SHA-512 initialization vector, to be precomputed.\n     * @private\n     */\n    _init: [],\n    /**\n     * Least significant 24 bits of SHA512 initialization values.\n     *\n     * Javascript only has 53 bits of precision, so we compute the 40 most\n     * significant bits and add the remaining 24 bits as constants.\n     *\n     * @private\n     */\n    _initr: [0xbcc908, 0xcaa73b, 0x94f82b, 0x1d36f1, 0xe682d1, 0x3e6c1f, 0x41bd6b, 0x7e2179],\n    /*\n  _init:\n  [0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n   0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179],\n  */\n    /**\n     * The SHA-512 hash key, to be precomputed.\n     * @private\n     */\n    _key: [],\n    /**\n     * Least significant 24 bits of SHA512 key values.\n     * @private\n     */\n    _keyr: [\n        0x28ae22, 0xef65cd, 0x4d3b2f, 0x89dbbc, 0x48b538, 0x05d019, 0x194f9b, 0x6d8118,\n        0x030242, 0x706fbe, 0xe4b28c, 0xffb4e2, 0x7b896f, 0x1696b1, 0xc71235, 0x692694,\n        0xf14ad2, 0x4f25e3, 0x8cd5b5, 0xac9c65, 0x2b0275, 0xa6e483, 0x41fbd4, 0x1153b5,\n        0x66dfab, 0xb43210, 0xfb213f, 0xef0ee4, 0xa88fc2, 0x0aa725, 0x03826f, 0x0e6e70,\n        0xd22ffc, 0x26c926, 0xc42aed, 0x95b3df, 0xaf63de, 0x77b2a8, 0xedaee6, 0x82353b,\n        0xf10364, 0x423001, 0xf89791, 0x54be30, 0xef5218, 0x65a910, 0x71202a, 0xbbd1b8,\n        0xd2d0c8, 0x41ab53, 0x8eeb99, 0x9b48a8, 0xc95a63, 0x418acb, 0x63e373, 0xb2b8a3,\n        0xefb2fc, 0x172f60, 0xf0ab72, 0x6439ec, 0x631e28, 0x82bde9, 0xc67915, 0x72532b,\n        0x26619c, 0xc0c207, 0xe0eb1e, 0x6ed178, 0x176fba, 0xc898a6, 0xf90dae, 0x1c471b,\n        0x047d84, 0xc72493, 0xc9bebc, 0x100d4c, 0x3e42b6, 0x657e2a, 0xd6faec, 0x475817\n    ],\n    /*\n  _key:\n  [0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,\n   0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,\n   0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,\n   0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,\n   0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,\n   0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,\n   0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,\n   0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,\n   0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,\n   0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,\n   0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,\n   0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,\n   0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,\n   0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,\n   0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,\n   0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,\n   0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,\n   0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,\n   0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,\n   0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817],\n  */\n    /**\n     * Function to precompute _init and _key.\n     * @private\n     */\n    _precompute: function () {\n        // XXX: This code is for precomputing the SHA256 constants, change for\n        //      SHA512 and re-enable.\n        var i = 0, prime = 2, factor, isPrime;\n        function frac(x) { return (x - Math.floor(x)) * 0x100000000 | 0; }\n        function frac2(x) { return (x - Math.floor(x)) * 0x10000000000 & 0xff; }\n        for (; i < 80; prime++) {\n            isPrime = true;\n            for (factor = 2; factor * factor <= prime; factor++) {\n                if (prime % factor === 0) {\n                    isPrime = false;\n                    break;\n                }\n            }\n            if (isPrime) {\n                if (i < 8) {\n                    this._init[i * 2] = frac(Math.pow(prime, 1 / 2));\n                    this._init[i * 2 + 1] = (frac2(Math.pow(prime, 1 / 2)) << 24) | this._initr[i];\n                }\n                this._key[i * 2] = frac(Math.pow(prime, 1 / 3));\n                this._key[i * 2 + 1] = (frac2(Math.pow(prime, 1 / 3)) << 24) | this._keyr[i];\n                i++;\n            }\n        }\n    },\n    /**\n     * Perform one cycle of SHA-512.\n     * @param {Uint32Array|bitArray} words one block of words.\n     * @private\n     */\n    _block: function (words) {\n        var i, wrh, wrl, h = this._h, k = this._key, h0h = h[0], h0l = h[1], h1h = h[2], h1l = h[3], h2h = h[4], h2l = h[5], h3h = h[6], h3l = h[7], h4h = h[8], h4l = h[9], h5h = h[10], h5l = h[11], h6h = h[12], h6l = h[13], h7h = h[14], h7l = h[15];\n        var w;\n        if (typeof Uint32Array !== 'undefined') {\n            // When words is passed to _block, it has 32 elements. SHA512 _block\n            // function extends words with new elements (at the end there are 160 elements).\n            // The problem is that if we use Uint32Array instead of Array,\n            // the length of Uint32Array cannot be changed. Thus, we replace words with a\n            // normal Array here.\n            w = Array(160); // do not use Uint32Array here as the instantiation is slower\n            for (var j = 0; j < 32; j++) {\n                w[j] = words[j];\n            }\n        }\n        else {\n            w = words;\n        }\n        // Working variables\n        var ah = h0h, al = h0l, bh = h1h, bl = h1l, ch = h2h, cl = h2l, dh = h3h, dl = h3l, eh = h4h, el = h4l, fh = h5h, fl = h5l, gh = h6h, gl = h6l, hh = h7h, hl = h7l;\n        for (i = 0; i < 80; i++) {\n            // load up the input word for this round\n            if (i < 16) {\n                wrh = w[i * 2];\n                wrl = w[i * 2 + 1];\n            }\n            else {\n                // Gamma0\n                var gamma0xh = w[(i - 15) * 2];\n                var gamma0xl = w[(i - 15) * 2 + 1];\n                var gamma0h = ((gamma0xl << 31) | (gamma0xh >>> 1)) ^\n                    ((gamma0xl << 24) | (gamma0xh >>> 8)) ^\n                    (gamma0xh >>> 7);\n                var gamma0l = ((gamma0xh << 31) | (gamma0xl >>> 1)) ^\n                    ((gamma0xh << 24) | (gamma0xl >>> 8)) ^\n                    ((gamma0xh << 25) | (gamma0xl >>> 7));\n                // Gamma1\n                var gamma1xh = w[(i - 2) * 2];\n                var gamma1xl = w[(i - 2) * 2 + 1];\n                var gamma1h = ((gamma1xl << 13) | (gamma1xh >>> 19)) ^\n                    ((gamma1xh << 3) | (gamma1xl >>> 29)) ^\n                    (gamma1xh >>> 6);\n                var gamma1l = ((gamma1xh << 13) | (gamma1xl >>> 19)) ^\n                    ((gamma1xl << 3) | (gamma1xh >>> 29)) ^\n                    ((gamma1xh << 26) | (gamma1xl >>> 6));\n                // Shortcuts\n                var wr7h = w[(i - 7) * 2];\n                var wr7l = w[(i - 7) * 2 + 1];\n                var wr16h = w[(i - 16) * 2];\n                var wr16l = w[(i - 16) * 2 + 1];\n                // W(round) = gamma0 + W(round - 7) + gamma1 + W(round - 16)\n                wrl = gamma0l + wr7l;\n                wrh = gamma0h + wr7h + ((wrl >>> 0) < (gamma0l >>> 0) ? 1 : 0);\n                wrl += gamma1l;\n                wrh += gamma1h + ((wrl >>> 0) < (gamma1l >>> 0) ? 1 : 0);\n                wrl += wr16l;\n                wrh += wr16h + ((wrl >>> 0) < (wr16l >>> 0) ? 1 : 0);\n            }\n            w[i * 2] = wrh |= 0;\n            w[i * 2 + 1] = wrl |= 0;\n            // Ch\n            var chh = (eh & fh) ^ (~eh & gh);\n            var chl = (el & fl) ^ (~el & gl);\n            // Maj\n            var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);\n            var majl = (al & bl) ^ (al & cl) ^ (bl & cl);\n            // Sigma0\n            var sigma0h = ((al << 4) | (ah >>> 28)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));\n            var sigma0l = ((ah << 4) | (al >>> 28)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));\n            // Sigma1\n            var sigma1h = ((el << 18) | (eh >>> 14)) ^ ((el << 14) | (eh >>> 18)) ^ ((eh << 23) | (el >>> 9));\n            var sigma1l = ((eh << 18) | (el >>> 14)) ^ ((eh << 14) | (el >>> 18)) ^ ((el << 23) | (eh >>> 9));\n            // K(round)\n            var krh = k[i * 2];\n            var krl = k[i * 2 + 1];\n            // t1 = h + sigma1 + ch + K(round) + W(round)\n            var t1l = hl + sigma1l;\n            var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);\n            t1l += chl;\n            t1h += chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);\n            t1l += krl;\n            t1h += krh + ((t1l >>> 0) < (krl >>> 0) ? 1 : 0);\n            t1l = t1l + wrl | 0; // FF32..FF34 perf issue https://bugzilla.mozilla.org/show_bug.cgi?id=1054972\n            t1h += wrh + ((t1l >>> 0) < (wrl >>> 0) ? 1 : 0);\n            // t2 = sigma0 + maj\n            var t2l = sigma0l + majl;\n            var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);\n            // Update working variables\n            hh = gh;\n            hl = gl;\n            gh = fh;\n            gl = fl;\n            fh = eh;\n            fl = el;\n            el = (dl + t1l) | 0;\n            eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;\n            dh = ch;\n            dl = cl;\n            ch = bh;\n            cl = bl;\n            bh = ah;\n            bl = al;\n            al = (t1l + t2l) | 0;\n            ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;\n        }\n        // Intermediate hash\n        h0l = h[1] = (h0l + al) | 0;\n        h[0] = (h0h + ah + ((h0l >>> 0) < (al >>> 0) ? 1 : 0)) | 0;\n        h1l = h[3] = (h1l + bl) | 0;\n        h[2] = (h1h + bh + ((h1l >>> 0) < (bl >>> 0) ? 1 : 0)) | 0;\n        h2l = h[5] = (h2l + cl) | 0;\n        h[4] = (h2h + ch + ((h2l >>> 0) < (cl >>> 0) ? 1 : 0)) | 0;\n        h3l = h[7] = (h3l + dl) | 0;\n        h[6] = (h3h + dh + ((h3l >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;\n        h4l = h[9] = (h4l + el) | 0;\n        h[8] = (h4h + eh + ((h4l >>> 0) < (el >>> 0) ? 1 : 0)) | 0;\n        h5l = h[11] = (h5l + fl) | 0;\n        h[10] = (h5h + fh + ((h5l >>> 0) < (fl >>> 0) ? 1 : 0)) | 0;\n        h6l = h[13] = (h6l + gl) | 0;\n        h[12] = (h6h + gh + ((h6l >>> 0) < (gl >>> 0) ? 1 : 0)) | 0;\n        h7l = h[15] = (h7l + hl) | 0;\n        h[14] = (h7h + hh + ((h7l >>> 0) < (hl >>> 0) ? 1 : 0)) | 0;\n    }\n};\n/** @fileOverview Javascript SHA-1 implementation.\n *\n * Based on the implementation in RFC 3174, method 1, and on the SJCL\n * SHA-256 implementation.\n *\n * @author Quinn Slack\n */\n/**\n * Context for a SHA-1 operation in progress.\n * @constructor\n */\nsjcl.hash.sha1 = function (hash) {\n    if (hash) {\n        this._h = hash._h.slice(0);\n        this._buffer = hash._buffer.slice(0);\n        this._length = hash._length;\n    }\n    else {\n        this.reset();\n    }\n};\n/**\n * Hash a string or an array of words.\n * @static\n * @param {bitArray|String} data the data to hash.\n * @return {bitArray} The hash value, an array of 5 big-endian words.\n */\nsjcl.hash.sha1.hash = function (data) {\n    return (new sjcl.hash.sha1()).update(data).finalize();\n};\nsjcl.hash.sha1.prototype = {\n    /**\n     * The hash's block size, in bits.\n     * @constant\n     */\n    blockSize: 512,\n    /**\n     * Reset the hash state.\n     * @return this\n     */\n    reset: function () {\n        this._h = this._init.slice(0);\n        this._buffer = [];\n        this._length = 0;\n        return this;\n    },\n    /**\n     * Input several words to the hash.\n     * @param {bitArray|String} data the data to hash.\n     * @return this\n     */\n    update: function (data) {\n        if (typeof data === \"string\") {\n            data = sjcl.codec.utf8String.toBits(data);\n        }\n        var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data), ol = this._length, nl = this._length = ol + sjcl.bitArray.bitLength(data);\n        if (nl > 9007199254740991) {\n            throw new sjcl.exception.invalid(\"Cannot hash more than 2^53 - 1 bits\");\n        }\n        if (typeof Uint32Array !== 'undefined') {\n            var c = new Uint32Array(b);\n            var j = 0;\n            for (i = this.blockSize + ol - ((this.blockSize + ol) & (this.blockSize - 1)); i <= nl; i += this.blockSize) {\n                this._block(c.subarray(16 * j, 16 * (j + 1)));\n                j += 1;\n            }\n            b.splice(0, 16 * j);\n        }\n        else {\n            for (i = this.blockSize + ol - ((this.blockSize + ol) & (this.blockSize - 1)); i <= nl; i += this.blockSize) {\n                this._block(b.splice(0, 16));\n            }\n        }\n        return this;\n    },\n    /**\n     * Complete hashing and output the hash value.\n     * @return {bitArray} The hash value, an array of 5 big-endian words. TODO\n     */\n    finalize: function () {\n        var i, b = this._buffer, h = this._h;\n        // Round out and push the buffer\n        b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]);\n        // Round out the buffer to a multiple of 16 words, less the 2 length words.\n        for (i = b.length + 2; i & 15; i++) {\n            b.push(0);\n        }\n        // append the length\n        b.push(Math.floor(this._length / 0x100000000));\n        b.push(this._length | 0);\n        while (b.length) {\n            this._block(b.splice(0, 16));\n        }\n        this.reset();\n        return h;\n    },\n    /**\n     * The SHA-1 initialization vector.\n     * @private\n     */\n    _init: [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0],\n    /**\n     * The SHA-1 hash key.\n     * @private\n     */\n    _key: [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6],\n    /**\n     * The SHA-1 logical functions f(0), f(1), ..., f(79).\n     * @private\n     */\n    _f: function (t, b, c, d) {\n        if (t <= 19) {\n            return (b & c) | (~b & d);\n        }\n        else if (t <= 39) {\n            return b ^ c ^ d;\n        }\n        else if (t <= 59) {\n            return (b & c) | (b & d) | (c & d);\n        }\n        else if (t <= 79) {\n            return b ^ c ^ d;\n        }\n    },\n    /**\n     * Circular left-shift operator.\n     * @private\n     */\n    _S: function (n, x) {\n        return (x << n) | (x >>> 32 - n);\n    },\n    /**\n     * Perform one cycle of SHA-1.\n     * @param {Uint32Array|bitArray} words one block of words.\n     * @private\n     */\n    _block: function (words) {\n        var t, tmp, a, b, c, d, e, h = this._h;\n        var w;\n        if (typeof Uint32Array !== 'undefined') {\n            // When words is passed to _block, it has 16 elements. SHA1 _block\n            // function extends words with new elements (at the end there are 80 elements).\n            // The problem is that if we use Uint32Array instead of Array,\n            // the length of Uint32Array cannot be changed. Thus, we replace words with a\n            // normal Array here.\n            w = Array(80); // do not use Uint32Array here as the instantiation is slower\n            for (var j = 0; j < 16; j++) {\n                w[j] = words[j];\n            }\n        }\n        else {\n            w = words;\n        }\n        a = h[0];\n        b = h[1];\n        c = h[2];\n        d = h[3];\n        e = h[4];\n        for (t = 0; t <= 79; t++) {\n            if (t >= 16) {\n                w[t] = this._S(1, w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]);\n            }\n            tmp = (this._S(5, a) + this._f(t, b, c, d) + e + w[t] +\n                this._key[Math.floor(t / 20)]) | 0;\n            e = d;\n            d = c;\n            c = this._S(30, b);\n            b = a;\n            a = tmp;\n        }\n        h[0] = (h[0] + a) | 0;\n        h[1] = (h[1] + b) | 0;\n        h[2] = (h[2] + c) | 0;\n        h[3] = (h[3] + d) | 0;\n        h[4] = (h[4] + e) | 0;\n    }\n};\n/** @fileOverview CBC mode implementation\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/**\n * Dangerous: CBC mode with PKCS#5 padding.\n * @namespace\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\nsjcl.mode.cbc = {\n    /** The name of the mode.\n     * @constant\n     */\n    name: \"cbc\",\n    /** Encrypt in CBC mode with PKCS#5 padding.\n     * @param {Object} prp The block cipher.  It must have a block size of 16 bytes.\n     * @param {bitArray} plaintext The plaintext data.\n     * @param {bitArray} iv The initialization value.\n     * @param {bitArray} [adata=[]] The authenticated data.  Must be empty.\n     * @param {boolean} usePadding True if padding shall be used, false otherwise.\n     * @return The encrypted data, an array of bytes.\n     * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.\n     */\n    encrypt: function (prp, plaintext, iv, adata, usePadding) {\n        if (adata && adata.length) {\n            throw new sjcl.exception.invalid(\"cbc can't authenticate data\");\n        }\n        if (sjcl.bitArray.bitLength(iv) !== 128) {\n            throw new sjcl.exception.invalid(\"cbc iv must be 128 bits\");\n        }\n        var i, w = sjcl.bitArray, xor = w._xor4, bl = w.bitLength(plaintext), bp = 0, output = [];\n        if (bl & 7) {\n            throw new sjcl.exception.invalid(\"pkcs#5 padding only works for multiples of a byte\");\n        }\n        for (i = 0; bp + 128 <= bl; i += 4, bp += 128) {\n            /* Encrypt a non-final block */\n            iv = prp.encrypt(xor(iv, plaintext.slice(i, i + 4)));\n            // TUTAO: replaced splice with push because of performance bug in chromium\n            // https://bugs.chromium.org/p/chromium/issues/detail?id=914395&can=1&q=splice&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified\n            //output.splice(i, 0, iv[0], iv[1], iv[2], iv[3]);\n            output.push(iv[0], iv[1], iv[2], iv[3]);\n        }\n        if (usePadding) {\n            /* Construct the pad. */\n            bl = (16 - ((bl >> 3) & 15)) * 0x1010101;\n            /* Pad and encrypt. */\n            iv = prp.encrypt(xor(iv, w.concat(plaintext, [bl, bl, bl, bl]).slice(i, i + 4)));\n            // TUTAO: replaced splice with push because of performance bug in chromium\n            // output.splice(i, 0, iv[0], iv[1], iv[2], iv[3]);\n            output.push(iv[0], iv[1], iv[2], iv[3]);\n        }\n        return output;\n    },\n    /** Decrypt in CBC mode.\n     * @param {Object} prp The block cipher.  It must have a block size of 16 bytes.\n     * @param {bitArray} ciphertext The ciphertext data.\n     * @param {bitArray} iv The initialization value.\n     * @param {bitArray} [adata=[]] The authenticated data.  It must be empty.\n     * @param {boolean} usePadding True if padding shall be used, false otherwise.\n     * @return The decrypted data, an array of bytes.\n     * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.\n     * @throws {sjcl.exception.corrupt} if if the message is corrupt.\n     */\n    decrypt: function (prp, ciphertext, iv, adata, usePadding) {\n        if (adata && adata.length) {\n            throw new sjcl.exception.invalid(\"cbc can't authenticate data\");\n        }\n        if (sjcl.bitArray.bitLength(iv) !== 128) {\n            throw new sjcl.exception.invalid(\"cbc iv must be 128 bits\");\n        }\n        if ((sjcl.bitArray.bitLength(ciphertext) & 127) || !ciphertext.length) {\n            throw new sjcl.exception.corrupt(\"cbc ciphertext must be a positive multiple of the block size\");\n        }\n        var i, w = sjcl.bitArray, xor = w._xor4, bi, bo, output = [];\n        adata = adata || [];\n        for (i = 0; i < ciphertext.length; i += 4) {\n            bi = ciphertext.slice(i, i + 4);\n            bo = xor(iv, prp.decrypt(bi));\n            // TUTAO: replaced splice with push because of performance bug in chromium\n            // https://bugs.chromium.org/p/chromium/issues/detail?id=914395&can=1&q=splice&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified\n            //output.splice(i, 0, bo[0], bo[1], bo[2], bo[3]);\n            output.push(bo[0], bo[1], bo[2], bo[3]);\n            iv = bi;\n        }\n        if (usePadding) {\n            /* check and remove the pad */\n            bi = output[i - 1] & 255;\n            if (bi === 0 || bi > 16) {\n                throw new sjcl.exception.corrupt(\"pkcs#5 padding corrupt\");\n            }\n            bo = bi * 0x1010101;\n            if (!w.equal(w.bitSlice([bo, bo, bo, bo], 0, bi * 8), w.bitSlice(output, output.length * 32 - bi * 8, output.length * 32))) {\n                throw new sjcl.exception.corrupt(\"pkcs#5 padding corrupt\");\n            }\n            return w.bitSlice(output, 0, output.length * 32 - bi * 8);\n        }\n        else {\n            return output;\n        }\n    }\n};\n/** @fileOverview GCM mode implementation.\n *\n * @author Juho Vähä-Herttua\n */\n/**\n * Galois/Counter mode.\n * @namespace\n */\nsjcl.mode.gcm = {\n    /**\n     * The name of the mode.\n     * @constant\n     */\n    name: \"gcm\",\n    /** Encrypt in GCM mode.\n     * @static\n     * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.\n     * @param {bitArray} plaintext The plaintext data.\n     * @param {bitArray} iv The initialization value.\n     * @param {bitArray} [adata=[]] The authenticated data.\n     * @param {Number} [tlen=128] The desired tag length, in bits.\n     * @return {bitArray} The encrypted data, an array of bytes.\n     */\n    encrypt: function (prf, plaintext, iv, adata, tlen) {\n        var out, data = plaintext.slice(0), w = sjcl.bitArray;\n        tlen = tlen || 128;\n        adata = adata || [];\n        // encrypt and tag\n        out = sjcl.mode.gcm._ctrMode(true, prf, data, adata, iv, tlen);\n        return w.concat(out.data, out.tag);\n    },\n    /** Decrypt in GCM mode.\n     * @static\n     * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.\n     * @param {bitArray} ciphertext The ciphertext data.\n     * @param {bitArray} iv The initialization value.\n     * @param {bitArray} [adata=[]] The authenticated data.\n     * @param {Number} [tlen=128] The desired tag length, in bits.\n     * @return {bitArray} The decrypted data.\n     */\n    decrypt: function (prf, ciphertext, iv, adata, tlen) {\n        var out, data = ciphertext.slice(0), tag, w = sjcl.bitArray, l = w.bitLength(data);\n        tlen = tlen || 128;\n        adata = adata || [];\n        // Slice tag out of data\n        if (tlen <= l) {\n            tag = w.bitSlice(data, l - tlen);\n            data = w.bitSlice(data, 0, l - tlen);\n        }\n        else {\n            tag = data;\n            data = [];\n        }\n        // decrypt and tag\n        out = sjcl.mode.gcm._ctrMode(false, prf, data, adata, iv, tlen);\n        if (!w.equal(out.tag, tag)) {\n            throw new sjcl.exception.corrupt(\"gcm: tag doesn't match\");\n        }\n        return out.data;\n    },\n    /* Compute the galois multiplication of X and Y\n     * @private\n     */\n    _galoisMultiply: function (x, y) {\n        var i, j, xi, Zi, Vi, lsb_Vi, w = sjcl.bitArray, xor = w._xor4;\n        Zi = [0, 0, 0, 0];\n        Vi = y.slice(0);\n        // Block size is 128 bits, run 128 times to get Z_128\n        for (i = 0; i < 128; i++) {\n            xi = (x[Math.floor(i / 32)] & (1 << (31 - i % 32))) !== 0;\n            if (xi) {\n                // Z_i+1 = Z_i ^ V_i\n                Zi = xor(Zi, Vi);\n            }\n            // Store the value of LSB(V_i)\n            lsb_Vi = (Vi[3] & 1) !== 0;\n            // V_i+1 = V_i >> 1\n            for (j = 3; j > 0; j--) {\n                Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31);\n            }\n            Vi[0] = Vi[0] >>> 1;\n            // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R\n            if (lsb_Vi) {\n                Vi[0] = Vi[0] ^ (0xe1 << 24);\n            }\n        }\n        return Zi;\n    },\n    _ghash: function (H, Y0, data) {\n        var Yi, i, l = data.length;\n        Yi = Y0.slice(0);\n        for (i = 0; i < l; i += 4) {\n            Yi[0] ^= 0xffffffff & data[i];\n            Yi[1] ^= 0xffffffff & data[i + 1];\n            Yi[2] ^= 0xffffffff & data[i + 2];\n            Yi[3] ^= 0xffffffff & data[i + 3];\n            Yi = sjcl.mode.gcm._galoisMultiply(Yi, H);\n        }\n        return Yi;\n    },\n    /** GCM CTR mode.\n     * Encrypt or decrypt data and tag with the prf in GCM-style CTR mode.\n     * @param {Boolean} encrypt True if encrypt, false if decrypt.\n     * @param {Object} prf The PRF.\n     * @param {bitArray} data The data to be encrypted or decrypted.\n     * @param {bitArray} iv The initialization vector.\n     * @param {bitArray} adata The associated data to be tagged.\n     * @param {Number} tlen The length of the tag, in bits.\n     */\n    _ctrMode: function (encrypt, prf, data, adata, iv, tlen) {\n        var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w = sjcl.bitArray;\n        // Calculate data lengths\n        l = data.length;\n        bl = w.bitLength(data);\n        abl = w.bitLength(adata);\n        ivbl = w.bitLength(iv);\n        // Calculate the parameters\n        H = prf.encrypt([0, 0, 0, 0]);\n        if (ivbl === 96) {\n            J0 = iv.slice(0);\n            J0 = w.concat(J0, [1]);\n        }\n        else {\n            J0 = sjcl.mode.gcm._ghash(H, [0, 0, 0, 0], iv);\n            J0 = sjcl.mode.gcm._ghash(H, J0, [0, 0, Math.floor(ivbl / 0x100000000), ivbl & 0xffffffff]);\n        }\n        S0 = sjcl.mode.gcm._ghash(H, [0, 0, 0, 0], adata);\n        // Initialize ctr and tag\n        ctr = J0.slice(0);\n        tag = S0.slice(0);\n        // If decrypting, calculate hash\n        if (!encrypt) {\n            tag = sjcl.mode.gcm._ghash(H, S0, data);\n        }\n        // Encrypt all the data\n        for (i = 0; i < l; i += 4) {\n            ctr[3]++;\n            enc = prf.encrypt(ctr);\n            data[i] ^= enc[0];\n            data[i + 1] ^= enc[1];\n            data[i + 2] ^= enc[2];\n            data[i + 3] ^= enc[3];\n        }\n        data = w.clamp(data, bl);\n        // If encrypting, calculate hash\n        if (encrypt) {\n            tag = sjcl.mode.gcm._ghash(H, S0, data);\n        }\n        // Calculate last block from bit lengths, ugly because bitwise operations are 32-bit\n        last = [\n            Math.floor(abl / 0x100000000), abl & 0xffffffff,\n            Math.floor(bl / 0x100000000), bl & 0xffffffff\n        ];\n        // Calculate the final tag block\n        tag = sjcl.mode.gcm._ghash(H, tag, last);\n        enc = prf.encrypt(J0);\n        tag[0] ^= enc[0];\n        tag[1] ^= enc[1];\n        tag[2] ^= enc[2];\n        tag[3] ^= enc[3];\n        return { tag: w.bitSlice(tag, 0, tlen), data: data };\n    }\n};\n/** @fileOverview HMAC implementation.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n */\n/** HMAC with the specified hash function.\n * @constructor\n * @param {bitArray} key the key for HMAC.\n * @param {Object} [Hash=sjcl.hash.sha256] The hash function to use.\n */\nsjcl.misc.hmac = function (key, Hash) {\n    this._hash = Hash = Hash || sjcl.hash.sha256;\n    var exKey = [[], []], i, bs = Hash.prototype.blockSize / 32;\n    this._baseHash = [new Hash(), new Hash()];\n    if (key.length > bs) {\n        key = Hash.hash(key);\n    }\n    for (i = 0; i < bs; i++) {\n        exKey[0][i] = key[i] ^ 0x36363636;\n        exKey[1][i] = key[i] ^ 0x5C5C5C5C;\n    }\n    this._baseHash[0].update(exKey[0]);\n    this._baseHash[1].update(exKey[1]);\n    this._resultHash = new Hash(this._baseHash[0]);\n};\n/** HMAC with the specified hash function.  Also called encrypt since it's a prf.\n * @param {bitArray|String} data The data to mac.\n */\nsjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {\n    if (!this._updated) {\n        this.update(data);\n        return this.digest(data);\n    }\n    else {\n        throw new sjcl.exception.invalid(\"encrypt on already updated hmac called!\");\n    }\n};\nsjcl.misc.hmac.prototype.reset = function () {\n    this._resultHash = new this._hash(this._baseHash[0]);\n    this._updated = false;\n};\nsjcl.misc.hmac.prototype.update = function (data) {\n    this._updated = true;\n    this._resultHash.update(data);\n};\nsjcl.misc.hmac.prototype.digest = function () {\n    var w = this._resultHash.finalize(), result = new (this._hash)(this._baseHash[1]).update(w).finalize();\n    this.reset();\n    return result;\n};\n/** @fileOverview Random number generator.\n *\n * @author Emily Stark\n * @author Mike Hamburg\n * @author Dan Boneh\n * @author Michael Brooks\n * @author Steve Thomas\n */\n/**\n * @class Random number generator\n * @description\n * <b>Use sjcl.random as a singleton for this class!</b>\n * <p>\n * This random number generator is a derivative of Ferguson and Schneier's\n * generator Fortuna.  It collects entropy from various events into several\n * pools, implemented by streaming SHA-256 instances.  It differs from\n * ordinary Fortuna in a few ways, though.\n * </p>\n *\n * <p>\n * Most importantly, it has an entropy estimator.  This is present because\n * there is a strong conflict here between making the generator available\n * as soon as possible, and making sure that it doesn't \"run on empty\".\n * In Fortuna, there is a saved state file, and the system is likely to have\n * time to warm up.\n * </p>\n *\n * <p>\n * Second, because users are unlikely to stay on the page for very long,\n * and to speed startup time, the number of pools increases logarithmically:\n * a new pool is created when the previous one is actually used for a reseed.\n * This gives the same asymptotic guarantees as Fortuna, but gives more\n * entropy to early reseeds.\n * </p>\n *\n * <p>\n * The entire mechanism here feels pretty klunky.  Furthermore, there are\n * several improvements that should be made, including support for\n * dedicated cryptographic functions that may be present in some browsers;\n * state files in local storage; cookies containing randomness; etc.  So\n * look for improvements in future versions.\n * </p>\n * @constructor\n */\nsjcl.prng = function (defaultParanoia) {\n    /* private */\n    this._pools = [new sjcl.hash.sha256()];\n    this._poolEntropy = [0];\n    this._reseedCount = 0;\n    this._robins = {};\n    this._eventId = 0;\n    this._collectorIds = {};\n    this._collectorIdNext = 0;\n    this._strength = 0;\n    this._poolStrength = 0;\n    this._nextReseed = 0;\n    this._key = [0, 0, 0, 0, 0, 0, 0, 0];\n    this._counter = [0, 0, 0, 0];\n    // this._cipher = undefined;\n    this._defaultParanoia = defaultParanoia;\n    // /* event listener stuff */\n    // this._collectorsStarted = false;\n    // this._callbacks = {progress: {}, seeded: {}};\n    // this._callbackI = 0;\n    /* constants */\n    this._NOT_READY = 0;\n    this._READY = 1;\n    this._REQUIRES_RESEED = 2;\n    this._MAX_WORDS_PER_BURST = 65536;\n    this._PARANOIA_LEVELS = [0, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024];\n    this._MILLISECONDS_PER_RESEED = 30000;\n    this._BITS_PER_RESEED = 80;\n};\nsjcl.prng.prototype = {\n    /** Generate several random words, and return them in an array.\n     * A word consists of 32 bits (4 bytes)\n     * @param {Number} nwords The number of words to generate.\n     */\n    randomWords: function (nwords, paranoia) {\n        var out = [], i, readiness = this.isReady(paranoia), g;\n        if (readiness === this._NOT_READY) {\n            throw new sjcl.exception.notReady(\"generator isn't seeded\");\n        }\n        else if (readiness & this._REQUIRES_RESEED) {\n            this._reseedFromPools(!(readiness & this._READY));\n        }\n        for (i = 0; i < nwords; i += 4) {\n            if ((i + 1) % this._MAX_WORDS_PER_BURST === 0) {\n                this._gate();\n            }\n            g = this._gen4words();\n            out.push(g[0], g[1], g[2], g[3]);\n        }\n        this._gate();\n        return out.slice(0, nwords);\n    },\n    // setDefaultParanoia: function (paranoia, allowZeroParanoia) {\n    // \tif (paranoia === 0 && allowZeroParanoia\n    // \t\t!== \"Setting paranoia=0 will ruin your security; use it only for testing\") {\n    // \t\tthrow new sjcl.exception.invalid(\"Setting paranoia=0 will ruin your security; use it only for testing\");\n    // \t}\n    //\n    // \tthis._defaultParanoia = paranoia;\n    // },\n    /**\n     * Add entropy to the pools.\n     * @param data The entropic value.  Should be a 32-bit integer, array of 32-bit integers, or string\n     * @param {Number} estimatedEntropy The estimated entropy of data, in bits\n     * @param {String} source The source of the entropy, eg \"mouse\"\n     */\n    addEntropy: function (data, estimatedEntropy, source) {\n        source = source || \"user\";\n        var id, i, tmp, t = (new Date()).valueOf(), robin = this._robins[source], oldReady = this.isReady(), err = 0, objName;\n        id = this._collectorIds[source];\n        if (id === undefined) {\n            id = this._collectorIds[source] = this._collectorIdNext++;\n        }\n        if (robin === undefined) {\n            robin = this._robins[source] = 0;\n        }\n        this._robins[source] = (this._robins[source] + 1) % this._pools.length;\n        switch (typeof (data)) {\n            case \"number\":\n                if (estimatedEntropy === undefined) {\n                    estimatedEntropy = 1;\n                }\n                this._pools[robin].update([id, this._eventId++, 1, estimatedEntropy, t, 1, data | 0]);\n                break;\n            case \"object\":\n                objName = Object.prototype.toString.call(data);\n                if (objName === \"[object Uint32Array]\") {\n                    tmp = [];\n                    for (i = 0; i < data.length; i++) {\n                        tmp.push(data[i]);\n                    }\n                    data = tmp;\n                }\n                else {\n                    if (objName !== \"[object Array]\") {\n                        err = 1;\n                    }\n                    for (i = 0; i < data.length && !err; i++) {\n                        if (typeof (data[i]) !== \"number\") {\n                            err = 1;\n                        }\n                    }\n                }\n                if (!err) {\n                    if (estimatedEntropy === undefined) {\n                        /* horrible entropy estimator */\n                        estimatedEntropy = 0;\n                        for (i = 0; i < data.length; i++) {\n                            tmp = data[i];\n                            while (tmp > 0) {\n                                estimatedEntropy++;\n                                tmp = tmp >>> 1;\n                            }\n                        }\n                    }\n                    this._pools[robin].update([id, this._eventId++, 2, estimatedEntropy, t, data.length].concat(data));\n                }\n                break;\n            case \"string\":\n                if (estimatedEntropy === undefined) {\n                    /* English text has just over 1 bit per character of entropy.\n                     * But this might be HTML or something, and have far less\n                     * entropy than English...  Oh well, let's just say one bit.\n                     */\n                    estimatedEntropy = data.length;\n                }\n                this._pools[robin].update([id, this._eventId++, 3, estimatedEntropy, t, data.length]);\n                this._pools[robin].update(data);\n                break;\n            default:\n                err = 1;\n        }\n        if (err) {\n            throw new sjcl.exception.bug(\"random: addEntropy only supports number, array of numbers or string\");\n        }\n        /* record the new strength */\n        this._poolEntropy[robin] += estimatedEntropy;\n        this._poolStrength += estimatedEntropy;\n        /* fire off events */\n        /* TUTAO.arm: removed bad implementation: _fireEvent calls static randomizer instance\n         if (oldReady === this._NOT_READY) {\n         if (this.isReady() !== this._NOT_READY) {\n         this._fireEvent(\"seeded\", Math.max(this._strength, this._poolStrength));\n         }\n         this._fireEvent(\"progress\", this.getProgress());\n         }*/\n    },\n    /** Is the generator ready? */\n    isReady: function (paranoia) {\n        var entropyRequired = this._PARANOIA_LEVELS[(paranoia !== undefined) ? paranoia : this._defaultParanoia];\n        if (this._strength && this._strength >= entropyRequired) {\n            return (this._poolEntropy[0] > this._BITS_PER_RESEED && (new Date()).valueOf() > this._nextReseed) ?\n                this._REQUIRES_RESEED | this._READY :\n                this._READY;\n        }\n        else {\n            return (this._poolStrength >= entropyRequired) ?\n                this._REQUIRES_RESEED | this._NOT_READY :\n                this._NOT_READY;\n        }\n    },\n    /** Generate 4 random words, no reseed, no gate.\n     * @private\n     */\n    _gen4words: function () {\n        for (var i = 0; i < 4; i++) {\n            this._counter[i] = this._counter[i] + 1 | 0;\n            if (this._counter[i]) {\n                break;\n            }\n        }\n        return this._cipher.encrypt(this._counter);\n    },\n    /* Rekey the AES instance with itself after a request, or every _MAX_WORDS_PER_BURST words.\n     * @private\n     */\n    _gate: function () {\n        this._key = this._gen4words().concat(this._gen4words());\n        this._cipher = new sjcl.cipher.aes(this._key);\n    },\n    /** Reseed the generator with the given words\n     * @private\n     */\n    _reseed: function (seedWords) {\n        this._key = sjcl.hash.sha256.hash(this._key.concat(seedWords));\n        this._cipher = new sjcl.cipher.aes(this._key);\n        for (var i = 0; i < 4; i++) {\n            this._counter[i] = this._counter[i] + 1 | 0;\n            if (this._counter[i]) {\n                break;\n            }\n        }\n    },\n    /** reseed the data from the entropy pools\n     * @param full If set, use all the entropy pools in the reseed.\n     */\n    _reseedFromPools: function (full) {\n        var reseedData = [], strength = 0, i;\n        this._nextReseed = reseedData[0] =\n            (new Date()).valueOf() + this._MILLISECONDS_PER_RESEED;\n        for (i = 0; i < 16; i++) {\n            /* On some browsers, this is cryptographically random.  So we might\n             * as well toss it in the pot and stir...\n             */\n            reseedData.push(Math.random() * 0x100000000 | 0);\n        }\n        for (i = 0; i < this._pools.length; i++) {\n            reseedData = reseedData.concat(this._pools[i].finalize());\n            strength += this._poolEntropy[i];\n            this._poolEntropy[i] = 0;\n            if (!full && (this._reseedCount & (1 << i))) {\n                break;\n            }\n        }\n        /* if we used the last pool, push a new one onto the stack */\n        if (this._reseedCount >= 1 << this._pools.length) {\n            this._pools.push(new sjcl.hash.sha256());\n            this._poolEntropy.push(0);\n        }\n        /* how strong was this reseed? */\n        this._poolStrength -= strength;\n        if (strength > this._strength) {\n            this._strength = strength;\n        }\n        this._reseedCount++;\n        this._reseed(reseedData);\n    },\n};\n/** an instance for the prng.\n * @see sjcl.prng\n */\n/* TUTAO.arm: removed static randomizer instance because we have our own\nsjcl.random = new sjcl.prng(6);\n\n(function () {\n    // function for getting nodejs crypto module. catches and ignores errors.\n    function getCryptoModule() {\n        try {\n            return require('crypto');\n        }\n        catch (e) {\n            return null;\n        }\n    }\n\n    try {\n        var buf, crypt, ab;\n\n        // get cryptographically strong entropy depending on runtime environment\n        if (typeof module !== 'undefined' && module.exports && (crypt = getCryptoModule()) && crypt.randomBytes) {\n            buf = crypt.randomBytes(1024 / 8);\n            buf = new Uint32Array(new Uint8Array(buf).buffer);\n            sjcl.random.addEntropy(buf, 1024, \"crypto.randomBytes\");\n\n        } else if (typeof window !== 'undefined' && typeof Uint32Array !== 'undefined') {\n            ab = new Uint32Array(32);\n            if (window.crypto && window.crypto.getRandomValues) {\n                window.crypto.getRandomValues(ab);\n            } else if (window.msCrypto && window.msCrypto.getRandomValues) {\n                window.msCrypto.getRandomValues(ab);\n            } else {\n                return;\n            }\n\n            // get cryptographically strong entropy in Webkit\n            sjcl.random.addEntropy(ab, 1024, \"crypto.getRandomValues\");\n\n        } else {\n            // no getRandomValues :-(\n        }\n    } catch (e) {\n        if (typeof window !== 'undefined' && window.console) {\n            console.log(\"There was an error collecting entropy from the browser:\");\n            console.log(e);\n            //we do not want the library to fail due to randomness not being maintained.\n        }\n    }\n }());*/\n/**\n * ArrayBuffer\n * @namespace\n */\nsjcl.codec.arrayBuffer = {\n    /** Convert from a bitArray to an ArrayBuffer.\n     * Will default to 8byte padding if padding is undefined*/\n    fromBits: function (arr, padding, padding_count) {\n        var out, i, ol, tmp, smallest;\n        padding = padding == undefined ? true : padding;\n        padding_count = padding_count || 8;\n        if (arr.length === 0) {\n            return new ArrayBuffer(0);\n        }\n        ol = sjcl.bitArray.bitLength(arr) / 8;\n        //check to make sure the bitLength is divisible by 8, if it isn't\n        //we can't do anything since arraybuffers work with bytes, not bits\n        if (sjcl.bitArray.bitLength(arr) % 8 !== 0) {\n            throw new sjcl.exception.invalid(\"Invalid bit size, must be divisble by 8 to fit in an arraybuffer correctly\");\n        }\n        if (padding && ol % padding_count !== 0) {\n            ol += padding_count - (ol % padding_count);\n        }\n        //padded temp for easy copying\n        tmp = new DataView(new ArrayBuffer(arr.length * 4));\n        for (i = 0; i < arr.length; i++) {\n            tmp.setUint32(i * 4, (arr[i] << 32)); //get rid of the higher bits\n        }\n        //now copy the final message if we are not going to 0 pad\n        out = new DataView(new ArrayBuffer(ol));\n        //save a step when the tmp and out bytelength are ===\n        if (out.byteLength === tmp.byteLength) {\n            return tmp.buffer;\n        }\n        smallest = tmp.byteLength < out.byteLength ? tmp.byteLength : out.byteLength;\n        for (i = 0; i < smallest; i++) {\n            out.setUint8(i, tmp.getUint8(i));\n        }\n        return out.buffer;\n    },\n    toBits: function (buffer) {\n        var i, out = [], len, inView, tmp;\n        if (buffer.byteLength === 0) {\n            return [];\n        }\n        inView = new DataView(buffer);\n        len = inView.byteLength - inView.byteLength % 4;\n        for (var i = 0; i < len; i += 4) {\n            out.push(inView.getUint32(i));\n        }\n        if (inView.byteLength % 4 != 0) {\n            tmp = new DataView(new ArrayBuffer(4));\n            for (var i = 0, l = inView.byteLength % 4; i < l; i++) {\n                //we want the data to the right, because partial slices off the starting bits\n                tmp.setUint8(i + 4 - l, inView.getUint8(len + i)); // big-endian,\n            }\n            out.push(sjcl.bitArray.partial((inView.byteLength % 4) * 8, tmp.getUint32(0)));\n        }\n        return out;\n    },\n};\nexport default sjcl;\n","// TODO reconcile with CryptoError in tutanota-3\nexport class CryptoError extends Error {\n    constructor(message, error) {\n        super(error ? message + \"> \" + (error.stack ? error.stack : error.message) : message);\n    }\n}\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nimport { CryptoError } from \"../misc/CryptoError.js\";\n/**\n * This Interface provides an abstraction of the random number generator implementation.\n */\nexport class Randomizer {\n    constructor() {\n        this.random = new sjcl.prng(6);\n    }\n    /**\n     * Adds entropy to the random number generator algorithm\n     * @param entropyCache with: number Any number value, entropy The amount of entropy in the number in bit,\n     * source The source of the number.\n     */\n    addEntropy(entropyCache) {\n        entropyCache.forEach((entry) => {\n            this.random.addEntropy(entry.data, entry.entropy, entry.source);\n        });\n        return Promise.resolve();\n    }\n    addStaticEntropy(bytes) {\n        bytes.forEach((byte) => {\n            this.random.addEntropy(byte, 8, \"static\");\n        });\n    }\n    /**\n     * Not used currently because we always have enough entropy using getRandomValues()\n     */\n    isReady() {\n        return this.random.isReady() !== 0;\n    }\n    /**\n     * Generates random data. The function initRandomDataGenerator must have been called prior to the first call to this function.\n     * @param nbrOfBytes The number of bytes the random data shall have.\n     * @return A hex coded string of random data.\n     * @throws {CryptoError} if the randomizer is not seeded (isReady == false)\n     */\n    generateRandomData(nbrOfBytes) {\n        try {\n            // read the minimal number of words to get nbrOfBytes\n            let nbrOfWords = Math.floor((nbrOfBytes + 3) / 4);\n            let words = this.random.randomWords(nbrOfWords);\n            let arrayBuffer = sjcl.codec.arrayBuffer.fromBits(words, false);\n            // simply cut off the exceeding bytes\n            return new Uint8Array(arrayBuffer, 0, nbrOfBytes); // truncate the arraybuffer as precaution\n        }\n        catch (e) {\n            throw new CryptoError(\"error during random number generation\", e);\n        }\n    }\n    /**\n     * Generate a number that fits in the range of an n-byte integer\n     */\n    generateRandomNumber(nbrOfBytes) {\n        const bytes = this.generateRandomData(nbrOfBytes);\n        let result = 0;\n        for (let i = 0; i < bytes.length; ++i) {\n            result += bytes[i] << (i * 8);\n        }\n        return result;\n    }\n}\n// TODO singleton should be created in the app?\n// the randomizer instance (singleton) that should be used throughout the app\nexport const random = new Randomizer();\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nconst sha256 = new sjcl.hash.sha256();\nexport const SHA256_HASH_LENGTH_BYTES = 32;\n/**\n * Create the hash of the given data.\n * @param uint8Array The bytes.\n * @return The hash.\n */\nexport function sha256Hash(uint8Array) {\n    try {\n        sha256.update(sjcl.codec.arrayBuffer.toBits(uint8Array.buffer));\n        return new Uint8Array(sjcl.codec.arrayBuffer.fromBits(sha256.finalize(), false));\n    }\n    finally {\n        sha256.reset();\n    }\n}\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nimport { base64ToBase64Url, base64ToUint8Array, concat, hexToUint8Array, uint8ArrayToArrayBuffer, uint8ArrayToBase64 } from \"@tutao/tutanota-utils\";\nimport { CryptoError } from \"./CryptoError.js\";\nimport { sha256Hash } from \"../hashes/Sha256.js\";\nconst PADDING_BLOCK_LENGTH = 16; // same for aes128 and aes256 as the block size is always 16 byte\nexport function padAes(bytes) {\n    let paddingLength = PADDING_BLOCK_LENGTH - (bytes.byteLength % PADDING_BLOCK_LENGTH);\n    let padding = new Uint8Array(paddingLength);\n    padding.fill(paddingLength);\n    return concat(bytes, padding);\n}\nexport function unpadAes(bytes) {\n    let paddingLength = bytes[bytes.byteLength - 1];\n    if (paddingLength === 0 || paddingLength > bytes.byteLength || paddingLength > PADDING_BLOCK_LENGTH) {\n        throw new CryptoError(\"invalid padding: \" + paddingLength);\n    }\n    let length = bytes.byteLength - paddingLength;\n    let result = new Uint8Array(length);\n    result.set(bytes.subarray(0, length));\n    return result;\n}\n/**\n * Creates the auth verifier from the password key.\n * @param passwordKey The key.\n * @returns The auth verifier\n */\nexport function createAuthVerifier(passwordKey) {\n    // FIXME Compatibility Test\n    return sha256Hash(bitArrayToUint8Array(passwordKey));\n}\nexport function createAuthVerifierAsBase64Url(passwordKey) {\n    return base64ToBase64Url(uint8ArrayToBase64(createAuthVerifier(passwordKey)));\n}\n/**\n * Provides the information if a key is 128 or 256 bit length.\n * @param key The key.\n * @returns True if the key length is 128, false if the key length is 256 bit.\n * @throws If the key is not 128 bit and not 256 bit.\n */\nexport function checkIs128BitKey(key) {\n    let bitLength = sjcl.bitArray.bitLength(key);\n    if (bitLength === 128) {\n        return true;\n    }\n    else if (bitLength === 256) {\n        return false;\n    }\n    else {\n        throw new CryptoError(\"invalid key bit length: \" + bitLength);\n    }\n}\n/**\n * Converts the given BitArray (SJCL) to an Uint8Array.\n * @param bits The BitArray.\n * @return The uint8array.\n */\nexport function bitArrayToUint8Array(bits) {\n    return new Uint8Array(sjcl.codec.arrayBuffer.fromBits(bits, false));\n}\n/**\n * Converts the given uint8array to a BitArray (SJCL).\n * @param uint8Array The uint8Array key.\n * @return The key.\n */\nexport function uint8ArrayToBitArray(uint8Array) {\n    return sjcl.codec.arrayBuffer.toBits(uint8ArrayToArrayBuffer(uint8Array));\n}\n/**\n * Converts the given key to a base64 coded string.\n * @param key The key.\n * @return The base64 coded string representation of the key.\n */\nexport function keyToBase64(key) {\n    return sjcl.codec.base64.fromBits(key);\n}\n/**\n * Converts the given base64 coded string to a key.\n * @param base64 The base64 coded string representation of the key.\n * @return The key.\n * @throws {CryptoError} If the conversion fails.\n */\nexport function base64ToKey(base64) {\n    try {\n        return sjcl.codec.base64.toBits(base64);\n    }\n    catch (e) {\n        throw new CryptoError(\"hex to aes key failed\", e);\n    }\n}\n// TODO test\nexport function uint8ArrayToKey(array) {\n    return base64ToKey(uint8ArrayToBase64(array));\n}\nexport function keyToUint8Array(key) {\n    return base64ToUint8Array(keyToBase64(key));\n}\nexport const fixedIv = hexToUint8Array(\"88888888888888888888888888888888\");\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nconst sha512 = new sjcl.hash.sha512();\nexport const SHA512_HASH_LENGTH_BYTES = 64;\n/**\n * Create the hash of the given data.\n * @param uint8Array The bytes.\n * @return The hash.\n */\nexport function sha512Hash(uint8Array) {\n    try {\n        sha512.update(sjcl.codec.arrayBuffer.toBits(uint8Array.buffer));\n        return new Uint8Array(sjcl.codec.arrayBuffer.fromBits(sha512.finalize(), false));\n    }\n    finally {\n        sha512.reset();\n    }\n}\n","import sjcl from \"../internal/sjcl.js\";\nimport { random } from \"../random/Randomizer.js\";\nimport { bitArrayToUint8Array, uint8ArrayToBitArray } from \"../misc/Utils.js\";\nimport { arrayEquals, concat, uint8ArrayToBase64 } from \"@tutao/tutanota-utils\";\nimport { sha256Hash } from \"../hashes/Sha256.js\";\nimport { CryptoError } from \"../misc/CryptoError.js\";\nimport { sha512Hash } from \"../hashes/Sha512.js\";\nexport const ENABLE_MAC = true;\nexport const IV_BYTE_LENGTH = 16;\nconst KEY_LENGTH_BYTES_AES_256 = 32;\nconst KEY_LENGTH_BITS_AES_256 = KEY_LENGTH_BYTES_AES_256 * 8;\nconst KEY_LENGTH_BYTES_AES_128 = 16;\nconst KEY_LENGTH_BITS_AES_128 = KEY_LENGTH_BYTES_AES_128 * 8;\nconst MAC_ENABLED_PREFIX = 1;\nconst MAC_LENGTH_BYTES = 32;\nexport function aes256RandomKey() {\n    return uint8ArrayToBitArray(random.generateRandomData(KEY_LENGTH_BYTES_AES_256));\n}\nexport function generateIV() {\n    return random.generateRandomData(IV_BYTE_LENGTH);\n}\n/**\n * Encrypts bytes with AES 256 in CBC mode.\n * @param key The key to use for the encryption.\n * @param bytes The plain text.\n * @param iv The initialization vector.\n * @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.\n * @param useMac If true, a 256 bit HMAC is appended to the encrypted data.\n * @return The encrypted text as words (sjcl internal structure)..\n */\nexport function aes256Encrypt(key, bytes, iv, usePadding = true, useMac = true) {\n    verifyKeySize(key, KEY_LENGTH_BITS_AES_256);\n    if (iv.length !== IV_BYTE_LENGTH) {\n        throw new CryptoError(`Illegal IV length: ${iv.length} (expected: ${IV_BYTE_LENGTH}): ${uint8ArrayToBase64(iv)} `);\n    }\n    let subKeys = getAes256SubKeys(key, useMac);\n    let encryptedBits = sjcl.mode.cbc.encrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(bytes), uint8ArrayToBitArray(iv), [], usePadding);\n    let data = concat(iv, bitArrayToUint8Array(encryptedBits));\n    if (useMac) {\n        let hmac = new sjcl.misc.hmac(subKeys.mKey, sjcl.hash.sha256);\n        let macBytes = bitArrayToUint8Array(hmac.encrypt(uint8ArrayToBitArray(data)));\n        data = concat(new Uint8Array([MAC_ENABLED_PREFIX]), data, macBytes);\n    }\n    return data;\n}\n/**\n * Decrypts the given words with AES 256 in CBC mode.\n * @param key The key to use for the decryption.\n * @param encryptedBytes The ciphertext.\n * @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.\n * @param useMac If true, a 256 bit HMAC is assumed to be appended to the encrypted data and it is checked before decryption.\n * @return The decrypted bytes.\n */\nexport function aes256Decrypt(key, encryptedBytes, usePadding = true, useMac = true) {\n    verifyKeySize(key, KEY_LENGTH_BITS_AES_256);\n    let subKeys = getAes256SubKeys(key, useMac);\n    let cipherTextWithoutMac;\n    if (useMac) {\n        cipherTextWithoutMac = encryptedBytes.subarray(1, encryptedBytes.length - MAC_LENGTH_BYTES);\n        let providedMacBytes = encryptedBytes.subarray(encryptedBytes.length - MAC_LENGTH_BYTES);\n        let hmac = new sjcl.misc.hmac(subKeys.mKey, sjcl.hash.sha256);\n        let computedMacBytes = bitArrayToUint8Array(hmac.encrypt(uint8ArrayToBitArray(cipherTextWithoutMac)));\n        if (!arrayEquals(providedMacBytes, computedMacBytes)) {\n            throw new CryptoError(\"invalid mac\");\n        }\n    }\n    else {\n        cipherTextWithoutMac = encryptedBytes;\n    }\n    // take the iv from the front of the encrypted data\n    const iv = cipherTextWithoutMac.slice(0, IV_BYTE_LENGTH);\n    if (iv.length !== IV_BYTE_LENGTH) {\n        throw new CryptoError(`Invalid IV length in AES256Decrypt: ${iv.length} bytes, must be 16 bytes (128 bits)`);\n    }\n    let ciphertext = cipherTextWithoutMac.slice(IV_BYTE_LENGTH);\n    try {\n        let decrypted = sjcl.mode.cbc.decrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(ciphertext), uint8ArrayToBitArray(iv), [], usePadding);\n        return new Uint8Array(bitArrayToUint8Array(decrypted));\n    }\n    catch (e) {\n        throw new CryptoError(\"aes decryption failed\", e);\n    }\n}\nfunction verifyKeySize(key, bitLength) {\n    if (sjcl.bitArray.bitLength(key) !== bitLength) {\n        throw new CryptoError(`Illegal key length: ${sjcl.bitArray.bitLength(key)} (expected: ${bitLength})`);\n    }\n}\n/************************ Legacy AES128 ************************/\nexport function aes128RandomKey() {\n    return uint8ArrayToBitArray(random.generateRandomData(KEY_LENGTH_BYTES_AES_128));\n}\n/**\n * Encrypts bytes with AES128 in CBC mode.\n * @param key The key to use for the encryption.\n * @param bytes The plain text.\n * @param iv The initialization vector.\n * @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.\n * @return The encrypted bytes\n */\nexport function aes128Encrypt(key, bytes, iv, usePadding, useMac) {\n    verifyKeySize(key, KEY_LENGTH_BITS_AES_128);\n    if (iv.length !== IV_BYTE_LENGTH) {\n        throw new CryptoError(`Illegal IV length: ${iv.length} (expected: ${IV_BYTE_LENGTH}): ${uint8ArrayToBase64(iv)} `);\n    }\n    let subKeys = getAes128SubKeys(key, useMac);\n    let encryptedBits = sjcl.mode.cbc.encrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(bytes), uint8ArrayToBitArray(iv), [], usePadding);\n    let data = concat(iv, bitArrayToUint8Array(encryptedBits));\n    if (useMac) {\n        let hmac = new sjcl.misc.hmac(subKeys.mKey, sjcl.hash.sha256);\n        let macBytes = bitArrayToUint8Array(hmac.encrypt(uint8ArrayToBitArray(data)));\n        data = concat(new Uint8Array([MAC_ENABLED_PREFIX]), data, macBytes);\n    }\n    return data;\n}\n/**\n * Decrypts the given words with AES128 in CBC mode.\n * @param key The key to use for the decryption.\n * @param encryptedBytes The ciphertext encoded as bytes.\n * @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.\n * @return The decrypted bytes.\n */\nexport function aes128Decrypt(key, encryptedBytes, usePadding = true) {\n    verifyKeySize(key, KEY_LENGTH_BITS_AES_128);\n    let useMac = encryptedBytes.length % 2 === 1;\n    let subKeys = getAes128SubKeys(key, useMac);\n    let cipherTextWithoutMac;\n    if (useMac) {\n        cipherTextWithoutMac = encryptedBytes.subarray(1, encryptedBytes.length - MAC_LENGTH_BYTES);\n        let providedMacBytes = encryptedBytes.subarray(encryptedBytes.length - MAC_LENGTH_BYTES);\n        let hmac = new sjcl.misc.hmac(subKeys.mKey, sjcl.hash.sha256);\n        let computedMacBytes = bitArrayToUint8Array(hmac.encrypt(uint8ArrayToBitArray(cipherTextWithoutMac)));\n        if (!arrayEquals(providedMacBytes, computedMacBytes)) {\n            throw new CryptoError(\"invalid mac\");\n        }\n    }\n    else {\n        cipherTextWithoutMac = encryptedBytes;\n    }\n    // take the iv from the front of the encrypted data\n    const iv = cipherTextWithoutMac.slice(0, IV_BYTE_LENGTH);\n    if (iv.length !== IV_BYTE_LENGTH) {\n        throw new CryptoError(`Invalid IV length in AES128Decrypt: ${iv.length} bytes, must be 16 bytes (128 bits)`);\n    }\n    let ciphertext = cipherTextWithoutMac.slice(IV_BYTE_LENGTH);\n    try {\n        let decrypted = sjcl.mode.cbc.decrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(ciphertext), uint8ArrayToBitArray(iv), [], usePadding);\n        return new Uint8Array(bitArrayToUint8Array(decrypted));\n    }\n    catch (e) {\n        throw new CryptoError(\"aes decryption failed\", e);\n    }\n}\nfunction getAes128SubKeys(key, mac) {\n    if (mac) {\n        let hashedKey = sha256Hash(bitArrayToUint8Array(key));\n        return {\n            cKey: uint8ArrayToBitArray(hashedKey.subarray(0, KEY_LENGTH_BYTES_AES_128)),\n            mKey: uint8ArrayToBitArray(hashedKey.subarray(KEY_LENGTH_BYTES_AES_128, KEY_LENGTH_BYTES_AES_128 * 2)),\n        };\n    }\n    else {\n        return {\n            cKey: key,\n            mKey: null,\n        };\n    }\n}\nfunction getAes256SubKeys(key, mac) {\n    if (mac) {\n        let hashedKey = sha512Hash(bitArrayToUint8Array(key));\n        return {\n            cKey: uint8ArrayToBitArray(hashedKey.subarray(0, KEY_LENGTH_BYTES_AES_256)),\n            mKey: uint8ArrayToBitArray(hashedKey.subarray(KEY_LENGTH_BYTES_AES_256, KEY_LENGTH_BYTES_AES_256 * 2)),\n        };\n    }\n    else {\n        return {\n            cKey: key,\n            mKey: null,\n        };\n    }\n}\n","// v2.2\n// tutao(map): slightly modified => made synchronous, removed unused code\nfunction bCrypt() {\n    this.GENSALT_DEFAULT_LOG2_ROUNDS = 10;\n    this.BCRYPT_SALT_LEN = 16;\n    this.BLOWFISH_NUM_ROUNDS = 16;\n    // commented next line because it is not used\n    //\tthis.PRNG = Clipperz.Crypto.PRNG.defaultRandomGenerator();\n    this.MAX_EXECUTION_TIME = 100;\n    this.P_orig = [\n        0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,\n        0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,\n        0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,\n        0xb5470917, 0x9216d5d9, 0x8979fb1b\n    ];\n    this.S_orig = [\n        0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed,\n        0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7,\n        0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3,\n        0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,\n        0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023,\n        0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,\n        0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda,\n        0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,\n        0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af,\n        0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6,\n        0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381,\n        0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,\n        0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d,\n        0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5,\n        0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a,\n        0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,\n        0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,\n        0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,\n        0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3,\n        0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,\n        0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,\n        0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b,\n        0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd,\n        0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,\n        0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f,\n        0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd,\n        0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39,\n        0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,\n        0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df,\n        0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,\n        0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e,\n        0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,\n        0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98,\n        0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565,\n        0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341,\n        0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,\n        0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0,\n        0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64,\n        0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191,\n        0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,\n        0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0,\n        0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,\n        0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5,\n        0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,\n        0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b,\n        0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f,\n        0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968,\n        0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,\n        0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,\n        0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6,\n        0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799,\n        0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,\n        0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71,\n        0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29,\n        0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6,\n        0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,\n        0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,\n        0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286,\n        0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec,\n        0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,\n        0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9,\n        0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,\n        0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e,\n        0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,\n        0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290,\n        0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810,\n        0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6,\n        0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,\n        0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847,\n        0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451,\n        0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6,\n        0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,\n        0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570,\n        0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,\n        0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,\n        0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,\n        0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708,\n        0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883,\n        0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185,\n        0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,\n        0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830,\n        0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239,\n        0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab,\n        0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,\n        0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19,\n        0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,\n        0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1,\n        0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,\n        0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,\n        0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3,\n        0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15,\n        0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,\n        0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2,\n        0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492,\n        0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174,\n        0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,\n        0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759,\n        0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,\n        0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc,\n        0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,\n        0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465,\n        0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a,\n        0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c,\n        0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,\n        0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e,\n        0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,\n        0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0,\n        0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,\n        0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462,\n        0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c,\n        0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399,\n        0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,\n        0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74,\n        0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397,\n        0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7,\n        0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,\n        0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802,\n        0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,\n        0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4,\n        0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,\n        0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2,\n        0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1,\n        0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c,\n        0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,\n        0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341,\n        0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8,\n        0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b,\n        0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,\n        0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,\n        0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,\n        0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc,\n        0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,\n        0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659,\n        0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f,\n        0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8,\n        0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,\n        0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be,\n        0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2,\n        0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255,\n        0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,\n        0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1,\n        0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,\n        0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025,\n        0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,\n        0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01,\n        0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641,\n        0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa,\n        0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,\n        0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409,\n        0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9,\n        0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3,\n        0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,\n        0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234,\n        0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf,\n        0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740,\n        0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,\n        0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f,\n        0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d,\n        0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8,\n        0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,\n        0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,\n        0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,\n        0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69,\n        0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,\n        0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a,\n        0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b,\n        0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd,\n        0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,\n        0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,\n        0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2,\n        0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb,\n        0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,\n        0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751,\n        0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,\n        0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369,\n        0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,\n        0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd,\n        0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45,\n        0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae,\n        0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,\n        0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08,\n        0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d,\n        0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,\n        0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,\n        0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e,\n        0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,\n        0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c,\n        0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,\n        0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361,\n        0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c,\n        0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be,\n        0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,\n        0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d,\n        0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891,\n        0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5,\n        0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,\n        0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292,\n        0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,\n        0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2,\n        0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,\n        0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,\n        0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8,\n        0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4,\n        0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,\n        0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6\n    ];\n    this.bf_crypt_ciphertext = [\n        0x4f727068, 0x65616e42, 0x65686f6c,\n        0x64657253, 0x63727944, 0x6f756274\n    ];\n    this.base64_code = [\n        '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',\n        'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',\n        'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',\n        'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\n        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',\n        '9'\n    ];\n    this.index_64 = [\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1,\n        54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1,\n        2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,\n        21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31,\n        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,\n        49, 50, 51, 52, 53, -1, -1, -1, -1, -1\n    ];\n    this.P;\n    this.S;\n    this.lr;\n    this.offp;\n}\nbCrypt.prototype.getByte = function (c) {\n    var ret = 0;\n    try {\n        var b = c.charCodeAt(0);\n    }\n    catch (err) {\n        b = c;\n    }\n    if (b > 127) {\n        return -128 + (b % 128);\n    }\n    else {\n        return b;\n    }\n};\nbCrypt.prototype.encode_base64 = function (d, len) {\n    var off = 0;\n    var rs = [];\n    var c1;\n    var c2;\n    if (len <= 0 || len > d.length) {\n        throw \"Invalid len\";\n    }\n    while (off < len) {\n        c1 = d[off++] & 0xff;\n        rs.push(this.base64_code[(c1 >> 2) & 0x3f]);\n        c1 = (c1 & 0x03) << 4;\n        if (off >= len) {\n            rs.push(this.base64_code[c1 & 0x3f]);\n            break;\n        }\n        c2 = d[off++] & 0xff;\n        c1 |= (c2 >> 4) & 0x0f;\n        rs.push(this.base64_code[c1 & 0x3f]);\n        c1 = (c2 & 0x0f) << 2;\n        if (off >= len) {\n            rs.push(this.base64_code[c1 & 0x3f]);\n            break;\n        }\n        c2 = d[off++] & 0xff;\n        c1 |= (c2 >> 6) & 0x03;\n        rs.push(this.base64_code[c1 & 0x3f]);\n        rs.push(this.base64_code[c2 & 0x3f]);\n    }\n    return rs.join('');\n};\nbCrypt.prototype.char64 = function (x) {\n    var code = x.charCodeAt(0);\n    if (code < 0 || code > this.index_64.length) {\n        return -1;\n    }\n    return this.index_64[code];\n};\nbCrypt.prototype.decode_base64 = function (s, maxolen) {\n    var off = 0;\n    var slen = s.length;\n    var olen = 0;\n    var rs = [];\n    var c1, c2, c3, c4, o;\n    if (maxolen <= 0) {\n        throw \"Invalid maxolen\";\n    }\n    while (off < slen - 1 && olen < maxolen) {\n        c1 = this.char64(s.charAt(off++));\n        c2 = this.char64(s.charAt(off++));\n        if (c1 == -1 || c2 == -1) {\n            break;\n        }\n        o = this.getByte(c1 << 2);\n        o |= (c2 & 0x30) >> 4;\n        rs.push(String.fromCharCode(o));\n        if (++olen >= maxolen || off >= slen) {\n            break;\n        }\n        c3 = this.char64(s.charAt(off++));\n        if (c3 == -1) {\n            break;\n        }\n        o = this.getByte((c2 & 0x0f) << 4);\n        o |= (c3 & 0x3c) >> 2;\n        rs.push(String.fromCharCode(o));\n        if (++olen >= maxolen || off >= slen) {\n            break;\n        }\n        c4 = this.char64(s.charAt(off++));\n        o = this.getByte((c3 & 0x03) << 6);\n        o |= c4;\n        rs.push(String.fromCharCode(o));\n        ++olen;\n    }\n    var ret = [];\n    for (off = 0; off < olen; off++) {\n        ret.push(this.getByte(rs[off]));\n    }\n    return ret;\n};\nbCrypt.prototype.encipher = function (lr, off) {\n    var i;\n    var n;\n    var l = lr[off];\n    var r = lr[off + 1];\n    l ^= this.P[0];\n    for (i = 0; i <= this.BLOWFISH_NUM_ROUNDS - 2;) {\n        // Feistel substitution on left word\n        n = this.S[(l >> 24) & 0xff];\n        n += this.S[0x100 | ((l >> 16) & 0xff)];\n        n ^= this.S[0x200 | ((l >> 8) & 0xff)];\n        n += this.S[0x300 | (l & 0xff)];\n        r ^= n ^ this.P[++i];\n        // Feistel substitution on right word\n        n = this.S[(r >> 24) & 0xff];\n        n += this.S[0x100 | ((r >> 16) & 0xff)];\n        n ^= this.S[0x200 | ((r >> 8) & 0xff)];\n        n += this.S[0x300 | (r & 0xff)];\n        l ^= n ^ this.P[++i];\n    }\n    lr[off] = r ^ this.P[this.BLOWFISH_NUM_ROUNDS + 1];\n    lr[off + 1] = l;\n};\nbCrypt.prototype.streamtoword = function (data, offp) {\n    var i;\n    var word = 0;\n    var off = offp;\n    for (i = 0; i < 4; i++) {\n        word = (word << 8) | (data[off] & 0xff);\n        off = (off + 1) % data.length;\n    }\n    this.offp = off;\n    return word;\n};\nbCrypt.prototype.init_key = function () {\n    this.P = this.P_orig.slice();\n    this.S = this.S_orig.slice();\n};\nbCrypt.prototype.key = function (key) {\n    var i;\n    this.offp = 0;\n    var lr = new Array(0x00000000, 0x00000000);\n    var plen = this.P.length;\n    var slen = this.S.length;\n    for (i = 0; i < plen; i++) {\n        this.P[i] = this.P[i] ^ this.streamtoword(key, this.offp);\n    }\n    for (i = 0; i < plen; i += 2) {\n        this.encipher(lr, 0);\n        this.P[i] = lr[0];\n        this.P[i + 1] = lr[1];\n    }\n    for (i = 0; i < slen; i += 2) {\n        this.encipher(lr, 0);\n        this.S[i] = lr[0];\n        this.S[i + 1] = lr[1];\n    }\n};\nbCrypt.prototype.ekskey = function (data, key) {\n    var i;\n    this.offp = 0;\n    var lr = new Array(0x00000000, 0x00000000);\n    var plen = this.P.length;\n    var slen = this.S.length;\n    for (i = 0; i < plen; i++)\n        this.P[i] = this.P[i] ^ this.streamtoword(key, this.offp);\n    this.offp = 0;\n    for (i = 0; i < plen; i += 2) {\n        lr[0] ^= this.streamtoword(data, this.offp);\n        lr[1] ^= this.streamtoword(data, this.offp);\n        this.encipher(lr, 0);\n        this.P[i] = lr[0];\n        this.P[i + 1] = lr[1];\n    }\n    for (i = 0; i < slen; i += 2) {\n        lr[0] ^= this.streamtoword(data, this.offp);\n        lr[1] ^= this.streamtoword(data, this.offp);\n        this.encipher(lr, 0);\n        this.S[i] = lr[0];\n        this.S[i + 1] = lr[1];\n    }\n};\n// removed arguments.callee from original version because it is not allowed in strict mode\nbCrypt.prototype.crypt_raw = function (password, salt, log_rounds) {\n    var rounds;\n    var j;\n    var cdata = this.bf_crypt_ciphertext.slice();\n    var clen = cdata.length;\n    var one_percent;\n    if (log_rounds < 4 || log_rounds > 31) {\n        throw \"Bad number of rounds\";\n    }\n    if (salt.length != this.BCRYPT_SALT_LEN) {\n        throw \"Bad _salt length\";\n    }\n    rounds = 1 << log_rounds;\n    one_percent = Math.floor(rounds / 100) + 1;\n    this.init_key();\n    this.ekskey(salt, password);\n    var obj = this;\n    var i = 0;\n    var roundFunction = null;\n    roundFunction = function () {\n        if (i < rounds) {\n            var start = new Date();\n            for (; i < rounds;) {\n                i = i + 1;\n                obj.key(password);\n                obj.key(salt);\n            }\n            return roundFunction();\n        }\n        else {\n            for (i = 0; i < 64; i++) {\n                for (j = 0; j < (clen >> 1); j++) {\n                    obj.encipher(cdata, j << 1);\n                }\n            }\n            var ret = [];\n            for (i = 0; i < clen; i++) {\n                ret.push(obj.getByte((cdata[i] >> 24) & 0xff));\n                ret.push(obj.getByte((cdata[i] >> 16) & 0xff));\n                ret.push(obj.getByte((cdata[i] >> 8) & 0xff));\n                ret.push(obj.getByte(cdata[i] & 0xff));\n            }\n            return ret;\n        }\n    };\n    return roundFunction();\n};\nexport default bCrypt;\n","export var KeyLength;\n(function (KeyLength) {\n    KeyLength[\"b128\"] = \"128\";\n    KeyLength[\"b256\"] = \"256\";\n})(KeyLength || (KeyLength = {}));\n","// @ts-ignore[untyped-import]\nimport bCrypt from \"../internal/bCrypt.js\";\nimport { random } from \"../random/Randomizer.js\";\nimport { stringToUtf8Uint8Array } from \"@tutao/tutanota-utils\";\nimport { uint8ArrayToBitArray } from \"../misc/Utils.js\";\nimport { KeyLength } from \"../misc/Constants.js\";\nimport { CryptoError } from \"../misc/CryptoError.js\";\nimport { sha256Hash } from \"./Sha256.js\";\nconst logRounds = 8; // pbkdf2 number of iterations\n/**\n * Create a 128 bit random _salt value.\n * return _salt 128 bit of random data, encoded as a hex string.\n */\nexport function generateRandomSalt() {\n    return random.generateRandomData(128 / 8);\n}\n/**\n * Create a 128 bit symmetric key from the given passphrase.\n * @param passphrase The passphrase to use for key generation as utf8 string.\n * @param salt 16 bytes of random data\n * @param keyLengthType Defines the length of the key that shall be generated.\n * @return resolved with the key\n */\nexport function generateKeyFromPassphrase(passphrase, salt, keyLengthType) {\n    // hash the password first to avoid login with multiples of a password, i.e. \"hello\" and \"hellohello\" produce the same key if the same _salt is used\n    let passphraseBytes = sha256Hash(stringToUtf8Uint8Array(passphrase));\n    let bytes = crypt_raw(passphraseBytes, salt, logRounds);\n    if (keyLengthType === KeyLength.b128) {\n        return uint8ArrayToBitArray(bytes.slice(0, 16));\n    }\n    else {\n        return uint8ArrayToBitArray(sha256Hash(bytes));\n    }\n}\nfunction crypt_raw(passphraseBytes, saltBytes, logRounds) {\n    try {\n        return _signedBytesToUint8Array(new bCrypt().crypt_raw(_uint8ArrayToSignedBytes(passphraseBytes), _uint8ArrayToSignedBytes(saltBytes), logRounds));\n    }\n    catch (e) {\n        const error = e;\n        throw new CryptoError(error.message, error);\n    }\n}\n/**\n * Converts an array of signed byte values (-128 to 127) to an Uint8Array (values 0 to 255).\n * @param signedBytes The signed byte values.\n * @return The unsigned byte values.\n */\nfunction _signedBytesToUint8Array(signedBytes) {\n    return new Uint8Array(new Int8Array(signedBytes));\n}\n/**\n * Converts an uint8Array (value 0 to 255) to an Array with unsigned bytes (-128 to 127).\n * @param unsignedBytes The unsigned byte values.\n * @return The signed byte values.\n */\nfunction _uint8ArrayToSignedBytes(unsignedBytes) {\n    return Array.from(new Uint8Array(new Int8Array(unsignedBytes)));\n}\n","import { random } from \"./Randomizer.js\";\n/**\n * This is the adapter to the PRNG interface required by JSBN.\n * @constructor\n */\nexport class SecureRandom {\n    /**\n     * Only this function is used by jsbn for getting random bytes. Each byte is a value between 0 and 255.\n     * @param array An array to fill with random bytes. The length of the array defines the number of bytes to create.\n     */\n    nextBytes(array) {\n        let bytes = random.generateRandomData(array.length);\n        for (var i = 0; i < array.length; i++) {\n            array[i] = bytes[i];\n        }\n    }\n}\n","import { SecureRandom } from '../random/SecureRandom.js';\n// Copyright (c) 2005  Tom Wu\n// All Rights Reserved.\n// See \"LICENSE\" for details.\n// Basic JavaScript BN library - subset useful for RSA encryption.\n// Bits per digit\nvar dbits;\n// JavaScript engine analysis\nvar canary = 0xdeadbeefcafe;\nvar j_lm = ((canary & 0xffffff) == 0xefcafe);\n// (public) Constructor\n// tutao: a = bitlength (1024)\n//        b = number of miller rabin test * 2\n//        c = SecureRandom\nexport function BigInteger(a, b, c) {\n    if (a != null) {\n        if (\"number\" == typeof a) {\n            this.fromNumber(a, b, c);\n        }\n        else if (b == null && \"string\" != typeof a) {\n            this.fromString(a, 256);\n        }\n        else {\n            this.fromString(a, b);\n        }\n    }\n}\n// return new, unset BigInteger\nfunction nbi() {\n    return new BigInteger(null);\n}\n// am: Compute w_j += (x*this_i), propagate carries,\n// c is initial carry, returns final carry.\n// c < 3*dvalue, x < 2*dvalue, this_i < dvalue\n// We need to select the fastest one that works in this environment.\n// am1: use a single mult and divide to get the high bits,\n// max digit bits should be 26 because\n// max internal value = 2*dvalue^2-2*dvalue (< 2^53)\nfunction am1(i, x, w, j, c, n) {\n    while (--n >= 0) {\n        var v = x * this[i++] + w[j] + c;\n        c = Math.floor(v / 0x4000000);\n        w[j++] = v & 0x3ffffff;\n    }\n    return c;\n}\n// am2 avoids a big mult-and-extract completely.\n// Max digit bits should be <= 30 because we do bitwise ops\n// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)\nfunction am2(i, x, w, j, c, n) {\n    var xl = x & 0x7fff, xh = x >> 15;\n    while (--n >= 0) {\n        var l = this[i] & 0x7fff;\n        var h = this[i++] >> 15;\n        var m = xh * l + h * xl;\n        l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);\n        c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);\n        w[j++] = l & 0x3fffffff;\n    }\n    return c;\n}\n// Alternately, set max digit bits to 28 since some\n// browsers slow down when dealing with 32-bit numbers.\nfunction am3(i, x, w, j, c, n) {\n    var xl = x & 0x3fff, xh = x >> 14;\n    while (--n >= 0) {\n        var l = this[i] & 0x3fff;\n        var h = this[i++] >> 14;\n        var m = xh * l + h * xl;\n        l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;\n        c = (l >> 28) + (m >> 14) + xh * h;\n        w[j++] = l & 0xfffffff;\n    }\n    return c;\n}\nif (j_lm && (typeof navigator === \"object\" && navigator.appName == \"Microsoft Internet Explorer\")) {\n    BigInteger.prototype.am = am2;\n    dbits = 30;\n}\nelse if (j_lm && (typeof navigator === \"object\" && navigator.appName != \"Netscape\")) {\n    BigInteger.prototype.am = am1;\n    dbits = 26;\n}\nelse { // Mozilla/Netscape seems to prefer am3\n    BigInteger.prototype.am = am3;\n    dbits = 28;\n}\nBigInteger.prototype.DB = dbits;\nBigInteger.prototype.DM = ((1 << dbits) - 1);\nBigInteger.prototype.DV = (1 << dbits);\nvar BI_FP = 52;\nBigInteger.prototype.FV = Math.pow(2, BI_FP);\nBigInteger.prototype.F1 = BI_FP - dbits;\nBigInteger.prototype.F2 = 2 * dbits - BI_FP;\n// Digit conversions\nvar BI_RM = \"0123456789abcdefghijklmnopqrstuvwxyz\";\nvar BI_RC = new Array();\nvar rr, vv;\nrr = \"0\".charCodeAt(0);\nfor (vv = 0; vv <= 9; ++vv)\n    BI_RC[rr++] = vv;\nrr = \"a\".charCodeAt(0);\nfor (vv = 10; vv < 36; ++vv)\n    BI_RC[rr++] = vv;\nrr = \"A\".charCodeAt(0);\nfor (vv = 10; vv < 36; ++vv)\n    BI_RC[rr++] = vv;\nfunction int2char(n) {\n    return BI_RM.charAt(n);\n}\nfunction intAt(s, i) {\n    var c = BI_RC[s.charCodeAt(i)];\n    return (c == null) ? -1 : c;\n}\n// (protected) copy this to r\nfunction bnpCopyTo(r) {\n    for (var i = this.t - 1; i >= 0; --i)\n        r[i] = this[i];\n    r.t = this.t;\n    r.s = this.s;\n}\n// (protected) set from integer value x, -DV <= x < DV\nfunction bnpFromInt(x) {\n    this.t = 1;\n    this.s = (x < 0) ? -1 : 0;\n    if (x > 0) {\n        this[0] = x;\n    }\n    else if (x < -1) {\n        this[0] = x + DV;\n    }\n    else {\n        this.t = 0;\n    }\n}\n// return bigint initialized to value\nfunction nbv(i) {\n    var r = nbi();\n    r.fromInt(i);\n    return r;\n}\n// (protected) set from string and radix\nfunction bnpFromString(s, b) {\n    var k;\n    if (b == 16) {\n        k = 4;\n    }\n    else if (b == 8) {\n        k = 3;\n    }\n    else if (b == 256) {\n        k = 8;\n    } // byte array\n    else if (b == 2) {\n        k = 1;\n    }\n    else if (b == 32) {\n        k = 5;\n    }\n    else if (b == 4) {\n        k = 2;\n    }\n    else {\n        this.fromRadix(s, b);\n        return;\n    }\n    this.t = 0;\n    this.s = 0;\n    var i = s.length, mi = false, sh = 0;\n    while (--i >= 0) {\n        var x = (k == 8) ? s[i] & 0xff : intAt(s, i);\n        if (x < 0) {\n            if (s.charAt(i) == \"-\")\n                mi = true;\n            continue;\n        }\n        mi = false;\n        if (sh == 0) {\n            this[this.t++] = x;\n        }\n        else if (sh + k > this.DB) {\n            this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;\n            this[this.t++] = (x >> (this.DB - sh));\n        }\n        else {\n            this[this.t - 1] |= x << sh;\n        }\n        sh += k;\n        if (sh >= this.DB)\n            sh -= this.DB;\n    }\n    if (k == 8 && (s[0] & 0x80) != 0) {\n        this.s = -1;\n        if (sh > 0)\n            this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;\n    }\n    this.clamp();\n    if (mi)\n        BigInteger.ZERO.subTo(this, this);\n}\n// (protected) clamp off excess high words\nfunction bnpClamp() {\n    var c = this.s & this.DM;\n    while (this.t > 0 && this[this.t - 1] == c)\n        --this.t;\n}\n// (public) return string representation in given radix\nfunction bnToString(b) {\n    if (this.s < 0)\n        return \"-\" + this.negate().toString(b);\n    var k;\n    if (b == 16) {\n        k = 4;\n    }\n    else if (b == 8) {\n        k = 3;\n    }\n    else if (b == 2) {\n        k = 1;\n    }\n    else if (b == 32) {\n        k = 5;\n    }\n    else if (b == 4) {\n        k = 2;\n    }\n    else {\n        return this.toRadix(b);\n    }\n    var km = (1 << k) - 1, d, m = false, r = \"\", i = this.t;\n    var p = this.DB - (i * this.DB) % k;\n    if (i-- > 0) {\n        if (p < this.DB && (d = this[i] >> p) > 0) {\n            m = true;\n            r = int2char(d);\n        }\n        while (i >= 0) {\n            if (p < k) {\n                d = (this[i] & ((1 << p) - 1)) << (k - p);\n                d |= this[--i] >> (p += this.DB - k);\n            }\n            else {\n                d = (this[i] >> (p -= k)) & km;\n                if (p <= 0) {\n                    p += this.DB;\n                    --i;\n                }\n            }\n            if (d > 0)\n                m = true;\n            if (m)\n                r += int2char(d);\n        }\n    }\n    return m ? r : \"0\";\n}\n// (public) -this\nfunction bnNegate() {\n    var r = nbi();\n    BigInteger.ZERO.subTo(this, r);\n    return r;\n}\n// (public) |this|\nfunction bnAbs() {\n    return (this.s < 0) ? this.negate() : this;\n}\n// (public) return + if this > a, - if this < a, 0 if equal\nfunction bnCompareTo(a) {\n    var r = this.s - a.s;\n    if (r != 0)\n        return r;\n    var i = this.t;\n    r = i - a.t;\n    if (r != 0)\n        return (this.s < 0) ? -r : r;\n    while (--i >= 0)\n        if ((r = this[i] - a[i]) != 0)\n            return r;\n    return 0;\n}\n// returns bit length of the integer x\nfunction nbits(x) {\n    var r = 1, t;\n    if ((t = x >>> 16) != 0) {\n        x = t;\n        r += 16;\n    }\n    if ((t = x >> 8) != 0) {\n        x = t;\n        r += 8;\n    }\n    if ((t = x >> 4) != 0) {\n        x = t;\n        r += 4;\n    }\n    if ((t = x >> 2) != 0) {\n        x = t;\n        r += 2;\n    }\n    if ((t = x >> 1) != 0) {\n        x = t;\n        r += 1;\n    }\n    return r;\n}\n// (public) return the number of bits in \"this\"\nfunction bnBitLength() {\n    if (this.t <= 0)\n        return 0;\n    return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));\n}\n// (protected) r = this << n*DB\nfunction bnpDLShiftTo(n, r) {\n    var i;\n    for (i = this.t - 1; i >= 0; --i)\n        r[i + n] = this[i];\n    for (i = n - 1; i >= 0; --i)\n        r[i] = 0;\n    r.t = this.t + n;\n    r.s = this.s;\n}\n// (protected) r = this >> n*DB\nfunction bnpDRShiftTo(n, r) {\n    for (var i = n; i < this.t; ++i)\n        r[i - n] = this[i];\n    r.t = Math.max(this.t - n, 0);\n    r.s = this.s;\n}\n// (protected) r = this << n\nfunction bnpLShiftTo(n, r) {\n    var bs = n % this.DB;\n    var cbs = this.DB - bs;\n    var bm = (1 << cbs) - 1;\n    var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i;\n    for (i = this.t - 1; i >= 0; --i) {\n        r[i + ds + 1] = (this[i] >> cbs) | c;\n        c = (this[i] & bm) << bs;\n    }\n    for (i = ds - 1; i >= 0; --i)\n        r[i] = 0;\n    r[ds] = c;\n    r.t = this.t + ds + 1;\n    r.s = this.s;\n    r.clamp();\n}\n// (protected) r = this >> n\nfunction bnpRShiftTo(n, r) {\n    r.s = this.s;\n    var ds = Math.floor(n / this.DB);\n    if (ds >= this.t) {\n        r.t = 0;\n        return;\n    }\n    var bs = n % this.DB;\n    var cbs = this.DB - bs;\n    var bm = (1 << bs) - 1;\n    r[0] = this[ds] >> bs;\n    for (var i = ds + 1; i < this.t; ++i) {\n        r[i - ds - 1] |= (this[i] & bm) << cbs;\n        r[i - ds] = this[i] >> bs;\n    }\n    if (bs > 0)\n        r[this.t - ds - 1] |= (this.s & bm) << cbs;\n    r.t = this.t - ds;\n    r.clamp();\n}\n// (protected) r = this - a\nfunction bnpSubTo(a, r) {\n    var i = 0, c = 0, m = Math.min(a.t, this.t);\n    while (i < m) {\n        c += this[i] - a[i];\n        r[i++] = c & this.DM;\n        c >>= this.DB;\n    }\n    if (a.t < this.t) {\n        c -= a.s;\n        while (i < this.t) {\n            c += this[i];\n            r[i++] = c & this.DM;\n            c >>= this.DB;\n        }\n        c += this.s;\n    }\n    else {\n        c += this.s;\n        while (i < a.t) {\n            c -= a[i];\n            r[i++] = c & this.DM;\n            c >>= this.DB;\n        }\n        c -= a.s;\n    }\n    r.s = (c < 0) ? -1 : 0;\n    if (c < -1) {\n        r[i++] = this.DV + c;\n    }\n    else if (c > 0)\n        r[i++] = c;\n    r.t = i;\n    r.clamp();\n}\n// (protected) r = this * a, r != this,a (HAC 14.12)\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyTo(a, r) {\n    var x = this.abs(), y = a.abs();\n    var i = x.t;\n    r.t = i + y.t;\n    while (--i >= 0)\n        r[i] = 0;\n    for (i = 0; i < y.t; ++i)\n        r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);\n    r.s = 0;\n    r.clamp();\n    if (this.s != a.s)\n        BigInteger.ZERO.subTo(r, r);\n}\n// (protected) r = this^2, r != this (HAC 14.16)\nfunction bnpSquareTo(r) {\n    var x = this.abs();\n    var i = r.t = 2 * x.t;\n    while (--i >= 0)\n        r[i] = 0;\n    for (i = 0; i < x.t - 1; ++i) {\n        var c = x.am(i, x[i], r, 2 * i, 0, 1);\n        if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {\n            r[i + x.t] -= x.DV;\n            r[i + x.t + 1] = 1;\n        }\n    }\n    if (r.t > 0)\n        r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);\n    r.s = 0;\n    r.clamp();\n}\n// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)\n// r != q, this != m.  q or r may be null.\nfunction bnpDivRemTo(m, q, r) {\n    var pm = m.abs();\n    if (pm.t <= 0)\n        return;\n    var pt = this.abs();\n    if (pt.t < pm.t) {\n        if (q != null)\n            q.fromInt(0);\n        if (r != null)\n            this.copyTo(r);\n        return;\n    }\n    if (r == null)\n        r = nbi();\n    var y = nbi(), ts = this.s, ms = m.s;\n    var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus\n    if (nsh > 0) {\n        pm.lShiftTo(nsh, y);\n        pt.lShiftTo(nsh, r);\n    }\n    else {\n        pm.copyTo(y);\n        pt.copyTo(r);\n    }\n    var ys = y.t;\n    var y0 = y[ys - 1];\n    if (y0 == 0)\n        return;\n    var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);\n    var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2;\n    var i = r.t, j = i - ys, t = (q == null) ? nbi() : q;\n    y.dlShiftTo(j, t);\n    if (r.compareTo(t) >= 0) {\n        r[r.t++] = 1;\n        r.subTo(t, r);\n    }\n    BigInteger.ONE.dlShiftTo(ys, t);\n    t.subTo(y, y); // \"negative\" y so we can replace sub with am later\n    while (y.t < ys)\n        y[y.t++] = 0;\n    while (--j >= 0) {\n        // Estimate quotient digit\n        var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);\n        if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out\n            y.dlShiftTo(j, t);\n            r.subTo(t, r);\n            while (r[i] < --qd)\n                r.subTo(t, r);\n        }\n    }\n    if (q != null) {\n        r.drShiftTo(ys, q);\n        if (ts != ms)\n            BigInteger.ZERO.subTo(q, q);\n    }\n    r.t = ys;\n    r.clamp();\n    if (nsh > 0)\n        r.rShiftTo(nsh, r); // Denormalize remainder\n    if (ts < 0)\n        BigInteger.ZERO.subTo(r, r);\n}\n// (public) this mod a\nfunction bnMod(a) {\n    var r = nbi();\n    this.abs().divRemTo(a, null, r);\n    if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0)\n        a.subTo(r, r);\n    return r;\n}\n// Modular reduction using \"classic\" algorithm\nfunction Classic(m) {\n    this.m = m;\n}\nfunction cConvert(x) {\n    if (x.s < 0 || x.compareTo(this.m) >= 0) {\n        return x.mod(this.m);\n    }\n    else {\n        return x;\n    }\n}\nfunction cRevert(x) {\n    return x;\n}\nfunction cReduce(x) {\n    x.divRemTo(this.m, null, x);\n}\nfunction cMulTo(x, y, r) {\n    x.multiplyTo(y, r);\n    this.reduce(r);\n}\nfunction cSqrTo(x, r) {\n    x.squareTo(r);\n    this.reduce(r);\n}\nClassic.prototype.convert = cConvert;\nClassic.prototype.revert = cRevert;\nClassic.prototype.reduce = cReduce;\nClassic.prototype.mulTo = cMulTo;\nClassic.prototype.sqrTo = cSqrTo;\n// (protected) return \"-1/this % 2^DB\"; useful for Mont. reduction\n// justification:\n//         xy == 1 (mod m)\n//         xy =  1+km\n//   xy(2-xy) = (1+km)(1-km)\n// x[y(2-xy)] = 1-k^2m^2\n// x[y(2-xy)] == 1 (mod m^2)\n// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2\n// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.\n// JS multiply \"overflows\" differently from C/C++, so care is needed here.\nfunction bnpInvDigit() {\n    if (this.t < 1)\n        return 0;\n    var x = this[0];\n    if ((x & 1) == 0)\n        return 0;\n    var y = x & 3; // y == 1/x mod 2^2\n    y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4\n    y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8\n    y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16\n    // last step - calculate inverse mod DV directly;\n    // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints\n    y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits\n    // we really want the negative inverse, and -DV < y < DV\n    return (y > 0) ? this.DV - y : -y;\n}\n// Montgomery reduction\nfunction Montgomery(m) {\n    this.m = m;\n    this.mp = m.invDigit();\n    this.mpl = this.mp & 0x7fff;\n    this.mph = this.mp >> 15;\n    this.um = (1 << (m.DB - 15)) - 1;\n    this.mt2 = 2 * m.t;\n}\n// xR mod m\nfunction montConvert(x) {\n    var r = nbi();\n    x.abs().dlShiftTo(this.m.t, r);\n    r.divRemTo(this.m, null, r);\n    if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0)\n        this.m.subTo(r, r);\n    return r;\n}\n// x/R mod m\nfunction montRevert(x) {\n    var r = nbi();\n    x.copyTo(r);\n    this.reduce(r);\n    return r;\n}\n// x = x/R mod m (HAC 14.32)\nfunction montReduce(x) {\n    while (x.t <= this.mt2) // pad x so am has enough room later\n        x[x.t++] = 0;\n    for (var i = 0; i < this.m.t; ++i) {\n        // faster way of calculating u0 = x[i]*mp mod DV\n        var j = x[i] & 0x7fff;\n        var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;\n        // use am to combine the multiply-shift-add into one call\n        j = i + this.m.t;\n        x[j] += this.m.am(0, u0, x, i, 0, this.m.t);\n        // propagate carry\n        while (x[j] >= x.DV) {\n            x[j] -= x.DV;\n            x[++j]++;\n        }\n    }\n    x.clamp();\n    x.drShiftTo(this.m.t, x);\n    if (x.compareTo(this.m) >= 0)\n        x.subTo(this.m, x);\n}\n// r = \"x^2/R mod m\"; x != r\nfunction montSqrTo(x, r) {\n    x.squareTo(r);\n    this.reduce(r);\n}\n// r = \"xy/R mod m\"; x,y != r\nfunction montMulTo(x, y, r) {\n    x.multiplyTo(y, r);\n    this.reduce(r);\n}\nMontgomery.prototype.convert = montConvert;\nMontgomery.prototype.revert = montRevert;\nMontgomery.prototype.reduce = montReduce;\nMontgomery.prototype.mulTo = montMulTo;\nMontgomery.prototype.sqrTo = montSqrTo;\n// (protected) true iff this is even\nfunction bnpIsEven() {\n    return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;\n}\n// (protected) this^e, e < 2^32, doing sqr and mul with \"r\" (HAC 14.79)\nfunction bnpExp(e, z) {\n    if (e > 0xffffffff || e < 1)\n        return BigInteger.ONE;\n    var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1;\n    g.copyTo(r);\n    while (--i >= 0) {\n        z.sqrTo(r, r2);\n        if ((e & (1 << i)) > 0) {\n            z.mulTo(r2, g, r);\n        }\n        else {\n            var t = r;\n            r = r2;\n            r2 = t;\n        }\n    }\n    return z.revert(r);\n}\n// (public) this^e % m, 0 <= e < 2^32\nfunction bnModPowInt(e, m) {\n    var z;\n    if (e < 256 || m.isEven())\n        z = new Classic(m);\n    else\n        z = new Montgomery(m);\n    return this.exp(e, z);\n}\n// protected\nBigInteger.prototype.copyTo = bnpCopyTo;\nBigInteger.prototype.fromInt = bnpFromInt;\nBigInteger.prototype.fromString = bnpFromString;\nBigInteger.prototype.clamp = bnpClamp;\nBigInteger.prototype.dlShiftTo = bnpDLShiftTo;\nBigInteger.prototype.drShiftTo = bnpDRShiftTo;\nBigInteger.prototype.lShiftTo = bnpLShiftTo;\nBigInteger.prototype.rShiftTo = bnpRShiftTo;\nBigInteger.prototype.subTo = bnpSubTo;\nBigInteger.prototype.multiplyTo = bnpMultiplyTo;\nBigInteger.prototype.squareTo = bnpSquareTo;\nBigInteger.prototype.divRemTo = bnpDivRemTo;\nBigInteger.prototype.invDigit = bnpInvDigit;\nBigInteger.prototype.isEven = bnpIsEven;\nBigInteger.prototype.exp = bnpExp;\n// public\nBigInteger.prototype.toString = bnToString;\nBigInteger.prototype.negate = bnNegate;\nBigInteger.prototype.abs = bnAbs;\nBigInteger.prototype.compareTo = bnCompareTo;\nBigInteger.prototype.bitLength = bnBitLength;\nBigInteger.prototype.mod = bnMod;\nBigInteger.prototype.modPowInt = bnModPowInt;\n// \"constants\"\nBigInteger.ZERO = nbv(0);\nBigInteger.ONE = nbv(1);\n// Copyright (c) 2005-2009  Tom Wu\n// All Rights Reserved.\n// See \"LICENSE\" for details.\n// Extended JavaScript BN functions, required for RSA private ops.\n// Version 1.1: new BigInteger(\"0\", 10) returns \"proper\" zero\n// Version 1.2: square() API, isProbablePrime fix\n// (public)\nfunction bnClone() {\n    var r = nbi();\n    this.copyTo(r);\n    return r;\n}\n// (public) return value as integer\nfunction bnIntValue() {\n    if (this.s < 0) {\n        if (this.t == 1) {\n            return this[0] - this.DV;\n        }\n        else if (this.t == 0)\n            return -1;\n    }\n    else if (this.t == 1) {\n        return this[0];\n    }\n    else if (this.t == 0)\n        return 0;\n    // assumes 16 < DB < 32\n    return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0];\n}\n// (public) return value as byte\nfunction bnByteValue() {\n    return (this.t == 0) ? this.s : (this[0] << 24) >> 24;\n}\n// (public) return value as short (assumes DB>=16)\nfunction bnShortValue() {\n    return (this.t == 0) ? this.s : (this[0] << 16) >> 16;\n}\n// (protected) return x s.t. r^x < DV\nfunction bnpChunkSize(r) {\n    return Math.floor(Math.LN2 * this.DB / Math.log(r));\n}\n// (public) 0 if this == 0, 1 if this > 0\nfunction bnSigNum() {\n    if (this.s < 0) {\n        return -1;\n    }\n    else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) {\n        return 0;\n    }\n    else {\n        return 1;\n    }\n}\n// (protected) convert to radix string\nfunction bnpToRadix(b) {\n    if (b == null)\n        b = 10;\n    if (this.signum() == 0 || b < 2 || b > 36)\n        return \"0\";\n    var cs = this.chunkSize(b);\n    var a = Math.pow(b, cs);\n    var d = nbv(a), y = nbi(), z = nbi(), r = \"\";\n    this.divRemTo(d, y, z);\n    while (y.signum() > 0) {\n        r = (a + z.intValue()).toString(b).substr(1) + r;\n        y.divRemTo(d, y, z);\n    }\n    return z.intValue().toString(b) + r;\n}\n// (protected) convert from radix string\nfunction bnpFromRadix(s, b) {\n    this.fromInt(0);\n    if (b == null)\n        b = 10;\n    var cs = this.chunkSize(b);\n    var d = Math.pow(b, cs), mi = false, j = 0, w = 0;\n    for (var i = 0; i < s.length; ++i) {\n        var x = intAt(s, i);\n        if (x < 0) {\n            if (s.charAt(i) == \"-\" && this.signum() == 0)\n                mi = true;\n            continue;\n        }\n        w = b * w + x;\n        if (++j >= cs) {\n            this.dMultiply(d);\n            this.dAddOffset(w, 0);\n            j = 0;\n            w = 0;\n        }\n    }\n    if (j > 0) {\n        this.dMultiply(Math.pow(b, j));\n        this.dAddOffset(w, 0);\n    }\n    if (mi)\n        BigInteger.ZERO.subTo(this, this);\n}\n// (protected) alternate constructor\n// tutao: on first invocation:\n//        a = bitlength (1024)\n//        b = number of miller rabin test * 2\n//        c = SecureRandom\n//       on second invocation:\n//        a = bitlength (1024)\n//        b = SecureRandom\n//        c == undefined\nfunction bnpFromNumber(a, b, c) {\n    if (\"number\" == typeof b) {\n        // new BigInteger(int,int,RNG)\n        if (a < 2) {\n            this.fromInt(1);\n        }\n        else {\n            this.fromNumber(a, c);\n            if (!this.testBit(a - 1)) // force MSB set\n             {\n                this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);\n            }\n            if (this.isEven())\n                this.dAddOffset(1, 0); // force odd\n            while (!this.isProbablePrime(b)) {\n                this.dAddOffset(2, 0);\n                if (this.bitLength() > a)\n                    this.subTo(BigInteger.ONE.shiftLeft(a - 1), this);\n            }\n        }\n    }\n    else {\n        // new BigInteger(int,RNG)\n        var x = new Array(), t = a & 7;\n        x.length = (a >> 3) + 1;\n        b.nextBytes(x);\n        if (t > 0)\n            x[0] &= ((1 << t) - 1);\n        else\n            x[0] = 0;\n        this.fromString(x, 256);\n    }\n}\n// (public) convert to bigendian byte array\nfunction bnToByteArray() {\n    var i = this.t, r = new Array();\n    r[0] = this.s;\n    var p = this.DB - (i * this.DB) % 8, d, k = 0;\n    if (i-- > 0) {\n        if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) {\n            r[k++] = d | (this.s << (this.DB - p));\n        }\n        while (i >= 0) {\n            if (p < 8) {\n                d = (this[i] & ((1 << p) - 1)) << (8 - p);\n                d |= this[--i] >> (p += this.DB - 8);\n            }\n            else {\n                d = (this[i] >> (p -= 8)) & 0xff;\n                if (p <= 0) {\n                    p += this.DB;\n                    --i;\n                }\n            }\n            if ((d & 0x80) != 0)\n                d |= -256;\n            if (k == 0 && (this.s & 0x80) != (d & 0x80))\n                ++k;\n            if (k > 0 || d != this.s)\n                r[k++] = d;\n        }\n    }\n    return r;\n}\nfunction bnEquals(a) {\n    return (this.compareTo(a) == 0);\n}\nfunction bnMin(a) {\n    return (this.compareTo(a) < 0) ? this : a;\n}\nfunction bnMax(a) {\n    return (this.compareTo(a) > 0) ? this : a;\n}\n// (protected) r = this op a (bitwise)\nfunction bnpBitwiseTo(a, op, r) {\n    var i, f, m = Math.min(a.t, this.t);\n    for (i = 0; i < m; ++i)\n        r[i] = op(this[i], a[i]);\n    if (a.t < this.t) {\n        f = a.s & this.DM;\n        for (i = m; i < this.t; ++i)\n            r[i] = op(this[i], f);\n        r.t = this.t;\n    }\n    else {\n        f = this.s & this.DM;\n        for (i = m; i < a.t; ++i)\n            r[i] = op(f, a[i]);\n        r.t = a.t;\n    }\n    r.s = op(this.s, a.s);\n    r.clamp();\n}\n// (public) this & a\nfunction op_and(x, y) {\n    return x & y;\n}\nfunction bnAnd(a) {\n    var r = nbi();\n    this.bitwiseTo(a, op_and, r);\n    return r;\n}\n// (public) this | a\nfunction op_or(x, y) {\n    return x | y;\n}\nfunction bnOr(a) {\n    var r = nbi();\n    this.bitwiseTo(a, op_or, r);\n    return r;\n}\n// (public) this ^ a\nfunction op_xor(x, y) {\n    return x ^ y;\n}\nfunction bnXor(a) {\n    var r = nbi();\n    this.bitwiseTo(a, op_xor, r);\n    return r;\n}\n// (public) this & ~a\nfunction op_andnot(x, y) {\n    return x & ~y;\n}\nfunction bnAndNot(a) {\n    var r = nbi();\n    this.bitwiseTo(a, op_andnot, r);\n    return r;\n}\n// (public) ~this\nfunction bnNot() {\n    var r = nbi();\n    for (var i = 0; i < this.t; ++i)\n        r[i] = this.DM & ~this[i];\n    r.t = this.t;\n    r.s = ~this.s;\n    return r;\n}\n// (public) this << n\nfunction bnShiftLeft(n) {\n    var r = nbi();\n    if (n < 0)\n        this.rShiftTo(-n, r);\n    else\n        this.lShiftTo(n, r);\n    return r;\n}\n// (public) this >> n\nfunction bnShiftRight(n) {\n    var r = nbi();\n    if (n < 0)\n        this.lShiftTo(-n, r);\n    else\n        this.rShiftTo(n, r);\n    return r;\n}\n// return index of lowest 1-bit in x, x < 2^31\nfunction lbit(x) {\n    if (x == 0)\n        return -1;\n    var r = 0;\n    if ((x & 0xffff) == 0) {\n        x >>= 16;\n        r += 16;\n    }\n    if ((x & 0xff) == 0) {\n        x >>= 8;\n        r += 8;\n    }\n    if ((x & 0xf) == 0) {\n        x >>= 4;\n        r += 4;\n    }\n    if ((x & 3) == 0) {\n        x >>= 2;\n        r += 2;\n    }\n    if ((x & 1) == 0)\n        ++r;\n    return r;\n}\n// (public) returns index of lowest 1-bit (or -1 if none)\nfunction bnGetLowestSetBit() {\n    for (var i = 0; i < this.t; ++i)\n        if (this[i] != 0)\n            return i * this.DB + lbit(this[i]);\n    if (this.s < 0)\n        return this.t * this.DB;\n    return -1;\n}\n// return number of 1 bits in x\nfunction cbit(x) {\n    var r = 0;\n    while (x != 0) {\n        x &= x - 1;\n        ++r;\n    }\n    return r;\n}\n// (public) return number of set bits\nfunction bnBitCount() {\n    var r = 0, x = this.s & this.DM;\n    for (var i = 0; i < this.t; ++i)\n        r += cbit(this[i] ^ x);\n    return r;\n}\n// (public) true iff nth bit is set\nfunction bnTestBit(n) {\n    var j = Math.floor(n / this.DB);\n    if (j >= this.t)\n        return (this.s != 0);\n    return ((this[j] & (1 << (n % this.DB))) != 0);\n}\n// (protected) this op (1<<n)\nfunction bnpChangeBit(n, op) {\n    var r = BigInteger.ONE.shiftLeft(n);\n    this.bitwiseTo(r, op, r);\n    return r;\n}\n// (public) this | (1<<n)\nfunction bnSetBit(n) {\n    return this.changeBit(n, op_or);\n}\n// (public) this & ~(1<<n)\nfunction bnClearBit(n) {\n    return this.changeBit(n, op_andnot);\n}\n// (public) this ^ (1<<n)\nfunction bnFlipBit(n) {\n    return this.changeBit(n, op_xor);\n}\n// (protected) r = this + a\nfunction bnpAddTo(a, r) {\n    var i = 0, c = 0, m = Math.min(a.t, this.t);\n    while (i < m) {\n        c += this[i] + a[i];\n        r[i++] = c & this.DM;\n        c >>= this.DB;\n    }\n    if (a.t < this.t) {\n        c += a.s;\n        while (i < this.t) {\n            c += this[i];\n            r[i++] = c & this.DM;\n            c >>= this.DB;\n        }\n        c += this.s;\n    }\n    else {\n        c += this.s;\n        while (i < a.t) {\n            c += a[i];\n            r[i++] = c & this.DM;\n            c >>= this.DB;\n        }\n        c += a.s;\n    }\n    r.s = (c < 0) ? -1 : 0;\n    if (c > 0) {\n        r[i++] = c;\n    }\n    else if (c < -1)\n        r[i++] = this.DV + c;\n    r.t = i;\n    r.clamp();\n}\n// (public) this + a\nfunction bnAdd(a) {\n    var r = nbi();\n    this.addTo(a, r);\n    return r;\n}\n// (public) this - a\nfunction bnSubtract(a) {\n    var r = nbi();\n    this.subTo(a, r);\n    return r;\n}\n// (public) this * a\nfunction bnMultiply(a) {\n    var r = nbi();\n    this.multiplyTo(a, r);\n    return r;\n}\n// (public) this^2\nfunction bnSquare() {\n    var r = nbi();\n    this.squareTo(r);\n    return r;\n}\n// (public) this / a\nfunction bnDivide(a) {\n    var r = nbi();\n    this.divRemTo(a, r, null);\n    return r;\n}\n// (public) this % a\nfunction bnRemainder(a) {\n    var r = nbi();\n    this.divRemTo(a, null, r);\n    return r;\n}\n// (public) [this/a,this%a]\nfunction bnDivideAndRemainder(a) {\n    var q = nbi(), r = nbi();\n    this.divRemTo(a, q, r);\n    return new Array(q, r);\n}\n// (protected) this *= n, this >= 0, 1 < n < DV\nfunction bnpDMultiply(n) {\n    this[this.t] = this.am(0, n - 1, this, 0, 0, this.t);\n    ++this.t;\n    this.clamp();\n}\n// (protected) this += n << w words, this >= 0\nfunction bnpDAddOffset(n, w) {\n    if (n == 0)\n        return;\n    while (this.t <= w)\n        this[this.t++] = 0;\n    this[w] += n;\n    while (this[w] >= this.DV) {\n        this[w] -= this.DV;\n        if (++w >= this.t)\n            this[this.t++] = 0;\n        ++this[w];\n    }\n}\n// A \"null\" reducer\nfunction NullExp() {\n}\nfunction nNop(x) {\n    return x;\n}\nfunction nMulTo(x, y, r) {\n    x.multiplyTo(y, r);\n}\nfunction nSqrTo(x, r) {\n    x.squareTo(r);\n}\nNullExp.prototype.convert = nNop;\nNullExp.prototype.revert = nNop;\nNullExp.prototype.mulTo = nMulTo;\nNullExp.prototype.sqrTo = nSqrTo;\n// (public) this^e\nfunction bnPow(e) {\n    return this.exp(e, new NullExp());\n}\n// (protected) r = lower n words of \"this * a\", a.t <= n\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyLowerTo(a, n, r) {\n    var i = Math.min(this.t + a.t, n);\n    r.s = 0; // assumes a,this >= 0\n    r.t = i;\n    while (i > 0)\n        r[--i] = 0;\n    var j;\n    for (j = r.t - this.t; i < j; ++i)\n        r[i + this.t] = this.am(0, a[i], r, i, 0, this.t);\n    for (j = Math.min(a.t, n); i < j; ++i)\n        this.am(0, a[i], r, i, 0, n - i);\n    r.clamp();\n}\n// (protected) r = \"this * a\" without lower n words, n > 0\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyUpperTo(a, n, r) {\n    --n;\n    var i = r.t = this.t + a.t - n;\n    r.s = 0; // assumes a,this >= 0\n    while (--i >= 0)\n        r[i] = 0;\n    for (i = Math.max(n - this.t, 0); i < a.t; ++i)\n        r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n);\n    r.clamp();\n    r.drShiftTo(1, r);\n}\n// Barrett modular reduction\nfunction Barrett(m) {\n    // setup Barrett\n    this.r2 = nbi();\n    this.q3 = nbi();\n    BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);\n    this.mu = this.r2.divide(m);\n    this.m = m;\n}\nfunction barrettConvert(x) {\n    if (x.s < 0 || x.t > 2 * this.m.t) {\n        return x.mod(this.m);\n    }\n    else if (x.compareTo(this.m) < 0) {\n        return x;\n    }\n    else {\n        var r = nbi();\n        x.copyTo(r);\n        this.reduce(r);\n        return r;\n    }\n}\nfunction barrettRevert(x) {\n    return x;\n}\n// x = x mod m (HAC 14.42)\nfunction barrettReduce(x) {\n    x.drShiftTo(this.m.t - 1, this.r2);\n    if (x.t > this.m.t + 1) {\n        x.t = this.m.t + 1;\n        x.clamp();\n    }\n    this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);\n    this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);\n    while (x.compareTo(this.r2) < 0)\n        x.dAddOffset(1, this.m.t + 1);\n    x.subTo(this.r2, x);\n    while (x.compareTo(this.m) >= 0)\n        x.subTo(this.m, x);\n}\n// r = x^2 mod m; x != r\nfunction barrettSqrTo(x, r) {\n    x.squareTo(r);\n    this.reduce(r);\n}\n// r = x*y mod m; x,y != r\nfunction barrettMulTo(x, y, r) {\n    x.multiplyTo(y, r);\n    this.reduce(r);\n}\nBarrett.prototype.convert = barrettConvert;\nBarrett.prototype.revert = barrettRevert;\nBarrett.prototype.reduce = barrettReduce;\nBarrett.prototype.mulTo = barrettMulTo;\nBarrett.prototype.sqrTo = barrettSqrTo;\n// (public) this^e % m (HAC 14.85)\nfunction bnModPow(e, m) {\n    // we switched to leemons bigint lib for modpow, as this is faster on safari browsers (reduced the decryption times: 9s -> 3,4s)\n    // TODO introduce switch for other browsers, as they are slower (by factor 0.5) because of the conversion overhead\n    var xHex = this.toString(16);\n    var eHex = e.toString(16);\n    var mHex = m.toString(16);\n    var result = powMod(str2bigInt(xHex, 16), str2bigInt(eHex, 16), str2bigInt(mHex, 16));\n    return new BigInteger(bigInt2str(result, 16), 16);\n    //  var i = e.bitLength(), k, r = nbv(1), z;\n    //  if(i <= 0) return r;\n    //  else if(i < 18) k = 1;\n    //  else if(i < 48) k = 3;\n    //  else if(i < 144) k = 4;\n    //  else if(i < 768) k = 5;\n    //  else k = 6;\n    //  if(i < 8)\n    //    z = new Classic(m);\n    //  else if(m.isEven())\n    //    z = new Barrett(m);\n    //  else\n    //    z = new Montgomery(m);\n    //\n    //  // precomputation\n    //  var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;\n    //  g[1] = z.convert(this);\n    //  if(k > 1) {\n    //    var g2 = nbi();\n    //    z.sqrTo(g[1],g2);\n    //    while(n <= km) {\n    //      g[n] = nbi();\n    //      z.mulTo(g2,g[n-2],g[n]);\n    //      n += 2;\n    //    }\n    //  }\n    //\n    //  var j = e.t-1, w, is1 = true, r2 = nbi(), t;\n    //  i = nbits(e[j])-1;\n    //  while(j >= 0) {\n    //    if(i >= k1) w = (e[j]>>(i-k1))&km;\n    //    else {\n    //      w = (e[j]&((1<<(i+1))-1))<<(k1-i);\n    //      if(j > 0) w |= e[j-1]>>(this.DB+i-k1);\n    //    }\n    //\n    //    n = k;\n    //    while((w&1) == 0) { w >>= 1; --n; }\n    //    if((i -= n) < 0) { i += this.DB; --j; }\n    //    if(is1) {\t// ret == 1, don't bother squaring or multiplying it\n    //      g[w].copyTo(r);\n    //      is1 = false;\n    //    }\n    //    else {\n    //      while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }\n    //      if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }\n    //      z.mulTo(r2,g[w],r);\n    //    }\n    //\n    //    while(j >= 0 && (e[j]&(1<<i)) == 0) {\n    //      z.sqrTo(r,r2); t = r; r = r2; r2 = t;\n    //      if(--i < 0) { i = this.DB-1; --j; }\n    //    }\n    //  }\n    //  return z.revert(r);\n}\n// (public) gcd(this,a) (HAC 14.54)\nfunction bnGCD(a) {\n    var x = (this.s < 0) ? this.negate() : this.clone();\n    var y = (a.s < 0) ? a.negate() : a.clone();\n    if (x.compareTo(y) < 0) {\n        var t = x;\n        x = y;\n        y = t;\n    }\n    var i = x.getLowestSetBit(), g = y.getLowestSetBit();\n    if (g < 0)\n        return x;\n    if (i < g)\n        g = i;\n    if (g > 0) {\n        x.rShiftTo(g, x);\n        y.rShiftTo(g, y);\n    }\n    while (x.signum() > 0) {\n        if ((i = x.getLowestSetBit()) > 0)\n            x.rShiftTo(i, x);\n        if ((i = y.getLowestSetBit()) > 0)\n            y.rShiftTo(i, y);\n        if (x.compareTo(y) >= 0) {\n            x.subTo(y, x);\n            x.rShiftTo(1, x);\n        }\n        else {\n            y.subTo(x, y);\n            y.rShiftTo(1, y);\n        }\n    }\n    if (g > 0)\n        y.lShiftTo(g, y);\n    return y;\n}\n// (protected) this % n, n < 2^26\nfunction bnpModInt(n) {\n    if (n <= 0)\n        return 0;\n    var d = this.DV % n, r = (this.s < 0) ? n - 1 : 0;\n    if (this.t > 0) {\n        if (d == 0) {\n            r = this[0] % n;\n        }\n        else {\n            for (var i = this.t - 1; i >= 0; --i)\n                r = (d * r + this[i]) % n;\n        }\n    }\n    return r;\n}\n// (public) 1/this % m (HAC 14.61)\nfunction bnModInverse(m) {\n    var ac = m.isEven();\n    if ((this.isEven() && ac) || m.signum() == 0)\n        return BigInteger.ZERO;\n    var u = m.clone(), v = this.clone();\n    var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);\n    while (u.signum() != 0) {\n        while (u.isEven()) {\n            u.rShiftTo(1, u);\n            if (ac) {\n                if (!a.isEven() || !b.isEven()) {\n                    a.addTo(this, a);\n                    b.subTo(m, b);\n                }\n                a.rShiftTo(1, a);\n            }\n            else if (!b.isEven())\n                b.subTo(m, b);\n            b.rShiftTo(1, b);\n        }\n        while (v.isEven()) {\n            v.rShiftTo(1, v);\n            if (ac) {\n                if (!c.isEven() || !d.isEven()) {\n                    c.addTo(this, c);\n                    d.subTo(m, d);\n                }\n                c.rShiftTo(1, c);\n            }\n            else if (!d.isEven())\n                d.subTo(m, d);\n            d.rShiftTo(1, d);\n        }\n        if (u.compareTo(v) >= 0) {\n            u.subTo(v, u);\n            if (ac)\n                a.subTo(c, a);\n            b.subTo(d, b);\n        }\n        else {\n            v.subTo(u, v);\n            if (ac)\n                c.subTo(a, c);\n            d.subTo(b, d);\n        }\n    }\n    if (v.compareTo(BigInteger.ONE) != 0)\n        return BigInteger.ZERO;\n    if (d.compareTo(m) >= 0)\n        return d.subtract(m);\n    if (d.signum() < 0)\n        d.addTo(m, d);\n    else\n        return d;\n    if (d.signum() < 0)\n        return d.add(m);\n    else\n        return d;\n}\nvar lowprimes = [\n    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,\n    113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,\n    241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,\n    383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521,\n    523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,\n    673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,\n    829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991,\n    997\n];\nvar lplim = (1 << 26) / lowprimes[lowprimes.length - 1];\n// (public) test primality with certainty >= 1-.5^t\nfunction bnIsProbablePrime(t) {\n    var i, x = this.abs();\n    if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) {\n        for (i = 0; i < lowprimes.length; ++i)\n            if (x[0] == lowprimes[i])\n                return true;\n        return false;\n    }\n    if (x.isEven())\n        return false;\n    i = 1;\n    while (i < lowprimes.length) {\n        var m = lowprimes[i], j = i + 1;\n        while (j < lowprimes.length && m < lplim)\n            m *= lowprimes[j++];\n        m = x.modInt(m);\n        while (i < j)\n            if (m % lowprimes[i++] == 0)\n                return false;\n    }\n    return x.millerRabin(t);\n}\n// (protected) true if probably prime (HAC 4.24, Miller-Rabin)\nfunction bnpMillerRabin(t) {\n    var n1 = this.subtract(BigInteger.ONE);\n    var k = n1.getLowestSetBit();\n    if (k <= 0)\n        return false;\n    var r = n1.shiftRight(k);\n    t = (t + 1) >> 1;\n    if (t > lowprimes.length)\n        t = lowprimes.length;\n    var a = nbi();\n    for (var i = 0; i < t; ++i) {\n        //Pick bases at random, instead of starting at 2\n        // TUTAO: It is fine to use Math.random() instead secure random here because it is only used for checking if the number is a prime. The number itself is generated with the secure random number generator.\n        a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]);\n        var y = a.modPow(r, this);\n        if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {\n            var j = 1;\n            while (j++ < k && y.compareTo(n1) != 0) {\n                y = y.modPowInt(2, this);\n                if (y.compareTo(BigInteger.ONE) == 0)\n                    return false;\n            }\n            if (y.compareTo(n1) != 0)\n                return false;\n        }\n    }\n    return true;\n}\n// protected\nBigInteger.prototype.chunkSize = bnpChunkSize;\nBigInteger.prototype.toRadix = bnpToRadix;\nBigInteger.prototype.fromRadix = bnpFromRadix;\nBigInteger.prototype.fromNumber = bnpFromNumber;\nBigInteger.prototype.bitwiseTo = bnpBitwiseTo;\nBigInteger.prototype.changeBit = bnpChangeBit;\nBigInteger.prototype.addTo = bnpAddTo;\nBigInteger.prototype.dMultiply = bnpDMultiply;\nBigInteger.prototype.dAddOffset = bnpDAddOffset;\nBigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;\nBigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;\nBigInteger.prototype.modInt = bnpModInt;\nBigInteger.prototype.millerRabin = bnpMillerRabin;\n// public\nBigInteger.prototype.clone = bnClone;\nBigInteger.prototype.intValue = bnIntValue;\nBigInteger.prototype.byteValue = bnByteValue;\nBigInteger.prototype.shortValue = bnShortValue;\nBigInteger.prototype.signum = bnSigNum;\nBigInteger.prototype.toByteArray = bnToByteArray;\nBigInteger.prototype.equals = bnEquals;\nBigInteger.prototype.min = bnMin;\nBigInteger.prototype.max = bnMax;\nBigInteger.prototype.and = bnAnd;\nBigInteger.prototype.or = bnOr;\nBigInteger.prototype.xor = bnXor;\nBigInteger.prototype.andNot = bnAndNot;\nBigInteger.prototype.not = bnNot;\nBigInteger.prototype.shiftLeft = bnShiftLeft;\nBigInteger.prototype.shiftRight = bnShiftRight;\nBigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;\nBigInteger.prototype.bitCount = bnBitCount;\nBigInteger.prototype.testBit = bnTestBit;\nBigInteger.prototype.setBit = bnSetBit;\nBigInteger.prototype.clearBit = bnClearBit;\nBigInteger.prototype.flipBit = bnFlipBit;\nBigInteger.prototype.add = bnAdd;\nBigInteger.prototype.subtract = bnSubtract;\nBigInteger.prototype.multiply = bnMultiply;\nBigInteger.prototype.divide = bnDivide;\nBigInteger.prototype.remainder = bnRemainder;\nBigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;\nBigInteger.prototype.modPow = bnModPow;\nBigInteger.prototype.modInverse = bnModInverse;\nBigInteger.prototype.pow = bnPow;\nBigInteger.prototype.gcd = bnGCD;\nBigInteger.prototype.isProbablePrime = bnIsProbablePrime;\n// JSBN-specific extension\nBigInteger.prototype.square = bnSquare;\n// BigInteger interfaces not implemented in jsbn:\n// BigInteger(int signum, byte[] magnitude)\n// double doubleValue()\n// float floatValue()\n// int hashCode()\n// long longValue()\n// static BigInteger valueOf(long val)\n// Depends on jsbn.js and rng.js\n// Version 1.1: support utf-8 encoding in pkcs1pad2\n// convert a (hex) string to a bignum object\nexport function parseBigInt(str, r) {\n    return new BigInteger(str, r);\n}\nfunction linebrk(s, n) {\n    var ret = \"\";\n    var i = 0;\n    while (i + n < s.length) {\n        ret += s.substring(i, i + n) + \"\\n\";\n        i += n;\n    }\n    return ret + s.substring(i, s.length);\n}\nfunction byte2Hex(b) {\n    if (b < 0x10) {\n        return \"0\" + b.toString(16);\n    }\n    else {\n        return b.toString(16);\n    }\n}\n// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint\nfunction pkcs1pad2(s, n) {\n    if (n < s.length + 11) { // TODO: fix for utf-8\n        alert(\"Message too long for RSA\");\n        return null;\n    }\n    var ba = new Array();\n    var i = s.length - 1;\n    while (i >= 0 && n > 0) {\n        var c = s.charCodeAt(i--);\n        if (c < 128) { // encode using utf-8\n            ba[--n] = c;\n        }\n        else if ((c > 127) && (c < 2048)) {\n            ba[--n] = (c & 63) | 128;\n            ba[--n] = (c >> 6) | 192;\n        }\n        else {\n            ba[--n] = (c & 63) | 128;\n            ba[--n] = ((c >> 6) & 63) | 128;\n            ba[--n] = (c >> 12) | 224;\n        }\n    }\n    ba[--n] = 0;\n    var rng = new SecureRandom();\n    var x = new Array();\n    while (n > 2) { // random non-zero pad\n        x[0] = 0;\n        while (x[0] == 0)\n            rng.nextBytes(x);\n        ba[--n] = x[0];\n    }\n    ba[--n] = 2;\n    ba[--n] = 0;\n    return new BigInteger(ba);\n}\n// \"empty\" RSA key constructor\nexport function RSAKey() {\n    this.n = null;\n    this.e = 0;\n    this.d = null;\n    this.p = null;\n    this.q = null;\n    this.dmp1 = null;\n    this.dmq1 = null;\n    this.coeff = null;\n}\n// Set the public key fields N and e from hex strings\nfunction RSASetPublic(N, E) {\n    if (N != null && E != null && N.length > 0 && E.length > 0) {\n        this.n = parseBigInt(N, 16);\n        this.e = parseInt(E, 16);\n    }\n    else {\n        alert(\"Invalid RSA public key\");\n    }\n}\n// Perform raw public operation on \"x\": return x^e (mod n)\nfunction RSADoPublic(x) {\n    return x.modPowInt(this.e, this.n);\n}\n// Return the PKCS#1 RSA encryption of \"text\" as an even-length hex string\nfunction RSAEncrypt(text) {\n    var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);\n    if (m == null)\n        return null;\n    var c = this.doPublic(m);\n    if (c == null)\n        return null;\n    var h = c.toString(16);\n    if ((h.length & 1) == 0)\n        return h;\n    else\n        return \"0\" + h;\n}\n// Return the PKCS#1 RSA encryption of \"text\" as a Base64-encoded string\n//function RSAEncryptB64(text) {\n//  var h = this.encrypt(text);\n//  if(h) return hex2b64(h); else return null;\n//}\n// protected\nRSAKey.prototype.doPublic = RSADoPublic;\n// public\nRSAKey.prototype.setPublic = RSASetPublic;\nRSAKey.prototype.encrypt = RSAEncrypt;\n//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;\n// Depends on rsa.js and jsbn2.js\n// Version 1.1: support utf-8 decoding in pkcs1unpad2\n// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext\nfunction pkcs1unpad2(d, n) {\n    var b = d.toByteArray();\n    var i = 0;\n    while (i < b.length && b[i] == 0)\n        ++i;\n    if (b.length - i != n - 1 || b[i] != 2) {\n        return null;\n    }\n    ++i;\n    while (b[i] != 0)\n        if (++i >= b.length)\n            return null;\n    var ret = \"\";\n    while (++i < b.length) {\n        var c = b[i] & 255;\n        if (c < 128) { // utf-8 decode\n            ret += String.fromCharCode(c);\n        }\n        else if ((c > 191) && (c < 224)) {\n            ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));\n            ++i;\n        }\n        else {\n            ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));\n            i += 2;\n        }\n    }\n    return ret;\n}\n// Set the private key fields N, e, and d from hex strings\nfunction RSASetPrivate(N, E, D) {\n    if (N != null && E != null && N.length > 0 && E.length > 0) {\n        this.n = parseBigInt(N, 16);\n        this.e = parseInt(E, 16);\n        this.d = parseBigInt(D, 16);\n    }\n    else {\n        alert(\"Invalid RSA private key\");\n    }\n}\n// Set the private key fields N, e, d and CRT params from hex strings\nfunction RSASetPrivateEx(N, E, D, P, Q, DP, DQ, C) {\n    if (N != null && E != null && N.length > 0 && E.length > 0) {\n        this.n = parseBigInt(N, 16);\n        this.e = parseInt(E, 16);\n        this.d = parseBigInt(D, 16);\n        this.p = parseBigInt(P, 16);\n        this.q = parseBigInt(Q, 16);\n        this.dmp1 = parseBigInt(DP, 16);\n        this.dmq1 = parseBigInt(DQ, 16);\n        this.coeff = parseBigInt(C, 16);\n    }\n    else {\n        alert(\"Invalid RSA private key\");\n    }\n}\n// Generate a new random private key B bits long, using public expt E\nfunction RSAGenerate(B, E) {\n    var rng = new SecureRandom();\n    var qs = B >> 1;\n    this.e = parseInt(E, 16);\n    var ee = new BigInteger(E, 16);\n    for (;;) {\n        for (;;) {\n            this.p = new BigInteger(B - qs, 10, rng); // tutao: changed parameter b from 1 to 10 (=> 5 rounds); according to HAC 4.49, we only need 2 rounds && discussion: https://github.com/digitalbazaar/forge/issues/28\n            // tutao: the prime probability is already guaranteed by the BigInteger constructor above; if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;\n            if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0)\n                break;\n        }\n        for (;;) {\n            // tutao: same changes as above\n            this.q = new BigInteger(qs, 10, rng);\n            if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0)\n                break;\n        }\n        if (this.p.compareTo(this.q) <= 0) {\n            var t = this.p;\n            this.p = this.q;\n            this.q = t;\n        }\n        var p1 = this.p.subtract(BigInteger.ONE);\n        var q1 = this.q.subtract(BigInteger.ONE);\n        var phi = p1.multiply(q1);\n        if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {\n            this.n = this.p.multiply(this.q);\n            this.d = ee.modInverse(phi);\n            this.dmp1 = this.d.mod(p1);\n            this.dmq1 = this.d.mod(q1);\n            this.coeff = this.q.modInverse(this.p);\n            break;\n        }\n    }\n}\n// Perform raw private operation on \"x\": return x^d (mod n)\nfunction RSADoPrivate(x) {\n    if (this.p == null || this.q == null) {\n        return x.modPow(this.d, this.n);\n    }\n    // TODO: re-calculate any missing CRT params\n    var xp = x.mod(this.p).modPow(this.dmp1, this.p);\n    var xq = x.mod(this.q).modPow(this.dmq1, this.q);\n    while (xp.compareTo(xq) < 0)\n        xp = xp.add(this.p);\n    return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);\n}\n// Return the PKCS#1 RSA decryption of \"ctext\".\n// \"ctext\" is an even-length hex string and the output is a plain string.\nfunction RSADecrypt(ctext) {\n    var c = parseBigInt(ctext, 16);\n    var m = this.doPrivate(c);\n    if (m == null)\n        return null;\n    return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);\n}\n// Return the PKCS#1 RSA decryption of \"ctext\".\n// \"ctext\" is a Base64-encoded string and the output is a plain string.\n//function RSAB64Decrypt(ctext) {\n//  var h = b64tohex(ctext);\n//  if(h) return this.decrypt(h); else return null;\n//}\n// protected\nRSAKey.prototype.doPrivate = RSADoPrivate;\n// public\nRSAKey.prototype.setPrivate = RSASetPrivate;\nRSAKey.prototype.setPrivateEx = RSASetPrivateEx;\nRSAKey.prototype.generate = RSAGenerate;\nRSAKey.prototype.decrypt = RSADecrypt;\n//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;\n////////////////////////////////////////////////////////////////////////////////////////\n// Big Integer Library v. 5.4\n// Created 2000, last modified 2009\n// Leemon Baird\n// www.leemon.com\n//\n// Version history:\n// v 5.4  3 Oct 2009\n//   - added \"var i\" to greaterShift() so i is not global. (Thanks to PŽter Szab— for finding that bug)\n//\n// v 5.3  21 Sep 2009\n//   - added randProbPrime(k) for probable primes\n//   - unrolled loop in mont_ (slightly faster)\n//   - millerRabin now takes a bigInt parameter rather than an int\n//\n// v 5.2  15 Sep 2009\n//   - fixed capitalization in call to int2bigInt in randBigInt\n//     (thanks to Emili Evripidou, Reinhold Behringer, and Samuel Macaleese for finding that bug)\n//\n// v 5.1  8 Oct 2007\n//   - renamed inverseModInt_ to inverseModInt since it doesn't change its parameters\n//   - added functions GCD and randBigInt, which call GCD_ and randBigInt_\n//   - fixed a bug found by Rob Visser (see comment with his name below)\n//   - improved comments\n//\n// This file is public domain.   You can use it for any purpose without restriction.\n// I do not guarantee that it is correct, so use it at your own risk.  If you use\n// it for something interesting, I'd appreciate hearing about it.  If you find\n// any bugs or make any improvements, I'd appreciate hearing about those too.\n// It would also be nice if my name and URL were left in the comments.  But none\n// of that is required.\n//\n// This code defines a bigInt library for arbitrary-precision integers.\n// A bigInt is an array of integers storing the value in chunks of bpe bits,\n// little endian (buff[0] is the least significant word).\n// Negative bigInts are stored two's complement.  Almost all the functions treat\n// bigInts as nonnegative.  The few that view them as two's complement say so\n// in their comments.  Some functions assume their parameters have at least one\n// leading zero element. Functions with an underscore at the end of the name put\n// their answer into one of the arrays passed in, and have unpredictable behavior\n// in case of overflow, so the caller must make sure the arrays are big enough to\n// hold the answer.  But the average user should never have to call any of the\n// underscored functions.  Each important underscored function has a wrapper function\n// of the same name without the underscore that takes care of the details for you.\n// For each underscored function where a parameter is modified, that same variable\n// must not be used as another argument too.  So, you cannot square x by doing\n// multMod_(x,x,n).  You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n).\n// Or simply use the multMod(x,x,n) function without the underscore, where\n// such issues never arise, because non-underscored functions never change\n// their parameters; they always allocate new memory for the answer that is returned.\n//\n// These functions are designed to avoid frequent dynamic memory allocation in the inner loop.\n// For most functions, if it needs a BigInt as a local variable it will actually use\n// a global, and will only allocate to it only when it's not the right size.  This ensures\n// that when a function is called repeatedly with same-sized parameters, it only allocates\n// memory on the first call.\n//\n// Note that for cryptographic purposes, the calls to Math.random() must\n// be replaced with calls to a better pseudorandom number generator.\n//\n// In the following, \"bigInt\" means a bigInt with at least one leading zero element,\n// and \"integer\" means a nonnegative integer less than radix.  In some cases, integer\n// can be negative.  Negative bigInts are 2s complement.\n//\n// The following functions do not modify their inputs.\n// Those returning a bigInt, string, or Array will dynamically allocate memory for that value.\n// Those returning a boolean will return the integer 0 (false) or 1 (true).\n// Those returning boolean or int will not allocate memory except possibly on the first\n// time they're called with a given parameter size.\n//\n// bigInt  add(x,y)               //return (x+y) for bigInts x and y.\n// bigInt  addInt(x,n)            //return (x+n) where x is a bigInt and n is an integer.\n// string  bigInt2str(x,base)     //return a string form of bigInt x in a given base, with 2 <= base <= 95\n// int     bitSize(x)             //return how many bits long the bigInt x is, not counting leading zeros\n// bigInt  dup(x)                 //return a copy of bigInt x\n// boolean equals(x,y)            //is the bigInt x equal to the bigint y?\n// boolean equalsInt(x,y)         //is bigint x equal to integer y?\n// bigInt  expand(x,n)            //return a copy of x with at least n elements, adding leading zeros if needed\n// Array   findPrimes(n)          //return array of all primes less than integer n\n// bigInt  GCD(x,y)               //return greatest common divisor of bigInts x and y (each with same number of elements).\n// boolean greater(x,y)           //is x>y?  (x and y are nonnegative bigInts)\n// boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?\n// bigInt  int2bigInt(t,n,m)      //return a bigInt equal to integer t, with at least n bits and m array elements\n// bigInt  inverseMod(x,n)        //return (x**(-1) mod n) for bigInts x and n.  If no inverse exists, it returns null\n// int     inverseModInt(x,n)     //return x**(-1) mod n, for integers x and n.  Return 0 if there is no inverse\n// boolean isZero(x)              //is the bigInt x equal to zero?\n// boolean millerRabin(x,b)       //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is bigInt, 1<b<x)\n// boolean millerRabinInt(x,b)    //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is int,    1<b<x)\n// bigInt  mod(x,n)               //return a new bigInt equal to (x mod n) for bigInts x and n.\n// int     modInt(x,n)            //return x mod n for bigInt x and integer n.\n// bigInt  mult(x,y)              //return x*y for bigInts x and y. This is faster when y<x.\n// bigInt  multMod(x,y,n)         //return (x*y mod n) for bigInts x,y,n.  For greater speed, let y<x.\n// boolean negative(x)            //is bigInt x negative?\n// bigInt  powMod(x,y,n)          //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.  0**0=1. Faster for odd n.\n// bigInt  randBigInt(n,s)        //return an n-bit random BigInt (n>=1).  If s=1, then the most significant of those n bits is set to 1.\n// bigInt  randTruePrime(k)       //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.\n// bigInt  randProbPrime(k)       //return a new, random, k-bit, probable prime bigInt (probability it's composite less than 2^-80).\n// bigInt  str2bigInt(s,b,n,m)    //return a bigInt for number represented in string s in base b with at least n bits and m array elements\n// bigInt  sub(x,y)               //return (x-y) for bigInts x and y.  Negative answers will be 2s complement\n// bigInt  trim(x,k)              //return a copy of x with exactly k leading zero elements\n//\n//\n// The following functions each have a non-underscored version, which most users should call instead.\n// These functions each write to a single parameter, and the caller is responsible for ensuring the array\n// passed in is large enough to hold the result.\n//\n// void    addInt_(x,n)          //do x=x+n where x is a bigInt and n is an integer\n// void    add_(x,y)             //do x=x+y for bigInts x and y\n// void    copy_(x,y)            //do x=y on bigInts x and y\n// void    copyInt_(x,n)         //do x=n on bigInt x and integer n\n// void    GCD_(x,y)             //set x to the greatest common divisor of bigInts x and y, (y is destroyed).  (This never overflows its array).\n// boolean inverseMod_(x,n)      //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist\n// void    mod_(x,n)             //do x=x mod n for bigInts x and n. (This never overflows its array).\n// void    mult_(x,y)            //do x=x*y for bigInts x and y.\n// void    multMod_(x,y,n)       //do x=x*y  mod n for bigInts x,y,n.\n// void    powMod_(x,y,n)        //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation.  0**0=1.\n// void    randBigInt_(b,n,s)    //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.\n// void    randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.\n// void    sub_(x,y)             //do x=x-y for bigInts x and y. Negative answers will be 2s complement.\n//\n// The following functions do NOT have a non-underscored version.\n// They each write a bigInt result to one or more parameters.  The caller is responsible for\n// ensuring the arrays passed in are large enough to hold the results.\n//\n// void addShift_(x,y,ys)       //do x=x+(y<<(ys*bpe))\n// void carry_(x)               //do carries and borrows so each element of the bigInt x fits in bpe bits.\n// void divide_(x,y,q,r)        //divide x by y giving quotient q and remainder r\n// int  divInt_(x,n)            //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).\n// int  eGCD_(x,y,d,a,b)        //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y\n// void halve_(x)               //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement.  (This never overflows its array).\n// void leftShift_(x,n)         //left shift bigInt x by n bits.  n<bpe.\n// void linComb_(x,y,a,b)       //do x=a*x+b*y for bigInts x and y and integers a and b\n// void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys\n// void mont_(x,y,n,np)         //Montgomery multiplication (see comments where the function is defined)\n// void multInt_(x,n)           //do x=x*n where x is a bigInt and n is an integer.\n// void rightShift_(x,n)        //right shift bigInt x by n bits.  0 <= n < bpe. (This never overflows its array).\n// void squareMod_(x,n)         //do x=x*x  mod n for bigInts x,n\n// void subShift_(x,y,ys)       //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.\n//\n// The following functions are based on algorithms from the _Handbook of Applied Cryptography_\n//    powMod_()           = algorithm 14.94, Montgomery exponentiation\n//    eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_\n//    GCD_()              = algorothm 14.57, Lehmer's algorithm\n//    mont_()             = algorithm 14.36, Montgomery multiplication\n//    divide_()           = algorithm 14.20  Multiple-precision division\n//    squareMod_()        = algorithm 14.16  Multiple-precision squaring\n//    randTruePrime_()    = algorithm  4.62, Maurer's algorithm\n//    millerRabin()       = algorithm  4.24, Miller-Rabin algorithm\n//\n// Profiling shows:\n//     randTruePrime_() spends:\n//         10% of its time in calls to powMod_()\n//         85% of its time in calls to millerRabin()\n//     millerRabin() spends:\n//         99% of its time in calls to powMod_()   (always with a base of 2)\n//     powMod_() spends:\n//         94% of its time in calls to mont_()  (almost always with x==y)\n//\n// This suggests there are several ways to speed up this library slightly:\n//     - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)\n//         -- this should especially focus on being fast when raising 2 to a power mod n\n//     - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test\n//     - tune the parameters in randTruePrime_(), including c, m, and recLimit\n//     - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking\n//       within the loop when all the parameters are the same length.\n//\n// There are several ideas that look like they wouldn't help much at all:\n//     - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)\n//     - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)\n//     - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square\n//       followed by a Montgomery reduction.  The intermediate answer will be twice as long as x, so that\n//       method would be slower.  This is unfortunate because the code currently spends almost all of its time\n//       doing mont_(x,x,...), both for randTruePrime_() and powMod_().  A faster method for Montgomery squaring\n//       would have a large impact on the speed of randTruePrime_() and powMod_().  HAC has a couple of poorly-worded\n//       sentences that seem to imply it's faster to do a non-modular square followed by a single\n//       Montgomery reduction, but that's obviously wrong.\n////////////////////////////////////////////////////////////////////////////////////////\n//globals\nvar bpe = 0; //bits stored per array element\nvar mask = 0; //AND this with an array element to chop it down to bpe bits\nvar radix = mask + 1; //equals 2^bpe.  A single 1 bit to the left of the last bit of mask.\n//the digits for converting to different bases\nconst digitsStr = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\\\\'\\\"+-';\n//initialize the global variables\nfor (bpe = 0; (1 << (bpe + 1)) > (1 << bpe); bpe++)\n    ; //bpe=number of bits in the mantissa on this platform\nbpe >>= 1; //bpe=number of bits in one element of the array representing the bigInt\nmask = (1 << bpe) - 1; //AND the mask with an integer to get its bpe least significant bits\nradix = mask + 1; //2^bpe.  a single 1 bit to the left of the first bit of mask\nconst one = int2bigInt(1, 1, 1); //constant used in powMod_()\n//the following global variables are scratchpad memory to\n//reduce dynamic memory allocation in the inner loop\nvar t = new Array(0);\nvar ss = t; //used in mult_()\nvar s0 = t; //used in multMod_(), squareMod_()\nvar s1 = t; //used in powMod_(), multMod_(), squareMod_()\nvar s2 = t; //used in powMod_(), multMod_()\nvar s3 = t; //used in powMod_()\nvar s4 = t;\nvar s5 = t; //used in mod_()\nvar s6 = t; //used in bigInt2str()\nvar s7 = t; //used in powMod_()\nvar T = t; //used in GCD_()\nvar sa = t; //used in mont_()\nvar mr_x1 = t;\nvar mr_r = t;\nvar mr_a = t; //used in millerRabin()\nvar eg_v = t;\nvar eg_u = t;\nvar eg_A = t;\nvar eg_B = t;\nvar eg_C = t;\nvar eg_D = t; //used in eGCD_(), inverseMod_()\nvar md_q1 = t;\nvar md_q2 = t;\nvar md_q3 = t;\nvar md_r = t;\nvar md_r1 = t;\nvar md_r2 = t;\nvar md_tt = t; //used in mod_()\nvar primes = t;\nvar pows = t;\nvar s_i = t;\nvar s_i2 = t;\nvar s_R = t;\nvar s_rm = t;\nvar s_q = t;\nvar s_n1 = t;\nvar s_a = t;\nvar s_r2 = t;\nvar s_n = t;\nvar s_b = t;\nvar s_d = t;\nvar s_x1 = t;\nvar s_x2 = t;\nvar s_aa = t; //used in randTruePrime_()\nvar rpprb = t; //used in randProbPrimeRounds() (which also uses \"primes\")\n////////////////////////////////////////////////////////////////////////////////////////\n//return array of all primes less than integer n\nfunction findPrimes(n) {\n    var i, s, p, ans;\n    s = new Array(n);\n    for (i = 0; i < n; i++)\n        s[i] = 0;\n    s[0] = 2;\n    p = 0; //first p elements of s are primes, the rest are a sieve\n    for (; s[p] < n;) { //s[p] is the pth prime\n        for (i = s[p] * s[p]; i < n; i += s[p]) //mark multiples of s[p]\n            s[i] = 1;\n        p++;\n        s[p] = s[p - 1] + 1;\n        for (; s[p] < n && s[s[p]]; s[p]++)\n            ; //find next prime (where s[p]==0)\n    }\n    ans = new Array(p);\n    for (i = 0; i < p; i++)\n        ans[i] = s[i];\n    return ans;\n}\n//does a single round of Miller-Rabin base b consider x to be a possible prime?\n//x is a bigInt, and b is an integer, with b<x\nfunction millerRabinInt(x, b) {\n    if (mr_x1.length != x.length) {\n        mr_x1 = dup(x);\n        mr_r = dup(x);\n        mr_a = dup(x);\n    }\n    copyInt_(mr_a, b);\n    return millerRabin(x, mr_a);\n}\n//does a single round of Miller-Rabin base b consider x to be a possible prime?\n//x and b are bigInts with b<x\nfunction millerRabin(x, b) {\n    var i, j, k, s;\n    if (mr_x1.length != x.length) {\n        mr_x1 = dup(x);\n        mr_r = dup(x);\n        mr_a = dup(x);\n    }\n    copy_(mr_a, b);\n    copy_(mr_r, x);\n    copy_(mr_x1, x);\n    addInt_(mr_r, -1);\n    addInt_(mr_x1, -1);\n    //s=the highest power of two that divides mr_r\n    k = 0;\n    for (i = 0; i < mr_r.length; i++)\n        for (j = 1; j < mask; j <<= 1)\n            if (x[i] & j) {\n                s = (k < mr_r.length + bpe ? k : 0);\n                i = mr_r.length;\n                j = mask;\n            }\n            else {\n                k++;\n            }\n    if (s) {\n        rightShift_(mr_r, s);\n    }\n    powMod_(mr_a, mr_r, x);\n    if (!equalsInt(mr_a, 1) && !equals(mr_a, mr_x1)) {\n        j = 1;\n        while (j <= s - 1 && !equals(mr_a, mr_x1)) {\n            squareMod_(mr_a, x);\n            if (equalsInt(mr_a, 1)) {\n                return 0;\n            }\n            j++;\n        }\n        if (!equals(mr_a, mr_x1)) {\n            return 0;\n        }\n    }\n    return 1;\n}\n//returns how many bits long the bigInt is, not counting leading zeros.\nfunction bitSize(x) {\n    var j, z, w;\n    for (j = x.length - 1; (x[j] == 0) && (j > 0); j--)\n        ;\n    for (z = 0, w = x[j]; w; (w >>= 1), z++)\n        ;\n    z += bpe * j;\n    return z;\n}\n//return a copy of x with at least n elements, adding leading zeros if needed\nfunction expand(x, n) {\n    var ans = int2bigInt(0, (x.length > n ? x.length : n) * bpe, 0);\n    copy_(ans, x);\n    return ans;\n}\n//return a k-bit true random prime using Maurer's algorithm.\nfunction randTruePrime(k) {\n    var ans = int2bigInt(0, k, 0);\n    randTruePrime_(ans, k);\n    return trim(ans, 1);\n}\n//return a k-bit random probable prime with probability of error < 2^-80\nfunction randProbPrime(k) {\n    if (k >= 600)\n        return randProbPrimeRounds(k, 2); //numbers from HAC table 4.3\n    if (k >= 550)\n        return randProbPrimeRounds(k, 4);\n    if (k >= 500)\n        return randProbPrimeRounds(k, 5);\n    if (k >= 400)\n        return randProbPrimeRounds(k, 6);\n    if (k >= 350)\n        return randProbPrimeRounds(k, 7);\n    if (k >= 300)\n        return randProbPrimeRounds(k, 9);\n    if (k >= 250)\n        return randProbPrimeRounds(k, 12); //numbers from HAC table 4.4\n    if (k >= 200)\n        return randProbPrimeRounds(k, 15);\n    if (k >= 150)\n        return randProbPrimeRounds(k, 18);\n    if (k >= 100)\n        return randProbPrimeRounds(k, 27);\n    return randProbPrimeRounds(k, 40); //number from HAC remark 4.26 (only an estimate)\n}\n//return a k-bit probable random prime using n rounds of Miller Rabin (after trial division with small primes)\nfunction randProbPrimeRounds(k, n) {\n    var ans, i, divisible, B;\n    B = 30000; //B is largest prime to use in trial division\n    ans = int2bigInt(0, k, 0);\n    //optimization: try larger and smaller B to find the best limit.\n    if (primes.length == 0) {\n        primes = findPrimes(30000);\n    } //check for divisibility by primes <=30000\n    if (rpprb.length != ans.length) {\n        rpprb = dup(ans);\n    }\n    for (;;) { //keep trying random values for ans until one appears to be prime\n        //optimization: pick a random number times L=2*3*5*...*p, plus a\n        //   random element of the list of all numbers in [0,L) not divisible by any prime up to p.\n        //   This can reduce the amount of random number generation.\n        randBigInt_(ans, k, 0); //ans = a random odd number to check\n        ans[0] |= 1;\n        divisible = 0;\n        //check ans for divisibility by small primes up to B\n        for (i = 0; (i < primes.length) && (primes[i] <= B); i++)\n            if (modInt(ans, primes[i]) == 0 && !equalsInt(ans, primes[i])) {\n                divisible = 1;\n                break;\n            }\n        //optimization: change millerRabin so the base can be bigger than the number being checked, then eliminate the while here.\n        //do n rounds of Miller Rabin, with random bases less than ans\n        for (i = 0; i < n && !divisible; i++) {\n            randBigInt_(rpprb, k, 0);\n            while (!greater(ans, rpprb)) //pick a random rpprb that's < ans\n                randBigInt_(rpprb, k, 0);\n            if (!millerRabin(ans, rpprb)) {\n                divisible = 1;\n            }\n        }\n        if (!divisible) {\n            return ans;\n        }\n    }\n}\n//return a new bigInt equal to (x mod n) for bigInts x and n.\nfunction mod(x, n) {\n    var ans = dup(x);\n    mod_(ans, n);\n    return trim(ans, 1);\n}\n//return (x+n) where x is a bigInt and n is an integer.\nfunction addInt(x, n) {\n    var ans = expand(x, x.length + 1);\n    addInt_(ans, n);\n    return trim(ans, 1);\n}\n//return x*y for bigInts x and y. This is faster when y<x.\nfunction mult(x, y) {\n    var ans = expand(x, x.length + y.length);\n    mult_(ans, y);\n    return trim(ans, 1);\n}\n//return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.  0**0=1. Faster for odd n.\nfunction powMod(x, y, n) {\n    var ans = expand(x, n.length);\n    powMod_(ans, trim(y, 2), trim(n, 2), 0); //this should work without the trim, but doesn't\n    return trim(ans, 1);\n}\n//return (x-y) for bigInts x and y.  Negative answers will be 2s complement\nfunction sub(x, y) {\n    var ans = expand(x, (x.length > y.length ? x.length + 1 : y.length + 1));\n    sub_(ans, y);\n    return trim(ans, 1);\n}\n//return (x+y) for bigInts x and y.\nfunction add(x, y) {\n    var ans = expand(x, (x.length > y.length ? x.length + 1 : y.length + 1));\n    add_(ans, y);\n    return trim(ans, 1);\n}\n//return (x**(-1) mod n) for bigInts x and n.  If no inverse exists, it returns null\nfunction inverseMod(x, n) {\n    var ans = expand(x, n.length);\n    var s;\n    s = inverseMod_(ans, n);\n    return s ? trim(ans, 1) : null;\n}\n//return (x*y mod n) for bigInts x,y,n.  For greater speed, let y<x.\nfunction multMod(x, y, n) {\n    var ans = expand(x, n.length);\n    multMod_(ans, y, n);\n    return trim(ans, 1);\n}\n/* TUTAO: not used\n //generate a k-bit true random prime using Maurer's algorithm,\n //and put it into ans.  The bigInt ans must be large enough to hold it.\n function randTruePrime_(ans,k) {\n var c,m,pm,dd,j,r,B,divisible,z,zz,recSize;\n\n if (primes.length==0)\n primes=findPrimes(30000);  //check for divisibility by primes <=30000\n\n if (pows.length==0) {\n pows=new Array(512);\n for (j=0;j<512;j++) {\n pows[j]=Math.pow(2,j/511.-1.);\n }\n }\n\n //c and m should be tuned for a particular machine and value of k, to maximize speed\n c=0.1;  //c=0.1 in HAC\n m=20;   //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits\n recLimit=20; //stop recursion when k <=recLimit.  Must have recLimit >= 2\n\n if (s_i2.length!=ans.length) {\n s_i2=dup(ans);\n s_R =dup(ans);\n s_n1=dup(ans);\n s_r2=dup(ans);\n s_d =dup(ans);\n s_x1=dup(ans);\n s_x2=dup(ans);\n s_b =dup(ans);\n s_n =dup(ans);\n s_i =dup(ans);\n s_rm=dup(ans);\n s_q =dup(ans);\n s_a =dup(ans);\n s_aa=dup(ans);\n }\n\n if (k <= recLimit) {  //generate small random primes by trial division up to its square root\n pm=(1<<((k+2)>>1))-1; //pm is binary number with all ones, just over sqrt(2^k)\n copyInt_(ans,0);\n for (dd=1;dd;) {\n dd=0;\n ans[0]= 1 | (1<<(k-1)) | Math.floor(Math.random()*(1<<k));  //random, k-bit, odd integer, with msb 1\n for (j=1;(j<primes.length) && ((primes[j]&pm)==primes[j]);j++) { //trial division by all primes 3...sqrt(2^k)\n if (0==(ans[0]%primes[j])) {\n dd=1;\n break;\n }\n }\n }\n carry_(ans);\n return;\n }\n\n B=c*k*k;    //try small primes up to B (or all the primes[] array if the largest is less than B).\n if (k>2*m)  //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits\n for (r=1; k-k*r<=m; )\n r=pows[Math.floor(Math.random()*512)];   //r=Math.pow(2,Math.random()-1);\n else\n r=.5;\n\n //simulation suggests the more complex algorithm using r=.333 is only slightly faster.\n\n recSize=Math.floor(r*k)+1;\n\n randTruePrime_(s_q,recSize);\n copyInt_(s_i2,0);\n s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe));   //s_i2=2^(k-2)\n divide_(s_i2,s_q,s_i,s_rm);                        //s_i=floor((2^(k-1))/(2q))\n\n z=bitSize(s_i);\n\n for (;;) {\n for (;;) {  //generate z-bit numbers until one falls in the range [0,s_i-1]\n randBigInt_(s_R,z,0);\n if (greater(s_i,s_R))\n break;\n }                //now s_R is in the range [0,s_i-1]\n addInt_(s_R,1);  //now s_R is in the range [1,s_i]\n add_(s_R,s_i);   //now s_R is in the range [s_i+1,2*s_i]\n\n copy_(s_n,s_q);\n mult_(s_n,s_R);\n multInt_(s_n,2);\n addInt_(s_n,1);    //s_n=2*s_R*s_q+1\n\n copy_(s_r2,s_R);\n multInt_(s_r2,2);  //s_r2=2*s_R\n\n //check s_n for divisibility by small primes up to B\n for (divisible=0,j=0; (j<primes.length) && (primes[j]<B); j++)\n if (modInt(s_n,primes[j])==0 && !equalsInt(s_n,primes[j])) {\n divisible=1;\n break;\n }\n\n if (!divisible)    //if it passes small primes check, then try a single Miller-Rabin base 2\n if (!millerRabinInt(s_n,2)) //this line represents 75% of the total runtime for randTruePrime_\n divisible=1;\n\n if (!divisible) {  //if it passes that test, continue checking s_n\n addInt_(s_n,-3);\n for (j=s_n.length-1;(s_n[j]==0) && (j>0); j--);  //strip leading zeros\n for (zz=0,w=s_n[j]; w; (w>>=1),zz++);\n zz+=bpe*j;                             //zz=number of bits in s_n, ignoring leading zeros\n for (;;) {  //generate z-bit numbers until one falls in the range [0,s_n-1]\n randBigInt_(s_a,zz,0);\n if (greater(s_n,s_a))\n break;\n }                //now s_a is in the range [0,s_n-1]\n addInt_(s_n,3);  //now s_a is in the range [0,s_n-4]\n addInt_(s_a,2);  //now s_a is in the range [2,s_n-2]\n copy_(s_b,s_a);\n copy_(s_n1,s_n);\n addInt_(s_n1,-1);\n powMod_(s_b,s_n1,s_n);   //s_b=s_a^(s_n-1) modulo s_n\n addInt_(s_b,-1);\n if (isZero(s_b)) {\n copy_(s_b,s_a);\n powMod_(s_b,s_r2,s_n);\n addInt_(s_b,-1);\n copy_(s_aa,s_n);\n copy_(s_d,s_b);\n GCD_(s_d,s_n);  //if s_b and s_n are relatively prime, then s_n is a prime\n if (equalsInt(s_d,1)) {\n copy_(ans,s_aa);\n return;     //if we've made it this far, then s_n is absolutely guaranteed to be prime\n }\n }\n }\n }\n }\n */\n//Return an n-bit random BigInt (n>=1).  If s=1, then the most significant of those n bits is set to 1.\nfunction randBigInt(n, s) {\n    var a, b;\n    a = Math.floor((n - 1) / bpe) + 2; //# array elements to hold the BigInt with a leading 0 element\n    b = int2bigInt(0, 0, a);\n    randBigInt_(b, n, s);\n    return b;\n}\n/* TUTAO: not used\n //Set b to an n-bit random BigInt.  If s=1, then the most significant of those n bits is set to 1.\n //Array b must be big enough to hold the result. Must have n>=1\n function randBigInt_(b,n,s) {\n var i,a;\n for (i=0;i<b.length;i++)\n b[i]=0;\n a=Math.floor((n-1)/bpe)+1; //# array elements to hold the BigInt\n for (i=0;i<a;i++) {\n b[i]=Math.floor(Math.random()*(1<<(bpe-1)));\n }\n b[a-1] &= (2<<((n-1)%bpe))-1;\n if (s==1)\n b[a-1] |= (1<<((n-1)%bpe));\n }\n */\n//Return the greatest common divisor of bigInts x and y (each with same number of elements).\nfunction GCD(x, y) {\n    var xc, yc;\n    xc = dup(x);\n    yc = dup(y);\n    GCD_(xc, yc);\n    return xc;\n}\n//set x to the greatest common divisor of bigInts x and y (each with same number of elements).\n//y is destroyed.\nfunction GCD_(x, y) {\n    var i, xp, yp, A, B, C, D, q, sing;\n    if (T.length != x.length) {\n        T = dup(x);\n    }\n    sing = 1;\n    while (sing) { //while y has nonzero elements other than y[0]\n        sing = 0;\n        for (i = 1; i < y.length; i++) //check if y has nonzero elements other than 0\n            if (y[i]) {\n                sing = 1;\n                break;\n            }\n        if (!sing)\n            break; //quit when y all zero elements except possibly y[0]\n        for (i = x.length; !x[i] && i >= 0; i--)\n            ; //find most significant element of x\n        xp = x[i];\n        yp = y[i];\n        A = 1;\n        B = 0;\n        C = 0;\n        D = 1;\n        while ((yp + C) && (yp + D)) {\n            q = Math.floor((xp + A) / (yp + C));\n            let qp = Math.floor((xp + B) / (yp + D));\n            if (q != qp) {\n                break;\n            }\n            t = A - q * C;\n            A = C;\n            C = t; //  do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp)\n            t = B - q * D;\n            B = D;\n            D = t;\n            t = xp - q * yp;\n            xp = yp;\n            yp = t;\n        }\n        if (B) {\n            copy_(T, x);\n            linComb_(x, y, A, B); //x=A*x+B*y\n            linComb_(y, T, D, C); //y=D*y+C*T\n        }\n        else {\n            mod_(x, y);\n            copy_(T, x);\n            copy_(x, y);\n            copy_(y, T);\n        }\n    }\n    if (y[0] == 0) {\n        return;\n    }\n    t = modInt(x, y[0]);\n    copyInt_(x, y[0]);\n    y[0] = t;\n    while (y[0]) {\n        x[0] %= y[0];\n        t = x[0];\n        x[0] = y[0];\n        y[0] = t;\n    }\n}\n//do x=x**(-1) mod n, for bigInts x and n.\n//If no inverse exists, it sets x to zero and returns 0, else it returns 1.\n//The x array must be at least as large as the n array.\nfunction inverseMod_(x, n) {\n    var k = 1 + 2 * Math.max(x.length, n.length);\n    if (!(x[0] & 1) && !(n[0] & 1)) { //if both inputs are even, then inverse doesn't exist\n        copyInt_(x, 0);\n        return 0;\n    }\n    if (eg_u.length != k) {\n        eg_u = new Array(k);\n        eg_v = new Array(k);\n        eg_A = new Array(k);\n        eg_B = new Array(k);\n        eg_C = new Array(k);\n        eg_D = new Array(k);\n    }\n    copy_(eg_u, x);\n    copy_(eg_v, n);\n    copyInt_(eg_A, 1);\n    copyInt_(eg_B, 0);\n    copyInt_(eg_C, 0);\n    copyInt_(eg_D, 1);\n    for (;;) {\n        while (!(eg_u[0] & 1)) { //while eg_u is even\n            halve_(eg_u);\n            if (!(eg_A[0] & 1) && !(eg_B[0] & 1)) { //if eg_A==eg_B==0 mod 2\n                halve_(eg_A);\n                halve_(eg_B);\n            }\n            else {\n                add_(eg_A, n);\n                halve_(eg_A);\n                sub_(eg_B, x);\n                halve_(eg_B);\n            }\n        }\n        while (!(eg_v[0] & 1)) { //while eg_v is even\n            halve_(eg_v);\n            if (!(eg_C[0] & 1) && !(eg_D[0] & 1)) { //if eg_C==eg_D==0 mod 2\n                halve_(eg_C);\n                halve_(eg_D);\n            }\n            else {\n                add_(eg_C, n);\n                halve_(eg_C);\n                sub_(eg_D, x);\n                halve_(eg_D);\n            }\n        }\n        if (!greater(eg_v, eg_u)) { //eg_v <= eg_u\n            sub_(eg_u, eg_v);\n            sub_(eg_A, eg_C);\n            sub_(eg_B, eg_D);\n        }\n        else { //eg_v > eg_u\n            sub_(eg_v, eg_u);\n            sub_(eg_C, eg_A);\n            sub_(eg_D, eg_B);\n        }\n        if (equalsInt(eg_u, 0)) {\n            if (negative(eg_C)) //make sure answer is nonnegative\n             {\n                add_(eg_C, n);\n            }\n            copy_(x, eg_C);\n            if (!equalsInt(eg_v, 1)) { //if GCD_(x,n)!=1, then there is no inverse\n                copyInt_(x, 0);\n                return 0;\n            }\n            return 1;\n        }\n    }\n}\n//return x**(-1) mod n, for integers x and n.  Return 0 if there is no inverse\nfunction inverseModInt(x, n) {\n    var a = 1, b = 0, t;\n    for (;;) {\n        if (x == 1)\n            return a;\n        if (x == 0)\n            return 0;\n        b -= a * Math.floor(n / x);\n        n %= x;\n        if (n == 1)\n            return b; //to avoid negatives, change this b to n-b, and each -= to +=\n        if (n == 0)\n            return 0;\n        a -= b * Math.floor(x / n);\n        x %= n;\n    }\n}\n//this deprecated function is for backward compatibility only.\nfunction inverseModInt_(x, n) {\n    return inverseModInt(x, n);\n}\n//Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that:\n//     v = GCD_(x,y) = a*x-b*y\n//The bigInts v, a, b, must have exactly as many elements as the larger of x and y.\nfunction eGCD_(x, y, v, a, b) {\n    var g = 0;\n    var k = Math.max(x.length, y.length);\n    if (eg_u.length != k) {\n        eg_u = new Array(k);\n        eg_A = new Array(k);\n        eg_B = new Array(k);\n        eg_C = new Array(k);\n        eg_D = new Array(k);\n    }\n    while (!(x[0] & 1) && !(y[0] & 1)) { //while x and y both even\n        halve_(x);\n        halve_(y);\n        g++;\n    }\n    copy_(eg_u, x);\n    copy_(v, y);\n    copyInt_(eg_A, 1);\n    copyInt_(eg_B, 0);\n    copyInt_(eg_C, 0);\n    copyInt_(eg_D, 1);\n    for (;;) {\n        while (!(eg_u[0] & 1)) { //while u is even\n            halve_(eg_u);\n            if (!(eg_A[0] & 1) && !(eg_B[0] & 1)) { //if A==B==0 mod 2\n                halve_(eg_A);\n                halve_(eg_B);\n            }\n            else {\n                add_(eg_A, y);\n                halve_(eg_A);\n                sub_(eg_B, x);\n                halve_(eg_B);\n            }\n        }\n        while (!(v[0] & 1)) { //while v is even\n            halve_(v);\n            if (!(eg_C[0] & 1) && !(eg_D[0] & 1)) { //if C==D==0 mod 2\n                halve_(eg_C);\n                halve_(eg_D);\n            }\n            else {\n                add_(eg_C, y);\n                halve_(eg_C);\n                sub_(eg_D, x);\n                halve_(eg_D);\n            }\n        }\n        if (!greater(v, eg_u)) { //v<=u\n            sub_(eg_u, v);\n            sub_(eg_A, eg_C);\n            sub_(eg_B, eg_D);\n        }\n        else { //v>u\n            sub_(v, eg_u);\n            sub_(eg_C, eg_A);\n            sub_(eg_D, eg_B);\n        }\n        if (equalsInt(eg_u, 0)) {\n            if (negative(eg_C)) { //make sure a (C)is nonnegative\n                add_(eg_C, y);\n                sub_(eg_D, x);\n            }\n            multInt_(eg_D, -1); ///make sure b (D) is nonnegative\n            copy_(a, eg_C);\n            copy_(b, eg_D);\n            leftShift_(v, g);\n            return;\n        }\n    }\n}\n//is bigInt x negative?\nfunction negative(x) {\n    return ((x[x.length - 1] >> (bpe - 1)) & 1);\n}\n//is (x << (shift*bpe)) > y?\n//x and y are nonnegative bigInts\n//shift is a nonnegative integer\nfunction greaterShift(x, y, shift) {\n    var i, kx = x.length, ky = y.length, k = ((kx + shift) < ky) ? (kx + shift) : ky;\n    for (i = ky - 1 - shift; i < kx && i >= 0; i++)\n        if (x[i] > 0) {\n            return 1;\n        } //if there are nonzeros in x to the left of the first column of y, then x is bigger\n    for (i = kx - 1 + shift; i < ky; i++)\n        if (y[i] > 0) {\n            return 0;\n        } //if there are nonzeros in y to the left of the first column of x, then x is not bigger\n    for (i = k - 1; i >= shift; i--)\n        if (x[i - shift] > y[i]) {\n            return 1;\n        }\n        else if (x[i - shift] < y[i])\n            return 0;\n    return 0;\n}\n//is x > y? (x and y both nonnegative)\nfunction greater(x, y) {\n    var i;\n    var k = (x.length < y.length) ? x.length : y.length;\n    for (i = x.length; i < y.length; i++)\n        if (y[i]) {\n            return 0;\n        } //y has more digits\n    for (i = y.length; i < x.length; i++)\n        if (x[i]) {\n            return 1;\n        } //x has more digits\n    for (i = k - 1; i >= 0; i--)\n        if (x[i] > y[i]) {\n            return 1;\n        }\n        else if (x[i] < y[i]) {\n            return 0;\n        }\n    return 0;\n}\n//divide x by y giving quotient q and remainder r.  (q=floor(x/y),  r=x mod y).  All 4 are bigints.\n//x must have at least one leading zero element.\n//y must be nonzero.\n//q and r must be arrays that are exactly the same length as x. (Or q can have more).\n//Must have x.length >= y.length >= 2.\nfunction divide_(x, y, q, r) {\n    var kx, ky;\n    var i, j, y1, y2, c, a, b;\n    copy_(r, x);\n    for (ky = y.length; y[ky - 1] == 0; ky--)\n        ; //ky is number of elements in y, not including leading zeros\n    //normalize: ensure the most significant element of y has its highest bit set\n    b = y[ky - 1];\n    for (a = 0; b; a++)\n        b >>= 1;\n    a = bpe - a; //a is how many bits to shift so that the high order bit of y is leftmost in its array element\n    leftShift_(y, a); //multiply both by 1<<a now, then divide both by that at the end\n    leftShift_(r, a);\n    //Rob Visser discovered a bug: the following line was originally just before the normalization.\n    for (kx = r.length; r[kx - 1] == 0 && kx > ky; kx--)\n        ; //kx is number of elements in normalized x, not including leading zeros\n    copyInt_(q, 0); // q=0\n    while (!greaterShift(y, r, kx - ky)) { // while (leftShift_(y,kx-ky) <= r) {\n        subShift_(r, y, kx - ky); //   r=r-leftShift_(y,kx-ky)\n        q[kx - ky]++; //   q[kx-ky]++;\n    } // }\n    for (i = kx - 1; i >= ky; i--) {\n        if (r[i] == y[ky - 1]) {\n            q[i - ky] = mask;\n        }\n        else {\n            q[i - ky] = Math.floor((r[i] * radix + r[i - 1]) / y[ky - 1]);\n        }\n        //The following for(;;) loop is equivalent to the commented while loop,\n        //except that the uncommented version avoids overflow.\n        //The commented loop comes from HAC, which assumes r[-1]==y[-1]==0\n        //  while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2])\n        //    q[i-ky]--;\n        for (;;) {\n            y2 = (ky > 1 ? y[ky - 2] : 0) * q[i - ky];\n            c = y2 >> bpe;\n            y2 = y2 & mask;\n            y1 = c + q[i - ky] * y[ky - 1];\n            c = y1 >> bpe;\n            y1 = y1 & mask;\n            if (c == r[i] ? y1 == r[i - 1] ? y2 > (i > 1 ? r[i - 2] : 0) : y1 > r[i - 1] : c > r[i]) {\n                q[i - ky]--;\n            }\n            else {\n                break;\n            }\n        }\n        linCombShift_(r, y, -q[i - ky], i - ky); //r=r-q[i-ky]*leftShift_(y,i-ky)\n        if (negative(r)) {\n            addShift_(r, y, i - ky); //r=r+leftShift_(y,i-ky)\n            q[i - ky]--;\n        }\n    }\n    rightShift_(y, a); //undo the normalization step\n    rightShift_(r, a); //undo the normalization step\n}\n//do carries and borrows so each element of the bigInt x fits in bpe bits.\nfunction carry_(x) {\n    var i, k, c, b;\n    k = x.length;\n    c = 0;\n    for (i = 0; i < k; i++) {\n        c += x[i];\n        b = 0;\n        if (c < 0) {\n            b = -(c >> bpe);\n            c += b * radix;\n        }\n        x[i] = c & mask;\n        c = (c >> bpe) - b;\n    }\n}\n//return x mod n for bigInt x and integer n.\nfunction modInt(x, n) {\n    var i, c = 0;\n    for (i = x.length - 1; i >= 0; i--)\n        c = (c * radix + x[i]) % n;\n    return c;\n}\n//convert the integer t into a bigInt with at least the given number of bits.\n//the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word)\n//Pad the array with leading zeros so that it has at least minSize elements.\n//There will always be at least one leading 0 element.\nfunction int2bigInt(t, bits, minSize) {\n    var i, k, buff;\n    k = Math.ceil(bits / bpe) + 1;\n    k = minSize > k ? minSize : k;\n    buff = new Array(k);\n    copyInt_(buff, t);\n    return buff;\n}\n//return the bigInt given a string representation in a given base.\n//Pad the array with leading zeros so that it has at least minSize elements.\n//If base=-1, then it reads in a space-separated list of array elements in decimal.\n//The array will always have at least one leading zero, unless base=-1.\nfunction str2bigInt(s, base, minSize) {\n    var d, i, j, x, y, kk;\n    var k = s.length;\n    if (base == -1) { //comma-separated list of array elements in decimal\n        x = new Array(0);\n        for (;;) {\n            y = new Array(x.length + 1);\n            for (i = 0; i < x.length; i++)\n                y[i + 1] = x[i];\n            y[0] = parseInt(s, 10);\n            x = y;\n            d = s.indexOf(',', 0);\n            if (d < 1) {\n                break;\n            }\n            s = s.substring(d + 1);\n            if (s.length == 0) {\n                break;\n            }\n        }\n        if (x.length < minSize) {\n            y = new Array(minSize);\n            copy_(y, x);\n            return y;\n        }\n        return x;\n    }\n    x = int2bigInt(0, base * k, 0);\n    for (i = 0; i < k; i++) {\n        d = digitsStr.indexOf(s.substring(i, i + 1), 0);\n        if (base <= 36 && d >= 36) //convert lowercase to uppercase if base<=36\n         {\n            d -= 26;\n        }\n        if (d >= base || d < 0) { //stop at first illegal character\n            break;\n        }\n        multInt_(x, base);\n        addInt_(x, d);\n    }\n    for (k = x.length; k > 0 && !x[k - 1]; k--)\n        ; //strip off leading zeros\n    k = minSize > k + 1 ? minSize : k + 1;\n    y = new Array(k);\n    kk = k < x.length ? k : x.length;\n    for (i = 0; i < kk; i++)\n        y[i] = x[i];\n    for (; i < k; i++)\n        y[i] = 0;\n    return y;\n}\n//is bigint x equal to integer y?\n//y must have less than bpe bits\nfunction equalsInt(x, y) {\n    var i;\n    if (x[0] != y) {\n        return 0;\n    }\n    for (i = 1; i < x.length; i++)\n        if (x[i]) {\n            return 0;\n        }\n    return 1;\n}\n//are bigints x and y equal?\n//this works even if x and y are different lengths and have arbitrarily many leading zeros\nfunction equals(x, y) {\n    var i;\n    var k = x.length < y.length ? x.length : y.length;\n    for (i = 0; i < k; i++)\n        if (x[i] != y[i]) {\n            return 0;\n        }\n    if (x.length > y.length) {\n        for (; i < x.length; i++)\n            if (x[i]) {\n                return 0;\n            }\n    }\n    else {\n        for (; i < y.length; i++)\n            if (y[i]) {\n                return 0;\n            }\n    }\n    return 1;\n}\n//is the bigInt x equal to zero?\nfunction isZero(x) {\n    var i;\n    for (i = 0; i < x.length; i++)\n        if (x[i]) {\n            return 0;\n        }\n    return 1;\n}\n//convert a bigInt into a string in a given base, from base 2 up to base 95.\n//Base -1 prints the contents of the array representing the number.\nfunction bigInt2str(x, base) {\n    var i, t, s = \"\";\n    if (s6.length != x.length) {\n        s6 = dup(x);\n    }\n    else {\n        copy_(s6, x);\n    }\n    if (base == -1) { //return the list of array contents\n        for (i = x.length - 1; i > 0; i--)\n            s += x[i] + ',';\n        s += x[0];\n    }\n    else { //return it in the given base\n        while (!isZero(s6)) {\n            t = divInt_(s6, base); //t=s6 % base; s6=floor(s6/base);\n            s = digitsStr.substring(t, t + 1) + s;\n        }\n    }\n    if (s.length == 0) {\n        s = \"0\";\n    }\n    return s;\n}\n//returns a duplicate of bigInt x\nfunction dup(x) {\n    var i, buff;\n    buff = new Array(x.length);\n    copy_(buff, x);\n    return buff;\n}\n//do x=y on bigInts x and y.  x must be an array at least as big as y (not counting the leading zeros in y).\nfunction copy_(x, y) {\n    var i;\n    var k = x.length < y.length ? x.length : y.length;\n    for (i = 0; i < k; i++)\n        x[i] = y[i];\n    for (i = k; i < x.length; i++)\n        x[i] = 0;\n}\n//do x=y on bigInt x and integer y.\nfunction copyInt_(x, n) {\n    var i, c;\n    for (c = n, i = 0; i < x.length; i++) {\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x+n where x is a bigInt and n is an integer.\n//x must be large enough to hold the result.\nfunction addInt_(x, n) {\n    var i, k, c, b;\n    x[0] += n;\n    k = x.length;\n    c = 0;\n    for (i = 0; i < k; i++) {\n        c += x[i];\n        b = 0;\n        if (c < 0) {\n            b = -(c >> bpe);\n            c += b * radix;\n        }\n        x[i] = c & mask;\n        c = (c >> bpe) - b;\n        if (!c)\n            return; //stop carrying as soon as the carry is zero\n    }\n}\n//right shift bigInt x by n bits.  0 <= n < bpe.\nfunction rightShift_(x, n) {\n    var i;\n    var k = Math.floor(n / bpe);\n    if (k) {\n        for (i = 0; i < x.length - k; i++) //right shift x by k elements\n            x[i] = x[i + k];\n        for (; i < x.length; i++)\n            x[i] = 0;\n        n %= bpe;\n    }\n    for (i = 0; i < x.length - 1; i++) {\n        x[i] = mask & ((x[i + 1] << (bpe - n)) | (x[i] >> n));\n    }\n    x[i] >>= n;\n}\n//do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement\nfunction halve_(x) {\n    var i;\n    for (i = 0; i < x.length - 1; i++) {\n        x[i] = mask & ((x[i + 1] << (bpe - 1)) | (x[i] >> 1));\n    }\n    x[i] = (x[i] >> 1) | (x[i] & (radix >> 1)); //most significant bit stays the same\n}\n//left shift bigInt x by n bits.\nfunction leftShift_(x, n) {\n    var i;\n    var k = Math.floor(n / bpe);\n    if (k) {\n        for (i = x.length; i >= k; i--) //left shift x by k elements\n            x[i] = x[i - k];\n        for (; i >= 0; i--)\n            x[i] = 0;\n        n %= bpe;\n    }\n    if (!n) {\n        return;\n    }\n    for (i = x.length - 1; i > 0; i--) {\n        x[i] = mask & ((x[i] << n) | (x[i - 1] >> (bpe - n)));\n    }\n    x[i] = mask & (x[i] << n);\n}\n//do x=x*n where x is a bigInt and n is an integer.\n//x must be large enough to hold the result.\nfunction multInt_(x, n) {\n    var i, k, c, b;\n    if (!n) {\n        return;\n    }\n    k = x.length;\n    c = 0;\n    for (i = 0; i < k; i++) {\n        c += x[i] * n;\n        b = 0;\n        if (c < 0) {\n            b = -(c >> bpe);\n            c += b * radix;\n        }\n        x[i] = c & mask;\n        c = (c >> bpe) - b;\n    }\n}\n//do x=floor(x/n) for bigInt x and integer n, and return the remainder\nfunction divInt_(x, n) {\n    var i, r = 0, s;\n    for (i = x.length - 1; i >= 0; i--) {\n        s = r * radix + x[i];\n        x[i] = Math.floor(s / n);\n        r = s % n;\n    }\n    return r;\n}\n//do the linear combination x=a*x+b*y for bigInts x and y, and integers a and b.\n//x must be large enough to hold the answer.\nfunction linComb_(x, y, a, b) {\n    var i, c, k, kk;\n    k = x.length < y.length ? x.length : y.length;\n    kk = x.length;\n    for (c = 0, i = 0; i < k; i++) {\n        c += a * x[i] + b * y[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; i < kk; i++) {\n        c += a * x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys.\n//x must be large enough to hold the answer.\nfunction linCombShift_(x, y, b, ys) {\n    var i, c, k, kk;\n    k = x.length < ys + y.length ? x.length : ys + y.length;\n    kk = x.length;\n    for (c = 0, i = ys; i < k; i++) {\n        c += x[i] + b * y[i - ys];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; c && i < kk; i++) {\n        c += x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x+(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.\n//x must be large enough to hold the answer.\nfunction addShift_(x, y, ys) {\n    var i, c, k, kk;\n    k = x.length < ys + y.length ? x.length : ys + y.length;\n    kk = x.length;\n    for (c = 0, i = ys; i < k; i++) {\n        c += x[i] + y[i - ys];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; c && i < kk; i++) {\n        c += x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x-(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.\n//x must be large enough to hold the answer.\nfunction subShift_(x, y, ys) {\n    var i, c, k, kk;\n    k = x.length < ys + y.length ? x.length : ys + y.length;\n    kk = x.length;\n    for (c = 0, i = ys; i < k; i++) {\n        c += x[i] - y[i - ys];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; c && i < kk; i++) {\n        c += x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x-y for bigInts x and y.\n//x must be large enough to hold the answer.\n//negative answers will be 2s complement\nfunction sub_(x, y) {\n    var i, c, k, kk;\n    k = x.length < y.length ? x.length : y.length;\n    for (c = 0, i = 0; i < k; i++) {\n        c += x[i] - y[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; c && i < x.length; i++) {\n        c += x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x+y for bigInts x and y.\n//x must be large enough to hold the answer.\nfunction add_(x, y) {\n    var i, c, k, kk;\n    k = x.length < y.length ? x.length : y.length;\n    for (c = 0, i = 0; i < k; i++) {\n        c += x[i] + y[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n    for (i = k; c && i < x.length; i++) {\n        c += x[i];\n        x[i] = c & mask;\n        c >>= bpe;\n    }\n}\n//do x=x*y for bigInts x and y.  This is faster when y<x.\nfunction mult_(x, y) {\n    var i;\n    if (ss.length != 2 * x.length) {\n        ss = new Array(2 * x.length);\n    }\n    copyInt_(ss, 0);\n    for (i = 0; i < y.length; i++)\n        if (y[i]) {\n            linCombShift_(ss, x, y[i], i);\n        } //ss=1*ss+y[i]*(x<<(i*bpe))\n    copy_(x, ss);\n}\n//do x=x mod n for bigInts x and n.\nfunction mod_(x, n) {\n    if (s4.length != x.length) {\n        s4 = dup(x);\n    }\n    else {\n        copy_(s4, x);\n    }\n    if (s5.length != x.length) {\n        s5 = dup(x);\n    }\n    divide_(s4, n, s5, x); //x = remainder of s4 / n\n}\n//do x=x*y mod n for bigInts x,y,n.\n//for greater speed, let y<x.\nfunction multMod_(x, y, n) {\n    var i;\n    if (s0.length != 2 * x.length) {\n        s0 = new Array(2 * x.length);\n    }\n    copyInt_(s0, 0);\n    for (i = 0; i < y.length; i++)\n        if (y[i]) {\n            linCombShift_(s0, x, y[i], i);\n        } //s0=1*s0+y[i]*(x<<(i*bpe))\n    mod_(s0, n);\n    copy_(x, s0);\n}\n//do x=x*x mod n for bigInts x,n.\nfunction squareMod_(x, n) {\n    var i, j, d, c, kx, kn, k;\n    for (kx = x.length; kx > 0 && !x[kx - 1]; kx--)\n        ; //ignore leading zeros in x\n    k = kx > n.length ? 2 * kx : 2 * n.length; //k=# elements in the product, which is twice the elements in the larger of x and n\n    if (s0.length != k) {\n        s0 = new Array(k);\n    }\n    copyInt_(s0, 0);\n    for (i = 0; i < kx; i++) {\n        c = s0[2 * i] + x[i] * x[i];\n        s0[2 * i] = c & mask;\n        c >>= bpe;\n        for (j = i + 1; j < kx; j++) {\n            c = s0[i + j] + 2 * x[i] * x[j] + c;\n            s0[i + j] = (c & mask);\n            c >>= bpe;\n        }\n        s0[i + kx] = c;\n    }\n    mod_(s0, n);\n    copy_(x, s0);\n}\n//return x with exactly k leading zero elements\nfunction trim(x, k) {\n    var i, y;\n    for (i = x.length; i > 0 && !x[i - 1]; i--)\n        ;\n    y = new Array(i + k);\n    copy_(y, x);\n    return y;\n}\n//do x=x**y mod n, where x,y,n are bigInts and ** is exponentiation.  0**0=1.\n//this is faster when n is odd.  x usually needs to have as many elements as n.\nfunction powMod_(x, y, n) {\n    var k1, k2, kn, np;\n    if (s7.length != n.length) {\n        s7 = dup(n);\n    }\n    //for even modulus, use a simple square-and-multiply algorithm,\n    //rather than using the more complex Montgomery algorithm.\n    if ((n[0] & 1) == 0) {\n        copy_(s7, x);\n        copyInt_(x, 1);\n        while (!equalsInt(y, 0)) {\n            if (y[0] & 1) {\n                multMod_(x, s7, n);\n            }\n            divInt_(y, 2);\n            squareMod_(s7, n);\n        }\n        return;\n    }\n    //calculate np from n for the Montgomery multiplications\n    copyInt_(s7, 0);\n    for (kn = n.length; kn > 0 && !n[kn - 1]; kn--)\n        ;\n    np = radix - inverseModInt(modInt(n, radix), radix);\n    s7[kn] = 1;\n    multMod_(x, s7, n); // x = x * 2**(kn*bp) mod n\n    if (s3.length != x.length) {\n        s3 = dup(x);\n    }\n    else {\n        copy_(s3, x);\n    }\n    for (k1 = y.length - 1; k1 > 0 & !y[k1]; k1--)\n        ; //k1=first nonzero element of y\n    if (y[k1] == 0) { //anything to the 0th power is 1\n        copyInt_(x, 1);\n        return;\n    }\n    for (k2 = 1 << (bpe - 1); k2 && !(y[k1] & k2); k2 >>= 1)\n        ; //k2=position of first 1 bit in y[k1]\n    for (;;) {\n        if (!(k2 >>= 1)) { //look at next bit of y\n            k1--;\n            if (k1 < 0) {\n                mont_(x, one, n, np);\n                return;\n            }\n            k2 = 1 << (bpe - 1);\n        }\n        mont_(x, x, n, np);\n        if (k2 & y[k1]) //if next bit is a 1\n         {\n            mont_(x, s3, n, np);\n        }\n    }\n}\n//do x=x*y*Ri mod n for bigInts x,y,n,\n//  where Ri = 2**(-kn*bpe) mod n, and kn is the\n//  number of elements in the n array, not\n//  counting leading zeros.\n//x array must have at least as many elemnts as the n array\n//It's OK if x and y are the same variable.\n//must have:\n//  x,y < n\n//  n is odd\n//  np = -(n^(-1)) mod radix\nfunction mont_(x, y, n, np) {\n    var i, j, c, ui, t, ks;\n    var kn = n.length;\n    var ky = y.length;\n    if (sa.length != kn) {\n        sa = new Array(kn);\n    }\n    copyInt_(sa, 0);\n    for (; kn > 0 && n[kn - 1] == 0; kn--)\n        ; //ignore leading zeros of n\n    for (; ky > 0 && y[ky - 1] == 0; ky--)\n        ; //ignore leading zeros of y\n    ks = sa.length - 1; //sa will never have more than this many nonzero elements.\n    //the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large numbers\n    for (i = 0; i < kn; i++) {\n        t = sa[0] + x[i] * y[0];\n        ui = ((t & mask) * np) & mask; //the inner \"& mask\" was needed on Safari (but not MSIE) at one time\n        c = (t + ui * n[0]) >> bpe;\n        t = x[i];\n        //do sa=(sa+x[i]*y+ui*n)/b   where b=2**bpe.  Loop is unrolled 5-fold for speed\n        j = 1;\n        for (; j < ky - 4;) {\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n        }\n        for (; j < ky;) {\n            c += sa[j] + ui * n[j] + t * y[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n        }\n        for (; j < kn - 4;) {\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n        }\n        for (; j < kn;) {\n            c += sa[j] + ui * n[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n        }\n        for (; j < ks;) {\n            c += sa[j];\n            sa[j - 1] = c & mask;\n            c >>= bpe;\n            j++;\n        }\n        sa[j - 1] = c & mask;\n    }\n    if (!greater(n, sa)) {\n        sub_(sa, n);\n    }\n    copy_(x, sa);\n}\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nconst sha1 = new sjcl.hash.sha1();\n/**\n * Create the hash of the given data.\n * @param uint8Array The bytes.\n * @return The hash.\n */\nexport function sha1Hash(uint8Array) {\n    try {\n        sha1.update(sjcl.codec.arrayBuffer.toBits(uint8Array.buffer));\n        return new Uint8Array(sjcl.codec.arrayBuffer.fromBits(sha1.finalize(), false));\n    }\n    finally {\n        sha1.reset();\n    }\n}\n","// @ts-ignore[untyped-import]\nimport sjcl from \"../internal/sjcl.js\";\nimport { bitArrayToUint8Array, uint8ArrayToBitArray } from \"./Utils.js\";\nimport { hexToUint8Array } from \"@tutao/tutanota-utils\";\nimport { random } from \"../random/Randomizer.js\";\nexport let DIGITS = 6;\nconst DIGITS_POWER = \n// 0   1   2    3    4      5       6        7         8\n[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000];\nconst base32 = sjcl.codec.base32;\nexport class TotpVerifier {\n    constructor(digits = DIGITS) {\n        this._digits = digits;\n    }\n    generateSecret() {\n        let key = random.generateRandomData(16);\n        let readableKey = TotpVerifier.readableKey(key);\n        return {\n            key,\n            readableKey,\n        };\n    }\n    /**\n     * This method generates a TOTP value for the given\n     * set of parameters.\n     *\n     * @param time : a value that reflects a time\n     * @param key  :  the shared secret. It is generated if it does not exist\n     * @return: the key and a numeric String in base 10 that includes truncationDigits digits\n     */\n    generateTotp(time, key) {\n        // Using the counter\n        // First 8 bytes are for the movingFactor\n        // Compliant with base RFC 4226 (HOTP)\n        let timeHex = time.toString(16);\n        while (timeHex.length < 16)\n            timeHex = \"0\" + timeHex;\n        let msg = hexToUint8Array(timeHex);\n        let hash = this.hmac_sha(key, msg);\n        let offset = hash[hash.length - 1] & 0xf;\n        let binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);\n        let code = binary % DIGITS_POWER[this._digits];\n        return code;\n    }\n    hmac_sha(key, text) {\n        let hmac = new sjcl.misc.hmac(uint8ArrayToBitArray(key), sjcl.hash.sha1);\n        return bitArrayToUint8Array(hmac.encrypt(uint8ArrayToBitArray(text)));\n    }\n    static readableKey(key) {\n        return base32\n            .fromBits(uint8ArrayToBitArray(key))\n            .toLowerCase()\n            .replace(/(.{4})/g, \"$1 \")\n            .replace(/=/g, \"\")\n            .trim();\n    }\n}\n","import {\n\tassertNotNull,\n\tbase64ToUint8Array,\n\tdowncast,\n\tisSameTypeRef,\n\tisSameTypeRefByAttr,\n\tneverNull,\n\tnoOp,\n\tofClass,\n\tstringToUtf8Uint8Array,\n\tTypeRef,\n\tuint8ArrayToBase64,\n\tuint8ArrayToHex,\n} from \"@tutao/tutanota-utils\"\nimport { BucketPermissionType, GroupType, PermissionType } from \"../../common/TutanotaConstants\"\nimport { HttpMethod, resolveTypeReference } from \"../../common/EntityFunctions\"\nimport type { BucketKey, BucketPermission, GroupMembership, InstanceSessionKey, Permission } from \"../../entities/sys/TypeRefs.js\"\nimport {\n\tBucketKeyTypeRef,\n\tBucketPermissionTypeRef,\n\tcreateInstanceSessionKey,\n\tcreatePublicKeyData,\n\tcreateUpdatePermissionKeyData,\n\tGroupInfoTypeRef,\n\tGroupTypeRef,\n\tPermissionTypeRef,\n\tPushIdentifierTypeRef,\n} from \"../../entities/sys/TypeRefs.js\"\nimport type { Contact, InternalRecipientKeyData } from \"../../entities/tutanota/TypeRefs.js\"\nimport {\n\tContactTypeRef,\n\tcreateEncryptTutanotaPropertiesData,\n\tcreateInternalRecipientKeyData,\n\tMailTypeRef,\n\tTutanotaPropertiesTypeRef,\n} from \"../../entities/tutanota/TypeRefs.js\"\nimport { typeRefToPath } from \"../rest/EntityRestClient\"\nimport { LockedError, NotFoundError, PayloadTooLargeError, TooManyRequestsError } from \"../../common/error/RestError\"\nimport { SessionKeyNotFoundError } from \"../../common/error/SessionKeyNotFoundError\" // importing with {} from CJS modules is not supported for dist-builds currently (must be a systemjs builder bug)\nimport { CryptoError } from \"../../common/error/CryptoError\"\nimport { birthdayToIsoDate, oldBirthdayToBirthday } from \"../../common/utils/BirthdayUtils\"\nimport type { Entity, SomeEntity, TypeModel } from \"../../common/EntityTypes\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport type { EntityClient } from \"../../common/EntityClient\"\nimport { RestClient } from \"../rest/RestClient\"\nimport {\n\taes128Encrypt,\n\taes128RandomKey,\n\tbitArrayToUint8Array,\n\tdecryptKey,\n\tdecryptRsaKey,\n\tENABLE_MAC,\n\tencryptKey,\n\thexToPublicKey,\n\tIV_BYTE_LENGTH,\n\trandom,\n\tuint8ArrayToBitArray,\n} from \"@tutao/tutanota-crypto\"\nimport { RecipientNotResolvedError } from \"../../common/error/RecipientNotResolvedError\"\nimport type { RsaImplementation } from \"./RsaImplementation\"\nimport { IServiceExecutor } from \"../../common/ServiceRequest\"\nimport { EncryptTutanotaPropertiesService } from \"../../entities/tutanota/Services\"\nimport { PublicKeyService, UpdatePermissionKeyService } from \"../../entities/sys/Services\"\nimport { UserFacade } from \"../facades/UserFacade\"\nimport { Aes128Key } from \"@tutao/tutanota-crypto/dist/encryption/Aes\"\nimport { elementIdPart } from \"../../common/utils/EntityUtils.js\"\nimport { InstanceMapper } from \"./InstanceMapper.js\"\nimport { OwnerEncSessionKeysUpdateQueue } from \"./OwnerEncSessionKeysUpdateQueue.js\"\n\nassertWorkerOrNode()\n\nexport function encryptBytes(sk: Aes128Key, value: Uint8Array): Uint8Array {\n\treturn aes128Encrypt(sk, value, random.generateRandomData(IV_BYTE_LENGTH), true, ENABLE_MAC)\n}\n\nexport function encryptString(sk: Aes128Key, value: string): Uint8Array {\n\treturn aes128Encrypt(sk, stringToUtf8Uint8Array(value), random.generateRandomData(IV_BYTE_LENGTH), true, ENABLE_MAC)\n}\n\nexport class CryptoFacade {\n\t// stores a mapping from mail body id to mail body session key. the mail body of a mail is encrypted with the same session key as the mail.\n\t// so when resolving the session key of a mail we cache it for the mail's body to avoid that the body's permission (+ bucket permission) have to be loaded.\n\t// this especially improves the performance when indexing mail bodies\n\tprivate readonly sessionKeyCache: Record<string, Aes128Key> = {}\n\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly rsa: RsaImplementation,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly ownerEncSessionKeysUpdateQueue: OwnerEncSessionKeysUpdateQueue,\n\t) {}\n\n\tasync applyMigrations<T>(typeRef: TypeRef<T>, data: any): Promise<T> {\n\t\tif (isSameTypeRef(typeRef, GroupInfoTypeRef) && data._ownerGroup == null) {\n\t\t\tlet customerGroupMembership = this.userFacade.getLoggedInUser().memberships.find((g: GroupMembership) => g.groupType === GroupType.Customer) as any\n\t\t\tlet customerGroupKey = this.userFacade.getGroupKey(customerGroupMembership.group)\n\t\t\treturn this.entityClient.loadAll(PermissionTypeRef, data._id[0]).then((listPermissions: Permission[]) => {\n\t\t\t\tlet customerGroupPermission = listPermissions.find((p) => p.group === customerGroupMembership.group)\n\t\t\t\tif (!customerGroupPermission) throw new SessionKeyNotFoundError(\"Permission not found, could not apply OwnerGroup migration\")\n\t\t\t\tlet listKey = decryptKey(customerGroupKey, (customerGroupPermission as any).symEncSessionKey)\n\t\t\t\tlet groupInfoSk = decryptKey(listKey, base64ToUint8Array(data._listEncSessionKey))\n\t\t\t\tdata._ownerGroup = customerGroupMembership.getGroup()\n\t\t\t\tdata._ownerEncSessionKey = uint8ArrayToBase64(encryptKey(customerGroupKey, groupInfoSk))\n\t\t\t\treturn data\n\t\t\t})\n\t\t} else if (isSameTypeRef(typeRef, TutanotaPropertiesTypeRef) && data._ownerEncSessionKey == null) {\n\t\t\t// EncryptTutanotaPropertiesService could be removed and replaced with an Migration that writes the key\n\t\t\tlet migrationData = createEncryptTutanotaPropertiesData()\n\t\t\tdata._ownerGroup = this.userFacade.getUserGroupId()\n\t\t\tlet groupEncSessionKey = encryptKey(this.userFacade.getUserGroupKey(), aes128RandomKey())\n\t\t\tdata._ownerEncSessionKey = uint8ArrayToBase64(groupEncSessionKey)\n\t\t\tmigrationData.properties = data._id\n\t\t\tmigrationData.symEncSessionKey = groupEncSessionKey\n\t\t\tconst result = await this.serviceExecutor.post(EncryptTutanotaPropertiesService, migrationData)\n\t\t\treturn data\n\t\t} else if (isSameTypeRef(typeRef, PushIdentifierTypeRef) && data._ownerEncSessionKey == null) {\n\t\t\t// set sessionKey for allowing encryption when old instance (< v43) is updated\n\t\t\treturn resolveTypeReference(typeRef)\n\t\t\t\t.then((typeModel) => this.updateOwnerEncSessionKey(typeModel, data, this.userFacade.getUserGroupKey(), aes128RandomKey()))\n\t\t\t\t.then(() => data)\n\t\t}\n\n\t\treturn data\n\t}\n\n\tapplyMigrationsForInstance<T>(decryptedInstance: T): Promise<T> {\n\t\tconst instanceType = downcast<Entity>(decryptedInstance)._type\n\n\t\tif (isSameTypeRef(instanceType, ContactTypeRef)) {\n\t\t\tconst contact: Contact = downcast(decryptedInstance)\n\n\t\t\tif (!contact.birthdayIso && contact.oldBirthdayAggregate) {\n\t\t\t\tcontact.birthdayIso = birthdayToIsoDate(contact.oldBirthdayAggregate)\n\t\t\t\tcontact.oldBirthdayAggregate = null\n\t\t\t\tcontact.oldBirthdayDate = null\n\t\t\t\treturn this.entityClient\n\t\t\t\t\t.update(contact)\n\t\t\t\t\t.catch(ofClass(LockedError, noOp))\n\t\t\t\t\t.then(() => decryptedInstance)\n\t\t\t} else if (!contact.birthdayIso && contact.oldBirthdayDate) {\n\t\t\t\tcontact.birthdayIso = birthdayToIsoDate(oldBirthdayToBirthday(contact.oldBirthdayDate))\n\t\t\t\tcontact.oldBirthdayDate = null\n\t\t\t\treturn this.entityClient\n\t\t\t\t\t.update(contact)\n\t\t\t\t\t.catch(ofClass(LockedError, noOp))\n\t\t\t\t\t.then(() => decryptedInstance)\n\t\t\t} else if (contact.birthdayIso && (contact.oldBirthdayAggregate || contact.oldBirthdayDate)) {\n\t\t\t\tcontact.oldBirthdayAggregate = null\n\t\t\t\tcontact.oldBirthdayDate = null\n\t\t\t\treturn this.entityClient\n\t\t\t\t\t.update(contact)\n\t\t\t\t\t.catch(ofClass(LockedError, noOp))\n\t\t\t\t\t.then(() => decryptedInstance)\n\t\t\t}\n\t\t}\n\n\t\treturn Promise.resolve(decryptedInstance)\n\t}\n\n\tasync resolveSessionKeyForInstance(instance: SomeEntity): Promise<Aes128Key | null> {\n\t\tconst typeModel = await resolveTypeReference(instance._type)\n\t\treturn this.resolveSessionKey(typeModel, instance)\n\t}\n\n\t/** Helper for the rare cases when we needed it on the client side. */\n\tasync resolveSessionKeyForInstanceBinary(instance: SomeEntity): Promise<Uint8Array | null> {\n\t\tconst key = await this.resolveSessionKeyForInstance(instance)\n\t\treturn key == null ? null : bitArrayToUint8Array(key)\n\t}\n\n\t/** Resolve a session key an {@param instance} using an already known {@param ownerKey}. */\n\tresolveSessionKeyWithOwnerKey(instance: Record<string, any>, ownerKey: Aes128Key): Aes128Key {\n\t\tlet key = instance._ownerEncSessionKey\n\t\tif (typeof key === \"string\") {\n\t\t\tkey = base64ToUint8Array(instance._ownerEncSessionKey)\n\t\t}\n\n\t\treturn decryptKey(ownerKey, key)\n\t}\n\n\t/**\n\t * Returns the session key for the provided type/instance:\n\t * * null, if the instance is unencrypted\n\t * * the decrypted _ownerEncSessionKey, if it is available\n\t * * the public decrypted session key, otherwise\n\t *\n\t * @param typeModel: the type model of the instance\n\t * @param instance The unencrypted (client-side) instance or encrypted (server-side) object literal\n\t */\n\tresolveSessionKey(typeModel: TypeModel, instance: Record<string, any>): Promise<Aes128Key | null> {\n\t\treturn Promise.resolve()\n\t\t\t.then(async () => {\n\t\t\t\tif (!typeModel.encrypted) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst elementId = this.getElementIdFromInstance(instance)\n\t\t\t\tconst sessionKey = this.sessionKeyCache[elementId]\n\t\t\t\tif (sessionKey) {\n\t\t\t\t\t// Reasons for the session key cache:\n\t\t\t\t\t// 1. Optimize resolving of session key for MailBody instances. Mail and MailBody share the same session key and we just want to laod the permission once.\n\t\t\t\t\t// 2. MailDetails entities cannot resolve the session key on their own. We always need to load the mail first and then put the mail session key into the cache as they share the same session key.\n\t\t\t\t\t// 3. With simplified permission system (BucketKey as AT on Mail) File instances cannot resolve the session key on their own, all session keys\n\t\t\t\t\t// are stored in the BucketKey of the mail. We do write ownerEncSessionKeys for Files but we might need file session keys before the update owner enc session key round trip is finished.\n\t\t\t\t\t// When we have ownerEncSessionKey we can remove the key from cache, but we need to keep it for MailDetails as we don't write ownerEncSessionKey for blob entities.\n\t\t\t\t\tif (instance._ownerEncSessionKey != null) {\n\t\t\t\t\t\tdelete this.sessionKeyCache[elementId]\n\t\t\t\t\t}\n\t\t\t\t\treturn sessionKey\n\t\t\t\t} else if (instance.bucketKey) {\n\t\t\t\t\t// if we have a bucket key, then we need to cache the session keys stored in the bucket key for details, files, etc.\n\t\t\t\t\t// we need to do this BEFORE we check the owner enc session key\n\t\t\t\t\tconst bucketKey = await this.convertBucketKeyToInstanceIfNecessary(instance.bucketKey)\n\t\t\t\t\treturn this.resolveWithBucketKey(bucketKey, instance, typeModel)\n\t\t\t\t} else if (instance._ownerEncSessionKey && this.userFacade.isFullyLoggedIn() && this.userFacade.hasGroup(instance._ownerGroup)) {\n\t\t\t\t\tconst gk = this.userFacade.getGroupKey(instance._ownerGroup)\n\t\t\t\t\treturn this.resolveSessionKeyWithOwnerKey(instance, gk)\n\t\t\t\t} else if (instance.ownerEncSessionKey) {\n\t\t\t\t\t// TODO this is a service instance: Rename all ownerEncSessionKey attributes to _ownerEncSessionKey\t and add _ownerGroupId (set ownerEncSessionKey here automatically after resolving the group)\n\t\t\t\t\t// add to payment data service\n\t\t\t\t\tconst gk = this.userFacade.getGroupKey(this.userFacade.getGroupId(GroupType.Mail))\n\t\t\t\t\treturn this.resolveSessionKeyWithOwnerKey(instance, gk)\n\t\t\t\t} else {\n\t\t\t\t\t// See PermissionType jsdoc for more info on permissions\n\t\t\t\t\tconst permissions = await this.entityClient.loadAll(PermissionTypeRef, instance._permissions)\n\t\t\t\t\treturn this.trySymmetricPermission(permissions) ?? (await this.resolveWithPublicOrExternalPermission(permissions, instance, typeModel))\n\t\t\t\t}\n\t\t\t})\n\t\t\t.then((sessionKey) => {\n\t\t\t\t// store the mail session key for the mail body because it is the same\n\t\t\t\tif (sessionKey && isSameTypeRefByAttr(MailTypeRef, typeModel.app, typeModel.name)) {\n\t\t\t\t\tif (this.isTuple(instance.mailDetails)) {\n\t\t\t\t\t\tthis.setSessionKeyCacheWithTuple(instance.mailDetails, sessionKey)\n\t\t\t\t\t} else if (this.isTuple(instance.mailDetailsDraft)) {\n\t\t\t\t\t\tthis.setSessionKeyCacheWithTuple(instance.mailDetailsDraft, sessionKey)\n\t\t\t\t\t} else if (instance.body) {\n\t\t\t\t\t\tthis.setSessionKeyCacheWithElementId(instance.body, sessionKey)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn sessionKey\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(CryptoError, (e) => {\n\t\t\t\t\tconsole.log(\"failed to resolve session key\", e)\n\t\t\t\t\tthrow new SessionKeyNotFoundError(\"Crypto error while resolving session key for instance \" + instance._id)\n\t\t\t\t}),\n\t\t\t)\n\t}\n\n\t/**\n\t * In case the given bucketKey is a literal the literal will be converted to an instance and return. In case the BucketKey is already an instance the instance is returned.\n\t * @param bucketKeyInstanceOrLiteral The bucket key as literal or instance\n\t */\n\tasync convertBucketKeyToInstanceIfNecessary(bucketKeyInstanceOrLiteral: Record<string, any>): Promise<BucketKey> {\n\t\tif (!this.isLiteralInstance(bucketKeyInstanceOrLiteral)) {\n\t\t\t// bucket key was already decoded from base 64\n\t\t\treturn bucketKeyInstanceOrLiteral as BucketKey\n\t\t} else {\n\t\t\t// decryptAndMapToInstance is misleading here, but we want to map the BucketKey aggregate and its session key from a literal to an instance\n\t\t\t// to have the encrypted keys in binary format and not a base 64. There is actually no decryption ongoing, just mapToInstance.\n\t\t\tconst bucketKeyTypeModel = await resolveTypeReference(BucketKeyTypeRef)\n\t\t\treturn (await this.instanceMapper.decryptAndMapToInstance(bucketKeyTypeModel, bucketKeyInstanceOrLiteral, null)) as BucketKey\n\t\t}\n\t}\n\n\tprivate isLiteralInstance(elementOrLiteral: Record<string, any>): boolean {\n\t\treturn typeof elementOrLiteral._type === \"undefined\"\n\t}\n\n\tprivate isTuple(element: unknown): element is IdTuple {\n\t\treturn element != null && Array.isArray(element)\n\t}\n\n\tprivate setSessionKeyCacheWithElementId(elementId: string, sessionKey: Aes128Key) {\n\t\tthis.sessionKeyCache[elementId] = sessionKey\n\t}\n\n\tprivate setSessionKeyCacheWithTuple(idTuple: IdTuple, sessionKey: Aes128Key) {\n\t\treturn this.setSessionKeyCacheWithElementId(elementIdPart(idTuple), sessionKey)\n\t}\n\n\tprivate trySymmetricPermission(listPermissions: Permission[]) {\n\t\tconst symmetricPermission: Permission | null =\n\t\t\tlistPermissions.find(\n\t\t\t\t(p) =>\n\t\t\t\t\t(p.type === PermissionType.Public_Symmetric || p.type === PermissionType.Symmetric) &&\n\t\t\t\t\tp._ownerGroup &&\n\t\t\t\t\tthis.userFacade.hasGroup(p._ownerGroup),\n\t\t\t) ?? null\n\n\t\tif (symmetricPermission) {\n\t\t\tconst gk = this.userFacade.getGroupKey(assertNotNull(symmetricPermission._ownerGroup))\n\t\t\treturn decryptKey(gk, assertNotNull(symmetricPermission._ownerEncSessionKey))\n\t\t}\n\t}\n\n\tprivate async resolveWithBucketKey(bucketKey: BucketKey, instance: Record<string, any>, typeModel: TypeModel): Promise<Aes128Key> {\n\t\tconst instanceElementId = this.getElementIdFromInstance(instance)\n\n\t\tlet decBucketKey: Aes128Key\n\t\tif (bucketKey.keyGroup && bucketKey.pubEncBucketKey) {\n\t\t\t// bucket key is encrypted with public key for internal recipient\n\t\t\tdecBucketKey = await this.decryptBucketKeyWithKeyPairOfGroup(bucketKey.keyGroup, bucketKey.pubEncBucketKey)\n\t\t} else if (bucketKey.groupEncBucketKey) {\n\t\t\t// secure external recipient\n\t\t\tlet keyGroup\n\t\t\tif (bucketKey.keyGroup) {\n\t\t\t\t// legacy code path for old external clients that used to encrypt bucket keys with user group keys.\n\t\t\t\t// should be dropped once all old external mailboxes are cleared\n\t\t\t\tkeyGroup = bucketKey.keyGroup\n\t\t\t} else {\n\t\t\t\t// by default, we try to decrypt the bucket key with the ownerGroupKey\n\t\t\t\tkeyGroup = neverNull(instance._ownerGroup)\n\t\t\t}\n\t\t\tdecBucketKey = decryptKey(this.userFacade.getGroupKey(keyGroup), bucketKey.groupEncBucketKey)\n\t\t} else {\n\t\t\tthrow new SessionKeyNotFoundError(`encrypted bucket key not set on instance ${typeModel.name}`)\n\t\t}\n\t\tconst { resolvedSessionKeyForInstance, instanceSessionKeys } = this.collectAllInstanceSessionKeys(bucketKey, decBucketKey, instanceElementId, instance)\n\t\tthis.ownerEncSessionKeysUpdateQueue.updateInstanceSessionKeys(instanceSessionKeys)\n\n\t\tif (resolvedSessionKeyForInstance) {\n\t\t\treturn resolvedSessionKeyForInstance\n\t\t} else {\n\t\t\tthrow new SessionKeyNotFoundError(\"no session key for instance \" + instance._id)\n\t\t}\n\t}\n\n\t/**\n\t * Resolves the session key for the provided instance and collects all other instances'\n\t * session keys in order to update them.\n\t */\n\tprivate collectAllInstanceSessionKeys(\n\t\tbucketKey: BucketKey,\n\t\tdecBucketKey: number[],\n\t\tinstanceElementId: string,\n\t\tinstance: Record<string, any>,\n\t): { resolvedSessionKeyForInstance: Aes128Key | undefined; instanceSessionKeys: InstanceSessionKey[] } {\n\t\tlet resolvedSessionKeyForInstance: Aes128Key | undefined = undefined\n\t\tconst instanceSessionKeys = bucketKey.bucketEncSessionKeys.map((instanceSessionKey) => {\n\t\t\tconst decryptedSessionKey = decryptKey(decBucketKey, instanceSessionKey.symEncSessionKey)\n\t\t\tif (instanceElementId == instanceSessionKey.instanceId) {\n\t\t\t\tresolvedSessionKeyForInstance = decryptedSessionKey\n\t\t\t} else {\n\t\t\t\t// only but session keys to the cache that are referencing another instance (e.g. File)\n\t\t\t\tthis.setSessionKeyCacheWithElementId(instanceSessionKey.instanceId, decryptedSessionKey)\n\t\t\t}\n\t\t\tconst ownerEncSessionKey = encryptKey(this.userFacade.getGroupKey(instance._ownerGroup), decryptedSessionKey)\n\t\t\tconst instanceSessionKeyWithOwnerEncSessionKey = createInstanceSessionKey(instanceSessionKey)\n\t\t\tinstanceSessionKeyWithOwnerEncSessionKey.symEncSessionKey = ownerEncSessionKey\n\t\t\treturn instanceSessionKeyWithOwnerEncSessionKey\n\t\t})\n\t\treturn { resolvedSessionKeyForInstance, instanceSessionKeys }\n\t}\n\n\tprivate async resolveWithPublicOrExternalPermission(\n\t\tlistPermissions: Permission[],\n\t\tinstance: Record<string, any>,\n\t\ttypeModel: TypeModel,\n\t): Promise<Aes128Key> {\n\t\tconst pubOrExtPermission = listPermissions.find((p) => p.type === PermissionType.Public || p.type === PermissionType.External) ?? null\n\n\t\tif (pubOrExtPermission == null) {\n\t\t\tconst typeName = `${typeModel.app}/${typeModel.name}`\n\t\t\tthrow new SessionKeyNotFoundError(\n\t\t\t\t`could not find permission for instance of type ${typeName} with id ${this.getElementIdFromInstance(instance)}: ${JSON.stringify(instance)}`,\n\t\t\t)\n\t\t}\n\n\t\tconst bucketPermissions = await this.entityClient.loadAll(BucketPermissionTypeRef, assertNotNull(pubOrExtPermission.bucket).bucketPermissions)\n\t\tconst bucketPermission = bucketPermissions.find(\n\t\t\t(bp) => (bp.type === BucketPermissionType.Public || bp.type === BucketPermissionType.External) && pubOrExtPermission._ownerGroup === bp._ownerGroup,\n\t\t)\n\n\t\t// find the bucket permission with the same group as the permission and public type\n\t\tif (bucketPermission == null) {\n\t\t\tthrow new SessionKeyNotFoundError(\"no corresponding bucket permission found\")\n\t\t}\n\n\t\tif (bucketPermission.type === BucketPermissionType.External) {\n\t\t\treturn this.decryptWithExternalBucket(bucketPermission, pubOrExtPermission, instance)\n\t\t} else {\n\t\t\treturn await this.decryptWithPublicBucket(bucketPermission, instance, pubOrExtPermission, typeModel)\n\t\t}\n\t}\n\n\tprivate decryptWithExternalBucket(bucketPermission: BucketPermission, pubOrExtPermission: Permission, instance: Record<string, any>) {\n\t\tlet bucketKey\n\n\t\tif (bucketPermission.ownerEncBucketKey != null) {\n\t\t\tbucketKey = decryptKey(this.userFacade.getGroupKey(neverNull(bucketPermission._ownerGroup)), bucketPermission.ownerEncBucketKey)\n\t\t} else if (bucketPermission.symEncBucketKey) {\n\t\t\tbucketKey = decryptKey(this.userFacade.getUserGroupKey(), bucketPermission.symEncBucketKey)\n\t\t} else {\n\t\t\tthrow new SessionKeyNotFoundError(\n\t\t\t\t`BucketEncSessionKey is not defined for Permission ${pubOrExtPermission._id.toString()} (Instance: ${JSON.stringify(instance)})`,\n\t\t\t)\n\t\t}\n\n\t\treturn decryptKey(bucketKey, neverNull(pubOrExtPermission.bucketEncSessionKey))\n\t}\n\n\tprivate async decryptBucketKeyWithKeyPairOfGroup(keyPairGroupId: Id, pubEncBucketKey: Uint8Array): Promise<Aes128Key> {\n\t\tconst group = await this.entityClient.load(GroupTypeRef, keyPairGroupId)\n\t\tconst keypair = group.keys[0]\n\t\ttry {\n\t\t\tconst privKey = decryptRsaKey(this.userFacade.getGroupKey(group._id), keypair.symEncPrivKey)\n\t\t\tconst decryptedBytes = await this.rsa.decrypt(privKey, pubEncBucketKey)\n\t\t\treturn uint8ArrayToBitArray(decryptedBytes)\n\t\t} catch (e) {\n\t\t\tconsole.log(\"failed to decrypt rsa key for group with id \" + group._id)\n\t\t\tthrow e\n\t\t}\n\t}\n\n\tprivate async decryptWithPublicBucket(\n\t\tbucketPermission: BucketPermission,\n\t\tinstance: Record<string, any>,\n\t\tpubOrExtPermission: Permission,\n\t\ttypeModel: TypeModel,\n\t): Promise<Aes128Key> {\n\t\tconst pubEncBucketKey = bucketPermission.pubEncBucketKey\n\t\tif (pubEncBucketKey == null) {\n\t\t\tthrow new SessionKeyNotFoundError(\n\t\t\t\t`PubEncBucketKey is not defined for BucketPermission ${bucketPermission._id.toString()} (Instance: ${JSON.stringify(instance)})`,\n\t\t\t)\n\t\t}\n\t\tconst bucketEncSessionKey = pubOrExtPermission.bucketEncSessionKey\n\t\tif (bucketEncSessionKey == null) {\n\t\t\tthrow new SessionKeyNotFoundError(\n\t\t\t\t`BucketEncSessionKey is not defined for Permission ${pubOrExtPermission._id.toString()} (Instance: ${JSON.stringify(instance)})`,\n\t\t\t)\n\t\t}\n\n\t\tconst bucketKey = await this.decryptBucketKeyWithKeyPairOfGroup(bucketPermission.group, pubEncBucketKey)\n\n\t\tconst sk = decryptKey(bucketKey, bucketEncSessionKey)\n\n\t\tif (bucketPermission._ownerGroup) {\n\t\t\t// is not defined for some old AccountingInfos\n\t\t\tlet bucketPermissionOwnerGroupKey = this.userFacade.getGroupKey(neverNull(bucketPermission._ownerGroup))\n\t\t\tlet bucketPermissionGroupKey = this.userFacade.getGroupKey(bucketPermission.group)\n\t\t\tawait this.updateWithSymPermissionKey(\n\t\t\t\ttypeModel,\n\t\t\t\tinstance,\n\t\t\t\tpubOrExtPermission,\n\t\t\t\tbucketPermission,\n\t\t\t\tbucketPermissionOwnerGroupKey,\n\t\t\t\tbucketPermissionGroupKey,\n\t\t\t\tsk,\n\t\t\t).catch(\n\t\t\t\tofClass(NotFoundError, () => {\n\t\t\t\t\tconsole.log(\"w> could not find instance to update permission\")\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t\treturn sk\n\t}\n\n\t/**\n\t * Returns the session key for the provided service response:\n\t * * null, if the instance is unencrypted\n\t * * the decrypted _ownerPublicEncSessionKey, if it is available\n\t * @param typeModel\n\t * @param instance The unencrypted (client-side) or encrypted (server-side) instance\n\t *\n\t */\n\tresolveServiceSessionKey(typeModel: TypeModel, instance: Record<string, any>): Promise<Aes128Key | null> {\n\t\tif (instance._ownerPublicEncSessionKey) {\n\t\t\treturn this.entityClient.load(GroupTypeRef, instance._ownerGroup).then((group) => {\n\t\t\t\tlet keypair = group.keys[0]\n\t\t\t\tlet gk = this.userFacade.getGroupKey(instance._ownerGroup)\n\t\t\t\tlet privKey\n\n\t\t\t\ttry {\n\t\t\t\t\tprivKey = decryptRsaKey(gk, keypair.symEncPrivKey)\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.log(\"failed to decrypt rsa key for group with id \" + group._id)\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\n\t\t\t\treturn this.rsa\n\t\t\t\t\t.decrypt(privKey, base64ToUint8Array(instance._ownerPublicEncSessionKey))\n\t\t\t\t\t.then((decryptedBytes) => uint8ArrayToBitArray(decryptedBytes))\n\t\t\t})\n\t\t}\n\n\t\treturn Promise.resolve(null)\n\t}\n\n\t/**\n\t * Creates a new _ownerEncSessionKey and assigns it to the provided entity\n\t * the entity must already have an _ownerGroup\n\t * @returns the generated key\n\t */\n\tsetNewOwnerEncSessionKey(model: TypeModel, entity: Record<string, any>, keyToEncryptSessionKey?: Aes128Key): Aes128Key | null {\n\t\tif (!entity._ownerGroup) {\n\t\t\tthrow new Error(`no owner group set  ${JSON.stringify(entity)}`)\n\t\t}\n\n\t\tif (model.encrypted) {\n\t\t\tif (entity._ownerEncSessionKey) {\n\t\t\t\tthrow new Error(`ownerEncSessionKey already set ${JSON.stringify(entity)}`)\n\t\t\t}\n\n\t\t\tconst sessionKey = aes128RandomKey()\n\t\t\tconst effectiveKeyToEncryptSessionKey = keyToEncryptSessionKey ?? this.userFacade.getGroupKey(entity._ownerGroup)\n\t\t\tentity._ownerEncSessionKey = encryptKey(effectiveKeyToEncryptSessionKey, sessionKey)\n\t\t\treturn sessionKey\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tencryptBucketKeyForInternalRecipient(\n\t\tbucketKey: Aes128Key,\n\t\trecipientMailAddress: string,\n\t\tnotFoundRecipients: Array<string>,\n\t): Promise<InternalRecipientKeyData | void> {\n\t\tlet keyData = createPublicKeyData()\n\t\tkeyData.mailAddress = recipientMailAddress\n\t\treturn this.serviceExecutor\n\t\t\t.get(PublicKeyService, keyData)\n\t\t\t.then((publicKeyData) => {\n\t\t\t\tlet publicKey = hexToPublicKey(uint8ArrayToHex(publicKeyData.pubKey))\n\t\t\t\tlet uint8ArrayBucketKey = bitArrayToUint8Array(bucketKey)\n\n\t\t\t\tif (notFoundRecipients.length === 0) {\n\t\t\t\t\treturn this.rsa.encrypt(publicKey, uint8ArrayBucketKey).then((encrypted) => {\n\t\t\t\t\t\tlet data = createInternalRecipientKeyData()\n\t\t\t\t\t\tdata.mailAddress = recipientMailAddress\n\t\t\t\t\t\tdata.pubEncBucketKey = encrypted\n\t\t\t\t\t\tdata.pubKeyVersion = publicKeyData.pubKeyVersion\n\t\t\t\t\t\treturn data\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(NotFoundError, (e) => {\n\t\t\t\t\tnotFoundRecipients.push(recipientMailAddress)\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(TooManyRequestsError, (e) => {\n\t\t\t\t\tthrow new RecipientNotResolvedError(\"\")\n\t\t\t\t}),\n\t\t\t)\n\t}\n\n\t/**\n\t * Updates the given public permission with the given symmetric key for faster access if the client is the leader and otherwise does nothing.\n\t * @param typeModel: the type model of the instance\n\t * @param instance The unencrypted (client-side) or encrypted (server-side) instance\n\t * @param permission The permission.\n\t * @param bucketPermission The bucket permission.\n\t * @param permissionOwnerGroupKey The symmetric group key for the owner group on the permission.\n\t * @param permissionGroupKey The symmetric group key of the group in the permission.\n\t * @param sessionKey The symmetric session key.\n\t */\n\tprivate updateWithSymPermissionKey(\n\t\ttypeModel: TypeModel,\n\t\tinstance: Record<string, any>,\n\t\tpermission: Permission,\n\t\tbucketPermission: BucketPermission,\n\t\tpermissionOwnerGroupKey: Aes128Key,\n\t\tpermissionGroupKey: Aes128Key,\n\t\tsessionKey: Aes128Key,\n\t): Promise<void> {\n\t\tif (!this.isLiteralInstance(instance) || !this.userFacade.isLeader()) {\n\t\t\t// do not update the session key in case of an unencrypted (client-side) instance\n\t\t\t// or in case we are not the leader client\n\t\t\treturn Promise.resolve()\n\t\t}\n\n\t\tif (!instance._ownerEncSessionKey && permission._ownerGroup === instance._ownerGroup) {\n\t\t\treturn this.updateOwnerEncSessionKey(typeModel, instance, permissionOwnerGroupKey, sessionKey)\n\t\t} else {\n\t\t\t// instances shared via permissions (e.g. body)\n\t\t\tlet updateService = createUpdatePermissionKeyData()\n\t\t\tupdateService.permission = permission._id\n\t\t\tupdateService.bucketPermission = bucketPermission._id\n\t\t\tupdateService.ownerEncSessionKey = encryptKey(permissionOwnerGroupKey, sessionKey)\n\t\t\tupdateService.symEncSessionKey = encryptKey(permissionGroupKey, sessionKey) // legacy can be removed\n\n\t\t\treturn this.serviceExecutor.post(UpdatePermissionKeyService, updateService).then(noOp)\n\t\t}\n\t}\n\n\tprivate updateOwnerEncSessionKey(typeModel: TypeModel, instance: Record<string, any>, ownerGroupKey: Aes128Key, sessionKey: Aes128Key): Promise<void> {\n\t\tinstance._ownerEncSessionKey = uint8ArrayToBase64(encryptKey(ownerGroupKey, sessionKey))\n\t\t// we have to call the rest client directly because instance is still the encrypted server-side version\n\t\tconst path = typeRefToPath(new TypeRef(typeModel.app, typeModel.name)) + \"/\" + (instance._id instanceof Array ? instance._id.join(\"/\") : instance._id)\n\t\tconst headers = this.userFacade.createAuthHeaders()\n\t\theaders.v = typeModel.version\n\t\treturn this.restClient\n\t\t\t.request(path, HttpMethod.PUT, {\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(instance),\n\t\t\t\tqueryParams: { updateOwnerEncSessionKey: \"true\" },\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(PayloadTooLargeError, (e) => {\n\t\t\t\t\tconsole.log(\"Could not update owner enc session key - PayloadTooLargeError\", e)\n\t\t\t\t}),\n\t\t\t)\n\t}\n\n\t/**\n\t * Only visible for testing.\n\t */\n\tgetSessionKeyCache(): Record<string, Aes128Key> {\n\t\treturn this.sessionKeyCache\n\t}\n\n\tprivate getElementIdFromInstance(instance: Record<string, any>): Id {\n\t\tif (typeof instance._id === \"string\") {\n\t\t\treturn instance._id\n\t\t} else {\n\t\t\tconst idTuple = instance._id as IdTuple\n\t\t\treturn elementIdPart(idTuple)\n\t\t}\n\t}\n}\n\nif (!(\"toJSON\" in Error.prototype)) {\n\tObject.defineProperty(Error.prototype as any, \"toJSON\", {\n\t\tvalue: function () {\n\t\t\tconst alt: Record<string, any> = {}\n\t\t\tfor (let key of Object.getOwnPropertyNames(this)) {\n\t\t\t\talt[key] = this[key]\n\t\t\t}\n\t\t\treturn alt\n\t\t},\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n}\n","import type { Base64Url, DeferredObject, Hex } from \"@tutao/tutanota-utils\"\nimport {\n\tarrayEquals,\n\tassertNotNull,\n\tBase64,\n\tbase64ToBase64Ext,\n\tbase64ToBase64Url,\n\tbase64ToUint8Array,\n\tbase64UrlToBase64,\n\tdefer,\n\thexToUint8Array,\n\tneverNull,\n\tofClass,\n\tuint8ArrayToBase64,\n\tutf8Uint8ArrayToString,\n} from \"@tutao/tutanota-utils\"\nimport {\n\tAutoLoginService,\n\tChangePasswordService,\n\tCustomerService,\n\tResetFactorsService,\n\tSaltService,\n\tSecondFactorAuthService,\n\tSessionService,\n\tTakeOverDeletedAddressService,\n} from \"../../entities/sys/Services\"\nimport { AccountType, CloseEventBusOption } from \"../../common/TutanotaConstants\"\nimport { CryptoError } from \"../../common/error/CryptoError\"\nimport type { GroupInfo, SaltReturn, SecondFactorAuthData, User } from \"../../entities/sys/TypeRefs.js\"\nimport {\n\tChallenge,\n\tcreateAutoLoginDataGet,\n\tcreateChangePasswordData,\n\tcreateCreateSessionData,\n\tcreateDeleteCustomerData,\n\tcreateResetFactorsDeleteData,\n\tcreateSaltData,\n\tcreateSecondFactorAuthDeleteData,\n\tcreateSecondFactorAuthGetData,\n\tCreateSessionReturn,\n\tcreateTakeOverDeletedAddressData,\n\tGroupInfoTypeRef,\n\tRecoverCodeTypeRef,\n\tSessionTypeRef,\n\tUserTypeRef,\n} from \"../../entities/sys/TypeRefs.js\"\nimport { TutanotaPropertiesTypeRef } from \"../../entities/tutanota/TypeRefs.js\"\nimport { HttpMethod, MediaType, resolveTypeReference } from \"../../common/EntityFunctions\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport { ConnectMode, EventBusClient } from \"../EventBusClient\"\nimport { EntityRestClient, typeRefToPath } from \"../rest/EntityRestClient\"\nimport { AccessExpiredError, ConnectionError, NotAuthenticatedError, NotFoundError, SessionExpiredError } from \"../../common/error/RestError\"\nimport type { WorkerImpl } from \"../WorkerImpl\"\nimport { CancelledError } from \"../../common/error/CancelledError\"\nimport { RestClient } from \"../rest/RestClient\"\nimport { EntityClient } from \"../../common/EntityClient\"\nimport { GENERATED_ID_BYTES_LENGTH, isSameId } from \"../../common/utils/EntityUtils\"\nimport type { Credentials } from \"../../../misc/credentials/Credentials\"\nimport {\n\taes128Decrypt,\n\taes128RandomKey,\n\taes256DecryptKey,\n\tbase64ToKey,\n\tcreateAuthVerifier,\n\tcreateAuthVerifierAsBase64Url,\n\tencryptKey,\n\tgenerateKeyFromPassphrase,\n\tgenerateRandomSalt,\n\tKeyLength,\n\tkeyToUint8Array,\n\trandom,\n\tsha256Hash,\n\tTotpSecret,\n\tTotpVerifier,\n\tuint8ArrayToBitArray,\n\tuint8ArrayToKey,\n} from \"@tutao/tutanota-crypto\"\nimport { CryptoFacade, encryptString } from \"../crypto/CryptoFacade\"\nimport { InstanceMapper } from \"../crypto/InstanceMapper\"\nimport { Aes128Key } from \"@tutao/tutanota-crypto/dist/encryption/Aes\"\nimport { IServiceExecutor } from \"../../common/ServiceRequest\"\nimport { SessionType } from \"../../common/SessionType\"\nimport { CacheStorageLateInitializer } from \"../rest/CacheStorageProxy\"\nimport { AuthDataProvider, UserFacade } from \"./UserFacade\"\nimport { LoginFailReason } from \"../../main/PageContextLoginListener.js\"\nimport { LoginIncompleteError } from \"../../common/error/LoginIncompleteError.js\"\nimport { EntropyFacade } from \"./EntropyFacade.js\"\nimport { BlobAccessTokenFacade } from \"./BlobAccessTokenFacade.js\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError.js\"\nimport { DatabaseKeyFactory } from \"../../../misc/credentials/DatabaseKeyFactory.js\"\n\nassertWorkerOrNode()\n\nexport type NewSessionData = {\n\tuser: User\n\tuserGroupInfo: GroupInfo\n\tsessionId: IdTuple\n\tcredentials: Credentials\n\tdatabaseKey: Uint8Array | null\n}\n\nexport type CacheInfo = {\n\tisPersistent: boolean\n\tisNewOfflineDb: boolean\n}\n\ninterface ResumeSessionResultData {\n\tuser: User\n\tuserGroupInfo: GroupInfo\n\tsessionId: IdTuple\n}\n\nexport const enum ResumeSessionErrorReason {\n\tOfflineNotAvailableForFree,\n}\n\nexport type InitCacheOptions = {\n\tuserId: Id\n\tdatabaseKey: Uint8Array | null\n\ttimeRangeDays: number | null\n\tforceNewDatabase: boolean\n}\n\ntype ResumeSessionSuccess = { type: \"success\"; data: ResumeSessionResultData }\ntype ResumeSessionFailure = { type: \"error\"; reason: ResumeSessionErrorReason }\ntype ResumeSessionResult = ResumeSessionSuccess | ResumeSessionFailure\n\ntype AsyncLoginState = { state: \"idle\" } | { state: \"running\" } | { state: \"failed\"; credentials: Credentials; cacheInfo: CacheInfo }\n\nexport interface LoginListener {\n\t/**\n\t * Partial login reached: cached entities and user are available.\n\t */\n\tonPartialLoginSuccess(): Promise<void>\n\n\t/**\n\t * Full login reached: any network requests can be made\n\t */\n\tonFullLoginSuccess(sessionType: SessionType, cacheInfo: CacheInfo): Promise<void>\n\n\t/**\n\t * call when the login fails for invalid session or other reasons\n\t */\n\tonLoginFailure(reason: LoginFailReason): Promise<void>\n\n\t/**\n\t * Shows a dialog with possibility to use second factor and with a message that the login can be approved from another client.\n\t */\n\tonSecondFactorChallenge(sessionId: IdTuple, challenges: ReadonlyArray<Challenge>, mailAddress: string | null): Promise<void>\n}\n\nexport class LoginFacade {\n\tprivate eventBusClient!: EventBusClient\n\t/**\n\t * Used for cancelling second factor and to not mix different attempts\n\t */\n\tprivate loginRequestSessionId: IdTuple | null = null\n\n\t/**\n\t * Used for cancelling second factor immediately\n\t */\n\tprivate loggingInPromiseWrapper: DeferredObject<void> | null = null\n\n\t/** On platforms with offline cache we do the actual login asynchronously and we can retry it. This is the state of such async login. */\n\tasyncLoginState: AsyncLoginState = { state: \"idle\" }\n\n\tconstructor(\n\t\treadonly worker: WorkerImpl,\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly loginListener: LoginListener,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t\t/**\n\t\t *  Only needed so that we can initialize the offline storage after login.\n\t\t *  This is necessary because we don't know if we'll be persistent or not until the user tries to login\n\t\t *  Once the credentials handling has been changed to *always* save in desktop, then this should become obsolete\n\t\t */\n\t\tprivate readonly cacheInitializer: CacheStorageLateInitializer,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly blobAccessTokenFacade: BlobAccessTokenFacade,\n\t\tprivate readonly entropyFacade: EntropyFacade,\n\t\tprivate readonly databaseKeyFactory: DatabaseKeyFactory,\n\t) {}\n\n\tinit(eventBusClient: EventBusClient) {\n\t\tthis.eventBusClient = eventBusClient\n\t}\n\n\tasync resetSession(): Promise<void> {\n\t\tthis.eventBusClient.close(CloseEventBusOption.Terminate)\n\t\tawait this.deInitCache()\n\t\tthis.userFacade.reset()\n\t}\n\n\t/**\n\t * Create session and log in. Changes internal state to refer to the logged in user.\n\t */\n\tasync createSession(\n\t\tmailAddress: string,\n\t\tpassphrase: string,\n\t\tclientIdentifier: string,\n\t\tsessionType: SessionType,\n\t\tdatabaseKey: Uint8Array | null,\n\t): Promise<NewSessionData> {\n\t\tif (this.userFacade.isPartiallyLoggedIn()) {\n\t\t\t// do not reset here because the event bus client needs to be kept if the same user is logged in as before\n\t\t\tconsole.log(\"session already exists, reuse data\")\n\t\t\t// check if it is the same user in _initSession()\n\t\t}\n\n\t\tconst userPassphraseKey = await this.loadUserPassphraseKey(mailAddress, passphrase)\n\t\t// the verifier is always sent as url parameter, so it must be url encoded\n\t\tconst authVerifier = createAuthVerifierAsBase64Url(userPassphraseKey)\n\t\tconst createSessionData = createCreateSessionData({\n\t\t\tmailAddress: mailAddress.toLowerCase().trim(),\n\t\t\tclientIdentifier,\n\t\t\tauthVerifier,\n\t\t})\n\n\t\tlet accessKey: Aes128Key | null = null\n\n\t\tif (sessionType === SessionType.Persistent) {\n\t\t\taccessKey = aes128RandomKey()\n\t\t\tcreateSessionData.accessKey = keyToUint8Array(accessKey)\n\t\t}\n\t\tconst createSessionReturn = await this.serviceExecutor.post(SessionService, createSessionData)\n\t\tconst sessionData = await this.waitUntilSecondFactorApprovedOrCancelled(createSessionReturn, mailAddress)\n\n\t\tconst forceNewDatabase = sessionType === SessionType.Persistent && databaseKey == null\n\t\tif (forceNewDatabase) {\n\t\t\tconsole.log(\"generating new database key for persistent session\")\n\t\t\tdatabaseKey = await this.databaseKeyFactory.generateKey()\n\t\t}\n\n\t\tconst cacheInfo = await this.initCache({\n\t\t\tuserId: sessionData.userId,\n\t\t\tdatabaseKey,\n\t\t\ttimeRangeDays: null,\n\t\t\tforceNewDatabase,\n\t\t})\n\t\tconst { user, userGroupInfo, accessToken } = await this.initSession(\n\t\t\tsessionData.userId,\n\t\t\tsessionData.accessToken,\n\t\t\tuserPassphraseKey,\n\t\t\tsessionType,\n\t\t\tcacheInfo,\n\t\t)\n\n\t\treturn {\n\t\t\tuser,\n\t\t\tuserGroupInfo,\n\t\t\tsessionId: sessionData.sessionId,\n\t\t\tcredentials: {\n\t\t\t\tlogin: mailAddress,\n\t\t\t\taccessToken,\n\t\t\t\tencryptedPassword: sessionType === SessionType.Persistent ? uint8ArrayToBase64(encryptString(neverNull(accessKey), passphrase)) : null,\n\t\t\t\tuserId: sessionData.userId,\n\t\t\t\ttype: \"internal\",\n\t\t\t},\n\t\t\t// we always try to make a persistent cache with a key for persistent session, but this\n\t\t\t// falls back to ephemeral cache in browsers. no point storing the key then.\n\t\t\tdatabaseKey: cacheInfo.isPersistent ? databaseKey : null,\n\t\t}\n\t}\n\n\t/**\n\t * If the second factor login has been cancelled a CancelledError is thrown.\n\t */\n\tprivate waitUntilSecondFactorApprovedOrCancelled(\n\t\tcreateSessionReturn: CreateSessionReturn,\n\t\tmailAddress: string | null,\n\t): Promise<{\n\t\tsessionId: IdTuple\n\t\tuserId: Id\n\t\taccessToken: Base64Url\n\t}> {\n\t\tlet p = Promise.resolve()\n\t\tlet sessionId = [this.getSessionListId(createSessionReturn.accessToken), this.getSessionElementId(createSessionReturn.accessToken)] as const\n\t\tthis.loginRequestSessionId = sessionId\n\n\t\tif (createSessionReturn.challenges.length > 0) {\n\t\t\t// Show a message to the user and give them a chance to complete the challenges.\n\t\t\tthis.loginListener.onSecondFactorChallenge(sessionId, createSessionReturn.challenges, mailAddress)\n\n\t\t\tp = this.waitUntilSecondFactorApproved(createSessionReturn.accessToken, sessionId, 0)\n\t\t}\n\n\t\tthis.loggingInPromiseWrapper = defer()\n\t\t// Wait for either login or cancel\n\t\treturn Promise.race([this.loggingInPromiseWrapper.promise, p]).then(() => ({\n\t\t\tsessionId,\n\t\t\taccessToken: createSessionReturn.accessToken,\n\t\t\tuserId: createSessionReturn.user,\n\t\t}))\n\t}\n\n\tprivate async waitUntilSecondFactorApproved(accessToken: Base64Url, sessionId: IdTuple, retryOnNetworkError: number): Promise<void> {\n\t\tlet secondFactorAuthGetData = createSecondFactorAuthGetData()\n\t\tsecondFactorAuthGetData.accessToken = accessToken\n\t\ttry {\n\t\t\tconst secondFactorAuthGetReturn = await this.serviceExecutor.get(SecondFactorAuthService, secondFactorAuthGetData)\n\t\t\tif (!this.loginRequestSessionId || !isSameId(this.loginRequestSessionId, sessionId)) {\n\t\t\t\tthrow new CancelledError(\"login cancelled\")\n\t\t\t}\n\n\t\t\tif (secondFactorAuthGetReturn.secondFactorPending) {\n\t\t\t\treturn this.waitUntilSecondFactorApproved(accessToken, sessionId, 0)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (e instanceof ConnectionError && retryOnNetworkError < 10) {\n\t\t\t\t// connection error can occur on ios when switching between apps, just retry in this case.\n\t\t\t\treturn this.waitUntilSecondFactorApproved(accessToken, sessionId, retryOnNetworkError + 1)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n\n\t/**\n\t * Create external (temporary mailbox for password-protected emails) session and log in.\n\t * Changes internal state to refer to the logged in user.\n\t */\n\tasync createExternalSession(\n\t\tuserId: Id,\n\t\tpassphrase: string,\n\t\tsalt: Uint8Array,\n\t\tclientIdentifier: string,\n\t\tpersistentSession: boolean,\n\t): Promise<NewSessionData> {\n\t\tif (this.userFacade.isPartiallyLoggedIn()) {\n\t\t\tthrow new Error(\"user already logged in\")\n\t\t}\n\n\t\tconsole.log(\"login external worker\")\n\t\tlet userPassphraseKey = generateKeyFromPassphrase(passphrase, salt, KeyLength.b128)\n\t\t// the verifier is always sent as url parameter, so it must be url encoded\n\t\tlet authVerifier = createAuthVerifierAsBase64Url(userPassphraseKey)\n\t\tlet authToken = base64ToBase64Url(uint8ArrayToBase64(sha256Hash(salt)))\n\t\tlet sessionData = createCreateSessionData()\n\t\tsessionData.user = userId\n\t\tsessionData.authToken = authToken\n\t\tsessionData.clientIdentifier = clientIdentifier\n\t\tsessionData.authVerifier = authVerifier\n\t\tlet accessKey: Aes128Key | null = null\n\n\t\tif (persistentSession) {\n\t\t\taccessKey = aes128RandomKey()\n\t\t\tsessionData.accessKey = keyToUint8Array(accessKey)\n\t\t}\n\n\t\tconst createSessionReturn = await this.serviceExecutor.post(SessionService, sessionData)\n\n\t\tlet sessionId = [this.getSessionListId(createSessionReturn.accessToken), this.getSessionElementId(createSessionReturn.accessToken)] as const\n\t\tconst cacheInfo = await this.initCache({\n\t\t\tuserId,\n\t\t\tdatabaseKey: null,\n\t\t\ttimeRangeDays: null,\n\t\t\tforceNewDatabase: true,\n\t\t})\n\t\tconst { user, userGroupInfo, accessToken } = await this.initSession(\n\t\t\tcreateSessionReturn.user,\n\t\t\tcreateSessionReturn.accessToken,\n\t\t\tuserPassphraseKey,\n\t\t\tSessionType.Login,\n\t\t\tcacheInfo,\n\t\t)\n\t\treturn {\n\t\t\tuser,\n\t\t\tuserGroupInfo,\n\t\t\tsessionId,\n\t\t\tcredentials: {\n\t\t\t\tlogin: userId,\n\t\t\t\taccessToken,\n\t\t\t\tencryptedPassword: accessKey ? uint8ArrayToBase64(encryptString(accessKey, passphrase)) : null,\n\t\t\t\tuserId,\n\t\t\t\ttype: \"external\",\n\t\t\t},\n\t\t\tdatabaseKey: null,\n\t\t}\n\t}\n\n\t/** Cancels 2FA process. */\n\tasync cancelCreateSession(sessionId: IdTuple): Promise<void> {\n\t\tif (!this.loginRequestSessionId || !isSameId(this.loginRequestSessionId, sessionId)) {\n\t\t\tthrow new Error(\"Trying to cancel session creation but the state is invalid\")\n\t\t}\n\n\t\tconst secondFactorAuthDeleteData = createSecondFactorAuthDeleteData({\n\t\t\tsession: sessionId,\n\t\t})\n\t\tawait this.serviceExecutor.delete(SecondFactorAuthService, secondFactorAuthDeleteData).catch(\n\t\t\tofClass(NotFoundError, (e) => {\n\t\t\t\t// This can happen during some odd behavior in browser where main loop would be blocked by webauthn (hello, FF) and then we would try to\n\t\t\t\t// cancel too late. No harm here anyway if the session is already gone.\n\t\t\t\tconsole.warn(\"Tried to cancel second factor but it was not there anymore\", e)\n\t\t\t}),\n\t\t)\n\t\tthis.loginRequestSessionId = null\n\t\tthis.loggingInPromiseWrapper?.reject(new CancelledError(\"login cancelled\"))\n\t}\n\n\t/** Finishes 2FA process either using second factor or approving session on another client. */\n\tasync authenticateWithSecondFactor(data: SecondFactorAuthData): Promise<void> {\n\t\tawait this.serviceExecutor.post(SecondFactorAuthService, data)\n\t}\n\n\t/**\n\t * Resumes previously created session (using persisted credentials).\n\t * @param credentials the saved credentials to use\n\t * @param externalUserSalt\n\t * @param databaseKey key to unlock the local database (if enabled)\n\t * @param timeRangeDays the user configured time range for the offline database\n\t */\n\tasync resumeSession(\n\t\tcredentials: Credentials,\n\t\texternalUserSalt: Uint8Array | null,\n\t\tdatabaseKey: Uint8Array | null,\n\t\ttimeRangeDays: number | null,\n\t): Promise<ResumeSessionResult> {\n\t\tif (this.userFacade.getUser() != null) {\n\t\t\tthrow new ProgrammingError(\n\t\t\t\t`Trying to resume the session for user ${credentials.userId} while already logged in for ${this.userFacade.getUser()?._id}`,\n\t\t\t)\n\t\t}\n\t\tif (this.asyncLoginState.state !== \"idle\") {\n\t\t\tthrow new ProgrammingError(`Trying to resume the session for user ${credentials.userId} while the asyncLoginState is ${this.asyncLoginState.state}`)\n\t\t}\n\t\tthis.userFacade.setAccessToken(credentials.accessToken)\n\t\t// important: any exit point from here on should deinit the cache if the login hasn't succeeded\n\t\tconst cacheInfo = await this.initCache({\n\t\t\tuserId: credentials.userId,\n\t\t\tdatabaseKey,\n\t\t\ttimeRangeDays,\n\t\t\tforceNewDatabase: false,\n\t\t})\n\t\tconst sessionId = this.getSessionId(credentials)\n\t\ttry {\n\t\t\t// using offline, free, have connection         -> sync login\n\t\t\t// using offline, free, no connection           -> indicate that offline login is not for free customers\n\t\t\t// using offline, premium, have connection      -> async login\n\t\t\t// using offline, premium, no connection        -> async login w/ later retry\n\t\t\t// no offline, free, have connection            -> sync login\n\t\t\t// no offline, free, no connection              -> sync login, fail with connection error\n\t\t\t// no offline, premium, have connection         -> sync login\n\t\t\t// no offline, premium, no connection           -> sync login, fail with connection error\n\n\t\t\t// If a user enables offline storage for the first time, after already having saved credentials\n\t\t\t// then upon their next login, they won't have an offline database available, meaning we have to do\n\t\t\t// synchronous login in order to load all of the necessary keys and such\n\t\t\t// the next time they login they will be able to do asynchronous login\n\t\t\tif (cacheInfo?.isPersistent && !cacheInfo.isNewOfflineDb) {\n\t\t\t\tconst user = await this.entityClient.load(UserTypeRef, credentials.userId)\n\t\t\t\tif (user.accountType !== AccountType.PREMIUM) {\n\t\t\t\t\t// if account is free do not start offline login/async login workflow.\n\t\t\t\t\t// await before return to catch errors here\n\t\t\t\t\treturn await this.finishResumeSession(credentials, externalUserSalt, cacheInfo).catch(\n\t\t\t\t\t\tofClass(ConnectionError, async () => {\n\t\t\t\t\t\t\tawait this.resetSession()\n\t\t\t\t\t\t\treturn { type: \"error\", reason: ResumeSessionErrorReason.OfflineNotAvailableForFree }\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tthis.userFacade.setUser(user)\n\t\t\t\tthis.loginListener.onPartialLoginSuccess()\n\n\t\t\t\t// Temporary workaround for the transitional period\n\t\t\t\t// Before offline login was enabled (in 3.96.4) we didn't use cache for the login process, only afterwards.\n\t\t\t\t// This could lead to a situation where we never loaded or saved user groupInfo but would try to use it now.\n\t\t\t\t// We can remove this after a few versions when the bulk of people who enabled offline will upgrade.\n\t\t\t\tlet userGroupInfo: GroupInfo\n\t\t\t\ttry {\n\t\t\t\t\tuserGroupInfo = await this.entityClient.load(GroupInfoTypeRef, user.userGroup.groupInfo)\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.log(\"Could not do start login, groupInfo is not cached, falling back to sync login\")\n\t\t\t\t\tif (e instanceof LoginIncompleteError) {\n\t\t\t\t\t\t// await before return to catch the errors here\n\t\t\t\t\t\treturn await this.finishResumeSession(credentials, externalUserSalt, cacheInfo)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// noinspection ExceptionCaughtLocallyJS: we want to make sure we go throw the same exit point\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Start full login async\n\t\t\t\tPromise.resolve().then(() => this.asyncResumeSession(credentials, cacheInfo))\n\t\t\t\tconst data = {\n\t\t\t\t\tuser,\n\t\t\t\t\tuserGroupInfo,\n\t\t\t\t\tsessionId,\n\t\t\t\t}\n\t\t\t\treturn { type: \"success\", data }\n\t\t\t} else {\n\t\t\t\t// await before return to catch errors here\n\t\t\t\treturn await this.finishResumeSession(credentials, externalUserSalt, cacheInfo)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// If we initialized the cache, but then we couldn't authenticate we should de-initialize\n\t\t\t// the cache again because we will initialize it for the next attempt.\n\t\t\t// It might be also called in initSession but the error can be thrown even before that (e.g. if the db is empty for some reason) so we reset\n\t\t\t// the session here as well, otherwise we might try to open the DB twice.\n\t\t\tawait this.resetSession()\n\t\t\tthrow e\n\t\t}\n\t}\n\n\tprivate getSessionId(credentials: Credentials): IdTuple {\n\t\treturn [this.getSessionListId(credentials.accessToken), this.getSessionElementId(credentials.accessToken)]\n\t}\n\n\tprivate async asyncResumeSession(credentials: Credentials, cacheInfo: CacheInfo): Promise<void> {\n\t\tif (this.asyncLoginState.state === \"running\") {\n\t\t\tthrow new Error(\"finishLoginResume run in parallel\")\n\t\t}\n\t\tthis.asyncLoginState = { state: \"running\" }\n\t\ttry {\n\t\t\tawait this.finishResumeSession(credentials, null, cacheInfo)\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotAuthenticatedError || e instanceof SessionExpiredError) {\n\t\t\t\t// For this type of errors we cannot use credentials anymore.\n\t\t\t\tthis.asyncLoginState = { state: \"idle\" }\n\t\t\t\tawait this.loginListener.onLoginFailure(LoginFailReason.SessionExpired)\n\t\t\t} else {\n\t\t\t\tthis.asyncLoginState = { state: \"failed\", credentials, cacheInfo }\n\t\t\t\tif (!(e instanceof ConnectionError)) await this.worker.sendError(e)\n\t\t\t\tawait this.loginListener.onLoginFailure(LoginFailReason.Error)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async finishResumeSession(credentials: Credentials, externalUserSalt: Uint8Array | null, cacheInfo: CacheInfo): Promise<ResumeSessionSuccess> {\n\t\tconst sessionId = this.getSessionId(credentials)\n\t\tconst sessionData = await this.loadSessionData(credentials.accessToken)\n\t\tconst encryptedPassword = base64ToUint8Array(assertNotNull(credentials.encryptedPassword, \"encryptedPassword was null!\"))\n\t\tconst passphrase = utf8Uint8ArrayToString(aes128Decrypt(sessionData.accessKey, encryptedPassword))\n\t\tlet userPassphraseKey: Aes128Key\n\n\t\tif (externalUserSalt) {\n\t\t\tawait this.checkOutdatedExternalSalt(credentials, sessionData, externalUserSalt)\n\t\t\tuserPassphraseKey = generateKeyFromPassphrase(passphrase, externalUserSalt, KeyLength.b128)\n\t\t} else {\n\t\t\tuserPassphraseKey = await this.loadUserPassphraseKey(credentials.login, passphrase)\n\t\t}\n\n\t\tconst { user, userGroupInfo } = await this.initSession(\n\t\t\tsessionData.userId,\n\t\t\tcredentials.accessToken,\n\t\t\tuserPassphraseKey,\n\t\t\tSessionType.Persistent,\n\t\t\tcacheInfo,\n\t\t)\n\n\t\tthis.asyncLoginState = { state: \"idle\" }\n\n\t\tconst data = {\n\t\t\tuser,\n\t\t\tuserGroupInfo,\n\t\t\tsessionId,\n\t\t}\n\n\t\treturn { type: \"success\", data }\n\t}\n\n\tprivate async initSession(\n\t\tuserId: Id,\n\t\taccessToken: Base64Url,\n\t\tuserPassphraseKey: Aes128Key,\n\t\tsessionType: SessionType,\n\t\tcacheInfo: CacheInfo,\n\t): Promise<{ user: User; accessToken: string; userGroupInfo: GroupInfo }> {\n\t\t// We might have userId already if:\n\t\t// - session has expired and a new one was created\n\t\t// - if it's a partial login\n\t\tconst userIdFromFormerLogin = this.userFacade.getUser()?._id ?? null\n\n\t\tif (userIdFromFormerLogin && userId !== userIdFromFormerLogin) {\n\t\t\tthrow new Error(\"different user is tried to login in existing other user's session\")\n\t\t}\n\n\t\tthis.userFacade.setAccessToken(accessToken)\n\n\t\ttry {\n\t\t\tconst user = await this.entityClient.load(UserTypeRef, userId)\n\t\t\tawait this.checkOutdatedPassword(user, accessToken, userPassphraseKey)\n\n\t\t\tconst wasPartiallyLoggedIn = this.userFacade.isPartiallyLoggedIn()\n\t\t\tif (!wasPartiallyLoggedIn) {\n\t\t\t\tthis.userFacade.setUser(user)\n\t\t\t\tthis.loginListener.onPartialLoginSuccess()\n\t\t\t}\n\t\t\tconst wasFullyLoggedIn = this.userFacade.isFullyLoggedIn()\n\n\t\t\tthis.userFacade.unlockUserGroupKey(userPassphraseKey)\n\t\t\tconst userGroupInfo = await this.entityClient.load(GroupInfoTypeRef, user.userGroup.groupInfo)\n\n\t\t\tawait this.loadEntropy()\n\n\t\t\t// If we have been fully logged in at least once already (probably expired ephemeral session)\n\t\t\t// then we just reconnnect and re-download missing events.\n\t\t\t// For new connections we have special handling.\n\t\t\tif (wasFullyLoggedIn) {\n\t\t\t\tthis.eventBusClient.connect(ConnectMode.Reconnect)\n\t\t\t} else {\n\t\t\t\tthis.eventBusClient.connect(ConnectMode.Initial)\n\t\t\t}\n\n\t\t\tawait this.entropyFacade.storeEntropy()\n\t\t\tthis.loginListener.onFullLoginSuccess(sessionType, cacheInfo)\n\t\t\treturn { user, accessToken, userGroupInfo }\n\t\t} catch (e) {\n\t\t\tthis.resetSession()\n\t\t\tthrow e\n\t\t}\n\t}\n\n\t/**\n\t * init an appropriate cache implementation. we will always try to create a persistent cache for persistent sessions and fall back to an ephemeral cache\n\t * in the browser.\n\t *\n\t * @param userId the user for which the cache is created\n\t * @param databaseKey the key to use\n\t * @param timeRangeDays how far into the past the cache keeps data around\n\t * @param forceNewDatabase true if the old database should be deleted if there is one\n\t * @private\n\t */\n\tprivate async initCache({ userId, databaseKey, timeRangeDays, forceNewDatabase }: InitCacheOptions): Promise<CacheInfo> {\n\t\tif (databaseKey != null) {\n\t\t\treturn this.cacheInitializer.initialize({ type: \"offline\", userId, databaseKey, timeRangeDays, forceNewDatabase })\n\t\t} else {\n\t\t\treturn this.cacheInitializer.initialize({ type: \"ephemeral\", userId })\n\t\t}\n\t}\n\n\tprivate async deInitCache(): Promise<void> {\n\t\treturn this.cacheInitializer.deInitialize()\n\t}\n\n\t/**\n\t * Check whether the passed salt for external user is up-to-date (whether an outdated link was used).\n\t */\n\tprivate async checkOutdatedExternalSalt(credentials: Credentials, sessionData: { userId: Id; accessKey: Aes128Key }, externalUserSalt: Uint8Array) {\n\t\tthis.userFacade.setAccessToken(credentials.accessToken)\n\t\tconst user = await this.entityClient.load(UserTypeRef, sessionData.userId)\n\t\tconst latestSaltHash = assertNotNull(user.externalAuthInfo!.latestSaltHash, \"latestSaltHash is not set!\")\n\t\tif (!arrayEquals(latestSaltHash, sha256Hash(externalUserSalt))) {\n\t\t\t// Do not delete session or credentials, we can still use them if the password\n\t\t\t// hasn't been changed.\n\t\t\tthis.resetSession()\n\t\t\tthrow new AccessExpiredError(\"Salt changed, outdated link?\")\n\t\t}\n\t}\n\n\t/**\n\t * Check that the password is not changed.\n\t * Normally this won't happen for internal users as all sessions are closed on password change. This may happen for external users when the sender has\n\t * changed the password.\n\t * We do not delete all sessions on the server when changing the external password to avoid that an external user is immediately logged out.\n\t */\n\tprivate async checkOutdatedPassword(user: User, accessToken: string, userPassphraseKey: Aes128Key) {\n\t\tif (uint8ArrayToBase64(user.verifier) !== uint8ArrayToBase64(sha256Hash(createAuthVerifier(userPassphraseKey)))) {\n\t\t\tconsole.log(\"External password has changed\")\n\t\t\t// delete the obsolete session to make sure it can not be used any more\n\t\t\tawait this.deleteSession(accessToken).catch((e) => console.error(\"Could not delete session\", e))\n\t\t\tawait this.resetSession()\n\t\t\tthrow new NotAuthenticatedError(\"External password has changed\")\n\t\t}\n\t}\n\n\tprivate loadUserPassphraseKey(mailAddress: string, passphrase: string): Promise<Aes128Key> {\n\t\tmailAddress = mailAddress.toLowerCase().trim()\n\t\tconst saltRequest = createSaltData({ mailAddress })\n\t\treturn this.serviceExecutor.get(SaltService, saltRequest).then((saltReturn: SaltReturn) => {\n\t\t\treturn generateKeyFromPassphrase(passphrase, saltReturn.salt, KeyLength.b128)\n\t\t})\n\t}\n\n\t/**\n\t * We use the accessToken that should be deleted for authentication. Therefore it can be invoked while logged in or logged out.\n\t */\n\tasync deleteSession(accessToken: Base64Url): Promise<void> {\n\t\tlet path = typeRefToPath(SessionTypeRef) + \"/\" + this.getSessionListId(accessToken) + \"/\" + this.getSessionElementId(accessToken)\n\t\tconst sessionTypeModel = await resolveTypeReference(SessionTypeRef)\n\n\t\tconst headers = {\n\t\t\taccessToken: neverNull(accessToken),\n\t\t\tv: sessionTypeModel.version,\n\t\t}\n\t\treturn this.restClient\n\t\t\t.request(path, HttpMethod.DELETE, {\n\t\t\t\theaders,\n\t\t\t\tresponseType: MediaType.Json,\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(NotAuthenticatedError, () => {\n\t\t\t\t\tconsole.log(\"authentication failed => session is already closed\")\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(NotFoundError, () => {\n\t\t\t\t\tconsole.log(\"authentication failed => session instance is already deleted\")\n\t\t\t\t}),\n\t\t\t)\n\t}\n\n\tprivate getSessionElementId(accessToken: Base64Url): Id {\n\t\tlet byteAccessToken = base64ToUint8Array(base64UrlToBase64(neverNull(accessToken)))\n\t\treturn base64ToBase64Url(uint8ArrayToBase64(sha256Hash(byteAccessToken.slice(GENERATED_ID_BYTES_LENGTH))))\n\t}\n\n\tprivate getSessionListId(accessToken: Base64Url): Id {\n\t\tlet byteAccessToken = base64ToUint8Array(base64UrlToBase64(neverNull(accessToken)))\n\t\treturn base64ToBase64Ext(uint8ArrayToBase64(byteAccessToken.slice(0, GENERATED_ID_BYTES_LENGTH)))\n\t}\n\n\tprivate async loadSessionData(accessToken: Base64Url): Promise<{\n\t\tuserId: Id\n\t\taccessKey: Aes128Key\n\t}> {\n\t\tconst path = typeRefToPath(SessionTypeRef) + \"/\" + this.getSessionListId(accessToken) + \"/\" + this.getSessionElementId(accessToken)\n\t\tconst SessionTypeModel = await resolveTypeReference(SessionTypeRef)\n\n\t\tlet headers = {\n\t\t\taccessToken: accessToken,\n\t\t\tv: SessionTypeModel.version,\n\t\t}\n\t\treturn this.restClient\n\t\t\t.request(path, HttpMethod.GET, {\n\t\t\t\theaders,\n\t\t\t\tresponseType: MediaType.Json,\n\t\t\t})\n\t\t\t.then((instance) => {\n\t\t\t\tlet session = JSON.parse(instance)\n\t\t\t\treturn {\n\t\t\t\t\tuserId: session.user,\n\t\t\t\t\taccessKey: base64ToKey(session.accessKey),\n\t\t\t\t}\n\t\t\t})\n\t}\n\n\t/**\n\t * Loads entropy from the last logout.\n\t */\n\tprivate loadEntropy(): Promise<void> {\n\t\treturn this.entityClient.loadRoot(TutanotaPropertiesTypeRef, this.userFacade.getUserGroupId()).then((tutanotaProperties) => {\n\t\t\tif (tutanotaProperties.groupEncEntropy) {\n\t\t\t\ttry {\n\t\t\t\t\tlet entropy = aes128Decrypt(this.userFacade.getUserGroupKey(), neverNull(tutanotaProperties.groupEncEntropy))\n\t\t\t\t\trandom.addStaticEntropy(entropy)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof CryptoError) {\n\t\t\t\t\t\tconsole.log(\"could not decrypt entropy\", error)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tasync changePassword(oldPassword: string, newPassword: string): Promise<void> {\n\t\tconst userSalt = assertNotNull(this.userFacade.getLoggedInUser().salt)\n\t\tlet oldAuthVerifier = createAuthVerifier(generateKeyFromPassphrase(oldPassword, userSalt, KeyLength.b128))\n\t\tlet salt = generateRandomSalt()\n\t\tlet userPassphraseKey = generateKeyFromPassphrase(newPassword, salt, KeyLength.b128)\n\t\tlet pwEncUserGroupKey = encryptKey(userPassphraseKey, this.userFacade.getUserGroupKey())\n\t\tlet authVerifier = createAuthVerifier(userPassphraseKey)\n\t\tlet service = createChangePasswordData()\n\t\tservice.oldVerifier = oldAuthVerifier\n\t\tservice.salt = salt\n\t\tservice.verifier = authVerifier\n\t\tservice.pwEncUserGroupKey = pwEncUserGroupKey\n\t\tawait this.serviceExecutor.post(ChangePasswordService, service)\n\t}\n\n\tasync deleteAccount(password: string, reason: string, takeover: string): Promise<void> {\n\t\tlet d = createDeleteCustomerData()\n\t\tconst userSalt = assertNotNull(this.userFacade.getLoggedInUser().salt)\n\t\td.authVerifier = createAuthVerifier(generateKeyFromPassphrase(password, userSalt, KeyLength.b128))\n\t\td.undelete = false\n\t\td.customer = neverNull(neverNull(this.userFacade.getLoggedInUser()).customer)\n\t\td.reason = reason\n\n\t\tif (takeover !== \"\") {\n\t\t\td.takeoverMailAddress = takeover\n\t\t} else {\n\t\t\td.takeoverMailAddress = null\n\t\t}\n\t\tawait this.serviceExecutor.delete(CustomerService, d)\n\t}\n\n\tdecryptUserPassword(userId: string, deviceToken: string, encryptedPassword: string): Promise<string> {\n\t\tconst getData = createAutoLoginDataGet()\n\t\tgetData.userId = userId\n\t\tgetData.deviceToken = deviceToken\n\t\treturn this.serviceExecutor.get(AutoLoginService, getData).then((returnData) => {\n\t\t\tconst key = uint8ArrayToKey(returnData.deviceKey)\n\t\t\treturn utf8Uint8ArrayToString(aes128Decrypt(key, base64ToUint8Array(encryptedPassword)))\n\t\t})\n\t}\n\n\t/** Changes user password to another one using recoverCode instead of the old password. */\n\trecoverLogin(mailAddress: string, recoverCode: string, newPassword: string, clientIdentifier: string): Promise<void> {\n\t\tconst sessionData = createCreateSessionData()\n\t\tconst recoverCodeKey = uint8ArrayToBitArray(hexToUint8Array(recoverCode))\n\t\tconst recoverCodeVerifier = createAuthVerifier(recoverCodeKey)\n\t\tconst recoverCodeVerifierBase64 = base64ToBase64Url(uint8ArrayToBase64(recoverCodeVerifier))\n\t\tsessionData.mailAddress = mailAddress.toLowerCase().trim()\n\t\tsessionData.clientIdentifier = clientIdentifier\n\t\tsessionData.recoverCodeVerifier = recoverCodeVerifierBase64\n\t\t// we need a separate entity rest client because to avoid caching of the user instance which is updated on password change. the web socket is not connected because we\n\t\t// don't do a normal login and therefore we would not get any user update events. we can not use permanentLogin=false with initSession because caching would be enabled\n\t\t// and therefore we would not be able to read the updated user\n\t\t// additionally we do not want to use initSession() to keep the LoginFacade stateless (except second factor handling) because we do not want to have any race conditions\n\t\t// when logging in normally after resetting the password\n\t\tconst tempAuthDataProvider: AuthDataProvider = {\n\t\t\tcreateAuthHeaders(): Dict {\n\t\t\t\treturn {}\n\t\t\t},\n\t\t\tisFullyLoggedIn(): boolean {\n\t\t\t\treturn false\n\t\t\t},\n\t\t}\n\t\tconst eventRestClient = new EntityRestClient(\n\t\t\ttempAuthDataProvider,\n\t\t\tthis.restClient,\n\t\t\t() => this.cryptoFacade,\n\t\t\tthis.instanceMapper,\n\t\t\tthis.blobAccessTokenFacade,\n\t\t)\n\t\tconst entityClient = new EntityClient(eventRestClient)\n\t\treturn this.serviceExecutor\n\t\t\t.post(SessionService, sessionData) // Don't pass email address to avoid proposing to reset second factor when we're resetting password\n\t\t\t.then((createSessionReturn) => this.waitUntilSecondFactorApprovedOrCancelled(createSessionReturn, null))\n\t\t\t.then((sessionData) => {\n\t\t\t\treturn entityClient\n\t\t\t\t\t.load(UserTypeRef, sessionData.userId, undefined, {\n\t\t\t\t\t\taccessToken: sessionData.accessToken,\n\t\t\t\t\t})\n\t\t\t\t\t.then((user) => {\n\t\t\t\t\t\tif (user.auth == null || user.auth.recoverCode == null) {\n\t\t\t\t\t\t\treturn Promise.reject(new Error(\"missing recover code\"))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst extraHeaders = {\n\t\t\t\t\t\t\taccessToken: sessionData.accessToken,\n\t\t\t\t\t\t\trecoverCodeVerifier: recoverCodeVerifierBase64,\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn entityClient.load(RecoverCodeTypeRef, user.auth.recoverCode, undefined, extraHeaders)\n\t\t\t\t\t})\n\t\t\t\t\t.then((recoverCode) => {\n\t\t\t\t\t\tconst groupKey = aes256DecryptKey(recoverCodeKey, recoverCode.recoverCodeEncUserGroupKey)\n\t\t\t\t\t\tlet salt = generateRandomSalt()\n\t\t\t\t\t\tlet userPassphraseKey = generateKeyFromPassphrase(newPassword, salt, KeyLength.b128)\n\t\t\t\t\t\tlet pwEncUserGroupKey = encryptKey(userPassphraseKey, groupKey)\n\t\t\t\t\t\tlet newPasswordVerifier = createAuthVerifier(userPassphraseKey)\n\t\t\t\t\t\tconst postData = createChangePasswordData()\n\t\t\t\t\t\tpostData.salt = salt\n\t\t\t\t\t\tpostData.pwEncUserGroupKey = pwEncUserGroupKey\n\t\t\t\t\t\tpostData.verifier = newPasswordVerifier\n\t\t\t\t\t\tpostData.recoverCodeVerifier = recoverCodeVerifier\n\t\t\t\t\t\tconst extraHeaders = {\n\t\t\t\t\t\t\taccessToken: sessionData.accessToken,\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn this.serviceExecutor.post(ChangePasswordService, postData, { extraHeaders })\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => this.deleteSession(sessionData.accessToken))\n\t\t\t})\n\t}\n\n\t/** Deletes second factors using recoverCode as second factor. */\n\tresetSecondFactors(mailAddress: string, password: string, recoverCode: Hex): Promise<void> {\n\t\treturn this.loadUserPassphraseKey(mailAddress, password).then((passphraseReturn) => {\n\t\t\tconst authVerifier = createAuthVerifierAsBase64Url(passphraseReturn)\n\t\t\tconst recoverCodeKey = uint8ArrayToBitArray(hexToUint8Array(recoverCode))\n\t\t\tconst recoverCodeVerifier = createAuthVerifierAsBase64Url(recoverCodeKey)\n\t\t\tconst deleteData = createResetFactorsDeleteData()\n\t\t\tdeleteData.mailAddress = mailAddress\n\t\t\tdeleteData.authVerifier = authVerifier\n\t\t\tdeleteData.recoverCodeVerifier = recoverCodeVerifier\n\t\t\treturn this.serviceExecutor.delete(ResetFactorsService, deleteData)\n\t\t})\n\t}\n\n\ttakeOverDeletedAddress(mailAddress: string, password: string, recoverCode: Hex | null, targetAccountMailAddress: string): Promise<void> {\n\t\treturn this.loadUserPassphraseKey(mailAddress, password).then((passphraseReturn) => {\n\t\t\tconst authVerifier = createAuthVerifierAsBase64Url(passphraseReturn)\n\t\t\tlet recoverCodeVerifier: Base64 | null = null\n\n\t\t\tif (recoverCode) {\n\t\t\t\tconst recoverCodeKey = uint8ArrayToBitArray(hexToUint8Array(recoverCode))\n\t\t\t\trecoverCodeVerifier = createAuthVerifierAsBase64Url(recoverCodeKey)\n\t\t\t}\n\n\t\t\tlet data = createTakeOverDeletedAddressData()\n\t\t\tdata.mailAddress = mailAddress\n\t\t\tdata.authVerifier = authVerifier\n\t\t\tdata.recoverCodeVerifier = recoverCodeVerifier\n\t\t\tdata.targetAccountMailAddress = targetAccountMailAddress\n\t\t\treturn this.serviceExecutor.post(TakeOverDeletedAddressService, data)\n\t\t})\n\t}\n\n\tgenerateTotpSecret(): Promise<TotpSecret> {\n\t\treturn this.getTotpVerifier().then((totp) => totp.generateSecret())\n\t}\n\n\tgenerateTotpCode(time: number, key: Uint8Array): Promise<number> {\n\t\treturn this.getTotpVerifier().then((totp) => totp.generateTotp(time, key))\n\t}\n\n\tprivate getTotpVerifier(): Promise<TotpVerifier> {\n\t\treturn Promise.resolve(new TotpVerifier())\n\t}\n\n\tasync retryAsyncLogin(): Promise<void> {\n\t\tif (this.asyncLoginState.state === \"running\") {\n\t\t\treturn\n\t\t} else if (this.asyncLoginState.state === \"failed\") {\n\t\t\tawait this.asyncResumeSession(this.asyncLoginState.credentials, this.asyncLoginState.cacheInfo)\n\t\t} else {\n\t\t\tthrow new Error(\"credentials went missing\")\n\t\t}\n\t}\n}\n\nexport type RecoverData = {\n\tuserEncRecoverCode: Uint8Array\n\trecoverCodeEncUserGroupKey: Uint8Array\n\thexCode: Hex\n\trecoveryCodeVerifier: Uint8Array\n}\n","import { assertWorkerOrNode, getApiOrigin, isAdminClient, isAndroidApp, isWebClient, isWorker } from \"../../common/Env\"\nimport { ConnectionError, handleRestError, PayloadTooLargeError, ServiceUnavailableError, TooManyRequestsError } from \"../../common/error/RestError\"\nimport { HttpMethod, MediaType } from \"../../common/EntityFunctions\"\nimport { assertNotNull, typedEntries, uint8ArrayToArrayBuffer } from \"@tutao/tutanota-utils\"\nimport { SuspensionHandler } from \"../SuspensionHandler\"\nimport { REQUEST_SIZE_LIMIT_DEFAULT, REQUEST_SIZE_LIMIT_MAP } from \"../../common/TutanotaConstants\"\nimport { SuspensionError } from \"../../common/error/SuspensionError.js\"\n\nassertWorkerOrNode()\n\nconst TAG = \"[RestClient]\"\n\ninterface ProgressListener {\n\tupload(percent: number): void\n\n\tdownload(percent: number): void\n}\n\nexport const enum SuspensionBehavior {\n\tSuspend,\n\tThrow,\n}\n\nexport interface RestClientOptions {\n\tbody?: string | Uint8Array\n\tresponseType?: MediaType\n\tprogressListener?: ProgressListener\n\tbaseUrl?: string\n\theaders?: Dict\n\tqueryParams?: Dict\n\tnoCORS?: boolean\n\t/** Default is to suspend all requests on rate limit. */\n\tsuspensionBehavior?: SuspensionBehavior\n}\n\n/**\n * Allows REST communication with the server.\n * The RestClient observes upload/download progress and times\n * out in case no data is sent or received for a certain time.\n *\n * Uses XmlHttpRequest as there is still no support for tracking\n * upload progress with fetch (see https://stackoverflow.com/a/69400632)\n */\nexport class RestClient {\n\tprivate id: number\n\tprivate suspensionHandler: SuspensionHandler\n\t// accurate to within a few seconds, depending on network speed\n\tprivate serverTimeOffsetMs: number | null = null\n\n\tconstructor(suspensionHandler: SuspensionHandler) {\n\t\tthis.id = 0\n\t\tthis.suspensionHandler = suspensionHandler\n\t}\n\n\trequest(path: string, method: HttpMethod, options: RestClientOptions = {}): Promise<any | null> {\n\t\t// @ts-ignore\n\t\tconst debug = typeof self !== \"undefined\" && self.debug\n\t\tconst verbose = isWorker() && debug\n\n\t\tthis.checkRequestSizeLimit(path, method, options.body ?? null)\n\n\t\tif (this.suspensionHandler.isSuspended()) {\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.request(path, method, options))\n\t\t} else {\n\t\t\treturn new Promise(async (resolve, reject) => {\n\t\t\t\tthis.id++\n\n\t\t\t\tconst queryParams: Dict = options.queryParams ?? {}\n\n\t\t\t\tif (method === HttpMethod.GET && typeof options.body === \"string\") {\n\t\t\t\t\tqueryParams[\"_body\"] = options.body // get requests are not allowed to send a body. Therefore, we convert our body to a paramater\n\t\t\t\t}\n\n\t\t\t\tif (options.noCORS) {\n\t\t\t\t\tqueryParams[\"cv\"] = env.versionNumber\n\t\t\t\t}\n\n\t\t\t\tconst origin = options.baseUrl ?? getApiOrigin()\n\t\t\t\tconst url = addParamsToUrl(new URL(origin + path), queryParams)\n\t\t\t\tconst xhr = new XMLHttpRequest()\n\t\t\t\txhr.open(method, url.toString())\n\n\t\t\t\tthis.setHeaders(xhr, options)\n\n\t\t\t\txhr.responseType = options.responseType === MediaType.Json || options.responseType === MediaType.Text ? \"text\" : \"arraybuffer\"\n\n\t\t\t\tconst abortAfterTimeout = () => {\n\t\t\t\t\tconst res = {\n\t\t\t\t\t\ttimeoutId: 0 as TimeoutID,\n\t\t\t\t\t\tabortFunction: () => {\n\t\t\t\t\t\t\tif (this.usingTimeoutAbort()) {\n\t\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} aborting ` + String(res.timeoutId))\n\t\t\t\t\t\t\t\txhr.abort()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\treturn res\n\t\t\t\t}\n\n\t\t\t\tconst t = abortAfterTimeout()\n\t\t\t\tlet timeout = setTimeout(t.abortFunction, env.timeout)\n\t\t\t\tt.timeoutId = timeout\n\n\t\t\t\tif (verbose) {\n\t\t\t\t\tconsole.log(TAG, `${this.id}: set initial timeout ${String(timeout)} of ${env.timeout}`)\n\t\t\t\t}\n\n\t\t\t\txhr.onload = () => {\n\t\t\t\t\t// XMLHttpRequestProgressEvent, but not needed\n\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} finished request. Clearing Timeout ${String(timeout)}.`)\n\t\t\t\t\t}\n\n\t\t\t\t\tclearTimeout(timeout)\n\n\t\t\t\t\tthis.saveServerTimeOffsetFromRequest(xhr)\n\n\t\t\t\t\tif (xhr.status === 200 || (method === HttpMethod.POST && xhr.status === 201)) {\n\t\t\t\t\t\tif (options.responseType === MediaType.Json || options.responseType === MediaType.Text) {\n\t\t\t\t\t\t\tresolve(xhr.response)\n\t\t\t\t\t\t} else if (options.responseType === MediaType.Binary) {\n\t\t\t\t\t\t\tresolve(new Uint8Array(xhr.response))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(null)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst suspensionTime = xhr.getResponseHeader(\"Retry-After\") || xhr.getResponseHeader(\"Suspension-Time\")\n\n\t\t\t\t\t\tif (isSuspensionResponse(xhr.status, suspensionTime) && options.suspensionBehavior === SuspensionBehavior.Throw) {\n\t\t\t\t\t\t\treject(new SuspensionError(`blocked for ${suspensionTime}, not suspending`))\n\t\t\t\t\t\t} else if (isSuspensionResponse(xhr.status, suspensionTime)) {\n\t\t\t\t\t\t\tthis.suspensionHandler.activateSuspensionIfInactive(Number(suspensionTime))\n\n\t\t\t\t\t\t\tresolve(this.suspensionHandler.deferRequest(() => this.request(path, method, options)))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlogFailedRequest(method, url, xhr, options)\n\t\t\t\t\t\t\treject(handleRestError(xhr.status, `| ${method} ${path}`, xhr.getResponseHeader(\"Error-Id\"), xhr.getResponseHeader(\"Precondition\")))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\txhr.onerror = function () {\n\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\tlogFailedRequest(method, url, xhr, options)\n\t\t\t\t\treject(handleRestError(xhr.status, ` | ${method} ${path}`, xhr.getResponseHeader(\"Error-Id\"), xhr.getResponseHeader(\"Precondition\")))\n\t\t\t\t}\n\n\t\t\t\t// don't add an EventListener for non-CORS requests, otherwise it would not meet the 'CORS-Preflight simple request' requirements\n\t\t\t\tif (!options.noCORS) {\n\t\t\t\t\txhr.upload.onprogress = (pe: ProgressEvent) => {\n\t\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} upload progress. Clearing Timeout ${String(timeout)}`, pe)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\t\tconst t = abortAfterTimeout()\n\t\t\t\t\t\ttimeout = setTimeout(t.abortFunction, env.timeout)\n\t\t\t\t\t\tt.timeoutId = timeout\n\n\t\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: set new timeout ${String(timeout)} of ${env.timeout}`)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (options.progressListener != null && pe.lengthComputable) {\n\t\t\t\t\t\t\t// see https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent\n\t\t\t\t\t\t\toptions.progressListener.upload((1 / pe.total) * pe.loaded)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\txhr.upload.ontimeout = (e) => {\n\t\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} upload timeout. calling error handler.`, e)\n\t\t\t\t\t\t}\n\t\t\t\t\t\txhr.onerror?.(e)\n\t\t\t\t\t}\n\n\t\t\t\t\txhr.upload.onerror = (e) => {\n\t\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} upload error. calling error handler.`, e)\n\t\t\t\t\t\t}\n\t\t\t\t\t\txhr.onerror?.(e)\n\t\t\t\t\t}\n\n\t\t\t\t\txhr.upload.onabort = (e) => {\n\t\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} upload aborted. calling error handler.`, e)\n\t\t\t\t\t\t}\n\t\t\t\t\t\txhr.onerror?.(e)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\txhr.onprogress = (pe: ProgressEvent) => {\n\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\tconsole.log(TAG, `${this.id}: ${String(new Date())} download progress. Clearing Timeout ${String(timeout)}`, pe)\n\t\t\t\t\t}\n\n\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\tlet t = abortAfterTimeout()\n\t\t\t\t\ttimeout = setTimeout(t.abortFunction, env.timeout)\n\t\t\t\t\tt.timeoutId = timeout\n\n\t\t\t\t\tif (verbose) {\n\t\t\t\t\t\tconsole.log(TAG, `${this.id}: set new timeout ${String(timeout)} of ${env.timeout}`)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.progressListener != null && pe.lengthComputable) {\n\t\t\t\t\t\t// see https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent\n\t\t\t\t\t\toptions.progressListener.download((1 / pe.total) * pe.loaded)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\txhr.onabort = () => {\n\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\treject(new ConnectionError(`Reached timeout of ${env.timeout}ms ${xhr.statusText} | ${method} ${path}`))\n\t\t\t\t}\n\n\t\t\t\tif (options.body instanceof Uint8Array) {\n\t\t\t\t\txhr.send(uint8ArrayToArrayBuffer(options.body))\n\t\t\t\t} else {\n\t\t\t\t\txhr.send(options.body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\t/** We only need to track timeout directly here on some platforms. Other platforms do it inside their network driver. */\n\tprivate usingTimeoutAbort() {\n\t\treturn isWebClient() || isAndroidApp()\n\t}\n\n\tprivate saveServerTimeOffsetFromRequest(xhr: XMLHttpRequest) {\n\t\t// Dates sent in the `Date` field of HTTP headers follow the format specified by rfc7231\n\t\t// JavaScript's Date expects dates in the format specified by rfc2822\n\t\t// rfc7231 provides three options of formats, the preferred one being IMF-fixdate. This one is definitely\n\t\t// parseable by any rfc2822 compatible parser, since it is a strict subset (with no folding white space) of the\n\t\t// format of rfc5322, which is the same as rfc2822 accepting more folding white spaces.\n\t\t// Furthermore, there is no reason to expect the server to return any of the other two accepted formats, which\n\t\t// are obsolete and accepted only for backwards compatibility.\n\t\tconst serverTimestamp = xhr.getResponseHeader(\"Date\")\n\n\t\tif (serverTimestamp != null) {\n\t\t\t// check that serverTimestamp has been returned\n\t\t\tconst serverTime = new Date(serverTimestamp).getTime()\n\n\t\t\tif (!isNaN(serverTime)) {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tthis.serverTimeOffsetMs = serverTime - now\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the time on the server based on the client time + the server time offset\n\t * The server time offset is calculated based on the date field in the header returned from REST requests.\n\t * will throw an error if offline or no rest requests have been made yet\n\t */\n\tgetServerTimestampMs(): number {\n\t\tconst timeOffset = assertNotNull(this.serverTimeOffsetMs, \"You can't get server time if no rest requests were made\")\n\t\treturn Date.now() + timeOffset\n\t}\n\n\t/**\n\t * Checks if the request body is too large.\n\t * Ignores the method because GET requests etc. should not exceed the limits neither.\n\t * This is done to avoid making the request, because the server will return a PayloadTooLargeError anyway.\n\t * */\n\tprivate checkRequestSizeLimit(path: string, method: HttpMethod, body: string | Uint8Array | null) {\n\t\tif (isAdminClient()) {\n\t\t\treturn\n\t\t}\n\n\t\tconst limit = REQUEST_SIZE_LIMIT_MAP.get(path) ?? REQUEST_SIZE_LIMIT_DEFAULT\n\n\t\tif (body && body.length > limit) {\n\t\t\tthrow new PayloadTooLargeError(`request body is too large. Path: ${path}, Method: ${method}, Body length: ${body.length}`)\n\t\t}\n\t}\n\n\tprivate setHeaders(xhr: XMLHttpRequest, options: RestClientOptions) {\n\t\tif (options.headers == null) {\n\t\t\toptions.headers = {}\n\t\t}\n\t\tconst { headers, body, responseType } = options\n\n\t\t// don't add custom and content-type headers for non-CORS requests, otherwise it would not meet the 'CORS-Preflight simple request' requirements\n\t\tif (!options.noCORS) {\n\t\t\theaders[\"cv\"] = env.versionNumber\n\t\t\tif (body instanceof Uint8Array) {\n\t\t\t\theaders[\"Content-Type\"] = MediaType.Binary\n\t\t\t} else if (typeof body === \"string\") {\n\t\t\t\theaders[\"Content-Type\"] = MediaType.Json\n\t\t\t}\n\t\t}\n\n\t\tif (responseType) {\n\t\t\theaders[\"Accept\"] = responseType\n\t\t}\n\t\tfor (const i in headers) {\n\t\t\txhr.setRequestHeader(i, headers[i])\n\t\t}\n\t}\n}\n\nexport function addParamsToUrl(url: URL, urlParams: Dict): URL {\n\tif (urlParams) {\n\t\tfor (const [key, value] of typedEntries(urlParams)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\turl.searchParams.set(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn url\n}\n\nexport function isSuspensionResponse(statusCode: number, suspensionTimeNumberString: string | null): boolean {\n\treturn Number(suspensionTimeNumberString) > 0 && (statusCode === TooManyRequestsError.CODE || statusCode === ServiceUnavailableError.CODE)\n}\n\nfunction logFailedRequest(method: HttpMethod, url: URL, xhr: XMLHttpRequest, options: RestClientOptions): void {\n\tconst args: Array<unknown> = [TAG, \"failed request\", method, url.toString(), xhr.status, xhr.statusText]\n\tif (options.headers != null) {\n\t\targs.push(Object.keys(options.headers))\n\t}\n\tif (options.body != null) {\n\t\tconst logBody = \"string\" === typeof options.body ? `[${options.body.length} characters]` : `[${options.body.length} bytes]`\n\t\targs.push(logBody)\n\t} else {\n\t\targs.push(\"no body\")\n\t}\n\tconsole.log(...args)\n}\n","import type { DeferredObject } from \"@tutao/tutanota-utils\"\nimport { defer, noOp } from \"@tutao/tutanota-utils\"\nimport type { SystemTimeout } from \"../common/utils/Scheduler.js\"\nimport { InfoMessageHandler } from \"../../gui/InfoMessageHandler.js\"\n\nexport class SuspensionHandler {\n\t_isSuspended: boolean\n\t_suspendedUntil: number\n\t_deferredRequests: Array<DeferredObject<any>>\n\t_hasSentInfoMessage: boolean\n\t_timeout: SystemTimeout\n\n\tconstructor(private readonly infoMessageHandler: InfoMessageHandler, systemTimeout: SystemTimeout) {\n\t\tthis._isSuspended = false\n\t\tthis._suspendedUntil = 0\n\t\tthis._deferredRequests = []\n\t\tthis._hasSentInfoMessage = false\n\t\tthis._timeout = systemTimeout\n\t}\n\n\t/**\n\t * Activates suspension states for the given amount of seconds. After the end of the suspension time all deferred requests are executed.\n\t */\n\t// if already suspended do we want to ignore incoming suspensions?\n\tactivateSuspensionIfInactive(suspensionDurationSeconds: number) {\n\t\tif (!this.isSuspended()) {\n\t\t\tconsole.log(`Activating suspension:  ${suspensionDurationSeconds}s`)\n\t\t\tthis._isSuspended = true\n\t\t\tconst suspensionStartTime = Date.now()\n\n\t\t\tthis._timeout.setTimeout(async () => {\n\t\t\t\tthis._isSuspended = false\n\t\t\t\tconsole.log(`Suspension released after ${(Date.now() - suspensionStartTime) / 1000}s`)\n\t\t\t\tawait this._onSuspensionComplete()\n\t\t\t}, suspensionDurationSeconds * 1000)\n\n\t\t\tif (!this._hasSentInfoMessage) {\n\t\t\t\tthis.infoMessageHandler.onInfoMessage({\n\t\t\t\t\ttranslationKey: \"clientSuspensionWait_label\",\n\t\t\t\t\targs: {},\n\t\t\t\t})\n\n\t\t\t\tthis._hasSentInfoMessage = true\n\t\t\t}\n\t\t}\n\t}\n\n\tisSuspended(): boolean {\n\t\treturn this._isSuspended\n\t}\n\n\t/**\n\t * Adds a request to the deferred queue.\n\t * @param request\n\t * @returns {Promise<T>}\n\t */\n\tdeferRequest(request: () => Promise<any>): Promise<any> {\n\t\tif (this._isSuspended) {\n\t\t\tconst deferredObject = defer()\n\n\t\t\tthis._deferredRequests.push(deferredObject)\n\n\t\t\t// assign request promise to deferred object\n\t\t\tdeferredObject.promise = deferredObject.promise.then(() => request())\n\t\t\treturn deferredObject.promise\n\t\t} else {\n\t\t\t// if suspension is not activated then immediately execute the request\n\t\t\treturn request()\n\t\t}\n\t}\n\n\tasync _onSuspensionComplete() {\n\t\tconst deferredRequests = this._deferredRequests\n\t\tthis._deferredRequests = []\n\n\t\t// do wee need to delay those requests?\n\t\tfor (let deferredRequest of deferredRequests) {\n\t\t\tdeferredRequest.resolve(null)\n\t\t\t// Ignore all errors here, any errors should be caught by whoever is handling the deferred request\n\t\t\tawait deferredRequest.promise.catch(noOp)\n\t\t}\n\t}\n}\n","import { aes256Decrypt, aes256Encrypt, aes256RandomKey, bitArrayToUint8Array, CryptoError, generateIV, uint8ArrayToBitArray } from \"@tutao/tutanota-crypto\"\nimport { CryptoError as TutanotaCryptoError } from \"../../common/error/CryptoError.js\"\n\nexport class DeviceEncryptionFacade {\n\t/**\n\t * Generates an encryption key.\n\t */\n\tasync generateKey(): Promise<Uint8Array> {\n\t\treturn bitArrayToUint8Array(aes256RandomKey())\n\t}\n\n\t/**\n\t * Encrypts {@param data} using {@param deviceKey}.\n\t * @param deviceKey Key used for encryption\n\t * @param data Data to encrypt.\n\t */\n\tasync encrypt(deviceKey: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n\t\treturn aes256Encrypt(uint8ArrayToBitArray(deviceKey), data, generateIV())\n\t}\n\n\t/**\n\t * Decrypts {@param encryptedData} using {@param deviceKey}.\n\t * @param deviceKey Key used for encryption\n\t * @param encryptedData Data to be decrypted.\n\t */\n\tasync decrypt(deviceKey: Uint8Array, encryptedData: Uint8Array): Promise<Uint8Array> {\n\t\ttry {\n\t\t\treturn aes256Decrypt(uint8ArrayToBitArray(deviceKey), encryptedData)\n\t\t} catch (e) {\n\t\t\t// CryptoError from tutanota-crypto is not mapped correctly across the worker bridge\n\t\t\t// so we map it to the CryptoError we can actually catch on the other side\n\t\t\tif (e instanceof CryptoError) {\n\t\t\t\tthrow new TutanotaCryptoError(\"Decrypting credentials failed\", e)\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n}\n","import { IV_BYTE_LENGTH, keyToUint8Array, Randomizer } from \"@tutao/tutanota-crypto\"\nimport { FileUri } from \"../common/FileApp\"\nimport { NativeCryptoFacade } from \"../common/generatedipc/NativeCryptoFacade\"\nimport { EncryptedFileInfo } from \"../common/generatedipc/EncryptedFileInfo\"\n\nexport class AesApp {\n\tconstructor(private readonly nativeCryptoFacade: NativeCryptoFacade, private readonly random: Randomizer) {}\n\n\t/**\n\t * Encrypts a file with the provided key\n\t * @return Returns the URI of the decrypted file. Resolves to an exception if the encryption failed.\n\t */\n\taesEncryptFile(key: Aes128Key, fileUrl: FileUri): Promise<EncryptedFileInfo> {\n\t\tconst iv = this.random.generateRandomData(IV_BYTE_LENGTH)\n\t\tconst encodedKey = keyToUint8Array(key)\n\t\treturn this.nativeCryptoFacade.aesEncryptFile(encodedKey, fileUrl, iv)\n\t}\n\n\t/**\n\t * Decrypt bytes with the provided key\n\t * @return Returns the URI of the decrypted file. Resolves to an exception if the encryption failed.\n\t */\n\taesDecryptFile(key: Aes128Key, fileUrl: FileUri): Promise<FileUri> {\n\t\tconst encodedKey = keyToUint8Array(key)\n\t\treturn this.nativeCryptoFacade.aesDecryptFile(encodedKey, fileUrl)\n\t}\n}\n","import type { NativeInterface } from \"../../../native/common/NativeInterface\"\nimport { isApp } from \"../../common/Env\"\nimport { generateRsaKey, random, rsaDecrypt, rsaEncrypt } from \"@tutao/tutanota-crypto\"\nimport type { PrivateKey, PublicKey, RsaKeyPair } from \"@tutao/tutanota-crypto\"\nimport { NativeCryptoFacadeSendDispatcher } from \"../../../native/common/generatedipc/NativeCryptoFacadeSendDispatcher\"\n\nexport async function createRsaImplementation(native: NativeInterface): Promise<RsaImplementation> {\n\tif (isApp()) {\n\t\tconst { RsaApp } = await import(\"../../../native/worker/RsaApp\")\n\t\treturn new RsaApp(new NativeCryptoFacadeSendDispatcher(native), random)\n\t} else {\n\t\treturn new RsaWeb()\n\t}\n}\n\nexport interface RsaImplementation {\n\tgenerateKey(): Promise<RsaKeyPair>\n\n\tencrypt(publicKey: PublicKey, bytes: Uint8Array): Promise<Uint8Array>\n\n\tdecrypt(privateKey: PrivateKey, bytes: Uint8Array): Promise<Uint8Array>\n}\n\nexport class RsaWeb implements RsaImplementation {\n\tasync generateKey(): Promise<RsaKeyPair> {\n\t\treturn generateRsaKey()\n\t}\n\n\tasync encrypt(publicKey: PublicKey, bytes: Uint8Array): Promise<Uint8Array> {\n\t\tconst seed = random.generateRandomData(32)\n\t\treturn rsaEncrypt(publicKey, bytes, seed)\n\t}\n\n\tasync decrypt(privateKey: PrivateKey, bytes: Uint8Array): Promise<Uint8Array> {\n\t\treturn rsaDecrypt(privateKey, bytes)\n\t}\n}\n","/**\n * lz4 compression/decompression routines adopted from the node-lz4 library\n * https://github.com/pierrec/node-lz4\n * (which is a port of the original LZ4 library http://www.lz4.org).\n *\n * node-lz4 does a lot of things we don't need and drags Node Buffer and\n * whatnot with it and subsequently weights 103KB.\n *\n * Modified to include auto-resizing of the buffer and slicing of the data.\n */\n\n/*\nCopyright (c) 2012 Pierre Curto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n */\n\n/**\n * The largest a mail body can be to send. See StringUtils in TutaDb\n */\nexport const UNCOMPRESSED_MAX_SIZE = 1024 * 1024\n\n/**\n * Decode a block. Assumptions: input contains all sequences of a\n * chunk.\n * @param input {Buffer} input data\n * @throws on invalid offset\n * @return {Uint8Array} decoded data\n */\nexport function uncompress(input: Uint8Array): Uint8Array {\n\tconst endIndex = input.length\n\tlet output = new Uint8Array(input.length * 6)\n\tlet j = 0\n\n\t// Process each sequence in the incoming data\n\tfor (let i = 0, n = endIndex; i < n; ) {\n\t\tlet token = input[i++]\n\t\t// Literals\n\t\tlet literals_length = token >> 4\n\n\t\tif (literals_length > 0) {\n\t\t\t// length of literals\n\t\t\tlet l = literals_length + 240\n\n\t\t\twhile (l === 255) {\n\t\t\t\tl = input[i++]\n\t\t\t\tliterals_length += l\n\t\t\t}\n\n\t\t\t// Copy the literals\n\t\t\tlet end = i + literals_length\n\t\t\tconst sizeNeeded = j + (end - i)\n\n\t\t\tif (output.length < sizeNeeded) {\n\t\t\t\tconst newSize = Math.max(output.length * 2, sizeNeeded)\n\t\t\t\tconst newOutput = new Uint8Array(newSize)\n\t\t\t\tnewOutput.set(output)\n\t\t\t\toutput = newOutput\n\t\t\t}\n\n\t\t\twhile (i < end) output[j++] = input[i++]\n\n\t\t\t// End of buffer?\n\t\t\tif (i === n) break // return j\n\t\t}\n\n\t\t// Match copy\n\t\t// 2 bytes offset (little endian)\n\t\tlet offset = input[i++] | (input[i++] << 8)\n\n\t\t// 0 is an invalid offset value\n\t\tif (offset === 0 || offset > j) {\n\t\t\t// was:\n\t\t\t// return -(i - 2)\n\t\t\tthrow new Error(`Invalid offset value. i: ${i}, -(i-2): ${-(i - 2)}`)\n\t\t}\n\n\t\t// length of match copy\n\t\tlet match_length = token & 0xf\n\t\tlet l = match_length + 240\n\n\t\twhile (l === 255) {\n\t\t\tl = input[i++]\n\t\t\tmatch_length += l\n\t\t}\n\n\t\t// Copy the match\n\t\tlet pos = j - offset // position of the match copy in the current output\n\n\t\tlet end = j + match_length + 4 // minmatch = 4\n\n\t\tconst sizeNeeded = end\n\n\t\tif (output.length < sizeNeeded) {\n\t\t\tconst newSize = Math.max(output.length * 2, sizeNeeded)\n\t\t\tconst newOutput = new Uint8Array(newSize)\n\t\t\tnewOutput.set(output)\n\t\t\toutput = newOutput\n\t\t}\n\n\t\twhile (j < end) output[j++] = output[pos++]\n\t}\n\n\treturn output.slice(0, j)\n}\n\nconst MAX_INPUT_SIZE = 0x7e000000\nconst MIN_MATCH = 4\nconst HASH_LOG = 16\nconst HASH_SHIFT = MIN_MATCH * 8 - HASH_LOG\nconst HASH_SIZE = 1 << HASH_LOG\nconst COPY_LENGTH = 8\nconst MF_LIMIT = COPY_LENGTH + MIN_MATCH\nconst SKIP_STRENGTH = 6\nconst ML_BITS = 4\nconst ML_MASK = (1 << ML_BITS) - 1\nconst RUN_BITS = 8 - ML_BITS\nconst RUN_MASK = (1 << RUN_BITS) - 1\nconst HASHER = 2654435761\n\n// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length\nfunction compressBound(isize: number) {\n\treturn isize > MAX_INPUT_SIZE ? 0 : (isize + isize / 255 + 16) | 0\n}\n\nexport function compress(source: Uint8Array): Uint8Array {\n\tif (source.length === 0) return new Uint8Array(0)\n\tconst dest = new Uint8Array(compressBound(source.length))\n\t// V8 optimization: non sparse array with integers\n\tconst hashTable = new Array(HASH_SIZE).fill(0)\n\tlet sourcePos = 0\n\tlet destPos = 0\n\tlet anchor = 0\n\tlet step = 1\n\tlet findMatchAttempts = (1 << SKIP_STRENGTH) + 3\n\tconst srcLength = source.length - MF_LIMIT\n\n\twhile (sourcePos + MIN_MATCH < srcLength) {\n\t\t// Find a match\n\t\t// min match of 4 bytes aka sequence\n\t\tconst sequenceLowBits = (source[sourcePos + 1] << 8) | source[sourcePos]\n\t\tconst sequenceHighBits = (source[sourcePos + 3] << 8) | source[sourcePos + 2]\n\t\t// compute hash for the current sequence\n\t\tconst hash = Math.imul(sequenceLowBits | (sequenceHighBits << 16), HASHER) >>> HASH_SHIFT\n\t\t// get the position of the sequence matching the hash\n\t\t// NB. since 2 different sequences may have the same hash\n\t\t// it is double-checked below\n\t\t// do -1 to distinguish between initialized and uninitialized values\n\t\tlet ref = hashTable[hash] - 1\n\t\t// save position of current sequence in hash table\n\t\thashTable[hash] = sourcePos + 1\n\n\t\t// first reference or within 64k limit or current sequence !== hashed one: no match\n\t\tif (\n\t\t\tref < 0 ||\n\t\t\t(sourcePos - ref) >>> 16 > 0 ||\n\t\t\t((source[ref + 3] << 8) | source[ref + 2]) != sequenceHighBits ||\n\t\t\t((source[ref + 1] << 8) | source[ref]) != sequenceLowBits\n\t\t) {\n\t\t\t// increase step if nothing found within limit\n\t\t\tstep = findMatchAttempts++ >> SKIP_STRENGTH\n\t\t\tsourcePos += step\n\t\t\tcontinue\n\t\t}\n\n\t\tfindMatchAttempts = (1 << SKIP_STRENGTH) + 3\n\t\t// got a match\n\t\tconst literals_length = sourcePos - anchor\n\t\tconst offset = sourcePos - ref\n\t\t// MIN_MATCH already verified\n\t\tsourcePos += MIN_MATCH\n\t\tref += MIN_MATCH\n\t\t// move to the end of the match (>=MIN_MATCH)\n\t\tlet match_length = sourcePos\n\n\t\twhile (sourcePos < srcLength && source[sourcePos] == source[ref]) {\n\t\t\tsourcePos++\n\t\t\tref++\n\t\t}\n\n\t\t// match length\n\t\tmatch_length = sourcePos - match_length\n\t\t// token\n\t\tconst token = match_length < ML_MASK ? match_length : ML_MASK\n\n\t\t// encode literals length\n\t\tif (literals_length >= RUN_MASK) {\n\t\t\tlet len\n\t\t\t// add match length to the token\n\t\t\tdest[destPos++] = (RUN_MASK << ML_BITS) + token\n\n\t\t\tfor (len = literals_length - RUN_MASK; len > 254; len -= 255) {\n\t\t\t\tdest[destPos++] = 255\n\t\t\t}\n\n\t\t\tdest[destPos++] = len\n\t\t} else {\n\t\t\t// add match length to the token\n\t\t\tdest[destPos++] = (literals_length << ML_BITS) + token\n\t\t}\n\n\t\t// write literals\n\t\tfor (let i = 0; i < literals_length; i++) {\n\t\t\tdest[destPos++] = source[anchor + i]\n\t\t}\n\n\t\t// encode offset\n\t\tdest[destPos++] = offset\n\t\tdest[destPos++] = offset >> 8\n\n\t\t// encode match length\n\t\tif (match_length >= ML_MASK) {\n\t\t\tmatch_length -= ML_MASK\n\n\t\t\twhile (match_length >= 255) {\n\t\t\t\tmatch_length -= 255\n\t\t\t\tdest[destPos++] = 255\n\t\t\t}\n\n\t\t\tdest[destPos++] = match_length\n\t\t}\n\n\t\tanchor = sourcePos\n\t}\n\n\t// Write last literals\n\t// encode literals length\n\tconst literals_length = source.length - anchor\n\n\tif (literals_length >= RUN_MASK) {\n\t\tlet ln = literals_length - RUN_MASK\n\t\t// add match length to the token\n\t\tdest[destPos++] = RUN_MASK << ML_BITS\n\n\t\twhile (ln > 254) {\n\t\t\tdest[destPos++] = 255\n\t\t\tln -= 255\n\t\t}\n\n\t\tdest[destPos++] = ln\n\t} else {\n\t\t// add match length to the token\n\t\tdest[destPos++] = literals_length << ML_BITS\n\t}\n\n\t// write literals\n\tsourcePos = anchor\n\n\twhile (sourcePos < source.length) {\n\t\tdest[destPos++] = source[sourcePos++]\n\t}\n\n\treturn dest.slice(0, destPos)\n}\n\nexport class CompressionError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t}\n}\n","import { resolveTypeReference } from \"../../common/EntityFunctions\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { base64ToBase64Url, base64ToUint8Array, downcast, stringToUtf8Uint8Array, uint8ArrayToBase64, utf8Uint8ArrayToString } from \"@tutao/tutanota-utils\"\nimport { AssociationType, Cardinality, Type, ValueType } from \"../../common/EntityConstants\"\nimport { compress, uncompress } from \"../Compression\"\nimport { TypeRef } from \"@tutao/tutanota-utils\"\nimport { promiseMap } from \"@tutao/tutanota-utils\"\nimport type { ModelValue, TypeModel } from \"../../common/EntityTypes\"\nimport { assertNotNull } from \"@tutao/tutanota-utils\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport type { Base64 } from \"@tutao/tutanota-utils\"\nimport { aes128Decrypt, aes128Encrypt, ENABLE_MAC, IV_BYTE_LENGTH, random } from \"@tutao/tutanota-crypto\"\n\nassertWorkerOrNode()\n\nexport class InstanceMapper {\n\t/**\n\t * Decrypts an object literal as received from the DB and maps it to an entity class (e.g. Mail)\n\t * @param model The TypeModel of the instance\n\t * @param instance The object literal as received from the DB\n\t * @param sk The session key, must be provided for encrypted instances\n\t * @returns The decrypted and mapped instance\n\t */\n\tdecryptAndMapToInstance<T>(model: TypeModel, instance: Record<string, any>, sk: Aes128Key | null): Promise<T> {\n\t\tlet decrypted: any = {\n\t\t\t_type: new TypeRef(model.app, model.name),\n\t\t}\n\n\t\tfor (let key of Object.keys(model.values)) {\n\t\t\tlet valueType = model.values[key]\n\t\t\tlet value = instance[key]\n\n\t\t\ttry {\n\t\t\t\tdecrypted[key] = decryptValue(key, valueType, value, sk)\n\t\t\t} catch (e) {\n\t\t\t\tif (decrypted._errors == null) {\n\t\t\t\t\tdecrypted._errors = {}\n\t\t\t\t}\n\n\t\t\t\tdecrypted._errors[key] = JSON.stringify(e)\n\t\t\t\tconsole.log(\"error when decrypting value on type:\", `[${model.app},${model.name}]`, \"key:\", key)\n\t\t\t} finally {\n\t\t\t\tif (valueType.encrypted) {\n\t\t\t\t\tif (valueType.final) {\n\t\t\t\t\t\t// we have to store the encrypted value to be able to restore it when updating the instance. this is not needed for data transfer types, but it does not hurt\n\t\t\t\t\t\tdecrypted[\"_finalEncrypted_\" + key] = value\n\t\t\t\t\t} else if (value === \"\") {\n\t\t\t\t\t\t// we have to store the default value to make sure that updates do not cause more storage use\n\t\t\t\t\t\tdecrypted[\"_defaultEncrypted_\" + key] = decrypted[key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn promiseMap(Object.keys(model.associations), (associationName) => {\n\t\t\tif (model.associations[associationName].type === AssociationType.Aggregation) {\n\t\t\t\tconst dependency = model.associations[associationName].dependency\n\t\t\t\treturn resolveTypeReference(new TypeRef(dependency || model.app, model.associations[associationName].refType)).then((aggregateTypeModel) => {\n\t\t\t\t\tlet aggregation = model.associations[associationName]\n\n\t\t\t\t\tif (aggregation.cardinality === Cardinality.ZeroOrOne && instance[associationName] == null) {\n\t\t\t\t\t\tdecrypted[associationName] = null\n\t\t\t\t\t} else if (instance[associationName] == null) {\n\t\t\t\t\t\tthrow new ProgrammingError(`Undefined aggregation ${model.name}:${associationName}`)\n\t\t\t\t\t} else if (aggregation.cardinality === Cardinality.Any) {\n\t\t\t\t\t\treturn promiseMap(instance[associationName], (aggregate) => {\n\t\t\t\t\t\t\treturn this.decryptAndMapToInstance(aggregateTypeModel, downcast<Record<string, any>>(aggregate), sk)\n\t\t\t\t\t\t}).then((decryptedAggregates) => {\n\t\t\t\t\t\t\tdecrypted[associationName] = decryptedAggregates\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn this.decryptAndMapToInstance(aggregateTypeModel, instance[associationName], sk).then((decryptedAggregate) => {\n\t\t\t\t\t\t\tdecrypted[associationName] = decryptedAggregate\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tdecrypted[associationName] = instance[associationName]\n\t\t\t}\n\t\t}).then(() => {\n\t\t\treturn decrypted\n\t\t})\n\t}\n\n\tencryptAndMapToLiteral<T>(model: TypeModel, instance: T, sk: Aes128Key | null): Promise<Record<string, unknown>> {\n\t\tlet encrypted: Record<string, unknown> = {}\n\t\tlet i = instance as any\n\n\t\tfor (let key of Object.keys(model.values)) {\n\t\t\tlet valueType = model.values[key]\n\t\t\tlet value = i[key]\n\n\t\t\t// restore the original encrypted value if it exists. it does not exist if this is a data transfer type or a newly created entity. check against null explicitely because \"\" is allowed\n\t\t\tif (valueType.encrypted && valueType.final && i[\"_finalEncrypted_\" + key] != null) {\n\t\t\t\tencrypted[key] = i[\"_finalEncrypted_\" + key]\n\t\t\t} else if (valueType.encrypted && i[\"_defaultEncrypted_\" + key] === value) {\n\t\t\t\t// restore the default encrypted value because it has not changed\n\t\t\t\tencrypted[key] = \"\"\n\t\t\t} else {\n\t\t\t\tencrypted[key] = encryptValue(key, valueType, value, sk)\n\t\t\t}\n\t\t}\n\n\t\tif (model.type === Type.Aggregated && !encrypted._id) {\n\t\t\tencrypted._id = base64ToBase64Url(uint8ArrayToBase64(random.generateRandomData(4)))\n\t\t}\n\n\t\treturn promiseMap(Object.keys(model.associations), (associationName) => {\n\t\t\tif (model.associations[associationName].type === AssociationType.Aggregation) {\n\t\t\t\tconst dependency = model.associations[associationName].dependency\n\t\t\t\treturn resolveTypeReference(new TypeRef(dependency || model.app, model.associations[associationName].refType)).then((aggregateTypeModel) => {\n\t\t\t\t\tlet aggregation = model.associations[associationName]\n\n\t\t\t\t\tif (aggregation.cardinality === Cardinality.ZeroOrOne && i[associationName] == null) {\n\t\t\t\t\t\tencrypted[associationName] = null\n\t\t\t\t\t} else if (i[associationName] == null) {\n\t\t\t\t\t\tthrow new ProgrammingError(`Undefined attribute ${model.name}:${associationName}`)\n\t\t\t\t\t} else if (aggregation.cardinality === Cardinality.Any) {\n\t\t\t\t\t\treturn promiseMap(i[associationName], (aggregate) => {\n\t\t\t\t\t\t\treturn this.encryptAndMapToLiteral(aggregateTypeModel, aggregate, sk)\n\t\t\t\t\t\t}).then((encryptedAggregates) => {\n\t\t\t\t\t\t\tencrypted[associationName] = encryptedAggregates\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn this.encryptAndMapToLiteral(aggregateTypeModel, i[associationName], sk).then((encryptedAggregate) => {\n\t\t\t\t\t\t\tencrypted[associationName] = encryptedAggregate\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tencrypted[associationName] = i[associationName]\n\t\t\t}\n\t\t}).then(() => {\n\t\t\treturn encrypted\n\t\t})\n\t}\n}\n\n// Exported for testing\nexport function encryptValue(valueName: string, valueType: ModelValue, value: any, sk: Aes128Key | null): string | Base64 | null {\n\tif (valueName === \"_id\" || valueName === \"_permissions\") {\n\t\treturn value\n\t} else if (value == null) {\n\t\tif (valueType.cardinality === Cardinality.ZeroOrOne) {\n\t\t\treturn null\n\t\t} else {\n\t\t\tthrow new ProgrammingError(`Value ${valueName} with cardinality ONE can not be null`)\n\t\t}\n\t} else if (valueType.encrypted) {\n\t\tlet bytes = value\n\n\t\tif (valueType.type !== ValueType.Bytes) {\n\t\t\tconst dbType = assertNotNull(convertJsToDbType(valueType.type, value))\n\t\t\tbytes = typeof dbType === \"string\" ? stringToUtf8Uint8Array(dbType) : dbType\n\t\t}\n\n\t\treturn uint8ArrayToBase64(aes128Encrypt(assertNotNull(sk), bytes, random.generateRandomData(IV_BYTE_LENGTH), true, ENABLE_MAC))\n\t} else {\n\t\tconst dbType = convertJsToDbType(valueType.type, value)\n\n\t\tif (typeof dbType === \"string\") {\n\t\t\treturn dbType\n\t\t} else {\n\t\t\treturn uint8ArrayToBase64(dbType)\n\t\t}\n\t}\n}\n\n// Exported for testing\nexport function decryptValue(valueName: string, valueType: ModelValue, value: (Base64 | null) | string, sk: Aes128Key | null): any {\n\tif (value == null) {\n\t\tif (valueType.cardinality === Cardinality.ZeroOrOne) {\n\t\t\treturn null\n\t\t} else {\n\t\t\tthrow new ProgrammingError(`Value ${valueName} with cardinality ONE can not be null`)\n\t\t}\n\t} else if (valueType.cardinality === Cardinality.One && value === \"\") {\n\t\treturn valueToDefault(valueType.type) // Migration for values added after the Type has been defined initially\n\t} else if (valueType.encrypted) {\n\t\tlet decryptedBytes = aes128Decrypt(sk as any, base64ToUint8Array(value as any))\n\n\t\tif (valueType.type === ValueType.Bytes) {\n\t\t\treturn decryptedBytes\n\t\t} else if (valueType.type === ValueType.CompressedString) {\n\t\t\treturn decompressString(decryptedBytes)\n\t\t} else {\n\t\t\treturn convertDbToJsType(valueType.type, utf8Uint8ArrayToString(decryptedBytes))\n\t\t}\n\t} else {\n\t\treturn convertDbToJsType(valueType.type, value)\n\t}\n}\n\n/**\n * Returns bytes when the type === Bytes or type === CompressedString, otherwise returns a string\n * @param type\n * @param value\n * @returns {string|string|NodeJS.Global.Uint8Array|*}\n */\nfunction convertJsToDbType(type: Values<typeof ValueType>, value: any): Uint8Array | string {\n\tif (type === ValueType.Bytes && value != null) {\n\t\treturn value\n\t} else if (type === ValueType.Boolean) {\n\t\treturn value ? \"1\" : \"0\"\n\t} else if (type === ValueType.Date) {\n\t\treturn value.getTime().toString()\n\t} else if (type === ValueType.CompressedString) {\n\t\treturn compressString(value)\n\t} else {\n\t\treturn value\n\t}\n}\n\nfunction convertDbToJsType(type: Values<typeof ValueType>, value: Base64 | string): any {\n\tif (type === ValueType.Bytes) {\n\t\treturn base64ToUint8Array(value as any)\n\t} else if (type === ValueType.Boolean) {\n\t\treturn value !== \"0\"\n\t} else if (type === ValueType.Date) {\n\t\treturn new Date(parseInt(value))\n\t} else if (type === ValueType.CompressedString) {\n\t\treturn decompressString(base64ToUint8Array(value))\n\t} else {\n\t\treturn value\n\t}\n}\n\nfunction compressString(uncompressed: string): Uint8Array {\n\treturn compress(stringToUtf8Uint8Array(uncompressed))\n}\n\nfunction decompressString(compressed: Uint8Array): string {\n\tif (compressed.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst output = uncompress(compressed)\n\treturn utf8Uint8ArrayToString(output)\n}\n\nfunction valueToDefault(type: Values<typeof ValueType>): Date | Uint8Array | string | boolean {\n\tswitch (type) {\n\t\tcase ValueType.String:\n\t\t\treturn \"\"\n\n\t\tcase ValueType.Number:\n\t\t\treturn \"0\"\n\n\t\tcase ValueType.Bytes:\n\t\t\treturn new Uint8Array(0)\n\n\t\tcase ValueType.Date:\n\t\t\treturn new Date()\n\n\t\tcase ValueType.Boolean:\n\t\t\treturn false\n\n\t\tcase ValueType.CompressedString:\n\t\t\treturn \"\"\n\n\t\tdefault:\n\t\t\tthrow new ProgrammingError(`${type} is not a valid value type`)\n\t}\n}\n","import { QueuedBatch } from \"../EventQueue.js\"\nimport { EntityUpdate } from \"../../entities/sys/TypeRefs.js\"\nimport { ListElementEntity, SomeEntity } from \"../../common/EntityTypes\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { TypeRef } from \"@tutao/tutanota-utils\"\nimport { EntityRestCache } from \"./DefaultEntityRestCache.js\"\n\nexport class AdminClientDummyEntityRestCache implements EntityRestCache {\n\tasync entityEventsReceived(batch: QueuedBatch): Promise<Array<EntityUpdate>> {\n\t\treturn batch.events\n\t}\n\n\tasync erase<T extends SomeEntity>(instance: T): Promise<void> {\n\t\tthrow new ProgrammingError(\"erase not implemented\")\n\t}\n\n\tasync load<T extends SomeEntity>(typeRef: TypeRef<T>, id: PropertyType<T, \"_id\">, queryParameters?: Dict, extraHeaders?: Dict): Promise<T> {\n\t\tthrow new ProgrammingError(\"load not implemented\")\n\t}\n\n\tasync loadMultiple<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementIds: Array<Id>): Promise<Array<T>> {\n\t\tthrow new ProgrammingError(\"loadMultiple not implemented\")\n\t}\n\n\tasync loadRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\tthrow new ProgrammingError(\"loadRange not implemented\")\n\t}\n\n\tasync purgeStorage(): Promise<void> {\n\t\treturn\n\t}\n\n\tasync setup<T extends SomeEntity>(listId: Id | null, instance: T, extraHeaders?: Dict): Promise<Id> {\n\t\tthrow new ProgrammingError(\"setup not implemented\")\n\t}\n\n\tasync setupMultiple<T extends SomeEntity>(listId: Id | null, instances: Array<T>): Promise<Array<Id>> {\n\t\tthrow new ProgrammingError(\"setupMultiple not implemented\")\n\t}\n\n\tasync update<T extends SomeEntity>(instance: T): Promise<void> {\n\t\tthrow new ProgrammingError(\"update not implemented\")\n\t}\n\n\tasync getLastEntityEventBatchForGroup(groupId: Id): Promise<Id | null> {\n\t\treturn null\n\t}\n\n\tasync setLastEntityEventBatchForGroup(groupId: Id, batchId: Id): Promise<void> {\n\t\treturn\n\t}\n\n\tasync recordSyncTime(): Promise<void> {\n\t\treturn\n\t}\n\n\tasync timeSinceLastSyncMs(): Promise<number | null> {\n\t\treturn null\n\t}\n\n\tasync isOutOfSync(): Promise<boolean> {\n\t\treturn false\n\t}\n}\n","import { Thunk } from \"@tutao/tutanota-utils\"\nimport { Scheduler } from \"../../common/utils/Scheduler.js\"\nimport { DateProvider } from \"../../common/DateProvider.js\"\n\n// exported for testing\n/** How often do we check for sleep. */\nexport const CHECK_INTERVAL = 5000\n/** How much time should have passed for us to assume that the app was suspended. */\nexport const SLEEP_INTERVAL = 15000\n\ninterface ScheduledState {\n\tscheduledId: number\n\tlastTime: number\n\treadonly onSleep: Thunk\n}\n\n/**\n * Class for detecting suspension state of the app/device.\n * When the device is entering the sleep mode the browser would pause the page. For most of the app it looks like no time has passed at all but when there\n * are external factors e.g. websocket connection we might need to know whether it happens.\n *\n * We detect such situation by scheduling periodic timer and measuring the time in between.\n *\n * Currently is only capable of having one sleep action at a time.\n */\nexport class SleepDetector {\n\tprivate scheduledState: ScheduledState | null = null\n\n\tconstructor(private readonly scheduler: Scheduler, private readonly dateProvider: DateProvider) {}\n\n\tstart(onSleep: Thunk): void {\n\t\tthis.stop()\n\t\tthis.scheduledState = {\n\t\t\tscheduledId: this.scheduler.schedulePeriodic(() => this.check(), CHECK_INTERVAL),\n\t\t\tlastTime: this.dateProvider.now(),\n\t\t\tonSleep,\n\t\t}\n\t}\n\n\tprivate check() {\n\t\tif (this.scheduledState == null) return\n\n\t\tconst now = this.dateProvider.now()\n\t\tif (now - this.scheduledState.lastTime > SLEEP_INTERVAL) {\n\t\t\tthis.scheduledState.onSleep()\n\t\t}\n\t\tthis.scheduledState.lastTime = now\n\t}\n\n\tstop(): void {\n\t\tif (this.scheduledState) {\n\t\t\tthis.scheduler.unschedulePeriodic(this.scheduledState.scheduledId)\n\t\t\tthis.scheduledState = null\n\t\t}\n\t}\n}\n","import { ListElementEntity } from \"../../common/EntityTypes.js\"\nimport { CalendarEvent, CalendarEventTypeRef } from \"../../entities/tutanota/TypeRefs.js\"\nimport { freezeMap, getTypeId, TypeRef } from \"@tutao/tutanota-utils\"\nimport { CUSTOM_MAX_ID, CUSTOM_MIN_ID, firstBiggerThanSecond, getElementId, LOAD_MULTIPLE_LIMIT } from \"../../common/utils/EntityUtils.js\"\nimport { resolveTypeReference } from \"../../common/EntityFunctions.js\"\nimport { CacheStorage, ExposedCacheStorage, Range } from \"./DefaultEntityRestCache.js\"\nimport { EntityRestClient } from \"./EntityRestClient.js\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError.js\"\n\n/**\n * update when implementing custom cache handlers.\n * add new types to the union when implementing new\n * custom cache handlers.\n */\ntype CustomCacheHandledType = never | CalendarEvent\n\n/**\n * makes sure that any {ref<A>, handler<A>} pair passed to\n * the constructor uses the same A for both props and that they\n * are types for which we actually do custom handling.\n */\ntype CustomCacheHandlerMapping = CustomCacheHandledType extends infer A\n\t? A extends ListElementEntity\n\t\t? { ref: TypeRef<A>; handler: CustomCacheHandler<A> }\n\t\t: never\n\t: never\n\n/**\n * wrapper for a TypeRef -> CustomCacheHandler map that's needed because we can't\n * use TypeRefs directly as map keys due to object identity not matching.\n *\n * it is mostly read-only\n */\nexport class CustomCacheHandlerMap {\n\tprivate readonly handlers: Map<string, CustomCacheHandler<ListElementEntity>> = new Map()\n\n\tconstructor(...args: Array<CustomCacheHandlerMapping>) {\n\t\tfor (const { ref, handler } of args) {\n\t\t\tconst key = getTypeId(ref)\n\t\t\tthis.handlers.set(key, handler)\n\t\t}\n\t\tthis.handlers = freezeMap(this.handlers)\n\t}\n\n\tget<T extends ListElementEntity>(typeRef: TypeRef<T>): CustomCacheHandler<T> | undefined {\n\t\tconst typeId = getTypeId(typeRef)\n\t\t// map is frozen after the constructor. constructor arg types are set up to uphold this invariant.\n\t\treturn this.handlers.get(typeId) as CustomCacheHandler<T> | undefined\n\t}\n\n\thas<T extends ListElementEntity>(typeRef: TypeRef<T>): boolean {\n\t\tconst typeId = getTypeId(typeRef)\n\t\treturn this.handlers.has(typeId)\n\t}\n}\n\n/**\n * Some types are not cached like other types, for example because their custom Ids are not sortable.\n * make sure to update CustomHandledType when implementing this for a new type.\n */\nexport interface CustomCacheHandler<T extends ListElementEntity> {\n\tloadRange(storage: ExposedCacheStorage, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]>\n\n\tgetElementIdsInCacheRange(storage: ExposedCacheStorage, listId: Id, ids: Array<Id>): Promise<Array<Id>>\n}\n\n/**\n * implements range loading in JS because the custom Ids of calendar events prevent us from doing\n * this effectively in the database.\n */\nexport class CustomCalendarEventCacheHandler implements CustomCacheHandler<CalendarEvent> {\n\tconstructor(private readonly entityRestClient: EntityRestClient) {}\n\n\tasync loadRange(storage: CacheStorage, listId: Id, start: Id, count: number, reverse: boolean): Promise<CalendarEvent[]> {\n\t\tconst range = await storage.getRangeForList(CalendarEventTypeRef, listId)\n\n\t\t//if offline db for this list is empty load from server\n\t\tlet rawList: Array<CalendarEvent> = []\n\t\tif (range == null) {\n\t\t\tlet chunk: Array<CalendarEvent> = []\n\t\t\tlet currentMin = CUSTOM_MIN_ID\n\t\t\twhile (true) {\n\t\t\t\tchunk = await this.entityRestClient.loadRange(CalendarEventTypeRef, listId, currentMin, LOAD_MULTIPLE_LIMIT, false)\n\t\t\t\trawList.push(...chunk)\n\t\t\t\tif (chunk.length < LOAD_MULTIPLE_LIMIT) break\n\t\t\t\tcurrentMin = getElementId(chunk[chunk.length - 1])\n\t\t\t}\n\t\t\tfor (const event of rawList) {\n\t\t\t\tawait storage.put(event)\n\t\t\t}\n\n\t\t\t// we have all events now\n\t\t\tawait storage.setNewRangeForList(CalendarEventTypeRef, listId, CUSTOM_MIN_ID, CUSTOM_MAX_ID)\n\t\t} else {\n\t\t\tthis.assertCorrectRange(range)\n\t\t\trawList = await storage.getWholeList(CalendarEventTypeRef, listId)\n\t\t\tconsole.log(`CalendarEvent list ${listId} has ${rawList.length} events`)\n\t\t}\n\t\tconst typeModel = await resolveTypeReference(CalendarEventTypeRef)\n\t\tconst sortedList = reverse\n\t\t\t? rawList\n\t\t\t\t\t.filter((calendarEvent) => firstBiggerThanSecond(start, getElementId(calendarEvent), typeModel))\n\t\t\t\t\t.sort((a, b) => (firstBiggerThanSecond(getElementId(b), getElementId(a), typeModel) ? 1 : -1))\n\t\t\t: rawList\n\t\t\t\t\t.filter((calendarEvent) => firstBiggerThanSecond(getElementId(calendarEvent), start, typeModel))\n\t\t\t\t\t.sort((a, b) => (firstBiggerThanSecond(getElementId(a), getElementId(b), typeModel) ? 1 : -1))\n\t\treturn sortedList.slice(0, count)\n\t}\n\n\tprivate assertCorrectRange(range: Range) {\n\t\tif (range.lower !== CUSTOM_MIN_ID || range.upper !== CUSTOM_MAX_ID) {\n\t\t\tthrow new ProgrammingError(`Invalid range for CalendarEvent: ${JSON.stringify(range)}`)\n\t\t}\n\t}\n\n\tasync getElementIdsInCacheRange(storage: CacheStorage, listId: Id, ids: Array<Id>): Promise<Array<Id>> {\n\t\tconst range = await storage.getRangeForList(CalendarEventTypeRef, listId)\n\t\tif (range) {\n\t\t\tthis.assertCorrectRange(range)\n\t\t\t// assume none of the given Ids are already cached to make sure they are loaded now\n\t\t\treturn ids\n\t\t} else {\n\t\t\treturn []\n\t\t}\n\t}\n}\n","import { BlobElementEntity, ElementEntity, ListElementEntity, SomeEntity, TypeModel } from \"../../common/EntityTypes.js\"\nimport { EntityRestClient, typeRefToPath } from \"./EntityRestClient.js\"\nimport { firstBiggerThanSecond, getElementId, getListId } from \"../../common/utils/EntityUtils.js\"\nimport { CacheStorage, LastUpdateTime } from \"./DefaultEntityRestCache.js\"\nimport { assertNotNull, clone, getFromMap, remove, TypeRef } from \"@tutao/tutanota-utils\"\nimport { CustomCacheHandlerMap } from \"./CustomCacheHandler.js\"\nimport { resolveTypeReference } from \"../../common/EntityFunctions.js\"\nimport { Type as TypeId } from \"../../common/EntityConstants.js\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError.js\"\n\n/** Cache for a single list. */\ntype ListCache = {\n\t/** All entities loaded inside the range. */\n\tallRange: Id[]\n\tlowerRangeId: Id\n\tupperRangeId: Id\n\t/** All the entities loaded, inside or outside the range (e.g. load for a single entity). */\n\telements: Map<Id, ListElementEntity>\n}\n\n/** Map from list id to list cache. */\ntype ListTypeCache = Map<Id, ListCache>\n\ntype BlobElementCache = {\n\t/** All the entities loaded, inside or outside the range (e.g. load for a single entity). */\n\telements: Map<Id, BlobElementEntity>\n}\n\n/** Map from list id to list cache. */\ntype BlobElementTypeCache = Map<Id, BlobElementCache>\n\nexport interface EphemeralStorageInitArgs {\n\tuserId: Id\n}\n\nexport class EphemeralCacheStorage implements CacheStorage {\n\t/** Path to id to entity map. */\n\tprivate readonly entities: Map<string, Map<Id, ElementEntity>> = new Map()\n\tprivate readonly lists: Map<string, ListTypeCache> = new Map()\n\tprivate readonly blobEntities: Map<string, BlobElementTypeCache> = new Map()\n\tprivate readonly customCacheHandlerMap: CustomCacheHandlerMap = new CustomCacheHandlerMap()\n\tprivate lastUpdateTime: number | null = null\n\tprivate userId: Id | null = null\n\tprivate lastBatchIdPerGroup = new Map<Id, Id>()\n\n\tinit({ userId }: EphemeralStorageInitArgs) {\n\t\tthis.userId = userId\n\t}\n\n\tdeinit() {\n\t\tthis.userId = null\n\t\tthis.entities.clear()\n\t\tthis.lists.clear()\n\t\tthis.blobEntities.clear()\n\t\tthis.lastUpdateTime = null\n\t\tthis.lastBatchIdPerGroup.clear()\n\t}\n\n\t/**\n\t * Get a given entity from the cache, expects that you have already checked for existence\n\t */\n\tasync get<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<T | null> {\n\t\t// We downcast because we can't prove that map has correct entity on the type level\n\t\tconst path = typeRefToPath(typeRef)\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\treturn clone((this.entities.get(path)?.get(id) as T | undefined) ?? null)\n\t\t\tcase TypeId.ListElement:\n\t\t\t\treturn clone((this.lists.get(path)?.get(assertNotNull(listId))?.elements.get(id) as T | undefined) ?? null)\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\treturn clone((this.blobEntities.get(path)?.get(assertNotNull(listId))?.elements.get(id) as T | undefined) ?? null)\n\t\t\tdefault:\n\t\t\t\tthrow new ProgrammingError(\"must be a persistent type\")\n\t\t}\n\t}\n\n\tasync deleteIfExists<T>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<void> {\n\t\tconst path = typeRefToPath(typeRef)\n\t\tlet typeModel: TypeModel\n\t\ttry {\n\t\t\ttypeModel = await resolveTypeReference(typeRef)\n\t\t} catch (e) {\n\t\t\t// prevent failed lookup for BlobToFileMapping - this catch block can be removed after May 2023\n\t\t\tconsole.log(\"couldn't resolve typeRef \", typeRef)\n\t\t\treturn\n\t\t}\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tthis.entities.get(path)?.delete(id)\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tconst cache = this.lists.get(path)?.get(assertNotNull(listId))\n\t\t\t\tif (cache != null) {\n\t\t\t\t\tcache.elements.delete(id)\n\t\t\t\t\tremove(cache.allRange, id)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tthis.blobEntities.get(path)?.get(assertNotNull(listId))?.elements.delete(id)\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new ProgrammingError(\"must be a persistent type\")\n\t\t}\n\t}\n\n\tprivate addElementEntity<T extends ElementEntity>(typeRef: TypeRef<T>, id: Id, entity: T) {\n\t\tgetFromMap(this.entities, typeRefToPath(typeRef), () => new Map()).set(id, entity)\n\t}\n\n\tasync isElementIdInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<boolean> {\n\t\tconst cache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\t\treturn cache != null && !firstBiggerThanSecond(id, cache.upperRangeId) && !firstBiggerThanSecond(cache.lowerRangeId, id)\n\t}\n\n\tasync put(originalEntity: SomeEntity): Promise<void> {\n\t\tconst entity = clone(originalEntity)\n\t\tconst typeRef = entity._type\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tconst elementEntity = entity as ElementEntity\n\t\t\t\tthis.addElementEntity(elementEntity._type, elementEntity._id, elementEntity)\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tconst listElementEntity = entity as ListElementEntity\n\t\t\t\tconst listElementTypeRef = typeRef as TypeRef<ListElementEntity>\n\t\t\t\tawait this.putListElement(listElementEntity, listElementTypeRef)\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tconst blobElementEntity = entity as BlobElementEntity\n\t\t\t\tconst blobTypeRef = typeRef as TypeRef<BlobElementEntity>\n\t\t\t\tawait this.putBlobElement(blobElementEntity, blobTypeRef)\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new ProgrammingError(\"must be a persistent type\")\n\t\t}\n\t}\n\n\tprivate async putBlobElement(entity: BlobElementEntity, typeRef: TypeRef<BlobElementEntity>) {\n\t\tconst listId = getListId(entity)\n\t\tconst elementId = getElementId(entity)\n\t\tconst cache = this.blobEntities.get(typeRefToPath(typeRef))?.get(listId)\n\t\tif (cache == null) {\n\t\t\t// first element in this list\n\t\t\tconst newCache = {\n\t\t\t\telements: new Map([[elementId, entity]]),\n\t\t\t}\n\t\t\tgetFromMap(this.blobEntities, typeRefToPath(typeRef), () => new Map()).set(listId, newCache)\n\t\t} else {\n\t\t\t// if the element already exists in the cache, overwrite it\n\t\t\tcache.elements.set(elementId, entity)\n\t\t}\n\t}\n\n\tprivate async putListElement(entity: ListElementEntity, typeRef: TypeRef<ListElementEntity>) {\n\t\tconst listId = getListId(entity)\n\t\tconst elementId = getElementId(entity)\n\t\tconst cache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\t\tif (cache == null) {\n\t\t\t// first element in this list\n\t\t\tconst newCache = {\n\t\t\t\tallRange: [elementId],\n\t\t\t\tlowerRangeId: elementId,\n\t\t\t\tupperRangeId: elementId,\n\t\t\t\telements: new Map([[elementId, entity]]),\n\t\t\t}\n\t\t\tgetFromMap(this.lists, typeRefToPath(typeRef), () => new Map()).set(listId, newCache)\n\t\t} else {\n\t\t\t// if the element already exists in the cache, overwrite it\n\t\t\t// add new element to existing list if necessary\n\t\t\tcache.elements.set(elementId, entity)\n\t\t\tif (await this.isElementIdInCacheRange(typeRef, listId, elementId)) {\n\t\t\t\tthis.insertIntoRange(cache.allRange, elementId)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate insertIntoRange(allRange: Array<Id>, elementId: Id) {\n\t\tfor (let i = 0; i < allRange.length; i++) {\n\t\t\tconst rangeElement = allRange[i]\n\t\t\tif (firstBiggerThanSecond(rangeElement, elementId)) {\n\t\t\t\tallRange.splice(i, 0, elementId)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (rangeElement === elementId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tallRange.push(elementId)\n\t}\n\n\tasync provideFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\n\t\tif (listCache == null) {\n\t\t\treturn []\n\t\t}\n\n\t\tlet range = listCache.allRange\n\t\tlet ids: Id[] = []\n\t\tif (reverse) {\n\t\t\tlet i\n\t\t\tfor (i = range.length - 1; i >= 0; i--) {\n\t\t\t\tif (firstBiggerThanSecond(start, range[i])) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i >= 0) {\n\t\t\t\tlet startIndex = i + 1 - count\n\t\t\t\tif (startIndex < 0) {\n\t\t\t\t\t// start index may be negative if more elements have been requested than available when getting elements reverse.\n\t\t\t\t\tstartIndex = 0\n\t\t\t\t}\n\t\t\t\tids = range.slice(startIndex, i + 1)\n\t\t\t\tids.reverse()\n\t\t\t} else {\n\t\t\t\tids = []\n\t\t\t}\n\t\t} else {\n\t\t\tconst i = range.findIndex((id) => firstBiggerThanSecond(id, start))\n\t\t\tids = range.slice(i, i + count)\n\t\t}\n\t\tlet result: T[] = []\n\t\tfor (let a = 0; a < ids.length; a++) {\n\t\t\tresult.push(clone(listCache.elements.get(ids[a]) as T))\n\t\t}\n\t\treturn result\n\t}\n\n\tasync getRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<{ lower: Id; upper: Id } | null> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\n\t\tif (listCache == null) {\n\t\t\treturn null\n\t\t}\n\n\t\treturn { lower: listCache.lowerRangeId, upper: listCache.upperRangeId }\n\t}\n\n\tasync setUpperRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\t\tif (listCache == null) {\n\t\t\tthrow new Error(\"list does not exist\")\n\t\t}\n\t\tlistCache.upperRangeId = id\n\t}\n\n\tasync setLowerRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\t\tif (listCache == null) {\n\t\t\tthrow new Error(\"list does not exist\")\n\t\t}\n\t\tlistCache.lowerRangeId = id\n\t}\n\n\t/**\n\t * Creates a new list cache if there is none. Resets everything but elements.\n\t * @param typeRef\n\t * @param listId\n\t * @param lower\n\t * @param upper\n\t */\n\tasync setNewRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, lower: Id, upper: Id): Promise<void> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\t\tif (listCache == null) {\n\t\t\tgetFromMap(this.lists, typeRefToPath(typeRef), () => new Map()).set(listId, {\n\t\t\t\tallRange: [],\n\t\t\t\tlowerRangeId: lower,\n\t\t\t\tupperRangeId: upper,\n\t\t\t\telements: new Map(),\n\t\t\t})\n\t\t} else {\n\t\t\tlistCache.lowerRangeId = lower\n\t\t\tlistCache.upperRangeId = upper\n\t\t\tlistCache.allRange = []\n\t\t}\n\t}\n\n\tasync getIdsInRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<Id>> {\n\t\treturn this.lists.get(typeRefToPath(typeRef))?.get(listId)?.allRange ?? []\n\t}\n\n\tasync getLastBatchIdForGroup(groupId: Id): Promise<Id | null> {\n\t\treturn this.lastBatchIdPerGroup.get(groupId) ?? null\n\t}\n\n\tasync putLastBatchIdForGroup(groupId: Id, batchId: Id): Promise<void> {\n\t\tthis.lastBatchIdPerGroup.set(groupId, batchId)\n\t}\n\n\tpurgeStorage(): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n\n\tasync getLastUpdateTime(): Promise<LastUpdateTime> {\n\t\treturn this.lastUpdateTime ? { type: \"recorded\", time: this.lastUpdateTime } : { type: \"never\" }\n\t}\n\n\tasync putLastUpdateTime(value: number): Promise<void> {\n\t\tthis.lastUpdateTime = value\n\t}\n\n\tasync getWholeList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<T>> {\n\t\tconst listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)\n\n\t\tif (listCache == null) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn listCache.allRange.map((id) => clone(listCache.elements.get(id) as T))\n\t}\n\n\tgetCustomCacheHandlerMap(entityRestClient: EntityRestClient): CustomCacheHandlerMap {\n\t\treturn this.customCacheHandlerMap\n\t}\n\n\tgetUserId(): Id {\n\t\treturn assertNotNull(this.userId, \"No user id, not initialized?\")\n\t}\n\n\tasync deleteAllOwnedBy(owner: Id): Promise<void> {\n\t\tfor (const typeMap of this.entities.values()) {\n\t\t\tfor (const [id, entity] of typeMap.entries()) {\n\t\t\t\tif (entity._ownerGroup === owner) {\n\t\t\t\t\ttypeMap.delete(id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const cacheForType of this.lists.values()) {\n\t\t\tthis.deleteAllOwnedByFromCache(cacheForType, owner)\n\t\t}\n\t\tfor (const cacheForType of this.blobEntities.values()) {\n\t\t\tthis.deleteAllOwnedByFromCache(cacheForType, owner)\n\t\t}\n\t\tthis.lastBatchIdPerGroup.delete(owner)\n\t}\n\n\tprivate deleteAllOwnedByFromCache(cacheForType: Map<Id, ListCache | BlobElementCache>, owner: string) {\n\t\t// If we find at least one element in the list that is owned by our target owner, we delete the entire list.\n\t\t// This is OK in most cases because the vast majority of lists are single owner.\n\t\t// For the other cases, we are just clearing the cache a bit sooner than needed.\n\t\tconst listIdsToDelete: string[] = []\n\t\tfor (const [listId, listCache] of cacheForType.entries()) {\n\t\t\tfor (const [id, element] of listCache.elements.entries()) {\n\t\t\t\tif (element._ownerGroup === owner) {\n\t\t\t\t\tlistIdsToDelete.push(listId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const listId of listIdsToDelete) {\n\t\t\tcacheForType.delete(listId)\n\t\t}\n\t}\n\n\tclearExcludedData(): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n\n\t/**\n\t * We want to lock the access to the \"ranges\" db when updating / reading the\n\t * offline available mail list ranges for each mail list (referenced using the listId)\n\t * @param listId the mail list that we want to lock\n\t */\n\tlockRangesDbAccess(listId: string): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n\n\t/**\n\t * This is the counterpart to the function \"lockRangesDbAccess(listId)\"\n\t * @param listId the mail list that we want to unlock\n\t */\n\tunlockRangesDbAccess(listId: string): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n}\n","import { CacheStorage, LastUpdateTime, Range } from \"./DefaultEntityRestCache.js\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { ListElementEntity, SomeEntity } from \"../../common/EntityTypes\"\nimport { TypeRef } from \"@tutao/tutanota-utils\"\nimport { OfflineStorage, OfflineStorageInitArgs } from \"../offline/OfflineStorage.js\"\nimport { WorkerImpl } from \"../WorkerImpl\"\nimport { EphemeralCacheStorage, EphemeralStorageInitArgs } from \"./EphemeralCacheStorage\"\nimport { EntityRestClient } from \"./EntityRestClient.js\"\nimport { CustomCacheHandlerMap } from \"./CustomCacheHandler.js\"\n\nexport interface EphemeralStorageArgs extends EphemeralStorageInitArgs {\n\ttype: \"ephemeral\"\n}\n\nexport type OfflineStorageArgs = OfflineStorageInitArgs & {\n\ttype: \"offline\"\n}\n\ninterface CacheStorageInitReturn {\n\t/** If the created storage is an OfflineStorage */\n\tisPersistent: boolean\n\t/** If a OfflineStorage was created, whether or not the backing database was created fresh or already existed */\n\tisNewOfflineDb: boolean\n}\n\nexport interface CacheStorageLateInitializer {\n\tinitialize(args: OfflineStorageArgs | EphemeralStorageArgs): Promise<CacheStorageInitReturn>\n\n\tdeInitialize(): Promise<void>\n}\n\ntype SomeStorage = OfflineStorage | EphemeralCacheStorage\n\n/**\n * This is necessary so that we can release offline storage mode without having to rewrite the credentials handling system. Since it's possible that\n * a desktop user might not use a persistent session, and we won't know until they try to log in, we can only decide what kind of cache storage to use at login\n * This implementation allows us to avoid modifying too much of the worker public API. Once we make this obsolete, all we will have to do is\n * remove the initialize parameter from the LoginFacade, and tidy up the WorkerLocator init\n *\n * Create a proxy to a cache storage object.\n * It will be uninitialized, and unusable until {@method CacheStorageLateInitializer.initializeCacheStorage} has been called on the returned object\n * Once it is initialized, then it is safe to use\n * @param factory A factory function to get a CacheStorage implementation when initialize is called\n * @return {CacheStorageLateInitializer} The uninitialized proxy and a function to initialize it\n */\nexport class LateInitializedCacheStorageImpl implements CacheStorageLateInitializer, CacheStorage {\n\tprivate _inner: SomeStorage | null = null\n\n\tconstructor(private readonly worker: WorkerImpl, private readonly offlineStorageProvider: () => Promise<null | OfflineStorage>) {}\n\n\tprivate get inner(): CacheStorage {\n\t\tif (this._inner == null) {\n\t\t\tthrow new ProgrammingError(\"Cache storage is not initialized\")\n\t\t}\n\n\t\treturn this._inner\n\t}\n\n\tasync initialize(args: OfflineStorageArgs | EphemeralStorageArgs): Promise<CacheStorageInitReturn> {\n\t\t// We might call this multiple times.\n\t\t// This happens when persistent credentials login fails and we need to start with new cache for new login.\n\t\tconst { storage, isPersistent, isNewOfflineDb } = await this.getStorage(args)\n\t\tthis._inner = storage\n\t\treturn {\n\t\t\tisPersistent,\n\t\t\tisNewOfflineDb,\n\t\t}\n\t}\n\n\tasync deInitialize(): Promise<void> {\n\t\tthis._inner?.deinit()\n\t}\n\n\tprivate async getStorage(\n\t\targs: OfflineStorageArgs | EphemeralStorageArgs,\n\t): Promise<{ storage: SomeStorage; isPersistent: boolean; isNewOfflineDb: boolean }> {\n\t\tif (args.type === \"offline\") {\n\t\t\ttry {\n\t\t\t\tconst storage = await this.offlineStorageProvider()\n\t\t\t\tif (storage != null) {\n\t\t\t\t\tconst isNewOfflineDb = await storage.init(args)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstorage,\n\t\t\t\t\t\tisPersistent: true,\n\t\t\t\t\t\tisNewOfflineDb,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t// Precaution in case something bad happens to offline database. We want users to still be able to log in.\n\t\t\t\tconsole.error(\"Error while initializing offline cache storage\", e)\n\t\t\t\tthis.worker.sendError(e)\n\t\t\t}\n\t\t}\n\t\t// both \"else\" case and fallback for unavailable storage and error cases\n\t\tconst storage = new EphemeralCacheStorage()\n\t\tawait storage.init(args)\n\t\treturn {\n\t\t\tstorage,\n\t\t\tisPersistent: false,\n\t\t\tisNewOfflineDb: false,\n\t\t}\n\t}\n\n\tdeleteIfExists<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<void> {\n\t\treturn this.inner.deleteIfExists(typeRef, listId, id)\n\t}\n\n\tget<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<T | null> {\n\t\treturn this.inner.get(typeRef, listId, id)\n\t}\n\n\tgetIdsInRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<Id>> {\n\t\treturn this.inner.getIdsInRange(typeRef, listId)\n\t}\n\n\tgetLastBatchIdForGroup(groupId: Id): Promise<Id | null> {\n\t\treturn this.inner.getLastBatchIdForGroup(groupId)\n\t}\n\n\tasync getLastUpdateTime(): Promise<LastUpdateTime> {\n\t\treturn this._inner ? this.inner.getLastUpdateTime() : { type: \"uninitialized\" }\n\t}\n\n\tgetRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Range | null> {\n\t\treturn this.inner.getRangeForList(typeRef, listId)\n\t}\n\n\tisElementIdInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<boolean> {\n\t\treturn this.inner.isElementIdInCacheRange(typeRef, listId, id)\n\t}\n\n\tprovideFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\treturn this.inner.provideFromRange(typeRef, listId, start, count, reverse)\n\t}\n\n\tgetWholeList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<T>> {\n\t\treturn this.inner.getWholeList(typeRef, listId)\n\t}\n\n\tpurgeStorage(): Promise<void> {\n\t\treturn this.inner.purgeStorage()\n\t}\n\n\tput(originalEntity: SomeEntity): Promise<void> {\n\t\treturn this.inner.put(originalEntity)\n\t}\n\n\tputLastBatchIdForGroup(groupId: Id, batchId: Id): Promise<void> {\n\t\treturn this.inner.putLastBatchIdForGroup(groupId, batchId)\n\t}\n\n\tputLastUpdateTime(value: number): Promise<void> {\n\t\treturn this.inner.putLastUpdateTime(value)\n\t}\n\n\tsetLowerRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\treturn this.inner.setLowerRangeForList(typeRef, listId, id)\n\t}\n\n\tsetNewRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, lower: Id, upper: Id): Promise<void> {\n\t\treturn this.inner.setNewRangeForList(typeRef, listId, lower, upper)\n\t}\n\n\tsetUpperRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\treturn this.inner.setUpperRangeForList(typeRef, listId, id)\n\t}\n\n\tgetCustomCacheHandlerMap(entityRestClient: EntityRestClient): CustomCacheHandlerMap {\n\t\treturn this.inner.getCustomCacheHandlerMap(entityRestClient)\n\t}\n\n\tgetUserId(): Id {\n\t\treturn this.inner.getUserId()\n\t}\n\n\tasync deleteAllOwnedBy(owner: Id): Promise<void> {\n\t\treturn this.inner.deleteAllOwnedBy(owner)\n\t}\n\n\tclearExcludedData(): Promise<void> {\n\t\treturn this.inner.clearExcludedData()\n\t}\n\n\t/**\n\t * We want to lock the access to the \"ranges\" db when updating / reading the\n\t * offline available mail list ranges for each mail list (referenced using the listId)\n\t * @param listId the mail list that we want to lock\n\t */\n\tlockRangesDbAccess(listId: Id): Promise<void> {\n\t\treturn this.inner.lockRangesDbAccess(listId)\n\t}\n\n\t/**\n\t * This is the counterpart to the function \"lockRangesDbAccess(listId)\"\n\t * @param listId the mail list that we want to unlock\n\t */\n\tunlockRangesDbAccess(listId: Id): Promise<void> {\n\t\treturn this.inner.unlockRangesDbAccess(listId)\n\t}\n}\n","import { HttpMethod, MediaType, resolveTypeReference } from \"../../common/EntityFunctions\"\nimport {\n\tDeleteService,\n\tExtraServiceParams,\n\tGetService,\n\tIServiceExecutor,\n\tMethodDefinition,\n\tParamTypeFromRef,\n\tPostService,\n\tPutService,\n\tReturnTypeFromRef,\n} from \"../../common/ServiceRequest.js\"\nimport { Entity } from \"../../common/EntityTypes\"\nimport { isSameTypeRef, lazy, TypeRef } from \"@tutao/tutanota-utils\"\nimport { RestClient } from \"./RestClient\"\nimport { InstanceMapper } from \"../crypto/InstanceMapper\"\nimport { CryptoFacade } from \"../crypto/CryptoFacade\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { AuthDataProvider } from \"../facades/UserFacade\"\nimport { LoginIncompleteError } from \"../../common/error/LoginIncompleteError.js\"\n\nassertWorkerOrNode()\n\ntype AnyService = GetService | PostService | PutService | DeleteService\n\nexport class ServiceExecutor implements IServiceExecutor {\n\tconstructor(\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly authDataProvider: AuthDataProvider,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly cryptoFacade: lazy<CryptoFacade>,\n\t) {}\n\n\tget<S extends GetService>(\n\t\tservice: S,\n\t\tdata: ParamTypeFromRef<S[\"get\"][\"data\"]>,\n\t\tparams?: ExtraServiceParams,\n\t): Promise<ReturnTypeFromRef<S[\"get\"][\"return\"]>> {\n\t\treturn this.executeServiceRequest(service, HttpMethod.GET, data, params)\n\t}\n\n\tpost<S extends PostService>(\n\t\tservice: S,\n\t\tdata: ParamTypeFromRef<S[\"post\"][\"data\"]>,\n\t\tparams?: ExtraServiceParams,\n\t): Promise<ReturnTypeFromRef<S[\"post\"][\"return\"]>> {\n\t\treturn this.executeServiceRequest(service, HttpMethod.POST, data, params)\n\t}\n\n\tput<S extends PutService>(\n\t\tservice: S,\n\t\tdata: ParamTypeFromRef<S[\"put\"][\"data\"]>,\n\t\tparams?: ExtraServiceParams,\n\t): Promise<ReturnTypeFromRef<S[\"put\"][\"return\"]>> {\n\t\treturn this.executeServiceRequest(service, HttpMethod.PUT, data, params)\n\t}\n\n\tdelete<S extends DeleteService>(\n\t\tservice: S,\n\t\tdata: ParamTypeFromRef<S[\"delete\"][\"data\"]>,\n\t\tparams?: ExtraServiceParams,\n\t): Promise<ReturnTypeFromRef<S[\"delete\"][\"return\"]>> {\n\t\treturn this.executeServiceRequest(service, HttpMethod.DELETE, data, params)\n\t}\n\n\tprivate async executeServiceRequest(\n\t\tservice: AnyService,\n\t\tmethod: HttpMethod,\n\t\trequestEntity: Entity | null,\n\t\tparams: ExtraServiceParams | undefined,\n\t): Promise<any> {\n\t\tconst methodDefinition = this.getMethodDefinition(service, method)\n\t\tif (\n\t\t\tmethodDefinition.return &&\n\t\t\tparams?.sessionKey == null &&\n\t\t\t(await resolveTypeReference(methodDefinition.return)).encrypted &&\n\t\t\t!this.authDataProvider.isFullyLoggedIn()\n\t\t) {\n\t\t\t// Short-circuit before we do an actual request which we can't decrypt\n\t\t\t// If we have a session key passed it doesn't mean that it is for the return type but it is likely\n\t\t\t// so we allow the request.\n\t\t\tthrow new LoginIncompleteError(`Tried to make service request with encrypted return type but is not fully logged in yet, service: ${service.name}`)\n\t\t}\n\n\t\tconst modelVersion = await this.getModelVersion(methodDefinition)\n\n\t\tconst path = `/rest/${service.app.toLowerCase()}/${service.name.toLowerCase()}`\n\t\tconst headers = { ...this.authDataProvider.createAuthHeaders(), ...params?.extraHeaders, v: modelVersion }\n\n\t\tconst encryptedEntity = await this.encryptDataIfNeeded(methodDefinition, requestEntity, service, method, params ?? null)\n\n\t\tconst data: string | undefined = await this.restClient.request(path, method, {\n\t\t\tqueryParams: params?.queryParams,\n\t\t\theaders,\n\t\t\tresponseType: MediaType.Json,\n\t\t\tbody: encryptedEntity ?? undefined,\n\t\t\tsuspensionBehavior: params?.suspensionBehavior,\n\t\t})\n\n\t\tif (methodDefinition.return) {\n\t\t\treturn await this.decryptResponse(methodDefinition.return, data as string, params)\n\t\t}\n\t}\n\n\tprivate getMethodDefinition(service: AnyService, method: HttpMethod): MethodDefinition {\n\t\tswitch (method) {\n\t\t\tcase HttpMethod.GET:\n\t\t\t\treturn (service as GetService)[\"get\"]\n\t\t\tcase HttpMethod.POST:\n\t\t\t\treturn (service as PostService)[\"post\"]\n\t\t\tcase HttpMethod.PUT:\n\t\t\t\treturn (service as PutService)[\"put\"]\n\t\t\tcase HttpMethod.DELETE:\n\t\t\t\treturn (service as DeleteService)[\"delete\"]\n\t\t}\n\t}\n\n\tprivate async getModelVersion(methodDefinition: MethodDefinition): Promise<string> {\n\t\t// This is some kind of a hack because we don't generate data for the whole model anywhere (unfortunately).\n\t\tconst someTypeRef = methodDefinition.data ?? methodDefinition.return\n\t\tif (someTypeRef == null) {\n\t\t\tthrow new ProgrammingError(\"Need either data or return for the service method!\")\n\t\t}\n\t\tconst model = await resolveTypeReference(someTypeRef)\n\t\treturn model.version\n\t}\n\n\tprivate async encryptDataIfNeeded(\n\t\tmethodDefinition: MethodDefinition,\n\t\trequestEntity: Entity | null,\n\t\tservice: AnyService,\n\t\tmethod: HttpMethod,\n\t\tparams: ExtraServiceParams | null,\n\t): Promise<string | null> {\n\t\tif (methodDefinition.data != null) {\n\t\t\tif (requestEntity == null || !isSameTypeRef(methodDefinition.data, requestEntity._type)) {\n\t\t\t\tthrow new ProgrammingError(`Invalid service data! ${service.name} ${method}`)\n\t\t\t}\n\n\t\t\tconst requestTypeModel = await resolveTypeReference(methodDefinition.data)\n\t\t\tif (requestTypeModel.encrypted && params?.sessionKey == null) {\n\t\t\t\tthrow new ProgrammingError(\"Must provide a session key for an encrypted data transfer type!: \" + service)\n\t\t\t}\n\n\t\t\tconst encryptedEntity = await this.instanceMapper.encryptAndMapToLiteral(requestTypeModel, requestEntity, params?.sessionKey ?? null)\n\t\t\treturn JSON.stringify(encryptedEntity)\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate async decryptResponse<T extends Entity>(typeRef: TypeRef<T>, data: string, params: ExtraServiceParams | undefined): Promise<T> {\n\t\tconst responseTypeModel = await resolveTypeReference(typeRef)\n\t\t// Filter out __proto__ to avoid prototype pollution.\n\t\tconst instance = JSON.parse(data, (k, v) => (k === \"__proto__\" ? undefined : v))\n\t\tconst resolvedSessionKey = await this.cryptoFacade().resolveServiceSessionKey(responseTypeModel, instance)\n\t\treturn this.instanceMapper.decryptAndMapToInstance(responseTypeModel, instance, resolvedSessionKey ?? params?.sessionKey ?? null)\n\t}\n}\n","import { GroupType } from \"../../common/TutanotaConstants\"\nimport { decryptKey } from \"@tutao/tutanota-crypto\"\nimport { assertNotNull, getFromMap } from \"@tutao/tutanota-utils\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError\"\nimport { createWebsocketLeaderStatus, GroupMembership, User, WebsocketLeaderStatus } from \"../../entities/sys/TypeRefs\"\nimport { Aes128Key } from \"@tutao/tutanota-crypto/dist/encryption/Aes\"\nimport { LoginIncompleteError } from \"../../common/error/LoginIncompleteError\"\n\nexport interface AuthDataProvider {\n\t/**\n\t * @return The map which contains authentication data for the logged in user.\n\t */\n\tcreateAuthHeaders(): Dict\n\n\tisFullyLoggedIn(): boolean\n}\n\n/** Holder for the user and session-related data on the worker side. */\nexport class UserFacade implements AuthDataProvider {\n\tprivate user: User | null = null\n\tprivate accessToken: string | null = null\n\t/** A cache for decrypted keys of each group. Encrypted keys are stored on membership.symEncGKey. */\n\tprivate groupKeys: Map<Id, Aes128Key> = new Map()\n\tprivate leaderStatus!: WebsocketLeaderStatus\n\n\tconstructor() {\n\t\tthis.reset()\n\t}\n\n\t// Login process is somehow multi-step and we don't use a separate network stack for it. So we have to break up setters.\n\t// 1. We need to download user. For that we need to set access token already (to authenticate the request for the server as its passed in headers).\n\t// 2. We need to get group keys. For that we need to unlock userGroupKey with userPasspharseKey\n\t// so this leads to this steps in UserFacade:\n\t// 1. Access token is set\n\t// 2. User is set\n\t// 3. UserGroupKey is unlocked\n\tsetAccessToken(accessToken: string | null) {\n\t\tthis.accessToken = accessToken\n\t}\n\n\tsetUser(user: User) {\n\t\tif (this.accessToken == null) {\n\t\t\tthrow new ProgrammingError(\"invalid state: no access token\")\n\t\t}\n\t\tthis.user = user\n\t}\n\n\tunlockUserGroupKey(userPassphraseKey: Aes128Key) {\n\t\tif (this.user == null) {\n\t\t\tthrow new ProgrammingError(\"Invalid state: no user\")\n\t\t}\n\t\tthis.groupKeys.set(this.getUserGroupId(), decryptKey(userPassphraseKey, this.user.userGroup.symEncGKey))\n\t}\n\n\tupdateUser(user: User) {\n\t\tif (this.user == null) {\n\t\t\tthrow new ProgrammingError(\"Update user is called without logging in. This function is not for you.\")\n\t\t}\n\t\tthis.user = user\n\t}\n\n\tgetUser(): User | null {\n\t\treturn this.user\n\t}\n\n\t/**\n\t * @return The map which contains authentication data for the logged in user.\n\t */\n\tcreateAuthHeaders(): Dict {\n\t\treturn this.accessToken\n\t\t\t? {\n\t\t\t\t\taccessToken: this.accessToken,\n\t\t\t  }\n\t\t\t: {}\n\t}\n\n\tgetUserGroupId(): Id {\n\t\treturn this.getLoggedInUser().userGroup.group\n\t}\n\n\tgetAllGroupIds(): Id[] {\n\t\tlet groups = this.getLoggedInUser().memberships.map((membership) => membership.group)\n\t\tgroups.push(this.getLoggedInUser().userGroup.group)\n\t\treturn groups\n\t}\n\n\tgetUserGroupKey(): Aes128Key {\n\t\t// the userGroupKey is always written after the login to this.groupKeys\n\t\t//if the user has only logged in offline this has not happened\n\t\tconst userGroupKey = this.groupKeys.get(this.getUserGroupId())\n\t\tif (userGroupKey == null) {\n\t\t\tif (this.isPartiallyLoggedIn()) {\n\t\t\t\tthrow new LoginIncompleteError(\"userGroupKey not available\")\n\t\t\t} else {\n\t\t\t\tthrow new ProgrammingError(\"Invalid state: userGroupKey is not available\")\n\t\t\t}\n\t\t}\n\t\treturn userGroupKey\n\t}\n\n\tgetGroupKey(groupId: Id): Aes128Key {\n\t\treturn getFromMap(this.groupKeys, groupId, () => {\n\t\t\treturn decryptKey(this.getUserGroupKey(), this.getMembership(groupId).symEncGKey)\n\t\t})\n\t}\n\n\tgetMembership(groupId: Id): GroupMembership {\n\t\tlet membership = this.getLoggedInUser().memberships.find((g: GroupMembership) => g.group === groupId)\n\n\t\tif (!membership) {\n\t\t\tthrow new Error(`No group with groupId ${groupId} found!`)\n\t\t}\n\n\t\treturn membership\n\t}\n\n\thasGroup(groupId: Id): boolean {\n\t\tif (!this.user) {\n\t\t\treturn false\n\t\t} else {\n\t\t\treturn groupId === this.user.userGroup.group || this.user.memberships.some((m) => m.group === groupId)\n\t\t}\n\t}\n\n\tgetGroupId(groupType: GroupType): Id {\n\t\tif (groupType === GroupType.User) {\n\t\t\treturn this.getUserGroupId()\n\t\t} else {\n\t\t\tlet membership = this.getLoggedInUser().memberships.find((m) => m.groupType === groupType)\n\n\t\t\tif (!membership) {\n\t\t\t\tthrow new Error(\"could not find groupType \" + groupType + \" for user \" + this.getLoggedInUser()._id)\n\t\t\t}\n\n\t\t\treturn membership.group\n\t\t}\n\t}\n\n\tgetGroupIds(groupType: GroupType): Id[] {\n\t\treturn this.getLoggedInUser()\n\t\t\t.memberships.filter((m) => m.groupType === groupType)\n\t\t\t.map((gm) => gm.group)\n\t}\n\n\tisPartiallyLoggedIn(): boolean {\n\t\treturn this.user != null\n\t}\n\n\tisFullyLoggedIn(): boolean {\n\t\t// We have userGroupKey and we can decrypt any other key - we are good to go\n\t\treturn this.groupKeys.size > 0\n\t}\n\n\tgetLoggedInUser(): User {\n\t\treturn assertNotNull(this.user)\n\t}\n\n\tsetLeaderStatus(status: WebsocketLeaderStatus) {\n\t\tthis.leaderStatus = status\n\t\tconsole.log(\"New leader status set:\", status.leaderStatus)\n\t}\n\n\tisLeader(): boolean {\n\t\treturn this.leaderStatus.leaderStatus\n\t}\n\n\treset() {\n\t\tthis.user = null\n\t\tthis.accessToken = null\n\t\tthis.groupKeys = new Map()\n\t\tthis.leaderStatus = createWebsocketLeaderStatus({\n\t\t\tleaderStatus: false,\n\t\t})\n\t}\n}\n","import { mapObject } from \"@tutao/tutanota-utils\"\n\nexport type SqlValue = null | string | number | Uint8Array\n\n/**\n * Type tag for values being passed to SQL statements\n */\nexport const enum SqlType {\n\tNull = \"SqlNull\",\n\tNumber = \"SqlNum\",\n\tString = \"SqlStr\",\n\tBytes = \"SqlBytes\",\n}\n\nexport type TaggedSqlValue =\n\t| { type: SqlType.Null; value: null }\n\t| { type: SqlType.String; value: string }\n\t| { type: SqlType.Number; value: number }\n\t| { type: SqlType.Bytes; value: Uint8Array }\n\nexport function tagSqlObject(params: Record<string, SqlValue>): Record<string, TaggedSqlValue> {\n\treturn mapObject((p: SqlValue) => tagSqlValue(p), params)\n}\n\nexport function tagSqlValue(param: SqlValue): TaggedSqlValue {\n\tif (typeof param === \"string\") {\n\t\treturn { type: SqlType.String, value: param }\n\t} else if (typeof param === \"number\") {\n\t\treturn { type: SqlType.Number, value: param }\n\t} else if (param == null) {\n\t\treturn { type: SqlType.Null, value: null }\n\t} else {\n\t\treturn { type: SqlType.Bytes, value: param }\n\t}\n}\n\nexport function untagSqlValue(tagged: TaggedSqlValue): SqlValue {\n\treturn tagged.value\n}\n\nexport function untagSqlObject(tagged: Record<string, TaggedSqlValue>): Record<string, SqlValue> {\n\treturn mapObject((p: TaggedSqlValue) => p.value, tagged)\n}\n","import { ElementEntity, ListElementEntity, SomeEntity, TypeModel } from \"../../common/EntityTypes.js\"\nimport {\n\telementIdPart,\n\tfirstBiggerThanSecond,\n\tGENERATED_MAX_ID,\n\tGENERATED_MIN_ID,\n\tgetElementId,\n\tlistIdPart,\n\ttimestampToGeneratedId,\n} from \"../../common/utils/EntityUtils.js\"\nimport { CacheStorage, expandId, ExposedCacheStorage, LastUpdateTime } from \"../rest/DefaultEntityRestCache.js\"\nimport * as cborg from \"cborg\"\nimport { EncodeOptions, Token, Type } from \"cborg\"\nimport { assert, assertNotNull, DAY_IN_MILLIS, getTypeId, groupByAndMap, groupByAndMapUniquely, mapNullable, TypeRef } from \"@tutao/tutanota-utils\"\nimport { isDesktop, isOfflineStorageAvailable, isTest } from \"../../common/Env.js\"\nimport { modelInfos, resolveTypeReference } from \"../../common/EntityFunctions.js\"\nimport { AccountType, OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS } from \"../../common/TutanotaConstants.js\"\nimport { DateProvider } from \"../../common/DateProvider.js\"\nimport { TokenOrNestedTokens } from \"cborg/types/interface\"\nimport {\n\tCalendarEventTypeRef,\n\tFileTypeRef,\n\tMailBodyTypeRef,\n\tMailDetailsBlobTypeRef,\n\tMailDetailsDraftTypeRef,\n\tMailFolderTypeRef,\n\tMailHeadersTypeRef,\n\tMailTypeRef,\n} from \"../../entities/tutanota/TypeRefs.js\"\nimport { UserTypeRef } from \"../../entities/sys/TypeRefs.js\"\nimport { OfflineStorageMigrator } from \"./OfflineStorageMigrator.js\"\nimport { CustomCacheHandlerMap, CustomCalendarEventCacheHandler } from \"../rest/CustomCacheHandler.js\"\nimport { EntityRestClient } from \"../rest/EntityRestClient.js\"\nimport { InterWindowEventFacadeSendDispatcher } from \"../../../native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js\"\nimport { SqlCipherFacade } from \"../../../native/common/generatedipc/SqlCipherFacade.js\"\nimport { SqlValue, TaggedSqlValue, tagSqlValue, untagSqlObject } from \"./SqlValue.js\"\nimport { FolderSystem } from \"../../common/mail/FolderSystem.js\"\nimport { isDetailsDraft, isLegacyMail } from \"../../common/MailWrapper.js\"\nimport { Type as TypeId } from \"../../common/EntityConstants.js\"\nimport { OutOfSyncError } from \"../../common/error/OutOfSyncError.js\"\nimport { isSpamOrTrashFolder } from \"../../common/mail/CommonMailUtils.js\"\n\nfunction dateEncoder(data: Date, typ: string, options: EncodeOptions): TokenOrNestedTokens | null {\n\tconst time = data.getTime()\n\treturn [\n\t\t// https://datatracker.ietf.org/doc/rfc8943/\n\t\tnew Token(Type.tag, 100),\n\t\tnew Token(time < 0 ? Type.negint : Type.uint, time),\n\t]\n}\n\nfunction dateDecoder(bytes: number): Date {\n\treturn new Date(bytes)\n}\n\nexport const customTypeEncoders: { [typeName: string]: typeof dateEncoder } = Object.freeze({\n\tDate: dateEncoder,\n})\n\ntype TypeDecoder = (_: any) => any\nexport const customTypeDecoders: Array<TypeDecoder> = (() => {\n\tconst tags: Array<TypeDecoder> = []\n\ttags[100] = dateDecoder\n\treturn tags\n})()\n\n/**\n * For each of these keys we track the current version in the database.\n * The keys are different model versions (because we need to migrate the data with certain model version changes) and \"offline\" key which is used to track\n * migrations that are needed for other reasons e.g. if DB structure changes or if we need to invalidate some tables.\n */\nexport type VersionMetadataBaseKey = keyof typeof modelInfos | \"offline\"\n\ntype VersionMetadataEntries = {\n\t// Yes this is cursed, give me a break\n\t[P in VersionMetadataBaseKey as `${P}-version`]: number\n}\n\nexport interface OfflineDbMeta extends VersionMetadataEntries {\n\tlastUpdateTime: number\n\ttimeRangeDays: number\n}\n\nconst TableDefinitions = Object.freeze({\n\t// plus ownerGroup added in a migration\n\tlist_entities:\n\t\t\"type TEXT NOT NULL, listId TEXT NOT NULL, elementId TEXT NOT NULL, ownerGroup TEXT, entity BLOB NOT NULL, PRIMARY KEY (type, listId, elementId)\",\n\t// plus ownerGroup added in a migration\n\telement_entities: \"type TEXT NOT NULL, elementId TEXT NOT NULL, ownerGroup TEXT, entity BLOB NOT NULL, PRIMARY KEY (type, elementId)\",\n\tranges: \"type TEXT NOT NULL, listId TEXT NOT NULL, lower TEXT NOT NULL, upper TEXT NOT NULL, PRIMARY KEY (type, listId)\",\n\tlastUpdateBatchIdPerGroupId: \"groupId TEXT NOT NULL, batchId TEXT NOT NULL, PRIMARY KEY (groupId)\",\n\tmetadata: \"key TEXT NOT NULL, value BLOB, PRIMARY KEY (key)\",\n\tblob_element_entities:\n\t\t\"type TEXT NOT NULL, listId TEXT NOT NULL, elementId TEXT NOT NULL, ownerGroup TEXT, entity BLOB NOT NULL, PRIMARY KEY (type, listId, elementId)\",\n} as const)\n\ntype Range = { lower: string; upper: string }\n\nexport interface OfflineStorageInitArgs {\n\tuserId: Id\n\tdatabaseKey: Uint8Array\n\ttimeRangeDays: number | null\n\tforceNewDatabase: boolean\n}\n\nexport class OfflineStorage implements CacheStorage, ExposedCacheStorage {\n\tprivate customCacheHandler: CustomCacheHandlerMap | null = null\n\tprivate userId: Id | null = null\n\tprivate timeRangeDays: number | null = null\n\n\tconstructor(\n\t\tprivate readonly sqlCipherFacade: SqlCipherFacade,\n\t\tprivate readonly interWindowEventSender: InterWindowEventFacadeSendDispatcher,\n\t\tprivate readonly dateProvider: DateProvider,\n\t\tprivate readonly migrator: OfflineStorageMigrator,\n\t) {\n\t\tassert(isOfflineStorageAvailable() || isTest(), \"Offline storage is not available.\")\n\t}\n\n\t/**\n\t * @return {boolean} whether the database was newly created or not\n\t */\n\tasync init({ userId, databaseKey, timeRangeDays, forceNewDatabase }: OfflineStorageInitArgs): Promise<boolean> {\n\t\tthis.userId = userId\n\t\tthis.timeRangeDays = timeRangeDays\n\t\tif (forceNewDatabase) {\n\t\t\tif (isDesktop()) {\n\t\t\t\tawait this.interWindowEventSender.localUserDataInvalidated(userId)\n\t\t\t}\n\t\t\tawait this.sqlCipherFacade.deleteDb(userId)\n\t\t}\n\t\t// We open database here, and it is closed in the native side when the window is closed or the page is reloaded\n\t\tawait this.sqlCipherFacade.openDb(userId, databaseKey)\n\t\tawait this.createTables()\n\t\ttry {\n\t\t\tawait this.migrator.migrate(this, this.sqlCipherFacade)\n\t\t} catch (e) {\n\t\t\tif (e instanceof OutOfSyncError) {\n\t\t\t\tconsole.warn(\"Offline db is out of sync!\", e)\n\t\t\t\tawait this.recreateDbFile(userId, databaseKey)\n\t\t\t\tawait this.migrator.migrate(this, this.sqlCipherFacade)\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t\t// if nothing is written here, it means it's a new database\n\t\treturn (await this.getLastUpdateTime()) == null\n\t}\n\n\tprivate async recreateDbFile(userId: string, databaseKey: Uint8Array): Promise<void> {\n\t\tconsole.log(`recreating DB file for userId ${userId}`)\n\t\tawait this.sqlCipherFacade.closeDb()\n\t\tawait this.sqlCipherFacade.deleteDb(userId)\n\t\tawait this.sqlCipherFacade.openDb(userId, databaseKey)\n\t\tawait this.createTables()\n\t}\n\n\t/**\n\t * currently, we close DBs from the native side (mainly on things like reload and on android's onDestroy)\n\t */\n\tasync deinit() {\n\t\tthis.userId = null\n\t\tawait this.sqlCipherFacade.closeDb()\n\t}\n\n\tasync deleteIfExists(typeRef: TypeRef<SomeEntity>, listId: Id | null, elementId: Id): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\t\tlet typeModel: TypeModel\n\t\ttry {\n\t\t\ttypeModel = await resolveTypeReference(typeRef)\n\t\t} catch (e) {\n\t\t\t// prevent failed lookup for BlobToFileMapping - this catch block can be removed after May 2023\n\t\t\tconsole.log(\"couldn't resolve typeRef \", typeRef)\n\t\t\treturn\n\t\t}\n\t\tlet preparedQuery\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tpreparedQuery = sql`DELETE FROM element_entities WHERE type = ${type} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM list_entities WHERE type = ${type} AND listId = ${listId} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM blob_element_entities WHERE type = ${type} AND listId = ${listId} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"must be a persistent type\")\n\t\t}\n\t\tawait this.sqlCipherFacade.run(preparedQuery.query, preparedQuery.params)\n\t}\n\n\tasync deleteAllOfType(typeRef: TypeRef<SomeEntity>): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\t\tlet typeModel: TypeModel\n\t\ttry {\n\t\t\ttypeModel = await resolveTypeReference(typeRef)\n\t\t} catch (e) {\n\t\t\t// prevent failed lookup for BlobToFileMapping - this catch block can be removed after May 2023\n\t\t\tconsole.log(\"couldn't resolve typeRef \", typeRef)\n\t\t\treturn\n\t\t}\n\t\tlet preparedQuery\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tpreparedQuery = sql`DELETE FROM element_entities WHERE type = ${type}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM list_entities WHERE type = ${type}`\n\t\t\t\tawait this.sqlCipherFacade.run(preparedQuery.query, preparedQuery.params)\n\t\t\t\tawait this.deleteAllRangesForType(type)\n\t\t\t\treturn\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM blob_element_entities WHERE type = ${type}`\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"must be a persistent type\")\n\t\t}\n\t\tawait this.sqlCipherFacade.run(preparedQuery.query, preparedQuery.params)\n\t}\n\n\tprivate async deleteAllRangesForType(type: string): Promise<void> {\n\t\tconst { query, params } = sql`DELETE FROM ranges WHERE type = ${type}`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync get<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, elementId: Id): Promise<T | null> {\n\t\tconst type = getTypeId(typeRef)\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\tlet preparedQuery\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tpreparedQuery = sql`SELECT entity from element_entities WHERE type = ${type} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tpreparedQuery = sql`SELECT entity from list_entities WHERE type = ${type} AND listId = ${listId} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tpreparedQuery = sql`SELECT entity from blob_element_entities WHERE type = ${type} AND listId = ${listId} AND elementId = ${elementId}`\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"must be a persistent type\")\n\t\t}\n\t\tconst result = await this.sqlCipherFacade.get(preparedQuery.query, preparedQuery.params)\n\t\treturn result?.entity ? this.deserialize(typeRef, result.entity.value as Uint8Array) : null\n\t}\n\n\tasync getIdsInRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<Id>> {\n\t\tconst type = getTypeId(typeRef)\n\t\tconst range = await this.getRange(type, listId)\n\t\tif (range == null) {\n\t\t\tthrow new Error(`no range exists for ${type} and list ${listId}`)\n\t\t}\n\t\tconst { lower, upper } = range\n\t\tconst { query, params } = sql`SELECT elementId FROM list_entities\nWHERE type = ${type}\nAND listId = ${listId}\nAND (elementId = ${lower}\nOR ${firstIdBigger(\"elementId\", lower)})\nAND NOT(${firstIdBigger(\"elementId\", upper)})`\n\t\tconst rows = await this.sqlCipherFacade.all(query, params)\n\t\treturn rows.map((row) => row.elementId.value as string)\n\t}\n\n\tasync getRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Range | null> {\n\t\treturn this.getRange(getTypeId(typeRef), listId)\n\t}\n\n\tasync isElementIdInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<boolean> {\n\t\tconst range = await this.getRangeForList(typeRef, listId)\n\t\treturn range != null && !firstBiggerThanSecond(id, range.upper) && !firstBiggerThanSecond(range.lower, id)\n\t}\n\n\tasync provideFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {\n\t\tconst type = getTypeId(typeRef)\n\t\tlet preparedQuery\n\t\tif (reverse) {\n\t\t\tpreparedQuery = sql`SELECT entity FROM list_entities WHERE type = ${type} AND listId = ${listId} AND ${firstIdBigger(\n\t\t\t\tstart,\n\t\t\t\t\"elementId\",\n\t\t\t)} ORDER BY LENGTH(elementId) DESC, elementId DESC LIMIT ${count}`\n\t\t} else {\n\t\t\tpreparedQuery = sql`SELECT entity FROM list_entities WHERE type = ${type} AND listId = ${listId} AND ${firstIdBigger(\n\t\t\t\t\"elementId\",\n\t\t\t\tstart,\n\t\t\t)} ORDER BY LENGTH(elementId) ASC, elementId ASC LIMIT ${count}`\n\t\t}\n\t\tconst { query, params } = preparedQuery\n\t\tconst serializedList: ReadonlyArray<Record<string, TaggedSqlValue>> = await this.sqlCipherFacade.all(query, params)\n\t\treturn this.deserializeList(\n\t\t\ttypeRef,\n\t\t\tserializedList.map((r) => r.entity.value as Uint8Array),\n\t\t)\n\t}\n\n\tasync put(originalEntity: SomeEntity): Promise<void> {\n\t\tconst serializedEntity = this.serialize(originalEntity)\n\t\tconst { listId, elementId } = expandId(originalEntity._id)\n\t\tconst type = getTypeId(originalEntity._type)\n\t\tconst ownerGroup = originalEntity._ownerGroup\n\t\tconst typeModel = await resolveTypeReference(originalEntity._type)\n\t\tlet preparedQuery: { query: string; params: TaggedSqlValue[] }\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tpreparedQuery = sql`INSERT OR REPLACE INTO element_entities (type, elementId, ownerGroup, entity) VALUES (${type}, ${elementId}, ${ownerGroup}, ${serializedEntity})`\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tpreparedQuery = sql`INSERT OR REPLACE INTO list_entities (type, listId, elementId, ownerGroup, entity) VALUES (${type}, ${listId}, ${elementId}, ${ownerGroup}, ${serializedEntity})`\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tpreparedQuery = sql`INSERT OR REPLACE INTO blob_element_entities (type, listId, elementId, ownerGroup, entity) VALUES (${type}, ${listId}, ${elementId}, ${ownerGroup}, ${serializedEntity})`\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"must be a persistent type\")\n\t\t}\n\t\tawait this.sqlCipherFacade.run(preparedQuery.query, preparedQuery.params)\n\t}\n\n\tasync setLowerRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\t\tconst { query, params } = sql`UPDATE ranges SET lower = ${id} WHERE type = ${type} AND listId = ${listId}`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync setUpperRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\t\tconst { query, params } = sql`UPDATE ranges SET upper = ${id} WHERE type = ${type} AND listId = ${listId}`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync setNewRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, lower: Id, upper: Id): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\t\tconst { query, params } = sql`INSERT OR REPLACE INTO ranges VALUES (${type}, ${listId}, ${lower}, ${upper})`\n\t\treturn this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync getLastBatchIdForGroup(groupId: Id): Promise<Id | null> {\n\t\tconst { query, params } = sql`SELECT batchId from lastUpdateBatchIdPerGroupId WHERE groupId = ${groupId}`\n\t\tconst row = (await this.sqlCipherFacade.get(query, params)) as { batchId: TaggedSqlValue } | null\n\t\treturn (row?.batchId?.value ?? null) as Id | null\n\t}\n\n\tasync putLastBatchIdForGroup(groupId: Id, batchId: Id): Promise<void> {\n\t\tconst { query, params } = sql`INSERT OR REPLACE INTO lastUpdateBatchIdPerGroupId VALUES (${groupId}, ${batchId})`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync getLastUpdateTime(): Promise<LastUpdateTime> {\n\t\tconst time = await this.getMetadata(\"lastUpdateTime\")\n\t\treturn time ? { type: \"recorded\", time } : { type: \"never\" }\n\t}\n\n\tasync putLastUpdateTime(ms: number): Promise<void> {\n\t\tawait this.putMetadata(\"lastUpdateTime\", ms)\n\t}\n\n\tasync purgeStorage(): Promise<void> {\n\t\tfor (let name of Object.keys(TableDefinitions)) {\n\t\t\tawait this.sqlCipherFacade.run(`DELETE FROM ${name}`, [])\n\t\t}\n\t}\n\n\tasync deleteRange(typeRef: TypeRef<unknown>, listId: string): Promise<void> {\n\t\tconst { query, params } = sql`DELETE FROM ranges WHERE type = ${getTypeId(typeRef)} AND listId = ${listId}`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tasync getListElementsOfType<T extends ListElementEntity>(typeRef: TypeRef<T>): Promise<Array<T>> {\n\t\tconst { query, params } = sql`SELECT entity from list_entities WHERE type = ${getTypeId(typeRef)}`\n\t\tconst items = (await this.sqlCipherFacade.all(query, params)) ?? []\n\t\treturn this.deserializeList(\n\t\t\ttypeRef,\n\t\t\titems.map((row) => row.entity.value as Uint8Array),\n\t\t)\n\t}\n\n\tasync getElementsOfType<T extends ElementEntity>(typeRef: TypeRef<T>): Promise<Array<T>> {\n\t\tconst { query, params } = sql`SELECT entity from element_entities WHERE type = ${getTypeId(typeRef)}`\n\t\tconst items = (await this.sqlCipherFacade.all(query, params)) ?? []\n\t\treturn this.deserializeList(\n\t\t\ttypeRef,\n\t\t\titems.map((row) => row.entity.value as Uint8Array),\n\t\t)\n\t}\n\n\tasync getWholeList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<T>> {\n\t\tconst { query, params } = sql`SELECT entity FROM list_entities WHERE type = ${getTypeId(typeRef)} AND listId = ${listId}`\n\t\tconst items = (await this.sqlCipherFacade.all(query, params)) ?? []\n\t\treturn this.deserializeList(\n\t\t\ttypeRef,\n\t\t\titems.map((row) => row.entity.value as Uint8Array),\n\t\t)\n\t}\n\n\tasync dumpMetadata(): Promise<Partial<OfflineDbMeta>> {\n\t\tconst query = \"SELECT * from metadata\"\n\t\tconst stored = (await this.sqlCipherFacade.all(query, [])).map((row) => [row.key.value as string, row.value.value as Uint8Array] as const)\n\t\treturn Object.fromEntries(stored.map(([key, value]) => [key, cborg.decode(value)])) as OfflineDbMeta\n\t}\n\n\tasync setStoredModelVersion(model: VersionMetadataBaseKey, version: number) {\n\t\treturn this.putMetadata(`${model}-version`, version)\n\t}\n\n\tgetCustomCacheHandlerMap(entityRestClient: EntityRestClient): CustomCacheHandlerMap {\n\t\tif (this.customCacheHandler == null) {\n\t\t\tthis.customCacheHandler = new CustomCacheHandlerMap({ ref: CalendarEventTypeRef, handler: new CustomCalendarEventCacheHandler(entityRestClient) })\n\t\t}\n\t\treturn this.customCacheHandler\n\t}\n\n\tgetUserId(): Id {\n\t\treturn assertNotNull(this.userId, \"No user id, not initialized?\")\n\t}\n\n\tasync deleteAllOwnedBy(owner: Id): Promise<void> {\n\t\t{\n\t\t\tconst { query, params } = sql`DELETE FROM element_entities WHERE ownerGroup = ${owner}`\n\t\t\tawait this.sqlCipherFacade.run(query, params)\n\t\t}\n\t\t{\n\t\t\t// first, check which list Ids contain entities owned by the lost group\n\t\t\tconst { query, params } = sql`SELECT listId, type FROM list_entities WHERE ownerGroup = ${owner}`\n\t\t\tconst rangeRows = await this.sqlCipherFacade.all(query, params)\n\t\t\tconst rows = rangeRows.map((row) => untagSqlObject(row) as { listId: string; type: string })\n\t\t\tconst listIdsByType: Map<string, Set<Id>> = groupByAndMapUniquely(\n\t\t\t\trows,\n\t\t\t\t(row) => row.type,\n\t\t\t\t(row) => row.listId,\n\t\t\t)\n\t\t\tfor (const [type, listIds] of listIdsByType.entries()) {\n\t\t\t\t// delete the ranges for those listIds\n\t\t\t\tconst deleteRangeQuery = sql`DELETE FROM ranges WHERE type = ${type} AND listId IN ${paramList(Array.from(listIds))}`\n\t\t\t\tawait this.sqlCipherFacade.run(deleteRangeQuery.query, deleteRangeQuery.params)\n\t\t\t\t// delete all entities that have one of those list Ids.\n\t\t\t\tconst deleteEntitiesQuery = sql`DELETE FROM list_entities WHERE type = ${type} AND listId IN ${paramList(Array.from(listIds))}`\n\t\t\t\tawait this.sqlCipherFacade.run(deleteEntitiesQuery.query, deleteEntitiesQuery.params)\n\t\t\t}\n\t\t}\n\t\t{\n\t\t\tconst { query, params } = sql`DELETE FROM blob_element_entities WHERE ownerGroup = ${owner}`\n\t\t\tawait this.sqlCipherFacade.run(query, params)\n\t\t}\n\t\t{\n\t\t\tconst { query, params } = sql`DELETE FROM lastUpdateBatchIdPerGroupId WHERE groupId = ${owner}`\n\t\t\tawait this.sqlCipherFacade.run(query, params)\n\t\t}\n\t}\n\n\tprivate async putMetadata<K extends keyof OfflineDbMeta>(key: K, value: OfflineDbMeta[K]): Promise<void> {\n\t\tlet encodedValue\n\t\ttry {\n\t\t\tencodedValue = cborg.encode(value)\n\t\t} catch (e) {\n\t\t\tconsole.log(\"[OfflineStorage] failed to encode metadata for key\", key, \"with value\", value)\n\t\t\tthrow e\n\t\t}\n\t\tconst { query, params } = sql`INSERT OR REPLACE INTO metadata VALUES (${key}, ${encodedValue})`\n\t\tawait this.sqlCipherFacade.run(query, params)\n\t}\n\n\tprivate async getMetadata<K extends keyof OfflineDbMeta>(key: K): Promise<OfflineDbMeta[K] | null> {\n\t\tconst { query, params } = sql`SELECT value from metadata WHERE key = ${key}`\n\t\tconst encoded = await this.sqlCipherFacade.get(query, params)\n\t\treturn encoded && cborg.decode(encoded.value.value as Uint8Array)\n\t}\n\n\t/**\n\t * Clear out unneeded data from the offline database (i.e. trash and spam lists, old data).\n\t * This will be called after login (CachePostLoginActions.ts) to ensure fast login time.\n\t * @param timeRangeDays: the maximum age of days that mails should be to be kept in the database. if null, will use a default value\n\t * @param userId id of the current user. default, last stored userId\n\t */\n\tasync clearExcludedData(timeRangeDays: number | null = this.timeRangeDays, userId: Id = this.getUserId()): Promise<void> {\n\t\tconst user = await this.get(UserTypeRef, null, userId)\n\n\t\t// Free users always have default time range regardless of what is stored\n\t\tconst isFreeUser = user?.accountType === AccountType.FREE\n\t\tconst timeRange = isFreeUser || timeRangeDays == null ? OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS : timeRangeDays\n\t\tconst cutoffTimestamp = this.dateProvider.now() - timeRange * DAY_IN_MILLIS\n\t\tconst cutoffId = timestampToGeneratedId(cutoffTimestamp)\n\t\tconst folders = await this.getListElementsOfType(MailFolderTypeRef)\n\t\tconst folderSystem = new FolderSystem(folders)\n\n\t\tfor (const folder of folders) {\n\t\t\tif (isSpamOrTrashFolder(folderSystem, folder)) {\n\t\t\t\tawait this.deleteMailList(folder.mails, GENERATED_MAX_ID)\n\t\t\t} else {\n\t\t\t\tawait this.deleteMailList(folder.mails, cutoffId)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async createTables() {\n\t\tfor (let [name, definition] of Object.entries(TableDefinitions)) {\n\t\t\tawait this.sqlCipherFacade.run(`CREATE TABLE IF NOT EXISTS ${name} (${definition})`, [])\n\t\t}\n\t}\n\n\tprivate async getRange(type: string, listId: Id): Promise<Range | null> {\n\t\tconst { query, params } = sql`SELECT upper, lower FROM ranges WHERE type = ${type} AND listId = ${listId}`\n\t\tconst row = (await this.sqlCipherFacade.get(query, params)) ?? null\n\t\treturn mapNullable(row, untagSqlObject) as Range | null\n\t}\n\n\t/**\n\t * This method deletes mails from {@param listId} what are older than {@param cutoffId}. as well as associated data\n\t *\n\t * For each mail we delete its body, headers, and all referenced attachments.\n\t *\n\t * When we delete the Files, we also delete the whole range for the user's File list. We need to delete the whole\n\t * range because we only have one file list per mailbox, so if we delete something from the middle of it, the range\n\t * will no longer be valid. (this is future proofing, because as of now there is not going to be a Range set for the\n\t * File list anyway, since we currently do not do range requests for Files.\n\t *\n\t * \tWe do not delete ConversationEntries because:\n\t * \t1. They are in the same list for the whole conversation so we can't adjust the range\n\t * \t2. We might need them in the future for showing the whole thread\n\t */\n\tprivate async deleteMailList(listId: Id, cutoffId: Id): Promise<void> {\n\t\t// We lock access to the \"ranges\" db here in order to prevent race conditions when accessing the \"ranges\" database.\n\t\tawait this.lockRangesDbAccess(listId)\n\t\ttry {\n\t\t\t// This must be done before deleting mails to know what the new range has to be\n\t\t\tawait this.updateRangeForList(MailTypeRef, listId, cutoffId)\n\t\t} finally {\n\t\t\t// We unlock access to the \"ranges\" db here. We lock it in order to prevent race conditions when accessing the \"ranges\" database.\n\t\t\tawait this.unlockRangesDbAccess(listId)\n\t\t}\n\n\t\tconst mailsToDelete: IdTuple[] = []\n\t\tconst headersToDelete: Id[] = []\n\t\tconst attachmentsTodelete: IdTuple[] = []\n\t\tconst mailbodiesToDelete: Id[] = []\n\t\tconst mailDetailsBlobToDelete: IdTuple[] = []\n\t\tconst mailDetailsDraftToDelete: IdTuple[] = []\n\n\t\tconst mails = await this.getWholeList(MailTypeRef, listId)\n\t\tfor (let mail of mails) {\n\t\t\tif (firstBiggerThanSecond(cutoffId, getElementId(mail))) {\n\t\t\t\tmailsToDelete.push(mail._id)\n\t\t\t\tfor (const id of mail.attachments) {\n\t\t\t\t\tattachmentsTodelete.push(id)\n\t\t\t\t}\n\t\t\t\tif (isLegacyMail(mail)) {\n\t\t\t\t\tmailbodiesToDelete.push(assertNotNull(mail.body))\n\t\t\t\t} else if (isDetailsDraft(mail)) {\n\t\t\t\t\tconst mailDetailsId = assertNotNull(mail.mailDetailsDraft)\n\t\t\t\t\tmailDetailsDraftToDelete.push(mailDetailsId)\n\t\t\t\t} else {\n\t\t\t\t\t// mailDetailsBlob\n\t\t\t\t\tconst mailDetailsId = assertNotNull(mail.mailDetails)\n\t\t\t\t\tmailDetailsBlobToDelete.push(mailDetailsId)\n\t\t\t\t}\n\t\t\t\tif (mail.headers) {\n\t\t\t\t\theadersToDelete.push(mail.headers)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait this.deleteIn(MailBodyTypeRef, null, mailbodiesToDelete)\n\t\tfor (let [listId, elementIds] of groupByAndMap(mailDetailsBlobToDelete, listIdPart, elementIdPart).entries()) {\n\t\t\tawait this.deleteIn(MailDetailsBlobTypeRef, listId, elementIds)\n\t\t}\n\t\tfor (let [listId, elementIds] of groupByAndMap(mailDetailsDraftToDelete, listIdPart, elementIdPart).entries()) {\n\t\t\tawait this.deleteIn(MailDetailsDraftTypeRef, listId, elementIds)\n\t\t}\n\t\tawait this.deleteIn(MailHeadersTypeRef, null, headersToDelete)\n\t\tfor (let [listId, elementIds] of groupByAndMap(attachmentsTodelete, listIdPart, elementIdPart).entries()) {\n\t\t\tawait this.deleteIn(FileTypeRef, listId, elementIds)\n\t\t\tawait this.deleteRange(FileTypeRef, listId)\n\t\t}\n\n\t\tawait this.deleteIn(MailTypeRef, listId, mailsToDelete.map(elementIdPart))\n\t}\n\n\tprivate async deleteIn(typeRef: TypeRef<unknown>, listId: Id | null, elementIds: Id[]): Promise<void> {\n\t\tlet preparedQuery: { query: string; params: TaggedSqlValue[] }\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\tswitch (typeModel.type) {\n\t\t\tcase TypeId.Element:\n\t\t\t\tpreparedQuery = sql`DELETE FROM element_entities WHERE type =${getTypeId(typeRef)} AND elementId IN ${paramList(elementIds)}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.ListElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM list_entities WHERE type = ${getTypeId(typeRef)} AND listId = ${listId} AND elementId IN ${paramList(\n\t\t\t\t\telementIds,\n\t\t\t\t)}`\n\t\t\t\tbreak\n\t\t\tcase TypeId.BlobElement:\n\t\t\t\tpreparedQuery = sql`DELETE FROM blob_element_entities WHERE type = ${getTypeId(typeRef)} AND listId = ${listId} AND elementId IN ${paramList(\n\t\t\t\t\telementIds,\n\t\t\t\t)}`\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"must be a persistent type\")\n\t\t}\n\t\treturn this.sqlCipherFacade.run(preparedQuery.query, preparedQuery.params)\n\t}\n\n\t/**\n\t * We want to lock the access to the \"ranges\" db when updating / reading the\n\t * offline available mail list ranges for each mail list (referenced using the listId).\n\t * @param listId the mail list that we want to lock\n\t */\n\tasync lockRangesDbAccess(listId: Id) {\n\t\tawait this.sqlCipherFacade.lockRangesDbAccess(listId)\n\t}\n\n\t/**\n\t * This is the counterpart to the function \"lockRangesDbAccess(listId)\".\n\t * @param listId the mail list that we want to unlock\n\t */\n\tasync unlockRangesDbAccess(listId: Id) {\n\t\tawait this.sqlCipherFacade.unlockRangesDbAccess(listId)\n\t}\n\n\tprivate async updateRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, cutoffId: Id): Promise<void> {\n\t\tconst type = getTypeId(typeRef)\n\n\t\tconst range = await this.getRange(type, listId)\n\t\tif (range == null) {\n\t\t\treturn\n\t\t}\n\n\t\t// If the range for a given list is complete from the beginning (starts at GENERATED_MIN_ID), then we only want to actually modify the\n\t\t// saved range if we would be removing elements from the list, in order to not lose the information that the range is complete in storage.\n\t\t// So we have to check how old the oldest element in said range is. If it is newer than cutoffId, then we will not modify the range,\n\t\t// otherwise we will just modify it normally\n\t\tif (range.lower === GENERATED_MIN_ID) {\n\t\t\tconst entities = await this.provideFromRange(typeRef, listId, GENERATED_MIN_ID, 1, false)\n\t\t\tconst id = mapNullable(entities[0], getElementId)\n\t\t\tconst rangeWontBeModified = id == null || firstBiggerThanSecond(id, cutoffId) || id === cutoffId\n\t\t\tif (rangeWontBeModified) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif (firstBiggerThanSecond(cutoffId, range.lower)) {\n\t\t\t// If the upper id of the range is below the cutoff, then the entire range will be deleted from the storage\n\t\t\t// so we just delete the range as well\n\t\t\t// Otherwise, we only want to modify\n\t\t\tif (firstBiggerThanSecond(cutoffId, range.upper)) {\n\t\t\t\tawait this.deleteRange(typeRef, listId)\n\t\t\t} else {\n\t\t\t\tawait this.setLowerRangeForList(typeRef, listId, cutoffId)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate serialize(originalEntity: SomeEntity): Uint8Array {\n\t\ttry {\n\t\t\treturn cborg.encode(originalEntity, { typeEncoders: customTypeEncoders })\n\t\t} catch (e) {\n\t\t\tconsole.log(\"[OfflineStorage] failed to encode entity of type\", originalEntity._type, \"with id\", originalEntity._id)\n\t\t\tthrow e\n\t\t}\n\t}\n\n\tprivate deserialize<T extends SomeEntity>(typeRef: TypeRef<T>, loaded: Uint8Array): T {\n\t\tconst deserialized = cborg.decode(loaded, { tags: customTypeDecoders })\n\t\t// TypeRef cannot be deserialized back automatically. We could write a codec for it but we don't actually\n\t\t// need to store it so we just \"patch\" it.\n\t\t// Some places rely on TypeRef being a class and not a plain object.\n\t\tdeserialized._type = typeRef\n\t\treturn deserialized\n\t}\n\n\tprivate deserializeList<T extends SomeEntity>(typeRef: TypeRef<T>, loaded: Array<Uint8Array>): Array<T> {\n\t\treturn loaded.map((entity) => this.deserialize(typeRef, entity))\n\t}\n}\n\n/*\n * used to automatically create the right amount of query params for selecting ids from a dynamic list.\n * must be used within sql`<query>` template string to inline the logic into the query\n */\nfunction paramList(params: SqlValue[]): SqlFragment {\n\tconst qs = params.map(() => \"?\").join(\",\")\n\treturn new SqlFragment(`(${qs})`, params)\n}\n\n/**\n * comparison to select ids that are bigger or smaller than a parameter id\n * must be used within sql`<query>` template string to inline the logic into the query\n */\nfunction firstIdBigger(...args: [string, \"elementId\"] | [\"elementId\", string]): SqlFragment {\n\tlet [l, r]: [string, string] = args\n\tlet v\n\tif (l === \"elementId\") {\n\t\tv = r\n\t\tr = \"?\"\n\t} else {\n\t\tv = l\n\t\tl = \"?\"\n\t}\n\treturn new SqlFragment(`(CASE WHEN length(${l}) > length(${r}) THEN 1 WHEN length(${l}) < length(${r}) THEN 0 ELSE ${l} > ${r} END)`, [v, v, v])\n}\n\n/**\n * this tagged template function exists because android doesn't allow us to define SQL functions, so we have made a way to inline\n * SQL fragments into queries.\n * to make it less error-prone, we automate the generation of the params array for the actual sql call.\n * In this way, we offload the escaping of actual user content to the SQL engine, which makes this safe from an SQLI point of view.\n *\n * usage example:\n * const type = \"sys/User\"\n * const listId = \"someList\"\n * const startId = \"ABC\"\n * sql`SELECT entity FROM list_entities WHERE type = ${type} AND listId = ${listId} AND ${firstIdBigger(startId, \"elementId\")}`\n *\n * this will result in\n * const {query, params} = {\n *     query: `SELECT entity FROM list_entities WHERE type = ? AND listId = ? AND (CASE WHEN length(?) > length(elementId) THEN 1 WHEN length(?) < length(elementId) THEN 0 ELSE ? > elementId END)`,\n *     params: [\n *     \t\t{type: SqlType.String, value: \"sys/User\"},\n *     \t\t{type: SqlType.String, value: \"someList\"},\n *     \t\t{type: SqlType.String, value: \"ABC\"},\n *     \t\t{type: SqlType.String, value: \"ABC\"},\n *     \t\t{type: SqlType.String, value: \"ABC\"}\n *     ]\n * }\n *\n * which can be consumed by sql.run(query, params)\n */\nexport function sql(queryParts: TemplateStringsArray, ...paramInstances: (SqlValue | SqlFragment)[]): { query: string; params: TaggedSqlValue[] } {\n\tlet query = \"\"\n\tlet params: TaggedSqlValue[] = []\n\tlet i = 0\n\tfor (i = 0; i < paramInstances.length; i++) {\n\t\tquery += queryParts[i]\n\t\tconst param = paramInstances[i]\n\t\tif (param instanceof SqlFragment) {\n\t\t\tquery += param.text\n\t\t\tparams.push(...param.params.map(tagSqlValue))\n\t\t} else {\n\t\t\tquery += \"?\"\n\t\t\tparams.push(tagSqlValue(param))\n\t\t}\n\t}\n\tquery += queryParts[i]\n\treturn { query, params }\n}\n\nclass SqlFragment {\n\tconstructor(readonly text: string, readonly params: SqlValue[]) {}\n}\n","import { OfflineStorage } from \"./OfflineStorage.js\"\nimport { modelInfos } from \"../../common/EntityFunctions.js\"\nimport { typedKeys, TypeRef } from \"@tutao/tutanota-utils\"\nimport { ElementEntity, ListElementEntity, SomeEntity } from \"../../common/EntityTypes.js\"\n\nexport async function migrateAllListElements<T extends ListElementEntity>(typeRef: TypeRef<T>, storage: OfflineStorage, migrations: Array<Migration<T>>) {\n\tlet entities = await storage.getListElementsOfType(typeRef)\n\n\tfor (const migration of migrations) {\n\t\tentities = entities.map(migration)\n\t}\n\n\tfor (const entity of entities) {\n\t\tawait storage.put(entity)\n\t}\n}\n\nexport async function migrateAllElements<T extends ElementEntity>(typeRef: TypeRef<T>, storage: OfflineStorage, migrations: Array<Migration<T>>) {\n\tlet entities = await storage.getElementsOfType(typeRef)\n\n\tfor (const migration of migrations) {\n\t\tentities = entities.map(migration)\n\t}\n\n\tfor (const entity of entities) {\n\t\tawait storage.put(entity)\n\t}\n}\n\nexport type Migration<T extends SomeEntity> = (entity: any) => T\n\nexport function renameAttribute<T extends SomeEntity>(oldName: string, newName: keyof T): Migration<T> {\n\treturn function (entity) {\n\t\tentity[newName] = entity[oldName as keyof T]\n\t\tdelete entity[oldName as keyof T]\n\t\treturn entity\n\t}\n}\n\nexport function removeValue<T extends SomeEntity>(valueName: string): Migration<T> {\n\treturn function (entity) {\n\t\tdelete entity[valueName as keyof T]\n\t\treturn entity\n\t}\n}\n\nexport function booleanToNumberValue<T extends SomeEntity>(attribute: string): Migration<T> {\n\treturn function (entity) {\n\t\t// same default value mapping as in the tutadb migration\n\t\tentity[attribute] = (entity[attribute] ? \"1\" : \"0\") as unknown as T[keyof T]\n\t\treturn entity\n\t}\n}\n\nexport async function clearDatabase(storage: OfflineStorage) {\n\tawait storage.purgeStorage()\n\tawait writeModelVersions(storage)\n}\n\nexport function deleteInstancesOfType<T extends SomeEntity>(storage: OfflineStorage, type: TypeRef<T>): Promise<void> {\n\treturn storage.deleteAllOfType(type)\n}\n\nasync function writeModelVersions(storage: OfflineStorage) {\n\tfor (const app of typedKeys(modelInfos)) {\n\t\tconst key = `${app}-version` as const\n\t\tlet version = modelInfos[app].version\n\t\tawait storage.setStoredModelVersion(app, version)\n\t}\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements } from \"../StandardMigrations.js\"\nimport { createMailBox, MailBoxTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const tutanota60: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 60,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllElements(MailBoxTypeRef, storage, [createMailBox])\n\t},\n}\n","import { OfflineDbMeta, OfflineStorage, VersionMetadataBaseKey } from \"./OfflineStorage.js\"\nimport { ModelInfos } from \"../../common/EntityFunctions.js\"\nimport { assertNotNull, typedEntries, typedKeys } from \"@tutao/tutanota-utils\"\nimport { ProgrammingError } from \"../../common/error/ProgrammingError.js\"\nimport { sys75 } from \"./migrations/sys-v75.js\"\nimport { sys76 } from \"./migrations/sys-v76.js\"\nimport { tutanota54 } from \"./migrations/tutanota-v54.js\"\nimport { sys79 } from \"./migrations/sys-v79.js\"\nimport { sys80 } from \"./migrations/sys-v80.js\"\nimport { SqlCipherFacade } from \"../../../native/common/generatedipc/SqlCipherFacade.js\"\nimport { offline1 } from \"./migrations/offline-v1.js\"\nimport { tutanota56 } from \"./migrations/tutanota-v56.js\"\nimport { tutanota58 } from \"./migrations/tutanota-v58.js\"\nimport { storage6 } from \"./migrations/storage-v6.js\"\nimport { tutanota57 } from \"./migrations/tutanota-v57.js\"\nimport { OutOfSyncError } from \"../../common/error/OutOfSyncError.js\"\nimport { sys83 } from \"./migrations/sys-v83.js\"\nimport { tutanota60 } from \"./migrations/tutanota-v60.js\"\nimport { sys84 } from \"./migrations/sys-v84.js\"\nimport { tutanota61 } from \"./migrations/tutanota-v61.js\"\nimport { sys85 } from \"./migrations/sys-v85.js\"\nimport { accounting5 } from \"./migrations/accounting-v5.js\"\nimport { sys86 } from \"./migrations/sys-v86.js\"\nimport { sys87 } from \"./migrations/sys-v87.js\"\nimport { sys88 } from \"./migrations/sys-v88.js\"\n\nexport interface OfflineMigration {\n\treadonly app: VersionMetadataBaseKey\n\treadonly version: number\n\n\tmigrate(storage: OfflineStorage, sqlCipherFacade: SqlCipherFacade): Promise<void>\n}\n\n/**\n * List of migrations that will be run when needed. Please add your migrations to the list.\n *\n * Normally you should only add them to the end of the list but with offline ones it can be a bit tricky since they change the db structure itself so sometimes\n * they should rather be in the beginning.\n */\nexport const OFFLINE_STORAGE_MIGRATIONS: ReadonlyArray<OfflineMigration> = [\n\toffline1,\n\tsys75, // DB dropped in offline1\n\tsys76, // DB dropped in offline1\n\tsys79, // DB dropped in offline1\n\tsys80, // DB dropped in offline1\n\ttutanota54, // DB dropped in offline1\n\ttutanota56,\n\ttutanota57,\n\ttutanota58,\n\ttutanota60,\n\tstorage6,\n\tsys83,\n\taccounting5,\n\tsys84,\n\ttutanota61,\n\tsys85,\n\tsys86,\n\tsys87,\n\tsys88,\n]\n\nconst CURRENT_OFFLINE_VERSION = 1\n\n/**\n * Migrator for the offline storage between different versions of model. It is tightly couples to the versions of API entities: every time we make an\n * \"incompatible\" change to the API model we need to update offline database somehow.\n *\n * Migrations are done manually but there are a few checks done:\n *  - compile time check that migration exists and is used in this file\n *  - runtime check that runtime model is compatible to the stored one after all the migrations are done.\n *\n *  To add a new migration create a migration with the filename matching ./migrations/{app}-v{version}.ts and use it in the `migrations` field on this\n *  migrator.\n *\n *  Migrations might read and write to the database and they should use StandardMigrations when needed.\n */\nexport class OfflineStorageMigrator {\n\tconstructor(private readonly migrations: ReadonlyArray<OfflineMigration>, private readonly modelInfos: ModelInfos) {}\n\n\tasync migrate(storage: OfflineStorage, sqlCipherFacade: SqlCipherFacade) {\n\t\tconst meta = await storage.dumpMetadata()\n\n\t\t// We did not write down the \"offline\" version from the beginning, so we need to figure out if we need to run the migration for the db structure or\n\t\t// not. Previously we've been checking that there's something in the meta table which is a pretty decent check. Unfortunately we had multiple bugs\n\t\t// which resulted in a state where we would re-create the offline db but not populate the meta table with the versions, the only thing that would be\n\t\t// written is lastUpdateTime.\n\t\t// {}                                                               -> new db, do not migrate offline\n\t\t// {\"base-version\": 1, \"lastUpdateTime\": 123, \"offline-version\": 1} -> up-to-date db, do not migrate offline\n\t\t// {\"lastUpdateTime\": 123}                                          -> broken state after the buggy recreation of db, delete the db\n\t\t// {\"base-version\": 1, \"lastUpdateTime\": 123}                       -> some very old state where we would actually have to migrate offline\n\t\tif (Object.keys(meta).length === 1 && meta.lastUpdateTime != null) {\n\t\t\tthrow new OutOfSyncError(\"Invalid DB state, missing model versions\")\n\t\t}\n\n\t\tconst populatedMeta = await this.populateModelVersions(meta, storage)\n\n\t\tif (this.isDbNewerThanCurrentClient(populatedMeta)) {\n\t\t\tthrow new OutOfSyncError(`offline database has newer schema than client`)\n\t\t}\n\n\t\tawait this.runMigrations(meta, storage, sqlCipherFacade)\n\t\tawait this.checkStateAfterMigrations(storage)\n\t}\n\n\tprivate async checkStateAfterMigrations(storage: OfflineStorage) {\n\t\t// Check that all the necessary migrations have been run, at least to the point where we are compatible.\n\t\tconst meta = await storage.dumpMetadata()\n\t\tfor (const app of typedKeys(this.modelInfos)) {\n\t\t\tconst compatibleSince = this.modelInfos[app].compatibleSince\n\t\t\tlet metaVersion = meta[`${app}-version`]!\n\t\t\tif (metaVersion < compatibleSince) {\n\t\t\t\tthrow new ProgrammingError(\n\t\t\t\t\t`You forgot to migrate your databases! ${app}.version should be >= ${this.modelInfos[app].compatibleSince} but in db it is ${metaVersion}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async runMigrations(meta: Partial<OfflineDbMeta>, storage: OfflineStorage, sqlCipherFacade: SqlCipherFacade) {\n\t\tfor (const { app, version, migrate } of this.migrations) {\n\t\t\tconst storedVersion = meta[`${app}-version`]!\n\t\t\tif (storedVersion < version) {\n\t\t\t\tconsole.log(`running offline db migration for ${app} from ${storedVersion} to ${version}`)\n\t\t\t\tawait migrate(storage, sqlCipherFacade)\n\t\t\t\tconsole.log(\"migration finished\")\n\t\t\t\tawait storage.setStoredModelVersion(app, version)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async populateModelVersions(meta: Readonly<Partial<OfflineDbMeta>>, storage: OfflineStorage): Promise<Partial<OfflineDbMeta>> {\n\t\t// We did not write down the \"offline\" version from the beginning, so we need to figure out if we need to run the migration for the db structure or\n\t\t// not. New DB will have up-to-date table definition but no metadata keys.\n\t\tconst isNewDb = Object.keys(meta).length === 0\n\n\t\t// copy metadata because it's going to be mutated\n\t\tconst newMeta = { ...meta }\n\t\t// Populate model versions if they haven't been written already\n\t\tfor (const app of typedKeys(this.modelInfos)) {\n\t\t\tawait this.prepopulateVersionIfNecessary(app, this.modelInfos[app].version, newMeta, storage)\n\t\t}\n\n\t\tif (isNewDb) {\n\t\t\tconsole.log(`new db, setting \"offline\" version to ${CURRENT_OFFLINE_VERSION}`)\n\t\t\t// this migration is not necessary for new databases and we want our canonical table definitions to represent the current state\n\t\t\tawait this.prepopulateVersionIfNecessary(\"offline\", CURRENT_OFFLINE_VERSION, newMeta, storage)\n\t\t} else {\n\t\t\t// we need to put 0 in because we expect all versions to be populated\n\t\t\tawait this.prepopulateVersionIfNecessary(\"offline\", 0, newMeta, storage)\n\t\t}\n\t\treturn newMeta\n\t}\n\n\t/**\n\t * update the metadata table to initialize the row of the app with the given model version\n\t *\n\t * NB: mutates meta\n\t */\n\tprivate async prepopulateVersionIfNecessary(app: VersionMetadataBaseKey, version: number, meta: Partial<OfflineDbMeta>, storage: OfflineStorage) {\n\t\tconst key = `${app}-version` as const\n\t\tconst storedVersion = meta[key]\n\t\tif (storedVersion == null) {\n\t\t\tmeta[key] = version\n\t\t\tawait storage.setStoredModelVersion(app, version)\n\t\t}\n\t}\n\n\t/**\n\t * it's possible that the user installed an older client over a newer one, and we don't have backwards migrations.\n\t * in that case, it's likely that the client can't even understand the contents of the db.\n\t * we're going to delete it and not migrate at all.\n\t * @private\n\t *\n\t * @returns true if the database we're supposed to migrate has any higher model versions than our highest migration for that model, false otherwise\n\t */\n\tprivate isDbNewerThanCurrentClient(meta: Partial<OfflineDbMeta>): boolean {\n\t\tfor (const [app, { version }] of typedEntries(this.modelInfos)) {\n\t\t\tconst storedVersion = meta[`${app}-version`]!\n\t\t\tif (storedVersion > version) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn assertNotNull(meta[`offline-version`]) > CURRENT_OFFLINE_VERSION\n\t}\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage, sql } from \"../OfflineStorage.js\"\nimport { SqlCipherFacade } from \"../../../../native/common/generatedipc/SqlCipherFacade.js\"\n\n/**\n * Migration to add ownerGroup\n */\nexport const offline1: OfflineMigration = {\n\tapp: \"offline\",\n\tversion: 1,\n\tasync migrate(storage: OfflineStorage, sqlCipherFacade: SqlCipherFacade) {\n\t\tawait addOwnerToElementEntities(sqlCipherFacade)\n\t\tawait addOwnerToListEntities(sqlCipherFacade)\n\n\t\tawait sqlCipherFacade.run(\"DELETE FROM ranges\", [])\n\t\tconsole.log(\"nuked ranges\")\n\t\tawait sqlCipherFacade.run(\"DELETE FROM list_entities\", [])\n\t\tconsole.log(\"nuked list_entities\")\n\t\tawait sqlCipherFacade.run(\"DELETE FROM element_entities\", [])\n\t\tconsole.log(\"nuked element_entities\")\n\t\tawait sqlCipherFacade.run(\"DELETE FROM metadata WHERE KEY = 'lastUpdateTime'\", [])\n\t\tconsole.log(\"nuked lastUpdateTime\")\n\t\tawait sqlCipherFacade.run(\"DELETE FROM lastUpdateBatchIdPerGroupId\", [])\n\t\tconsole.log(\"nuked lastUpdateBatchIdPerGroupId\")\n\t},\n}\n\nasync function addOwnerToElementEntities(sqlCipherFacade: SqlCipherFacade) {\n\tconst { query, params } = sql`ALTER TABLE element_entities add column ownerGroup TEXT`\n\tawait sqlCipherFacade.run(query, params)\n}\n\nasync function addOwnerToListEntities(sqlCipherFacade: SqlCipherFacade) {\n\tconst { query, params } = sql`ALTER TABLE list_entities add column ownerGroup TEXT`\n\tawait sqlCipherFacade.run(query, params)\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { createGiftCard, GiftCardTypeRef } from \"../../../entities/sys/TypeRefs.js\"\nimport { booleanToNumberValue, migrateAllListElements, renameAttribute } from \"../StandardMigrations.js\"\nimport { GiftCardStatus } from \"../../../../subscription/giftcards/GiftCardUtils.js\"\n\nexport const sys75: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 75,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllListElements(GiftCardTypeRef, storage, [\n\t\t\tbooleanToNumberValue(\"usable\"),\n\t\t\trenameAttribute(\"usable\", \"status\"),\n\t\t\t// add value\n\t\t\tcreateGiftCard,\n\t\t])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { GiftCardTypeRef } from \"../../../entities/sys/TypeRefs.js\"\nimport { migrateAllListElements, removeValue } from \"../StandardMigrations.js\"\n\nexport const sys76: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 76,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllListElements(GiftCardTypeRef, storage, [removeValue(\"country\")])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\n\nexport const sys79: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 79,\n\tasync migrate(storage: OfflineStorage) {},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements } from \"../StandardMigrations.js\"\nimport { createCustomerProperties, CustomerPropertiesTypeRef } from \"../../../entities/sys/TypeRefs.js\"\n\nexport const sys80: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 80,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllElements(CustomerPropertiesTypeRef, storage, [createCustomerProperties])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements } from \"../StandardMigrations.js\"\nimport { createUserSettingsGroupRoot, UserSettingsGroupRootTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const tutanota54: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 54,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllElements(UserSettingsGroupRootTypeRef, storage, [createUserSettingsGroupRoot])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements } from \"../StandardMigrations.js\"\nimport { createMailboxGroupRoot, createMailboxProperties, MailboxGroupRootTypeRef, MailboxPropertiesTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const tutanota56: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 56,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllElements(MailboxPropertiesTypeRef, storage, [createMailboxProperties])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements, renameAttribute } from \"../StandardMigrations.js\"\nimport { MailBoxTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const tutanota57: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 57,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllElements(MailBoxTypeRef, storage, [renameAttribute(\"systemFolders\", \"folders\")])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllListElements } from \"../StandardMigrations.js\"\nimport { createMail, MailTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const tutanota58: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 58,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllListElements(MailTypeRef, storage, [createMail])\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\n\nexport const storage6: OfflineMigration = {\n\tapp: \"storage\",\n\tversion: 6,\n\tasync migrate(storage: OfflineStorage) {},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllListElements, Migration } from \"../StandardMigrations.js\"\nimport { Mail, MailTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const sys83: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 83,\n\tasync migrate(storage: OfflineStorage) {\n\t\tawait migrateAllListElements(MailTypeRef, storage, [migrateBucketKey()])\n\t},\n}\n\nfunction migrateBucketKey(): Migration<Mail> {\n\treturn function (mail) {\n\t\tconst bucketKey = mail.bucketKey\n\t\tif (bucketKey != null) {\n\t\t\tbucketKey[\"keyGroup\"] = bucketKey[\"pubKeyGroup\"]\n\t\t\tdelete bucketKey[\"pubKeyGroup\"]\n\t\t\tbucketKey[\"groupEncBucketKey\"] = bucketKey[\"ownerEncBucketKey\"]\n\t\t\tdelete bucketKey[\"ownerEncBucketKey\"]\n\t\t}\n\t\treturn mail\n\t}\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { migrateAllElements } from \"../StandardMigrations.js\"\nimport { createMailBox, MailBoxTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\n\nexport const accounting5: OfflineMigration = {\n\tapp: \"accounting\",\n\tversion: 5,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// only changes to data transfer types\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { CustomerTypeRef } from \"../../../entities/sys/TypeRefs.js\"\nimport { deleteInstancesOfType } from \"../StandardMigrations.js\"\n\nexport const sys84: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 84,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// no need to do anything as all existing types that were modified (customer, customerInfo) either only have\n\t\t// information for new customers or the information will anyway be requested with the ReferralCodeService\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { CalendarEventTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { deleteInstancesOfType } from \"../StandardMigrations.js\"\n\nexport const tutanota61: OfflineMigration = {\n\tapp: \"tutanota\",\n\tversion: 61,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// we need to delete them because we may already have added exclusions to an event\n\t\t// with a newer client and cached it with the older client.\n\t\t// server will have back-migrated the exclusions away and there's no way for us to get at\n\t\t// them without re-downlading.\n\t\tawait deleteInstancesOfType(storage, CalendarEventTypeRef)\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\n\nexport const sys85: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 85,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// no-op, missed notifications are not cached so we don't need to migrate them.\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { deleteInstancesOfType } from \"../StandardMigrations.js\"\nimport { BookingTypeRef, CustomerInfoTypeRef, GroupInfoTypeRef, GroupTypeRef, UserTypeRef } from \"../../../entities/sys/TypeRefs.js\"\n\nexport const sys86: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 86,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// delete stored CustomerInfo for new pricing model values and force client to retrieve a new one\n\t\tawait deleteInstancesOfType(storage, CustomerInfoTypeRef)\n\t\tawait deleteInstancesOfType(storage, GroupTypeRef)\n\t\tawait deleteInstancesOfType(storage, BookingTypeRef)\n\t\t// We also delete GroupInfo and UserType ref to disable offline login. Otherwise clients will see an unexpected error message with pure offline login.\n\t\tawait deleteInstancesOfType(storage, GroupInfoTypeRef)\n\t\tawait deleteInstancesOfType(storage, UserTypeRef)\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { deleteInstancesOfType } from \"../StandardMigrations.js\"\nimport { BookingTypeRef, CustomerInfoTypeRef, GroupInfoTypeRef, GroupTypeRef, UserTypeRef } from \"../../../entities/sys/TypeRefs.js\"\n\nexport const sys87: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 87,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// we only added customPlan to customerInfo and don't need to migrate anything (as it is only retrieved via the PlanService)\n\t},\n}\n","import { OfflineMigration } from \"../OfflineStorageMigrator.js\"\nimport { OfflineStorage } from \"../OfflineStorage.js\"\nimport { deleteInstancesOfType } from \"../StandardMigrations.js\"\nimport { FileTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { CustomerInfoTypeRef, UserTypeRef } from \"../../../entities/sys/TypeRefs.js\"\n\nexport const sys88: OfflineMigration = {\n\tapp: \"sys\",\n\tversion: 88,\n\tasync migrate(storage: OfflineStorage) {\n\t\t// Delete File instances to ensure that only File instances with references to Blobs exist\n\t\tawait deleteInstancesOfType(storage, FileTypeRef)\n\t\t// The business flag on the CustomerInfo's PlanConfiguration has been split in two, and one of the new flags inherits the value from the old one, but\n\t\t// we need to populate the other one still.\n\t\tawait deleteInstancesOfType(storage, CustomerInfoTypeRef)\n\t\t// We also delete UserType ref to disable offline login. Otherwise, clients will see an unexpected error message with pure offline login.\n\t\tawait deleteInstancesOfType(storage, UserTypeRef)\n\t},\n}\n","import { EntropySource, Randomizer } from \"@tutao/tutanota-crypto\"\nimport { UserFacade } from \"./UserFacade.js\"\nimport { createEntropyData } from \"../../entities/tutanota/TypeRefs.js\"\nimport { encryptBytes } from \"../crypto/CryptoFacade.js\"\nimport { EntropyService } from \"../../entities/tutanota/Services.js\"\nimport { noOp, ofClass } from \"@tutao/tutanota-utils\"\nimport { ConnectionError, LockedError, ServiceUnavailableError } from \"../../common/error/RestError.js\"\nimport { IServiceExecutor } from \"../../common/ServiceRequest.js\"\n\nexport interface EntropyDataChunk {\n\tsource: EntropySource\n\tentropy: number\n\tdata: number | Array<number>\n}\n\n/** A class which accumulates the entropy and stores it on the server. */\nexport class EntropyFacade {\n\tprivate newEntropy: number = -1\n\tprivate lastEntropyUpdate: number = Date.now()\n\n\tconstructor(private readonly userFacade: UserFacade, private readonly serviceExecutor: IServiceExecutor, private readonly random: Randomizer) {}\n\n\t/**\n\t * Adds entropy to the randomizer. Updated the stored entropy for a user when enough entropy has been collected.\n\t */\n\taddEntropy(entropy: EntropyDataChunk[]): Promise<void> {\n\t\ttry {\n\t\t\treturn this.random.addEntropy(entropy)\n\t\t} finally {\n\t\t\tthis.newEntropy = this.newEntropy + entropy.reduce((sum, value) => value.entropy + sum, 0)\n\t\t\tconst now = new Date().getTime()\n\n\t\t\tif (this.newEntropy > 5000 && now - this.lastEntropyUpdate > 1000 * 60 * 5) {\n\t\t\t\tthis.lastEntropyUpdate = now\n\t\t\t\tthis.newEntropy = 0\n\t\t\t\tthis.storeEntropy()\n\t\t\t}\n\t\t}\n\t}\n\n\tstoreEntropy(): Promise<void> {\n\t\t// We only store entropy to the server if we are the leader\n\t\tif (!this.userFacade.isFullyLoggedIn() || !this.userFacade.isLeader()) return Promise.resolve()\n\t\tconst userGroupKey = this.userFacade.getUserGroupKey()\n\t\tconst entropyData = createEntropyData({\n\t\t\tgroupEncEntropy: encryptBytes(userGroupKey, this.random.generateRandomData(32)),\n\t\t})\n\t\treturn this.serviceExecutor\n\t\t\t.put(EntropyService, entropyData)\n\t\t\t.catch(ofClass(LockedError, noOp))\n\t\t\t.catch(\n\t\t\t\tofClass(ConnectionError, (e) => {\n\t\t\t\t\tconsole.log(\"could not store entropy\", e)\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(ServiceUnavailableError, (e) => {\n\t\t\t\t\tconsole.log(\"could not store entropy\", e)\n\t\t\t\t}),\n\t\t\t)\n\t}\n}\n","import { ArchiveDataType } from \"../../common/TutanotaConstants\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport { BlobAccessTokenService } from \"../../entities/storage/Services\"\nimport { Blob } from \"../../entities/sys/TypeRefs.js\"\nimport { IServiceExecutor } from \"../../common/ServiceRequest\"\nimport { BlobServerAccessInfo, createBlobAccessTokenPostIn, createBlobReadData, createBlobWriteData, createInstanceId } from \"../../entities/storage/TypeRefs\"\nimport { DateProvider } from \"../../common/DateProvider.js\"\nimport { resolveTypeReference } from \"../../common/EntityFunctions.js\"\nimport { AuthDataProvider } from \"./UserFacade.js\"\nimport { SomeEntity } from \"../../common/EntityTypes.js\"\nimport { TypeRef } from \"@tutao/tutanota-utils\"\n\nassertWorkerOrNode()\n\n/**\n * Common interface for instances that are referencing blobs. Main purpose is to have a proper way to access the attribute for the Blob aggregated type\n * because the name of the attribute can be different for each instance.\n *\n */\nexport type BlobReferencingInstance = {\n\telementId: Id\n\n\tlistId: Id | null\n\n\tblobs: Blob[]\n\n\tentity: SomeEntity\n}\n\n/**\n * The BlobAccessTokenFacade requests blobAccessTokens from the BlobAccessTokenService to get or post to the BlobService (binary blobs)\n * or DefaultBlobElementResource (instances).\n *\n * All tokens are cached.\n */\nexport class BlobAccessTokenFacade {\n\t// cache for blob access tokens that are valid for the whole archive (key:<archiveId>)\n\tprivate readonly readArchiveCache: BlobAccessTokenCache<string>\n\t// cache for blob access tokens that are valid for blobs from a given instance were the user does not own the archive (key:<instanceElementId>).\n\tprivate readonly readBlobCache: BlobAccessTokenCache<string>\n\t// cache for upload requests are valid for the whole archive (key:<ownerGroup + archiveDataType>).\n\tprivate readonly writeCache: BlobAccessTokenCache<string>\n\n\tconstructor(\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly dateProvider: DateProvider,\n\t\tprivate readonly authDataProvider: AuthDataProvider,\n\t) {\n\t\tthis.readArchiveCache = new BlobAccessTokenCache<Id>(dateProvider)\n\t\tthis.readBlobCache = new BlobAccessTokenCache<Id>(dateProvider)\n\t\tthis.writeCache = new BlobAccessTokenCache<string>(dateProvider)\n\t}\n\n\t/**\n\t * Requests a token that allows uploading blobs for the given ArchiveDataType and ownerGroup.\n\t * @param archiveDataType The type of data that should be stored.\n\t * @param ownerGroupId The ownerGroup were the data belongs to (e.g. group of type mail)\n\t */\n\tasync requestWriteToken(archiveDataType: ArchiveDataType, ownerGroupId: Id): Promise<BlobServerAccessInfo> {\n\t\tconst requestNewToken = async () => {\n\t\t\tconst tokenRequest = createBlobAccessTokenPostIn({\n\t\t\t\tarchiveDataType,\n\t\t\t\twrite: createBlobWriteData({\n\t\t\t\t\tarchiveOwnerGroup: ownerGroupId,\n\t\t\t\t}),\n\t\t\t})\n\t\t\tconst { blobAccessInfo } = await this.serviceExecutor.post(BlobAccessTokenService, tokenRequest)\n\t\t\treturn blobAccessInfo\n\t\t}\n\t\tconst key = this.makeWriteCacheKey(ownerGroupId, archiveDataType)\n\t\treturn this.writeCache.getToken(key, requestNewToken)\n\t}\n\n\tprivate makeWriteCacheKey(ownerGroupId: string, archiveDataType: ArchiveDataType) {\n\t\treturn ownerGroupId + archiveDataType\n\t}\n\n\t/**\n\t * Remove a given write token from the cache.\n\t * @param archiveDataType\n\t * @param ownerGroupId\n\t */\n\tevictWriteToken(archiveDataType: ArchiveDataType, ownerGroupId: Id): void {\n\t\tconst key = this.makeWriteCacheKey(ownerGroupId, archiveDataType)\n\t\tthis.writeCache.evict(key)\n\t}\n\n\t/**\n\t * Requests a token that grants read access to all blobs that are referenced by the given instance.\n\t * A user must be owner of the instance but must not be owner of the archive were the blobs are stored in.\n\t * @param archiveDataType specify the data type\n\t * @param referencingInstance the instance that references the blobs\n\t */\n\tasync requestReadTokenBlobs(archiveDataType: ArchiveDataType, referencingInstance: BlobReferencingInstance): Promise<BlobServerAccessInfo> {\n\t\tconst requestNewToken = async () => {\n\t\t\tconst archiveId = this.getArchiveId(referencingInstance.blobs)\n\t\t\tconst instanceListId = referencingInstance.listId\n\t\t\tconst instanceId = referencingInstance.elementId\n\t\t\tconst instanceIds = [createInstanceId({ instanceId })]\n\t\t\tconst tokenRequest = createBlobAccessTokenPostIn({\n\t\t\t\tarchiveDataType,\n\t\t\t\tread: createBlobReadData({\n\t\t\t\t\tarchiveId,\n\t\t\t\t\tinstanceListId,\n\t\t\t\t\tinstanceIds,\n\t\t\t\t}),\n\t\t\t})\n\t\t\tconst { blobAccessInfo } = await this.serviceExecutor.post(BlobAccessTokenService, tokenRequest)\n\t\t\treturn blobAccessInfo\n\t\t}\n\t\treturn this.readBlobCache.getToken(referencingInstance.elementId, requestNewToken)\n\t}\n\n\t/**\n\t * Remove a given read blobs token from the cache.\n\t * @param referencingInstance\n\t */\n\tevictReadBlobsToken(referencingInstance: BlobReferencingInstance): void {\n\t\tthis.readBlobCache.evict(referencingInstance.elementId)\n\t}\n\n\t/**\n\t * Requests a token that grants access to all blobs stored in the given archive. The user must own the archive (member of group)\n\t * @param archiveId ID for the archive to read blobs from\n\t */\n\tasync requestReadTokenArchive(archiveId: Id): Promise<BlobServerAccessInfo> {\n\t\tconst requestNewToken = async () => {\n\t\t\tconst tokenRequest = createBlobAccessTokenPostIn({\n\t\t\t\tarchiveDataType: null,\n\t\t\t\tread: createBlobReadData({\n\t\t\t\t\tarchiveId,\n\t\t\t\t\tinstanceIds: [],\n\t\t\t\t}),\n\t\t\t})\n\t\t\tconst { blobAccessInfo } = await this.serviceExecutor.post(BlobAccessTokenService, tokenRequest)\n\t\t\treturn blobAccessInfo\n\t\t}\n\t\treturn this.readArchiveCache.getToken(archiveId, requestNewToken)\n\t}\n\n\t/**\n\t * Remove a given read archive token from the cache.\n\t * @param archiveId\n\t */\n\tevictArchiveToken(archiveId: Id): void {\n\t\tthis.readArchiveCache.evict(archiveId)\n\t}\n\n\tprivate getArchiveId(blobs: Blob[]) {\n\t\tif (blobs.length == 0) {\n\t\t\tthrow new Error(\"must pass blobs\")\n\t\t}\n\t\tlet archiveIds = new Set(blobs.map((b) => b.archiveId))\n\t\tif (archiveIds.size != 1) {\n\t\t\tthrow new Error(`only one archive id allowed, but was ${archiveIds}`)\n\t\t}\n\t\treturn blobs[0].archiveId\n\t}\n\n\t/**\n\t *\n\t * @param blobServerAccessInfo\n\t * @param additionalRequestParams\n\t * @param typeRef the typeRef that shall be used to determine the correct model version\n\t */\n\tpublic async createQueryParams(blobServerAccessInfo: BlobServerAccessInfo, additionalRequestParams: Dict, typeRef: TypeRef<any>): Promise<Dict> {\n\t\tconst typeModel = await resolveTypeReference(typeRef)\n\t\treturn Object.assign(\n\t\t\tadditionalRequestParams,\n\t\t\t{\n\t\t\t\tblobAccessToken: blobServerAccessInfo.blobAccessToken,\n\t\t\t\tv: typeModel.version,\n\t\t\t},\n\t\t\tthis.authDataProvider.createAuthHeaders(),\n\t\t)\n\t}\n}\n\n/**\n * Checks if the given access token can be used for another blob service requests.\n * @param blobServerAccessInfo\n * @param dateProvider\n */\nfunction canBeUsedForAnotherRequest(blobServerAccessInfo: BlobServerAccessInfo, dateProvider: DateProvider): boolean {\n\treturn blobServerAccessInfo.expires.getTime() > dateProvider.now()\n}\n\nclass BlobAccessTokenCache<K> {\n\tprivate cache: Map<K, BlobServerAccessInfo>\n\tprivate dateProvider: DateProvider\n\n\tconstructor(dateProvider: DateProvider) {\n\t\tthis.cache = new Map<K, BlobServerAccessInfo>()\n\t\tthis.dateProvider = dateProvider\n\t}\n\n\tpublic async getToken(key: K, loader: () => Promise<BlobServerAccessInfo>): Promise<BlobServerAccessInfo> {\n\t\tconst cached = this.cache.get(key)\n\t\tif (cached && canBeUsedForAnotherRequest(cached, this.dateProvider)) {\n\t\t\treturn cached\n\t\t} else {\n\t\t\tconst newToken = await loader()\n\t\t\tthis.cache.set(key, newToken)\n\t\t\treturn newToken\n\t\t}\n\t}\n\n\tpublic evict(key: K): void {\n\t\tthis.cache.delete(key)\n\t}\n}\n","import { debounce } from \"@tutao/tutanota-utils\"\nimport type { InstanceSessionKey } from \"../../entities/sys/TypeRefs.js\"\nimport { createUpdateSessionKeysPostIn } from \"../../entities/sys/TypeRefs.js\"\nimport { LockedError } from \"../../common/error/RestError\"\nimport { assertWorkerOrNode } from \"../../common/Env\"\nimport { IServiceExecutor } from \"../../common/ServiceRequest\"\nimport { UpdateSessionKeysService } from \"../../entities/sys/Services\"\nimport { UserFacade } from \"../facades/UserFacade\"\n\nassertWorkerOrNode()\n\nexport const UPDATE_SESSION_KEYS_SERVICE_DEBOUNCE_MS = 50\n\n/**\n * This queue collects updates for ownerEncSessionKeys and debounces the update request to the UpdateSessionKeysService,\n * in order to update as many instances in one request as possible.\n *\n * In case of LockedErrors it will retry. In case of other errors it will discard the update.\n * (The next time the instance session key is resolved using the bucket key a new update attempt will be made for those instances.)\n */\nexport class OwnerEncSessionKeysUpdateQueue {\n\tprivate updateInstanceSessionKeyQueue: Array<InstanceSessionKey> = []\n\tprivate readonly invokeUpdateSessionKeyService: () => Promise<void>\n\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\t// allow passing the timeout for testability\n\t\tdebounceTimeoutMs: number = UPDATE_SESSION_KEYS_SERVICE_DEBOUNCE_MS,\n\t) {\n\t\tthis.invokeUpdateSessionKeyService = debounce(debounceTimeoutMs, () => this.sendUpdateRequest())\n\t}\n\n\t/**\n\t * Add the ownerEncSessionKey updates to the queue and debounce the update request.\n\t *\n\t * @param instanceSessionKeys all instanceSessionKeys from one bucketKey containing the ownerEncSessionKey as symEncSessionKey\n\t */\n\tupdateInstanceSessionKeys(instanceSessionKeys: Array<InstanceSessionKey>) {\n\t\tif (this.userFacade.isLeader()) {\n\t\t\tthis.updateInstanceSessionKeyQueue.push(...instanceSessionKeys)\n\t\t\tthis.invokeUpdateSessionKeyService()\n\t\t}\n\t}\n\n\tprivate async sendUpdateRequest() {\n\t\tconst input = createUpdateSessionKeysPostIn({\n\t\t\townerEncSessionKeys: this.updateInstanceSessionKeyQueue,\n\t\t})\n\t\tthis.updateInstanceSessionKeyQueue = []\n\t\tif (input.ownerEncSessionKeys.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait this.serviceExecutor.post(UpdateSessionKeysService, input)\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof LockedError) {\n\t\t\t\t\tthis.updateInstanceSessionKeyQueue.push(...input.ownerEncSessionKeys)\n\t\t\t\t\tthis.invokeUpdateSessionKeyService()\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import { EventBusListener } from \"./EventBusClient.js\"\nimport { WsConnectionState } from \"../main/WorkerClient.js\"\nimport { EntityUpdate, UserTypeRef, WebsocketCounterData, WebsocketLeaderStatus } from \"../entities/sys/TypeRefs.js\"\nimport { PhishingMarker } from \"../entities/tutanota/TypeRefs.js\"\nimport { WebsocketConnectivityListener } from \"../../misc/WebsocketConnectivityModel.js\"\nimport { WorkerImpl } from \"./WorkerImpl.js\"\nimport { isAdminClient, isTest } from \"../common/Env.js\"\nimport { MailFacade } from \"./facades/lazy/MailFacade.js\"\nimport type { Indexer } from \"./search/Indexer.js\"\nimport { UserFacade } from \"./facades/UserFacade.js\"\nimport { EntityClient } from \"../common/EntityClient.js\"\nimport { OperationType } from \"../common/TutanotaConstants.js\"\nimport { isSameTypeRefByAttr, lazyAsync } from \"@tutao/tutanota-utils\"\nimport { isSameId } from \"../common/utils/EntityUtils.js\"\nimport { ExposedEventController } from \"../main/EventController.js\"\n\n/** A bit of glue to distribute event bus events across the app. */\nexport class EventBusEventCoordinator implements EventBusListener {\n\tconstructor(\n\t\tprivate readonly worker: WorkerImpl,\n\t\tprivate readonly connectivityListener: WebsocketConnectivityListener,\n\t\tprivate readonly mailFacade: lazyAsync<MailFacade>,\n\t\tprivate readonly indexer: lazyAsync<Indexer>,\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly eventController: ExposedEventController,\n\t) {}\n\n\tonWebsocketStateChanged(state: WsConnectionState) {\n\t\tthis.connectivityListener.updateWebSocketState(state)\n\t}\n\n\tasync onEntityEventsReceived(events: EntityUpdate[], batchId: Id, groupId: Id): Promise<void> {\n\t\tawait this.entityEventsReceived(events)\n\t\tawait (await this.mailFacade()).entityEventsReceived(events)\n\t\tawait this.eventController.onEntityUpdateReceived(events, groupId)\n\t\t// Call the indexer in this last step because now the processed event is stored and the indexer has a separate event queue that\n\t\t// shall not receive the event twice.\n\t\tif (!isTest() && !isAdminClient()) {\n\t\t\tconst queuedBatch = { groupId, batchId, events }\n\t\t\tconst indexer = await this.indexer()\n\t\t\tindexer.addBatchesToQueue([queuedBatch])\n\t\t\tindexer.startProcessing()\n\t\t}\n\t}\n\n\tasync onPhishingMarkersReceived(markers: PhishingMarker[]) {\n\t\t;(await this.mailFacade()).phishingMarkersUpdateReceived(markers)\n\t}\n\n\tonError(tutanotaError: Error) {\n\t\tthis.worker.sendError(tutanotaError)\n\t}\n\n\tonLeaderStatusChanged(leaderStatus: WebsocketLeaderStatus) {\n\t\tthis.connectivityListener.onLeaderStatusChanged(leaderStatus)\n\t}\n\n\tonCounterChanged(counter: WebsocketCounterData) {\n\t\tthis.eventController.onCountersUpdateReceived(counter)\n\t}\n\n\tprivate async entityEventsReceived(data: EntityUpdate[]): Promise<void> {\n\t\t// This is a compromise to not add entityClient to UserFacade which would introduce a circular dep.\n\t\tfor (const update of data) {\n\t\t\tconst user = this.userFacade.getUser()\n\t\t\tif (\n\t\t\t\tuser != null &&\n\t\t\t\tupdate.operation === OperationType.UPDATE &&\n\t\t\t\tisSameTypeRefByAttr(UserTypeRef, update.application, update.type) &&\n\t\t\t\tisSameId(user._id, update.instanceId)\n\t\t\t) {\n\t\t\t\tthis.userFacade.updateUser(await this.entityClient.load(UserTypeRef, user._id))\n\t\t\t}\n\t\t}\n\t}\n}\n","import { aes256RandomKey, keyToBase64 } from \"@tutao/tutanota-crypto\"\nimport { Logger } from \"../../common/Logger.js\"\n\n/**\n *  Loose collection of functions that should be run on the worker side e.g. because they take too much time and don't belong anywhere else.\n *  (read: kitchen sink).\n */\nexport class WorkerFacade {\n\tasync generateSsePushIdentifer(): Promise<string> {\n\t\treturn keyToBase64(aes256RandomKey())\n\t}\n\n\tasync getLog(): Promise<string[]> {\n\t\tconst global = self as any\n\t\tconst logger = global.logger as Logger | undefined\n\n\t\tif (logger) {\n\t\t\treturn logger.getEntries()\n\t\t} else {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tasync urlify(html: string): Promise<string> {\n\t\tconst { urlify } = await import(\"../Urlifier.js\")\n\t\treturn urlify(html)\n\t}\n}\n","import { CacheInfo, LoginFacade, LoginListener } from \"./facades/LoginFacade\"\nimport type { WorkerImpl } from \"./WorkerImpl\"\nimport type { Indexer } from \"./search/Indexer\"\nimport type { EntityRestInterface } from \"./rest/EntityRestClient\"\nimport { EntityRestClient } from \"./rest/EntityRestClient\"\nimport type { UserManagementFacade } from \"./facades/lazy/UserManagementFacade.js\"\nimport { CacheStorage, DefaultEntityRestCache } from \"./rest/DefaultEntityRestCache.js\"\nimport type { GroupManagementFacade } from \"./facades/lazy/GroupManagementFacade.js\"\nimport type { MailFacade } from \"./facades/lazy/MailFacade.js\"\nimport type { MailAddressFacade } from \"./facades/lazy/MailAddressFacade.js\"\nimport type { FileFacade } from \"./facades/lazy/FileFacade.js\"\nimport type { CustomerFacade } from \"./facades/lazy/CustomerFacade.js\"\nimport type { CounterFacade } from \"./facades/lazy/CounterFacade.js\"\nimport { EventBusClient } from \"./EventBusClient\"\nimport { assertWorkerOrNode, getWebsocketOrigin, isAdminClient, isOfflineStorageAvailable, isTest } from \"../common/Env\"\nimport { Const } from \"../common/TutanotaConstants\"\nimport type { BrowserData } from \"../../misc/ClientConstants\"\nimport type { CalendarFacade } from \"./facades/lazy/CalendarFacade.js\"\nimport type { ShareFacade } from \"./facades/lazy/ShareFacade.js\"\nimport { RestClient } from \"./rest/RestClient\"\nimport { SuspensionHandler } from \"./SuspensionHandler\"\nimport { EntityClient } from \"../common/EntityClient\"\nimport type { GiftCardFacade } from \"./facades/lazy/GiftCardFacade.js\"\nimport type { ConfigurationDatabase } from \"./facades/lazy/ConfigurationDatabase.js\"\nimport type { ContactFormFacade } from \"./facades/lazy/ContactFormFacade.js\"\nimport { DeviceEncryptionFacade } from \"./facades/DeviceEncryptionFacade\"\nimport type { NativeInterface } from \"../../native/common/NativeInterface\"\nimport { NativeFileApp } from \"../../native/common/FileApp\"\nimport { AesApp } from \"../../native/worker/AesApp\"\nimport type { RsaImplementation } from \"./crypto/RsaImplementation\"\nimport { createRsaImplementation } from \"./crypto/RsaImplementation\"\nimport { CryptoFacade } from \"./crypto/CryptoFacade\"\nimport { InstanceMapper } from \"./crypto/InstanceMapper\"\nimport { AdminClientDummyEntityRestCache } from \"./rest/AdminClientDummyEntityRestCache.js\"\nimport { SleepDetector } from \"./utils/SleepDetector.js\"\nimport { SchedulerImpl } from \"../common/utils/Scheduler.js\"\nimport { NoZoneDateProvider } from \"../common/utils/NoZoneDateProvider.js\"\nimport { LateInitializedCacheStorageImpl } from \"./rest/CacheStorageProxy\"\nimport { IServiceExecutor } from \"../common/ServiceRequest\"\nimport { ServiceExecutor } from \"./rest/ServiceExecutor\"\nimport type { BookingFacade } from \"./facades/lazy/BookingFacade.js\"\nimport type { BlobFacade } from \"./facades/lazy/BlobFacade.js\"\nimport { UserFacade } from \"./facades/UserFacade\"\nimport { OfflineStorage } from \"./offline/OfflineStorage.js\"\nimport { OFFLINE_STORAGE_MIGRATIONS, OfflineStorageMigrator } from \"./offline/OfflineStorageMigrator.js\"\nimport { modelInfos } from \"../common/EntityFunctions.js\"\nimport { FileFacadeSendDispatcher } from \"../../native/common/generatedipc/FileFacadeSendDispatcher.js\"\nimport { NativePushFacadeSendDispatcher } from \"../../native/common/generatedipc/NativePushFacadeSendDispatcher.js\"\nimport { NativeCryptoFacadeSendDispatcher } from \"../../native/common/generatedipc/NativeCryptoFacadeSendDispatcher\"\nimport { random } from \"@tutao/tutanota-crypto\"\nimport { ExportFacadeSendDispatcher } from \"../../native/common/generatedipc/ExportFacadeSendDispatcher.js\"\nimport { assertNotNull, delay, lazyAsync, lazyMemoized, ofClass } from \"@tutao/tutanota-utils\"\nimport { InterWindowEventFacadeSendDispatcher } from \"../../native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js\"\nimport { SqlCipherFacadeSendDispatcher } from \"../../native/common/generatedipc/SqlCipherFacadeSendDispatcher.js\"\nimport { EntropyFacade } from \"./facades/EntropyFacade.js\"\nimport { BlobAccessTokenFacade } from \"./facades/BlobAccessTokenFacade.js\"\nimport { OwnerEncSessionKeysUpdateQueue } from \"./crypto/OwnerEncSessionKeysUpdateQueue.js\"\nimport { EventBusEventCoordinator } from \"./EventBusEventCoordinator.js\"\nimport { WorkerFacade } from \"./facades/WorkerFacade.js\"\nimport type { SearchFacade } from \"./search/SearchFacade.js\"\nimport { Challenge } from \"../entities/sys/TypeRefs.js\"\nimport { LoginFailReason } from \"../main/PageContextLoginListener.js\"\nimport { ConnectionError, ServiceUnavailableError } from \"../common/error/RestError.js\"\nimport { SessionType } from \"../common/SessionType.js\"\n\nassertWorkerOrNode()\n\nexport type WorkerLocatorType = {\n\t// network & encryption\n\trestClient: RestClient\n\tserviceExecutor: IServiceExecutor\n\tcrypto: CryptoFacade\n\tinstanceMapper: InstanceMapper\n\tcacheStorage: CacheStorage\n\tcache: EntityRestInterface\n\tcachingEntityClient: EntityClient\n\teventBusClient: EventBusClient\n\trsa: RsaImplementation\n\tentropyFacade: EntropyFacade\n\tblobAccessToken: BlobAccessTokenFacade\n\n\t// login\n\tuser: UserFacade\n\tlogin: LoginFacade\n\n\t// domains\n\tfile: lazyAsync<FileFacade>\n\tblob: lazyAsync<BlobFacade>\n\tmail: lazyAsync<MailFacade>\n\tcalendar: lazyAsync<CalendarFacade>\n\tcounters: lazyAsync<CounterFacade>\n\tConst: Record<string, any>\n\n\t// search & indexing\n\tindexer: lazyAsync<Indexer>\n\tsearch: lazyAsync<SearchFacade>\n\n\t// management facades\n\tgroupManagement: lazyAsync<GroupManagementFacade>\n\tuserManagement: lazyAsync<UserManagementFacade>\n\tcustomer: lazyAsync<CustomerFacade>\n\tgiftCards: lazyAsync<GiftCardFacade>\n\tmailAddress: lazyAsync<MailAddressFacade>\n\tcontactFormFacade: lazyAsync<ContactFormFacade>\n\tbooking: lazyAsync<BookingFacade>\n\tshare: lazyAsync<ShareFacade>\n\n\t// misc & native\n\tconfigFacade: lazyAsync<ConfigurationDatabase>\n\tdeviceEncryptionFacade: DeviceEncryptionFacade\n\tnative: NativeInterface\n\tworkerFacade: WorkerFacade\n\n\t// used to cache between resets\n\t_browserData: BrowserData\n}\nexport const locator: WorkerLocatorType = {} as any\n\nexport async function initLocator(worker: WorkerImpl, browserData: BrowserData) {\n\tlocator.user = new UserFacade()\n\tlocator.workerFacade = new WorkerFacade()\n\tconst dateProvider = new NoZoneDateProvider()\n\n\tconst mainInterface = worker.getMainInterface()\n\n\tconst suspensionHandler = new SuspensionHandler(mainInterface.infoMessageHandler, self)\n\tlocator.instanceMapper = new InstanceMapper()\n\tlocator.rsa = await createRsaImplementation(worker)\n\tlocator.restClient = new RestClient(suspensionHandler)\n\tlocator.serviceExecutor = new ServiceExecutor(locator.restClient, locator.user, locator.instanceMapper, () => locator.crypto)\n\tlocator.entropyFacade = new EntropyFacade(locator.user, locator.serviceExecutor, random)\n\tlocator.blobAccessToken = new BlobAccessTokenFacade(locator.serviceExecutor, dateProvider, locator.user)\n\tconst entityRestClient = new EntityRestClient(locator.user, locator.restClient, () => locator.crypto, locator.instanceMapper, locator.blobAccessToken)\n\tlocator._browserData = browserData\n\n\tlocator.native = worker\n\tlocator.booking = lazyMemoized(async () => {\n\t\tconst { BookingFacade } = await import(\"./facades/lazy/BookingFacade.js\")\n\t\treturn new BookingFacade(locator.serviceExecutor)\n\t})\n\n\tconst offlineStorageProvider = async () => {\n\t\tif (isOfflineStorageAvailable() && !isAdminClient()) {\n\t\t\treturn new OfflineStorage(\n\t\t\t\tnew SqlCipherFacadeSendDispatcher(locator.native),\n\t\t\t\tnew InterWindowEventFacadeSendDispatcher(worker),\n\t\t\t\tdateProvider,\n\t\t\t\tnew OfflineStorageMigrator(OFFLINE_STORAGE_MIGRATIONS, modelInfos),\n\t\t\t)\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tconst maybeUninitializedStorage = new LateInitializedCacheStorageImpl(worker, offlineStorageProvider)\n\n\tlocator.cacheStorage = maybeUninitializedStorage\n\n\tconst fileApp = new NativeFileApp(new FileFacadeSendDispatcher(worker), new ExportFacadeSendDispatcher(worker))\n\n\t// We don't want to cache within the admin client\n\tlet cache: DefaultEntityRestCache | null = null\n\tif (!isAdminClient()) {\n\t\tcache = new DefaultEntityRestCache(entityRestClient, maybeUninitializedStorage)\n\t}\n\n\tlocator.cache = cache ?? entityRestClient\n\n\tlocator.cachingEntityClient = new EntityClient(locator.cache)\n\tlocator.indexer = lazyMemoized(async () => {\n\t\tconst { Indexer } = await import(\"./search/Indexer.js\")\n\t\treturn new Indexer(entityRestClient, mainInterface.infoMessageHandler, browserData, locator.cache as DefaultEntityRestCache)\n\t})\n\n\tlocator.crypto = new CryptoFacade(\n\t\tlocator.user,\n\t\tlocator.cachingEntityClient,\n\t\tlocator.restClient,\n\t\tlocator.rsa,\n\t\tlocator.serviceExecutor,\n\t\tlocator.instanceMapper,\n\t\tnew OwnerEncSessionKeysUpdateQueue(locator.user, locator.serviceExecutor),\n\t)\n\n\tconst loginListener: LoginListener = {\n\t\tonPartialLoginSuccess(): Promise<void> {\n\t\t\treturn mainInterface.loginListener.onPartialLoginSuccess()\n\t\t},\n\n\t\tonFullLoginSuccess(sessionType: SessionType, cacheInfo: CacheInfo): Promise<void> {\n\t\t\tif (!isTest() && sessionType !== SessionType.Temporary && !isAdminClient()) {\n\t\t\t\t// index new items in background\n\t\t\t\tconsole.log(\"initIndexer after log in\")\n\n\t\t\t\tinitIndexer(worker, cacheInfo)\n\t\t\t}\n\n\t\t\treturn mainInterface.loginListener.onFullLoginSuccess(sessionType, cacheInfo)\n\t\t},\n\n\t\tonLoginFailure(reason: LoginFailReason): Promise<void> {\n\t\t\treturn mainInterface.loginListener.onLoginFailure(reason)\n\t\t},\n\n\t\tonSecondFactorChallenge(sessionId: IdTuple, challenges: ReadonlyArray<Challenge>, mailAddress: string | null): Promise<void> {\n\t\t\treturn mainInterface.loginListener.onSecondFactorChallenge(sessionId, challenges, mailAddress)\n\t\t},\n\t}\n\n\tlocator.deviceEncryptionFacade = new DeviceEncryptionFacade()\n\tconst { DatabaseKeyFactory } = await import(\"../../misc/credentials/DatabaseKeyFactory.js\")\n\tlocator.login = new LoginFacade(\n\t\tworker,\n\t\tlocator.restClient,\n\t\t/**\n\t\t * we don't want to try to use the cache in the login facade, because it may not be available (when no user is logged in)\n\t\t */\n\t\tnew EntityClient(locator.cache),\n\t\tloginListener,\n\t\tlocator.instanceMapper,\n\t\tlocator.crypto,\n\t\tmaybeUninitializedStorage,\n\t\tlocator.serviceExecutor,\n\t\tlocator.user,\n\t\tlocator.blobAccessToken,\n\t\tlocator.entropyFacade,\n\t\tnew DatabaseKeyFactory(locator.deviceEncryptionFacade),\n\t)\n\n\tlocator.search = lazyMemoized(async () => {\n\t\tconst { SearchFacade } = await import(\"./search/SearchFacade.js\")\n\t\tconst indexer = await locator.indexer()\n\t\tconst suggestionFacades = [indexer._contact.suggestionFacade, indexer._groupInfo.suggestionFacade, indexer._whitelabelChildIndexer.suggestionFacade]\n\t\treturn new SearchFacade(locator.user, indexer.db, indexer._mail, suggestionFacades, browserData, locator.cachingEntityClient)\n\t})\n\tlocator.counters = lazyMemoized(async () => {\n\t\tconst { CounterFacade } = await import(\"./facades/lazy/CounterFacade.js\")\n\t\treturn new CounterFacade(locator.serviceExecutor)\n\t})\n\tlocator.groupManagement = lazyMemoized(async () => {\n\t\tconst { GroupManagementFacade } = await import(\"./facades/lazy/GroupManagementFacade.js\")\n\t\treturn new GroupManagementFacade(locator.user, await locator.counters(), locator.cachingEntityClient, locator.rsa, locator.serviceExecutor)\n\t})\n\tlocator.userManagement = lazyMemoized(async () => {\n\t\tconst { UserManagementFacade } = await import(\"./facades/lazy/UserManagementFacade.js\")\n\t\treturn new UserManagementFacade(\n\t\t\tlocator.user,\n\t\t\tawait locator.groupManagement(),\n\t\t\tawait locator.counters(),\n\t\t\tlocator.rsa,\n\t\t\tlocator.cachingEntityClient,\n\t\t\tlocator.serviceExecutor,\n\t\t\tmainInterface.operationProgressTracker,\n\t\t)\n\t})\n\tlocator.customer = lazyMemoized(async () => {\n\t\tconst { CustomerFacade } = await import(\"./facades/lazy/CustomerFacade.js\")\n\t\treturn new CustomerFacade(\n\t\t\tlocator.user,\n\t\t\tawait locator.groupManagement(),\n\t\t\tawait locator.userManagement(),\n\t\t\tawait locator.counters(),\n\t\t\tlocator.rsa,\n\t\t\tlocator.cachingEntityClient,\n\t\t\tlocator.serviceExecutor,\n\t\t\tawait locator.booking(),\n\t\t\tlocator.crypto,\n\t\t\tmainInterface.operationProgressTracker,\n\t\t)\n\t})\n\tconst aesApp = new AesApp(new NativeCryptoFacadeSendDispatcher(worker), random)\n\tlocator.blob = lazyMemoized(async () => {\n\t\tconst { BlobFacade } = await import(\"./facades/lazy/BlobFacade.js\")\n\t\treturn new BlobFacade(\n\t\t\tlocator.user,\n\t\t\tlocator.serviceExecutor,\n\t\t\tlocator.restClient,\n\t\t\tsuspensionHandler,\n\t\t\tfileApp,\n\t\t\taesApp,\n\t\t\tlocator.instanceMapper,\n\t\t\tlocator.crypto,\n\t\t\tlocator.blobAccessToken,\n\t\t)\n\t})\n\tlocator.file = lazyMemoized(async () => {\n\t\tconst { FileFacade } = await import(\"./facades/lazy/FileFacade.js\")\n\t\treturn new FileFacade(\n\t\t\tlocator.user,\n\t\t\tlocator.restClient,\n\t\t\tsuspensionHandler,\n\t\t\tfileApp,\n\t\t\taesApp,\n\t\t\tlocator.instanceMapper,\n\t\t\tlocator.serviceExecutor,\n\t\t\tlocator.crypto,\n\t\t)\n\t})\n\tlocator.mail = lazyMemoized(async () => {\n\t\tconst { MailFacade } = await import(\"./facades/lazy/MailFacade.js\")\n\t\treturn new MailFacade(\n\t\t\tlocator.user,\n\t\t\tawait locator.file(),\n\t\t\tlocator.cachingEntityClient,\n\t\t\tlocator.crypto,\n\t\t\tlocator.serviceExecutor,\n\t\t\tawait locator.blob(),\n\t\t\tfileApp,\n\t\t)\n\t})\n\tconst nativePushFacade = new NativePushFacadeSendDispatcher(worker)\n\tlocator.calendar = lazyMemoized(async () => {\n\t\tconst { CalendarFacade } = await import(\"./facades/lazy/CalendarFacade.js\")\n\t\treturn new CalendarFacade(\n\t\t\tlocator.user,\n\t\t\tawait locator.groupManagement(),\n\t\t\tassertNotNull(cache),\n\t\t\tnativePushFacade,\n\t\t\tmainInterface.operationProgressTracker,\n\t\t\tlocator.instanceMapper,\n\t\t\tlocator.serviceExecutor,\n\t\t\tlocator.crypto,\n\t\t\tmainInterface.infoMessageHandler,\n\t\t)\n\t})\n\n\tlocator.mailAddress = lazyMemoized(async () => {\n\t\tconst { MailAddressFacade } = await import(\"./facades/lazy/MailAddressFacade.js\")\n\t\treturn new MailAddressFacade(\n\t\t\tlocator.user,\n\t\t\tawait locator.groupManagement(),\n\t\t\tlocator.serviceExecutor,\n\t\t\tnew EntityClient(entityRestClient), // without cache\n\t\t)\n\t})\n\tconst scheduler = new SchedulerImpl(dateProvider, self, self)\n\n\tconst eventBusCoordinator = new EventBusEventCoordinator(\n\t\tworker,\n\t\tmainInterface.wsConnectivityListener,\n\t\tlocator.mail,\n\t\tlocator.indexer,\n\t\tlocator.user,\n\t\tlocator.cachingEntityClient,\n\t\tmainInterface.eventController,\n\t)\n\n\tlocator.eventBusClient = new EventBusClient(\n\t\teventBusCoordinator,\n\t\tcache ?? new AdminClientDummyEntityRestCache(),\n\t\tlocator.user,\n\t\tlocator.cachingEntityClient,\n\t\tlocator.instanceMapper,\n\t\t(path) => new WebSocket(getWebsocketOrigin() + path),\n\t\tnew SleepDetector(scheduler, dateProvider),\n\t\tmainInterface.progressTracker,\n\t)\n\tlocator.login.init(locator.eventBusClient)\n\tlocator.Const = Const\n\tlocator.share = lazyMemoized(async () => {\n\t\tconst { ShareFacade } = await import(\"./facades/lazy/ShareFacade.js\")\n\t\treturn new ShareFacade(locator.user, locator.crypto, locator.serviceExecutor, locator.cachingEntityClient)\n\t})\n\tlocator.giftCards = lazyMemoized(async () => {\n\t\tconst { GiftCardFacade } = await import(\"./facades/lazy/GiftCardFacade.js\")\n\t\treturn new GiftCardFacade(locator.user, await locator.customer(), locator.serviceExecutor, locator.crypto)\n\t})\n\tlocator.configFacade = lazyMemoized(async () => {\n\t\tconst { ConfigurationDatabase } = await import(\"./facades/lazy/ConfigurationDatabase.js\")\n\t\treturn new ConfigurationDatabase(locator.user)\n\t})\n\tlocator.contactFormFacade = lazyMemoized(async () => {\n\t\tconst { ContactFormFacade } = await import(\"./facades/lazy/ContactFormFacade.js\")\n\t\treturn new ContactFormFacade(locator.restClient, locator.instanceMapper)\n\t})\n}\n\nconst RETRY_TIMOUT_AFTER_INIT_INDEXER_ERROR_MS = 30000\n\nfunction initIndexer(worker: WorkerImpl, cacheInfo: CacheInfo): Promise<void> {\n\treturn locator.indexer().then((indexer) => {\n\t\treturn indexer\n\t\t\t.init({\n\t\t\t\tuser: assertNotNull(locator.user.getUser()),\n\t\t\t\tuserGroupKey: locator.user.getUserGroupKey(),\n\t\t\t\tcacheInfo,\n\t\t\t})\n\t\t\t.catch(\n\t\t\t\tofClass(ServiceUnavailableError, () => {\n\t\t\t\t\tconsole.log(\"Retry init indexer in 30 seconds after ServiceUnavailableError\")\n\t\t\t\t\treturn delay(RETRY_TIMOUT_AFTER_INIT_INDEXER_ERROR_MS).then(() => {\n\t\t\t\t\t\tconsole.log(\"_initIndexer after ServiceUnavailableError\")\n\t\t\t\t\t\treturn initIndexer(worker, cacheInfo)\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(\n\t\t\t\tofClass(ConnectionError, () => {\n\t\t\t\t\tconsole.log(\"Retry init indexer in 30 seconds after ConnectionError\")\n\t\t\t\t\treturn delay(RETRY_TIMOUT_AFTER_INIT_INDEXER_ERROR_MS).then(() => {\n\t\t\t\t\t\tconsole.log(\"_initIndexer after ConnectionError\")\n\t\t\t\t\t\treturn initIndexer(worker, cacheInfo)\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch((e) => {\n\t\t\t\tworker.sendError(e)\n\t\t\t})\n\t})\n}\n\nexport async function resetLocator(): Promise<void> {\n\tawait locator.login.resetSession()\n\tawait initLocator(locator.login.worker, locator._browserData)\n}\n\nif (typeof self !== \"undefined\") {\n\t;(self as unknown as WorkerGlobalScope).locator = locator // export in worker scope\n}\n","import type { Commands } from \"../common/MessageDispatcher\"\nimport { errorToObj, MessageDispatcher, Request, WorkerTransport } from \"../common/MessageDispatcher\"\nimport { CryptoError } from \"../common/error/CryptoError\"\nimport { BookingFacade } from \"./facades/lazy/BookingFacade.js\"\nimport { NotAuthenticatedError } from \"../common/error/RestError\"\nimport { ProgrammingError } from \"../common/error/ProgrammingError\"\nimport { initLocator, locator, resetLocator } from \"./WorkerLocator\"\nimport { assertWorkerOrNode, isMainOrNode } from \"../common/Env\"\nimport type { ContactFormFacade } from \"./facades/lazy/ContactFormFacade.js\"\nimport type { BrowserData } from \"../../misc/ClientConstants\"\nimport { CryptoFacade } from \"./crypto/CryptoFacade\"\nimport type { GiftCardFacade } from \"./facades/lazy/GiftCardFacade.js\"\nimport type { LoginFacade, LoginListener } from \"./facades/LoginFacade\"\nimport type { CustomerFacade } from \"./facades/lazy/CustomerFacade.js\"\nimport type { GroupManagementFacade } from \"./facades/lazy/GroupManagementFacade.js\"\nimport { ConfigurationDatabase } from \"./facades/lazy/ConfigurationDatabase.js\"\nimport { CalendarFacade } from \"./facades/lazy/CalendarFacade.js\"\nimport { MailFacade } from \"./facades/lazy/MailFacade.js\"\nimport { ShareFacade } from \"./facades/lazy/ShareFacade.js\"\nimport { CounterFacade } from \"./facades/lazy/CounterFacade.js\"\nimport type { Indexer } from \"./search/Indexer\"\nimport { SearchFacade } from \"./search/SearchFacade\"\nimport { MailAddressFacade } from \"./facades/lazy/MailAddressFacade.js\"\nimport { FileFacade } from \"./facades/lazy/FileFacade.js\"\nimport { UserManagementFacade } from \"./facades/lazy/UserManagementFacade.js\"\nimport { DelayedImpls, exposeLocalDelayed, exposeRemote } from \"../common/WorkerProxy\"\nimport type { DeviceEncryptionFacade } from \"./facades/DeviceEncryptionFacade\"\nimport { random } from \"@tutao/tutanota-crypto\"\nimport type { NativeInterface } from \"../../native/common/NativeInterface\"\nimport type { EntityRestInterface } from \"./rest/EntityRestClient\"\nimport { RestClient } from \"./rest/RestClient\"\nimport { IServiceExecutor } from \"../common/ServiceRequest.js\"\nimport { BlobFacade } from \"./facades/lazy/BlobFacade.js\"\nimport { ExposedCacheStorage } from \"./rest/DefaultEntityRestCache.js\"\nimport { BlobAccessTokenFacade } from \"./facades/BlobAccessTokenFacade.js\"\nimport { WebsocketConnectivityListener } from \"../../misc/WebsocketConnectivityModel.js\"\nimport { EventBusClient } from \"./EventBusClient.js\"\nimport { EntropyFacade } from \"./facades/EntropyFacade.js\"\nimport { ExposedProgressTracker } from \"../main/ProgressTracker.js\"\nimport { ExposedEventController } from \"../main/EventController.js\"\nimport { ExposedOperationProgressTracker } from \"../main/OperationProgressTracker.js\"\nimport { WorkerFacade } from \"./facades/WorkerFacade.js\"\nimport { InfoMessageHandler } from \"../../gui/InfoMessageHandler.js\"\n\nassertWorkerOrNode()\n\nexport interface WorkerRandomizer {\n\tgenerateRandomNumber(numBytes: number): Promise<number>\n}\n\nexport interface ExposedEventBus {\n\ttryReconnect: EventBusClient[\"tryReconnect\"]\n\tclose: EventBusClient[\"close\"]\n}\n\n/** Interface of the facades exposed by the worker, basically interface for the worker itself */\nexport interface WorkerInterface {\n\treadonly loginFacade: LoginFacade\n\treadonly customerFacade: CustomerFacade\n\treadonly giftCardFacade: GiftCardFacade\n\treadonly groupManagementFacade: GroupManagementFacade\n\treadonly configFacade: ConfigurationDatabase\n\treadonly calendarFacade: CalendarFacade\n\treadonly mailFacade: MailFacade\n\treadonly shareFacade: ShareFacade\n\treadonly counterFacade: CounterFacade\n\treadonly indexerFacade: Indexer\n\treadonly searchFacade: SearchFacade\n\treadonly bookingFacade: BookingFacade\n\treadonly mailAddressFacade: MailAddressFacade\n\treadonly fileFacade: FileFacade\n\treadonly blobAccessTokenFacade: BlobAccessTokenFacade\n\treadonly blobFacade: BlobFacade\n\treadonly userManagementFacade: UserManagementFacade\n\treadonly contactFormFacade: ContactFormFacade\n\treadonly deviceEncryptionFacade: DeviceEncryptionFacade\n\treadonly restInterface: EntityRestInterface\n\treadonly serviceExecutor: IServiceExecutor\n\treadonly cryptoFacade: CryptoFacade\n\treadonly cacheStorage: ExposedCacheStorage\n\treadonly random: WorkerRandomizer\n\treadonly eventBus: ExposedEventBus\n\treadonly entropyFacade: EntropyFacade\n\treadonly workerFacade: WorkerFacade\n}\n\n/** Interface for the \"main\"/webpage context of the app, interface for the worker client. */\nexport interface MainInterface {\n\treadonly loginListener: LoginListener\n\treadonly wsConnectivityListener: WebsocketConnectivityListener\n\treadonly progressTracker: ExposedProgressTracker\n\treadonly eventController: ExposedEventController\n\treadonly operationProgressTracker: ExposedOperationProgressTracker\n\treadonly infoMessageHandler: InfoMessageHandler\n}\n\ntype WorkerRequest = Request<WorkerRequestType>\n\nexport class WorkerImpl implements NativeInterface {\n\tprivate readonly _scope: DedicatedWorkerGlobalScope\n\tprivate readonly _dispatcher: MessageDispatcher<MainRequestType, WorkerRequestType>\n\n\tconstructor(self: DedicatedWorkerGlobalScope) {\n\t\tthis._scope = self\n\t\tthis._dispatcher = new MessageDispatcher(new WorkerTransport(this._scope), this.queueCommands(this.exposedInterface))\n\t}\n\n\tasync init(browserData: BrowserData): Promise<void> {\n\t\tawait initLocator(this, browserData)\n\t\tconst workerScope = this._scope\n\n\t\t// only register oncaught error handler if we are in the *real* worker scope\n\t\t// Otherwise uncaught error handler might end up in an infinite loop for test cases.\n\t\tif (workerScope && !isMainOrNode()) {\n\t\t\tworkerScope.addEventListener(\"unhandledrejection\", (event: PromiseRejectionEvent) => {\n\t\t\t\tthis.sendError(event.reason)\n\t\t\t})\n\n\t\t\t// @ts-ignore\n\t\t\tworkerScope.onerror = (e: string | Event, source, lineno, colno, error) => {\n\t\t\t\tconsole.error(\"workerImpl.onerror\", e, source, lineno, colno, error)\n\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tthis.sendError(error)\n\t\t\t\t} else {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tconst err = new Error(e)\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\terr.lineNumber = lineno\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\terr.columnNumber = colno\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\terr.fileName = source\n\t\t\t\t\tthis.sendError(err)\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\tget exposedInterface(): DelayedImpls<WorkerInterface> {\n\t\treturn {\n\t\t\tasync loginFacade() {\n\t\t\t\treturn locator.login\n\t\t\t},\n\n\t\t\tasync customerFacade() {\n\t\t\t\treturn locator.customer()\n\t\t\t},\n\n\t\t\tasync giftCardFacade() {\n\t\t\t\treturn locator.giftCards()\n\t\t\t},\n\n\t\t\tasync groupManagementFacade() {\n\t\t\t\treturn locator.groupManagement()\n\t\t\t},\n\n\t\t\tasync configFacade() {\n\t\t\t\treturn locator.configFacade()\n\t\t\t},\n\n\t\t\tasync calendarFacade() {\n\t\t\t\treturn locator.calendar()\n\t\t\t},\n\n\t\t\tasync mailFacade() {\n\t\t\t\treturn locator.mail()\n\t\t\t},\n\n\t\t\tasync shareFacade() {\n\t\t\t\treturn locator.share()\n\t\t\t},\n\n\t\t\tasync counterFacade() {\n\t\t\t\treturn locator.counters()\n\t\t\t},\n\n\t\t\tasync indexerFacade() {\n\t\t\t\treturn locator.indexer()\n\t\t\t},\n\n\t\t\tasync searchFacade() {\n\t\t\t\treturn locator.search()\n\t\t\t},\n\n\t\t\tasync bookingFacade() {\n\t\t\t\treturn locator.booking()\n\t\t\t},\n\n\t\t\tasync mailAddressFacade() {\n\t\t\t\treturn locator.mailAddress()\n\t\t\t},\n\n\t\t\tasync fileFacade() {\n\t\t\t\treturn locator.file()\n\t\t\t},\n\n\t\t\tasync blobAccessTokenFacade() {\n\t\t\t\treturn locator.blobAccessToken\n\t\t\t},\n\n\t\t\tasync blobFacade() {\n\t\t\t\treturn locator.blob()\n\t\t\t},\n\n\t\t\tasync userManagementFacade() {\n\t\t\t\treturn locator.userManagement()\n\t\t\t},\n\n\t\t\tasync contactFormFacade() {\n\t\t\t\treturn locator.contactFormFacade()\n\t\t\t},\n\n\t\t\tasync deviceEncryptionFacade() {\n\t\t\t\treturn locator.deviceEncryptionFacade\n\t\t\t},\n\n\t\t\tasync restInterface() {\n\t\t\t\treturn locator.cache\n\t\t\t},\n\n\t\t\tasync serviceExecutor() {\n\t\t\t\treturn locator.serviceExecutor\n\t\t\t},\n\n\t\t\tasync cryptoFacade() {\n\t\t\t\treturn locator.crypto\n\t\t\t},\n\n\t\t\tasync cacheStorage() {\n\t\t\t\treturn locator.cacheStorage\n\t\t\t},\n\n\t\t\tasync random() {\n\t\t\t\treturn {\n\t\t\t\t\tasync generateRandomNumber(nbrOfBytes: number) {\n\t\t\t\t\t\treturn random.generateRandomNumber(nbrOfBytes)\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tasync eventBus() {\n\t\t\t\treturn locator.eventBusClient\n\t\t\t},\n\n\t\t\tasync entropyFacade() {\n\t\t\t\treturn locator.entropyFacade\n\t\t\t},\n\n\t\t\tasync workerFacade() {\n\t\t\t\treturn locator.workerFacade\n\t\t\t},\n\t\t}\n\t}\n\n\tqueueCommands(exposedWorker: DelayedImpls<WorkerInterface>): Commands<WorkerRequestType> {\n\t\treturn {\n\t\t\tsetup: async (message) => {\n\t\t\t\tconsole.error(\"WorkerImpl: setup was called after bootstrap! message: \", message)\n\t\t\t},\n\t\t\ttestEcho: (message) =>\n\t\t\t\tPromise.resolve({\n\t\t\t\t\tmsg: \">>> \" + message.args[0].msg,\n\t\t\t\t}),\n\t\t\ttestError: (message) => {\n\t\t\t\tconst errorTypes = {\n\t\t\t\t\tProgrammingError,\n\t\t\t\t\tCryptoError,\n\t\t\t\t\tNotAuthenticatedError,\n\t\t\t\t}\n\t\t\t\t// @ts-ignore\n\t\t\t\tlet ErrorType = errorTypes[message.args[0].errorType]\n\t\t\t\treturn Promise.reject(new ErrorType(`wtf: ${message.args[0].errorType}`))\n\t\t\t},\n\t\t\treset: (message: WorkerRequest) => {\n\t\t\t\treturn resetLocator()\n\t\t\t},\n\t\t\trestRequest: (message: WorkerRequest) => {\n\t\t\t\t// This horror is to add auth headers to the admin client\n\t\t\t\tconst args = message.args as Parameters<RestClient[\"request\"]>\n\t\t\t\tlet [path, method, options] = args\n\t\t\t\toptions = options ?? {}\n\t\t\t\toptions.headers = { ...locator.user.createAuthHeaders(), ...options.headers }\n\t\t\t\treturn locator.restClient.request(path, method, options)\n\t\t\t},\n\n\t\t\tfacade: exposeLocalDelayed<DelayedImpls<WorkerInterface>, WorkerRequestType>(exposedWorker),\n\t\t}\n\t}\n\n\tinvokeNative(requestType: string, args: ReadonlyArray<unknown>): Promise<any> {\n\t\treturn this._dispatcher.postRequest(new Request(\"execNative\", [requestType, args]))\n\t}\n\n\tgetMainInterface(): MainInterface {\n\t\treturn exposeRemote<MainInterface>((request) => this._dispatcher.postRequest(request))\n\t}\n\n\tsendError(e: Error): Promise<void> {\n\t\treturn this._dispatcher.postRequest(new Request(\"error\", [errorToObj(e)]))\n\t}\n}\n","/// <reference no-default-lib=\"true\"/>\n/// <reference lib=\"ES2020\" />\n/// <reference lib=\"webworker\" />\nimport { WorkerImpl } from \"./WorkerImpl\"\nimport { Logger, replaceNativeLogger } from \"../common/Logger\"\n\n/**\n * Receives the first message from the client and initializes the WorkerImpl to receive all future messages. Sends a response to the client on this first message.\n */\nself.onmessage = function (msg) {\n\tconst data = msg.data\n\n\tif (data.requestType === \"setup\") {\n\t\tself.env = data.args[0]\n\t\treplaceNativeLogger(self, new Logger())\n\t\tPromise.resolve()\n\t\t\t.then(async () => {\n\t\t\t\tconst initialRandomizerEntropy = data.args[1]\n\t\t\t\tconst browserData = data.args[2]\n\n\t\t\t\tif (initialRandomizerEntropy == null || browserData == null) {\n\t\t\t\t\tthrow new Error(\"Invalid Worker arguments\")\n\t\t\t\t}\n\n\t\t\t\t// @ts-ignore\n\t\t\t\tconst workerImpl = new WorkerImpl(typeof self !== \"undefined\" ? self : null)\n\t\t\t\tawait workerImpl.init(browserData)\n\t\t\t\tworkerImpl.exposedInterface.entropyFacade().then((entropyFacade) => entropyFacade.addEntropy(initialRandomizerEntropy))\n\t\t\t\tself.postMessage({\n\t\t\t\t\tid: data.id,\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tvalue: {},\n\t\t\t\t})\n\t\t\t})\n\t\t\t.catch((e) => {\n\t\t\t\tself.postMessage({\n\t\t\t\t\tid: data.id,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\terror: JSON.stringify({\n\t\t\t\t\t\tname: \"Error\",\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tstack: e.stack,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t})\n\t} else {\n\t\tthrow new Error(\"worker not yet ready. Request type: \" + data.requestType)\n\t}\n}\n","import type { PrivateKey, PublicKey, Randomizer, RsaKeyPair } from \"@tutao/tutanota-crypto\"\nimport type { RsaImplementation } from \"../../api/worker/crypto/RsaImplementation\"\nimport { NativeCryptoFacade } from \"../common/generatedipc/NativeCryptoFacade\"\n\nexport class RsaApp implements RsaImplementation {\n\tconstructor(private readonly nativeCryptoFacade: NativeCryptoFacade, private readonly rng: Randomizer) {}\n\n\tgenerateKey(): Promise<RsaKeyPair> {\n\t\tconst seed = this.rng.generateRandomData(512)\n\n\t\treturn this.nativeCryptoFacade.generateRsaKey(seed)\n\t}\n\n\t/**\n\t * Encrypt bytes with the provided publicKey\n\t */\n\tasync encrypt(publicKey: PublicKey, bytes: Uint8Array): Promise<Uint8Array> {\n\t\tconst seed = this.rng.generateRandomData(32)\n\n\t\treturn await this.nativeCryptoFacade.rsaEncrypt(publicKey, bytes, seed)\n\t}\n\n\t/**\n\t * Decrypt bytes with the provided privateKey\n\t */\n\tasync decrypt(privateKey: PrivateKey, bytes: Uint8Array): Promise<Uint8Array> {\n\t\treturn await this.nativeCryptoFacade.rsaDecrypt(privateKey, bytes)\n\t}\n}\n","import { getDayShifted, getStartOfDay } from \"@tutao/tutanota-utils\"\n\nexport interface DateProvider {\n\tgetStartOfDayShiftedBy(shiftByDays: number): Date\n}\n\nexport class LocalTimeDateProvider implements DateProvider {\n\tgetStartOfDayShiftedBy(shiftByDays: number): Date {\n\t\treturn getStartOfDay(getDayShifted(new Date(), shiftByDays))\n\t}\n}\n"],"names":["value","buf","stringToUtf8Uint8Array","h1","i","len","dtv","DataView","buffer","byteOffset","remainder","byteLength","bytes","x86mix32","getUint32","k1","mul32","x86hash32c1","rol32","x86hash32c2","x86fmix32","db","oss","os","deleteObjectStore","e","console","warn","encryptionKey","privateKey","iv","aes128Encrypt","hexToUint8Array","_keyArrayToHex","_base64ToBigInt","modulus","privateExponent","primeP","primeQ","primeExponentP","primeExponentQ","crtCoefficient","_privateKeyToArray","privateKeyToHex","random","generateRandomData","IV_BYTE_LENGTH","key","bitArrayToUint8Array","fixedIv","slice","length","aes256Encrypt","uint8ArrayToBitArray","aes128Decrypt","concat","publicKey","_publicKeyToArray","MOVABLE_EVENT_TYPE_REFS","MailTypeRef","CustomerInfoTypeRef","isMovableEventType","event","some","typeRef","isSameTypeRefByAttr","application","type","batchMod","batch","entityId","batchAsUpdateData","isSameId","instanceId","operation","containsEventOfType","ProgrammingError","EventQueue","constructor","optimizationEnabled","queueAction","this","_eventQueue","_lastOperationForEntity","Map","_queueAction","_optimizationEnabled","_processingBatch","_paused","addBatches","batches","add","batchId","groupId","events","newEvents","newBatch","_optimizingAddEvents","push","update","set","start","newEvent","elementId","lastBatchForEntity","get","newEntityModification","lastEntityModification","_replace","deleteEvent","assertNotNull","getEventOfType","oldDelete","unshift","firstMoveIndex","findIndex","queuedBatch","firstMoveBatch","createEvent","remove","removeEventsForInstance","JSON","stringify","startIndex","findAllAndRemove","batchInThePast","_processNext","queueSize","next","then","shift","delete","catch","ServiceUnavailableError","ConnectionError","error","clear","splice","k","keys","pause","resume","newMod","filter","exports","ProgressMonitorDelegate","progressTracker","totalAmount","ref","registerMonitor","async","amount","workDoneForMonitor","assertWorkerOrNode","RETRY_AFTER_SERVICE_UNAVAILABLE_ERROR_MS","RECONNECT_INTERVAL","Object","freeze","SMALL","MEDIUM","LARGE","EventBusClient","listener","cache","userFacade","entity","instanceMapper","socketFactory","sleepDetector","immediateReconnect","lastAntiphishingMarkersId","serviceUnavailableRetry","failedConnectionAttempts","state","lastEntityEventIds","lastAddedBatchForGroup","socket","reconnectTimer","connectTimer","progressMonitor","NoopProgressMonitor","eventQueue","modification","eventQueueCallback","entityUpdateMessageQueue","entityUpdateMessageQueueCallback","reset","connect","connectMode","log","onWebsocketStateChanged","completed","eventGroups","workDone","authHeaders","createAuthHeaders","path","sysModelInfo","version","tutanotaModelInfo","env","versionNumber","getLoggedInUser","_id","accessToken","unsubscribeFromOldWebsocket","onopen","onOpen","onclose","onClose","onerror","onError","onmessage","message","onMessage","tryReconnect","closeOption","terminate","_a","close","closeIfOpen","enableAutomaticState","delay","clearTimeout","setTimeout","reconnect","p","initEntityEvents","data","split","decryptAndMapToInstance","resolveTypeReference","WebsocketEntityDataTypeRef","parse","eventBatchId","eventBatchOwner","eventBatch","counterData","WebsocketCounterDataTypeRef","onCounterChanged","PhishingMarkerWebsocketDataTypeRef","lastId","onPhishingMarkersReceived","markers","WebsocketLeaderStatusTypeRef","setLeaderStatus","onLeaderStatusChanged","createWebsocketLeaderStatus","leaderStatus","stop","serverCode","code","NotAuthorizedError","CODE","AccessDeactivatedError","AccessBlockedError","includes","handleRestError","SessionExpiredError","isFullyLoggedIn","reconnectionInterval","SECOND_MS","randomIntFromInterval","existingConnection","size","loadMissedEntityEvents","initOnNewConnection","ofClass","CancelledError","promise","OutOfSyncError","purgeStorage","lastIds","someIdsWereCached","retrieveLastEntityEventIds","recordSyncTime","cachedBatchId","getLastEntityEventBatchForGroup","loadRange","EntityEventBatchTypeRef","GENERATED_MAX_ID","getElementId","GENERATED_MIN_ID","setLastEntityEventBatchForGroup","checkOutOfSync","eventBatches","loadEntityEventsForGroup","addBatch","loadAll","getLastEventBatchIdOrMinIdForGroup","isOutOfSync","processEventBatch","lastForGroup","firstBiggerThanSecond","identity","readyState","WebSocket","OPEN","CLOSED","CLOSING","index","binarySearch","compareOldestFirst","wasAdded","isTerminated","filteredEvents","entityEventsReceived","onEntityEventsReceived","retryPromise","lastThrow","memberships","membership","groupType","GroupType","MailingList","map","group","userGroup","IGNORED_TYPES","PermissionTypeRef","BucketPermissionTypeRef","SessionTypeRef","SecondFactorTypeRef","RecoverCodeTypeRef","RejectedSenderTypeRef","DefaultEntityRestCache","entityRestClient","storage","id","queryParameters","extraHeaders","listId","expandId","cachedEntity","load","isIgnoredType","put","loadMultiple","elementIds","_loadMultiple","setup","instance","options","setupMultiple","instances","erase","getLastBatchIdForGroup","putLastBatchIdForGroup","timeSinceLastSync","timeSinceLastSyncMs","timestamp","getServerTimestampMs","putLastUpdateTime","lastUpdate","getLastUpdateTime","lastUpdateTime","time","getRestClient","deleteFromCacheIfExists","deleteIfExists","ids","entitiesInCache","idsToLoad","items","entitiesFromServer","entities","count","reverse","getCustomCacheHandlerMap","has","typeModel","values","ValueType","GeneratedId","isCachedType","lockRangesDbAccess","range","getRangeForList","populateNewListWithRange","startId","upper","lower","isStartIdWithinRange","isRangeRequestAwayFromExistingRange","extendTowardsRange","extendAwayFromRange","extendFromWithinRange","provideFromRange","unlockRangesDbAccess","setNewRangeForList","updateRangeInStorage","newStart","newCount","recalculateRangeRequest","loadStartId","requestCount","Math","max","isElementIdInCacheRange","countRequested","wasReverseRequest","receivedEntities","elementsToAdd","setLowerRangeForList","getFirstOrThrow","setUpperRangeForList","Promise","all","element","allRangeList","getIdsInRange","elementsToRead","startElementId","indexOfStart","indexOf","createUpdatesForLETs","regularUpdates","updatesArray","getUpdateInstanceId","instanceListId","isSameTypeRef","TypeRef","createUpdatesForLETsPerList","groupBy","postMultipleEventUpdates","updates","firstUpdate","customHandlers","idsInCacheRange","getElementIdsInCacheRange","updatesNotInCacheRange","returnedInstances","returnedIds","otherEventUpdates","handledUpdate","processUpdateEvent","mail","mailDetails","MailDetailsBlobTypeRef","processCreateEvent","flat","isExpectedErrorForSynchronization","cached","newEntity","UserTypeRef","handleUpdatedUser","oldUser","getUserId","newUser","removedShips","difference","l","r","ship","deleteAllOwnedBy","ret","NotFoundError","app","typeRefToPath","toLowerCase","EntityRestClient","_crypto","lazyCrypto","authDataProvider","restClient","blobAccessTokenFacade","ownerKey","queryParams","headers","_validateAndPrepareRestRequest","json","request","responseType","migratedEntity","applyMigrations","sessionKey","resolveSessionKeyWithOwnerKey","resolveSessionKey","SessionKeyNotFoundError","applyMigrationsForInstance","rangeRequestParams","String","undefined","Type","ListElement","Error","_handleLoadMultipleResult","idChunks","splitInChunks","LOAD_MULTIPLE_LIMIT","loadedChunks","promiseMap","idChunk","join","BlobElement","loadMultipleBlobElements","archiveId","doBlobRequestWithRetry","blobServerAccessInfo","requestReadTokenArchive","additionalRequestParams","assign","allParams","createQueryParams","tryServers","servers","serverUrl","baseUrl","noCORS","evictArchiveToken","loadedEntities","model","PushIdentifierTypeRef","concurrency","_decryptMapAndMigrate","decryptedInstance","_type","sk","setNewOwnerEncSessionKey","encryptedEntity","encryptAndMapToLiteral","persistencePostReturn","body","generatedId","instanceChunks","POST_MULTIPLE_LIMIT","errors","failedInstances","instanceChunk","encryptedEntities","parseSetupMultiple","PayloadTooLargeError","Boolean","isOfflineError","SetupMultipleError","_verifyType","encrypted","LoginIncompleteError","name","NotAuthenticatedError","v","resolve","result","mapper","errorMsg","server","url","InternalServerError","doBlobRequest","doEvictTokenBeforeRetry","sjcl","cipher","hash","keyexchange","mode","misc","codec","exception","corrupt","toString","invalid","bug","notReady","aes","_tables","_precompute","j","tmp","encKey","decKey","sbox","decTable","keyLen","rcon","_key","prototype","encrypt","_crypt","decrypt","x","xInv","x2","x4","s","tEnc","tDec","encTable","sboxInv","d","th","input","dir","a2","b2","c2","a","b","c","nInnerRounds","kIndex","out","table","t0","t1","t2","t3","bitArray","bitSlice","bstart","bend","_shiftRight","clamp","extract","blength","sh","floor","a1","last","getPartial","bitLength","ceil","partial","_end","round","equal","carry","last2","shift2","pop","_xor4","y","byteswapM","utf8String","fromBits","arr","bl","fromCharCode","decodeURIComponent","escape","toBits","str","unescape","encodeURIComponent","charCodeAt","base32","_chars","_hexChars","BITS","BASE","REMAINING","_noEquals","_hex","bits","ta","charAt","replace","toUpperCase","format","base32hex","base64","_url","substr","sha256","_h","_buffer","_length","finalize","blockSize","_init","ol","nl","Uint32Array","_block","subarray","h","factor","isPrime","prime","frac","pow","w","h0","h2","h3","h4","h5","h6","h7","sha512","_initr","_keyr","frac2","words","wrh","wrl","h0h","h0l","h1h","h1l","h2h","h2l","h3h","h3l","h4h","h4l","h5h","h5l","h6h","h6l","h7h","h7l","Array","ah","al","bh","ch","cl","dh","dl","eh","el","fh","fl","gh","gl","hh","hl","gamma0xh","gamma0xl","gamma0h","gamma0l","gamma1xh","gamma1xl","gamma1h","gamma1l","wr7h","wr7l","wr16h","wr16l","chh","chl","majh","majl","sigma0h","sigma0l","sigma1h","sigma1l","krh","krl","t1l","t1h","t2l","sha1","_f","t","_S","n","cbc","prp","plaintext","adata","usePadding","xor","bp","output","ciphertext","bi","bo","gcm","prf","tlen","_ctrMode","tag","_galoisMultiply","Zi","Vi","lsb_Vi","_ghash","H","Y0","Yi","J0","S0","enc","ctr","abl","ivbl","hmac","Hash","_hash","exKey","bs","_baseHash","_resultHash","mac","_updated","digest","prng","defaultParanoia","_pools","_poolEntropy","_reseedCount","_robins","_eventId","_collectorIds","_collectorIdNext","_strength","_poolStrength","_nextReseed","_counter","_defaultParanoia","_NOT_READY","_READY","_REQUIRES_RESEED","_MAX_WORDS_PER_BURST","_PARANOIA_LEVELS","_MILLISECONDS_PER_RESEED","_BITS_PER_RESEED","randomWords","nwords","paranoia","g","readiness","isReady","_reseedFromPools","_gate","_gen4words","addEntropy","estimatedEntropy","source","Date","valueOf","robin","objName","err","call","entropyRequired","_cipher","_reseed","seedWords","full","reseedData","strength","arrayBuffer","padding","padding_count","smallest","ArrayBuffer","setUint32","setUint8","getUint8","inView","CryptoError","super","stack","entropyCache","forEach","entry","entropy","addStaticEntropy","byte","nbrOfBytes","nbrOfWords","Uint8Array","generateRandomNumber","sha256Hash","uint8Array","createAuthVerifier","passwordKey","createAuthVerifierAsBase64Url","base64ToBase64Url","uint8ArrayToBase64","uint8ArrayToArrayBuffer","keyToBase64","base64ToKey","keyToUint8Array","base64ToUint8Array","ENABLE_MAC","KEY_LENGTH_BYTES_AES_256","KEY_LENGTH_BITS_AES_256","KEY_LENGTH_BYTES_AES_128","KEY_LENGTH_BITS_AES_128","MAC_ENABLED_PREFIX","MAC_LENGTH_BYTES","aes256RandomKey","useMac","verifyKeySize","subKeys","getAes256SubKeys","encryptedBits","cKey","macBytes","mKey","aes256Decrypt","encryptedBytes","cipherTextWithoutMac","providedMacBytes","computedMacBytes","arrayEquals","decrypted","aes128RandomKey","getAes128SubKeys","hashedKey","sha512Hash","bCrypt","GENSALT_DEFAULT_LOG2_ROUNDS","BCRYPT_SALT_LEN","BLOWFISH_NUM_ROUNDS","MAX_EXECUTION_TIME","P_orig","S_orig","bf_crypt_ciphertext","base64_code","index_64","P","S","lr","offp","KeyLength","getByte","encode_base64","c1","off","rs","char64","decode_base64","maxolen","c3","c4","o","slen","olen","encipher","streamtoword","word","init_key","plen","ekskey","crypt_raw","password","salt","log_rounds","rounds","cdata","clen","obj","roundFunction","logRounds","generateRandomSalt","generateKeyFromPassphrase","passphrase","keyLengthType","passphraseBytes","saltBytes","signedBytes","_uint8ArrayToSignedBytes","Int8Array","b128","unsignedBytes","from","SecureRandom","nextBytes","array","dbits","BigInteger","fromNumber","fromString","nbi","navigator","appName","am","xl","xh","m","DB","DM","DV","FV","F1","F2","rr","vv","BI_RM","BI_RC","int2char","intAt","nbv","fromInt","nbits","Classic","Montgomery","mp","invDigit","mpl","mph","um","mt2","op_and","op_or","op_xor","op_andnot","lbit","cbit","NullExp","nNop","convert","compareTo","mod","revert","reduce","divRemTo","mulTo","multiplyTo","sqrTo","squareTo","abs","dlShiftTo","ZERO","subTo","copyTo","u0","drShiftTo","fromRadix","mi","lShiftTo","cbs","bm","ds","rShiftTo","min","q","pm","pt","ts","ms","nsh","ys","y0","yt","d1","d2","ONE","qd","isEven","exp","z","r2","negate","toRadix","km","modPowInt","lowprimes","lplim","parseBigInt","RSAKey","dmp1","dmq1","coeff","chunkSize","LN2","signum","cs","intValue","dMultiply","dAddOffset","testBit","bitwiseTo","shiftLeft","isProbablePrime","op","f","changeBit","addTo","multiplyLowerTo","multiplyUpperTo","modInt","millerRabin","n1","subtract","getLowestSetBit","shiftRight","modPow","clone","byteValue","shortValue","toByteArray","equals","and","or","andNot","not","bitCount","setBit","clearBit","flipBit","multiply","divide","divideAndRemainder","ans","xHex","eHex","mHex","base","s6","dup","copy_","isZero","divInt_","digitsStr","substring","bigInt2str","str2bigInt","k2","kn","np","s7","copyInt_","radix","inverseModInt","multMod_","s3","bpe","mont_","one","equalsInt","squareMod_","powMod_","int2bigInt","expand","trim","modInverse","ac","u","gcd","square","doPublic","setPublic","N","E","parseInt","alert","text","ba","rng","pkcs1pad2","doPrivate","xp","xq","setPrivate","D","setPrivateEx","Q","DP","DQ","C","generate","B","qs","ee","p1","q1","phi","ctext","pkcs1unpad2","mask","s0","s4","s5","sa","negative","greaterShift","kx","ky","minSize","buff","kk","multInt_","addInt_","rightShift_","leftShift_","linCombShift_","addShift_","subShift_","mod_","y1","y2","divide_","ui","ks","greater","sub_","RSA_KEY_LENGTH_BITS","RSA_PUBLIC_EXPONENT","rsaEncrypt","seed","rsa","publicExponent","paddedBytes","keyLength","hashLength","block","blockLength","defHash","nbrOfZeros","_getPSBlock","dbMask","mgf1","seedMask","oaepPad","bigInt","uint8ArrayToHex","_padAndUnpadLeadingZeros","rsaDecrypt","oaepUnpad","targetByteLength","byteArray","lastExtraByte","counter","T","base64ToHex","_hexLen","string","hexLen","hex","param","_hexToKeyArray","pos","nextParamLen","_validateKeyLength","hexToPrivateKey","privateKeyHex","int8ArrayToBase64","hexToPublicKey","publicKeyHex","encryptKey","decryptKey","decryptRsaKey","encryptedPrivateKey","DIGITS_POWER","TotpVerifier","digits","_digits","generateSecret","readableKey","generateTotp","timeHex","msg","hmac_sha","offset","static","encryptBytes","encryptString","CryptoFacade","entityClient","serviceExecutor","ownerEncSessionKeysUpdateQueue","sessionKeyCache","GroupInfoTypeRef","_ownerGroup","customerGroupMembership","find","Customer","customerGroupKey","getGroupKey","listPermissions","customerGroupPermission","listKey","symEncSessionKey","groupInfoSk","_listEncSessionKey","getGroup","_ownerEncSessionKey","TutanotaPropertiesTypeRef","migrationData","createEncryptTutanotaPropertiesData","getUserGroupId","groupEncSessionKey","getUserGroupKey","properties","post","EncryptTutanotaPropertiesService","updateOwnerEncSessionKey","instanceType","downcast","ContactTypeRef","contact","birthdayIso","oldBirthdayAggregate","birthdayToIsoDate","oldBirthdayDate","LockedError","noOp","oldBirthdayToBirthday","resolveSessionKeyForInstance","getElementIdFromInstance","bucketKey","convertBucketKeyToInstanceIfNecessary","resolveWithBucketKey","hasGroup","gk","ownerEncSessionKey","getGroupId","Mail","permissions","_permissions","trySymmetricPermission","resolveWithPublicOrExternalPermission","isTuple","setSessionKeyCacheWithTuple","mailDetailsDraft","setSessionKeyCacheWithElementId","bucketKeyInstanceOrLiteral","isLiteralInstance","bucketKeyTypeModel","BucketKeyTypeRef","elementOrLiteral","isArray","idTuple","elementIdPart","symmetricPermission","instanceElementId","decBucketKey","keyGroup","pubEncBucketKey","decryptBucketKeyWithKeyPairOfGroup","groupEncBucketKey","neverNull","resolvedSessionKeyForInstance","instanceSessionKeys","collectAllInstanceSessionKeys","updateInstanceSessionKeys","bucketEncSessionKeys","instanceSessionKey","decryptedSessionKey","instanceSessionKeyWithOwnerEncSessionKey","createInstanceSessionKey","pubOrExtPermission","typeName","bucketPermission","bucket","bucketPermissions","decryptWithExternalBucket","decryptWithPublicBucket","ownerEncBucketKey","symEncBucketKey","bucketEncSessionKey","keyPairGroupId","GroupTypeRef","keypair","privKey","symEncPrivKey","bucketPermissionOwnerGroupKey","bucketPermissionGroupKey","updateWithSymPermissionKey","resolveServiceSessionKey","_ownerPublicEncSessionKey","decryptedBytes","keyToEncryptSessionKey","effectiveKeyToEncryptSessionKey","encryptBucketKeyForInternalRecipient","recipientMailAddress","notFoundRecipients","keyData","createPublicKeyData","mailAddress","PublicKeyService","publicKeyData","pubKey","uint8ArrayBucketKey","createInternalRecipientKeyData","pubKeyVersion","TooManyRequestsError","RecipientNotResolvedError","permission","permissionOwnerGroupKey","permissionGroupKey","isLeader","updateService","createUpdatePermissionKeyData","UpdatePermissionKeyService","ownerGroupKey","getSessionKeyCache","defineProperty","alt","getOwnPropertyNames","configurable","writable","LoginFacade","worker","loginListener","cryptoFacade","cacheInitializer","entropyFacade","databaseKeyFactory","loginRequestSessionId","loggingInPromiseWrapper","asyncLoginState","init","eventBusClient","deInitCache","clientIdentifier","sessionType","databaseKey","isPartiallyLoggedIn","userPassphraseKey","loadUserPassphraseKey","authVerifier","createSessionData","createCreateSessionData","accessKey","createSessionReturn","SessionService","sessionData","waitUntilSecondFactorApprovedOrCancelled","forceNewDatabase","generateKey","cacheInfo","initCache","userId","timeRangeDays","user","userGroupInfo","initSession","sessionId","credentials","login","encryptedPassword","isPersistent","getSessionListId","getSessionElementId","challenges","onSecondFactorChallenge","waitUntilSecondFactorApproved","defer","race","retryOnNetworkError","secondFactorAuthGetData","createSecondFactorAuthGetData","secondFactorAuthGetReturn","SecondFactorAuthService","secondFactorPending","persistentSession","authToken","secondFactorAuthDeleteData","createSecondFactorAuthDeleteData","session","reject","externalUserSalt","getUser","setAccessToken","getSessionId","isNewOfflineDb","accountType","AccountType","PREMIUM","finishResumeSession","resetSession","reason","setUser","onPartialLoginSuccess","groupInfo","asyncResumeSession","onLoginFailure","sendError","loadSessionData","utf8Uint8ArrayToString","checkOutdatedExternalSalt","userIdFromFormerLogin","_b","checkOutdatedPassword","wasFullyLoggedIn","unlockUserGroupKey","loadEntropy","storeEntropy","onFullLoginSuccess","initialize","deInitialize","latestSaltHash","externalAuthInfo","AccessExpiredError","verifier","deleteSession","saltRequest","createSaltData","SaltService","saltReturn","sessionTypeModel","byteAccessToken","base64UrlToBase64","GENERATED_ID_BYTES_LENGTH","base64ToBase64Ext","loadRoot","tutanotaProperties","groupEncEntropy","oldPassword","newPassword","oldAuthVerifier","pwEncUserGroupKey","service","createChangePasswordData","oldVerifier","ChangePasswordService","takeover","createDeleteCustomerData","userSalt","undelete","customer","takeoverMailAddress","CustomerService","decryptUserPassword","deviceToken","getData","createAutoLoginDataGet","AutoLoginService","returnData","deviceKey","recoverLogin","recoverCode","recoverCodeKey","recoverCodeVerifier","recoverCodeVerifierBase64","eventRestClient","EntityClient","auth","groupKey","recoverCodeEncUserGroupKey","newPasswordVerifier","postData","finally","resetSecondFactors","passphraseReturn","deleteData","createResetFactorsDeleteData","ResetFactorsService","takeOverDeletedAddress","targetAccountMailAddress","createTakeOverDeletedAddressData","TakeOverDeletedAddressService","generateTotpSecret","getTotpVerifier","totp","generateTotpCode","TAG","RestClient","suspensionHandler","serverTimeOffsetMs","method","debug","self","verbose","isWorker","checkRequestSizeLimit","isSuspended","deferRequest","origin","getApiOrigin","addParamsToUrl","URL","xhr","XMLHttpRequest","open","setHeaders","abortAfterTimeout","res","timeoutId","abortFunction","usingTimeoutAbort","abort","timeout","onload","saveServerTimeOffsetFromRequest","status","response","suspensionTime","getResponseHeader","isSuspensionResponse","suspensionBehavior","SuspensionError","activateSuspensionIfInactive","Number","logFailedRequest","upload","onprogress","pe","progressListener","lengthComputable","total","loaded","ontimeout","onabort","download","statusText","send","isWebClient","isAndroidApp","serverTimestamp","serverTime","getTime","isNaN","now","timeOffset","isAdminClient","limit","REQUEST_SIZE_LIMIT_MAP","REQUEST_SIZE_LIMIT_DEFAULT","setRequestHeader","urlParams","typedEntries","searchParams","statusCode","suspensionTimeNumberString","args","logBody","SuspensionHandler","infoMessageHandler","systemTimeout","_isSuspended","_suspendedUntil","_deferredRequests","_hasSentInfoMessage","_timeout","suspensionDurationSeconds","suspensionStartTime","_onSuspensionComplete","onInfoMessage","translationKey","deferredObject","deferredRequests","deferredRequest","DeviceEncryptionFacade","encryptedData","TutanotaCryptoError","AesApp","nativeCryptoFacade","aesEncryptFile","fileUrl","encodedKey","aesDecryptFile","RsaWeb","generateRsaKey","MAX_INPUT_SIZE","MIN_MATCH","HASH_SHIFT","HASH_SIZE","MF_LIMIT","SKIP_STRENGTH","ML_BITS","ML_MASK","RUN_MASK","HASHER","compress","dest","isize","hashTable","fill","sourcePos","destPos","anchor","step","findMatchAttempts","srcLength","sequenceLowBits","sequenceHighBits","imul","literals_length","match_length","token","ln","InstanceMapper","valueType","decryptValue","_errors","final","associations","associationName","AssociationType","Aggregation","dependency","refType","aggregateTypeModel","aggregation","cardinality","Cardinality","ZeroOrOne","Any","aggregate","decryptedAggregates","decryptedAggregate","encryptValue","Aggregated","encryptedAggregates","encryptedAggregate","valueName","Bytes","dbType","convertJsToDbType","One","CompressedString","valueToDefault","decompressString","convertDbToJsType","compressed","endIndex","end","sizeNeeded","newSize","newOutput","uncompress","AdminClientDummyEntityRestCache","SleepDetector","scheduler","dateProvider","scheduledState","onSleep","scheduledId","schedulePeriodic","check","lastTime","unschedulePeriodic","CustomCacheHandlerMap","handlers","handler","getTypeId","freezeMap","typeId","CustomCalendarEventCacheHandler","CalendarEventTypeRef","rawList","chunk","currentMin","CUSTOM_MIN_ID","CUSTOM_MAX_ID","assertCorrectRange","getWholeList","calendarEvent","sort","EphemeralCacheStorage","lists","blobEntities","customCacheHandlerMap","lastBatchIdPerGroup","deinit","TypeId","Element","_e","_d","_c","elements","_g","allRange","addElementEntity","getFromMap","upperRangeId","lowerRangeId","originalEntity","elementEntity","listElementEntity","listElementTypeRef","putListElement","blobElementEntity","blobTypeRef","putBlobElement","getListId","newCache","insertIntoRange","rangeElement","listCache","owner","typeMap","entries","cacheForType","deleteAllOwnedByFromCache","listIdsToDelete","clearExcludedData","LateInitializedCacheStorageImpl","offlineStorageProvider","_inner","inner","getStorage","ServiceExecutor","params","executeServiceRequest","requestEntity","methodDefinition","getMethodDefinition","return","modelVersion","getModelVersion","encryptDataIfNeeded","decryptResponse","someTypeRef","requestTypeModel","responseTypeModel","resolvedSessionKey","UserFacade","groupKeys","symEncGKey","updateUser","getAllGroupIds","groups","userGroupKey","getMembership","User","getGroupIds","gm","tagSqlValue","untagSqlObject","tagged","mapObject","dateDecoder","customTypeEncoders","typ","Token","negint","uint","customTypeDecoders","tags","TableDefinitions","list_entities","element_entities","ranges","lastUpdateBatchIdPerGroupId","metadata","blob_element_entities","OfflineStorage","sqlCipherFacade","interWindowEventSender","migrator","customCacheHandler","assert","isOfflineStorageAvailable","isTest","isDesktop","localUserDataInvalidated","deleteDb","openDb","createTables","migrate","recreateDbFile","closeDb","preparedQuery","sql","run","query","deleteAllRangesForType","deserialize","getRange","firstIdBigger","row","serializedList","deserializeList","serializedEntity","serialize","ownerGroup","getMetadata","putMetadata","stored","fromEntries","cborg.decode","rows","listIdsByType","groupByAndMapUniquely","listIds","deleteRangeQuery","paramList","deleteEntitiesQuery","encodedValue","cborg.encode","encoded","timeRange","FREE","OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS","cutoffTimestamp","DAY_IN_MILLIS","cutoffId","timestampToGeneratedId","folders","getListElementsOfType","MailFolderTypeRef","folderSystem","FolderSystem","folder","isSpamOrTrashFolder","deleteMailList","mails","definition","mapNullable","updateRangeForList","mailsToDelete","headersToDelete","attachmentsTodelete","mailbodiesToDelete","mailDetailsBlobToDelete","mailDetailsDraftToDelete","attachments","isLegacyMail","isDetailsDraft","mailDetailsId","deleteIn","MailBodyTypeRef","groupByAndMap","listIdPart","MailDetailsDraftTypeRef","MailHeadersTypeRef","FileTypeRef","deleteRange","typeEncoders","deserialized","SqlFragment","queryParts","paramInstances","migrateAllListElements","migrations","migration","migrateAllElements","getElementsOfType","renameAttribute","oldName","newName","deleteInstancesOfType","deleteAllOfType","OFFLINE_STORAGE_MIGRATIONS","addOwnerToElementEntities","addOwnerToListEntities","attribute","GiftCardTypeRef","createGiftCard","CustomerPropertiesTypeRef","createCustomerProperties","UserSettingsGroupRootTypeRef","createUserSettingsGroupRoot","MailboxPropertiesTypeRef","createMailboxProperties","MailBoxTypeRef","createMail","createMailBox","BookingTypeRef","OfflineStorageMigrator","modelInfos","meta","dumpMetadata","populatedMeta","populateModelVersions","isDbNewerThanCurrentClient","runMigrations","checkStateAfterMigrations","typedKeys","compatibleSince","metaVersion","storedVersion","setStoredModelVersion","isNewDb","newMeta","prepopulateVersionIfNecessary","EntropyFacade","newEntropy","lastEntropyUpdate","sum","entropyData","createEntropyData","EntropyService","BlobAccessTokenFacade","readArchiveCache","BlobAccessTokenCache","readBlobCache","writeCache","archiveDataType","ownerGroupId","makeWriteCacheKey","getToken","tokenRequest","createBlobAccessTokenPostIn","write","createBlobWriteData","archiveOwnerGroup","blobAccessInfo","BlobAccessTokenService","evictWriteToken","evict","referencingInstance","getArchiveId","blobs","instanceIds","createInstanceId","read","createBlobReadData","evictReadBlobsToken","archiveIds","Set","blobAccessToken","loader","expires","newToken","OwnerEncSessionKeysUpdateQueue","debounceTimeoutMs","updateInstanceSessionKeyQueue","invokeUpdateSessionKeyService","debounce","sendUpdateRequest","createUpdateSessionKeysPostIn","ownerEncSessionKeys","UpdateSessionKeysService","EventBusEventCoordinator","connectivityListener","mailFacade","indexer","eventController","updateWebSocketState","onEntityUpdateReceived","addBatchesToQueue","startProcessing","phishingMarkersUpdateReceived","tutanotaError","onCountersUpdateReceived","WorkerFacade","logger","getEntries","html","urlify","module","import","locator","initLocator","browserData","workerFacade","NoZoneDateProvider","mainInterface","getMainInterface","native","isApp","RsaApp","NativeCryptoFacadeSendDispatcher","createRsaImplementation","crypto","_browserData","booking","lazyMemoized","BookingFacade","maybeUninitializedStorage","SqlCipherFacadeSendDispatcher","InterWindowEventFacadeSendDispatcher","cacheStorage","fileApp","NativeFileApp","FileFacadeSendDispatcher","ExportFacadeSendDispatcher","cachingEntityClient","Indexer","I","initIndexer","deviceEncryptionFacade","DatabaseKeyFactory","fb","search","SearchFacade","suggestionFacades","_contact","suggestionFacade","_groupInfo","_whitelabelChildIndexer","_mail","counters","CounterFacade","groupManagement","GroupManagementFacade","G","userManagement","UserManagementFacade","U","operationProgressTracker","CustomerFacade","aesApp","blob","BlobFacade","file","FileFacade","F","MailFacade","M","nativePushFacade","NativePushFacadeSendDispatcher","calendar","CalendarFacade","MailAddressFacade","SchedulerImpl","eventBusCoordinator","wsConnectivityListener","getWebsocketOrigin","Const","share","ShareFacade","giftCards","GiftCardFacade","configFacade","ConfigurationDatabase","contactFormFacade","ContactFormFacade","RETRY_TIMOUT_AFTER_INIT_INDEXER_ERROR_MS","WorkerImpl","_scope","_dispatcher","MessageDispatcher","WorkerTransport","queueCommands","exposedInterface","workerScope","isMainOrNode","addEventListener","lineno","colno","lineNumber","columnNumber","fileName","exposedWorker","testEcho","testError","ErrorType","errorType","resetLocator","restRequest","facade","exposeLocalDelayed","invokeNative","requestType","postRequest","Request","exposeRemote","errorToObj","replaceNativeLogger","Logger","initialRandomizerEntropy","workerImpl","postMessage","getStartOfDayShiftedBy","shiftByDays","getStartOfDay","getDayShifted"],"mappings":"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,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,WAAY,UAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,SAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,UAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,QAAY,UAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,SAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,UAAY,WAAY,UAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,SAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,SAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,SAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,UAAY,UAAY,UAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,UAAY,UAChD,WAAY,WAAY,WAAY,WAAY,UAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,UAAY,UAAY,UAChD,WAAY,UAAY,WAAY,UAAY,WAChD,WAAY,SAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,UAAY,UAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,SAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,UAAY,WAAY,WAAY,UAChD,WAAY,SAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,UAAY,UAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,UAChD,UAAY,WAAY,UAAY,UAAY,UAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,UAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,SAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,UAChD,UAAY,WAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,SAAY,WAAY,SAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,UAAY,UAChD,SAAY,WAAY,WAAY,WAAY,UAChD,UAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,UAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,UAAY,UAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,QAChD,UAAY,UAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,UAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,UAAY,SAAY,WAChD,SAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,SAAY,SAAY,WAAY,WAChD,WAAY,WAAY,UAAY,UAAY,WAChD,UAAY,WAAY,WAAY,SAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,UAAY,UAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,UAAY,WAChD,UAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY,UAAY,SAAY,UAAY,WAChD,UAAY,WAAY,WAAY,UAAY,SAChD,UAAY,WAAY,WAAY,WAAY,UAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,UAAY,WAChD,WAAY,WAAY,SAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,UAChD,WAAY,UAAY,UAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,WAChD,UAAY,WAAY,WAAY,UAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,UAAY,WAAY,WAAY,WAAY,WAChD,WAAY,WAAY,UAAY,WAAY,UAChD,WAAY,WAAY,UAAY,WAAY,WAChD,WAAY,WAAY,WAAY,WAAY,WAChD,WAAY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"}