@metamask-previews/profile-sync-controller 21.0.0-preview-159e55f8 → 21.0.0-preview-00abdb8d
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 +8 -0
- package/dist/controllers/user-storage/UserStorageController.cjs +8 -0
- package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.cts +5 -1
- package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.mts +5 -1
- package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.mjs +8 -0
- package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
- package/dist/sdk/user-storage.cjs +25 -15
- package/dist/sdk/user-storage.cjs.map +1 -1
- package/dist/sdk/user-storage.d.cts +3 -0
- package/dist/sdk/user-storage.d.cts.map +1 -1
- package/dist/sdk/user-storage.d.mts +3 -0
- package/dist/sdk/user-storage.d.mts.map +1 -1
- package/dist/sdk/user-storage.mjs +25 -15
- package/dist/sdk/user-storage.mjs.map +1 -1
- package/dist/shared/encryption/cache.cjs +1 -23
- package/dist/shared/encryption/cache.cjs.map +1 -1
- package/dist/shared/encryption/cache.d.cts +0 -8
- package/dist/shared/encryption/cache.d.cts.map +1 -1
- package/dist/shared/encryption/cache.d.mts +0 -8
- package/dist/shared/encryption/cache.d.mts.map +1 -1
- package/dist/shared/encryption/cache.mjs +0 -21
- package/dist/shared/encryption/cache.mjs.map +1 -1
- package/dist/shared/encryption/constants.cjs +6 -1
- package/dist/shared/encryption/constants.cjs.map +1 -1
- package/dist/shared/encryption/constants.d.cts +2 -0
- package/dist/shared/encryption/constants.d.cts.map +1 -1
- package/dist/shared/encryption/constants.d.mts +2 -0
- package/dist/shared/encryption/constants.d.mts.map +1 -1
- package/dist/shared/encryption/constants.mjs +5 -0
- package/dist/shared/encryption/constants.mjs.map +1 -1
- package/dist/shared/encryption/encryption.cjs +39 -24
- package/dist/shared/encryption/encryption.cjs.map +1 -1
- package/dist/shared/encryption/encryption.d.cts +9 -3
- package/dist/shared/encryption/encryption.d.cts.map +1 -1
- package/dist/shared/encryption/encryption.d.mts +9 -3
- package/dist/shared/encryption/encryption.d.mts.map +1 -1
- package/dist/shared/encryption/encryption.mjs +41 -26
- package/dist/shared/encryption/encryption.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,UAAU,CAAC;CACjB,CAAC;AAiBF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,UAAU,GACf,WAAW,GAAG,SAAS,CAazB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,GACd,IAAI,CAeN"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SHARED_SALT } from "./constants.mjs";
|
|
2
1
|
import { byteArrayToBase64 } from "./utils.mjs";
|
|
3
2
|
const MAX_PASSWORD_CACHES = 100;
|
|
4
3
|
const MAX_SALT_CACHES = 100;
|
|
@@ -27,26 +26,6 @@ export function getCachedKeyBySalt(hashedPassword, salt) {
|
|
|
27
26
|
key: cachedKey,
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Gets the cached key that was generated without a salt, if it exists.
|
|
32
|
-
* This is unique per hashed password.
|
|
33
|
-
*
|
|
34
|
-
* @param hashedPassword - hashed password for cache lookup
|
|
35
|
-
* @returns the cached key
|
|
36
|
-
*/
|
|
37
|
-
export function getCachedKeyGeneratedWithSharedSalt(hashedPassword) {
|
|
38
|
-
const cache = getPasswordCache(hashedPassword);
|
|
39
|
-
const base64Salt = byteArrayToBase64(SHARED_SALT);
|
|
40
|
-
const cachedKey = cache.get(base64Salt);
|
|
41
|
-
if (!cachedKey) {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
salt: SHARED_SALT,
|
|
46
|
-
base64Salt,
|
|
47
|
-
key: cachedKey,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
29
|
/**
|
|
51
30
|
* Sets a key to the in memory cache.
|
|
52
31
|
* We have set an arbitrary size of 10 cached keys per hashed password.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cache.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,oBAAgB;AAQ5C,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,MAA7B,cAAc,CAAC,cAAc,IAAM,IAAI,GAAG,EAAE,EAAC;IAC7C,OAAO,cAAc,CAAC,cAAc,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,cAAsB,EACtB,IAAgB;IAEhB,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;QACd,OAAO,SAAS,CAAC;KAClB;IAED,OAAO;QACL,IAAI;QACJ,UAAU;QACV,GAAG,EAAE,SAAS;KACf,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,IAAgB,EAChB,GAAe;IAEf,sBAAsB;IACtB,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,mBAAmB,EAAE;QAC5D,cAAc,GAAG,EAAE,CAAC;KACrB;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;QAChC,KAAK,CAAC,KAAK,EAAE,CAAC;KACf;IAED,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import { byteArrayToBase64 } from './utils';\n\ntype CachedEntry = {\n salt: Uint8Array;\n base64Salt: string;\n key: Uint8Array;\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>;\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,\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 * 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,\n key: Uint8Array,\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,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SHARED_SALT = exports.SCRYPT_p = exports.SCRYPT_r = exports.SCRYPT_N = exports.SCRYPT_SALT_SIZE = exports.ALGORITHM_KEY_SIZE = exports.ALGORITHM_NONCE_SIZE = void 0;
|
|
3
|
+
exports.SHARED_SALT_V2 = exports.SHARED_SALT = exports.SCRYPT_p = exports.SCRYPT_r = exports.SCRYPT_N_V2 = exports.SCRYPT_N = exports.SCRYPT_SALT_SIZE = exports.ALGORITHM_KEY_SIZE = exports.ALGORITHM_NONCE_SIZE = void 0;
|
|
4
4
|
// Nonce/Key Sizes
|
|
5
5
|
exports.ALGORITHM_NONCE_SIZE = 12; // 12 bytes
|
|
6
6
|
exports.ALGORITHM_KEY_SIZE = 16; // 16 bytes
|
|
@@ -8,9 +8,14 @@ exports.ALGORITHM_KEY_SIZE = 16; // 16 bytes
|
|
|
8
8
|
// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt
|
|
9
9
|
exports.SCRYPT_SALT_SIZE = 16; // 16 bytes
|
|
10
10
|
exports.SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)
|
|
11
|
+
exports.SCRYPT_N_V2 = 2;
|
|
11
12
|
exports.SCRYPT_r = 8; // Block size parameter
|
|
12
13
|
exports.SCRYPT_p = 1; // Parallelization parameter
|
|
13
14
|
exports.SHARED_SALT = new Uint8Array([
|
|
14
15
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
15
16
|
]);
|
|
17
|
+
// Different salt for SCRYPT_N_V2 to prevent cache collisions on outdated clients
|
|
18
|
+
exports.SHARED_SALT_V2 = new Uint8Array([
|
|
19
|
+
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
20
|
+
]);
|
|
16
21
|
//# sourceMappingURL=constants.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":";;;AAAA,kBAAkB;AACL,QAAA,oBAAoB,GAAG,EAAE,CAAC,CAAC,WAAW;AACtC,QAAA,kBAAkB,GAAG,EAAE,CAAC,CAAC,WAAW;AAEjD,kBAAkB;AAClB,+FAA+F;AAClF,QAAA,gBAAgB,GAAG,EAAE,CAAC,CAAC,WAAW;AAClC,QAAA,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,wDAAwD;AAC5E,QAAA,QAAQ,GAAG,CAAC,CAAC,CAAC,uBAAuB;AACrC,QAAA,QAAQ,GAAG,CAAC,CAAC,CAAC,4BAA4B;AAE1C,QAAA,WAAW,GAAG,IAAI,UAAU,CAAC;IACxC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACrD,CAAC,CAAC","sourcesContent":["// Nonce/Key Sizes\nexport const ALGORITHM_NONCE_SIZE = 12; // 12 bytes\nexport const ALGORITHM_KEY_SIZE = 16; // 16 bytes\n\n// Scrypt settings\n// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt\nexport const SCRYPT_SALT_SIZE = 16; // 16 bytes\nexport const SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)\nexport const SCRYPT_r = 8; // Block size parameter\nexport const SCRYPT_p = 1; // Parallelization parameter\n\nexport const SHARED_SALT = new Uint8Array([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n]);\n"]}
|
|
1
|
+
{"version":3,"file":"constants.cjs","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":";;;AAAA,kBAAkB;AACL,QAAA,oBAAoB,GAAG,EAAE,CAAC,CAAC,WAAW;AACtC,QAAA,kBAAkB,GAAG,EAAE,CAAC,CAAC,WAAW;AAEjD,kBAAkB;AAClB,+FAA+F;AAClF,QAAA,gBAAgB,GAAG,EAAE,CAAC,CAAC,WAAW;AAClC,QAAA,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,wDAAwD;AAC5E,QAAA,WAAW,GAAG,CAAC,CAAC;AAChB,QAAA,QAAQ,GAAG,CAAC,CAAC,CAAC,uBAAuB;AACrC,QAAA,QAAQ,GAAG,CAAC,CAAC,CAAC,4BAA4B;AAE1C,QAAA,WAAW,GAAG,IAAI,UAAU,CAAC;IACxC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACrD,CAAC,CAAC;AACH,iFAAiF;AACpE,QAAA,cAAc,GAAG,IAAI,UAAU,CAAC;IAC3C,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CAC/D,CAAC,CAAC","sourcesContent":["// Nonce/Key Sizes\nexport const ALGORITHM_NONCE_SIZE = 12; // 12 bytes\nexport const ALGORITHM_KEY_SIZE = 16; // 16 bytes\n\n// Scrypt settings\n// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt\nexport const SCRYPT_SALT_SIZE = 16; // 16 bytes\nexport const SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)\nexport const SCRYPT_N_V2 = 2;\nexport const SCRYPT_r = 8; // Block size parameter\nexport const SCRYPT_p = 1; // Parallelization parameter\n\nexport const SHARED_SALT = new Uint8Array([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n]);\n// Different salt for SCRYPT_N_V2 to prevent cache collisions on outdated clients\nexport const SHARED_SALT_V2 = new Uint8Array([\n 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n]);\n"]}
|
|
@@ -2,7 +2,9 @@ export declare const ALGORITHM_NONCE_SIZE = 12;
|
|
|
2
2
|
export declare const ALGORITHM_KEY_SIZE = 16;
|
|
3
3
|
export declare const SCRYPT_SALT_SIZE = 16;
|
|
4
4
|
export declare const SCRYPT_N: number;
|
|
5
|
+
export declare const SCRYPT_N_V2 = 2;
|
|
5
6
|
export declare const SCRYPT_r = 8;
|
|
6
7
|
export declare const SCRYPT_p = 1;
|
|
7
8
|
export declare const SHARED_SALT: Uint8Array;
|
|
9
|
+
export declare const SHARED_SALT_V2: Uint8Array;
|
|
8
10
|
//# sourceMappingURL=constants.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.cts","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAIrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,QAAQ,QAAU,CAAC;AAChC,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,eAAO,MAAM,WAAW,YAEtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.cts","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAIrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,QAAQ,QAAU,CAAC;AAChC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,eAAO,MAAM,WAAW,YAEtB,CAAC;AAEH,eAAO,MAAM,cAAc,YAEzB,CAAC"}
|
|
@@ -2,7 +2,9 @@ export declare const ALGORITHM_NONCE_SIZE = 12;
|
|
|
2
2
|
export declare const ALGORITHM_KEY_SIZE = 16;
|
|
3
3
|
export declare const SCRYPT_SALT_SIZE = 16;
|
|
4
4
|
export declare const SCRYPT_N: number;
|
|
5
|
+
export declare const SCRYPT_N_V2 = 2;
|
|
5
6
|
export declare const SCRYPT_r = 8;
|
|
6
7
|
export declare const SCRYPT_p = 1;
|
|
7
8
|
export declare const SHARED_SALT: Uint8Array;
|
|
9
|
+
export declare const SHARED_SALT_V2: Uint8Array;
|
|
8
10
|
//# sourceMappingURL=constants.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAIrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,QAAQ,QAAU,CAAC;AAChC,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,eAAO,MAAM,WAAW,YAEtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAIrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,QAAQ,QAAU,CAAC;AAChC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,eAAO,MAAM,WAAW,YAEtB,CAAC;AAEH,eAAO,MAAM,cAAc,YAEzB,CAAC"}
|
|
@@ -5,9 +5,14 @@ export const ALGORITHM_KEY_SIZE = 16; // 16 bytes
|
|
|
5
5
|
// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt
|
|
6
6
|
export const SCRYPT_SALT_SIZE = 16; // 16 bytes
|
|
7
7
|
export const SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)
|
|
8
|
+
export const SCRYPT_N_V2 = 2;
|
|
8
9
|
export const SCRYPT_r = 8; // Block size parameter
|
|
9
10
|
export const SCRYPT_p = 1; // Parallelization parameter
|
|
10
11
|
export const SHARED_SALT = new Uint8Array([
|
|
11
12
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
12
13
|
]);
|
|
14
|
+
// Different salt for SCRYPT_N_V2 to prevent cache collisions on outdated clients
|
|
15
|
+
export const SHARED_SALT_V2 = new Uint8Array([
|
|
16
|
+
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
|
17
|
+
]);
|
|
13
18
|
//# sourceMappingURL=constants.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC,CAAC,WAAW;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC,CAAC,WAAW;AAEjD,kBAAkB;AAClB,+FAA+F;AAC/F,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC,CAAC,WAAW;AAC/C,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,wDAAwD;AACzF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,uBAAuB;AAClD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,4BAA4B;AAEvD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,UAAU,CAAC;IACxC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACrD,CAAC,CAAC","sourcesContent":["// Nonce/Key Sizes\nexport const ALGORITHM_NONCE_SIZE = 12; // 12 bytes\nexport const ALGORITHM_KEY_SIZE = 16; // 16 bytes\n\n// Scrypt settings\n// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt\nexport const SCRYPT_SALT_SIZE = 16; // 16 bytes\nexport const SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)\nexport const SCRYPT_r = 8; // Block size parameter\nexport const SCRYPT_p = 1; // Parallelization parameter\n\nexport const SHARED_SALT = new Uint8Array([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n]);\n"]}
|
|
1
|
+
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/constants.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC,CAAC,WAAW;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC,CAAC,WAAW;AAEjD,kBAAkB;AAClB,+FAA+F;AAC/F,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC,CAAC,WAAW;AAC/C,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,wDAAwD;AACzF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,uBAAuB;AAClD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,4BAA4B;AAEvD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,UAAU,CAAC;IACxC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACrD,CAAC,CAAC;AACH,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC;IAC3C,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CAC/D,CAAC,CAAC","sourcesContent":["// Nonce/Key Sizes\nexport const ALGORITHM_NONCE_SIZE = 12; // 12 bytes\nexport const ALGORITHM_KEY_SIZE = 16; // 16 bytes\n\n// Scrypt settings\n// see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt\nexport const SCRYPT_SALT_SIZE = 16; // 16 bytes\nexport const SCRYPT_N = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)\nexport const SCRYPT_N_V2 = 2;\nexport const SCRYPT_r = 8; // Block size parameter\nexport const SCRYPT_p = 1; // Parallelization parameter\n\nexport const SHARED_SALT = new Uint8Array([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n]);\n// Different salt for SCRYPT_N_V2 to prevent cache collisions on outdated clients\nexport const SHARED_SALT_V2 = new Uint8Array([\n 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n]);\n"]}
|
|
@@ -19,21 +19,37 @@ class EncryptorDecryptor {
|
|
|
19
19
|
constructor() {
|
|
20
20
|
_EncryptorDecryptor_instances.add(this);
|
|
21
21
|
}
|
|
22
|
-
async encryptString(plaintext, password,
|
|
22
|
+
async encryptString(plaintext, password, options) {
|
|
23
23
|
try {
|
|
24
|
-
|
|
24
|
+
const encryptedString = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_encryptStringV1).call(this, plaintext, password, options?.nativeScryptCrypto, {
|
|
25
|
+
N: constants_1.SCRYPT_N_V2,
|
|
26
|
+
});
|
|
27
|
+
const encryptedData = JSON.parse(encryptedString);
|
|
28
|
+
await options?.onEncrypt?.({
|
|
29
|
+
v: encryptedData.v,
|
|
30
|
+
t: encryptedData.t,
|
|
31
|
+
o: encryptedData.o,
|
|
32
|
+
saltLen: encryptedData.saltLen,
|
|
33
|
+
});
|
|
34
|
+
return encryptedString;
|
|
25
35
|
}
|
|
26
36
|
catch (e) {
|
|
27
37
|
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
|
|
28
38
|
throw new Error(`Unable to encrypt string - ${errorMessage}`);
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
|
-
async decryptString(encryptedDataStr, password,
|
|
41
|
+
async decryptString(encryptedDataStr, password, options) {
|
|
32
42
|
try {
|
|
33
43
|
const encryptedData = JSON.parse(encryptedDataStr);
|
|
44
|
+
await options?.onDecrypt?.({
|
|
45
|
+
v: encryptedData.v,
|
|
46
|
+
t: encryptedData.t,
|
|
47
|
+
o: encryptedData.o,
|
|
48
|
+
saltLen: encryptedData.saltLen,
|
|
49
|
+
});
|
|
34
50
|
if (encryptedData.v === '1') {
|
|
35
51
|
if (encryptedData.t === 'scrypt') {
|
|
36
|
-
return await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_decryptStringV1).call(this, encryptedData, password, nativeScryptCrypto);
|
|
52
|
+
return await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_decryptStringV1).call(this, encryptedData, password, options?.nativeScryptCrypto);
|
|
37
53
|
}
|
|
38
54
|
}
|
|
39
55
|
throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
|
|
@@ -63,24 +79,24 @@ class EncryptorDecryptor {
|
|
|
63
79
|
throw new Error(`Unable to get salt - ${errorMessage}`);
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const strSet = new Set(salts.map((arr) => arr.toString()));
|
|
78
|
-
return strSet.size === salts.length;
|
|
82
|
+
doesEntryNeedReEncryption(encryptedDataStr) {
|
|
83
|
+
try {
|
|
84
|
+
const encryptedData = JSON.parse(encryptedDataStr);
|
|
85
|
+
// Only check N value - in production, only two valid scenarios exist:
|
|
86
|
+
// 1. N:2**17 + SHARED_SALT (old code)
|
|
87
|
+
// 2. N:2 + SHARED_SALT_V2 (new code)
|
|
88
|
+
return encryptedData.o?.N !== constants_1.SCRYPT_N_V2;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
79
93
|
}
|
|
80
94
|
}
|
|
81
|
-
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto
|
|
95
|
+
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto, scryptOverrides = {
|
|
96
|
+
N: constants_1.SCRYPT_N,
|
|
97
|
+
}) {
|
|
82
98
|
const { key, salt } = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_getOrGenerateScryptKey).call(this, password, {
|
|
83
|
-
N:
|
|
99
|
+
N: scryptOverrides.N,
|
|
84
100
|
r: constants_1.SCRYPT_r,
|
|
85
101
|
p: constants_1.SCRYPT_p,
|
|
86
102
|
dkLen: constants_1.ALGORITHM_KEY_SIZE,
|
|
@@ -95,7 +111,7 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
95
111
|
t: 'scrypt',
|
|
96
112
|
d: encryptedData,
|
|
97
113
|
o: {
|
|
98
|
-
N:
|
|
114
|
+
N: scryptOverrides.N,
|
|
99
115
|
r: constants_1.SCRYPT_r,
|
|
100
116
|
p: constants_1.SCRYPT_p,
|
|
101
117
|
dkLen: constants_1.ALGORITHM_KEY_SIZE,
|
|
@@ -132,16 +148,15 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
132
148
|
return (0, aes_1.gcm)(key, nonce).decrypt(ciphertext);
|
|
133
149
|
}, _EncryptorDecryptor_getOrGenerateScryptKey = async function _EncryptorDecryptor_getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
|
|
134
150
|
const hashedPassword = createSHA256Hash(password);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
: (0, cache_1.getCachedKeyGeneratedWithSharedSalt)(hashedPassword);
|
|
151
|
+
const targetSalt = salt ?? (o.N === constants_1.SCRYPT_N_V2 ? constants_1.SHARED_SALT_V2 : constants_1.SHARED_SALT);
|
|
152
|
+
const cachedKey = (0, cache_1.getCachedKeyBySalt)(hashedPassword, targetSalt);
|
|
138
153
|
if (cachedKey) {
|
|
139
154
|
return {
|
|
140
155
|
key: cachedKey.key,
|
|
141
156
|
salt: cachedKey.salt,
|
|
142
157
|
};
|
|
143
158
|
}
|
|
144
|
-
const newSalt =
|
|
159
|
+
const newSalt = targetSalt;
|
|
145
160
|
let newKey;
|
|
146
161
|
if (nativeScryptCrypto) {
|
|
147
162
|
newKey = await nativeScryptCrypto((0, utils_2.stringToByteArray)(password), newSalt, o.N, o.r, o.p, o.dkLen);
|
|
@@ -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 { getCachedKeyBySalt, setCachedKey } from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n SCRYPT_N,\n SCRYPT_N_V2,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n SHARED_SALT_V2,\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 options?: {\n nativeScryptCrypto?: NativeScrypt;\n onEncrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;\n },\n ): Promise<string> {\n try {\n const encryptedString = await this.#encryptStringV1(\n plaintext,\n password,\n options?.nativeScryptCrypto,\n {\n N: SCRYPT_N_V2,\n },\n );\n\n const encryptedData: EncryptedPayload = JSON.parse(encryptedString);\n await options?.onEncrypt?.({\n v: encryptedData.v,\n t: encryptedData.t,\n o: encryptedData.o,\n saltLen: encryptedData.saltLen,\n });\n\n return encryptedString;\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 options?: {\n nativeScryptCrypto?: NativeScrypt;\n onDecrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;\n },\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n\n await options?.onDecrypt?.({\n v: encryptedData.v,\n t: encryptedData.t,\n o: encryptedData.o,\n saltLen: encryptedData.saltLen,\n });\n\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n options?.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 scryptOverrides = {\n N: SCRYPT_N,\n },\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: scryptOverrides.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: scryptOverrides.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 doesEntryNeedReEncryption(encryptedDataStr: string): boolean {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n\n // Only check N value - in production, only two valid scenarios exist:\n // 1. N:2**17 + SHARED_SALT (old code)\n // 2. N:2 + SHARED_SALT_V2 (new code)\n return encryptedData.o?.N !== SCRYPT_N_V2;\n } catch {\n return false;\n }\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 const targetSalt =\n salt ?? (o.N === SCRYPT_N_V2 ? SHARED_SALT_V2 : SHARED_SALT);\n\n const cachedKey = getCachedKeyBySalt(hashedPassword, targetSalt);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n const newSalt = targetSalt;\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"]}
|
|
@@ -13,10 +13,16 @@ export type EncryptedPayload = {
|
|
|
13
13
|
};
|
|
14
14
|
declare class EncryptorDecryptor {
|
|
15
15
|
#private;
|
|
16
|
-
encryptString(plaintext: string, password: string,
|
|
17
|
-
|
|
16
|
+
encryptString(plaintext: string, password: string, options?: {
|
|
17
|
+
nativeScryptCrypto?: NativeScrypt;
|
|
18
|
+
onEncrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
decryptString(encryptedDataStr: string, password: string, options?: {
|
|
21
|
+
nativeScryptCrypto?: NativeScrypt;
|
|
22
|
+
onDecrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;
|
|
23
|
+
}): Promise<string>;
|
|
18
24
|
getSalt(encryptedDataStr: string): Uint8Array;
|
|
19
|
-
|
|
25
|
+
doesEntryNeedReEncryption(encryptedDataStr: string): boolean;
|
|
20
26
|
}
|
|
21
27
|
declare const encryption: EncryptorDecryptor;
|
|
22
28
|
export default encryption;
|
|
@@ -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":"AAwBA,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;;IAChB,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,kBAAkB,CAAC,EAAE,YAAY,CAAC;QAClC,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,MAAM,CAAC;IA0BZ,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,kBAAkB,CAAC,EAAE,YAAY,CAAC;QAClC,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,MAAM,CAAC;IA+GlB,OAAO,CAAC,gBAAgB,EAAE,MAAM;IA0BhC,yBAAyB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO;CAmF7D;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;AAC5C,eAAe,UAAU,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
|
|
@@ -13,10 +13,16 @@ export type EncryptedPayload = {
|
|
|
13
13
|
};
|
|
14
14
|
declare class EncryptorDecryptor {
|
|
15
15
|
#private;
|
|
16
|
-
encryptString(plaintext: string, password: string,
|
|
17
|
-
|
|
16
|
+
encryptString(plaintext: string, password: string, options?: {
|
|
17
|
+
nativeScryptCrypto?: NativeScrypt;
|
|
18
|
+
onEncrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
decryptString(encryptedDataStr: string, password: string, options?: {
|
|
21
|
+
nativeScryptCrypto?: NativeScrypt;
|
|
22
|
+
onDecrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;
|
|
23
|
+
}): Promise<string>;
|
|
18
24
|
getSalt(encryptedDataStr: string): Uint8Array;
|
|
19
|
-
|
|
25
|
+
doesEntryNeedReEncryption(encryptedDataStr: string): boolean;
|
|
20
26
|
}
|
|
21
27
|
declare const encryption: EncryptorDecryptor;
|
|
22
28
|
export default encryption;
|
|
@@ -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":"AAwBA,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;;IAChB,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,kBAAkB,CAAC,EAAE,YAAY,CAAC;QAClC,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,MAAM,CAAC;IA0BZ,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,kBAAkB,CAAC,EAAE,YAAY,CAAC;QAClC,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,MAAM,CAAC;IA+GlB,OAAO,CAAC,gBAAgB,EAAE,MAAM;IA0BhC,yBAAyB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO;CAmF7D;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;AAC5C,eAAe,UAAU,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
|
|
@@ -9,28 +9,44 @@ 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
|
-
import { getCachedKeyBySalt,
|
|
13
|
-
import { ALGORITHM_KEY_SIZE, ALGORITHM_NONCE_SIZE, SCRYPT_N, SCRYPT_p, SCRYPT_r, SCRYPT_SALT_SIZE, SHARED_SALT } from "./constants.mjs";
|
|
12
|
+
import { getCachedKeyBySalt, setCachedKey } from "./cache.mjs";
|
|
13
|
+
import { ALGORITHM_KEY_SIZE, ALGORITHM_NONCE_SIZE, SCRYPT_N, SCRYPT_N_V2, SCRYPT_p, SCRYPT_r, SCRYPT_SALT_SIZE, SHARED_SALT, SHARED_SALT_V2 } 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
18
|
}
|
|
19
|
-
async encryptString(plaintext, password,
|
|
19
|
+
async encryptString(plaintext, password, options) {
|
|
20
20
|
try {
|
|
21
|
-
|
|
21
|
+
const encryptedString = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_encryptStringV1).call(this, plaintext, password, options?.nativeScryptCrypto, {
|
|
22
|
+
N: SCRYPT_N_V2,
|
|
23
|
+
});
|
|
24
|
+
const encryptedData = JSON.parse(encryptedString);
|
|
25
|
+
await options?.onEncrypt?.({
|
|
26
|
+
v: encryptedData.v,
|
|
27
|
+
t: encryptedData.t,
|
|
28
|
+
o: encryptedData.o,
|
|
29
|
+
saltLen: encryptedData.saltLen,
|
|
30
|
+
});
|
|
31
|
+
return encryptedString;
|
|
22
32
|
}
|
|
23
33
|
catch (e) {
|
|
24
34
|
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
|
|
25
35
|
throw new Error(`Unable to encrypt string - ${errorMessage}`);
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
|
-
async decryptString(encryptedDataStr, password,
|
|
38
|
+
async decryptString(encryptedDataStr, password, options) {
|
|
29
39
|
try {
|
|
30
40
|
const encryptedData = JSON.parse(encryptedDataStr);
|
|
41
|
+
await options?.onDecrypt?.({
|
|
42
|
+
v: encryptedData.v,
|
|
43
|
+
t: encryptedData.t,
|
|
44
|
+
o: encryptedData.o,
|
|
45
|
+
saltLen: encryptedData.saltLen,
|
|
46
|
+
});
|
|
31
47
|
if (encryptedData.v === '1') {
|
|
32
48
|
if (encryptedData.t === 'scrypt') {
|
|
33
|
-
return await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_decryptStringV1).call(this, encryptedData, password, nativeScryptCrypto);
|
|
49
|
+
return await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_decryptStringV1).call(this, encryptedData, password, options?.nativeScryptCrypto);
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
|
|
@@ -60,24 +76,24 @@ class EncryptorDecryptor {
|
|
|
60
76
|
throw new Error(`Unable to get salt - ${errorMessage}`);
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const strSet = new Set(salts.map((arr) => arr.toString()));
|
|
75
|
-
return strSet.size === salts.length;
|
|
79
|
+
doesEntryNeedReEncryption(encryptedDataStr) {
|
|
80
|
+
try {
|
|
81
|
+
const encryptedData = JSON.parse(encryptedDataStr);
|
|
82
|
+
// Only check N value - in production, only two valid scenarios exist:
|
|
83
|
+
// 1. N:2**17 + SHARED_SALT (old code)
|
|
84
|
+
// 2. N:2 + SHARED_SALT_V2 (new code)
|
|
85
|
+
return encryptedData.o?.N !== SCRYPT_N_V2;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
|
-
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto
|
|
92
|
+
_EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptStringV1 = async function _EncryptorDecryptor_encryptStringV1(plaintext, password, nativeScryptCrypto, scryptOverrides = {
|
|
93
|
+
N: SCRYPT_N,
|
|
94
|
+
}) {
|
|
79
95
|
const { key, salt } = await __classPrivateFieldGet(this, _EncryptorDecryptor_instances, "m", _EncryptorDecryptor_getOrGenerateScryptKey).call(this, password, {
|
|
80
|
-
N:
|
|
96
|
+
N: scryptOverrides.N,
|
|
81
97
|
r: SCRYPT_r,
|
|
82
98
|
p: SCRYPT_p,
|
|
83
99
|
dkLen: ALGORITHM_KEY_SIZE,
|
|
@@ -92,7 +108,7 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
92
108
|
t: 'scrypt',
|
|
93
109
|
d: encryptedData,
|
|
94
110
|
o: {
|
|
95
|
-
N:
|
|
111
|
+
N: scryptOverrides.N,
|
|
96
112
|
r: SCRYPT_r,
|
|
97
113
|
p: SCRYPT_p,
|
|
98
114
|
dkLen: ALGORITHM_KEY_SIZE,
|
|
@@ -129,16 +145,15 @@ _EncryptorDecryptor_instances = new WeakSet(), _EncryptorDecryptor_encryptString
|
|
|
129
145
|
return gcm(key, nonce).decrypt(ciphertext);
|
|
130
146
|
}, _EncryptorDecryptor_getOrGenerateScryptKey = async function _EncryptorDecryptor_getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
|
|
131
147
|
const hashedPassword = createSHA256Hash(password);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
: getCachedKeyGeneratedWithSharedSalt(hashedPassword);
|
|
148
|
+
const targetSalt = salt ?? (o.N === SCRYPT_N_V2 ? SHARED_SALT_V2 : SHARED_SALT);
|
|
149
|
+
const cachedKey = getCachedKeyBySalt(hashedPassword, targetSalt);
|
|
135
150
|
if (cachedKey) {
|
|
136
151
|
return {
|
|
137
152
|
key: cachedKey.key,
|
|
138
153
|
salt: cachedKey.salt,
|
|
139
154
|
};
|
|
140
155
|
}
|
|
141
|
-
const newSalt =
|
|
156
|
+
const newSalt = targetSalt;
|
|
142
157
|
let newKey;
|
|
143
158
|
if (nativeScryptCrypto) {
|
|
144
159
|
newKey = await nativeScryptCrypto(stringToByteArray(password), newSalt, o.N, o.r, o.p, o.dkLen);
|
|
@@ -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 { getCachedKeyBySalt, setCachedKey } from './cache';\nimport {\n ALGORITHM_KEY_SIZE,\n ALGORITHM_NONCE_SIZE,\n SCRYPT_N,\n SCRYPT_N_V2,\n SCRYPT_p,\n SCRYPT_r,\n SCRYPT_SALT_SIZE,\n SHARED_SALT,\n SHARED_SALT_V2,\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 options?: {\n nativeScryptCrypto?: NativeScrypt;\n onEncrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;\n },\n ): Promise<string> {\n try {\n const encryptedString = await this.#encryptStringV1(\n plaintext,\n password,\n options?.nativeScryptCrypto,\n {\n N: SCRYPT_N_V2,\n },\n );\n\n const encryptedData: EncryptedPayload = JSON.parse(encryptedString);\n await options?.onEncrypt?.({\n v: encryptedData.v,\n t: encryptedData.t,\n o: encryptedData.o,\n saltLen: encryptedData.saltLen,\n });\n\n return encryptedString;\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 options?: {\n nativeScryptCrypto?: NativeScrypt;\n onDecrypt?: (encryptedData: Omit<EncryptedPayload, 'd'>) => Promise<void>;\n },\n ): Promise<string> {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n\n await options?.onDecrypt?.({\n v: encryptedData.v,\n t: encryptedData.t,\n o: encryptedData.o,\n saltLen: encryptedData.saltLen,\n });\n\n if (encryptedData.v === '1') {\n if (encryptedData.t === 'scrypt') {\n return await this.#decryptStringV1(\n encryptedData,\n password,\n options?.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 scryptOverrides = {\n N: SCRYPT_N,\n },\n ): Promise<string> {\n const { key, salt } = await this.#getOrGenerateScryptKey(\n password,\n {\n N: scryptOverrides.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: scryptOverrides.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 doesEntryNeedReEncryption(encryptedDataStr: string): boolean {\n try {\n const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n\n // Only check N value - in production, only two valid scenarios exist:\n // 1. N:2**17 + SHARED_SALT (old code)\n // 2. N:2 + SHARED_SALT_V2 (new code)\n return encryptedData.o?.N !== SCRYPT_N_V2;\n } catch {\n return false;\n }\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 const targetSalt =\n salt ?? (o.N === SCRYPT_N_V2 ? SHARED_SALT_V2 : SHARED_SALT);\n\n const cachedKey = getCachedKeyBySalt(hashedPassword, targetSalt);\n\n if (cachedKey) {\n return {\n key: cachedKey.key,\n salt: cachedKey.salt,\n };\n }\n\n const newSalt = targetSalt;\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/profile-sync-controller",
|
|
3
|
-
"version": "21.0.0-preview-
|
|
3
|
+
"version": "21.0.0-preview-00abdb8d",
|
|
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",
|