@metamask/profile-sync-controller 24.0.0 → 25.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -1
- package/dist/controllers/authentication/AuthenticationController.cjs +22 -0
- package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.mjs +22 -0
- package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.cjs +14 -80
- package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.cts +4 -54
- package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.mts +4 -54
- package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.mjs +10 -76
- package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
- package/dist/controllers/user-storage/constants.cjs +0 -3
- package/dist/controllers/user-storage/constants.cjs.map +1 -1
- package/dist/controllers/user-storage/constants.d.cts +0 -2
- package/dist/controllers/user-storage/constants.d.cts.map +1 -1
- package/dist/controllers/user-storage/constants.d.mts +0 -2
- package/dist/controllers/user-storage/constants.d.mts.map +1 -1
- package/dist/controllers/user-storage/constants.mjs +0 -3
- package/dist/controllers/user-storage/constants.mjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs +28 -2
- package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs +28 -2
- package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs.map +1 -1
- package/dist/shared/encryption/constants.cjs +2 -1
- package/dist/shared/encryption/constants.cjs.map +1 -1
- package/dist/shared/encryption/constants.d.cts +1 -0
- package/dist/shared/encryption/constants.d.cts.map +1 -1
- package/dist/shared/encryption/constants.d.mts +1 -0
- package/dist/shared/encryption/constants.d.mts.map +1 -1
- package/dist/shared/encryption/constants.mjs +1 -0
- package/dist/shared/encryption/constants.mjs.map +1 -1
- package/dist/shared/encryption/encryption.cjs +39 -6
- package/dist/shared/encryption/encryption.cjs.map +1 -1
- package/dist/shared/encryption/encryption.d.cts.map +1 -1
- package/dist/shared/encryption/encryption.d.mts.map +1 -1
- package/dist/shared/encryption/encryption.mjs +40 -7
- package/dist/shared/encryption/encryption.mjs.map +1 -1
- package/package.json +8 -7
- package/dist/controllers/user-storage/account-syncing/constants.cjs +0 -51
- package/dist/controllers/user-storage/account-syncing/constants.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/constants.d.cts +0 -4
- package/dist/controllers/user-storage/account-syncing/constants.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/constants.d.mts +0 -4
- package/dist/controllers/user-storage/account-syncing/constants.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/constants.mjs +0 -48
- package/dist/controllers/user-storage/account-syncing/constants.mjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/controller-integration.cjs +0 -271
- package/dist/controllers/user-storage/account-syncing/controller-integration.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts +0 -38
- package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts +0 -38
- package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/controller-integration.mjs +0 -265
- package/dist/controllers/user-storage/account-syncing/controller-integration.mjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.cjs +0 -41
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.cts +0 -8
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.mts +0 -8
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.mjs +0 -37
- package/dist/controllers/user-storage/account-syncing/setup-subscriptions.mjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/sync-utils.cjs +0 -61
- package/dist/controllers/user-storage/account-syncing/sync-utils.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/sync-utils.d.cts +0 -30
- package/dist/controllers/user-storage/account-syncing/sync-utils.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/sync-utils.d.mts +0 -30
- package/dist/controllers/user-storage/account-syncing/sync-utils.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/sync-utils.mjs +0 -55
- package/dist/controllers/user-storage/account-syncing/sync-utils.mjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/types.cjs +0 -3
- package/dist/controllers/user-storage/account-syncing/types.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/types.d.cts +0 -25
- package/dist/controllers/user-storage/account-syncing/types.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/types.d.mts +0 -25
- package/dist/controllers/user-storage/account-syncing/types.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/types.mjs +0 -2
- package/dist/controllers/user-storage/account-syncing/types.mjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/utils.cjs +0 -36
- package/dist/controllers/user-storage/account-syncing/utils.cjs.map +0 -1
- package/dist/controllers/user-storage/account-syncing/utils.d.cts +0 -18
- package/dist/controllers/user-storage/account-syncing/utils.d.cts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/utils.d.mts +0 -18
- package/dist/controllers/user-storage/account-syncing/utils.d.mts.map +0 -1
- package/dist/controllers/user-storage/account-syncing/utils.mjs +0 -31
- package/dist/controllers/user-storage/account-syncing/utils.mjs.map +0 -1
|
@@ -4,7 +4,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
5
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
6
|
};
|
|
7
|
-
var _EncryptorDecryptor_instances, _EncryptorDecryptor_encryptStringV1, _EncryptorDecryptor_decryptStringV1, _EncryptorDecryptor_encrypt, _EncryptorDecryptor_decrypt, _EncryptorDecryptor_getOrGenerateScryptKey;
|
|
7
|
+
var _EncryptorDecryptor_instances, _EncryptorDecryptor_kdfPromiseCache, _EncryptorDecryptor_encryptStringV1, _EncryptorDecryptor_decryptStringV1, _EncryptorDecryptor_encrypt, _EncryptorDecryptor_decrypt, _EncryptorDecryptor_getOrGenerateScryptKey, _EncryptorDecryptor_createKdfCacheKey, _EncryptorDecryptor_performKdfOperation;
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.createSHA256Hash = void 0;
|
|
10
10
|
const aes_1 = require("@noble/ciphers/aes");
|
|
@@ -18,6 +18,8 @@ const utils_2 = require("./utils.cjs");
|
|
|
18
18
|
class EncryptorDecryptor {
|
|
19
19
|
constructor() {
|
|
20
20
|
_EncryptorDecryptor_instances.add(this);
|
|
21
|
+
// Promise cache for ongoing KDF operations to prevent duplicate work
|
|
22
|
+
_EncryptorDecryptor_kdfPromiseCache.set(this, new Map());
|
|
21
23
|
}
|
|
22
24
|
async encryptString(plaintext, password, nativeScryptCrypto) {
|
|
23
25
|
try {
|
|
@@ -78,7 +80,7 @@ class EncryptorDecryptor {
|
|
|
78
80
|
return strSet.size === salts.length;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
|
-
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto) {
|
|
83
|
+
_EncryptorDecryptor_kdfPromiseCache = new WeakMap(), _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto) {
|
|
82
84
|
const { key, salt } = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_getOrGenerateScryptKey).call(this, password, {
|
|
83
85
|
N: constants_1.SCRYPT_N,
|
|
84
86
|
r: constants_1.SCRYPT_r,
|
|
@@ -132,6 +134,7 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
132
134
|
return (0, aes_1.gcm)(key, nonce).decrypt(ciphertext);
|
|
133
135
|
}, _EncryptorDecryptor_getOrGenerateScryptKey = async function _EncryptorDecryptor_getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
|
|
134
136
|
const hashedPassword = createSHA256Hash(password);
|
|
137
|
+
// Check if we already have the key cached
|
|
135
138
|
const cachedKey = salt
|
|
136
139
|
? (0, cache_1.getCachedKeyBySalt)(hashedPassword, salt)
|
|
137
140
|
: (0, cache_1.getCachedKeyGeneratedWithSharedSalt)(hashedPassword);
|
|
@@ -141,23 +144,53 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
141
144
|
salt: cachedKey.salt,
|
|
142
145
|
};
|
|
143
146
|
}
|
|
147
|
+
// Create a unique cache key for this KDF operation
|
|
144
148
|
const newSalt = salt ?? constants_1.SHARED_SALT;
|
|
149
|
+
const cacheKey = __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_createKdfCacheKey).call(this, hashedPassword, o, newSalt, nativeScryptCrypto);
|
|
150
|
+
// Check if there's already an ongoing KDF operation with the same parameters
|
|
151
|
+
const existingPromise = __classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").get(cacheKey);
|
|
152
|
+
if (existingPromise) {
|
|
153
|
+
return existingPromise;
|
|
154
|
+
}
|
|
155
|
+
// Limit cache size to prevent unbounded growth
|
|
156
|
+
if (__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").size >= constants_1.MAX_KDF_PROMISE_CACHE_SIZE) {
|
|
157
|
+
// Remove the oldest entry (first inserted)
|
|
158
|
+
const firstKey = __classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").keys().next().value;
|
|
159
|
+
if (firstKey) {
|
|
160
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").delete(firstKey);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Create and cache the promise for the KDF operation
|
|
164
|
+
const kdfPromise = __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_performKdfOperation).call(this, password, o, newSalt, hashedPassword, nativeScryptCrypto);
|
|
165
|
+
// Cache the promise and set up cleanup
|
|
166
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").set(cacheKey, kdfPromise);
|
|
167
|
+
// Clean up the cache after completion (both success and failure)
|
|
168
|
+
// eslint-disable-next-line no-void
|
|
169
|
+
void kdfPromise.finally(() => {
|
|
170
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").delete(cacheKey);
|
|
171
|
+
});
|
|
172
|
+
return kdfPromise;
|
|
173
|
+
}, _EncryptorDecryptor_createKdfCacheKey = function _EncryptorDecryptor_createKdfCacheKey(hashedPassword, o, salt, nativeScryptCrypto) {
|
|
174
|
+
const saltStr = (0, utils_2.byteArrayToBase64)(salt);
|
|
175
|
+
const hasNative = Boolean(nativeScryptCrypto);
|
|
176
|
+
return `${hashedPassword}:${o.N}:${o.r}:${o.p}:${o.dkLen}:${saltStr}:${hasNative}`;
|
|
177
|
+
}, _EncryptorDecryptor_performKdfOperation = async function _EncryptorDecryptor_performKdfOperation(password, o, salt, hashedPassword, nativeScryptCrypto) {
|
|
145
178
|
let newKey;
|
|
146
179
|
if (nativeScryptCrypto) {
|
|
147
|
-
newKey = await nativeScryptCrypto((0, utils_2.stringToByteArray)(password),
|
|
180
|
+
newKey = await nativeScryptCrypto((0, utils_2.stringToByteArray)(password), salt, o.N, o.r, o.p, o.dkLen);
|
|
148
181
|
}
|
|
149
182
|
else {
|
|
150
|
-
newKey = await (0, scrypt_1.scryptAsync)(password,
|
|
183
|
+
newKey = await (0, scrypt_1.scryptAsync)(password, salt, {
|
|
151
184
|
N: o.N,
|
|
152
185
|
r: o.r,
|
|
153
186
|
p: o.p,
|
|
154
187
|
dkLen: o.dkLen,
|
|
155
188
|
});
|
|
156
189
|
}
|
|
157
|
-
(0, cache_1.setCachedKey)(hashedPassword,
|
|
190
|
+
(0, cache_1.setCachedKey)(hashedPassword, salt, newKey);
|
|
158
191
|
return {
|
|
159
192
|
key: newKey,
|
|
160
|
-
salt
|
|
193
|
+
salt,
|
|
161
194
|
};
|
|
162
195
|
};
|
|
163
196
|
const encryption = new EncryptorDecryptor();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.cjs","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';\n\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n SCRYPT_N,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n} from './constants';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils';\nimport type { NativeScrypt } from '../types/encryption';\n\nexport type EncryptedPayload = {\n // version\n v: '1';\n\n // key derivation function algorithm - scrypt\n t: 'scrypt';\n\n // data\n d: string;\n\n // encryption options - scrypt\n o: {\n N: number;\n r: number;\n p: number;\n dkLen: number;\n };\n\n // Salt options\n saltLen: number;\n};\n\nclass EncryptorDecryptor {\n async encryptString(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n return await this.#encryptStringV1(\n plaintext,\n password,\n nativeScryptCrypto,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to encrypt string - ${errorMessage}`);\n }\n }\n\n async decryptString(\n encryptedDataStr: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n nativeScryptCrypto,\n );\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to decrypt string - ${errorMessage}`);\n }\n }\n\n async #encryptStringV1(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n undefined,\n nativeScryptCrypto,\n );\n\n // Encrypt and prepend salt.\n const plaintextRaw = utf8ToBytes(plaintext);\n const ciphertextAndNonceAndSalt = concatBytes(\n salt,\n this.#encrypt(plaintextRaw, key),\n );\n\n // Convert to Base64\n const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);\n\n const encryptedPayload: EncryptedPayload = {\n v: '1',\n t: 'scrypt',\n d: encryptedData,\n o: {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n saltLen: SCRYPT_SALT_SIZE,\n };\n\n return JSON.stringify(encryptedPayload);\n }\n\n async #decryptStringV1(\n data: EncryptedPayload,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(\n saltLen,\n ciphertextAndNonceAndSalt.length,\n );\n\n // Derive the key.\n const { key } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n },\n salt,\n nativeScryptCrypto,\n );\n\n // Decrypt and return result.\n return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));\n }\n\n getSalt(encryptedDataStr: string) {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n const { d: base64CiphertextAndNonceAndSalt, saltLen } = encryptedData;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n return salt;\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to get salt - ${errorMessage}`);\n }\n }\n\n getIfEntriesHaveDifferentSalts(entries: string[]): boolean {\n const salts = entries\n .map((e) => {\n try {\n return this.getSalt(e);\n } catch {\n return undefined;\n }\n })\n .filter((s): s is Uint8Array => s !== undefined);\n\n const strSet = new Set(salts.map((arr) => arr.toString()));\n return strSet.size === salts.length;\n }\n\n #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {\n const nonce = randomBytes(ALGORITHM_NONCE_SIZE);\n\n // Encrypt and prepend nonce.\n const ciphertext = gcm(key, nonce).encrypt(plaintext);\n\n return concatBytes(nonce, ciphertext);\n }\n\n #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {\n // Create buffers of nonce and ciphertext.\n const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);\n const ciphertext = ciphertextAndNonce.slice(\n ALGORITHM_NONCE_SIZE,\n ciphertextAndNonce.length,\n );\n\n // Decrypt and return result.\n return gcm(key, nonce).decrypt(ciphertext);\n }\n\n async #getOrGenerateScryptKey(\n password: string,\n o: EncryptedPayload['o'],\n salt?: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ) {\n const hashedPassword = createSHA256Hash(password);\n const cachedKey = salt\n ? getCachedKeyBySalt(hashedPassword, salt)\n : getCachedKeyGeneratedWithSharedSalt(hashedPassword);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n const newSalt = salt ?? SHARED_SALT;\n\n let newKey: Uint8Array;\n\n if (nativeScryptCrypto) {\n newKey = await nativeScryptCrypto(\n stringToByteArray(password),\n newSalt,\n o.N,\n o.r,\n o.p,\n o.dkLen,\n );\n } else {\n newKey = await scryptAsync(password, newSalt, {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n });\n }\n\n setCachedKey(hashedPassword, newSalt, newKey);\n\n return {\n key: newKey,\n salt: newSalt,\n };\n }\n}\n\nconst encryption = new EncryptorDecryptor();\nexport default encryption;\n\n/**\n * Receive a SHA256 hash from a given string\n *\n * @param data - input\n * @returns sha256 hash\n */\nexport function createSHA256Hash(data: string): string {\n const hashedData = sha256(data);\n return bytesToHex(hashedData);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"encryption.cjs","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';\n\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n MAX_KDF_PROMISE_CACHE_SIZE,\n SCRYPT_N,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n} from './constants';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils';\nimport type { NativeScrypt } from '../types/encryption';\n\nexport type EncryptedPayload = {\n // version\n v: '1';\n\n // key derivation function algorithm - scrypt\n t: 'scrypt';\n\n // data\n d: string;\n\n // encryption options - scrypt\n o: {\n N: number;\n r: number;\n p: number;\n dkLen: number;\n };\n\n // Salt options\n saltLen: number;\n};\n\nclass EncryptorDecryptor {\n // Promise cache for ongoing KDF operations to prevent duplicate work\n readonly #kdfPromiseCache = new Map<\n string,\n Promise<{ key: Uint8Array; salt: Uint8Array }>\n >();\n\n async encryptString(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n return await this.#encryptStringV1(\n plaintext,\n password,\n nativeScryptCrypto,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to encrypt string - ${errorMessage}`);\n }\n }\n\n async decryptString(\n encryptedDataStr: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n nativeScryptCrypto,\n );\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to decrypt string - ${errorMessage}`);\n }\n }\n\n async #encryptStringV1(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n undefined,\n nativeScryptCrypto,\n );\n\n // Encrypt and prepend salt.\n const plaintextRaw = utf8ToBytes(plaintext);\n const ciphertextAndNonceAndSalt = concatBytes(\n salt,\n this.#encrypt(plaintextRaw, key),\n );\n\n // Convert to Base64\n const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);\n\n const encryptedPayload: EncryptedPayload = {\n v: '1',\n t: 'scrypt',\n d: encryptedData,\n o: {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n saltLen: SCRYPT_SALT_SIZE,\n };\n\n return JSON.stringify(encryptedPayload);\n }\n\n async #decryptStringV1(\n data: EncryptedPayload,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(\n saltLen,\n ciphertextAndNonceAndSalt.length,\n );\n\n // Derive the key.\n const { key } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n },\n salt,\n nativeScryptCrypto,\n );\n\n // Decrypt and return result.\n return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));\n }\n\n getSalt(encryptedDataStr: string) {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n const { d: base64CiphertextAndNonceAndSalt, saltLen } = encryptedData;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n return salt;\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to get salt - ${errorMessage}`);\n }\n }\n\n getIfEntriesHaveDifferentSalts(entries: string[]): boolean {\n const salts = entries\n .map((e) => {\n try {\n return this.getSalt(e);\n } catch {\n return undefined;\n }\n })\n .filter((s): s is Uint8Array => s !== undefined);\n\n const strSet = new Set(salts.map((arr) => arr.toString()));\n return strSet.size === salts.length;\n }\n\n #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {\n const nonce = randomBytes(ALGORITHM_NONCE_SIZE);\n\n // Encrypt and prepend nonce.\n const ciphertext = gcm(key, nonce).encrypt(plaintext);\n\n return concatBytes(nonce, ciphertext);\n }\n\n #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {\n // Create buffers of nonce and ciphertext.\n const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);\n const ciphertext = ciphertextAndNonce.slice(\n ALGORITHM_NONCE_SIZE,\n ciphertextAndNonce.length,\n );\n\n // Decrypt and return result.\n return gcm(key, nonce).decrypt(ciphertext);\n }\n\n async #getOrGenerateScryptKey(\n password: string,\n o: EncryptedPayload['o'],\n salt?: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ) {\n const hashedPassword = createSHA256Hash(password);\n\n // Check if we already have the key cached\n const cachedKey = salt\n ? getCachedKeyBySalt(hashedPassword, salt)\n : getCachedKeyGeneratedWithSharedSalt(hashedPassword);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n // Create a unique cache key for this KDF operation\n const newSalt = salt ?? SHARED_SALT;\n const cacheKey = this.#createKdfCacheKey(\n hashedPassword,\n o,\n newSalt,\n nativeScryptCrypto,\n );\n\n // Check if there's already an ongoing KDF operation with the same parameters\n const existingPromise = this.#kdfPromiseCache.get(cacheKey);\n if (existingPromise) {\n return existingPromise;\n }\n\n // Limit cache size to prevent unbounded growth\n if (this.#kdfPromiseCache.size >= MAX_KDF_PROMISE_CACHE_SIZE) {\n // Remove the oldest entry (first inserted)\n const firstKey = this.#kdfPromiseCache.keys().next().value;\n if (firstKey) {\n this.#kdfPromiseCache.delete(firstKey);\n }\n }\n\n // Create and cache the promise for the KDF operation\n const kdfPromise = this.#performKdfOperation(\n password,\n o,\n newSalt,\n hashedPassword,\n nativeScryptCrypto,\n );\n\n // Cache the promise and set up cleanup\n this.#kdfPromiseCache.set(cacheKey, kdfPromise);\n\n // Clean up the cache after completion (both success and failure)\n // eslint-disable-next-line no-void\n void kdfPromise.finally(() => {\n this.#kdfPromiseCache.delete(cacheKey);\n });\n\n return kdfPromise;\n }\n\n #createKdfCacheKey(\n hashedPassword: string,\n o: EncryptedPayload['o'],\n salt: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ): string {\n const saltStr = byteArrayToBase64(salt);\n const hasNative = Boolean(nativeScryptCrypto);\n return `${hashedPassword}:${o.N}:${o.r}:${o.p}:${o.dkLen}:${saltStr}:${hasNative}`;\n }\n\n async #performKdfOperation(\n password: string,\n o: EncryptedPayload['o'],\n salt: Uint8Array,\n hashedPassword: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<{ key: Uint8Array; salt: Uint8Array }> {\n let newKey: Uint8Array;\n\n if (nativeScryptCrypto) {\n newKey = await nativeScryptCrypto(\n stringToByteArray(password),\n salt,\n o.N,\n o.r,\n o.p,\n o.dkLen,\n );\n } else {\n newKey = await scryptAsync(password, salt, {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n });\n }\n\n setCachedKey(hashedPassword, salt, newKey);\n\n return {\n key: newKey,\n salt,\n };\n }\n}\n\nconst encryption = new EncryptorDecryptor();\nexport default encryption;\n\n/**\n * Receive a SHA256 hash from a given string\n *\n * @param data - input\n * @returns sha256 hash\n */\nexport function createSHA256Hash(data: string): string {\n const hashedData = sha256(data);\n return bytesToHex(hashedData);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.cts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encryption.d.cts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,gCAA4B;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAE7B,CAAC,EAAE,GAAG,CAAC;IAGP,CAAC,EAAE,QAAQ,CAAC;IAGZ,CAAC,EAAE,MAAM,CAAC;IAGV,CAAC,EAAE;QACD,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IAGF,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,cAAM,kBAAkB;;IAOhB,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC;IAaZ,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC;IAoGlB,OAAO,CAAC,gBAAgB,EAAE,MAAM;IA0BhC,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO;CAkJ3D;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;AAC5C,eAAe,UAAU,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encryption.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,gCAA4B;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAE7B,CAAC,EAAE,GAAG,CAAC;IAGP,CAAC,EAAE,QAAQ,CAAC;IAGZ,CAAC,EAAE,MAAM,CAAC;IAGV,CAAC,EAAE;QACD,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IAGF,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,cAAM,kBAAkB;;IAOhB,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC;IAaZ,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC;IAoGlB,OAAO,CAAC,gBAAgB,EAAE,MAAM;IA0BhC,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO;CAkJ3D;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;AAC5C,eAAe,UAAU,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
|
|
@@ -3,18 +3,20 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
3
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
|
-
var _EncryptorDecryptor_instances, _EncryptorDecryptor_encryptStringV1, _EncryptorDecryptor_decryptStringV1, _EncryptorDecryptor_encrypt, _EncryptorDecryptor_decrypt, _EncryptorDecryptor_getOrGenerateScryptKey;
|
|
6
|
+
var _EncryptorDecryptor_instances, _EncryptorDecryptor_kdfPromiseCache, _EncryptorDecryptor_encryptStringV1, _EncryptorDecryptor_decryptStringV1, _EncryptorDecryptor_encrypt, _EncryptorDecryptor_decrypt, _EncryptorDecryptor_getOrGenerateScryptKey, _EncryptorDecryptor_createKdfCacheKey, _EncryptorDecryptor_performKdfOperation;
|
|
7
7
|
import { gcm } from "@noble/ciphers/aes";
|
|
8
8
|
import { randomBytes } from "@noble/ciphers/webcrypto";
|
|
9
9
|
import { scryptAsync } from "@noble/hashes/scrypt";
|
|
10
10
|
import { sha256 } from "@noble/hashes/sha256";
|
|
11
11
|
import { utf8ToBytes, concatBytes, bytesToHex } from "@noble/hashes/utils";
|
|
12
12
|
import { getCachedKeyBySalt, getCachedKeyGeneratedWithSharedSalt, setCachedKey } from "./cache.mjs";
|
|
13
|
-
import { ALGORITHM_KEY_SIZE, ALGORITHM_NONCE_SIZE, SCRYPT_N, SCRYPT_p, SCRYPT_r, SCRYPT_SALT_SIZE, SHARED_SALT } from "./constants.mjs";
|
|
13
|
+
import { ALGORITHM_KEY_SIZE, ALGORITHM_NONCE_SIZE, MAX_KDF_PROMISE_CACHE_SIZE, SCRYPT_N, SCRYPT_p, SCRYPT_r, SCRYPT_SALT_SIZE, SHARED_SALT } from "./constants.mjs";
|
|
14
14
|
import { base64ToByteArray, byteArrayToBase64, bytesToUtf8, stringToByteArray } from "./utils.mjs";
|
|
15
15
|
class EncryptorDecryptor {
|
|
16
16
|
constructor() {
|
|
17
17
|
_EncryptorDecryptor_instances.add(this);
|
|
18
|
+
// Promise cache for ongoing KDF operations to prevent duplicate work
|
|
19
|
+
_EncryptorDecryptor_kdfPromiseCache.set(this, new Map());
|
|
18
20
|
}
|
|
19
21
|
async encryptString(plaintext, password, nativeScryptCrypto) {
|
|
20
22
|
try {
|
|
@@ -75,7 +77,7 @@ class EncryptorDecryptor {
|
|
|
75
77
|
return strSet.size === salts.length;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
|
-
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto) {
|
|
80
|
+
_EncryptorDecryptor_kdfPromiseCache = new WeakMap(), _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto) {
|
|
79
81
|
const { key, salt } = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_getOrGenerateScryptKey).call(this, password, {
|
|
80
82
|
N: SCRYPT_N,
|
|
81
83
|
r: SCRYPT_r,
|
|
@@ -129,6 +131,7 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
129
131
|
return gcm(key, nonce).decrypt(ciphertext);
|
|
130
132
|
}, _EncryptorDecryptor_getOrGenerateScryptKey = async function _EncryptorDecryptor_getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
|
|
131
133
|
const hashedPassword = createSHA256Hash(password);
|
|
134
|
+
// Check if we already have the key cached
|
|
132
135
|
const cachedKey = salt
|
|
133
136
|
? getCachedKeyBySalt(hashedPassword, salt)
|
|
134
137
|
: getCachedKeyGeneratedWithSharedSalt(hashedPassword);
|
|
@@ -138,23 +141,53 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
138
141
|
salt: cachedKey.salt,
|
|
139
142
|
};
|
|
140
143
|
}
|
|
144
|
+
// Create a unique cache key for this KDF operation
|
|
141
145
|
const newSalt = salt ?? SHARED_SALT;
|
|
146
|
+
const cacheKey = __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_createKdfCacheKey).call(this, hashedPassword, o, newSalt, nativeScryptCrypto);
|
|
147
|
+
// Check if there's already an ongoing KDF operation with the same parameters
|
|
148
|
+
const existingPromise = __classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").get(cacheKey);
|
|
149
|
+
if (existingPromise) {
|
|
150
|
+
return existingPromise;
|
|
151
|
+
}
|
|
152
|
+
// Limit cache size to prevent unbounded growth
|
|
153
|
+
if (__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").size >= MAX_KDF_PROMISE_CACHE_SIZE) {
|
|
154
|
+
// Remove the oldest entry (first inserted)
|
|
155
|
+
const firstKey = __classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").keys().next().value;
|
|
156
|
+
if (firstKey) {
|
|
157
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").delete(firstKey);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Create and cache the promise for the KDF operation
|
|
161
|
+
const kdfPromise = __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_performKdfOperation).call(this, password, o, newSalt, hashedPassword, nativeScryptCrypto);
|
|
162
|
+
// Cache the promise and set up cleanup
|
|
163
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").set(cacheKey, kdfPromise);
|
|
164
|
+
// Clean up the cache after completion (both success and failure)
|
|
165
|
+
// eslint-disable-next-line no-void
|
|
166
|
+
void kdfPromise.finally(() => {
|
|
167
|
+
__classPrivateFieldGet(this, _EncryptorDecryptor_kdfPromiseCache, "f").delete(cacheKey);
|
|
168
|
+
});
|
|
169
|
+
return kdfPromise;
|
|
170
|
+
}, _EncryptorDecryptor_createKdfCacheKey = function _EncryptorDecryptor_createKdfCacheKey(hashedPassword, o, salt, nativeScryptCrypto) {
|
|
171
|
+
const saltStr = byteArrayToBase64(salt);
|
|
172
|
+
const hasNative = Boolean(nativeScryptCrypto);
|
|
173
|
+
return `${hashedPassword}:${o.N}:${o.r}:${o.p}:${o.dkLen}:${saltStr}:${hasNative}`;
|
|
174
|
+
}, _EncryptorDecryptor_performKdfOperation = async function _EncryptorDecryptor_performKdfOperation(password, o, salt, hashedPassword, nativeScryptCrypto) {
|
|
142
175
|
let newKey;
|
|
143
176
|
if (nativeScryptCrypto) {
|
|
144
|
-
newKey = await nativeScryptCrypto(stringToByteArray(password),
|
|
177
|
+
newKey = await nativeScryptCrypto(stringToByteArray(password), salt, o.N, o.r, o.p, o.dkLen);
|
|
145
178
|
}
|
|
146
179
|
else {
|
|
147
|
-
newKey = await scryptAsync(password,
|
|
180
|
+
newKey = await scryptAsync(password, salt, {
|
|
148
181
|
N: o.N,
|
|
149
182
|
r: o.r,
|
|
150
183
|
p: o.p,
|
|
151
184
|
dkLen: o.dkLen,
|
|
152
185
|
});
|
|
153
186
|
}
|
|
154
|
-
setCachedKey(hashedPassword,
|
|
187
|
+
setCachedKey(hashedPassword, salt, newKey);
|
|
155
188
|
return {
|
|
156
189
|
key: newKey,
|
|
157
|
-
salt
|
|
190
|
+
salt,
|
|
158
191
|
};
|
|
159
192
|
};
|
|
160
193
|
const encryption = new EncryptorDecryptor();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';\n\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n SCRYPT_N,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n} from './constants';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils';\nimport type { NativeScrypt } from '../types/encryption';\n\nexport type EncryptedPayload = {\n // version\n v: '1';\n\n // key derivation function algorithm - scrypt\n t: 'scrypt';\n\n // data\n d: string;\n\n // encryption options - scrypt\n o: {\n N: number;\n r: number;\n p: number;\n dkLen: number;\n };\n\n // Salt options\n saltLen: number;\n};\n\nclass EncryptorDecryptor {\n async encryptString(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n return await this.#encryptStringV1(\n plaintext,\n password,\n nativeScryptCrypto,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to encrypt string - ${errorMessage}`);\n }\n }\n\n async decryptString(\n encryptedDataStr: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n nativeScryptCrypto,\n );\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to decrypt string - ${errorMessage}`);\n }\n }\n\n async #encryptStringV1(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n undefined,\n nativeScryptCrypto,\n );\n\n // Encrypt and prepend salt.\n const plaintextRaw = utf8ToBytes(plaintext);\n const ciphertextAndNonceAndSalt = concatBytes(\n salt,\n this.#encrypt(plaintextRaw, key),\n );\n\n // Convert to Base64\n const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);\n\n const encryptedPayload: EncryptedPayload = {\n v: '1',\n t: 'scrypt',\n d: encryptedData,\n o: {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n saltLen: SCRYPT_SALT_SIZE,\n };\n\n return JSON.stringify(encryptedPayload);\n }\n\n async #decryptStringV1(\n data: EncryptedPayload,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(\n saltLen,\n ciphertextAndNonceAndSalt.length,\n );\n\n // Derive the key.\n const { key } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n },\n salt,\n nativeScryptCrypto,\n );\n\n // Decrypt and return result.\n return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));\n }\n\n getSalt(encryptedDataStr: string) {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n const { d: base64CiphertextAndNonceAndSalt, saltLen } = encryptedData;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n return salt;\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to get salt - ${errorMessage}`);\n }\n }\n\n getIfEntriesHaveDifferentSalts(entries: string[]): boolean {\n const salts = entries\n .map((e) => {\n try {\n return this.getSalt(e);\n } catch {\n return undefined;\n }\n })\n .filter((s): s is Uint8Array => s !== undefined);\n\n const strSet = new Set(salts.map((arr) => arr.toString()));\n return strSet.size === salts.length;\n }\n\n #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {\n const nonce = randomBytes(ALGORITHM_NONCE_SIZE);\n\n // Encrypt and prepend nonce.\n const ciphertext = gcm(key, nonce).encrypt(plaintext);\n\n return concatBytes(nonce, ciphertext);\n }\n\n #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {\n // Create buffers of nonce and ciphertext.\n const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);\n const ciphertext = ciphertextAndNonce.slice(\n ALGORITHM_NONCE_SIZE,\n ciphertextAndNonce.length,\n );\n\n // Decrypt and return result.\n return gcm(key, nonce).decrypt(ciphertext);\n }\n\n async #getOrGenerateScryptKey(\n password: string,\n o: EncryptedPayload['o'],\n salt?: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ) {\n const hashedPassword = createSHA256Hash(password);\n const cachedKey = salt\n ? getCachedKeyBySalt(hashedPassword, salt)\n : getCachedKeyGeneratedWithSharedSalt(hashedPassword);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n const newSalt = salt ?? SHARED_SALT;\n\n let newKey: Uint8Array;\n\n if (nativeScryptCrypto) {\n newKey = await nativeScryptCrypto(\n stringToByteArray(password),\n newSalt,\n o.N,\n o.r,\n o.p,\n o.dkLen,\n );\n } else {\n newKey = await scryptAsync(password, newSalt, {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n });\n }\n\n setCachedKey(hashedPassword, newSalt, newKey);\n\n return {\n key: newKey,\n salt: newSalt,\n };\n }\n}\n\nconst encryption = new EncryptorDecryptor();\nexport default encryption;\n\n/**\n * Receive a SHA256 hash from a given string\n *\n * @param data - input\n * @returns sha256 hash\n */\nexport function createSHA256Hash(data: string): string {\n const hashedData = sha256(data);\n return bytesToHex(hashedData);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"encryption.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';\n\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n MAX_KDF_PROMISE_CACHE_SIZE,\n SCRYPT_N,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n} from './constants';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils';\nimport type { NativeScrypt } from '../types/encryption';\n\nexport type EncryptedPayload = {\n // version\n v: '1';\n\n // key derivation function algorithm - scrypt\n t: 'scrypt';\n\n // data\n d: string;\n\n // encryption options - scrypt\n o: {\n N: number;\n r: number;\n p: number;\n dkLen: number;\n };\n\n // Salt options\n saltLen: number;\n};\n\nclass EncryptorDecryptor {\n // Promise cache for ongoing KDF operations to prevent duplicate work\n readonly #kdfPromiseCache = new Map<\n string,\n Promise<{ key: Uint8Array; salt: Uint8Array }>\n >();\n\n async encryptString(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n return await this.#encryptStringV1(\n plaintext,\n password,\n nativeScryptCrypto,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to encrypt string - ${errorMessage}`);\n }\n }\n\n async decryptString(\n encryptedDataStr: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n nativeScryptCrypto,\n );\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to decrypt string - ${errorMessage}`);\n }\n }\n\n async #encryptStringV1(\n plaintext: string,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n undefined,\n nativeScryptCrypto,\n );\n\n // Encrypt and prepend salt.\n const plaintextRaw = utf8ToBytes(plaintext);\n const ciphertextAndNonceAndSalt = concatBytes(\n salt,\n this.#encrypt(plaintextRaw, key),\n );\n\n // Convert to Base64\n const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);\n\n const encryptedPayload: EncryptedPayload = {\n v: '1',\n t: 'scrypt',\n d: encryptedData,\n o: {\n N: SCRYPT_N,\n r: SCRYPT_r,\n p: SCRYPT_p,\n dkLen: ALGORITHM_KEY_SIZE,\n },\n saltLen: SCRYPT_SALT_SIZE,\n };\n\n return JSON.stringify(encryptedPayload);\n }\n\n async #decryptStringV1(\n data: EncryptedPayload,\n password: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<string> {\n const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(\n saltLen,\n ciphertextAndNonceAndSalt.length,\n );\n\n // Derive the key.\n const { key } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n },\n salt,\n nativeScryptCrypto,\n );\n\n // Decrypt and return result.\n return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));\n }\n\n getSalt(encryptedDataStr: string) {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n const { d: base64CiphertextAndNonceAndSalt, saltLen } = encryptedData;\n\n // Decode the base64.\n const ciphertextAndNonceAndSalt = base64ToByteArray(\n base64CiphertextAndNonceAndSalt,\n );\n\n // Create buffers of salt and ciphertextAndNonce.\n const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n return salt;\n }\n }\n throw new Error(\n `Unsupported encrypted data payload - ${encryptedDataStr}`,\n );\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(`Unable to get salt - ${errorMessage}`);\n }\n }\n\n getIfEntriesHaveDifferentSalts(entries: string[]): boolean {\n const salts = entries\n .map((e) => {\n try {\n return this.getSalt(e);\n } catch {\n return undefined;\n }\n })\n .filter((s): s is Uint8Array => s !== undefined);\n\n const strSet = new Set(salts.map((arr) => arr.toString()));\n return strSet.size === salts.length;\n }\n\n #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {\n const nonce = randomBytes(ALGORITHM_NONCE_SIZE);\n\n // Encrypt and prepend nonce.\n const ciphertext = gcm(key, nonce).encrypt(plaintext);\n\n return concatBytes(nonce, ciphertext);\n }\n\n #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {\n // Create buffers of nonce and ciphertext.\n const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);\n const ciphertext = ciphertextAndNonce.slice(\n ALGORITHM_NONCE_SIZE,\n ciphertextAndNonce.length,\n );\n\n // Decrypt and return result.\n return gcm(key, nonce).decrypt(ciphertext);\n }\n\n async #getOrGenerateScryptKey(\n password: string,\n o: EncryptedPayload['o'],\n salt?: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ) {\n const hashedPassword = createSHA256Hash(password);\n\n // Check if we already have the key cached\n const cachedKey = salt\n ? getCachedKeyBySalt(hashedPassword, salt)\n : getCachedKeyGeneratedWithSharedSalt(hashedPassword);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n // Create a unique cache key for this KDF operation\n const newSalt = salt ?? SHARED_SALT;\n const cacheKey = this.#createKdfCacheKey(\n hashedPassword,\n o,\n newSalt,\n nativeScryptCrypto,\n );\n\n // Check if there's already an ongoing KDF operation with the same parameters\n const existingPromise = this.#kdfPromiseCache.get(cacheKey);\n if (existingPromise) {\n return existingPromise;\n }\n\n // Limit cache size to prevent unbounded growth\n if (this.#kdfPromiseCache.size >= MAX_KDF_PROMISE_CACHE_SIZE) {\n // Remove the oldest entry (first inserted)\n const firstKey = this.#kdfPromiseCache.keys().next().value;\n if (firstKey) {\n this.#kdfPromiseCache.delete(firstKey);\n }\n }\n\n // Create and cache the promise for the KDF operation\n const kdfPromise = this.#performKdfOperation(\n password,\n o,\n newSalt,\n hashedPassword,\n nativeScryptCrypto,\n );\n\n // Cache the promise and set up cleanup\n this.#kdfPromiseCache.set(cacheKey, kdfPromise);\n\n // Clean up the cache after completion (both success and failure)\n // eslint-disable-next-line no-void\n void kdfPromise.finally(() => {\n this.#kdfPromiseCache.delete(cacheKey);\n });\n\n return kdfPromise;\n }\n\n #createKdfCacheKey(\n hashedPassword: string,\n o: EncryptedPayload['o'],\n salt: Uint8Array,\n nativeScryptCrypto?: NativeScrypt,\n ): string {\n const saltStr = byteArrayToBase64(salt);\n const hasNative = Boolean(nativeScryptCrypto);\n return `${hashedPassword}:${o.N}:${o.r}:${o.p}:${o.dkLen}:${saltStr}:${hasNative}`;\n }\n\n async #performKdfOperation(\n password: string,\n o: EncryptedPayload['o'],\n salt: Uint8Array,\n hashedPassword: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<{ key: Uint8Array; salt: Uint8Array }> {\n let newKey: Uint8Array;\n\n if (nativeScryptCrypto) {\n newKey = await nativeScryptCrypto(\n stringToByteArray(password),\n salt,\n o.N,\n o.r,\n o.p,\n o.dkLen,\n );\n } else {\n newKey = await scryptAsync(password, salt, {\n N: o.N,\n r: o.r,\n p: o.p,\n dkLen: o.dkLen,\n });\n }\n\n setCachedKey(hashedPassword, salt, newKey);\n\n return {\n key: newKey,\n salt,\n };\n }\n}\n\nconst encryption = new EncryptorDecryptor();\nexport default encryption;\n\n/**\n * Receive a SHA256 hash from a given string\n *\n * @param data - input\n * @returns sha256 hash\n */\nexport function createSHA256Hash(data: string): string {\n const hashedData = sha256(data);\n return bytesToHex(hashedData);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/profile-sync-controller",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.1.0",
|
|
4
4
|
"description": "The profile sync helps developers synchronize data across multiple clients and devices in a privacy-preserving way. All data saved in the user storage database is encrypted client-side to preserve privacy. The user storage provides a modular design, giving developers the flexibility to construct and manage their storage spaces in a way that best suits their needs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -100,9 +100,10 @@
|
|
|
100
100
|
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
101
101
|
},
|
|
102
102
|
"dependencies": {
|
|
103
|
-
"@metamask/base-controller": "^8.
|
|
103
|
+
"@metamask/base-controller": "^8.4.0",
|
|
104
104
|
"@metamask/snaps-sdk": "^9.0.0",
|
|
105
105
|
"@metamask/snaps-utils": "^11.0.0",
|
|
106
|
+
"@metamask/utils": "^11.8.1",
|
|
106
107
|
"@noble/ciphers": "^1.3.0",
|
|
107
108
|
"@noble/hashes": "^1.8.0",
|
|
108
109
|
"immer": "^9.0.6",
|
|
@@ -112,11 +113,11 @@
|
|
|
112
113
|
"devDependencies": {
|
|
113
114
|
"@lavamoat/allow-scripts": "^3.0.4",
|
|
114
115
|
"@lavamoat/preinstall-always-fail": "^2.1.0",
|
|
115
|
-
"@metamask/
|
|
116
|
+
"@metamask/address-book-controller": "^6.1.1",
|
|
116
117
|
"@metamask/auto-changelog": "^3.4.4",
|
|
117
|
-
"@metamask/keyring-api": "^
|
|
118
|
-
"@metamask/keyring-controller": "^23.
|
|
119
|
-
"@metamask/keyring-internal-api": "^
|
|
118
|
+
"@metamask/keyring-api": "^21.0.0",
|
|
119
|
+
"@metamask/keyring-controller": "^23.1.0",
|
|
120
|
+
"@metamask/keyring-internal-api": "^9.0.0",
|
|
120
121
|
"@metamask/providers": "^22.1.0",
|
|
121
122
|
"@metamask/snaps-controllers": "^14.0.1",
|
|
122
123
|
"@types/jest": "^27.4.1",
|
|
@@ -132,7 +133,7 @@
|
|
|
132
133
|
"webextension-polyfill": "^0.12.0"
|
|
133
134
|
},
|
|
134
135
|
"peerDependencies": {
|
|
135
|
-
"@metamask/
|
|
136
|
+
"@metamask/address-book-controller": "^6.1.1",
|
|
136
137
|
"@metamask/keyring-controller": "^23.0.0",
|
|
137
138
|
"@metamask/providers": "^22.0.0",
|
|
138
139
|
"@metamask/snaps-controllers": "^14.0.0",
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LOCALIZED_DEFAULT_ACCOUNT_NAMES = exports.USER_STORAGE_VERSION_KEY = exports.USER_STORAGE_VERSION = void 0;
|
|
4
|
-
exports.USER_STORAGE_VERSION = '1';
|
|
5
|
-
// Force cast. We don't really care about the type here since we treat it as a unique symbol
|
|
6
|
-
exports.USER_STORAGE_VERSION_KEY = 'v';
|
|
7
|
-
// We need this in order to know if an account is a default account or not when we do account syncing
|
|
8
|
-
exports.LOCALIZED_DEFAULT_ACCOUNT_NAMES = [
|
|
9
|
-
'Account',
|
|
10
|
-
'መለያ',
|
|
11
|
-
'الحساب',
|
|
12
|
-
'Профил',
|
|
13
|
-
'অ্যাকাউন্ট',
|
|
14
|
-
'Compte',
|
|
15
|
-
'Účet',
|
|
16
|
-
'Konto',
|
|
17
|
-
'Λογαριασμός',
|
|
18
|
-
'Cuenta',
|
|
19
|
-
'حساب',
|
|
20
|
-
'Tili',
|
|
21
|
-
'એકાઉન્ટ',
|
|
22
|
-
'חשבון',
|
|
23
|
-
'अकाउंट',
|
|
24
|
-
'खाता',
|
|
25
|
-
'Račun',
|
|
26
|
-
'Kont',
|
|
27
|
-
'Fiók',
|
|
28
|
-
'Akun',
|
|
29
|
-
'アカウント',
|
|
30
|
-
'ಖಾತೆ',
|
|
31
|
-
'계정',
|
|
32
|
-
'Paskyra',
|
|
33
|
-
'Konts',
|
|
34
|
-
'അക്കൗണ്ട്',
|
|
35
|
-
'खाते',
|
|
36
|
-
'Akaun',
|
|
37
|
-
'Conta',
|
|
38
|
-
'Cont',
|
|
39
|
-
'Счет',
|
|
40
|
-
'налог',
|
|
41
|
-
'Akaunti',
|
|
42
|
-
'கணக்கு',
|
|
43
|
-
'ఖాతా',
|
|
44
|
-
'บัญชี',
|
|
45
|
-
'Hesap',
|
|
46
|
-
'Обліковий запис',
|
|
47
|
-
'Tài khoản',
|
|
48
|
-
'账户',
|
|
49
|
-
'帳戶',
|
|
50
|
-
];
|
|
51
|
-
//# sourceMappingURL=constants.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,oBAAoB,GAAG,GAAG,CAAC;AAExC,4FAA4F;AAC/E,QAAA,wBAAwB,GAAkB,GAAY,CAAC;AAEpE,qGAAqG;AACxF,QAAA,+BAA+B,GAAG;IAC7C,SAAS;IACT,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,MAAM;IACN,OAAO;IACP,aAAa;IACb,QAAQ;IACR,MAAM;IACN,MAAM;IACN,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,IAAI;IACJ,SAAS;IACT,OAAO;IACP,WAAW;IACX,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;IACP,OAAO;IACP,iBAAiB;IACjB,WAAW;IACX,IAAI;IACJ,IAAI;CACI,CAAC","sourcesContent":["export const USER_STORAGE_VERSION = '1';\n\n// Force cast. We don't really care about the type here since we treat it as a unique symbol\nexport const USER_STORAGE_VERSION_KEY: unique symbol = 'v' as never;\n\n// We need this in order to know if an account is a default account or not when we do account syncing\nexport const LOCALIZED_DEFAULT_ACCOUNT_NAMES = [\n 'Account',\n 'መለያ',\n 'الحساب',\n 'Профил',\n 'অ্যাকাউন্ট',\n 'Compte',\n 'Účet',\n 'Konto',\n 'Λογαριασμός',\n 'Cuenta',\n 'حساب',\n 'Tili',\n 'એકાઉન્ટ',\n 'חשבון',\n 'अकाउंट',\n 'खाता',\n 'Račun',\n 'Kont',\n 'Fiók',\n 'Akun',\n 'アカウント',\n 'ಖಾತೆ',\n '계정',\n 'Paskyra',\n 'Konts',\n 'അക്കൗണ്ട്',\n 'खाते',\n 'Akaun',\n 'Conta',\n 'Cont',\n 'Счет',\n 'налог',\n 'Akaunti',\n 'கணக்கு',\n 'ఖాతా',\n 'บัญชี',\n 'Hesap',\n 'Обліковий запис',\n 'Tài khoản',\n '账户',\n '帳戶',\n] as const;\n"]}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const USER_STORAGE_VERSION = "1";
|
|
2
|
-
export declare const USER_STORAGE_VERSION_KEY: unique symbol;
|
|
3
|
-
export declare const LOCALIZED_DEFAULT_ACCOUNT_NAMES: readonly ["Account", "መለያ", "الحساب", "Профил", "অ্যাকাউন্ট", "Compte", "Účet", "Konto", "Λογαριασμός", "Cuenta", "حساب", "Tili", "એકાઉન્ટ", "חשבון", "अकाउंट", "खाता", "Račun", "Kont", "Fiók", "Akun", "アカウント", "ಖಾತೆ", "계정", "Paskyra", "Konts", "അക്കൗണ്ട്", "खाते", "Akaun", "Conta", "Cont", "Счет", "налог", "Akaunti", "கணக்கு", "ఖాతా", "บัญชี", "Hesap", "Обліковий запис", "Tài khoản", "账户", "帳戶"];
|
|
4
|
-
//# sourceMappingURL=constants.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,wBAAwB,EAAE,OAAO,MAAqB,CAAC;AAGpE,eAAO,MAAM,+BAA+B,gZA0ClC,CAAC"}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const USER_STORAGE_VERSION = "1";
|
|
2
|
-
export declare const USER_STORAGE_VERSION_KEY: unique symbol;
|
|
3
|
-
export declare const LOCALIZED_DEFAULT_ACCOUNT_NAMES: readonly ["Account", "መለያ", "الحساب", "Профил", "অ্যাকাউন্ট", "Compte", "Účet", "Konto", "Λογαριασμός", "Cuenta", "حساب", "Tili", "એકાઉન્ટ", "חשבון", "अकाउंट", "खाता", "Račun", "Kont", "Fiók", "Akun", "アカウント", "ಖಾತೆ", "계정", "Paskyra", "Konts", "അക്കൗണ്ട്", "खाते", "Akaun", "Conta", "Cont", "Счет", "налог", "Akaunti", "கணக்கு", "ఖాతా", "บัญชี", "Hesap", "Обліковий запис", "Tài khoản", "账户", "帳戶"];
|
|
4
|
-
//# sourceMappingURL=constants.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,wBAAwB,EAAE,OAAO,MAAqB,CAAC;AAGpE,eAAO,MAAM,+BAA+B,gZA0ClC,CAAC"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export const USER_STORAGE_VERSION = '1';
|
|
2
|
-
// Force cast. We don't really care about the type here since we treat it as a unique symbol
|
|
3
|
-
export const USER_STORAGE_VERSION_KEY = 'v';
|
|
4
|
-
// We need this in order to know if an account is a default account or not when we do account syncing
|
|
5
|
-
export const LOCALIZED_DEFAULT_ACCOUNT_NAMES = [
|
|
6
|
-
'Account',
|
|
7
|
-
'መለያ',
|
|
8
|
-
'الحساب',
|
|
9
|
-
'Профил',
|
|
10
|
-
'অ্যাকাউন্ট',
|
|
11
|
-
'Compte',
|
|
12
|
-
'Účet',
|
|
13
|
-
'Konto',
|
|
14
|
-
'Λογαριασμός',
|
|
15
|
-
'Cuenta',
|
|
16
|
-
'حساب',
|
|
17
|
-
'Tili',
|
|
18
|
-
'એકાઉન્ટ',
|
|
19
|
-
'חשבון',
|
|
20
|
-
'अकाउंट',
|
|
21
|
-
'खाता',
|
|
22
|
-
'Račun',
|
|
23
|
-
'Kont',
|
|
24
|
-
'Fiók',
|
|
25
|
-
'Akun',
|
|
26
|
-
'アカウント',
|
|
27
|
-
'ಖಾತೆ',
|
|
28
|
-
'계정',
|
|
29
|
-
'Paskyra',
|
|
30
|
-
'Konts',
|
|
31
|
-
'അക്കൗണ്ട്',
|
|
32
|
-
'खाते',
|
|
33
|
-
'Akaun',
|
|
34
|
-
'Conta',
|
|
35
|
-
'Cont',
|
|
36
|
-
'Счет',
|
|
37
|
-
'налог',
|
|
38
|
-
'Akaunti',
|
|
39
|
-
'கணக்கு',
|
|
40
|
-
'ఖాతా',
|
|
41
|
-
'บัญชี',
|
|
42
|
-
'Hesap',
|
|
43
|
-
'Обліковий запис',
|
|
44
|
-
'Tài khoản',
|
|
45
|
-
'账户',
|
|
46
|
-
'帳戶',
|
|
47
|
-
];
|
|
48
|
-
//# sourceMappingURL=constants.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,wBAAwB,GAAkB,GAAY,CAAC;AAEpE,qGAAqG;AACrG,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,SAAS;IACT,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,MAAM;IACN,OAAO;IACP,aAAa;IACb,QAAQ;IACR,MAAM;IACN,MAAM;IACN,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,IAAI;IACJ,SAAS;IACT,OAAO;IACP,WAAW;IACX,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;IACP,OAAO;IACP,iBAAiB;IACjB,WAAW;IACX,IAAI;IACJ,IAAI;CACI,CAAC","sourcesContent":["export const USER_STORAGE_VERSION = '1';\n\n// Force cast. We don't really care about the type here since we treat it as a unique symbol\nexport const USER_STORAGE_VERSION_KEY: unique symbol = 'v' as never;\n\n// We need this in order to know if an account is a default account or not when we do account syncing\nexport const LOCALIZED_DEFAULT_ACCOUNT_NAMES = [\n 'Account',\n 'መለያ',\n 'الحساب',\n 'Профил',\n 'অ্যাকাউন্ট',\n 'Compte',\n 'Účet',\n 'Konto',\n 'Λογαριασμός',\n 'Cuenta',\n 'حساب',\n 'Tili',\n 'એકાઉન્ટ',\n 'חשבון',\n 'अकाउंट',\n 'खाता',\n 'Račun',\n 'Kont',\n 'Fiók',\n 'Akun',\n 'アカウント',\n 'ಖಾತೆ',\n '계정',\n 'Paskyra',\n 'Konts',\n 'അക്കൗണ്ട്',\n 'खाते',\n 'Akaun',\n 'Conta',\n 'Cont',\n 'Счет',\n 'налог',\n 'Akaunti',\n 'கணக்கு',\n 'ఖాతా',\n 'บัญชี',\n 'Hesap',\n 'Обліковий запис',\n 'Tài khoản',\n '账户',\n '帳戶',\n] as const;\n"]}
|