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 7 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
54 changes: 54 additions & 0 deletions app/core/Encryptor/lib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import {
LEGACY_DERIVATION_OPTIONS,
DERIVATION_OPTIONS_MINIMUM_OWASP2023,
} from './constants';
import { stringToBytes } from '@metamask/utils';
import { NativeModules } from 'react-native';

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

describe('lib', () => {
afterEach(() => {
jest.restoreAllMocks();
});
describe('getLib', () => {
it('returns the original library', () => {
const lib = AesLib;
Expand Down Expand Up @@ -61,5 +66,54 @@ describe('lib', () => {
`Invalid number of iterations, should be: ${LEGACY_DERIVATION_OPTIONS.params.iterations}`,
);
});

it('should use 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
const lib = getEncryptionLibrary(ENCRYPTION_LIBRARY.original);

await expect(
lib.pbkdf2(
mockPasswordBytes,
mockSaltBytes,
mockIterations,
mockKeyLength,
),
).resolves.toBeDefined();
});
it('should use the native implementation of pbkdf2 with forked aes', async () => {
NativeModules.AesForked.pbkdf2 = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
'd5217329ae279885bbfe1f25ac3aacc9adabc3c9c0b9bdbaa1c095c8b03dcad0d703f96a4fa453c960a9a3e540c585fd7e6406edae20b995dcef6a0883919457',
),
);

const mockPasswordBytes = stringToBytes(mockPassword);
const mockSaltBytes = stringToBytes(mockSalt);
const mockIterations = 2048;
const mockKeyLength = 64; // 512 bits
const lib = getEncryptionLibrary(ENCRYPTION_LIBRARY.original);

await expect(
lib.pbkdf2(
mockPasswordBytes,
mockSaltBytes,
mockIterations,
mockKeyLength,
),
).resolves.toBeDefined();
});
});
});
35 changes: 35 additions & 0 deletions app/core/Encryptor/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LEGACY_DERIVATION_OPTIONS,
} from './constants';
import { EncryptionLibrary, KeyDerivationOptions } from './types';
import { bytesToString, hexToBytes } from '@metamask/utils';

// Actual native libraries
const Aes = NativeModules.Aes;
Expand Down Expand Up @@ -52,6 +53,23 @@ class AesEncryptionLibrary implements EncryptionLibrary {

decrypt = async (data: string, key: string, iv: unknown): Promise<string> =>
await Aes.decrypt(data, key, iv, CipherAlgorithm.cbc);

pbkdf2 = async (
tommasini marked this conversation as resolved.
Show resolved Hide resolved
password: Uint8Array,
salt: Uint8Array,
iterations: number,
keyLength: number,
): Promise<Uint8Array> => {
const derivationKey = await Aes.pbkdf2(
bytesToString(password),
bytesToString(salt),
iterations,
keyLength * 8,
ShaAlgorithm.Sha512,
);

return hexToBytes(derivationKey);
};
}

class AesForkedEncryptionLibrary implements EncryptionLibrary {
Expand Down Expand Up @@ -87,6 +105,23 @@ class AesForkedEncryptionLibrary implements EncryptionLibrary {

decrypt = async (data: string, key: string, iv: unknown): Promise<string> =>
await AesForked.decrypt(data, key, iv);

pbkdf2 = async (
password: Uint8Array,
salt: Uint8Array,
iterations: number,
keyLength: number,
): Promise<Uint8Array> => {
const derivationKey = await Aes.pbkdf2(
bytesToString(password),
bytesToString(salt),
iterations,
keyLength * 8, //512 bits
ShaAlgorithm.Sha512,
);

return hexToBytes(derivationKey);
};
}

// Those wrappers are stateless, we can build them only once!
Expand Down
15 changes: 15 additions & 0 deletions app/core/Encryptor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ export interface EncryptionLibrary {
* @returns The decrypted original data.
*/
decrypt(data: string, key: string, iv: string): Promise<string>;
/**
* Derives a key using PBKDF2.
*
* @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 of the key to generate.
* @returns The generated key.
*/
pbkdf2(
password: Uint8Array,
salt: Uint8Array,
iterations: number,
keyLength: number,
): Promise<Uint8Array>;
}

/**
Expand Down
9 changes: 9 additions & 0 deletions app/core/Engine/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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 @@ -199,6 +200,7 @@
EngineContext,
TransactionEventPayload,
} from './types';
import { AesLib } from '../Encryptor/lib';

const NON_EMPTY = 'NON_EMPTY';

Expand Down Expand Up @@ -505,6 +507,13 @@

additionalKeyrings.push(ledgerKeyringBuilder);

const hdKeyringBuilder = () =>
new HDKeyring({
cryptographicFunctions: { pbkdf2Sha512: AesLib.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 @@ -1851,12 +1860,12 @@
const { tokenBalances } = backgroundState.TokenBalancesController;

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

Check warning on line 1863 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 1868 in app/core/Engine/Engine.ts

View workflow job for this annotation

GitHub Actions / scripts (lint)

Unexpected label in break statement
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@
"@metamask/composable-controller": "^3.0.0",
"@metamask/controller-utils": "^11.3.0",
"@metamask/design-tokens": "^4.0.0",
"@metamask/eth-ledger-bridge-keyring": "^6.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",
"@metamask/eth-query": "^4.0.0",
"@metamask/eth-sig-util": "^7.0.2",
"@metamask/eth-snap-keyring": "^4.3.3",
Expand Down
31 changes: 27 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4258,6 +4258,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 @@ -4551,6 +4563,17 @@
"@metamask/utils" "^9.1.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 Expand Up @@ -5221,10 +5244,10 @@
lodash "^4.17.21"
uuid "^8.3.2"

"@metamask/utils@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-10.0.0.tgz#9285e6e195810e8b7c875147ac64981b4be51733"
integrity sha512-EoNZJijLqBbir8ikuiHBHfhCqE1s8Odae3bhtRAd8itJB109xmfFF84djY/iaQI+EAp59Sy7iwengfRohaTK8A==
"@metamask/utils@^10.0.0", "@metamask/utils@^10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-10.0.1.tgz#a765f96c20e35fc51c068fb9f88a3332b40b215e"
integrity sha512-zHgAitJtRwviVVFnRUA2PLRMaAwatr3jiHgiH7mPicJaeSK4ma01aGR4fHy0iy5tlVo1ZiioTmJ1Hbp8FZ6pSg==
dependencies:
"@ethereumjs/tx" "^4.2.0"
"@metamask/superstruct" "^3.1.0"
Expand Down
Loading