Repository: MetaMask/snaps
Pull request #3217: perf!: Use mnemonic seed for state persistence key derivation
Author: login: FrederikBolding
Created: 2025-03-12T09:53:12Z | Merged: 2025-03-13T09:18:14Z

Bump key-tree to the latest version which includes a bunch of performance improvements. One of these performance improvements is the ability to use `.fromSeed`. This PR adds functionality to derive the keys used for state encryption using the mnemonic seed instead of the mnemonic phrase. This should save us more than half of the compute time required for deriving keys used for state persistence.

This PR does not yet tackle `getEntropy` or `getBipEntropy` methods, these will be supported in a follow-up.

Closes https://github.com/MetaMask/snaps/issues/3218

**Breaking changes**
- `SnapController` now expects a `getMnemonicSeed` hook that must return the BIP39 seed for the user's primary mnemonic.


Diff:
diff --git a/packages/examples/packages/bip32/package.json b/packages/examples/packages/bip32/package.json
index 511330905b..c5c9075a1c 100644
--- a/packages/examples/packages/bip32/package.json
+++ b/packages/examples/packages/bip32/package.json
@@ -43,7 +43,7 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/utils": "^11.2.0",
     "@noble/ed25519": "^1.6.0",
diff --git a/packages/examples/packages/bip32/snap.manifest.json b/packages/examples/packages/bip32/snap.manifest.json
index de3617b099..10a28caebe 100644
--- a/packages/examples/packages/bip32/snap.manifest.json
+++ b/packages/examples/packages/bip32/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "nS48OH2I7YacX70E4rPCF+870ZdKRaJb66DwFU6zu8M=",
+    "shasum": "HwrsD47LbsSGHt9MwKLSl50VI8lnsY1yxq/LPa1BBcc=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/bip44/package.json b/packages/examples/packages/bip44/package.json
index eb926331dd..82dbdee64e 100644
--- a/packages/examples/packages/bip44/package.json
+++ b/packages/examples/packages/bip44/package.json
@@ -43,7 +43,7 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/utils": "^11.2.0",
     "@noble/bls12-381": "^1.2.0"
diff --git a/packages/examples/packages/bip44/snap.manifest.json b/packages/examples/packages/bip44/snap.manifest.json
index 769b32a799..160d6f0cbb 100644
--- a/packages/examples/packages/bip44/snap.manifest.json
+++ b/packages/examples/packages/bip44/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "AeCVkpJQuy7JZHh5vQioQzJIbeR2H9hjYWmuGH8rww0=",
+    "shasum": "slHRPz+TwwHJfPqaZKAwCmSUAbxi8aAvwkGWY5MJ1Fk=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json
index cab72fc2c7..1358b8d646 100644
--- a/packages/examples/packages/browserify-plugin/snap.manifest.json
+++ b/packages/examples/packages/browserify-plugin/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "mLWzf5XFmDFHwK9UjU+XBds5oRdj/uXwycZlVhpsyDc=",
+    "shasum": "M83EcfIuVLDGdfzDz6ydToeMztnTSLhjTHpkiGwi3Mo=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json
index 506db0ca51..ae89a5892e 100644
--- a/packages/examples/packages/browserify/snap.manifest.json
+++ b/packages/examples/packages/browserify/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "Yf282fJUrz51Ex+hWO9f+VobgiZHhX4MJD2mkEWYAdw=",
+    "shasum": "AEZW5peO3bzO6yrbWCplB1yo/tyNirb6bAUXwSuspBc=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json b/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json
index 711beca387..83a8cd34b2 100644
--- a/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json
+++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json
@@ -43,10 +43,10 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/utils": "^11.2.0",
-    "@noble/hashes": "^1.3.1"
+    "@noble/hashes": "^1.7.1"
   },
   "devDependencies": {
     "@jest/globals": "^29.5.0",
diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
index a4960e0a0a..4ee1470990 100644
--- a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
+++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "xs0Nhu0ONoHzoY0QfyPEutaBMwlMGQsoZgG88/bLxNQ=",
+    "shasum": "crdduVCAntL3FBIJw+t7ETyP9mbZLuXShtUyP4FXU5o=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/package.json b/packages/examples/packages/invoke-snap/packages/core-signer/package.json
index 9a6fe3ba06..5ddc58ff37 100644
--- a/packages/examples/packages/invoke-snap/packages/core-signer/package.json
+++ b/packages/examples/packages/invoke-snap/packages/core-signer/package.json
@@ -43,7 +43,7 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/utils": "^11.2.0",
     "@noble/curves": "^1.1.0",
@@ -55,7 +55,7 @@
     "@metamask/auto-changelog": "^4.1.0",
     "@metamask/snaps-cli": "workspace:^",
     "@metamask/snaps-jest": "workspace:^",
-    "@noble/hashes": "^1.3.1",
+    "@noble/hashes": "^1.7.1",
     "@swc/core": "1.3.78",
     "@swc/jest": "^0.2.26",
     "@types/node": "18.14.2",
diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
index 1a2e8bf9b0..7c7414fbb6 100644
--- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
+++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
@@ -7,7 +7,7 @@
     "url": "https://github.com/MetaMask/snaps.git"
   },
   "source": {
-    "shasum": "uJbZD9CQgbJNvLk1lKmtfeWJNKAUf+vjZSNr+YK0Pak=",
+    "shasum": "4MKzJCtc1uh4GzIg60F9R7FBWD8LZW8LEMe9Au7UxWQ=",
     "location": {
       "npm": {
         "filePath": "dist/bundle.js",
diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json
index e1d37bdc50..6f3d3b9fd6 100644
--- a/packages/snaps-controllers/package.json
+++ b/packages/snaps-controllers/package.json
@@ -83,7 +83,7 @@
     "@metamask/base-controller": "^8.0.0",
     "@metamask/json-rpc-engine": "^10.0.2",
     "@metamask/json-rpc-middleware-stream": "^8.0.7",
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/object-multiplex": "^2.1.0",
     "@metamask/permission-controller": "^11.0.6",
     "@metamask/phishing-controller": "^12.4.0",
@@ -115,6 +115,7 @@
     "@metamask/auto-changelog": "^4.1.0",
     "@metamask/browser-passworder": "^6.0.0",
     "@metamask/template-snap": "^0.7.0",
+    "@noble/hashes": "^1.7.1",
     "@swc/core": "1.3.78",
     "@swc/jest": "^0.2.26",
     "@ts-bridge/cli": "^0.6.1",
diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx
index 4dd68e957d..d0b5e393ea 100644
--- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx
+++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx
@@ -52,17 +52,18 @@ import {
   MOCK_SNAP_NAME,
   DEFAULT_SOURCE_PATH,
   DEFAULT_ICON_PATH,
-  TEST_SECRET_RECOVERY_PHRASE_BYTES,
+  TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
 } from '@metamask/snaps-utils/test-utils';
 import type { SemVerRange, SemVerVersion, Json } from '@metamask/utils';
 import {
-  hexToBytes,
   assert,
   AssertionError,
   base64ToBytes,
   stringToBytes,
   createDeferredPromise,
 } from '@metamask/utils';
+import { hmac } from '@noble/hashes/hmac';
+import { sha512 } from '@noble/hashes/sha512';
 import { File } from 'buffer';
 import { webcrypto } from 'crypto';
 import fetchMock from 'jest-fetch-mock';
@@ -2134,9 +2135,9 @@ describe('SnapController', () => {
       DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS,
     );
 
-    const getMnemonic = jest
+    const getMnemonicSeed = jest
       .fn()
-      .mockReturnValue(TEST_SECRET_RECOVERY_PHRASE_BYTES);
+      .mockReturnValue(TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES);
 
     const snapController = getSnapController(
       getSnapControllerOptions({
@@ -2149,14 +2150,14 @@ describe('SnapController', () => {
             [MOCK_SNAP_ID]: mockEncryptedState,
           },
         },
-        getMnemonic,
+        getMnemonicSeed,
       }),
     );
 
     expect(
       await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true),
     ).toStrictEqual(state);
-    expect(getMnemonic).toHaveBeenCalledTimes(1);
+    expect(getMnemonicSeed).toHaveBeenCalledTimes(1);
 
     rootMessenger.publish('KeyringController:lock');
 
@@ -2168,7 +2169,7 @@ describe('SnapController', () => {
     // decrypt the state again. This is not an ideal way to test this, but it
     // is the easiest to test this without exposing the internal state of the
     // `SnapController`.
-    expect(getMnemonic).toHaveBeenCalledTimes(2);
+    expect(getMnemonicSeed).toHaveBeenCalledTimes(2);
 
     snapController.destroy();
   });
@@ -9628,9 +9629,11 @@ describe('SnapController', () => {
     it('uses custom client cryptography functions', async () => {
       const messenger = getSnapControllerMessenger();
 
-      const pbkdf2Sha512 = jest
+      const hmacSha512 = jest
         .fn()
-        .mockResolvedValue(hexToBytes(ENCRYPTION_KEY));
+        .mockImplementation((key: Uint8Array, data: Uint8Array) =>
+          hmac(sha512, key, data),
+        );
 
       const snapController = getSnapController(
         getSnapControllerOptions({
@@ -9639,7 +9642,7 @@ describe('SnapController', () => {
             snaps: getPersistedSnapsState(),
           },
           clientCryptography: {
-            pbkdf2Sha512,
+            hmacSha512,
           },
         }),
       );
@@ -9656,7 +9659,7 @@ describe('SnapController', () => {
 
       await promise;
 
-      expect(pbkdf2Sha512).toHaveBeenCalledTimes(1);
+      expect(hmacSha512).toHaveBeenCalledTimes(10);
 
       snapController.destroy();
     });
diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts
index 982c2bfbfc..5c8b635e87 100644
--- a/packages/snaps-controllers/src/snaps/SnapController.ts
+++ b/packages/snaps-controllers/src/snaps/SnapController.ts
@@ -747,11 +747,11 @@ type SnapControllerArgs = {
   encryptor: ExportableKeyEncryptor;
 
   /**
-   * A hook to access the mnemonic of the user's primary keyring.
+   * A hook to access the mnemonic seed of the user's primary keyring.
    *
-   * @returns The mnemonic as bytes.
+   * @returns The mnemonic seed as bytes.
    */
-  getMnemonic: () => Promise<Uint8Array>;
+  getMnemonicSeed: () => Promise<Uint8Array>;
 
   /**
    * A hook to get dynamic feature flags at runtime.
@@ -848,7 +848,7 @@ export class SnapController extends BaseController<
 
   readonly #encryptor: ExportableKeyEncryptor;
 
-  readonly #getMnemonic: () => Promise<Uint8Array>;
+  readonly #getMnemonicSeed: () => Promise<Uint8Array>;
 
   readonly #getFeatureFlags: () => DynamicFeatureFlags;
 
@@ -885,7 +885,7 @@ export class SnapController extends BaseController<
     detectSnapLocation: detectSnapLocationFunction = detectSnapLocation,
     preinstalledSnaps = null,
     encryptor,
-    getMnemonic,
+    getMnemonicSeed,
     getFeatureFlags = () => ({}),
     clientCryptography,
   }: SnapControllerArgs) {
@@ -941,7 +941,7 @@ export class SnapController extends BaseController<
     this.maxRequestTime = maxRequestTime;
     this.#detectSnapLocation = detectSnapLocationFunction;
     this.#encryptor = encryptor;
-    this.#getMnemonic = getMnemonic;
+    this.#getMnemonicSeed = getMnemonicSeed;
     this.#getFeatureFlags = getFeatureFlags;
     this.#clientCryptography = clientCryptography;
     this.#preinstalledSnaps = preinstalledSnaps;
@@ -1824,11 +1824,11 @@ export class SnapController extends BaseController<
     }
 
     const salt = passedSalt ?? this.#encryptor.generateSalt();
-    const mnemonicPhrase = await this.#getMnemonic();
+    const seed = await this.#getMnemonicSeed();
 
     const entropy = await getEncryptionEntropy({
       snapId,
-      mnemonicPhrase,
+      seed,
       cryptographicFunctions: this.#clientCryptography,
     });
 
diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts
index 628c710267..386a4dfb24 100644
--- a/packages/snaps-controllers/src/test-utils/controller.ts
+++ b/packages/snaps-controllers/src/test-utils/controller.ts
@@ -31,7 +31,7 @@ import {
   MOCK_LOCAL_SNAP_ID,
   MOCK_ORIGIN,
   MOCK_SNAP_ID,
-  TEST_SECRET_RECOVERY_PHRASE_BYTES,
+  TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
 } from '@metamask/snaps-utils/test-utils';
 import type { Json } from '@metamask/utils';
 
@@ -581,7 +581,8 @@ export const getSnapControllerOptions = (
     },
     state: undefined,
     fetchFunction: jest.fn(),
-    getMnemonic: async () => Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_BYTES),
+    getMnemonicSeed: async () =>
+      Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES),
     clientCryptography: {},
     encryptor: getSnapControllerEncryptor(),
     ...opts,
@@ -613,7 +614,8 @@ export const getSnapControllerWithEESOptions = ({
     closeAllConnections: jest.fn(),
     messenger: snapControllerMessenger,
     rootMessenger,
-    getMnemonic: async () => Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_BYTES),
+    getMnemonicSeed: async () =>
+      Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES),
     encryptor: getSnapControllerEncryptor(),
     fetchFunction: jest.fn(),
     ...options,
diff --git a/packages/snaps-rpc-methods/jest.config.js b/packages/snaps-rpc-methods/jest.config.js
index 67c7595d39..f9f0fb5aa3 100644
--- a/packages/snaps-rpc-methods/jest.config.js
+++ b/packages/snaps-rpc-methods/jest.config.js
@@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
   ],
   coverageThreshold: {
     global: {
-      branches: 95.19,
-      functions: 98.62,
-      lines: 98.82,
-      statements: 98.5,
+      branches: 95.2,
+      functions: 98.63,
+      lines: 98.83,
+      statements: 98.51,
     },
   },
 });
diff --git a/packages/snaps-rpc-methods/package.json b/packages/snaps-rpc-methods/package.json
index b1b3d6b76d..820a5984ff 100644
--- a/packages/snaps-rpc-methods/package.json
+++ b/packages/snaps-rpc-methods/package.json
@@ -55,14 +55,14 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/permission-controller": "^11.0.6",
     "@metamask/rpc-errors": "^7.0.2",
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/snaps-utils": "workspace:^",
     "@metamask/superstruct": "^3.1.0",
     "@metamask/utils": "^11.2.0",
-    "@noble/hashes": "^1.3.1",
+    "@noble/hashes": "^1.7.1",
     "luxon": "^3.5.0"
   },
   "devDependencies": {
diff --git a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts
index 032fd0625e..f5969ecb8f 100644
--- a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts
+++ b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts
@@ -14,7 +14,7 @@ import type { NonEmptyArray } from '@metamask/utils';
 import { assertStruct } from '@metamask/utils';
 
 import type { MethodHooksObject } from '../utils';
-import { getSecretRecoveryPhrase, deriveEntropy } from '../utils';
+import { getSecretRecoveryPhrase, deriveEntropyFromMnemonic } from '../utils';
 
 const targetName = 'snap_getEntropy';
 
@@ -141,7 +141,7 @@ function getEntropyImplementation({
       params.source,
     );
 
-    return deriveEntropy({
+    return deriveEntropyFromMnemonic({
       input: origin,
       salt: params.salt,
       mnemonicPhrase,
diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts
index 2e9945e3c9..f4444a35d5 100644
--- a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts
+++ b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts
@@ -2,7 +2,7 @@ import { PermissionType, SubjectType } from '@metamask/permission-controller';
 import { ManageStateOperation } from '@metamask/snaps-sdk';
 import {
   MOCK_SNAP_ID,
-  TEST_SECRET_RECOVERY_PHRASE_BYTES,
+  TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
 } from '@metamask/snaps-utils/test-utils';
 import { webcrypto } from 'crypto';
 
@@ -20,7 +20,7 @@ const ENCRYPTION_KEY =
 describe('getEncryptionEntropy', () => {
   it('returns the encryption entropy for the snap ID', async () => {
     const result = await getEncryptionEntropy({
-      mnemonicPhrase: TEST_SECRET_RECOVERY_PHRASE_BYTES,
+      seed: TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
       snapId: MOCK_SNAP_ID,
     });
 
@@ -29,7 +29,7 @@ describe('getEncryptionEntropy', () => {
 
   it('accepts custom cryptographic functions', async () => {
     const result = await getEncryptionEntropy({
-      mnemonicPhrase: TEST_SECRET_RECOVERY_PHRASE_BYTES,
+      seed: TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
       snapId: MOCK_SNAP_ID,
       cryptographicFunctions: {},
     });
diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.ts b/packages/snaps-rpc-methods/src/restricted/manageState.ts
index a99848ce3a..74e3f88cdc 100644
--- a/packages/snaps-rpc-methods/src/restricted/manageState.ts
+++ b/packages/snaps-rpc-methods/src/restricted/manageState.ts
@@ -13,7 +13,7 @@ import type { Json, NonEmptyArray } from '@metamask/utils';
 import { isObject, getJsonSize } from '@metamask/utils';
 
 import type { MethodHooksObject } from '../utils';
-import { deriveEntropy } from '../utils';
+import { deriveEntropyFromSeed } from '../utils';
 
 // The salt used for SIP-6-based entropy derivation.
 export const STATE_ENCRYPTION_SALT = 'snap_manageState encryption';
@@ -111,7 +111,7 @@ export const STORAGE_SIZE_LIMIT = 64_000_000; // In bytes (64 MB)
 
 type GetEncryptionKeyArgs = {
   snapId: string;
-  mnemonicPhrase: Uint8Array;
+  seed: Uint8Array;
   cryptographicFunctions?: CryptographicFunctions | undefined;
 };
 
@@ -124,19 +124,19 @@ type GetEncryptionKeyArgs = {
  *
  * @param args - The encryption key args.
  * @param args.snapId - The ID of the snap to get the encryption key for.
- * @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key
+ * @param args.seed - The mnemonic seed to derive the encryption key
  * from.
  * @param args.cryptographicFunctions - The cryptographic functions to use for
  * the client.
  * @returns The state encryption key.
  */
 export async function getEncryptionEntropy({
-  mnemonicPhrase,
+  seed,
   snapId,
   cryptographicFunctions,
 }: GetEncryptionKeyArgs) {
-  return await deriveEntropy({
-    mnemonicPhrase,
+  return await deriveEntropyFromSeed({
+    seed,
     input: snapId,
     salt: STATE_ENCRYPTION_SALT,
     magic: STATE_ENCRYPTION_MAGIC_VALUE,
diff --git a/packages/snaps-rpc-methods/src/utils.test.ts b/packages/snaps-rpc-methods/src/utils.test.ts
index 4d064eb5a3..dfc4f967a0 100644
--- a/packages/snaps-rpc-methods/src/utils.test.ts
+++ b/packages/snaps-rpc-methods/src/utils.test.ts
@@ -1,11 +1,15 @@
 import { rpcErrors } from '@metamask/rpc-errors';
 import { SIP_6_MAGIC_VALUE } from '@metamask/snaps-utils';
-import { TEST_SECRET_RECOVERY_PHRASE_BYTES } from '@metamask/snaps-utils/test-utils';
+import {
+  TEST_SECRET_RECOVERY_PHRASE_BYTES,
+  TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
+} from '@metamask/snaps-utils/test-utils';
 import { create, is } from '@metamask/superstruct';
 
 import { ENTROPY_VECTORS } from './__fixtures__';
 import {
-  deriveEntropy,
+  deriveEntropyFromMnemonic,
+  deriveEntropyFromSeed,
   getNode,
   getPathPrefix,
   getSecretRecoveryPhrase,
@@ -13,12 +17,12 @@ import {
   StateKeyStruct,
 } from './utils';
 
-describe('deriveEntropy', () => {
+describe('deriveEntropyFromMnemonic', () => {
   it.each(ENTROPY_VECTORS)(
     'derives entropy from the given parameters',
     async ({ snapId, salt, entropy }) => {
       expect(
-        await deriveEntropy({
+        await deriveEntropyFromMnemonic({
           input: snapId,
           salt,
           mnemonicPhrase: TEST_SECRET_RECOVERY_PHRASE_BYTES,
@@ -30,6 +34,23 @@ describe('deriveEntropy', () => {
   );
 });
 
+describe('deriveEntropyFromSeed', () => {
+  it.each(ENTROPY_VECTORS)(
+    'derives entropy from the given parameters',
+    async ({ snapId, salt, entropy }) => {
+      expect(
+        await deriveEntropyFromSeed({
+          input: snapId,
+          salt,
+          seed: TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES,
+          magic: SIP_6_MAGIC_VALUE,
+          cryptographicFunctions: {},
+        }),
+      ).toStrictEqual(entropy);
+    },
+  );
+});
+
 describe('getPathPrefix', () => {
   it('returns "bip32" for "secp256k1"', () => {
     expect(getPathPrefix('secp256k1')).toBe('bip32');
diff --git a/packages/snaps-rpc-methods/src/utils.ts b/packages/snaps-rpc-methods/src/utils.ts
index 3803262790..37f58ff2a7 100644
--- a/packages/snaps-rpc-methods/src/utils.ts
+++ b/packages/snaps-rpc-methods/src/utils.ts
@@ -90,7 +90,7 @@ function getDerivationPathArray(hash: Uint8Array): HardenedBIP32Node[] {
   return array;
 }
 
-type DeriveEntropyOptions = {
+type BaseDeriveEntropyOptions = {
   /**
    * The input value to derive entropy from.
    */
@@ -101,11 +101,6 @@ type DeriveEntropyOptions = {
    */
   salt?: string;
 
-  /**
-   * The mnemonic phrase to use for entropy derivation.
-   */
-  mnemonicPhrase: Uint8Array;
-
   /**
    * A hardened BIP-32 index, which is used to derive the root key from the
    * mnemonic phrase.
@@ -118,6 +113,95 @@ type DeriveEntropyOptions = {
   cryptographicFunctions: CryptographicFunctions | undefined;
 };
 
+type SeedDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic seed to use for entropy derivation.
+   */
+  seed: Uint8Array;
+};
+
+type MnemonicDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic phrase to use for entropy derivation.
+   */
+  mnemonicPhrase: Uint8Array;
+};
+
+/**
+ * Get the derivation path to use for entropy derivation.
+ *
+ * This is based on the reference implementation of
+ * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).
+ *
+ * @param options - The options for entropy derivation.
+ * @param options.input - The input value to derive entropy from.
+ * @param options.salt - An optional salt to use when deriving entropy.
+ * @param options.magic - A hardened BIP-32 index, which is used to derive the
+ * root key from the mnemonic phrase.
+ * @returns The derivation path to be used for entropy key derivation.
+ */
+function getEntropyDerivationPath({
+  input,
+  salt,
+  magic,
+}: Required<Omit<BaseDeriveEntropyOptions, 'cryptographicFunctions'>>):
+  | BIP32Node[]
+  | SLIP10PathNode[] {
+  const inputBytes = stringToBytes(input);
+  const saltBytes = stringToBytes(salt);
+
+  // Get the derivation path from the snap ID.
+  const hash = keccak256(concatBytes([inputBytes, keccak256(saltBytes)]));
+  const computedDerivationPath = getDerivationPathArray(hash);
+
+  return [`bip32:${magic}`, ...computedDerivationPath];
+}
+
+/**
+ * Derive entropy from the given mnemonic seed and salt.
+ *
+ * This is based on the reference implementation of
+ * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).
+ *
+ * @param options - The options for entropy derivation.
+ * @param options.input - The input value to derive entropy from.
+ * @param options.salt - An optional salt to use when deriving entropy.
+ * @param options.seed - The mnemonic seed to use for entropy
+ * derivation.
+ * @param options.magic - A hardened BIP-32 index, which is used to derive the
+ * root key from the mnemonic phrase.
+ * @param options.cryptographicFunctions - The cryptographic functions to use
+ * for the derivation.
+ * @returns The derived entropy.
+ */
+export async function deriveEntropyFromSeed({
+  input,
+  salt = '',
+  seed,
+  magic,
+  cryptographicFunctions,
+}: SeedDeriveEntropyOptions) {
+  const computedDerivationPath = getEntropyDerivationPath({
+    input,
+    salt,
+    magic,
+  });
+
+  // Derive the private key using BIP-32.
+  const { privateKey } = await SLIP10Node.fromSeed(
+    {
+      derivationPath: [seed, ...computedDerivationPath],
+      curve: 'secp256k1',
+    },
+    cryptographicFunctions,
+  );
+
+  // This should never happen, but this keeps TypeScript happy.
+  assert(privateKey, 'Failed to derive the entropy.');
+
+  return add0x(privateKey);
+}
+
 /**
  * Derive entropy from the given mnemonic phrase and salt.
  *
@@ -135,28 +219,23 @@ type DeriveEntropyOptions = {
  * for the derivation.
  * @returns The derived entropy.
  */
-export async function deriveEntropy({
+export async function deriveEntropyFromMnemonic({
   input,
   salt = '',
   mnemonicPhrase,
   magic,
   cryptographicFunctions,
-}: DeriveEntropyOptions): Promise<Hex> {
-  const inputBytes = stringToBytes(input);
-  const saltBytes = stringToBytes(salt);
-
-  // Get the derivation path from the snap ID.
-  const hash = keccak256(concatBytes([inputBytes, keccak256(saltBytes)]));
-  const computedDerivationPath = getDerivationPathArray(hash);
+}: MnemonicDeriveEntropyOptions): Promise<Hex> {
+  const computedDerivationPath = getEntropyDerivationPath({
+    input,
+    salt,
+    magic,
+  });
 
   // Derive the private key using BIP-32.
   const { privateKey } = await SLIP10Node.fromDerivationPath(
     {
-      derivationPath: [
-        mnemonicPhrase,
-        `bip32:${magic}`,
-        ...computedDerivationPath,
-      ],
+      derivationPath: [mnemonicPhrase, ...computedDerivationPath],
       curve: 'secp256k1',
     },
     cryptographicFunctions,
diff --git a/packages/snaps-sdk/package.json b/packages/snaps-sdk/package.json
index a355c8efc7..8789e12834 100644
--- a/packages/snaps-sdk/package.json
+++ b/packages/snaps-sdk/package.json
@@ -91,7 +91,7 @@
     "test:watch": "jest --watch"
   },
   "dependencies": {
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/providers": "^20.0.0",
     "@metamask/rpc-errors": "^7.0.2",
     "@metamask/superstruct": "^3.1.0",
diff --git a/packages/snaps-simulation/package.json b/packages/snaps-simulation/package.json
index e9b2022378..d7408b837d 100644
--- a/packages/snaps-simulation/package.json
+++ b/packages/snaps-simulation/package.json
@@ -59,7 +59,7 @@
     "@metamask/eth-json-rpc-middleware": "^15.3.0",
     "@metamask/json-rpc-engine": "^10.0.2",
     "@metamask/json-rpc-middleware-stream": "^8.0.7",
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/permission-controller": "^11.0.6",
     "@metamask/phishing-controller": "^12.4.0",
     "@metamask/snaps-controllers": "workspace:^",
diff --git a/packages/snaps-simulator/package.json b/packages/snaps-simulator/package.json
index 0338706984..44b41838e2 100644
--- a/packages/snaps-simulator/package.json
+++ b/packages/snaps-simulator/package.json
@@ -50,7 +50,7 @@
     "@metamask/eth-json-rpc-middleware": "^15.3.0",
     "@metamask/json-rpc-engine": "^10.0.2",
     "@metamask/json-rpc-middleware-stream": "^8.0.7",
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/permission-controller": "^11.0.6",
     "@metamask/snaps-controllers": "workspace:^",
     "@metamask/snaps-execution-environments": "workspace:^",
@@ -60,7 +60,7 @@
     "@metamask/superstruct": "^3.1.0",
     "@metamask/utils": "^11.2.0",
     "@minoru/react-dnd-treeview": "^3.4.4",
-    "@noble/hashes": "^1.3.1",
+    "@noble/hashes": "^1.7.1",
     "@reduxjs/toolkit": "^1.9.5",
     "date-fns": "^2.30.0",
     "fast-deep-equal": "^3.1.3",
diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json
index 161d408100..86e8605186 100644
--- a/packages/snaps-utils/package.json
+++ b/packages/snaps-utils/package.json
@@ -80,7 +80,7 @@
     "@babel/core": "^7.23.2",
     "@babel/types": "^7.23.0",
     "@metamask/base-controller": "^8.0.0",
-    "@metamask/key-tree": "^10.0.2",
+    "@metamask/key-tree": "^10.1.0",
     "@metamask/permission-controller": "^11.0.6",
     "@metamask/rpc-errors": "^7.0.2",
     "@metamask/slip44": "^4.1.0",
@@ -88,7 +88,7 @@
     "@metamask/snaps-sdk": "workspace:^",
     "@metamask/superstruct": "^3.1.0",
     "@metamask/utils": "^11.2.0",
-    "@noble/hashes": "^1.3.1",
+    "@noble/hashes": "^1.7.1",
     "@scure/base": "^1.1.1",
     "chalk": "^4.1.2",
     "cron-parser": "^4.5.0",
diff --git a/packages/snaps-utils/src/test-utils/common.ts b/packages/snaps-utils/src/test-utils/common.ts
index ac92b1493c..25b2d73fd5 100644
--- a/packages/snaps-utils/src/test-utils/common.ts
+++ b/packages/snaps-utils/src/test-utils/common.ts
@@ -8,6 +8,14 @@ export const TEST_SECRET_RECOVERY_PHRASE_BYTES = mnemonicPhraseToBytes(
   TEST_SECRET_RECOVERY_PHRASE,
 );
 
+// await mnemonicToSeed('test test test test test test test test test test test ball')
+export const TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES = new Uint8Array([
+  44, 232, 45, 62, 149, 146, 73, 117, 90, 217, 78, 33, 68, 145, 185, 177, 102,
+  61, 41, 58, 21, 196, 248, 21, 155, 72, 140, 191, 191, 66, 144, 46, 47, 188,
+  165, 16, 149, 48, 252, 179, 255, 31, 120, 228, 174, 203, 27, 194, 102, 9, 173,
+  1, 47, 174, 216, 184, 227, 85, 112, 105, 241, 209, 73, 65,
+]);
+
 /**
  * Tens/hundreds legacy tests use creation utils.
  *
diff --git a/yarn.lock b/yarn.lock
index 2041a1d4b6..0f5e2fbbcc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4005,7 +4005,7 @@ __metadata:
     "@jest/globals": "npm:^29.5.0"
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/snaps-cli": "workspace:^"
     "@metamask/snaps-jest": "workspace:^"
     "@metamask/snaps-sdk": "workspace:^"
@@ -4033,7 +4033,7 @@ __metadata:
     "@jest/globals": "npm:^29.5.0"
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/snaps-cli": "workspace:^"
     "@metamask/snaps-jest": "workspace:^"
     "@metamask/snaps-sdk": "workspace:^"
@@ -4147,12 +4147,12 @@ __metadata:
     "@jest/globals": "npm:^29.5.0"
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/snaps-cli": "workspace:^"
     "@metamask/snaps-jest": "workspace:^"
     "@metamask/snaps-sdk": "workspace:^"
     "@metamask/utils": "npm:^11.2.0"
-    "@noble/hashes": "npm:^1.3.1"
+    "@noble/hashes": "npm:^1.7.1"
     "@swc/core": "npm:1.3.78"
     "@swc/jest": "npm:^0.2.26"
     "@types/node": "npm:18.14.2"
@@ -4195,13 +4195,13 @@ __metadata:
     "@jest/globals": "npm:^29.5.0"
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/snaps-cli": "workspace:^"
     "@metamask/snaps-jest": "workspace:^"
     "@metamask/snaps-sdk": "workspace:^"
     "@metamask/utils": "npm:^11.2.0"
     "@noble/curves": "npm:^1.1.0"
-    "@noble/hashes": "npm:^1.3.1"
+    "@noble/hashes": "npm:^1.7.1"
     "@swc/core": "npm:1.3.78"
     "@swc/jest": "npm:^0.2.26"
     "@types/node": "npm:18.14.2"
@@ -4837,16 +4837,16 @@ __metadata:
   languageName: unknown
   linkType: soft
 
-"@metamask/key-tree@npm:^10.0.2":
-  version: 10.0.2
-  resolution: "@metamask/key-tree@npm:10.0.2"
+"@metamask/key-tree@npm:^10.1.0":
+  version: 10.1.0
+  resolution: "@metamask/key-tree@npm:10.1.0"
   dependencies:
     "@metamask/scure-bip39": "npm:^2.1.1"
     "@metamask/utils": "npm:^11.0.1"
-    "@noble/curves": "npm:^1.2.0"
+    "@noble/curves": "npm:^1.8.1"
     "@noble/hashes": "npm:^1.3.2"
     "@scure/base": "npm:^1.0.0"
-  checksum: 10/fd2e445c75dc3cd3976fdc38a5029ee71a6f4afcbbf5c9a17152bba70cf35df8095caa853ae62eef90a51b43f23eeb9546fc6eb7d93a099d82effe8dc7592259
+  checksum: 10/b1292de03257d52fe306a0a9131d17cad46bad1a1b36df15b09ca9fe0168fb1d2a0c4f33febe88cd03656ad585273ebfe54cd67ceeb12f5d69d85df6a2b57e99
   languageName: node
   linkType: hard
 
@@ -5398,7 +5398,7 @@ __metadata:
     "@metamask/browser-passworder": "npm:^6.0.0"
     "@metamask/json-rpc-engine": "npm:^10.0.2"
     "@metamask/json-rpc-middleware-stream": "npm:^8.0.7"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/object-multiplex": "npm:^2.1.0"
     "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/phishing-controller": "npm:^12.4.0"
@@ -5410,6 +5410,7 @@ __metadata:
     "@metamask/snaps-utils": "workspace:^"
     "@metamask/template-snap": "npm:^0.7.0"
     "@metamask/utils": "npm:^11.2.0"
+    "@noble/hashes": "npm:^1.7.1"
     "@swc/core": "npm:1.3.78"
     "@swc/jest": "npm:^0.2.26"
     "@ts-bridge/cli": "npm:^0.6.1"
@@ -5620,14 +5621,14 @@ __metadata:
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
     "@metamask/json-rpc-engine": "npm:^10.0.2"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/rpc-errors": "npm:^7.0.2"
     "@metamask/snaps-sdk": "workspace:^"
     "@metamask/snaps-utils": "workspace:^"
     "@metamask/superstruct": "npm:^3.1.0"
     "@metamask/utils": "npm:^11.2.0"
-    "@noble/hashes": "npm:^1.3.1"
+    "@noble/hashes": "npm:^1.7.1"
     "@swc/core": "npm:1.3.78"
     "@swc/jest": "npm:^0.2.26"
     "@ts-bridge/cli": "npm:^0.6.1"
@@ -5651,7 +5652,7 @@ __metadata:
   dependencies:
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/providers": "npm:^20.0.0"
     "@metamask/rpc-errors": "npm:^7.0.2"
     "@metamask/superstruct": "npm:^3.1.0"
@@ -5682,7 +5683,7 @@ __metadata:
     "@metamask/eth-json-rpc-middleware": "npm:^15.3.0"
     "@metamask/json-rpc-engine": "npm:^10.0.2"
     "@metamask/json-rpc-middleware-stream": "npm:^8.0.7"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/phishing-controller": "npm:^12.4.0"
     "@metamask/snaps-controllers": "workspace:^"
@@ -5729,7 +5730,7 @@ __metadata:
     "@metamask/eth-json-rpc-middleware": "npm:^15.3.0"
     "@metamask/json-rpc-engine": "npm:^10.0.2"
     "@metamask/json-rpc-middleware-stream": "npm:^8.0.7"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/snaps-controllers": "workspace:^"
     "@metamask/snaps-execution-environments": "workspace:^"
@@ -5739,7 +5740,7 @@ __metadata:
     "@metamask/superstruct": "npm:^3.1.0"
     "@metamask/utils": "npm:^11.2.0"
     "@minoru/react-dnd-treeview": "npm:^3.4.4"
-    "@noble/hashes": "npm:^1.3.1"
+    "@noble/hashes": "npm:^1.7.1"
     "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.10"
     "@redux-saga/is": "npm:^1.1.3"
     "@redux-saga/symbols": "npm:^1.1.3"
@@ -5820,7 +5821,7 @@ __metadata:
     "@lavamoat/allow-scripts": "npm:^3.0.4"
     "@metamask/auto-changelog": "npm:^4.1.0"
     "@metamask/base-controller": "npm:^8.0.0"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/key-tree": "npm:^10.1.0"
     "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/post-message-stream": "npm:^9.0.0"
     "@metamask/rpc-errors": "npm:^7.0.2"
@@ -5829,7 +5830,7 @@ __metadata:
     "@metamask/snaps-sdk": "workspace:^"
     "@metamask/superstruct": "npm:^3.1.0"
     "@metamask/utils": "npm:^11.2.0"
-    "@noble/hashes": "npm:^1.3.1"
+    "@noble/hashes": "npm:^1.7.1"
     "@scure/base": "npm:^1.1.1"
     "@swc/core": "npm:1.3.78"
     "@swc/jest": "npm:^0.2.26"
@@ -6184,7 +6185,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@noble/curves@npm:1.4.2, @noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.4.0":
+"@noble/curves@npm:1.4.2, @noble/curves@npm:~1.4.0":
   version: 1.4.2
   resolution: "@noble/curves@npm:1.4.2"
   dependencies:
@@ -6193,6 +6194,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:^1.8.1":
+  version: 1.8.1
+  resolution: "@noble/curves@npm:1.8.1"
+  dependencies:
+    "@noble/hashes": "npm:1.7.1"
+  checksum: 10/e861db372cc0734b02a4c61c0f5a6688d4a7555edca3d8a9e7c846c9aa103ca52d3c3818e8bc333a1a95b5be7f370ff344668d5d759471b11c2d14c7f24b3984
+  languageName: node
+  linkType: hard
+
 "@noble/ed25519@npm:^1.6.0":
   version: 1.6.0
   resolution: "@noble/ed25519@npm:1.6.0"
@@ -6214,10 +6224,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:^1.4.0":
-  version: 1.5.0
-  resolution: "@noble/hashes@npm:1.5.0"
-  checksum: 10/da7fc7af52af7afcf59810a7eea6155075464ff462ffda2572dc6d57d53e2669b1ea2ec774e814f6273f1697e567f28d36823776c9bf7068cba2a2855140f26e
+"@noble/hashes@npm:1.7.1, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.7.1":
+  version: 1.7.1
+  resolution: "@noble/hashes@npm:1.7.1"
+  checksum: 10/ca3120da0c3e7881d6a481e9667465cc9ebbee1329124fb0de442e56d63fef9870f8cc96f264ebdb18096e0e36cebc0e6e979a872d545deb0a6fed9353f17e05
   languageName: node
   linkType: hard
 


Reviews:
user: login: Mrtenz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T08:54:54Z
user: login: FrederikBolding
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T08:55:58Z
user: login: Mrtenz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T09:07:37Z
user: login: Mrtenz
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T09:07:49Z

Review comments:
diff_hunk: @@ -118,6 +113,95 @@ type DeriveEntropyOptions = {
   cryptographicFunctions: CryptographicFunctions | undefined;
 };
 
+type SeedDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic seed to use for entropy derivation.
+   */
+  seed: Uint8Array;
+};
+
+type MnemonicDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic phrase to use for entropy derivation.
+   */
+  mnemonicPhrase: Uint8Array;
+};
+
+/**
+ * Get the derivation path to use for entropy derivation.
+ *
+ * This is based on the reference implementation of
+ * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).
+ *
+ * @param options - The options for entropy derivation.
+ * @param options.input - The input value to derive entropy from.
+ * @param options.salt - An optional salt to use when deriving entropy.
+ * @param options.magic - A hardened BIP-32 index, which is used to derive the
+ * root key from the mnemonic phrase.
+ * @returns The derivation path to be used for entropy key derivation.
+ */
+function getEntropyDerivationPath({
+  input,
+  salt,
+  magic,
+}: Required<Omit<BaseDeriveEntropyOptions, 'cryptographicFunctions'>>):
path: packages/snaps-rpc-methods/src/utils.ts
user: login: Mrtenz
body: Why is `cryptographicFunctions` required here if it's not used?
created_at: 2025-03-13T08:51:13Z
author_association: MEMBER
diff_hunk: @@ -118,6 +113,95 @@ type DeriveEntropyOptions = {
   cryptographicFunctions: CryptographicFunctions | undefined;
 };
 
+type SeedDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic seed to use for entropy derivation.
+   */
+  seed: Uint8Array;
+};
+
+type MnemonicDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic phrase to use for entropy derivation.
+   */
+  mnemonicPhrase: Uint8Array;
+};
+
+/**
+ * Get the derivation path to use for entropy derivation.
+ *
+ * This is based on the reference implementation of
+ * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).
+ *
+ * @param options - The options for entropy derivation.
+ * @param options.input - The input value to derive entropy from.
+ * @param options.salt - An optional salt to use when deriving entropy.
+ * @param options.magic - A hardened BIP-32 index, which is used to derive the
+ * root key from the mnemonic phrase.
+ * @returns The derivation path to be used for entropy key derivation.
+ */
+function getEntropyDerivationPath({
+  input,
+  salt,
+  magic,
+}: Required<Omit<BaseDeriveEntropyOptions, 'cryptographicFunctions'>>):
path: packages/snaps-rpc-methods/src/utils.ts
user: login: FrederikBolding
body: It's not required, it is omitted and the remaining elements are made required, such as `salt` which was previously allowed to be undefined with a default value. But I've kept the default value in the calling functions instead
created_at: 2025-03-13T08:55:58Z
author_association: MEMBER
diff_hunk: @@ -118,6 +113,95 @@ type DeriveEntropyOptions = {
   cryptographicFunctions: CryptographicFunctions | undefined;
 };
 
+type SeedDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic seed to use for entropy derivation.
+   */
+  seed: Uint8Array;
+};
+
+type MnemonicDeriveEntropyOptions = BaseDeriveEntropyOptions & {
+  /**
+   * The mnemonic phrase to use for entropy derivation.
+   */
+  mnemonicPhrase: Uint8Array;
+};
+
+/**
+ * Get the derivation path to use for entropy derivation.
+ *
+ * This is based on the reference implementation of
+ * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).
+ *
+ * @param options - The options for entropy derivation.
+ * @param options.input - The input value to derive entropy from.
+ * @param options.salt - An optional salt to use when deriving entropy.
+ * @param options.magic - A hardened BIP-32 index, which is used to derive the
+ * root key from the mnemonic phrase.
+ * @returns The derivation path to be used for entropy key derivation.
+ */
+function getEntropyDerivationPath({
+  input,
+  salt,
+  magic,
+}: Required<Omit<BaseDeriveEntropyOptions, 'cryptographicFunctions'>>):
path: packages/snaps-rpc-methods/src/utils.ts
user: login: Mrtenz
body: Oh, I misread.
created_at: 2025-03-13T09:07:37Z
author_association: MEMBER

PR comments:
user: login: socket-security[bot]
created_at: 2025-03-12T09:56:23Z
body: <!-- overview-comment -->
**Removed dependencies detected.** Learn more about [Socket for GitHub ↗︎](https://socket.dev?utm_medium=gh)

**🚮 Removed packages:** [npm/@noble/hashes@1.5.0](https://socket.dev/npm/package/@noble/hashes/overview/1.5.0)



[**View full report↗︎**](https://socket.dev/dashboard/org/MetaMask/diff/7a079edf-6397-4530-8c8b-607f1f0810e4/5c18d160-7509-4c10-80cf-8b77c630bcef)
author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-12T10:21:35Z
body: ## [Codecov](https://app.codecov.io/gh/MetaMask/snaps/pull/3217?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.94%. Comparing base [(`61853b0`)](https://app.codecov.io/gh/MetaMask/snaps/commit/61853b06a2902e5ada782faf739d6b2a0d097388?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) to head [(`56637c4`)](https://app.codecov.io/gh/MetaMask/snaps/commit/56637c4126be1dbcd15c49b8fb3a20ee869c123b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask).
> Report is 2 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #3217   +/-   ##
=======================================
  Coverage   94.93%   94.94%           
=======================================
  Files         507      507           
  Lines       11185    11195   +10     
  Branches     1722     1723    +1     
=======================================
+ Hits        10619    10629   +10     
  Misses        566      566           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/MetaMask/snaps/pull/3217?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE