@sidhujag/sysweb3-keyring 1.0.545 → 1.0.547
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/coverage/clover.xml +2875 -0
- package/coverage/coverage-final.json +29468 -0
- package/coverage/lcov-report/base.css +354 -0
- package/coverage/lcov-report/block-navigation.js +85 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +320 -0
- package/coverage/lcov-report/prettify.css +101 -0
- package/coverage/lcov-report/prettify.js +1008 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +191 -0
- package/coverage/lcov-report/src/index.html +276 -0
- package/coverage/lcov-report/src/index.ts.html +114 -0
- package/coverage/lcov-report/src/initial-state.ts.html +558 -0
- package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
- package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
- package/coverage/lcov-report/src/ledger/index.html +216 -0
- package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
- package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
- package/coverage/lcov-report/src/signers.ts.html +591 -0
- package/coverage/lcov-report/src/storage.ts.html +198 -0
- package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
- package/coverage/lcov-report/src/transactions/index.html +216 -0
- package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
- package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
- package/coverage/lcov-report/src/trezor/index.html +176 -0
- package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
- package/coverage/lcov-report/src/types.ts.html +1443 -0
- package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
- package/coverage/lcov-report/src/utils/index.html +196 -0
- package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
- package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
- package/coverage/lcov-report/test/helpers/index.html +176 -0
- package/coverage/lcov.info +4832 -0
- package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
- package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
- package/{cjs → dist/cjs}/transactions/ethereum.js +6 -2
- package/dist/cjs/transactions/ethereum.js.map +1 -0
- package/dist/package.json +50 -0
- package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
- package/examples/basic-usage.js +140 -0
- package/jest.config.js +32 -0
- package/package.json +31 -13
- package/readme.md +201 -0
- package/src/declare.d.ts +7 -0
- package/src/errorUtils.ts +83 -0
- package/src/hardware-wallet-manager.ts +655 -0
- package/src/index.ts +12 -0
- package/src/initial-state.ts +108 -0
- package/src/keyring-manager.ts +2698 -0
- package/src/ledger/bitcoin_client/index.ts +19 -0
- package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
- package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
- package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
- package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
- package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
- package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
- package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
- package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
- package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
- package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
- package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
- package/src/ledger/consts.ts +3 -0
- package/src/ledger/index.ts +685 -0
- package/src/ledger/types.ts +74 -0
- package/src/network-utils.ts +99 -0
- package/src/providers.ts +345 -0
- package/src/signers.ts +158 -0
- package/src/storage.ts +63 -0
- package/src/transactions/__tests__/integration.test.ts +303 -0
- package/src/transactions/__tests__/syscoin.test.ts +409 -0
- package/src/transactions/ethereum.ts +2503 -0
- package/src/transactions/index.ts +2 -0
- package/src/transactions/syscoin.ts +542 -0
- package/src/trezor/index.ts +1050 -0
- package/src/types.ts +366 -0
- package/src/utils/derivation-paths.ts +133 -0
- package/src/utils/psbt.ts +24 -0
- package/src/utils.ts +191 -0
- package/test/README.md +158 -0
- package/test/__mocks__/ledger-mock.js +20 -0
- package/test/__mocks__/trezor-mock.js +75 -0
- package/test/cleanup-summary.md +167 -0
- package/test/helpers/README.md +78 -0
- package/test/helpers/constants.ts +79 -0
- package/test/helpers/setup.ts +714 -0
- package/test/integration/import-validation.spec.ts +588 -0
- package/test/unit/hardware/ledger.spec.ts +869 -0
- package/test/unit/hardware/trezor.spec.ts +828 -0
- package/test/unit/keyring-manager/account-management.spec.ts +970 -0
- package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
- package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
- package/test/unit/keyring-manager/initialization.spec.ts +782 -0
- package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
- package/test/unit/keyring-manager/security.spec.ts +505 -0
- package/test/unit/keyring-manager/state-management.spec.ts +375 -0
- package/test/unit/network/network-management.spec.ts +372 -0
- package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
- package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
- package/tsconfig.json +14 -0
- package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
- package/cjs/transactions/ethereum.js.map +0 -1
- /package/{README.md → dist/README.md} +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/index.js +0 -0
- /package/{cjs → dist/cjs}/index.js.map +0 -0
- /package/{cjs → dist/cjs}/initial-state.js +0 -0
- /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
- /package/{cjs → dist/cjs}/network-utils.js +0 -0
- /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
- /package/{cjs → dist/cjs}/providers.js +0 -0
- /package/{cjs → dist/cjs}/providers.js.map +0 -0
- /package/{cjs → dist/cjs}/signers.js +0 -0
- /package/{cjs → dist/cjs}/signers.js.map +0 -0
- /package/{cjs → dist/cjs}/storage.js +0 -0
- /package/{cjs → dist/cjs}/storage.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
- /package/{cjs → dist/cjs}/types.js +0 -0
- /package/{cjs → dist/cjs}/types.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
- /package/{cjs → dist/cjs}/utils.js +0 -0
- /package/{cjs → dist/cjs}/utils.js.map +0 -0
- /package/{types → dist/types}/errorUtils.d.ts +0 -0
- /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
- /package/{types → dist/types}/index.d.ts +0 -0
- /package/{types → dist/types}/initial-state.d.ts +0 -0
- /package/{types → dist/types}/keyring-manager.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
- /package/{types → dist/types}/ledger/consts.d.ts +0 -0
- /package/{types → dist/types}/ledger/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/types.d.ts +0 -0
- /package/{types → dist/types}/network-utils.d.ts +0 -0
- /package/{types → dist/types}/providers.d.ts +0 -0
- /package/{types → dist/types}/signers.d.ts +0 -0
- /package/{types → dist/types}/storage.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
- /package/{types → dist/types}/transactions/index.d.ts +0 -0
- /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
- /package/{types → dist/types}/trezor/index.d.ts +0 -0
- /package/{types → dist/types}/types.d.ts +0 -0
- /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
- /package/{types → dist/types}/utils/psbt.d.ts +0 -0
- /package/{types → dist/types}/utils.d.ts +0 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { INetworkType } from '@sidhujag/sysweb3-network';
|
|
2
|
+
|
|
3
|
+
import { KeyringManager, KeyringAccountType } from '../../../src';
|
|
4
|
+
import { FAKE_PASSWORD, PEACE_SEED_PHRASE } from '../../helpers/constants';
|
|
5
|
+
import { setupMocks } from '../../helpers/setup';
|
|
6
|
+
|
|
7
|
+
describe('KeyringManager - Security', () => {
|
|
8
|
+
let keyringManager: KeyringManager;
|
|
9
|
+
let mockVaultStateGetter: jest.Mock;
|
|
10
|
+
let currentVaultState: any;
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
setupMocks();
|
|
14
|
+
// Set up vault-keys that would normally be created by Pali's MainController
|
|
15
|
+
await setupTestVault(FAKE_PASSWORD);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('Password Management', () => {
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
// Set up EVM vault state
|
|
21
|
+
currentVaultState = createMockVaultState({
|
|
22
|
+
activeAccountId: 0,
|
|
23
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
24
|
+
networkType: INetworkType.Ethereum,
|
|
25
|
+
chainId: 1,
|
|
26
|
+
});
|
|
27
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
28
|
+
|
|
29
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
30
|
+
PEACE_SEED_PHRASE,
|
|
31
|
+
FAKE_PASSWORD,
|
|
32
|
+
mockVaultStateGetter
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should require password for sensitive operations', async () => {
|
|
37
|
+
// getSeed requires password
|
|
38
|
+
await expect(keyringManager.getSeed('wrong_password')).rejects.toThrow(
|
|
39
|
+
'Invalid password'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// getPrivateKeyByAccountId requires password
|
|
43
|
+
await expect(
|
|
44
|
+
keyringManager.getPrivateKeyByAccountId(
|
|
45
|
+
0,
|
|
46
|
+
KeyringAccountType.HDAccount,
|
|
47
|
+
'wrong_password'
|
|
48
|
+
)
|
|
49
|
+
).rejects.toThrow('Invalid password');
|
|
50
|
+
|
|
51
|
+
// forgetMainWallet requires password
|
|
52
|
+
await expect(
|
|
53
|
+
keyringManager.forgetMainWallet('wrong_password')
|
|
54
|
+
).rejects.toThrow('Invalid password');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should validate password complexity in real implementation', async () => {
|
|
58
|
+
// Note: This is a placeholder for password complexity validation
|
|
59
|
+
// In real implementation, weak passwords should be rejected
|
|
60
|
+
const weakPasswords = ['123', 'abc', 'password', ''];
|
|
61
|
+
|
|
62
|
+
// Current implementation accepts any password
|
|
63
|
+
// This test documents expected behavior for future implementation
|
|
64
|
+
// Future: should reject weak passwords
|
|
65
|
+
expect(weakPasswords.length).toBeGreaterThan(0); // Placeholder assertion
|
|
66
|
+
// for (const weakPassword of weakPasswords) {
|
|
67
|
+
// await expect(
|
|
68
|
+
// KeyringManager.createInitialized(PEACE_SEED_PHRASE, weakPassword, INetworkType.Ethereum, mockVaultStateGetter)
|
|
69
|
+
// ).rejects.toThrow('Password too weak');
|
|
70
|
+
// }
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle password changes securely', async () => {
|
|
74
|
+
// Current implementation doesn't support password changes
|
|
75
|
+
// This test documents expected behavior for future implementation
|
|
76
|
+
// Future implementation:
|
|
77
|
+
// await keyringManager.changePassword(FAKE_PASSWORD, 'newPassword123!');
|
|
78
|
+
// keyringManager.lockWallet();
|
|
79
|
+
// const result = await keyringManager.unlock('newPassword123!');
|
|
80
|
+
// expect(result.canLogin).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('Encryption', () => {
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
// Set up EVM vault state
|
|
87
|
+
currentVaultState = createMockVaultState({
|
|
88
|
+
activeAccountId: 0,
|
|
89
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
90
|
+
networkType: INetworkType.Ethereum,
|
|
91
|
+
chainId: 1,
|
|
92
|
+
});
|
|
93
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
94
|
+
|
|
95
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
96
|
+
PEACE_SEED_PHRASE,
|
|
97
|
+
FAKE_PASSWORD,
|
|
98
|
+
mockVaultStateGetter
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should encrypt sensitive data in memory', async () => {
|
|
103
|
+
// Test that keyring is unlocked (session data exists)
|
|
104
|
+
expect(keyringManager.isUnlocked()).toBe(true);
|
|
105
|
+
|
|
106
|
+
// Test that we can retrieve the seed (proving session data is encrypted and functional)
|
|
107
|
+
const retrievedSeed = await keyringManager.getSeed(FAKE_PASSWORD);
|
|
108
|
+
expect(retrievedSeed).toBe(PEACE_SEED_PHRASE);
|
|
109
|
+
|
|
110
|
+
// Test that private keys are properly encrypted in storage
|
|
111
|
+
const activeAccount = keyringManager.getActiveAccount().activeAccount;
|
|
112
|
+
expect(activeAccount.address).toBeDefined();
|
|
113
|
+
expect(activeAccount.xpub).toBeDefined();
|
|
114
|
+
// Private key should not be exposed in public API
|
|
115
|
+
expect(activeAccount).not.toHaveProperty('xprv');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should encrypt private keys in vault state', async () => {
|
|
119
|
+
// Check HD account in vault state
|
|
120
|
+
const hdAccount =
|
|
121
|
+
currentVaultState.accounts[KeyringAccountType.HDAccount][0];
|
|
122
|
+
|
|
123
|
+
// xprv should be encrypted
|
|
124
|
+
expect(hdAccount.xprv).toBeDefined();
|
|
125
|
+
expect(hdAccount.xprv.startsWith('0x')).toBe(false); // Not plaintext hex
|
|
126
|
+
expect(hdAccount.xprv.length).toBeGreaterThan(66); // Longer than raw key
|
|
127
|
+
|
|
128
|
+
// Import an account
|
|
129
|
+
const privateKey =
|
|
130
|
+
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318';
|
|
131
|
+
const imported = await keyringManager.importAccount(privateKey);
|
|
132
|
+
|
|
133
|
+
// Update vault state to include the imported account
|
|
134
|
+
currentVaultState.accounts[KeyringAccountType.Imported][imported.id] = {
|
|
135
|
+
id: imported.id,
|
|
136
|
+
label: 'Imported 1',
|
|
137
|
+
address: imported.address,
|
|
138
|
+
xpub: imported.xpub,
|
|
139
|
+
xprv: imported.xprv,
|
|
140
|
+
isImported: true,
|
|
141
|
+
isTrezorWallet: false,
|
|
142
|
+
isLedgerWallet: false,
|
|
143
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
144
|
+
assets: { syscoin: [], ethereum: [] },
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const importedAccount =
|
|
148
|
+
currentVaultState.accounts[KeyringAccountType.Imported][imported.id];
|
|
149
|
+
expect(importedAccount.xprv).not.toBe(privateKey);
|
|
150
|
+
expect(importedAccount.xprv.length).toBeGreaterThan(privateKey.length);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should clear sensitive data on lock', () => {
|
|
154
|
+
// Verify wallet is unlocked
|
|
155
|
+
expect(keyringManager.isUnlocked()).toBe(true);
|
|
156
|
+
|
|
157
|
+
// Lock wallet
|
|
158
|
+
keyringManager.lockWallet();
|
|
159
|
+
|
|
160
|
+
// Session data should be cleared - keyring should not be unlocked
|
|
161
|
+
expect(keyringManager.isUnlocked()).toBe(false);
|
|
162
|
+
|
|
163
|
+
// Should not be able to perform sensitive operations without unlocking
|
|
164
|
+
expect(keyringManager.getActiveAccount()).toBeDefined(); // Public data still accessible
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('Key Derivation Security', () => {
|
|
169
|
+
it('should use standard derivation paths', async () => {
|
|
170
|
+
// Set up EVM vault state
|
|
171
|
+
const evmVaultState = createMockVaultState({
|
|
172
|
+
activeAccountId: 0,
|
|
173
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
174
|
+
networkType: INetworkType.Ethereum,
|
|
175
|
+
chainId: 1,
|
|
176
|
+
});
|
|
177
|
+
const evmVaultStateGetter = jest.fn(() => evmVaultState);
|
|
178
|
+
|
|
179
|
+
// EVM should use BIP44 m/44'/60'/0'/0/x
|
|
180
|
+
const evmKeyring = await KeyringManager.createInitialized(
|
|
181
|
+
PEACE_SEED_PHRASE,
|
|
182
|
+
FAKE_PASSWORD,
|
|
183
|
+
evmVaultStateGetter
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Set up UTXO vault state
|
|
187
|
+
const utxoVaultState = createMockVaultState({
|
|
188
|
+
activeAccountId: 0,
|
|
189
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
190
|
+
networkType: INetworkType.Syscoin,
|
|
191
|
+
chainId: 57,
|
|
192
|
+
});
|
|
193
|
+
const utxoVaultStateGetter = jest.fn(() => utxoVaultState);
|
|
194
|
+
|
|
195
|
+
// UTXO should use BIP84 m/84'/57'/0'/0/x for Syscoin
|
|
196
|
+
const utxoKeyring = await KeyringManager.createInitialized(
|
|
197
|
+
PEACE_SEED_PHRASE,
|
|
198
|
+
FAKE_PASSWORD,
|
|
199
|
+
utxoVaultStateGetter
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Addresses should be different due to different derivation paths
|
|
203
|
+
const evmAddress = evmKeyring.getActiveAccount().activeAccount.address;
|
|
204
|
+
const utxoAddress = utxoKeyring.getActiveAccount().activeAccount.address;
|
|
205
|
+
|
|
206
|
+
expect(evmAddress).not.toBe(utxoAddress);
|
|
207
|
+
expect(evmAddress.startsWith('0x')).toBe(true);
|
|
208
|
+
expect(utxoAddress.match(/^(sys1|tsys1)/)).toBeTruthy();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should not expose intermediate keys', async () => {
|
|
212
|
+
// Set up EVM vault state
|
|
213
|
+
currentVaultState = createMockVaultState({
|
|
214
|
+
activeAccountId: 0,
|
|
215
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
216
|
+
networkType: INetworkType.Ethereum,
|
|
217
|
+
chainId: 1,
|
|
218
|
+
});
|
|
219
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
220
|
+
|
|
221
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
222
|
+
PEACE_SEED_PHRASE,
|
|
223
|
+
FAKE_PASSWORD,
|
|
224
|
+
mockVaultStateGetter
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Only final private keys should be accessible, not master keys
|
|
228
|
+
const privateKey = await keyringManager.getPrivateKeyByAccountId(
|
|
229
|
+
0,
|
|
230
|
+
KeyringAccountType.HDAccount,
|
|
231
|
+
FAKE_PASSWORD
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Should be account private key, not master private key
|
|
235
|
+
expect(privateKey.startsWith('0x')).toBe(true);
|
|
236
|
+
expect(privateKey.length).toBe(66); // Standard Ethereum private key length
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('Vault Security', () => {
|
|
241
|
+
it('should protect vault with proper encryption', async () => {
|
|
242
|
+
// Vault should require password to decrypt
|
|
243
|
+
keyringManager = new KeyringManager();
|
|
244
|
+
|
|
245
|
+
// Set up mock vault state getter for initializeWalletSecurely
|
|
246
|
+
currentVaultState = createMockVaultState({
|
|
247
|
+
activeAccountId: 0,
|
|
248
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
249
|
+
networkType: INetworkType.Ethereum,
|
|
250
|
+
chainId: 1,
|
|
251
|
+
});
|
|
252
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
253
|
+
keyringManager.setVaultStateGetter(mockVaultStateGetter);
|
|
254
|
+
|
|
255
|
+
await keyringManager.initializeWalletSecurely(
|
|
256
|
+
PEACE_SEED_PHRASE,
|
|
257
|
+
FAKE_PASSWORD
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Lock and try to unlock with wrong password
|
|
261
|
+
keyringManager.lockWallet();
|
|
262
|
+
const wrongResult = await keyringManager.unlock('wrong_password');
|
|
263
|
+
expect(wrongResult.canLogin).toBe(false);
|
|
264
|
+
|
|
265
|
+
// Correct password should work
|
|
266
|
+
const correctResult = await keyringManager.unlock(FAKE_PASSWORD);
|
|
267
|
+
expect(correctResult.canLogin).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should use salt for password hashing', async () => {
|
|
271
|
+
// Mock storage should contain vault-keys with salt
|
|
272
|
+
const mockStorage = (global as any).mockStorage || new Map();
|
|
273
|
+
|
|
274
|
+
// Set up EVM vault state
|
|
275
|
+
currentVaultState = createMockVaultState({
|
|
276
|
+
activeAccountId: 0,
|
|
277
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
278
|
+
networkType: INetworkType.Ethereum,
|
|
279
|
+
chainId: 1,
|
|
280
|
+
});
|
|
281
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
282
|
+
|
|
283
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
284
|
+
PEACE_SEED_PHRASE,
|
|
285
|
+
FAKE_PASSWORD,
|
|
286
|
+
mockVaultStateGetter
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// In real implementation, vault-keys should exist
|
|
290
|
+
// This verifies the mock is set up correctly
|
|
291
|
+
const vaultKeys = await mockStorage.get('vault-keys');
|
|
292
|
+
if (vaultKeys) {
|
|
293
|
+
expect(vaultKeys.salt).toBeDefined();
|
|
294
|
+
expect(vaultKeys.hash).toBeDefined();
|
|
295
|
+
expect(vaultKeys.salt.length).toBeGreaterThan(0);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('Import Security', () => {
|
|
301
|
+
beforeEach(async () => {
|
|
302
|
+
// Set up EVM vault state
|
|
303
|
+
currentVaultState = createMockVaultState({
|
|
304
|
+
activeAccountId: 0,
|
|
305
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
306
|
+
networkType: INetworkType.Ethereum,
|
|
307
|
+
chainId: 1,
|
|
308
|
+
});
|
|
309
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
310
|
+
|
|
311
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
312
|
+
PEACE_SEED_PHRASE,
|
|
313
|
+
FAKE_PASSWORD,
|
|
314
|
+
mockVaultStateGetter
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should validate imported keys before storing', async () => {
|
|
319
|
+
// Invalid keys should be rejected
|
|
320
|
+
const invalidKeys = [
|
|
321
|
+
'not_a_key',
|
|
322
|
+
'0x',
|
|
323
|
+
'0xZZZZ', // Invalid hex
|
|
324
|
+
'0x' + '0'.repeat(63), // Too short
|
|
325
|
+
'0x' + '0'.repeat(65), // Too long
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
for (const key of invalidKeys) {
|
|
329
|
+
await expect(keyringManager.importAccount(key)).rejects.toThrow();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should isolate imported keys from HD keys', async () => {
|
|
334
|
+
// Create a clean keyring with only HD accounts (no placeholder imported accounts)
|
|
335
|
+
const cleanVaultState = createMockVaultState({
|
|
336
|
+
activeAccountId: 0,
|
|
337
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
338
|
+
networkType: INetworkType.Ethereum,
|
|
339
|
+
chainId: 1,
|
|
340
|
+
});
|
|
341
|
+
// Clear imported accounts
|
|
342
|
+
cleanVaultState.accounts[KeyringAccountType.Imported] = {};
|
|
343
|
+
const cleanVaultStateGetter = jest.fn(() => cleanVaultState);
|
|
344
|
+
|
|
345
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
346
|
+
PEACE_SEED_PHRASE,
|
|
347
|
+
FAKE_PASSWORD,
|
|
348
|
+
cleanVaultStateGetter
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
// Check initial state - should have 1 HD account, 0 imported
|
|
352
|
+
let hdAccounts = cleanVaultState.accounts[KeyringAccountType.HDAccount];
|
|
353
|
+
let importedAccounts =
|
|
354
|
+
cleanVaultState.accounts[KeyringAccountType.Imported];
|
|
355
|
+
expect(Object.keys(hdAccounts).length).toBe(1);
|
|
356
|
+
expect(Object.keys(importedAccounts).length).toBe(0);
|
|
357
|
+
|
|
358
|
+
// Import a key
|
|
359
|
+
const importedKey =
|
|
360
|
+
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318';
|
|
361
|
+
const imported = await keyringManager.importAccount(importedKey);
|
|
362
|
+
|
|
363
|
+
// Update vault state to include the imported account
|
|
364
|
+
cleanVaultState.accounts[KeyringAccountType.Imported][imported.id] = {
|
|
365
|
+
id: imported.id,
|
|
366
|
+
label: 'Imported 1',
|
|
367
|
+
address: imported.address,
|
|
368
|
+
xpub: imported.xpub,
|
|
369
|
+
xprv: imported.xprv,
|
|
370
|
+
isImported: true,
|
|
371
|
+
isTrezorWallet: false,
|
|
372
|
+
isLedgerWallet: false,
|
|
373
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
374
|
+
assets: { syscoin: [], ethereum: [] },
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// HD and imported accounts should be in separate stores
|
|
378
|
+
hdAccounts = cleanVaultState.accounts[KeyringAccountType.HDAccount];
|
|
379
|
+
importedAccounts = cleanVaultState.accounts[KeyringAccountType.Imported];
|
|
380
|
+
|
|
381
|
+
// HD accounts should remain unchanged (still 1), imported should now have 1
|
|
382
|
+
expect(Object.keys(hdAccounts).length).toBe(1);
|
|
383
|
+
expect(Object.keys(importedAccounts).length).toBe(1);
|
|
384
|
+
|
|
385
|
+
// They should have separate ID sequences and be isolated
|
|
386
|
+
expect(hdAccounts[0]).toBeDefined(); // HD account 0 exists
|
|
387
|
+
expect(importedAccounts[0]).toBeDefined(); // Imported account 0 exists
|
|
388
|
+
expect(hdAccounts[0].isImported).toBe(false);
|
|
389
|
+
expect(importedAccounts[0].isImported).toBe(true);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe('Memory Security', () => {
|
|
394
|
+
it('should not leak sensitive data in error messages', async () => {
|
|
395
|
+
// Set up EVM vault state
|
|
396
|
+
currentVaultState = createMockVaultState({
|
|
397
|
+
activeAccountId: 0,
|
|
398
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
399
|
+
networkType: INetworkType.Ethereum,
|
|
400
|
+
chainId: 1,
|
|
401
|
+
});
|
|
402
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
403
|
+
|
|
404
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
405
|
+
PEACE_SEED_PHRASE,
|
|
406
|
+
FAKE_PASSWORD,
|
|
407
|
+
mockVaultStateGetter
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
await keyringManager.getSeed('wrong_password');
|
|
412
|
+
} catch (error) {
|
|
413
|
+
// Error message should not contain the actual password
|
|
414
|
+
expect(error.message).not.toContain(FAKE_PASSWORD);
|
|
415
|
+
expect(error.message).not.toContain(PEACE_SEED_PHRASE);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
await keyringManager.importAccount('invalid_key');
|
|
420
|
+
} catch (error) {
|
|
421
|
+
// Error should not leak the attempted key
|
|
422
|
+
expect(error.message).not.toContain('invalid_key');
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should clear sensitive data on errors', async () => {
|
|
427
|
+
// If initialization fails, no sensitive data should remain
|
|
428
|
+
keyringManager = new KeyringManager();
|
|
429
|
+
|
|
430
|
+
// Set up mock vault state getter for initializeWalletSecurely
|
|
431
|
+
currentVaultState = createMockVaultState({
|
|
432
|
+
activeAccountId: 0,
|
|
433
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
434
|
+
networkType: INetworkType.Ethereum,
|
|
435
|
+
chainId: 1,
|
|
436
|
+
});
|
|
437
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
438
|
+
keyringManager.setVaultStateGetter(mockVaultStateGetter);
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
await keyringManager.initializeWalletSecurely(
|
|
442
|
+
'invalid seed phrase',
|
|
443
|
+
FAKE_PASSWORD
|
|
444
|
+
);
|
|
445
|
+
} catch (error) {
|
|
446
|
+
// Wallet should not be unlocked after failed initialization
|
|
447
|
+
expect(keyringManager.isUnlocked()).toBe(false);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('Access Control', () => {
|
|
453
|
+
beforeEach(async () => {
|
|
454
|
+
// Use clean vault state without placeholder imported accounts
|
|
455
|
+
currentVaultState = createMockVaultState({
|
|
456
|
+
activeAccountId: 0,
|
|
457
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
458
|
+
networkType: INetworkType.Ethereum,
|
|
459
|
+
chainId: 1,
|
|
460
|
+
});
|
|
461
|
+
// Clear imported accounts
|
|
462
|
+
currentVaultState.accounts[KeyringAccountType.Imported] = {};
|
|
463
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
464
|
+
|
|
465
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
466
|
+
PEACE_SEED_PHRASE,
|
|
467
|
+
FAKE_PASSWORD,
|
|
468
|
+
mockVaultStateGetter
|
|
469
|
+
);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('should require unlock for sensitive operations', async () => {
|
|
473
|
+
keyringManager.lockWallet();
|
|
474
|
+
|
|
475
|
+
// These should fail when locked
|
|
476
|
+
await expect(keyringManager.addNewAccount()).rejects.toThrow();
|
|
477
|
+
await expect(
|
|
478
|
+
keyringManager.importAccount('0x' + '0'.repeat(64))
|
|
479
|
+
).rejects.toThrow();
|
|
480
|
+
|
|
481
|
+
// These should work when locked (public data)
|
|
482
|
+
expect(() => keyringManager.getNetwork()).not.toThrow();
|
|
483
|
+
expect(() => keyringManager.getActiveAccount()).not.toThrow();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('should validate account ownership', async () => {
|
|
487
|
+
// Can only get private keys for accounts that exist
|
|
488
|
+
await expect(
|
|
489
|
+
keyringManager.getPrivateKeyByAccountId(
|
|
490
|
+
999, // Non-existent account
|
|
491
|
+
KeyringAccountType.HDAccount,
|
|
492
|
+
FAKE_PASSWORD
|
|
493
|
+
)
|
|
494
|
+
).rejects.toThrow('Account not found');
|
|
495
|
+
|
|
496
|
+
// Can only access accounts of the correct type
|
|
497
|
+
expect(() =>
|
|
498
|
+
keyringManager.getAccountById(
|
|
499
|
+
0,
|
|
500
|
+
KeyringAccountType.Imported // Wrong type
|
|
501
|
+
)
|
|
502
|
+
).toThrow('Account not found');
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
});
|