@metamask-previews/profile-sync-controller 33.0.0-preview-9e306636a → 33.0.0-preview-378f5fcf7

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 CHANGED
@@ -9,7 +9,6 @@ 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))
13
12
  - Bump `immer` from `^9.0.21` to `^11.1.18` ([#10382](https://github.com/MetaMask/core/pull/10382))
14
13
 
15
14
  ### Fixed
@@ -1,7 +1,7 @@
1
1
  type CachedEntry = {
2
- salt: Uint8Array<ArrayBuffer>;
2
+ salt: Uint8Array;
3
3
  base64Salt: string;
4
- key: Uint8Array<ArrayBuffer>;
4
+ key: Uint8Array;
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<ArrayBuffer>): CachedEntry | undefined;
13
+ export declare function getCachedKeyBySalt(hashedPassword: string, salt: Uint8Array): 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<ArrayBuffer>, key: Uint8Array<ArrayBuffer>): void;
30
+ export declare function setCachedKey(hashedPassword: string, salt: Uint8Array, key: Uint8Array): 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,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
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/cache.ts"],"names":[],"mappings":"AAGA,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;;;;;;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,EAChB,GAAG,EAAE,UAAU,GACd,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,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
+ {"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,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,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,IAAgB,EAChB,GAAe;IAEf,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;\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 * 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,\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 +1 @@
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
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"AAMA,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,CAWjB;IAEK,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC,CAmBjB;IAiFD,OAAO,CAAC,gBAAgB,EAAE,MAAM,2BAwB/B;IAED,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAazD;CAqIF;AAED,QAAA,MAAM,UAAU,oBAA2B,CAAC;eAC7B,UAAU;AAEzB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD"}
@@ -1,7 +1,8 @@
1
- import { decrypt, encrypt } from '@metamask/cryptography/aes-gcm';
2
- import { getErrorMessage, stringToBytes, bytesToHex, concatBytes, remove0x, } from '@metamask/utils';
1
+ import { gcm } from '@noble/ciphers/aes';
2
+ import { randomBytes } from '@noble/ciphers/webcrypto';
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';
5
6
  import { getCachedKeyBySalt, getCachedKeyGeneratedWithSharedSalt, setCachedKey, } from './cache.js';
6
7
  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';
7
8
  import { base64ToByteArray, byteArrayToBase64, bytesToUtf8, stringToByteArray, } from './utils.js';
@@ -13,7 +14,8 @@ class EncryptorDecryptor {
13
14
  return await this.#encryptStringV1(plaintext, password, nativeScryptCrypto);
14
15
  }
15
16
  catch (e) {
16
- throw new Error(`Unable to encrypt string - ${getErrorMessage(e)}`);
17
+ const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
18
+ throw new Error(`Unable to encrypt string - ${errorMessage}`);
17
19
  }
18
20
  }
19
21
  async decryptString(encryptedDataStr, password, nativeScryptCrypto) {
@@ -27,7 +29,8 @@ class EncryptorDecryptor {
27
29
  throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
28
30
  }
29
31
  catch (e) {
30
- throw new Error(`Unable to decrypt string - ${getErrorMessage(e)}`);
32
+ const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
33
+ throw new Error(`Unable to decrypt string - ${errorMessage}`);
31
34
  }
32
35
  }
33
36
  async #encryptStringV1(plaintext, password, nativeScryptCrypto) {
@@ -38,11 +41,8 @@ class EncryptorDecryptor {
38
41
  dkLen: ALGORITHM_KEY_SIZE,
39
42
  }, undefined, nativeScryptCrypto);
40
43
  // Encrypt and prepend salt.
41
- const plaintextRaw = stringToBytes(plaintext);
42
- const ciphertextAndNonceAndSalt = concatBytes([
43
- salt,
44
- await this.#encrypt(plaintextRaw, key),
45
- ]);
44
+ const plaintextRaw = utf8ToBytes(plaintext);
45
+ const ciphertextAndNonceAndSalt = concatBytes(salt, this.#encrypt(plaintextRaw, key));
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(await this.#decrypt(ciphertextAndNonce, key));
77
+ return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));
78
78
  }
79
79
  getSalt(encryptedDataStr) {
80
80
  try {
@@ -92,7 +92,8 @@ class EncryptorDecryptor {
92
92
  throw new Error(`Unsupported encrypted data payload - ${encryptedDataStr}`);
93
93
  }
94
94
  catch (e) {
95
- throw new Error(`Unable to get salt - ${getErrorMessage(e)}`);
95
+ const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
96
+ throw new Error(`Unable to get salt - ${errorMessage}`);
96
97
  }
97
98
  }
98
99
  getIfEntriesHaveDifferentSalts(entries) {
@@ -109,17 +110,18 @@ class EncryptorDecryptor {
109
110
  const strSet = new Set(salts.map((arr) => arr.toString()));
110
111
  return strSet.size === salts.length;
111
112
  }
112
- async #encrypt(plaintext, key) {
113
+ #encrypt(plaintext, key) {
114
+ const nonce = randomBytes(ALGORITHM_NONCE_SIZE);
113
115
  // Encrypt and prepend nonce.
114
- const { ciphertext, iv } = await encrypt(key, plaintext);
115
- return concatBytes([iv, ciphertext]);
116
+ const ciphertext = gcm(key, nonce).encrypt(plaintext);
117
+ return concatBytes(nonce, ciphertext);
116
118
  }
117
- async #decrypt(ciphertextAndNonce, key) {
119
+ #decrypt(ciphertextAndNonce, key) {
118
120
  // Create buffers of nonce and ciphertext.
119
121
  const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);
120
122
  const ciphertext = ciphertextAndNonce.slice(ALGORITHM_NONCE_SIZE, ciphertextAndNonce.length);
121
123
  // Decrypt and return result.
122
- return decrypt(key, nonce, ciphertext);
124
+ return gcm(key, nonce).decrypt(ciphertext);
123
125
  }
124
126
  async #getOrGenerateScryptKey(password, o, salt, nativeScryptCrypto) {
125
127
  const hashedPassword = createSHA256Hash(password);
@@ -171,12 +173,12 @@ class EncryptorDecryptor {
171
173
  newKey = await nativeScryptCrypto(stringToByteArray(password), salt, o.N, o.r, o.p, o.dkLen);
172
174
  }
173
175
  else {
174
- newKey = (await scryptAsync(password, salt, {
176
+ newKey = await scryptAsync(password, salt, {
175
177
  N: o.N,
176
178
  r: o.r,
177
179
  p: o.p,
178
180
  dkLen: o.dkLen,
179
- }));
181
+ });
180
182
  }
181
183
  setCachedKey(hashedPassword, salt, newKey);
182
184
  return {
@@ -195,6 +197,6 @@ export default encryption;
195
197
  */
196
198
  export function createSHA256Hash(data) {
197
199
  const hashedData = sha256(data);
198
- return remove0x(bytesToHex(hashedData));
200
+ return bytesToHex(hashedData);
199
201
  }
200
202
  //# 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 { 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
+ {"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,2 +1,2 @@
1
- export declare type NativeScrypt = (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, size: number) => Promise<Uint8Array<ArrayBuffer>>;
1
+ export declare type NativeScrypt = (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, size: number) => Promise<Uint8Array>;
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,WAAW,CAAC,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,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<ArrayBuffer>>;\n"]}
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>;\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-9e306636a",
3
+ "version": "33.0.0-preview-378f5fcf7",
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",
80
79
  "@metamask/key-tree": "^10.1.1",
81
80
  "@metamask/keyring-controller": "^28.1.0",
82
81
  "@metamask/messenger": "^3.0.0",
83
82
  "@metamask/seedless-onboarding-controller": "^11.0.1",
84
83
  "@metamask/superstruct": "^3.4.1",
85
84
  "@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",