UNPKG

matrix-react-sdk

Version:
127 lines (120 loc) 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildAndEncodePickleKey = buildAndEncodePickleKey; exports.encryptPickleKey = encryptPickleKey; exports.getPickleAdditionalData = getPickleAdditionalData; var _base = require("matrix-js-sdk/src/base64"); var _logger = require("matrix-js-sdk/src/logger"); /* Copyright 2024 New Vector Ltd. Copyright 2020-2024 The Matrix.org Foundation C.I.C. Copyright 2018 New Vector Ltd Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ // Note: we don't import the base64 utils from `matrix-js-sdk/src/matrix` because this file // is used by Element Web's service worker, and importing `matrix` brings in ~1mb of stuff // we don't need. Instead, we ignore the import restriction and only bring in what we actually // need. // Note: `base64` is not public in the js-sdk, so if it changes/breaks, that's on us. We should // be okay with our frequent tests, locked versioning, etc though. We'll pick up problems well // before release. // eslint-disable-next-line no-restricted-imports /** * Encrypted format of a pickle key, as stored in IndexedDB. */ /** * Calculates the `additionalData` for the AES-GCM key used by the pickling processes. This * additional data is *not* encrypted, but *is* authenticated. The additional data is constructed * from the user ID and device ID provided. * * The later-constructed pickle key is used to decrypt values, such as access tokens, from IndexedDB. * * See https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams for more information on * `additionalData`. * * @param {string} userId The user ID who owns the pickle key. * @param {string} deviceId The device ID which owns the pickle key. * @return {Uint8Array} The additional data as a Uint8Array. */ function getPickleAdditionalData(userId, deviceId) { const additionalData = new Uint8Array(userId.length + deviceId.length + 1); for (let i = 0; i < userId.length; i++) { additionalData[i] = userId.charCodeAt(i); } additionalData[userId.length] = 124; // "|" for (let i = 0; i < deviceId.length; i++) { additionalData[userId.length + 1 + i] = deviceId.charCodeAt(i); } return additionalData; } /** * Encrypt the given pickle key, ready for storage in the database. * * @param pickleKey - The key to be encrypted. * @param userId - The user ID the pickle key belongs to. * @param deviceId - The device ID the pickle key belongs to. * * @returns Data object ready for storing in indexeddb. */ async function encryptPickleKey(pickleKey, userId, deviceId) { if (!crypto?.subtle) { return undefined; } const cryptoKey = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]); const iv = new Uint8Array(32); crypto.getRandomValues(iv); const additionalData = getPickleAdditionalData(userId, deviceId); const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData }, cryptoKey, pickleKey); return { encrypted, iv, cryptoKey }; } /** * Decrypts the provided data into a pickle key and base64-encodes it ready for use elsewhere. * * If `data` is undefined in part or in full, returns undefined. * * If crypto functions are not available, returns undefined regardless of input. * * @param data An object containing the encrypted pickle key data: encrypted payload, initialization vector (IV), and crypto key. Typically loaded from indexedDB. * @param userId The user ID the pickle key belongs to. * @param deviceId The device ID the pickle key belongs to. * @returns A promise that resolves to the encoded pickle key, or undefined if the key cannot be built and encoded. */ async function buildAndEncodePickleKey(data, userId, deviceId) { if (!crypto?.subtle) { return undefined; } if (!data || !data.encrypted || !data.iv || !data.cryptoKey) { return undefined; } try { const additionalData = getPickleAdditionalData(userId, deviceId); const pickleKeyBuf = await crypto.subtle.decrypt({ name: "AES-GCM", iv: data.iv, additionalData }, data.cryptoKey, data.encrypted); if (pickleKeyBuf) { return (0, _base.encodeUnpaddedBase64)(pickleKeyBuf); } } catch (e) { _logger.logger.error("Error decrypting pickle key"); } return undefined; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZSIsInJlcXVpcmUiLCJfbG9nZ2VyIiwiZ2V0UGlja2xlQWRkaXRpb25hbERhdGEiLCJ1c2VySWQiLCJkZXZpY2VJZCIsImFkZGl0aW9uYWxEYXRhIiwiVWludDhBcnJheSIsImxlbmd0aCIsImkiLCJjaGFyQ29kZUF0IiwiZW5jcnlwdFBpY2tsZUtleSIsInBpY2tsZUtleSIsImNyeXB0byIsInN1YnRsZSIsInVuZGVmaW5lZCIsImNyeXB0b0tleSIsImdlbmVyYXRlS2V5IiwibmFtZSIsIml2IiwiZ2V0UmFuZG9tVmFsdWVzIiwiZW5jcnlwdGVkIiwiZW5jcnlwdCIsImJ1aWxkQW5kRW5jb2RlUGlja2xlS2V5IiwiZGF0YSIsInBpY2tsZUtleUJ1ZiIsImRlY3J5cHQiLCJlbmNvZGVVbnBhZGRlZEJhc2U2NCIsImUiLCJsb2dnZXIiLCJlcnJvciJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy91dGlscy90b2tlbnMvcGlja2xpbmcudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMjAtMjAyNCBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuQ29weXJpZ2h0IDIwMTggTmV3IFZlY3RvciBMdGRcbkNvcHlyaWdodCAyMDE2IEF2aXJhbCBEYXNndXB0YVxuQ29weXJpZ2h0IDIwMTYgT3Blbk1hcmtldCBMdGRcblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuLy8gTm90ZTogd2UgZG9uJ3QgaW1wb3J0IHRoZSBiYXNlNjQgdXRpbHMgZnJvbSBgbWF0cml4LWpzLXNkay9zcmMvbWF0cml4YCBiZWNhdXNlIHRoaXMgZmlsZVxuLy8gaXMgdXNlZCBieSBFbGVtZW50IFdlYidzIHNlcnZpY2Ugd29ya2VyLCBhbmQgaW1wb3J0aW5nIGBtYXRyaXhgIGJyaW5ncyBpbiB+MW1iIG9mIHN0dWZmXG4vLyB3ZSBkb24ndCBuZWVkLiBJbnN0ZWFkLCB3ZSBpZ25vcmUgdGhlIGltcG9ydCByZXN0cmljdGlvbiBhbmQgb25seSBicmluZyBpbiB3aGF0IHdlIGFjdHVhbGx5XG4vLyBuZWVkLlxuLy8gTm90ZTogYGJhc2U2NGAgaXMgbm90IHB1YmxpYyBpbiB0aGUganMtc2RrLCBzbyBpZiBpdCBjaGFuZ2VzL2JyZWFrcywgdGhhdCdzIG9uIHVzLiBXZSBzaG91bGRcbi8vIGJlIG9rYXkgd2l0aCBvdXIgZnJlcXVlbnQgdGVzdHMsIGxvY2tlZCB2ZXJzaW9uaW5nLCBldGMgdGhvdWdoLiBXZSdsbCBwaWNrIHVwIHByb2JsZW1zIHdlbGxcbi8vIGJlZm9yZSByZWxlYXNlLlxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXJlc3RyaWN0ZWQtaW1wb3J0c1xuaW1wb3J0IHsgZW5jb2RlVW5wYWRkZWRCYXNlNjQgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvYmFzZTY0XCI7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbG9nZ2VyXCI7XG5cbi8qKlxuICogRW5jcnlwdGVkIGZvcm1hdCBvZiBhIHBpY2tsZSBrZXksIGFzIHN0b3JlZCBpbiBJbmRleGVkREIuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRW5jcnlwdGVkUGlja2xlS2V5IHtcbiAgICAvKiogVGhlIGVuY3J5cHRlZCBwYXlsb2FkLiAqL1xuICAgIGVuY3J5cHRlZD86IEJ1ZmZlclNvdXJjZTtcblxuICAgIC8qKiBJbml0aWFsaXNhdGlvbiB2ZWN0b3IgZm9yIHRoZSBlbmNyeXB0aW9uLiAqL1xuICAgIGl2PzogQnVmZmVyU291cmNlO1xuXG4gICAgLyoqIFRoZSBlbmNyeXB0aW9uIGtleSB3aGljaCB3YXMgdXNlZCB0byBlbmNyeXB0IHRoZSBwYXlsb2FkLiAqL1xuICAgIGNyeXB0b0tleT86IENyeXB0b0tleTtcbn1cblxuLyoqXG4gKiBDYWxjdWxhdGVzIHRoZSBgYWRkaXRpb25hbERhdGFgIGZvciB0aGUgQUVTLUdDTSBrZXkgdXNlZCBieSB0aGUgcGlja2xpbmcgcHJvY2Vzc2VzLiBUaGlzXG4gKiBhZGRpdGlvbmFsIGRhdGEgaXMgKm5vdCogZW5jcnlwdGVkLCBidXQgKmlzKiBhdXRoZW50aWNhdGVkLiBUaGUgYWRkaXRpb25hbCBkYXRhIGlzIGNvbnN0cnVjdGVkXG4gKiBmcm9tIHRoZSB1c2VyIElEIGFuZCBkZXZpY2UgSUQgcHJvdmlkZWQuXG4gKlxuICogVGhlIGxhdGVyLWNvbnN0cnVjdGVkIHBpY2tsZSBrZXkgaXMgdXNlZCB0byBkZWNyeXB0IHZhbHVlcywgc3VjaCBhcyBhY2Nlc3MgdG9rZW5zLCBmcm9tIEluZGV4ZWREQi5cbiAqXG4gKiBTZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0Flc0djbVBhcmFtcyBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvblxuICogYGFkZGl0aW9uYWxEYXRhYC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdXNlcklkIFRoZSB1c2VyIElEIHdobyBvd25zIHRoZSBwaWNrbGUga2V5LlxuICogQHBhcmFtIHtzdHJpbmd9IGRldmljZUlkIFRoZSBkZXZpY2UgSUQgd2hpY2ggb3ducyB0aGUgcGlja2xlIGtleS5cbiAqIEByZXR1cm4ge1VpbnQ4QXJyYXl9IFRoZSBhZGRpdGlvbmFsIGRhdGEgYXMgYSBVaW50OEFycmF5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGlja2xlQWRkaXRpb25hbERhdGEodXNlcklkOiBzdHJpbmcsIGRldmljZUlkOiBzdHJpbmcpOiBVaW50OEFycmF5IHtcbiAgICBjb25zdCBhZGRpdGlvbmFsRGF0YSA9IG5ldyBVaW50OEFycmF5KHVzZXJJZC5sZW5ndGggKyBkZXZpY2VJZC5sZW5ndGggKyAxKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHVzZXJJZC5sZW5ndGg7IGkrKykge1xuICAgICAgICBhZGRpdGlvbmFsRGF0YVtpXSA9IHVzZXJJZC5jaGFyQ29kZUF0KGkpO1xuICAgIH1cbiAgICBhZGRpdGlvbmFsRGF0YVt1c2VySWQubGVuZ3RoXSA9IDEyNDsgLy8gXCJ8XCJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRldmljZUlkLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGFkZGl0aW9uYWxEYXRhW3VzZXJJZC5sZW5ndGggKyAxICsgaV0gPSBkZXZpY2VJZC5jaGFyQ29kZUF0KGkpO1xuICAgIH1cbiAgICByZXR1cm4gYWRkaXRpb25hbERhdGE7XG59XG5cbi8qKlxuICogRW5jcnlwdCB0aGUgZ2l2ZW4gcGlja2xlIGtleSwgcmVhZHkgZm9yIHN0b3JhZ2UgaW4gdGhlIGRhdGFiYXNlLlxuICpcbiAqIEBwYXJhbSBwaWNrbGVLZXkgLSBUaGUga2V5IHRvIGJlIGVuY3J5cHRlZC5cbiAqIEBwYXJhbSB1c2VySWQgLSBUaGUgdXNlciBJRCB0aGUgcGlja2xlIGtleSBiZWxvbmdzIHRvLlxuICogQHBhcmFtIGRldmljZUlkIC0gVGhlIGRldmljZSBJRCB0aGUgcGlja2xlIGtleSBiZWxvbmdzIHRvLlxuICpcbiAqIEByZXR1cm5zIERhdGEgb2JqZWN0IHJlYWR5IGZvciBzdG9yaW5nIGluIGluZGV4ZWRkYi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGVuY3J5cHRQaWNrbGVLZXkoXG4gICAgcGlja2xlS2V5OiBVaW50OEFycmF5LFxuICAgIHVzZXJJZDogc3RyaW5nLFxuICAgIGRldmljZUlkOiBzdHJpbmcsXG4pOiBQcm9taXNlPEVuY3J5cHRlZFBpY2tsZUtleSB8IHVuZGVmaW5lZD4ge1xuICAgIGlmICghY3J5cHRvPy5zdWJ0bGUpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgY29uc3QgY3J5cHRvS2V5ID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5nZW5lcmF0ZUtleSh7IG5hbWU6IFwiQUVTLUdDTVwiLCBsZW5ndGg6IDI1NiB9LCBmYWxzZSwgW1wiZW5jcnlwdFwiLCBcImRlY3J5cHRcIl0pO1xuICAgIGNvbnN0IGl2ID0gbmV3IFVpbnQ4QXJyYXkoMzIpO1xuICAgIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMoaXYpO1xuXG4gICAgY29uc3QgYWRkaXRpb25hbERhdGEgPSBnZXRQaWNrbGVBZGRpdGlvbmFsRGF0YSh1c2VySWQsIGRldmljZUlkKTtcbiAgICBjb25zdCBlbmNyeXB0ZWQgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmVuY3J5cHQoeyBuYW1lOiBcIkFFUy1HQ01cIiwgaXYsIGFkZGl0aW9uYWxEYXRhIH0sIGNyeXB0b0tleSwgcGlja2xlS2V5KTtcbiAgICByZXR1cm4geyBlbmNyeXB0ZWQsIGl2LCBjcnlwdG9LZXkgfTtcbn1cblxuLyoqXG4gKiBEZWNyeXB0cyB0aGUgcHJvdmlkZWQgZGF0YSBpbnRvIGEgcGlja2xlIGtleSBhbmQgYmFzZTY0LWVuY29kZXMgaXQgcmVhZHkgZm9yIHVzZSBlbHNld2hlcmUuXG4gKlxuICogSWYgYGRhdGFgIGlzIHVuZGVmaW5lZCBpbiBwYXJ0IG9yIGluIGZ1bGwsIHJldHVybnMgdW5kZWZpbmVkLlxuICpcbiAqICBJZiBjcnlwdG8gZnVuY3Rpb25zIGFyZSBub3QgYXZhaWxhYmxlLCByZXR1cm5zIHVuZGVmaW5lZCByZWdhcmRsZXNzIG9mIGlucHV0LlxuICpcbiAqIEBwYXJhbSBkYXRhIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBlbmNyeXB0ZWQgcGlja2xlIGtleSBkYXRhOiBlbmNyeXB0ZWQgcGF5bG9hZCwgaW5pdGlhbGl6YXRpb24gdmVjdG9yIChJViksIGFuZCBjcnlwdG8ga2V5LiBUeXBpY2FsbHkgbG9hZGVkIGZyb20gaW5kZXhlZERCLlxuICogQHBhcmFtIHVzZXJJZCBUaGUgdXNlciBJRCB0aGUgcGlja2xlIGtleSBiZWxvbmdzIHRvLlxuICogQHBhcmFtIGRldmljZUlkIFRoZSBkZXZpY2UgSUQgdGhlIHBpY2tsZSBrZXkgYmVsb25ncyB0by5cbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBlbmNvZGVkIHBpY2tsZSBrZXksIG9yIHVuZGVmaW5lZCBpZiB0aGUga2V5IGNhbm5vdCBiZSBidWlsdCBhbmQgZW5jb2RlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJ1aWxkQW5kRW5jb2RlUGlja2xlS2V5KFxuICAgIGRhdGE6IEVuY3J5cHRlZFBpY2tsZUtleSB8IHVuZGVmaW5lZCxcbiAgICB1c2VySWQ6IHN0cmluZyxcbiAgICBkZXZpY2VJZDogc3RyaW5nLFxuKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgICBpZiAoIWNyeXB0bz8uc3VidGxlKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGlmICghZGF0YSB8fCAhZGF0YS5lbmNyeXB0ZWQgfHwgIWRhdGEuaXYgfHwgIWRhdGEuY3J5cHRvS2V5KSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgYWRkaXRpb25hbERhdGEgPSBnZXRQaWNrbGVBZGRpdGlvbmFsRGF0YSh1c2VySWQsIGRldmljZUlkKTtcbiAgICAgICAgY29uc3QgcGlja2xlS2V5QnVmID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5kZWNyeXB0KFxuICAgICAgICAgICAgeyBuYW1lOiBcIkFFUy1HQ01cIiwgaXY6IGRhdGEuaXYsIGFkZGl0aW9uYWxEYXRhIH0sXG4gICAgICAgICAgICBkYXRhLmNyeXB0b0tleSxcbiAgICAgICAgICAgIGRhdGEuZW5jcnlwdGVkLFxuICAgICAgICApO1xuICAgICAgICBpZiAocGlja2xlS2V5QnVmKSB7XG4gICAgICAgICAgICByZXR1cm4gZW5jb2RlVW5wYWRkZWRCYXNlNjQocGlja2xlS2V5QnVmKTtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKFwiRXJyb3IgZGVjcnlwdGluZyBwaWNrbGUga2V5XCIp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