Skip to content
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

chore: Add eth hd keyring and key tree to decrease unlock time #12428

Merged
merged 31 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4ed8536
add eth hd keyring and key tree to decrease unlock time
tommasini Nov 26, 2024
2834fba
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 26, 2024
1bc2376
update types and tests of encryptor
tommasini Nov 26, 2024
e6e8c10
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 26, 2024
3e0c818
remove patches and update eth hd keyring to v9
tommasini Nov 27, 2024
e3d3f5a
deduplicate
tommasini Nov 27, 2024
c1704e9
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 27, 2024
e3970d3
Isolate pbkdf2 in a single file, add test coverage to test multiple s…
tommasini Nov 27, 2024
66fda61
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 27, 2024
38a192b
remove unnecessary tsdocs in test file
tommasini Nov 27, 2024
70d693f
address const name nit
tommasini Nov 27, 2024
b85855b
remove pbkdf2 forked, since we do not need it for this work
tommasini Nov 27, 2024
3b774b1
remove the ubused imports from lib
tommasini Nov 27, 2024
6335615
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 27, 2024
601f173
merge main and solve conflicts
tommasini Nov 27, 2024
0533237
Update app/core/Encryptor/pbkdf2.ts
tommasini Nov 27, 2024
a8c39a0
Update app/core/Encryptor/pbkdf2.test.ts
tommasini Nov 27, 2024
2666dbe
add bytesToBits util function
tommasini Nov 27, 2024
a436b52
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 28, 2024
da57e1f
update keyring controller patch
tommasini Nov 28, 2024
84dc57e
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 28, 2024
51b7b82
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 28, 2024
2d533a0
Update app/core/Encryptor/pbkdf2.test.ts
tommasini Nov 28, 2024
2cc6d96
Update app/core/Encryptor/pbkdf2.test.ts
tommasini Nov 28, 2024
085e8e1
Update app/core/Encryptor/pbkdf2.test.ts
tommasini Nov 28, 2024
d901073
Update app/core/Encryptor/pbkdf2.test.ts
tommasini Nov 28, 2024
9fdc49b
Update app/util/bytes.test.ts
tommasini Nov 28, 2024
6447f42
rename bytesToBits to bytesLengthToBitsLength
tommasini Nov 28, 2024
64ed517
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 28, 2024
0716d9f
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Nov 29, 2024
1dba714
Merge branch 'main' into chore/add-eth-hd-keyring
tommasini Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/core/Encryptor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
DERIVATION_OPTIONS_MINIMUM_OWASP2023,
DERIVATION_OPTIONS_DEFAULT_OWASP2023,
} from './constants';

import { pbkdf2 } from './pbkdf2';
export {
Encryptor,
ENCRYPTION_LIBRARY,
LEGACY_DERIVATION_OPTIONS,
DERIVATION_OPTIONS_MINIMUM_OWASP2023,
DERIVATION_OPTIONS_DEFAULT_OWASP2023,
pbkdf2,
};
76 changes: 76 additions & 0 deletions app/core/Encryptor/pbkdf2.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { stringToBytes } from '@metamask/utils';
import { pbkdf2 } from './pbkdf2';
import { NativeModules } from 'react-native';

const mockPassword = 'mockPassword';
const mockSalt = '00112233445566778899001122334455';

describe('pbkdf2', () => {
afterEach(() => {
jest.restoreAllMocks();
});

Check warning on line 12 in app/core/Encryptor/pbkdf2.test.ts

View workflow job for this annotation

GitHub Actions / scripts (lint)

Trailing spaces not allowed
it('uses the native implementation of pbkdf2 with main aes', async () => {
NativeModules.Aes.pbkdf2 = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
'd5217329ae279885bbfe1f25ac3aacc9adabc3c9c0b9bdbaa1c095c8b03dcad0d703f96a4fa453c960a9a3e540c585fd7e6406edae20b995dcef6a0883919457',
),
);

const mockPasswordBytes = stringToBytes(mockPassword);
const mockSaltBytes = stringToBytes(mockSalt);
const mockIterations = 2048;
const mockKeyLength = 64; // 512 bits

await expect(
pbkdf2(mockPasswordBytes, mockSaltBytes, mockIterations, mockKeyLength),
).resolves.toBeDefined();
});

it('throws on native module errors', async () => {
NativeModules.Aes.pbkdf2 = jest
.fn()
.mockRejectedValue(new Error('Native module error'));

const mockPasswordBytes = stringToBytes('password');
const mockSaltBytes = stringToBytes('salt');

await expect(
pbkdf2(mockPasswordBytes, mockSaltBytes, 2048, 64),
).rejects.toThrow('Native module error');
});

it('does not fail when empty password', async () => {
NativeModules.Aes.pbkdf2 = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
'0000000000000000000000000000000000000000000000000000000000000000',
),
);

const mockPasswordBytes = stringToBytes('');
const mockSaltBytes = stringToBytes(mockSalt);

const result = await pbkdf2(mockPasswordBytes, mockSaltBytes, 2048, 64);
expect(result).toBeDefined();
});

it('does not fail when empty salt', async () => {
NativeModules.Aes.pbkdf2 = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
'f347723a89a783c4de6a65d6a066d0e9a9c13319f8389f97d0566c79d87b6f80',
),
);

const mockPasswordBytes = stringToBytes(mockPassword);
const mockSaltBytes = stringToBytes('');

const result = await pbkdf2(mockPasswordBytes, mockSaltBytes, 2048, 64);
expect(result).toBeDefined();
});
});
33 changes: 33 additions & 0 deletions app/core/Encryptor/pbkdf2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { bytesToString, hexToBytes } from '@metamask/utils';
import { NativeModules } from 'react-native';
import { ShaAlgorithm } from './constants';
import { bytesLengthToBitsLength } from '../../util/bytes';

/**
* Derives a key using PBKDF2.
tommasini marked this conversation as resolved.
Show resolved Hide resolved
*
* @param password - The password used to generate the key.
* @param salt - The salt used during key generation.
* @param iterations - The number of iterations used during key generation.
* @param keyLength - The length (in bytes) of the key to generate.
* @returns The generated key.
*/
const pbkdf2 = async (
password: Uint8Array,
salt: Uint8Array,
iterations: number,
keyLength: number,
): Promise<Uint8Array> => {
const Aes = NativeModules.Aes;
const derivedKey = await Aes.pbkdf2(
bytesToString(password),
bytesToString(salt),
iterations,
bytesLengthToBitsLength(keyLength),
ShaAlgorithm.Sha512,
);

return hexToBytes(derivedKey);
};

export { pbkdf2 };
10 changes: 9 additions & 1 deletion app/core/Engine/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
AcceptOptions,
ApprovalController,
} from '@metamask/approval-controller';
import HDKeyring from '@metamask/eth-hd-keyring';
import { SelectedNetworkController } from '@metamask/selected-network-controller';
import {
PermissionController,
Expand Down Expand Up @@ -80,7 +81,7 @@
LedgerMobileBridge,
LedgerTransportMiddleware,
} from '@metamask/eth-ledger-bridge-keyring';
import { Encryptor, LEGACY_DERIVATION_OPTIONS } from '../Encryptor';
import { Encryptor, LEGACY_DERIVATION_OPTIONS, pbkdf2 } from '../Encryptor';
import {
isMainnetByChainId,
fetchEstimatedMultiLayerL1Fee,
Expand Down Expand Up @@ -522,6 +523,13 @@

additionalKeyrings.push(ledgerKeyringBuilder);

const hdKeyringBuilder = () =>
new HDKeyring({
cryptographicFunctions: { pbkdf2Sha512: pbkdf2 },
});
hdKeyringBuilder.type = HDKeyring.type;
additionalKeyrings.push(hdKeyringBuilder);

///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
const snapKeyringBuildMessenger = this.controllerMessenger.getRestricted({
name: 'SnapKeyringBuilder',
Expand Down Expand Up @@ -1869,12 +1877,12 @@
const { tokenBalances } = backgroundState.TokenBalancesController;

let tokenFound = false;
tokenLoop: for (const chains of Object.values(tokenBalances)) {

Check warning on line 1880 in app/core/Engine/Engine.ts

View workflow job for this annotation

GitHub Actions / scripts (lint)

Unexpected labeled statement
for (const tokens of Object.values(chains)) {
for (const balance of Object.values(tokens)) {
if (!isZero(balance)) {
tokenFound = true;
break tokenLoop;

Check warning on line 1885 in app/core/Engine/Engine.ts

View workflow job for this annotation

GitHub Actions / scripts (lint)

Unexpected label in break statement
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions app/util/bytes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { bytesLengthToBitsLength } from './bytes';

describe('bytesLengthToBitsLength', () => {
it('converts bytes length to bits length', () => {
expect(bytesLengthToBitsLength(1)).toBe(8);
expect(bytesLengthToBitsLength(2)).toBe(16);
expect(bytesLengthToBitsLength(32)).toBe(256);
expect(bytesLengthToBitsLength(0)).toBe(0);
});
});
10 changes: 10 additions & 0 deletions app/util/bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ export default function byteArrayToHex(value: Uint8Array): string {
}
return '0x' + result.join('');
}

/**
* Converts bytes length to bits length
*
* @param bytesLength - Bytes length to convert
* @returns Bits length
*/
export function bytesLengthToBitsLength(bytesLength: number): number {
return bytesLength * 8;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"@metamask/composable-controller": "^3.0.0",
"@metamask/controller-utils": "^11.3.0",
"@metamask/design-tokens": "^4.0.0",
"@metamask/eth-hd-keyring": "^9.0.0",
"@metamask/eth-json-rpc-filters": "^8.0.0",
"@metamask/eth-json-rpc-middleware": "^11.0.2",
"@metamask/eth-ledger-bridge-keyring": "^6.0.0",
Expand Down
14 changes: 14 additions & 0 deletions patches/@metamask+keyring-controller+18.0.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
diff --git a/node_modules/@metamask/keyring-controller/dist/KeyringController.cjs b/node_modules/@metamask/keyring-controller/dist/KeyringController.cjs
index 8529ba1..77871be 100644
--- a/node_modules/@metamask/keyring-controller/dist/KeyringController.cjs
+++ b/node_modules/@metamask/keyring-controller/dist/KeyringController.cjs
@@ -1379,7 +1379,8 @@ async function _KeyringController_newKeyring(type, data) {
if (!keyring.generateRandomMnemonic) {
throw new Error(constants_1.KeyringControllerError.UnsupportedGenerateRandomMnemonic);
}
- keyring.generateRandomMnemonic();
+ // This patch can be removed once all the keyrings types are updated across the monorepo, the work is in progress atm
+ await keyring.generateRandomMnemonic();
await keyring.addAccounts(1);
}
await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_checkForDuplicate).call(this, type, await keyring.getAccounts());
23 changes: 23 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4255,6 +4255,18 @@
"@metamask/utils" "^9.2.1"
ethereum-cryptography "^2.1.2"

"@metamask/eth-hd-keyring@^9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@metamask/eth-hd-keyring/-/eth-hd-keyring-9.0.0.tgz#cba58308af5cfb9b7b1b958eeea380ec9c798e64"
integrity sha512-dhvirCCWmFGd0HyiEmho0Zwdl2g86kLA+K78f3uHnV1PV0ELKsMgRqlcA8OuXlrz0jnGQPDRNFXGG6/yFyyLlg==
dependencies:
"@ethereumjs/util" "^8.1.0"
"@metamask/eth-sig-util" "^8.0.0"
"@metamask/key-tree" "^10.0.0"
"@metamask/scure-bip39" "^2.1.1"
"@metamask/utils" "^9.2.1"
ethereum-cryptography "^2.1.2"

"@metamask/eth-json-rpc-filters@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-filters/-/eth-json-rpc-filters-8.0.0.tgz#fd0ca224dc198e270e142c1f2007e05cacb5f16a"
Expand Down Expand Up @@ -4548,6 +4560,17 @@
"@metamask/utils" "^10.0.0"
readable-stream "^3.6.2"

"@metamask/key-tree@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@metamask/key-tree/-/key-tree-10.0.0.tgz#58eb9b7ba2b92a5ffa170ce4efdd236e5ac7e891"
integrity sha512-U95FwOOg4d61uJp1x2g0MH66eOtjLwsthZiBGMgP3PYMgdOb4exHynBCFqZ6wxxQbYGdDyBjC6USVRT7idkGKw==
dependencies:
"@metamask/scure-bip39" "^2.1.1"
"@metamask/utils" "^10.0.1"
"@noble/curves" "^1.2.0"
"@noble/hashes" "^1.3.2"
"@scure/base" "^1.0.0"

"@metamask/key-tree@^9.0.0", "@metamask/key-tree@^9.1.2":
version "9.1.2"
resolved "https://registry.yarnpkg.com/@metamask/key-tree/-/key-tree-9.1.2.tgz#3f89fc7990c395be3aa9c3e6e045d3d28768149b"
Expand Down
Loading