@metamask-previews/profile-sync-controller 33.0.0-preview-247ba396c → 33.0.0-preview-9e306636a
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 +1 -0
- package/dist/shared/encryption/cache.d.ts +4 -4
- package/dist/shared/encryption/cache.d.ts.map +1 -1
- package/dist/shared/encryption/cache.js.map +1 -1
- package/dist/shared/encryption/encryption.d.ts.map +1 -1
- package/dist/shared/encryption/encryption.js +19 -21
- package/dist/shared/encryption/encryption.js.map +1 -1
- package/dist/shared/types/encryption.d.ts +1 -1
- package/dist/shared/types/encryption.d.ts.map +1 -1
- package/dist/shared/types/encryption.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Changed
|
|
11
11
|
|
|
12
|
+
- Replace JS AES implementation with `@metamask/cryptography` ([#10621](https://github.com/MetaMask/core/pull/10621))
|
|
12
13
|
- Bump `immer` from `^9.0.21` to `^11.1.18` ([#10382](https://github.com/MetaMask/core/pull/10382))
|
|
13
14
|
|
|
14
15
|
### Fixed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type CachedEntry = {
|
|
2
|
-
salt: Uint8Array
|
|
2
|
+
salt: Uint8Array<ArrayBuffer>;
|
|
3
3
|
base64Salt: string;
|
|
4
|
-
key: Uint8Array
|
|
4
|
+
key: Uint8Array<ArrayBuffer>;
|
|
5
5
|
};
|
|
6
6
|
/**
|
|
7
7
|
* Returns a given cached derived key from a hashed password and salt
|
|
@@ -10,7 +10,7 @@ type CachedEntry = {
|
|
|
10
10
|
* @param salt - provide salt to receive cached key
|
|
11
11
|
* @returns cached key
|
|
12
12
|
*/
|
|
13
|
-
export declare function getCachedKeyBySalt(hashedPassword: string, salt: Uint8Array): CachedEntry | undefined;
|
|
13
|
+
export declare function getCachedKeyBySalt(hashedPassword: string, salt: Uint8Array<ArrayBuffer>): CachedEntry | undefined;
|
|
14
14
|
/**
|
|
15
15
|
* Gets the cached key that was generated without a salt, if it exists.
|
|
16
16
|
* This is unique per hashed password.
|
|
@@ -27,6 +27,6 @@ export declare function getCachedKeyGeneratedWithSharedSalt(hashedPassword: stri
|
|
|
27
27
|
* @param salt - salt to set new derived key
|
|
28
28
|
* @param key - derived key we are setting
|
|
29
29
|
*/
|
|
30
|
-
export declare function setCachedKey(hashedPassword: string, salt: Uint8Array
|
|
30
|
+
export declare function setCachedKey(hashedPassword: string, salt: Uint8Array<ArrayBuffer>, key: Uint8Array<ArrayBuffer>): void;
|
|
31
31
|
export {};
|
|
32
32
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;CAC9B,CAAC;AAiBF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,GAC5B,WAAW,GAAG,SAAS,CAazB;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CACjD,cAAc,EAAE,MAAM,GACrB,WAAW,GAAG,SAAS,CAczB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,EAC7B,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,GAC3B,IAAI,CAeN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAQ/C,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,eAAe,GAAG,GAAG,CAAC;AAQ5B,IAAI,cAAc,GAAyB,EAAE,CAAC;AAC9C,MAAM,gBAAgB,GAAG,CAAC,cAAsB,EAAE,EAAE;IAClD,cAAc,CAAC,cAAc,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;IAC7C,OAAO,cAAc,CAAC,cAAc,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,cAAsB,EACtB,
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAQ/C,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,eAAe,GAAG,GAAG,CAAC;AAQ5B,IAAI,cAAc,GAAyB,EAAE,CAAC;AAC9C,MAAM,gBAAgB,GAAG,CAAC,cAAsB,EAAE,EAAE;IAClD,cAAc,CAAC,cAAc,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;IAC7C,OAAO,cAAc,CAAC,cAAc,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,cAAsB,EACtB,IAA6B;IAE7B,MAAM,KAAK,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,IAAI;QACJ,UAAU;QACV,GAAG,EAAE,SAAS;KACf,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mCAAmC,CACjD,cAAsB;IAEtB,MAAM,KAAK,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAExC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,UAAU;QACV,GAAG,EAAE,SAAS;KACf,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,IAA6B,EAC7B,GAA4B;IAE5B,sBAAsB;IACtB,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC7D,cAAc,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAE3C,kBAAkB;IAClB,IAAI,KAAK,CAAC,IAAI,GAAG,eAAe,EAAE,CAAC;QACjC,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import { SHARED_SALT } from './constants.js';\nimport { byteArrayToBase64 } from './utils.js';\n\ntype CachedEntry = {\n salt: Uint8Array<ArrayBuffer>;\n base64Salt: string;\n key: Uint8Array<ArrayBuffer>;\n};\n\nconst MAX_PASSWORD_CACHES = 100;\nconst MAX_SALT_CACHES = 100;\n\n/**\n * In-Memory Caching derived keys based from a given salt and password.\n */\ntype PasswordMemCachedKDF = {\n [hashedPassword: string]: Map<string, Uint8Array<ArrayBuffer>>;\n};\nlet inMemCachedKDF: PasswordMemCachedKDF = {};\nconst getPasswordCache = (hashedPassword: string) => {\n inMemCachedKDF[hashedPassword] ??= new Map();\n return inMemCachedKDF[hashedPassword];\n};\n\n/**\n * Returns a given cached derived key from a hashed password and salt\n *\n * @param hashedPassword - hashed password for cache lookup\n * @param salt - provide salt to receive cached key\n * @returns cached key\n */\nexport function getCachedKeyBySalt(\n hashedPassword: string,\n salt: Uint8Array<ArrayBuffer>,\n): CachedEntry | undefined {\n const cache = getPasswordCache(hashedPassword);\n const base64Salt = byteArrayToBase64(salt);\n const cachedKey = cache.get(base64Salt);\n if (!cachedKey) {\n return undefined;\n }\n\n return {\n salt,\n base64Salt,\n key: cachedKey,\n };\n}\n\n/**\n * Gets the cached key that was generated without a salt, if it exists.\n * This is unique per hashed password.\n *\n * @param hashedPassword - hashed password for cache lookup\n * @returns the cached key\n */\nexport function getCachedKeyGeneratedWithSharedSalt(\n hashedPassword: string,\n): CachedEntry | undefined {\n const cache = getPasswordCache(hashedPassword);\n const base64Salt = byteArrayToBase64(SHARED_SALT);\n const cachedKey = cache.get(base64Salt);\n\n if (!cachedKey) {\n return undefined;\n }\n\n return {\n salt: SHARED_SALT,\n base64Salt,\n key: cachedKey,\n };\n}\n\n/**\n * Sets a key to the in memory cache.\n * We have set an arbitrary size of 10 cached keys per hashed password.\n *\n * @param hashedPassword - hashed password for cache lookup\n * @param salt - salt to set new derived key\n * @param key - derived key we are setting\n */\nexport function setCachedKey(\n hashedPassword: string,\n salt: Uint8Array<ArrayBuffer>,\n key: Uint8Array<ArrayBuffer>,\n): void {\n // Max password caches\n if (Object.keys(inMemCachedKDF).length > MAX_PASSWORD_CACHES) {\n inMemCachedKDF = {};\n }\n\n const cache = getPasswordCache(hashedPassword);\n const base64Salt = byteArrayToBase64(salt);\n\n // Max salt caches\n if (cache.size > MAX_SALT_CACHES) {\n cache.clear();\n }\n\n cache.set(base64Salt, key);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAuB3D,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,CAUjB;IAEK,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC,CAkBjB;IAiFD,OAAO,CAAC,gBAAgB,EAAE,MAAM,2BAuB/B;IAED,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAazD;CAyIF;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;eAC7B,UAAU;AAEzB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { decrypt, encrypt } from '@metamask/cryptography/aes-gcm';
|
|
2
|
+
import { getErrorMessage, stringToBytes, bytesToHex, concatBytes, remove0x, } from '@metamask/utils';
|
|
3
3
|
import { scryptAsync } from '@noble/hashes/scrypt';
|
|
4
4
|
import { sha256 } from '@noble/hashes/sha256';
|
|
5
|
-
import { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';
|
|
6
5
|
import { getCachedKeyBySalt, getCachedKeyGeneratedWithSharedSalt, setCachedKey, } from './cache.js';
|
|
7
6
|
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.js';
|
|
8
7
|
import { base64ToByteArray, byteArrayToBase64, bytesToUtf8, stringToByteArray, } from './utils.js';
|
|
@@ -14,8 +13,7 @@ class EncryptorDecryptor {
|
|
|
14
13
|
return await this.#encryptStringV1(plaintext, password, nativeScryptCrypto);
|
|
15
14
|
}
|
|
16
15
|
catch (e) {
|
|
17
|
-
|
|
18
|
-
throw new Error(`Unable to encrypt string - ${errorMessage}`);
|
|
16
|
+
throw new Error(`Unable to encrypt string - ${getErrorMessage(e)}`);
|
|
19
17
|
}
|
|
20
18
|
}
|
|
21
19
|
async decryptString(encryptedDataStr, password, nativeScryptCrypto) {
|
|
@@ -29,8 +27,7 @@ class EncryptorDecryptor {
|
|
|
29
27
|
throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
|
|
30
28
|
}
|
|
31
29
|
catch (e) {
|
|
32
|
-
|
|
33
|
-
throw new Error(`Unable to decrypt string - ${errorMessage}`);
|
|
30
|
+
throw new Error(`Unable to decrypt string - ${getErrorMessage(e)}`);
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
async #encryptStringV1(plaintext, password, nativeScryptCrypto) {
|
|
@@ -41,8 +38,11 @@ class EncryptorDecryptor {
|
|
|
41
38
|
dkLen: ALGORITHM_KEY_SIZE,
|
|
42
39
|
}, undefined, nativeScryptCrypto);
|
|
43
40
|
// Encrypt and prepend salt.
|
|
44
|
-
const plaintextRaw =
|
|
45
|
-
const ciphertextAndNonceAndSalt = concatBytes(
|
|
41
|
+
const plaintextRaw = stringToBytes(plaintext);
|
|
42
|
+
const ciphertextAndNonceAndSalt = concatBytes([
|
|
43
|
+
salt,
|
|
44
|
+
await this.#encrypt(plaintextRaw, key),
|
|
45
|
+
]);
|
|
46
46
|
// Convert to Base64
|
|
47
47
|
const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);
|
|
48
48
|
const encryptedPayload = {
|
|
@@ -74,7 +74,7 @@ class EncryptorDecryptor {
|
|
|
74
74
|
dkLen: o.dkLen,
|
|
75
75
|
}, salt, nativeScryptCrypto);
|
|
76
76
|
// Decrypt and return result.
|
|
77
|
-
return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));
|
|
77
|
+
return bytesToUtf8(await this.#decrypt(ciphertextAndNonce, key));
|
|
78
78
|
}
|
|
79
79
|
getSalt(encryptedDataStr) {
|
|
80
80
|
try {
|
|
@@ -92,8 +92,7 @@ class EncryptorDecryptor {
|
|
|
92
92
|
throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
|
|
93
93
|
}
|
|
94
94
|
catch (e) {
|
|
95
|
-
|
|
96
|
-
throw new Error(`Unable to get salt - ${errorMessage}`);
|
|
95
|
+
throw new Error(`Unable to get salt - ${getErrorMessage(e)}`);
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
getIfEntriesHaveDifferentSalts(entries) {
|
|
@@ -110,18 +109,17 @@ class EncryptorDecryptor {
|
|
|
110
109
|
const strSet = new Set(salts.map((arr) => arr.toString()));
|
|
111
110
|
return strSet.size === salts.length;
|
|
112
111
|
}
|
|
113
|
-
#encrypt(plaintext, key) {
|
|
114
|
-
const nonce = randomBytes(ALGORITHM_NONCE_SIZE);
|
|
112
|
+
async #encrypt(plaintext, key) {
|
|
115
113
|
// Encrypt and prepend nonce.
|
|
116
|
-
const ciphertext =
|
|
117
|
-
return concatBytes(
|
|
114
|
+
const { ciphertext, iv } = await encrypt(key, plaintext);
|
|
115
|
+
return concatBytes([iv, ciphertext]);
|
|
118
116
|
}
|
|
119
|
-
#decrypt(ciphertextAndNonce, key) {
|
|
117
|
+
async #decrypt(ciphertextAndNonce, key) {
|
|
120
118
|
// Create buffers of nonce and ciphertext.
|
|
121
119
|
const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);
|
|
122
120
|
const ciphertext = ciphertextAndNonce.slice(ALGORITHM_NONCE_SIZE, ciphertextAndNonce.length);
|
|
123
121
|
// Decrypt and return result.
|
|
124
|
-
return
|
|
122
|
+
return decrypt(key, nonce, ciphertext);
|
|
125
123
|
}
|
|
126
124
|
async #getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
|
|
127
125
|
const hashedPassword = createSHA256Hash(password);
|
|
@@ -173,12 +171,12 @@ class EncryptorDecryptor {
|
|
|
173
171
|
newKey = await nativeScryptCrypto(stringToByteArray(password), salt, o.N, o.r, o.p, o.dkLen);
|
|
174
172
|
}
|
|
175
173
|
else {
|
|
176
|
-
newKey = await scryptAsync(password, salt, {
|
|
174
|
+
newKey = (await scryptAsync(password, salt, {
|
|
177
175
|
N: o.N,
|
|
178
176
|
r: o.r,
|
|
179
177
|
p: o.p,
|
|
180
178
|
dkLen: o.dkLen,
|
|
181
|
-
});
|
|
179
|
+
}));
|
|
182
180
|
}
|
|
183
181
|
setCachedKey(hashedPassword, salt, newKey);
|
|
184
182
|
return {
|
|
@@ -197,6 +195,6 @@ export default encryption;
|
|
|
197
195
|
*/
|
|
198
196
|
export function createSHA256Hash(data) {
|
|
199
197
|
const hashedData = sha256(data);
|
|
200
|
-
return bytesToHex(hashedData);
|
|
198
|
+
return remove0x(bytesToHex(hashedData));
|
|
201
199
|
}
|
|
202
200
|
//# sourceMappingURL=encryption.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.js","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 type { NativeScrypt } from '../types/encryption.js';\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache.js';\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.js';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils.js';\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;\n }\n })\n .filter((s): s is Uint8Array<ArrayBuffer> => 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
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { decrypt, encrypt } from '@metamask/cryptography/aes-gcm';\nimport {\n getErrorMessage,\n stringToBytes,\n bytesToHex,\n concatBytes,\n remove0x,\n} from '@metamask/utils';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport type { NativeScrypt } from '../types/encryption.js';\nimport {\n getCachedKeyBySalt,\n getCachedKeyGeneratedWithSharedSalt,\n setCachedKey,\n} from './cache.js';\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.js';\nimport {\n base64ToByteArray,\n byteArrayToBase64,\n bytesToUtf8,\n stringToByteArray,\n} from './utils.js';\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<ArrayBuffer>; salt: Uint8Array<ArrayBuffer> }>\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 throw new Error(`Unable to encrypt string - ${getErrorMessage(e)}`);\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 throw new Error(`Unable to decrypt string - ${getErrorMessage(e)}`);\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 = stringToBytes(plaintext);\n const ciphertextAndNonceAndSalt = concatBytes([\n salt,\n await 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(await 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 throw new Error(`Unable to get salt - ${getErrorMessage(e)}`);\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;\n }\n })\n .filter((s): s is Uint8Array<ArrayBuffer> => s !== undefined);\n\n const strSet = new Set(salts.map((arr) => arr.toString()));\n return strSet.size === salts.length;\n }\n\n async #encrypt(\n plaintext: Uint8Array<ArrayBuffer>,\n key: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\n // Encrypt and prepend nonce.\n const { ciphertext, iv } = await encrypt(key, plaintext);\n\n return concatBytes([iv, ciphertext]);\n }\n\n async #decrypt(\n ciphertextAndNonce: Uint8Array<ArrayBuffer>,\n key: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\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 decrypt(key, nonce, ciphertext);\n }\n\n async #getOrGenerateScryptKey(\n password: string,\n o: EncryptedPayload['o'],\n salt?: Uint8Array<ArrayBuffer>,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<{ key: Uint8Array<ArrayBuffer>; salt: Uint8Array<ArrayBuffer> }> {\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<ArrayBuffer>,\n hashedPassword: string,\n nativeScryptCrypto?: NativeScrypt,\n ): Promise<{ key: Uint8Array<ArrayBuffer>; salt: Uint8Array<ArrayBuffer> }> {\n let newKey: Uint8Array<ArrayBuffer>;\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 })) as Uint8Array<ArrayBuffer>;\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 remove0x(bytesToHex(hashedData));\n}\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare type NativeScrypt = (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, size: number) => Promise<Uint8Array
|
|
1
|
+
export declare type NativeScrypt = (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, size: number) => Promise<Uint8Array<ArrayBuffer>>;
|
|
2
2
|
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/shared/types/encryption.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,MAAM,YAAY,GAAG,CACjC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,UAAU,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/shared/types/encryption.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,MAAM,YAAY,GAAG,CACjC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../../src/shared/types/encryption.ts"],"names":[],"mappings":"","sourcesContent":["export declare type NativeScrypt = (\n passwd: Uint8Array,\n salt: Uint8Array,\n N: number,\n r: number,\n p: number,\n size: number,\n) => Promise<Uint8Array
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../../src/shared/types/encryption.ts"],"names":[],"mappings":"","sourcesContent":["export declare type NativeScrypt = (\n passwd: Uint8Array,\n salt: Uint8Array,\n N: number,\n r: number,\n p: number,\n size: number,\n) => Promise<Uint8Array<ArrayBuffer>>;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/profile-sync-controller",
|
|
3
|
-
"version": "33.0.0-preview-
|
|
3
|
+
"version": "33.0.0-preview-9e306636a",
|
|
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
|
"Ethereum",
|
|
@@ -76,13 +76,13 @@
|
|
|
76
76
|
"@metamask/address-book-controller": "^8.0.0",
|
|
77
77
|
"@metamask/base-controller": "^10.0.0",
|
|
78
78
|
"@metamask/controller-utils": "^13.0.0",
|
|
79
|
+
"@metamask/cryptography": "^1.0.0",
|
|
79
80
|
"@metamask/key-tree": "^10.1.1",
|
|
80
81
|
"@metamask/keyring-controller": "^28.1.0",
|
|
81
82
|
"@metamask/messenger": "^3.0.0",
|
|
82
83
|
"@metamask/seedless-onboarding-controller": "^11.0.1",
|
|
83
84
|
"@metamask/superstruct": "^3.4.1",
|
|
84
85
|
"@metamask/utils": "^12.0.0",
|
|
85
|
-
"@noble/ciphers": "^1.3.0",
|
|
86
86
|
"@noble/curves": "^1.9.2",
|
|
87
87
|
"@noble/hashes": "^1.8.0",
|
|
88
88
|
"@signinwithethereum/siwe": "^4.2.1",
|