-
-
Notifications
You must be signed in to change notification settings - Fork 219
Persist profile storage keys to e2ee backend #5630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
f328904
to
d492e67
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review in progress :) We should add a new entry to the changelog for this PR also.
## **Description** This is a version bump for the preinstalled @metamask/message-signing-snap to v1.1.1 This latest version has a number of improvements including support for multiple entropy sources (following SIP-30) and encryption, as well as results differentiated by origin. This snap is used by the AuthenticationController / UserStorageController and the latest version is necessary to make progress in the controllers. [](https://codespaces.new/MetaMask/metamask-extension/pull/31943?quickstart=1) ## **Related issues** relates to: [IDENTITY-7](https://consensyssoftware.atlassian.net/browse/IDENTITY-7) required by: MetaMask/core#5630 relates to: MetaMask/metamask-mobile#14653 ## **Manual testing steps** No user-facing changes ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [IDENTITY-7]: https://consensyssoftware.atlassian.net/browse/IDENTITY-7?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Signed-off-by: Mircea Nistor <[email protected]>
## **Description** This is a version bump for the preinstalled @metamask/message-signing-snap to v1.1.1 This latest version has a number of improvements including support for multiple entropy sources (following SIP-30) and encryption, as well as results differentiated by origin. This snap is used by the AuthenticationController / UserStorageController and the latest version is necessary to make progress in the controllers. [](https://codespaces.new/MetaMask/metamask-extension/pull/31943?quickstart=1) ## **Related issues** relates to: [IDENTITY-7](https://consensyssoftware.atlassian.net/browse/IDENTITY-7) required by: MetaMask/core#5630 relates to: MetaMask/metamask-mobile#14653 ## **Manual testing steps** No user-facing changes ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [IDENTITY-7]: https://consensyssoftware.atlassian.net/browse/IDENTITY-7?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Signed-off-by: Mircea Nistor <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks amazing, I know it's WIP so I've added a few comments and will wait for de-draft to approve
@@ -144,7 +148,7 @@ export function createEntryPath<T extends boolean>( | |||
options: { | |||
validateAgainstSchema: T; | |||
} = { validateAgainstSchema: true as T }, | |||
): string { | |||
): `${string}/${string}` { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hehe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
easier inference downstream
@@ -426,11 +513,11 @@ export class UserStorage { | |||
try { | |||
const headers = await this.#getAuthorizationHeader(); | |||
const storageKey = await this.getStorageKey(); | |||
const encryptedPath = createEntryPath(path, storageKey, { | |||
const entryPath = createEntryPath(path, storageKey, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice renaming this here also
profileStorageKeyPath = createEntryPath( | ||
`${USER_STORAGE_FEATURE_NAMES.keys}.${PROFILE_STORAGE_KEY}`, | ||
srpKey, | ||
{ validateAgainstSchema: false }, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel this could be moved L165 as a const assignment, and we could remove the if (profileStorageKeyPath)
L193
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh well, we could save some compute time & memory by doing this here only if (srpKey)
. Felt it was a tad complicated when reading, but it makes sense in this regard. Feel free to dismiss the above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it like this for the case where the deterministic encryption layer is missing.
Then this all short circuits to the behavior we had before
@@ -11,6 +11,7 @@ import type { | |||
StateMetadata, | |||
} from '@metamask/base-controller'; | |||
import { BaseController } from '@metamask/base-controller'; | |||
import { encrypt } from '@metamask/eth-sig-util'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: WDYT we maybe alias encrypt
so it's not confusing where it's coming from when reading L389 (@metamask/eth-sig-util
or shared/encryption/encryption.ts
).
I wouldn't know what to use though 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point!
I'll rename it
async #snapGetEncryptionPublicKey(): Promise<string> { | ||
if (this.#_snapEncryptionKeyCache) { | ||
return this.#_snapEncryptionKeyCache; | ||
} | ||
|
||
if (!this.#isUnlocked) { | ||
throw new Error( | ||
'#snapGetEncryptionPublicKey - unable to call snap, wallet is locked', | ||
); | ||
} | ||
|
||
const result = (await this.messagingSystem.call( | ||
'SnapController:handleRequest', | ||
createSnapEncryptionPublicKeyRequest(), | ||
)) as string; | ||
|
||
this.#_snapEncryptionKeyCache = result; | ||
|
||
return result; | ||
} | ||
|
||
#_snapDecryptMessageCache: Record<string, string> = {}; | ||
|
||
/** | ||
* Calls the snap to attempt to decrypt the message. | ||
* | ||
* @param ciphertext - the encrypted text to decrypt. | ||
* @returns The decrypted message, if decryption was possible. | ||
*/ | ||
async #snapDecryptMessage(ciphertext: string): Promise<string> { | ||
if (this.#_snapDecryptMessageCache[ciphertext]) { | ||
return this.#_snapDecryptMessageCache[ciphertext]; | ||
} | ||
|
||
if (!this.#isUnlocked) { | ||
throw new Error( | ||
'#snapDecryptMessage - unable to call snap, wallet is locked', | ||
); | ||
} | ||
|
||
const result = (await this.messagingSystem.call( | ||
'SnapController:handleRequest', | ||
createSnapDecryptMessageRequest(ciphertext), | ||
)) as string; | ||
|
||
if (result) { | ||
this.#_snapDecryptMessageCache[ciphertext] = result; | ||
} | ||
|
||
return result; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I feel there's a lot of overlap between those two methods and #snapSignMessage
, should we try to refactor and have one snap interaction method that handles designated caches and params?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it could provide some marginal readability benefits, but at the cost of increasing the complexity of this PR.
For now, we have 4 methods using this pattern, all in one file, all rather small, so I don't think it's a very big deal.
Let's do this in a separate issue/PR, WDYT?
@@ -1,5 +1,6 @@ | |||
import type { NotNamespacedBy } from '@metamask/base-controller'; | |||
import { Messenger } from '@metamask/base-controller'; | |||
import { decrypt, type EthEncryptedData } from '@metamask/eth-sig-util'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Same comment as for UserStorageController.ts
encrypt
, should we alias this so its clear its coming from a library?
This initial approach uses SRP/identifier specific storage keys as an intermediate layer for storing e2ee profile specific keys
…ofile storage keys
d492e67
to
a6f7ce3
Compare
## **Description** This is a version bump for the preinstalled @metamask/message-signing-snap to v1.1.1 This latest version has a number of improvements including support for multiple entropy sources (following SIP-30) and encryption, as well as results differentiated by origin. This snap is used by the AuthenticationController / UserStorageController and the latest version is necessary to make progress in the controllers. [](https://codespaces.new/MetaMask/metamask-extension/pull/31943?quickstart=1) ## **Related issues** relates to: [IDENTITY-7](https://consensyssoftware.atlassian.net/browse/IDENTITY-7) required by: MetaMask/core#5630 relates to: MetaMask/metamask-mobile#14653 ## **Manual testing steps** No user-facing changes ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [IDENTITY-7]: https://consensyssoftware.atlassian.net/browse/IDENTITY-7?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Signed-off-by: Mircea Nistor <[email protected]>
## **Description** This is a version bump for the preinstalled @metamask/message-signing-snap to v1.1.1 This latest version has a number of improvements including support for multiple entropy sources (following SIP-30) and encryption, as well as results differentiated by origin. This snap is used by the AuthenticationController / UserStorageController and the latest version is necessary to make progress in the controllers. [](https://codespaces.new/MetaMask/metamask-extension/pull/31943?quickstart=1) ## **Related issues** relates to: [IDENTITY-7](https://consensyssoftware.atlassian.net/browse/IDENTITY-7) required by: MetaMask/core#5630 relates to: MetaMask/metamask-mobile#14653 ## **Manual testing steps** No user-facing changes ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [IDENTITY-7]: https://consensyssoftware.atlassian.net/browse/IDENTITY-7?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Signed-off-by: Mircea Nistor <[email protected]>
## **Description** This is a version bump for the preinstalled @metamask/message-signing-snap to v1.1.1 This latest version has a number of improvements including support for multiple entropy sources (following SIP-30) and encryption, as well as results differentiated by origin. This snap is used by the AuthenticationController / UserStorageController and the latest version is necessary to make progress in the controllers. ## **Related issues** relates to: [IDENTITY-7](https://consensyssoftware.atlassian.net/browse/IDENTITY-7) required by: MetaMask/core#5630 relates to: MetaMask/metamask-extension#31943 ## **Manual testing steps** There are no user-facing changes to test ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [X] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [IDENTITY-7]: https://consensyssoftware.atlassian.net/browse/IDENTITY-7?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Don't merge until @metamask/[email protected]+ is available in the clients.
see MetaMask/metamask-extension#31943
and MetaMask/metamask-mobile#14653
Explanation
The backup and sync
UserStorageController
internally uses somestorageKey
s for end to end encryption (and collision avoidance). For now, these storage keys are derived using data specific to the SRP and profile ID but this blocks any progress on pairing multiple SRPs.This PR introduces a new way to manage these profile storage keys, by leveraging the new deterministic encryption capabilities in the
@metamask/message-signing-snap
.The profile storage keys, once derived, are encrypted for the public key provided by the snap and stored in the user-storage backend under a deterministic, SRP specific, key.
The user storage component will attempt to download and decrypt the profile storage key and only derive it if it cannot find it.
This is not a complete replacement of the way storage keys are managed, as we need to remain compatible with previous deployments, but at least it starts storing the encrypted keys in the right box.
This work is a prerequisite to proper pairing of profiles in the milti-SRP universe, where we need to allow different SRPs to supplement each-other's profile-data so that the user maintains a coherent experience regardless of how or where they import their different SRPs.
Dependencies
@metamask/eth-sig-util
I introduced this "new" dependency to perform the encryption.
I figured it was safe to use it since this is already a core dependency in several other controllers and is also used in the clients.
Ideally, the ERC1024 functionality that is imported from that library would be isolated in a smaller library and reused in the message-signing-snap as well. (the snap uses a different set of smaller libraries to implement the same functionality).
This new dependency may affect the bundle size of the
@metamask/profile-sync-controller
package, but it should continue to be tree-shakeable.@metamask/[email protected]
While not a direct dependency of this library, the controllers do rely on this preinstalled snap to perform signing and decryption. The decryption functionality was implemented in v1.1.0 of the snap, so that version needs to exist in the clients before a new version of the
@metamask/profile-sync-controller
is released and used.References
Related to IDENTITY-7
BLOCKED by: MetaMask/metamask-extension#31943
BLOCKED by: MetaMask/metamask-mobile#14653
Changelog
Checklist