diff --git a/packages/examples/packages/bip32/package.json b/packages/examples/packages/bip32/package.json index 2a972cded8..0ba965316f 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": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/snaps-sdk": "workspace:^", "@metamask/utils": "^10.0.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 fb6ab13920..43b8514e21 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": "ZgNEuQpFXjusvZX+0dsqN/jWaTFnk1T9mePMO2OxoQs=", + "shasum": "hRuh420QB8uksiS3rFwrvqNoQD5XTH/QyWkhFkmNBD8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/bip44/package.json b/packages/examples/packages/bip44/package.json index d12e82f896..1584ef9876 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": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/snaps-sdk": "workspace:^", "@metamask/utils": "^10.0.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 5792d0cf6d..f2130d0f11 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": "dxKtnBcjmsoplDWa7k/dGTEVKyxl3slprHFoosSOPVI=", + "shasum": "kztNgPuBct9iJIGhWZs2i/yluGPJSQi0xl5+00opVGs=", "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 7a5a1390b8..fed1f39649 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": "ecGX3duI1nyJ8BOjkIPLze204JXMQKL8Eq1ir8Mm/dg=", + "shasum": "4yLB19XYAdGgHBPFlVOzCkb/JUZCjSajPRSQWs+a3uE=", "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 c66561183e..c650c852f4 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": "KSkMBlnuET6wdxlrTCFlg6h1GDiCK8ShQoTbKPse0Ek=", + "shasum": "+0hxp1uhfCqe9KR+4RPDSPGHFTgRyGULKLn9XWwCmsY=", "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 b41db1a855..3e6d6b930b 100644 --- a/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json +++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json @@ -43,7 +43,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@metamask/key-tree": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/snaps-sdk": "workspace:^", "@metamask/utils": "^10.0.0", "@noble/hashes": "^1.3.1" 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 fa001b79ff..1f11726d56 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": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/snaps-sdk": "workspace:^", "@metamask/utils": "^10.0.0", "@noble/curves": "^1.1.0", 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 1ae001d6c6..183c065105 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": "edIB0mUiM5lDzUI7tbS+VWnBxyB/ujEYRoQ/luywavA=", + "shasum": "g0lygIry0x1ULrACMgFTncUXfstO2l+7iM7/D65BXqY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index 67542f9c3c..e60bc4d8d7 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -83,6 +83,7 @@ "@metamask/base-controller": "^7.0.2", "@metamask/json-rpc-engine": "^10.0.1", "@metamask/json-rpc-middleware-stream": "^8.0.5", + "@metamask/key-tree": "^10.0.1", "@metamask/object-multiplex": "^2.0.0", "@metamask/permission-controller": "^11.0.3", "@metamask/phishing-controller": "^12.0.2", diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index 2653ec545b..4c69531223 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -55,6 +55,7 @@ import { } from '@metamask/snaps-utils/test-utils'; import type { SemVerRange, SemVerVersion, Json } from '@metamask/utils'; import { + hexToBytes, assert, AssertionError, base64ToBytes, @@ -8959,6 +8960,38 @@ describe('SnapController', () => { snapController.destroy(); }); + + it('uses custom client cryptography functions', async () => { + const messenger = getSnapControllerMessenger(); + + const pbkdf2Sha512 = jest + .fn() + .mockResolvedValue(hexToBytes(ENCRYPTION_KEY)); + + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + clientCryptography: { + pbkdf2Sha512, + }, + }), + ); + + const state = { foo: 'bar' }; + await messenger.call( + 'SnapController:updateSnapState', + MOCK_SNAP_ID, + state, + true, + ); + + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); + + snapController.destroy(); + }); }); describe('SnapController:clearSnapState', () => { diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index b7499b4e05..7b655839d0 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -8,6 +8,7 @@ import type { ControllerStateChangeEvent, } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; +import type { CryptographicFunctions } from '@metamask/key-tree'; import type { Caveat, GetEndowments, @@ -711,7 +712,14 @@ type SnapControllerArgs = { * @returns The feature flags. */ getFeatureFlags: () => DynamicFeatureFlags; + + /** + * The cryptographic functions to use for the client. This may be an empty + * object to fall back to the default cryptographic functions. + */ + clientCryptography?: CryptographicFunctions; }; + type AddSnapArgs = { id: SnapId; origin: string; @@ -799,6 +807,8 @@ export class SnapController extends BaseController< #getFeatureFlags: () => DynamicFeatureFlags; + #clientCryptography: CryptographicFunctions | undefined; + #detectSnapLocation: typeof detectSnapLocation; #snapsRuntimeData: Map; @@ -832,6 +842,7 @@ export class SnapController extends BaseController< encryptor, getMnemonic, getFeatureFlags = () => ({}), + clientCryptography, }: SnapControllerArgs) { super({ messenger, @@ -887,6 +898,7 @@ export class SnapController extends BaseController< this.#encryptor = encryptor; this.#getMnemonic = getMnemonic; this.#getFeatureFlags = getFeatureFlags; + this.#clientCryptography = clientCryptography; this.#preinstalledSnaps = preinstalledSnaps; this._onUnhandledSnapError = this._onUnhandledSnapError.bind(this); this._onOutboundRequest = this._onOutboundRequest.bind(this); @@ -1754,7 +1766,13 @@ export class SnapController extends BaseController< const salt = passedSalt ?? this.#encryptor.generateSalt(); const mnemonicPhrase = await this.#getMnemonic(); - const entropy = await getEncryptionEntropy({ snapId, mnemonicPhrase }); + + const entropy = await getEncryptionEntropy({ + snapId, + mnemonicPhrase, + cryptographicFunctions: this.#clientCryptography, + }); + const encryptionKey = await this.#encryptor.keyFromPassword( entropy, salt, diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 4bea263330..d02ed0ee4a 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -573,6 +573,7 @@ export const getSnapControllerOptions = ( state: undefined, fetchFunction: jest.fn(), getMnemonic: async () => Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_BYTES), + clientCryptography: {}, encryptor: getSnapControllerEncryptor(), ...opts, } as SnapControllerConstructorParams; @@ -580,6 +581,7 @@ export const getSnapControllerOptions = ( options.state = { snaps: {}, snapStates: {}, + unencryptedSnapStates: {}, ...options.state, }; return options; diff --git a/packages/snaps-rpc-methods/package.json b/packages/snaps-rpc-methods/package.json index b4e8054f0f..3d9e4a006e 100644 --- a/packages/snaps-rpc-methods/package.json +++ b/packages/snaps-rpc-methods/package.json @@ -55,7 +55,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@metamask/key-tree": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/permission-controller": "^11.0.3", "@metamask/rpc-errors": "^7.0.1", "@metamask/snaps-sdk": "workspace:^", diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.test.ts b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.test.ts index 11000c9937..18f9912323 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.test.ts @@ -11,6 +11,7 @@ describe('specificationBuilder', () => { const methodHooks = { getMnemonic: jest.fn(), getUnlockPromise: jest.fn(), + getClientCryptography: jest.fn(), }; const specification = getBip32EntropyBuilder.specificationBuilder({ @@ -62,10 +63,15 @@ describe('getBip32EntropyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( - // @ts-expect-error Missing other required properties. - await getBip32EntropyImplementation({ getUnlockPromise, getMnemonic })({ + await getBip32EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ params: { path: ['m', "44'", "1'"], curve: 'secp256k1' }, }), ).toMatchInlineSnapshot(` @@ -75,6 +81,7 @@ describe('getBip32EntropyImplementation', () => { "depth": 2, "index": 2147483649, "masterFingerprint": 1404659567, + "network": "mainnet", "parentFingerprint": 1829122711, "privateKey": "0xc73cedb996e7294f032766853a8b7ba11ab4ce9755fc052f2f7b9000044c99af", "publicKey": "0x048e129862c1de5ca86468add43b001d32fd34b8113de716ecd63fa355b7f1165f0e76f5dc6095100f9fdaa76ddf28aa3f21406ac5fda7c71ffbedb45634fe2ceb", @@ -87,10 +94,15 @@ describe('getBip32EntropyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( - // @ts-expect-error Missing other required properties. - await getBip32EntropyImplementation({ getUnlockPromise, getMnemonic })({ + await getBip32EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ params: { path: ['m', "44'", "1'", "0'", '0', '1'], curve: 'secp256k1', @@ -103,6 +115,7 @@ describe('getBip32EntropyImplementation', () => { "depth": 5, "index": 1, "masterFingerprint": 1404659567, + "network": "mainnet", "parentFingerprint": 3495658567, "privateKey": "0x43a9353dfebf7209c3feb1843510299e2b0f4fa09151dccc3824df88451be37c", "publicKey": "0x0467f3cac111f47782b6c2d8d0984d51e22c128d24ec3eaca044509a386771d17206c740c7337c399d8ade8f52a60029340f288e11de82fffd3b69c5b863f6a515", @@ -116,9 +129,15 @@ describe('getBip32EntropyImplementation', () => { .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); + expect( - // @ts-expect-error Missing other required properties. - await getBip32EntropyImplementation({ getUnlockPromise, getMnemonic })({ + await getBip32EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ params: { path: ['m', "44'", "1'", "0'", "0'", "1'"], curve: 'ed25519', @@ -131,6 +150,7 @@ describe('getBip32EntropyImplementation', () => { "depth": 5, "index": 2147483649, "masterFingerprint": 650419359, + "network": "mainnet", "parentFingerprint": 660188756, "privateKey": "0x5e6ebe8f5c33833e6c86f8769da173daa206b9dfd1956efcd2b115d82376bb5e", "publicKey": "0x0012affaf55babdfb59b76adcf00f69442f019974124639108470409d47e25e19f", @@ -144,9 +164,15 @@ describe('getBip32EntropyImplementation', () => { .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); + expect( - // @ts-expect-error Missing other required properties. - await getBip32EntropyImplementation({ getUnlockPromise, getMnemonic })({ + await getBip32EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ params: { path: ['m', "44'", "1'", "0'", "0'", "1'"], curve: 'ed25519Bip32', @@ -159,11 +185,49 @@ describe('getBip32EntropyImplementation', () => { "depth": 5, "index": 2147483649, "masterFingerprint": 1587894111, + "network": "mainnet", "parentFingerprint": 3236688876, "privateKey": "0x88a59d7aa9fe82d8f98843ef474195178eb71956dee597252e7a5fbeebbc734e9b5bfdd17f82144a2bea78c8ab19bef26dc93f36e96eaa41453b65cb3daa1817", "publicKey": "0xd91d18b4540a2f30341e8463d5f9b25b14fae9a236dcbea338b668a318bb0867", } `); }); + + it('uses custom client cryptography functions', async () => { + const getUnlockPromise = jest.fn().mockResolvedValue(undefined); + const getMnemonic = jest + .fn() + .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + + const pbkdf2Sha512 = jest.fn().mockResolvedValue(new Uint8Array(64)); + const getClientCryptography = jest.fn().mockReturnValue({ + pbkdf2Sha512, + }); + + expect( + await getBip32EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ + params: { path: ['m', "44'", "1'"], curve: 'secp256k1' }, + }), + ).toMatchInlineSnapshot(` + { + "chainCode": "0x8472428420c7fd8ef7280545bb6d2bde1d7c6b490556ccd59895f242716388d1", + "curve": "secp256k1", + "depth": 2, + "index": 2147483649, + "masterFingerprint": 3276136937, + "network": "mainnet", + "parentFingerprint": 1981505209, + "privateKey": "0x71d945aba22cd337ff26a107073ae2606dee5dbf7ecfe5c25870b8eaf62b9f1b", + "publicKey": "0x0491c4b234ca9b394f40d90f09092e04fd3bca2aa68c57e1311b25acfd972c5a6fc7ffd19e7812127473aa2bd827917b6ec7b57bec73cf022fc1f1fa0593f48770", + } + `); + + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts index 535e11a83b..4734d71e9c 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts @@ -1,3 +1,4 @@ +import type { CryptographicFunctions } from '@metamask/key-tree'; import type { PermissionSpecificationBuilder, PermissionValidatorConstraint, @@ -31,6 +32,15 @@ export type GetBip32EntropyMethodHooks = { * @returns A promise that resolves once the extension is unlocked. */ getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise; + + /** + * Get the cryptographic functions to use for the client. This may return an + * empty object or `undefined` to fall back to the default cryptographic + * functions. + * + * @returns The cryptographic functions to use for the client. + */ + getClientCryptography: () => CryptographicFunctions | undefined; }; type GetBip32EntropySpecificationBuilderOptions = { @@ -81,6 +91,7 @@ const specificationBuilder: PermissionSpecificationBuilder< const methodHooks: MethodHooksObject = { getMnemonic: true, getUnlockPromise: true, + getClientCryptography: true, }; export const getBip32EntropyBuilder = Object.freeze({ @@ -96,12 +107,15 @@ export const getBip32EntropyBuilder = Object.freeze({ * @param hooks.getMnemonic - A function to retrieve the Secret Recovery Phrase of the user. * @param hooks.getUnlockPromise - A function that resolves once the MetaMask extension is unlocked * and prompts the user to unlock their MetaMask if it is locked. + * @param hooks.getClientCryptography - A function to retrieve the cryptographic + * functions to use for the client. * @returns The method implementation which returns a `JsonSLIP10Node`. * @throws If the params are invalid. */ export function getBip32EntropyImplementation({ getMnemonic, getUnlockPromise, + getClientCryptography, }: GetBip32EntropyMethodHooks) { return async function getBip32Entropy( args: RestrictedMethodOptions, @@ -115,6 +129,7 @@ export function getBip32EntropyImplementation({ curve: params.curve, path: params.path, secretRecoveryPhrase: await getMnemonic(), + cryptographicFunctions: getClientCryptography(), }); return node.toJSON(); diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.test.ts b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.test.ts index 91706bd1b8..a626f5f3b4 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.test.ts @@ -11,6 +11,7 @@ describe('specificationBuilder', () => { const methodHooks = { getMnemonic: jest.fn(), getUnlockPromise: jest.fn(), + getClientCryptography: jest.fn(), }; const specification = getBip32PublicKeyBuilder.specificationBuilder({ @@ -62,11 +63,13 @@ describe('getBip32PublicKeyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( await getBip32PublicKeyImplementation({ getUnlockPromise, getMnemonic, + getClientCryptography, // @ts-expect-error Missing other required properties. })({ params: { @@ -84,11 +87,13 @@ describe('getBip32PublicKeyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( await getBip32PublicKeyImplementation({ getUnlockPromise, getMnemonic, + getClientCryptography, // @ts-expect-error Missing other required properties. })({ params: { @@ -106,11 +111,13 @@ describe('getBip32PublicKeyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( await getBip32PublicKeyImplementation({ getUnlockPromise, getMnemonic, + getClientCryptography, // @ts-expect-error Missing other required properties. })({ params: { @@ -128,11 +135,13 @@ describe('getBip32PublicKeyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( await getBip32PublicKeyImplementation({ getUnlockPromise, getMnemonic, + getClientCryptography, // @ts-expect-error Missing other required properties. })({ params: { @@ -145,5 +154,36 @@ describe('getBip32PublicKeyImplementation', () => { `"0x022de17487a660993177ce2a85bb73b6cd9ad436184d57bdf5a93f5db430bea914"`, ); }); + + it('uses custom client cryptography functions', async () => { + const getUnlockPromise = jest.fn().mockResolvedValue(undefined); + const getMnemonic = jest + .fn() + .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + + const pbkdf2Sha512 = jest.fn().mockResolvedValue(new Uint8Array(64)); + const getClientCryptography = jest.fn().mockReturnValue({ + pbkdf2Sha512, + }); + + expect( + await getBip32PublicKeyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ + params: { + path: ['m', "44'", "1'", '1', '2', '3'], + curve: 'secp256k1', + compressed: true, + }, + }), + ).toMatchInlineSnapshot( + `"0x03102d63c39b6dda3f9aa06b247c50653cd9d01a91efce00ccc8735e9714058a01"`, + ); + + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts index 7f8ee3b746..ba9cfc81a0 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts @@ -1,3 +1,4 @@ +import type { CryptographicFunctions } from '@metamask/key-tree'; import type { PermissionSpecificationBuilder, PermissionValidatorConstraint, @@ -37,6 +38,15 @@ export type GetBip32PublicKeyMethodHooks = { * @returns A promise that resolves once the extension is unlocked. */ getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise; + + /** + * Get the cryptographic functions to use for the client. This may return an + * empty object or `undefined` to fall back to the default cryptographic + * functions. + * + * @returns The cryptographic functions to use for the client. + */ + getClientCryptography: () => CryptographicFunctions | undefined; }; type GetBip32PublicKeySpecificationBuilderOptions = { @@ -95,6 +105,7 @@ const specificationBuilder: PermissionSpecificationBuilder< const methodHooks: MethodHooksObject = { getMnemonic: true, getUnlockPromise: true, + getClientCryptography: true, }; export const getBip32PublicKeyBuilder = Object.freeze({ @@ -110,12 +121,15 @@ export const getBip32PublicKeyBuilder = Object.freeze({ * @param hooks.getMnemonic - A function to retrieve the Secret Recovery Phrase of the user. * @param hooks.getUnlockPromise - A function that resolves once the MetaMask extension is unlocked * and prompts the user to unlock their MetaMask if it is locked. + * @param hooks.getClientCryptography - A function to retrieve the cryptographic + * functions to use for the client. * @returns The method implementation which returns a public key. * @throws If the params are invalid. */ export function getBip32PublicKeyImplementation({ getMnemonic, getUnlockPromise, + getClientCryptography, }: GetBip32PublicKeyMethodHooks) { return async function getBip32PublicKey( args: RestrictedMethodOptions, @@ -134,6 +148,7 @@ export function getBip32PublicKeyImplementation({ curve: params.curve, path: params.path, secretRecoveryPhrase: await getMnemonic(), + cryptographicFunctions: getClientCryptography(), }); if (params.compressed) { diff --git a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.test.ts b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.test.ts index 7b7a937375..e689b55cb1 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.test.ts @@ -11,6 +11,7 @@ describe('specificationBuilder', () => { const methodHooks = { getMnemonic: jest.fn(), getUnlockPromise: jest.fn(), + getClientCryptography: jest.fn(), }; const specification = getBip44EntropyBuilder.specificationBuilder({ @@ -62,10 +63,15 @@ describe('getBip44EntropyImplementation', () => { const getMnemonic = jest .fn() .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + const getClientCryptography = jest.fn().mockReturnValue({}); expect( - // @ts-expect-error Missing other required properties. - await getBip44EntropyImplementation({ getUnlockPromise, getMnemonic })({ + await getBip44EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ params: { coinType: 1 }, }), ).toMatchInlineSnapshot(` @@ -75,6 +81,7 @@ describe('getBip44EntropyImplementation', () => { "depth": 2, "index": 2147483649, "masterFingerprint": 1404659567, + "network": "mainnet", "parentFingerprint": 1829122711, "path": "m / bip32:44' / bip32:1'", "privateKey": "0xc73cedb996e7294f032766853a8b7ba11ab4ce9755fc052f2f7b9000044c99af", @@ -82,5 +89,43 @@ describe('getBip44EntropyImplementation', () => { } `); }); + + it('uses custom client cryptography functions', async () => { + const getUnlockPromise = jest.fn().mockResolvedValue(undefined); + const getMnemonic = jest + .fn() + .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + + const pbkdf2Sha512 = jest.fn().mockResolvedValue(new Uint8Array(64)); + const getClientCryptography = jest.fn().mockReturnValue({ + pbkdf2Sha512, + }); + + expect( + await getBip44EntropyImplementation({ + getUnlockPromise, + getMnemonic, + getClientCryptography, + // @ts-expect-error Missing other required properties. + })({ + params: { coinType: 1 }, + }), + ).toMatchInlineSnapshot(` + { + "chainCode": "0x8472428420c7fd8ef7280545bb6d2bde1d7c6b490556ccd59895f242716388d1", + "coin_type": 1, + "depth": 2, + "index": 2147483649, + "masterFingerprint": 3276136937, + "network": "mainnet", + "parentFingerprint": 1981505209, + "path": "m / bip32:44' / bip32:1'", + "privateKey": "0x71d945aba22cd337ff26a107073ae2606dee5dbf7ecfe5c25870b8eaf62b9f1b", + "publicKey": "0x0491c4b234ca9b394f40d90f09092e04fd3bca2aa68c57e1311b25acfd972c5a6fc7ffd19e7812127473aa2bd827917b6ec7b57bec73cf022fc1f1fa0593f48770", + } + `); + + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts index 840129ab66..8fff798b8d 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts @@ -1,3 +1,4 @@ +import type { CryptographicFunctions } from '@metamask/key-tree'; import { BIP44CoinTypeNode } from '@metamask/key-tree'; import type { PermissionSpecificationBuilder, @@ -30,6 +31,15 @@ export type GetBip44EntropyMethodHooks = { * @returns A promise that resolves once the extension is unlocked. */ getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise; + + /** + * Get the cryptographic functions to use for the client. This may return an + * empty object or `undefined` to fall back to the default cryptographic + * functions. + * + * @returns The cryptographic functions to use for the client. + */ + getClientCryptography: () => CryptographicFunctions | undefined; }; type GetBip44EntropySpecificationBuilderOptions = { @@ -81,6 +91,7 @@ const specificationBuilder: PermissionSpecificationBuilder< const methodHooks: MethodHooksObject = { getMnemonic: true, getUnlockPromise: true, + getClientCryptography: true, }; export const getBip44EntropyBuilder = Object.freeze({ @@ -98,12 +109,15 @@ export const getBip44EntropyBuilder = Object.freeze({ * @param hooks.getUnlockPromise - A function that resolves once the MetaMask * extension is unlocked and prompts the user to unlock their MetaMask if it is * locked. + * @param hooks.getClientCryptography - A function to retrieve the cryptographic + * functions to use for the client. * @returns The method implementation which returns a `BIP44CoinTypeNode`. * @throws If the params are invalid. */ export function getBip44EntropyImplementation({ getMnemonic, getUnlockPromise, + getClientCryptography, }: GetBip44EntropyMethodHooks) { return async function getBip44Entropy( args: RestrictedMethodOptions, @@ -113,11 +127,11 @@ export function getBip44EntropyImplementation({ // `args.params` is validated by the decorator, so it's safe to assert here. const params = args.params as GetBip44EntropyParams; - const node = await BIP44CoinTypeNode.fromDerivationPath([ - await getMnemonic(), - `bip32:44'`, - `bip32:${params.coinType}'`, - ]); + const node = await BIP44CoinTypeNode.fromDerivationPath( + [await getMnemonic(), `bip32:44'`, `bip32:${params.coinType}'`], + 'mainnet', + getClientCryptography(), + ); return node.toJSON(); }; diff --git a/packages/snaps-rpc-methods/src/restricted/getEntropy.test.ts b/packages/snaps-rpc-methods/src/restricted/getEntropy.test.ts index af3f61a7d6..b59be5f495 100644 --- a/packages/snaps-rpc-methods/src/restricted/getEntropy.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/getEntropy.test.ts @@ -14,6 +14,7 @@ describe('getEntropyBuilder', () => { methodHooks: { getMnemonic: true, getUnlockPromise: true, + getClientCryptography: true, }, }); }); @@ -22,6 +23,7 @@ describe('getEntropyBuilder', () => { const methodHooks = { getMnemonic: jest.fn(), getUnlockPromise: jest.fn(), + getClientCryptography: jest.fn(), }; expect( @@ -43,10 +45,12 @@ describe('getEntropyImplementation', () => { .mockImplementation(() => TEST_SECRET_RECOVERY_PHRASE_BYTES); const getUnlockPromise = jest.fn(); + const getClientCryptography = jest.fn().mockReturnValue({}); const methodHooks = { getMnemonic, getUnlockPromise, + getClientCryptography, }; const implementation = getEntropyBuilder.specificationBuilder({ @@ -68,4 +72,43 @@ describe('getEntropyImplementation', () => { '0x6d8e92de419401c7da3cedd5f60ce5635b26059c2a4a8003877fec83653a4921', ); }); + + it('uses custom client cryptography functions', async () => { + const getUnlockPromise = jest.fn().mockResolvedValue(undefined); + const getMnemonic = jest + .fn() + .mockResolvedValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + + const pbkdf2Sha512 = jest.fn().mockResolvedValue(new Uint8Array(64)); + const getClientCryptography = jest.fn().mockReturnValue({ + pbkdf2Sha512, + }); + + const methodHooks = { + getMnemonic, + getUnlockPromise, + getClientCryptography, + }; + + const implementation = getEntropyBuilder.specificationBuilder({ + methodHooks, + }).methodImplementation; + + const result = await implementation({ + method: 'snap_getEntropy', + params: { + version: 1, + salt: 'foo', + }, + context: { + origin: MOCK_SNAP_ID, + }, + }); + + expect(result).toBe( + '0x9bea47f2180fd874147f2f455a5ccc779826cfeff005605190cf0c568b3de7b5', + ); + + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts index ecf9709ff1..b5f685cc2c 100644 --- a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts @@ -1,3 +1,4 @@ +import type { CryptographicFunctions } from '@metamask/key-tree'; import type { PermissionSpecificationBuilder, RestrictedMethodOptions, @@ -62,6 +63,7 @@ const specificationBuilder: PermissionSpecificationBuilder< const methodHooks: MethodHooksObject = { getMnemonic: true, getUnlockPromise: true, + getClientCryptography: true, }; export const getEntropyBuilder = Object.freeze({ @@ -82,6 +84,15 @@ export type GetEntropyHooks = { * @returns A promise that resolves once the extension is unlocked. */ getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise; + + /** + * Get the cryptographic functions to use for the client. This may return an + * empty object or `undefined` to fall back to the default cryptographic + * functions. + * + * @returns The cryptographic functions to use for the client. + */ + getClientCryptography: () => CryptographicFunctions | undefined; }; /** @@ -94,11 +105,14 @@ export type GetEntropyHooks = { * primary keyring. * @param hooks.getUnlockPromise - The method to get a promise that resolves * once the extension is unlocked. + * @param hooks.getClientCryptography - A function to retrieve the cryptographic + * functions to use for the client. * @returns The method implementation. */ function getEntropyImplementation({ getMnemonic, getUnlockPromise, + getClientCryptography, }: GetEntropyHooks) { return async function getEntropy( options: RestrictedMethodOptions, @@ -123,6 +137,7 @@ function getEntropyImplementation({ salt: params.salt, mnemonicPhrase, magic: SIP_6_MAGIC_VALUE, + cryptographicFunctions: getClientCryptography(), }); }; } diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts index 8d0ab5f31e..a6b2cfc45b 100644 --- a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts @@ -26,6 +26,16 @@ describe('getEncryptionEntropy', () => { expect(result).toBe(ENCRYPTION_KEY); }); + + it('accepts custom cryptographic functions', async () => { + const result = await getEncryptionEntropy({ + mnemonicPhrase: TEST_SECRET_RECOVERY_PHRASE_BYTES, + snapId: MOCK_SNAP_ID, + cryptographicFunctions: {}, + }); + + expect(result).toBe(ENCRYPTION_KEY); + }); }); describe('snap_manageState', () => { diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.ts b/packages/snaps-rpc-methods/src/restricted/manageState.ts index 73a9c5f43c..a8bfe4a81b 100644 --- a/packages/snaps-rpc-methods/src/restricted/manageState.ts +++ b/packages/snaps-rpc-methods/src/restricted/manageState.ts @@ -1,3 +1,4 @@ +import type { CryptographicFunctions } from '@metamask/key-tree'; import type { PermissionSpecificationBuilder, RestrictedMethodOptions, @@ -111,6 +112,7 @@ export const STORAGE_SIZE_LIMIT = 104857600; // In bytes (100MB) type GetEncryptionKeyArgs = { snapId: string; mnemonicPhrase: Uint8Array; + cryptographicFunctions?: CryptographicFunctions | undefined; }; /** @@ -124,17 +126,21 @@ type GetEncryptionKeyArgs = { * @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 * from. + * @param args.cryptographicFunctions - The cryptographic functions to use for + * the client. * @returns The state encryption key. */ export async function getEncryptionEntropy({ mnemonicPhrase, snapId, + cryptographicFunctions, }: GetEncryptionKeyArgs) { return await deriveEntropy({ mnemonicPhrase, input: snapId, salt: STATE_ENCRYPTION_SALT, magic: STATE_ENCRYPTION_MAGIC_VALUE, + cryptographicFunctions, }); } diff --git a/packages/snaps-rpc-methods/src/utils.test.ts b/packages/snaps-rpc-methods/src/utils.test.ts index cc48a2fa4e..6241b90e4e 100644 --- a/packages/snaps-rpc-methods/src/utils.test.ts +++ b/packages/snaps-rpc-methods/src/utils.test.ts @@ -56,6 +56,7 @@ describe('getNode', () => { "depth": 2, "index": 2147483649, "masterFingerprint": 1404659567, + "network": "mainnet", "parentFingerprint": 1829122711, "privateKey": "0xc73cedb996e7294f032766853a8b7ba11ab4ce9755fc052f2f7b9000044c99af", "publicKey": "0x048e129862c1de5ca86468add43b001d32fd34b8113de716ecd63fa355b7f1165f0e76f5dc6095100f9fdaa76ddf28aa3f21406ac5fda7c71ffbedb45634fe2ceb", @@ -77,6 +78,7 @@ describe('getNode', () => { "depth": 2, "index": 2147483649, "masterFingerprint": 650419359, + "network": "mainnet", "parentFingerprint": 4080844380, "privateKey": "0x9dee85af06f9b94d2451549f5a9b0a3bbba9e2513daebc793ca5c9a13e80cafa", "publicKey": "0x00c9aaf347832dc3b1dbb7aab4f41e5e04c64446b819c0761571c27b9f90eacb27", diff --git a/packages/snaps-rpc-methods/src/utils.ts b/packages/snaps-rpc-methods/src/utils.ts index 4b285c3b77..500038ff5b 100644 --- a/packages/snaps-rpc-methods/src/utils.ts +++ b/packages/snaps-rpc-methods/src/utils.ts @@ -3,6 +3,7 @@ import type { BIP32Node, SLIP10PathNode, SupportedCurve, + CryptographicFunctions, } from '@metamask/key-tree'; import { SLIP10Node } from '@metamask/key-tree'; import type { MagicValue } from '@metamask/snaps-utils'; @@ -106,6 +107,11 @@ type DeriveEntropyOptions = { * mnemonic phrase. */ magic: MagicValue; + + /** + * The cryptographic functions to use for the derivation. + */ + cryptographicFunctions: CryptographicFunctions | undefined; }; /** @@ -121,6 +127,8 @@ type DeriveEntropyOptions = { * 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 deriveEntropy({ @@ -128,6 +136,7 @@ export async function deriveEntropy({ salt = '', mnemonicPhrase, magic, + cryptographicFunctions, }: DeriveEntropyOptions): Promise { const inputBytes = stringToBytes(input); const saltBytes = stringToBytes(salt); @@ -137,14 +146,17 @@ export async function deriveEntropy({ const computedDerivationPath = getDerivationPathArray(hash); // Derive the private key using BIP-32. - const { privateKey } = await SLIP10Node.fromDerivationPath({ - derivationPath: [ - mnemonicPhrase, - `bip32:${magic}`, - ...computedDerivationPath, - ], - curve: 'secp256k1', - }); + const { privateKey } = await SLIP10Node.fromDerivationPath( + { + derivationPath: [ + mnemonicPhrase, + `bip32:${magic}`, + ...computedDerivationPath, + ], + curve: 'secp256k1', + }, + cryptographicFunctions, + ); // This should never happen, but this keeps TypeScript happy. assert(privateKey, 'Failed to derive the entropy.'); @@ -186,6 +198,7 @@ type GetNodeArgs = { curve: SupportedCurve; secretRecoveryPhrase: Uint8Array; path: string[]; + cryptographicFunctions: CryptographicFunctions | undefined; }; /** @@ -200,22 +213,28 @@ type GetNodeArgs = { * derivation. * @param options.path - The derivation path to use as array, starting with an * "m" as the first item. + * @param options.cryptographicFunctions - The cryptographic functions to use + * for the node. * @returns The `key-tree` SLIP-10 node. */ export async function getNode({ curve, secretRecoveryPhrase, path, + cryptographicFunctions, }: GetNodeArgs) { const prefix = getPathPrefix(curve); - return await SLIP10Node.fromDerivationPath({ - curve, - derivationPath: [ - secretRecoveryPhrase, - ...(path.slice(1).map((index) => `${prefix}:${index}`) as - | BIP32Node[] - | SLIP10PathNode[]), - ], - }); + return await SLIP10Node.fromDerivationPath( + { + curve, + derivationPath: [ + secretRecoveryPhrase, + ...(path.slice(1).map((index) => `${prefix}:${index}`) as + | BIP32Node[] + | SLIP10PathNode[]), + ], + }, + cryptographicFunctions, + ); } diff --git a/packages/snaps-sdk/package.json b/packages/snaps-sdk/package.json index 82d9c56261..4608aedf93 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": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/providers": "^18.1.1", "@metamask/rpc-errors": "^7.0.1", "@metamask/superstruct": "^3.1.0", diff --git a/packages/snaps-simulation/package.json b/packages/snaps-simulation/package.json index c2e9a2b4ea..3d3e9114e3 100644 --- a/packages/snaps-simulation/package.json +++ b/packages/snaps-simulation/package.json @@ -59,7 +59,7 @@ "@metamask/eth-json-rpc-middleware": "^15.0.0", "@metamask/json-rpc-engine": "^10.0.1", "@metamask/json-rpc-middleware-stream": "^8.0.5", - "@metamask/key-tree": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/permission-controller": "^11.0.3", "@metamask/phishing-controller": "^12.0.2", "@metamask/snaps-controllers": "workspace:^", diff --git a/packages/snaps-simulation/src/simulation.test.ts b/packages/snaps-simulation/src/simulation.test.ts index 479bc8d002..fc5adf79d0 100644 --- a/packages/snaps-simulation/src/simulation.test.ts +++ b/packages/snaps-simulation/src/simulation.test.ts @@ -485,6 +485,26 @@ describe('getHooks', () => { await close(); }); + + it('returns the `getClientCryptography` hook', async () => { + const { snapId, close } = await getMockServer(); + + const location = detectSnapLocation(snapId, { + allowLocal: true, + }); + const snapFiles = await fetchSnap(snapId, location); + + const { getClientCryptography } = getHooks( + getMockOptions(), + snapFiles, + snapId, + controllerMessenger, + ); + + expect(getClientCryptography()).toStrictEqual({}); + + await close(); + }); }); describe('registerActions', () => { diff --git a/packages/snaps-simulation/src/simulation.ts b/packages/snaps-simulation/src/simulation.ts index 29fd2405c0..cdffb66251 100644 --- a/packages/snaps-simulation/src/simulation.ts +++ b/packages/snaps-simulation/src/simulation.ts @@ -4,7 +4,10 @@ import type { } from '@metamask/base-controller'; import { ControllerMessenger } from '@metamask/base-controller'; import { createEngineStream } from '@metamask/json-rpc-middleware-stream'; -import { mnemonicPhraseToBytes } from '@metamask/key-tree'; +import { + type CryptographicFunctions, + mnemonicPhraseToBytes, +} from '@metamask/key-tree'; import { PhishingDetectorResultType } from '@metamask/phishing-controller'; import type { AbstractExecutionService } from '@metamask/snaps-controllers'; import { @@ -116,6 +119,15 @@ export type MiddlewareHooks = { * @returns A boolean flag signaling whether the client is locked. */ getIsLocked: () => boolean; + + /** + * Get the cryptographic functions to use for the client. This may return an + * empty object to fall back to the default cryptographic functions. + * + * @returns The cryptographic functions to use for the client. + */ + getClientCryptography: () => CryptographicFunctions; + createInterface: ( content: Component, context?: InterfaceContext, @@ -265,6 +277,7 @@ export function getHooks( getSnapFile: async (path: string, encoding: AuxiliaryFileEncoding) => await getSnapFile(snapFiles.auxiliaryFiles, path, encoding), getIsLocked: () => false, + getClientCryptography: () => ({}), createInterface: async (...args) => controllerMessenger.call( 'SnapInterfaceController:createInterface', diff --git a/packages/snaps-simulator/package.json b/packages/snaps-simulator/package.json index 957c18cb7d..e3ecd0c943 100644 --- a/packages/snaps-simulator/package.json +++ b/packages/snaps-simulator/package.json @@ -50,7 +50,7 @@ "@metamask/eth-json-rpc-middleware": "^15.0.0", "@metamask/json-rpc-engine": "^10.0.1", "@metamask/json-rpc-middleware-stream": "^8.0.5", - "@metamask/key-tree": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/permission-controller": "^11.0.3", "@metamask/snaps-controllers": "workspace:^", "@metamask/snaps-execution-environments": "workspace:^", diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index e64f23a9ba..a754916a47 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": "^7.0.2", - "@metamask/key-tree": "^9.1.2", + "@metamask/key-tree": "^10.0.1", "@metamask/permission-controller": "^11.0.3", "@metamask/rpc-errors": "^7.0.1", "@metamask/slip44": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index a6db9f6694..2eebc4175c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3927,7 +3927,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-sdk": "workspace:^" @@ -3968,7 +3968,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-sdk": "workspace:^" @@ -4133,7 +4133,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-sdk": "workspace:^" @@ -4191,7 +4191,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-sdk": "workspace:^" @@ -5043,16 +5043,16 @@ __metadata: languageName: unknown linkType: soft -"@metamask/key-tree@npm:^9.1.2": - version: 9.1.2 - resolution: "@metamask/key-tree@npm:9.1.2" +"@metamask/key-tree@npm:^10.0.1": + version: 10.0.1 + resolution: "@metamask/key-tree@npm:10.0.1" dependencies: "@metamask/scure-bip39": "npm:^2.1.1" - "@metamask/utils": "npm:^9.0.0" + "@metamask/utils": "npm:^10.0.1" "@noble/curves": "npm:^1.2.0" "@noble/hashes": "npm:^1.3.2" "@scure/base": "npm:^1.0.0" - checksum: 10/9b178a4156b2f36bf630564dd0530c41c6356492971d2bcc8f979c79c81144945823a5b770e4097e12b89b42133b81f00c95a7b8fe9931ea1dd928989ee3c406 + checksum: 10/a49902ef2b6f1f0c461eb4510b0c1a07d2bae2a13c4fd0f05e9c50116f6cf3db997ae9bc039197fa705e60ca410d32c52ded2d349d37452a3f2682263a1a6472 languageName: node linkType: hard @@ -5716,6 +5716,7 @@ __metadata: "@metamask/eslint-config-typescript": "npm:^12.1.0" "@metamask/json-rpc-engine": "npm:^10.0.1" "@metamask/json-rpc-middleware-stream": "npm:^8.0.5" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/permission-controller": "npm:^11.0.3" "@metamask/phishing-controller": "npm:^12.0.2" @@ -5990,7 +5991,7 @@ __metadata: "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" "@metamask/json-rpc-engine": "npm:^10.0.1" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/permission-controller": "npm:^11.0.3" "@metamask/rpc-errors": "npm:^7.0.1" "@metamask/snaps-sdk": "workspace:^" @@ -6033,7 +6034,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/providers": "npm:^18.1.1" "@metamask/rpc-errors": "npm:^7.0.1" "@metamask/superstruct": "npm:^3.1.0" @@ -6078,7 +6079,7 @@ __metadata: "@metamask/eth-json-rpc-middleware": "npm:^15.0.0" "@metamask/json-rpc-engine": "npm:^10.0.1" "@metamask/json-rpc-middleware-stream": "npm:^8.0.5" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/permission-controller": "npm:^11.0.3" "@metamask/phishing-controller": "npm:^12.0.2" "@metamask/snaps-controllers": "workspace:^" @@ -6139,7 +6140,7 @@ __metadata: "@metamask/eth-json-rpc-middleware": "npm:^15.0.0" "@metamask/json-rpc-engine": "npm:^10.0.1" "@metamask/json-rpc-middleware-stream": "npm:^8.0.5" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/permission-controller": "npm:^11.0.3" "@metamask/snaps-controllers": "workspace:^" "@metamask/snaps-execution-environments": "workspace:^" @@ -6245,7 +6246,7 @@ __metadata: "@metamask/eslint-config-jest": "npm:^12.1.0" "@metamask/eslint-config-nodejs": "npm:^12.1.0" "@metamask/eslint-config-typescript": "npm:^12.1.0" - "@metamask/key-tree": "npm:^9.1.2" + "@metamask/key-tree": "npm:^10.0.1" "@metamask/permission-controller": "npm:^11.0.3" "@metamask/post-message-stream": "npm:^8.1.1" "@metamask/rpc-errors": "npm:^7.0.1" @@ -6456,9 +6457,9 @@ __metadata: languageName: unknown linkType: soft -"@metamask/utils@npm:^10.0.0": - version: 10.0.0 - resolution: "@metamask/utils@npm:10.0.0" +"@metamask/utils@npm:^10.0.0, @metamask/utils@npm:^10.0.1": + version: 10.0.1 + resolution: "@metamask/utils@npm:10.0.1" dependencies: "@ethereumjs/tx": "npm:^4.2.0" "@metamask/superstruct": "npm:^3.1.0" @@ -6469,7 +6470,7 @@ __metadata: pony-cause: "npm:^2.1.10" semver: "npm:^7.5.4" uuid: "npm:^9.0.1" - checksum: 10/9c2e6421f685d8a45145b6026a6f9fd0701eb5a2e8490fc6d18e64c103d5a62097f301cbc797790da52ceb5853bd9f65845c934b00299e69e5e6736c52b32f0f + checksum: 10/c8e3d7578d05a1da4abb6c6712ec78ef6990801269f6529f4bb237b7d6e228d10a40738ccab81ad554f2fd51670267d086dc5be1a31c6d1f7040d4c0469d9d13 languageName: node linkType: hard