Skip to content
Merged
2 changes: 1 addition & 1 deletion packages/examples/packages/bip32/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/bip32/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/bip44/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/bip44/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/browserify/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "KSkMBlnuET6wdxlrTCFlg6h1GDiCK8ShQoTbKPse0Ek=",
"shasum": "+0hxp1uhfCqe9KR+4RPDSPGHFTgRyGULKLn9XWwCmsY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-controllers/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 92.89,
"functions": 96.71,
"lines": 98,
"lines": 98.01,
"statements": 97.71
}
1 change: 1 addition & 0 deletions packages/snaps-controllers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 22 additions & 1 deletion packages/snaps-controllers/src/snaps/SnapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -711,7 +712,16 @@ type SnapControllerArgs = {
* @returns The feature flags.
*/
getFeatureFlags: () => DynamicFeatureFlags;

/**
* 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;
};

type AddSnapArgs = {
id: SnapId;
origin: string;
Expand Down Expand Up @@ -799,6 +809,8 @@ export class SnapController extends BaseController<

#getFeatureFlags: () => DynamicFeatureFlags;

#getClientCryptography: () => CryptographicFunctions;

#detectSnapLocation: typeof detectSnapLocation;

#snapsRuntimeData: Map<SnapId, SnapRuntimeData>;
Expand Down Expand Up @@ -832,6 +844,7 @@ export class SnapController extends BaseController<
encryptor,
getMnemonic,
getFeatureFlags = () => ({}),
getClientCryptography,
}: SnapControllerArgs) {
super({
messenger,
Expand Down Expand Up @@ -887,6 +900,7 @@ export class SnapController extends BaseController<
this.#encryptor = encryptor;
this.#getMnemonic = getMnemonic;
this.#getFeatureFlags = getFeatureFlags;
this.#getClientCryptography = getClientCryptography;
this.#preinstalledSnaps = preinstalledSnaps;
this._onUnhandledSnapError = this._onUnhandledSnapError.bind(this);
this._onOutboundRequest = this._onOutboundRequest.bind(this);
Expand Down Expand Up @@ -1754,7 +1768,14 @@ export class SnapController extends BaseController<

const salt = passedSalt ?? this.#encryptor.generateSalt();
const mnemonicPhrase = await this.#getMnemonic();
const entropy = await getEncryptionEntropy({ snapId, mnemonicPhrase });
const cryptographicFunctions = this.#getClientCryptography();

const entropy = await getEncryptionEntropy({
snapId,
mnemonicPhrase,
cryptographicFunctions,
});

const encryptionKey = await this.#encryptor.keyFromPassword(
entropy,
salt,
Expand Down
2 changes: 2 additions & 0 deletions packages/snaps-controllers/src/test-utils/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,13 +573,15 @@ export const getSnapControllerOptions = (
state: undefined,
fetchFunction: jest.fn(),
getMnemonic: async () => Promise.resolve(TEST_SECRET_RECOVERY_PHRASE_BYTES),
getClientCryptography: () => ({}),
encryptor: getSnapControllerEncryptor(),
...opts,
} as SnapControllerConstructorParams;

options.state = {
snaps: {},
snapStates: {},
unencryptedSnapStates: {},
...options.state,
};
return options;
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-rpc-methods/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = deepmerge(baseConfig, {
],
coverageThreshold: {
global: {
branches: 92.88,
branches: 92.91,
functions: 97.26,
lines: 97.84,
statements: 97.36,
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-rpc-methods/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:^",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('specificationBuilder', () => {
const methodHooks = {
getMnemonic: jest.fn(),
getUnlockPromise: jest.fn(),
getClientCryptography: jest.fn(),
};

const specification = getBip32EntropyBuilder.specificationBuilder({
Expand Down Expand Up @@ -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(`
Expand All @@ -75,6 +81,7 @@ describe('getBip32EntropyImplementation', () => {
"depth": 2,
"index": 2147483649,
"masterFingerprint": 1404659567,
"network": "mainnet",
"parentFingerprint": 1829122711,
"privateKey": "0xc73cedb996e7294f032766853a8b7ba11ab4ce9755fc052f2f7b9000044c99af",
"publicKey": "0x048e129862c1de5ca86468add43b001d32fd34b8113de716ecd63fa355b7f1165f0e76f5dc6095100f9fdaa76ddf28aa3f21406ac5fda7c71ffbedb45634fe2ceb",
Expand All @@ -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',
Expand All @@ -103,6 +115,7 @@ describe('getBip32EntropyImplementation', () => {
"depth": 5,
"index": 1,
"masterFingerprint": 1404659567,
"network": "mainnet",
"parentFingerprint": 3495658567,
"privateKey": "0x43a9353dfebf7209c3feb1843510299e2b0f4fa09151dccc3824df88451be37c",
"publicKey": "0x0467f3cac111f47782b6c2d8d0984d51e22c128d24ec3eaca044509a386771d17206c740c7337c399d8ade8f52a60029340f288e11de82fffd3b69c5b863f6a515",
Expand All @@ -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',
Expand All @@ -131,6 +150,7 @@ describe('getBip32EntropyImplementation', () => {
"depth": 5,
"index": 2147483649,
"masterFingerprint": 650419359,
"network": "mainnet",
"parentFingerprint": 660188756,
"privateKey": "0x5e6ebe8f5c33833e6c86f8769da173daa206b9dfd1956efcd2b115d82376bb5e",
"publicKey": "0x0012affaf55babdfb59b76adcf00f69442f019974124639108470409d47e25e19f",
Expand All @@ -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',
Expand All @@ -159,6 +185,7 @@ describe('getBip32EntropyImplementation', () => {
"depth": 5,
"index": 2147483649,
"masterFingerprint": 1587894111,
"network": "mainnet",
"parentFingerprint": 3236688876,
"privateKey": "0x88a59d7aa9fe82d8f98843ef474195178eb71956dee597252e7a5fbeebbc734e9b5bfdd17f82144a2bea78c8ab19bef26dc93f36e96eaa41453b65cb3daa1817",
"publicKey": "0xd91d18b4540a2f30341e8463d5f9b25b14fae9a236dcbea338b668a318bb0867",
Expand Down
14 changes: 14 additions & 0 deletions packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CryptographicFunctions } from '@metamask/key-tree';
import type {
PermissionSpecificationBuilder,
PermissionValidatorConstraint,
Expand Down Expand Up @@ -31,6 +32,14 @@ export type GetBip32EntropyMethodHooks = {
* @returns A promise that resolves once the extension is unlocked.
*/
getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise<void>;

/**
* 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;
};

type GetBip32EntropySpecificationBuilderOptions = {
Expand Down Expand Up @@ -81,6 +90,7 @@ const specificationBuilder: PermissionSpecificationBuilder<
const methodHooks: MethodHooksObject<GetBip32EntropyMethodHooks> = {
getMnemonic: true,
getUnlockPromise: true,
getClientCryptography: true,
};

export const getBip32EntropyBuilder = Object.freeze({
Expand All @@ -96,12 +106,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<GetBip32EntropyParams>,
Expand All @@ -115,6 +128,7 @@ export function getBip32EntropyImplementation({
curve: params.curve,
path: params.path,
secretRecoveryPhrase: await getMnemonic(),
cryptographicFunctions: getClientCryptography(),
});

return node.toJSON();
Expand Down
Loading
Loading