@sidhujag/sysweb3-keyring 2.1.11 → 2.1.12
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/cjs/host-keyring.js +3 -1
- package/cjs/host-keyring.js.map +1 -1
- package/cjs/index.js +3 -1
- package/cjs/index.js.map +1 -1
- package/cjs/keyring-core.js +609 -36
- package/cjs/keyring-core.js.map +1 -1
- package/cjs/keyring-host.js +9 -2
- package/cjs/keyring-host.js.map +1 -1
- package/cjs/private-records.js +291 -9
- package/cjs/private-records.js.map +1 -1
- package/cjs/public-session.js +72 -0
- package/cjs/public-session.js.map +1 -0
- package/cjs/software.js +3 -1
- package/cjs/software.js.map +1 -1
- package/cjs/transactions/syscoin-core.js +159 -96
- package/cjs/transactions/syscoin-core.js.map +1 -1
- package/cjs/types.js.map +1 -1
- package/esm/keyring-core.js +611 -38
- package/esm/keyring-core.js.map +1 -1
- package/esm/keyring-host.js +9 -2
- package/esm/keyring-host.js.map +1 -1
- package/esm/private-records.js +284 -9
- package/esm/private-records.js.map +1 -1
- package/esm/public-session.js +65 -0
- package/esm/public-session.js.map +1 -0
- package/esm/transactions/syscoin-core.js +159 -96
- package/esm/transactions/syscoin-core.js.map +1 -1
- package/esm/types.js.map +1 -1
- package/package.json +1 -1
- package/types/host-keyring.d.ts +4 -0
- package/types/index.d.ts +3 -1
- package/types/keyring-core.d.ts +37 -5
- package/types/keyring-host.d.ts +4 -0
- package/types/private-records.d.ts +30 -0
- package/types/public-session.d.ts +19 -0
- package/types/software.d.ts +5 -0
- package/types/transactions/syscoin-core.d.ts +13 -1
- package/types/types.d.ts +9 -1
package/esm/keyring-core.js
CHANGED
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _KeyringCore_instances, _KeyringCore_deviceUnlockRetired, _KeyringCore_sessionOwnership, _KeyringCore_createDeviceUnlockControls, _KeyringCore_acceptSessionFrom, _KeyringCore_assertSessionOwner, _KeyringCore_captureSessionGuard, _KeyringCore_replaceSessionBuffers, _KeyringCore_finishUnlock, _KeyringCore_installVaultSession, _KeyringCore_recreateSessionFromVault;
|
|
12
|
+
var _KeyringCore_instances, _KeyringCore_recordAccess, _KeyringCore_recordAccessOwner, _KeyringCore_deviceUnlockRetired, _KeyringCore_sessionOwnership, _KeyringCore_createDeviceUnlockControls, _KeyringCore_capturePublicSessionGuard, _KeyringCore_acceptSessionFrom, _KeyringCore_assertSessionOwner, _KeyringCore_captureSessionGuard, _KeyringCore_replaceSessionBuffers, _KeyringCore_finishUnlock, _KeyringCore_installVaultSession, _KeyringCore_recreateSessionFromVault;
|
|
13
13
|
import ecc from '@bitcoinerlab/secp256k1';
|
|
14
14
|
import { INetworkType, getNetworkConfig, } from '@sidhujag/sysweb3-network';
|
|
15
15
|
import { BIP32Factory } from 'bip32';
|
|
@@ -31,10 +31,11 @@ const SLH_DSA_SETUP_SECRET_HEX_LENGTH = 64;
|
|
|
31
31
|
const SLH_DSA_PARAMETER_SET = 'SLH-DSA-SHA2-128-24';
|
|
32
32
|
import { currentVaultRefusals } from './current-vault-refusal';
|
|
33
33
|
import { isHexString } from './ethers-v6';
|
|
34
|
-
import { assertHardwareOperationCurrent } from './hardware-transaction-authority';
|
|
34
|
+
import { assertHardwareOperationCurrent, captureHardwareTransactionAuthority, } from './hardware-transaction-authority';
|
|
35
35
|
import { initialActiveImportedAccountState, initialActiveLedgerAccountState, initialActiveTrezorAccountState, } from './initial-state';
|
|
36
36
|
import { takeKeyringHostBinding } from './keyring-host';
|
|
37
|
-
import { decryptPrivateRecordBytes, encryptPrivateRecordBytes, privateRecordProfileId, } from './private-records';
|
|
37
|
+
import { decryptPrivateRecordBytes, sealPublicRecordCheckpoint, restorePublicRecordCheckpoint, encryptPrivateRecordBytes, privateRecordProfileId, createPrivateRecordAccess, activePrivateRecordDomainSet, privateRecordDomainSet, privateRecordAccessCanTransfer, transferPrivateRecordAccess, } from './private-records';
|
|
38
|
+
import { capturePublicSessionRequest, capturePublicSessionCheckpoint, capturePublicSessionKey, publicSessionStateDigest, } from './public-session';
|
|
38
39
|
import { getSyscoinSigners } from './signers';
|
|
39
40
|
import { snapshotEvmHdIndex } from './software-hd-account';
|
|
40
41
|
import { decryptCurrentVault, VaultAuthenticationError } from './storage-core';
|
|
@@ -160,9 +161,16 @@ export class KeyringCore {
|
|
|
160
161
|
this.sessionMnemonic = null; // can be a mnemonic or a zprv, can be changed to a zprv when using an imported wallet
|
|
161
162
|
this.privateRecordSessionRevision = 0;
|
|
162
163
|
this.sessionLifetime = new AbortController();
|
|
164
|
+
this.publicSession = false;
|
|
165
|
+
this.publicSessionRevision = 0;
|
|
166
|
+
_KeyringCore_recordAccess.set(this, new Set());
|
|
167
|
+
_KeyringCore_recordAccessOwner.set(this, Object.freeze({}));
|
|
163
168
|
_KeyringCore_deviceUnlockRetired.set(this, false);
|
|
164
169
|
_KeyringCore_sessionOwnership.set(this, void 0);
|
|
165
|
-
this.createSyscoinTransactions = () => new SyscoinTransactionsCore(this.getSigner, this.getReadOnlySigner, this.getAccountsState, this.syscoinHardwareSigners, this.
|
|
170
|
+
this.createSyscoinTransactions = () => new SyscoinTransactionsCore(this.getSigner, this.getReadOnlySigner, this.getAccountsState, this.syscoinHardwareSigners, this.capturePublicWalletContext, this.secretLifetime, () => {
|
|
171
|
+
if (this.secretLifetime === 'operation' && this.isUnlocked())
|
|
172
|
+
throw new Error('Prepare PSBT before the secret operation');
|
|
173
|
+
});
|
|
166
174
|
this.requireLedgerSigner = () => {
|
|
167
175
|
if (!this.ledgerSigner)
|
|
168
176
|
throw createHardwareSignerUnavailableError('Ledger');
|
|
@@ -179,23 +187,25 @@ export class KeyringCore {
|
|
|
179
187
|
? KeyringAccountType.Imported
|
|
180
188
|
: KeyringAccountType.HDAccount;
|
|
181
189
|
};
|
|
182
|
-
this.isUnlocked = () => !!this.sessionPassword &&
|
|
190
|
+
this.isUnlocked = () => !!this.sessionPassword &&
|
|
191
|
+
!this.sessionPassword.isCleared() &&
|
|
192
|
+
(this.secretLifetime === 'session' ||
|
|
193
|
+
(!!this.secretOperation?.active && this.secretOperation.working));
|
|
194
|
+
/** Public authenticated account context is independent of decrypted key availability. */
|
|
195
|
+
this.hasPublicSession = () => {
|
|
196
|
+
return this.secretLifetime === 'session'
|
|
197
|
+
? this.isUnlocked()
|
|
198
|
+
: this.publicSession &&
|
|
199
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current ===
|
|
200
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token;
|
|
201
|
+
};
|
|
183
202
|
this.lockWallet = async () => {
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.
|
|
189
|
-
|
|
190
|
-
if (this.sessionMnemonic) {
|
|
191
|
-
this.sessionMnemonic.clear();
|
|
192
|
-
this.sessionMnemonic = null;
|
|
193
|
-
}
|
|
194
|
-
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
195
|
-
if (ownership.tracker.current === ownership.token) {
|
|
196
|
-
ownership.tracker.current = undefined;
|
|
197
|
-
ownership.tracker.epoch++;
|
|
198
|
-
}
|
|
203
|
+
this.clearRecordAccess();
|
|
204
|
+
this.publicSession = false;
|
|
205
|
+
this.publicSessionRevision++;
|
|
206
|
+
if (this.secretOperation)
|
|
207
|
+
this.secretOperation.active = false;
|
|
208
|
+
this.clearSecretMaterial(false);
|
|
199
209
|
// Clear transaction handlers that may hold HD signers
|
|
200
210
|
if (this.syscoinTransaction) {
|
|
201
211
|
// Replace with empty object to clear references
|
|
@@ -223,26 +233,35 @@ export class KeyringCore {
|
|
|
223
233
|
};
|
|
224
234
|
/** Retire hardware before preparing a synchronous, one-use session move. */
|
|
225
235
|
this.prepareSessionTransfer = async (targetKeyring, assertCurrent) => {
|
|
226
|
-
|
|
236
|
+
const publicOnly = this.secretLifetime === 'operation';
|
|
237
|
+
if (publicOnly && this.secretOperation)
|
|
238
|
+
throw new Error('Secret operation is busy');
|
|
239
|
+
if (publicOnly ? !this.hasPublicSession() : !this.isUnlocked()) {
|
|
227
240
|
throw new Error('Source keyring must be unlocked to transfer session');
|
|
228
241
|
}
|
|
229
242
|
// Cross-wallet transfers must fail before either keyring's buffers change.
|
|
230
243
|
if (!(targetKeyring instanceof KeyringCore) ||
|
|
231
244
|
targetKeyring.authority !== this.authority ||
|
|
245
|
+
targetKeyring.secretLifetime !== this.secretLifetime ||
|
|
232
246
|
targetKeyring.storage !== this.storage ||
|
|
233
247
|
targetKeyring.vaultPersistence !== this.vaultPersistence ||
|
|
234
248
|
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").tracker !== __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker) {
|
|
235
249
|
throw new Error('Cannot transfer session between different wallet authorities');
|
|
236
250
|
}
|
|
237
|
-
const sourceGuard =
|
|
251
|
+
const sourceGuard = publicOnly
|
|
252
|
+
? __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)
|
|
253
|
+
: __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
238
254
|
const targetRevision = targetKeyring.privateRecordSessionRevision;
|
|
239
255
|
const ledger = this.ledgerSigner;
|
|
240
256
|
const trezor = this.trezorSigner;
|
|
241
257
|
const retirementChecks = [];
|
|
242
258
|
const checkOwnership = () => {
|
|
243
259
|
sourceGuard();
|
|
244
|
-
if (
|
|
245
|
-
|
|
260
|
+
if ((publicOnly &&
|
|
261
|
+
(this.secretOperation || targetKeyring.secretOperation)) ||
|
|
262
|
+
(publicOnly
|
|
263
|
+
? !this.hasPublicSession()
|
|
264
|
+
: !this.isUnlocked() || !this.sessionMnemonic) ||
|
|
246
265
|
this.ledgerSigner !== ledger ||
|
|
247
266
|
this.trezorSigner !== trezor ||
|
|
248
267
|
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").destroyed ||
|
|
@@ -294,12 +313,39 @@ export class KeyringCore {
|
|
|
294
313
|
check();
|
|
295
314
|
if (targetKeyring === this)
|
|
296
315
|
return;
|
|
297
|
-
|
|
298
|
-
|
|
316
|
+
if (publicOnly) {
|
|
317
|
+
const sourceRecords = [...__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")].filter((access) => privateRecordAccessCanTransfer(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")));
|
|
318
|
+
const retiringRecords = [...__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")].filter((access) => !sourceRecords.includes(access));
|
|
319
|
+
targetKeyring.clearRecordAccess();
|
|
320
|
+
// No facade is delivered until the synchronous authority move is complete.
|
|
321
|
+
const targetOwner = {};
|
|
322
|
+
const movedRecords = sourceRecords.map((access) => transferPrivateRecordAccess(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"), __classPrivateFieldGet(targetKeyring, _KeyringCore_recordAccessOwner, "f"), () => {
|
|
323
|
+
if (!targetOwner.guard)
|
|
324
|
+
throw new Error('Record access transfer is incomplete');
|
|
325
|
+
targetOwner.guard();
|
|
326
|
+
}));
|
|
327
|
+
for (const access of retiringRecords)
|
|
328
|
+
access.dispose();
|
|
329
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").clear();
|
|
330
|
+
this.publicSession = false;
|
|
331
|
+
this.publicSessionRevision++;
|
|
332
|
+
targetKeyring.publicSession = true;
|
|
333
|
+
targetKeyring.publicSessionRevision++;
|
|
334
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current =
|
|
335
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").token;
|
|
336
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.epoch++;
|
|
337
|
+
targetOwner.guard = __classPrivateFieldGet(targetKeyring, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(targetKeyring);
|
|
338
|
+
for (const access of movedRecords)
|
|
339
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_recordAccess, "f").add(access);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_instances, "m", _KeyringCore_acceptSessionFrom).call(targetKeyring, this);
|
|
343
|
+
this.retireSessionLifetime();
|
|
344
|
+
// Move the original SecureBuffers without copying their contents.
|
|
345
|
+
this.sessionPassword = null;
|
|
346
|
+
this.sessionMnemonic = null;
|
|
347
|
+
}
|
|
299
348
|
__classPrivateFieldSet(this, _KeyringCore_deviceUnlockRetired, true, "f");
|
|
300
|
-
// Move the original SecureBuffers without copying their contents.
|
|
301
|
-
this.sessionPassword = null;
|
|
302
|
-
this.sessionMnemonic = null;
|
|
303
349
|
},
|
|
304
350
|
});
|
|
305
351
|
};
|
|
@@ -350,7 +396,7 @@ export class KeyringCore {
|
|
|
350
396
|
};
|
|
351
397
|
};
|
|
352
398
|
this.getActiveUtxoAddressAccount = (id, ownedRead) => {
|
|
353
|
-
const captured = captureOwnedSyscoinRead(ownedRead, this.getAccountsState, this.
|
|
399
|
+
const captured = captureOwnedSyscoinRead(ownedRead, this.getAccountsState, this.capturePublicWalletContext);
|
|
354
400
|
if (id !== captured.accountId)
|
|
355
401
|
throw new Error('UTXO account is not active');
|
|
356
402
|
const account = this.getVault().accounts[captured.accountType]?.[id];
|
|
@@ -729,8 +775,6 @@ export class KeyringCore {
|
|
|
729
775
|
const isLedger = activeAccountType === KeyringAccountType.Ledger;
|
|
730
776
|
const isTrezor = activeAccountType === KeyringAccountType.Trezor;
|
|
731
777
|
const isHardwareWallet = isTrezor || isLedger;
|
|
732
|
-
if (!this.sessionPassword)
|
|
733
|
-
throw new Error('Wallet is locked cant proceed with transaction');
|
|
734
778
|
const activeAccountData = accounts[activeAccountType][activeAccountId];
|
|
735
779
|
if (!activeAccountData) {
|
|
736
780
|
throw new Error(`Active account (${activeAccountType}:${activeAccountId}) not found. Account switching may be in progress.`);
|
|
@@ -741,11 +785,14 @@ export class KeyringCore {
|
|
|
741
785
|
if (isTrezor || activeAccountData.isTrezorWallet)
|
|
742
786
|
this.requireTrezorSigner();
|
|
743
787
|
if (isHardwareWallet) {
|
|
788
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)();
|
|
744
789
|
return {
|
|
745
790
|
address,
|
|
746
791
|
decryptedPrivateKey: '',
|
|
747
792
|
};
|
|
748
793
|
}
|
|
794
|
+
if (!this.sessionPassword)
|
|
795
|
+
throw new Error('Wallet is locked cant proceed with transaction');
|
|
749
796
|
if (!xprv) {
|
|
750
797
|
throw new Error(`Private key not found for account ${activeAccountType}:${activeAccountId}. Account may not be fully initialized.`);
|
|
751
798
|
}
|
|
@@ -796,7 +843,40 @@ export class KeyringCore {
|
|
|
796
843
|
throw error;
|
|
797
844
|
}
|
|
798
845
|
};
|
|
846
|
+
this.captureEthereumSigningContext = () => {
|
|
847
|
+
const { activeAccount } = this.getVault();
|
|
848
|
+
if (activeAccount.type === KeyringAccountType.Ledger ||
|
|
849
|
+
activeAccount.type === KeyringAccountType.Trezor)
|
|
850
|
+
return this.capturePublicWalletContext();
|
|
851
|
+
return this.capturePsbtSigningContext();
|
|
852
|
+
};
|
|
853
|
+
this.capturePublicWalletContext = () => {
|
|
854
|
+
if (this.secretLifetime === 'session')
|
|
855
|
+
return this.capturePsbtSigningContext();
|
|
856
|
+
const publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
857
|
+
const vault = this.getVault();
|
|
858
|
+
const selected = { ...vault.activeAccount };
|
|
859
|
+
const account = vault.accounts[selected.type]?.[selected.id];
|
|
860
|
+
const identity = { address: account?.address, xpub: account?.xpub };
|
|
861
|
+
const network = { ...vault.activeNetwork };
|
|
862
|
+
const check = () => {
|
|
863
|
+
publicGuard();
|
|
864
|
+
const current = this.getVault(), currentAccount = current.accounts[selected.type]?.[selected.id];
|
|
865
|
+
if (current.activeAccount.id !== selected.id ||
|
|
866
|
+
current.activeAccount.type !== selected.type ||
|
|
867
|
+
currentAccount?.address !== identity.address ||
|
|
868
|
+
currentAccount?.xpub !== identity.xpub ||
|
|
869
|
+
current.activeNetwork.kind !== network.kind ||
|
|
870
|
+
current.activeNetwork.chainId !== network.chainId ||
|
|
871
|
+
current.activeNetwork.slip44 !== network.slip44 ||
|
|
872
|
+
current.activeNetwork.url !== network.url)
|
|
873
|
+
throw new Error('Wallet public context changed');
|
|
874
|
+
};
|
|
875
|
+
check();
|
|
876
|
+
return check;
|
|
877
|
+
};
|
|
799
878
|
this.capturePsbtSigningContext = (bindSessionRevision = true) => {
|
|
879
|
+
this.assertSecretAccess();
|
|
800
880
|
const session = this.sessionPassword;
|
|
801
881
|
const revision = this.privateRecordSessionRevision;
|
|
802
882
|
const vault = this.getVault();
|
|
@@ -1127,6 +1207,7 @@ export class KeyringCore {
|
|
|
1127
1207
|
};
|
|
1128
1208
|
// NEW: Separate session initialization from account creation
|
|
1129
1209
|
this.initializeSession = async (seedPhrase, password) => {
|
|
1210
|
+
this.assertSecretAccess();
|
|
1130
1211
|
// Validate inputs first
|
|
1131
1212
|
if (!BIP84.validateMnemonic(seedPhrase)) {
|
|
1132
1213
|
throw new Error('Invalid Seed');
|
|
@@ -1232,12 +1313,41 @@ export class KeyringCore {
|
|
|
1232
1313
|
}
|
|
1233
1314
|
};
|
|
1234
1315
|
this.initializeWalletSecurely = async (seedPhrase, password) => {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1316
|
+
if (this.secretLifetime === 'session') {
|
|
1317
|
+
await this.initializeSession(seedPhrase, password);
|
|
1318
|
+
return await this.createFirstAccount();
|
|
1319
|
+
}
|
|
1320
|
+
if (this.secretOperation)
|
|
1321
|
+
throw new Error('Secret operation is busy');
|
|
1322
|
+
const operation = {
|
|
1323
|
+
active: true,
|
|
1324
|
+
working: true,
|
|
1325
|
+
assertCurrent: () => {
|
|
1326
|
+
if (!operation.active || this.secretOperation !== operation)
|
|
1327
|
+
throw new Error('Secret operation cancelled');
|
|
1328
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this);
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
this.secretOperation = operation;
|
|
1332
|
+
try {
|
|
1333
|
+
await this.initializeSession(seedPhrase, password);
|
|
1334
|
+
operation.assertCurrent();
|
|
1335
|
+
const result = await this.createFirstAccount();
|
|
1336
|
+
operation.assertCurrent();
|
|
1337
|
+
this.publicSession = true;
|
|
1338
|
+
this.publicSessionRevision++;
|
|
1339
|
+
return result;
|
|
1340
|
+
}
|
|
1341
|
+
finally {
|
|
1342
|
+
operation.active = false;
|
|
1343
|
+
this.clearSecretMaterial(this.publicSession);
|
|
1344
|
+
if (this.secretOperation === operation)
|
|
1345
|
+
this.secretOperation = undefined;
|
|
1346
|
+
}
|
|
1238
1347
|
};
|
|
1239
1348
|
const dependencies = takeKeyringHostBinding(binding);
|
|
1240
1349
|
this.authority = dependencies.authority;
|
|
1350
|
+
this.secretLifetime = dependencies.secretLifetime;
|
|
1241
1351
|
__classPrivateFieldSet(this, _KeyringCore_sessionOwnership, {
|
|
1242
1352
|
tracker: dependencies.sessionOwner,
|
|
1243
1353
|
token: Symbol(),
|
|
@@ -1252,7 +1362,7 @@ export class KeyringCore {
|
|
|
1252
1362
|
this.initialTrezorAccountState = initialActiveTrezorAccountState;
|
|
1253
1363
|
this.initialLedgerAccountState = initialActiveLedgerAccountState;
|
|
1254
1364
|
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1255
|
-
this.ethereumTransaction = new EthereumTransactions(this.getNetwork, this.getDecryptedPrivateKey, this.getAccountsState, this.ledgerSigner, this.trezorSigner, this.
|
|
1365
|
+
this.ethereumTransaction = new EthereumTransactions(this.getNetwork, this.getDecryptedPrivateKey, this.getAccountsState, this.ledgerSigner, this.trezorSigner, this.captureEthereumSigningContext, dependencies.captureEvmSubmission);
|
|
1256
1366
|
if (dependencies.deviceUnlock) {
|
|
1257
1367
|
const { port, receive } = dependencies.deviceUnlock;
|
|
1258
1368
|
const readVaultPair = this.storage.readDeviceUnlockVaultPair;
|
|
@@ -1273,6 +1383,399 @@ export class KeyringCore {
|
|
|
1273
1383
|
// No need to explicitly set active account - it's managed externally
|
|
1274
1384
|
return account;
|
|
1275
1385
|
}
|
|
1386
|
+
capturePublicSessionGuard() {
|
|
1387
|
+
return __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
1388
|
+
}
|
|
1389
|
+
assertSecretAccess() {
|
|
1390
|
+
if (this.secretLifetime === 'session')
|
|
1391
|
+
return;
|
|
1392
|
+
const operation = this.secretOperation;
|
|
1393
|
+
if (!operation?.active || !operation.working)
|
|
1394
|
+
throw new Error('A secret operation is required');
|
|
1395
|
+
operation.assertCurrent();
|
|
1396
|
+
}
|
|
1397
|
+
clearSecretMaterial(keepPublicSession) {
|
|
1398
|
+
this.retireSessionLifetime();
|
|
1399
|
+
this.sessionPassword?.clear();
|
|
1400
|
+
this.sessionPassword = null;
|
|
1401
|
+
this.sessionMnemonic?.clear();
|
|
1402
|
+
this.sessionMnemonic = null;
|
|
1403
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
1404
|
+
if (!keepPublicSession && ownership.tracker.current === ownership.token) {
|
|
1405
|
+
ownership.tracker.current = undefined;
|
|
1406
|
+
ownership.tracker.epoch++;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
/** The SDK returns only the work result, never its credential or secret lease. */
|
|
1410
|
+
async withSecretOperation(credential, options, work) {
|
|
1411
|
+
if (this.secretLifetime !== 'operation')
|
|
1412
|
+
throw new Error('Operation secret policy is required');
|
|
1413
|
+
if (this.secretOperation)
|
|
1414
|
+
throw new Error('Secret operation is busy');
|
|
1415
|
+
if (!credential ||
|
|
1416
|
+
typeof credential.withSecret !== 'function' ||
|
|
1417
|
+
typeof work !== 'function' ||
|
|
1418
|
+
!options ||
|
|
1419
|
+
typeof options.assertCurrent !== 'function' ||
|
|
1420
|
+
!options.signal ||
|
|
1421
|
+
typeof options.signal.addEventListener !== 'function')
|
|
1422
|
+
throw new TypeError('Invalid secret operation');
|
|
1423
|
+
let publicGuard = this.hasPublicSession()
|
|
1424
|
+
? __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)
|
|
1425
|
+
: undefined;
|
|
1426
|
+
const initialGuard = publicGuard ? undefined : __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1427
|
+
const authority = captureHardwareTransactionAuthority(options);
|
|
1428
|
+
const withSecret = credential.withSecret.bind(credential);
|
|
1429
|
+
if (this.secretOperation)
|
|
1430
|
+
throw new Error('Secret operation is busy');
|
|
1431
|
+
const operation = {
|
|
1432
|
+
active: true,
|
|
1433
|
+
working: false,
|
|
1434
|
+
assertCurrent: () => {
|
|
1435
|
+
if (!operation.active ||
|
|
1436
|
+
this.secretOperation !== operation ||
|
|
1437
|
+
authority.signal.aborted)
|
|
1438
|
+
throw new Error('Secret operation cancelled');
|
|
1439
|
+
(publicGuard ?? initialGuard)();
|
|
1440
|
+
authority.assertCurrent();
|
|
1441
|
+
(publicGuard ?? initialGuard)();
|
|
1442
|
+
},
|
|
1443
|
+
};
|
|
1444
|
+
this.secretOperation = operation;
|
|
1445
|
+
let entered = false, completed = false, succeeded = false, createdPublicRevision, result;
|
|
1446
|
+
let pending;
|
|
1447
|
+
const retire = () => {
|
|
1448
|
+
operation.active = false;
|
|
1449
|
+
if (this.secretOperation === operation)
|
|
1450
|
+
this.clearSecretMaterial(this.hasPublicSession());
|
|
1451
|
+
};
|
|
1452
|
+
authority.signal.addEventListener('abort', retire, { once: true });
|
|
1453
|
+
try {
|
|
1454
|
+
operation.assertCurrent();
|
|
1455
|
+
await withSecret(operation.assertCurrent, (secret) => {
|
|
1456
|
+
if (entered) {
|
|
1457
|
+
const rejected = Promise.reject(new Error('Secret credential was already consumed'));
|
|
1458
|
+
void rejected.catch(() => undefined);
|
|
1459
|
+
return rejected;
|
|
1460
|
+
}
|
|
1461
|
+
entered = true;
|
|
1462
|
+
pending = (async () => {
|
|
1463
|
+
try {
|
|
1464
|
+
operation.assertCurrent();
|
|
1465
|
+
if (typeof secret !== 'string' || !secret)
|
|
1466
|
+
throw new TypeError('Invalid secret credential');
|
|
1467
|
+
const authenticated = await this.unlockSession(secret);
|
|
1468
|
+
if (!operation.active ||
|
|
1469
|
+
this.secretOperation !== operation ||
|
|
1470
|
+
authority.signal.aborted)
|
|
1471
|
+
throw new Error('Secret operation cancelled');
|
|
1472
|
+
authority.assertCurrent();
|
|
1473
|
+
if (!authenticated.canLogin)
|
|
1474
|
+
throw new Error('Invalid secret credential');
|
|
1475
|
+
if (!publicGuard) {
|
|
1476
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this);
|
|
1477
|
+
if (!this.sessionPassword || this.sessionPassword.isCleared())
|
|
1478
|
+
throw new Error('Secret operation cancelled');
|
|
1479
|
+
this.publicSession = true;
|
|
1480
|
+
createdPublicRevision = ++this.publicSessionRevision;
|
|
1481
|
+
publicGuard = this.capturePublicSessionGuard();
|
|
1482
|
+
}
|
|
1483
|
+
operation.assertCurrent();
|
|
1484
|
+
operation.working = true;
|
|
1485
|
+
result = await work(secret);
|
|
1486
|
+
operation.assertCurrent();
|
|
1487
|
+
completed = true;
|
|
1488
|
+
}
|
|
1489
|
+
finally {
|
|
1490
|
+
operation.working = false;
|
|
1491
|
+
if (this.secretOperation === operation)
|
|
1492
|
+
this.clearSecretMaterial(this.hasPublicSession());
|
|
1493
|
+
}
|
|
1494
|
+
})();
|
|
1495
|
+
void pending.catch(() => undefined);
|
|
1496
|
+
return pending;
|
|
1497
|
+
});
|
|
1498
|
+
if (pending)
|
|
1499
|
+
await pending;
|
|
1500
|
+
operation.assertCurrent();
|
|
1501
|
+
if (!entered || !completed)
|
|
1502
|
+
throw new Error('Secret credential was not consumed');
|
|
1503
|
+
succeeded = true;
|
|
1504
|
+
return result;
|
|
1505
|
+
}
|
|
1506
|
+
finally {
|
|
1507
|
+
if (!succeeded &&
|
|
1508
|
+
createdPublicRevision !== undefined &&
|
|
1509
|
+
this.publicSessionRevision === createdPublicRevision &&
|
|
1510
|
+
this.hasPublicSession()) {
|
|
1511
|
+
this.clearRecordAccess();
|
|
1512
|
+
this.publicSession = false;
|
|
1513
|
+
this.publicSessionRevision++;
|
|
1514
|
+
}
|
|
1515
|
+
retire();
|
|
1516
|
+
authority.signal.removeEventListener('abort', retire);
|
|
1517
|
+
if (this.secretOperation === operation)
|
|
1518
|
+
this.secretOperation = undefined;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
/** Seal a host-validated immutable public manifest from a currently authorized secret session. */
|
|
1522
|
+
async createPublicSessionCheckpoint(input, wrappingKey, assertCurrent) {
|
|
1523
|
+
this.assertSecretAccess();
|
|
1524
|
+
const request = capturePublicSessionRequest(input);
|
|
1525
|
+
const session = this.privateRecordSession();
|
|
1526
|
+
const password = this.sessionPassword;
|
|
1527
|
+
const readPair = this.storage.readDeviceUnlockVaultPair;
|
|
1528
|
+
if (!password || typeof readPair !== 'function') {
|
|
1529
|
+
session.secret.fill(0);
|
|
1530
|
+
throw new Error('Public session requires atomic wallet storage');
|
|
1531
|
+
}
|
|
1532
|
+
const check = () => {
|
|
1533
|
+
this.assertSecretAccess();
|
|
1534
|
+
session.assertCurrent();
|
|
1535
|
+
assertHardwareOperationCurrent(assertCurrent);
|
|
1536
|
+
this.assertSecretAccess();
|
|
1537
|
+
session.assertCurrent();
|
|
1538
|
+
};
|
|
1539
|
+
let wrappingSecret, vaultKey, passwordKey;
|
|
1540
|
+
try {
|
|
1541
|
+
wrappingSecret = capturePublicSessionKey(wrappingKey);
|
|
1542
|
+
check();
|
|
1543
|
+
const pair = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1544
|
+
check();
|
|
1545
|
+
if (!pair)
|
|
1546
|
+
throw new Error('Public session wallet is unavailable');
|
|
1547
|
+
vaultKey = this.hexToBytes(password.toString());
|
|
1548
|
+
let difference = vaultKey.length ^ wrappingSecret.length;
|
|
1549
|
+
for (let index = 0; index < wrappingSecret.length; index++)
|
|
1550
|
+
difference |= wrappingSecret[index] ^ (vaultKey[index] ?? 0);
|
|
1551
|
+
if (difference === 0)
|
|
1552
|
+
throw new Error('Public session key must be independent of the vault key');
|
|
1553
|
+
// Catch direct reuse of a 32-byte UTF-8 wallet password as well. Random
|
|
1554
|
+
// wrapping bytes normally fail strict decoding and need no extra KDF.
|
|
1555
|
+
// This detects known credential reuse, not arbitrary key provenance.
|
|
1556
|
+
let candidatePassword;
|
|
1557
|
+
try {
|
|
1558
|
+
candidatePassword = new TextDecoder('utf-8', {
|
|
1559
|
+
fatal: true,
|
|
1560
|
+
ignoreBOM: true,
|
|
1561
|
+
}).decode(wrappingSecret);
|
|
1562
|
+
}
|
|
1563
|
+
catch {
|
|
1564
|
+
// A binary public-view key is expected and does not need decoding.
|
|
1565
|
+
}
|
|
1566
|
+
if (candidatePassword !== undefined) {
|
|
1567
|
+
passwordKey = this.hexToBytes(await this.deriveStoredSessionKey(candidatePassword, {
|
|
1568
|
+
salt: pair.salt,
|
|
1569
|
+
}));
|
|
1570
|
+
check();
|
|
1571
|
+
let passwordDifference = vaultKey.length ^ passwordKey.length;
|
|
1572
|
+
for (let index = 0; index < passwordKey.length; index++)
|
|
1573
|
+
passwordDifference |= passwordKey[index] ^ (vaultKey[index] ?? 0);
|
|
1574
|
+
passwordKey.fill(0);
|
|
1575
|
+
passwordKey = undefined;
|
|
1576
|
+
if (passwordDifference === 0)
|
|
1577
|
+
throw new Error('Public session key must be independent of the vault password');
|
|
1578
|
+
}
|
|
1579
|
+
vaultKey.fill(0);
|
|
1580
|
+
vaultKey = undefined;
|
|
1581
|
+
// A checkpoint must never attest a different pair using an old live root.
|
|
1582
|
+
const payload = await decryptCurrentVault(pair.vault, password.toString());
|
|
1583
|
+
check();
|
|
1584
|
+
if (!payload ||
|
|
1585
|
+
typeof payload !== 'object' ||
|
|
1586
|
+
Object.getOwnPropertyDescriptor(payload, 'mnemonic')?.value?.normalize('NFKD') !== new TextDecoder().decode(session.secret))
|
|
1587
|
+
throw new Error('Public session wallet changed');
|
|
1588
|
+
const digest = await publicSessionStateDigest(request.publicState, check);
|
|
1589
|
+
const checkpoint = await sealPublicRecordCheckpoint(session.secret, wrappingSecret, {
|
|
1590
|
+
walletBindingId: request.walletBindingId,
|
|
1591
|
+
pairFingerprint: pair.pairFingerprint,
|
|
1592
|
+
stateDigest: digest,
|
|
1593
|
+
domains: request.recordDomains,
|
|
1594
|
+
}, check);
|
|
1595
|
+
const current = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1596
|
+
check();
|
|
1597
|
+
if (!current ||
|
|
1598
|
+
current.vault !== pair.vault ||
|
|
1599
|
+
current.salt !== pair.salt ||
|
|
1600
|
+
current.pairFingerprint !== pair.pairFingerprint)
|
|
1601
|
+
throw new Error('Public session wallet changed');
|
|
1602
|
+
return checkpoint;
|
|
1603
|
+
}
|
|
1604
|
+
finally {
|
|
1605
|
+
session.secret.fill(0);
|
|
1606
|
+
wrappingSecret?.fill(0);
|
|
1607
|
+
vaultKey?.fill(0);
|
|
1608
|
+
passwordKey?.fill(0);
|
|
1609
|
+
request.publicState.fill(0);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
/** Restore only authenticated public state and record keys. Never decrypts the vault. */
|
|
1613
|
+
async restorePublicSession(credential, input, options, work) {
|
|
1614
|
+
if (this.secretLifetime !== 'operation')
|
|
1615
|
+
throw new Error('Operation secret policy is required');
|
|
1616
|
+
// Capture cancellation before any caller-controlled property or guard runs.
|
|
1617
|
+
const initialGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1618
|
+
initialGuard();
|
|
1619
|
+
if (this.secretOperation || this.hasPublicSession() || this.isUnlocked())
|
|
1620
|
+
throw new Error('Public session restore requires a locked wallet');
|
|
1621
|
+
if (!credential ||
|
|
1622
|
+
typeof credential.withKey !== 'function' ||
|
|
1623
|
+
typeof work !== 'function')
|
|
1624
|
+
throw new TypeError('Invalid public session restore');
|
|
1625
|
+
const readPair = this.storage.readDeviceUnlockVaultPair;
|
|
1626
|
+
if (typeof readPair !== 'function')
|
|
1627
|
+
throw new Error('Public session requires atomic wallet storage');
|
|
1628
|
+
const withKey = credential.withKey.bind(credential);
|
|
1629
|
+
const request = capturePublicSessionRequest(input);
|
|
1630
|
+
const checkpoint = capturePublicSessionCheckpoint(input);
|
|
1631
|
+
const authority = captureHardwareTransactionAuthority(options);
|
|
1632
|
+
initialGuard();
|
|
1633
|
+
// A reentrant host guard may have admitted a different operation.
|
|
1634
|
+
if (this.secretOperation || this.hasPublicSession() || this.isUnlocked())
|
|
1635
|
+
throw new Error('Public session restore requires a locked wallet');
|
|
1636
|
+
let publicGuard;
|
|
1637
|
+
const operation = {
|
|
1638
|
+
active: true,
|
|
1639
|
+
working: false,
|
|
1640
|
+
assertCurrent: () => {
|
|
1641
|
+
if (!operation.active ||
|
|
1642
|
+
this.secretOperation !== operation ||
|
|
1643
|
+
authority.signal.aborted)
|
|
1644
|
+
throw new Error('Public session restore cancelled');
|
|
1645
|
+
(publicGuard ?? initialGuard)();
|
|
1646
|
+
authority.assertCurrent();
|
|
1647
|
+
if (!operation.active ||
|
|
1648
|
+
this.secretOperation !== operation ||
|
|
1649
|
+
authority.signal.aborted)
|
|
1650
|
+
throw new Error('Public session restore cancelled');
|
|
1651
|
+
(publicGuard ?? initialGuard)();
|
|
1652
|
+
},
|
|
1653
|
+
};
|
|
1654
|
+
const check = operation.assertCurrent;
|
|
1655
|
+
let restored;
|
|
1656
|
+
let succeeded = false, entered = false, completed = false;
|
|
1657
|
+
let pending, result;
|
|
1658
|
+
const retire = () => {
|
|
1659
|
+
operation.active = false;
|
|
1660
|
+
if (this.secretOperation !== operation)
|
|
1661
|
+
return;
|
|
1662
|
+
this.clearRecordAccess();
|
|
1663
|
+
this.publicSession = false;
|
|
1664
|
+
this.publicSessionRevision++;
|
|
1665
|
+
this.clearSecretMaterial(false);
|
|
1666
|
+
};
|
|
1667
|
+
this.secretOperation = operation;
|
|
1668
|
+
authority.signal.addEventListener('abort', retire, { once: true });
|
|
1669
|
+
try {
|
|
1670
|
+
check();
|
|
1671
|
+
const pair = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1672
|
+
check();
|
|
1673
|
+
if (!pair)
|
|
1674
|
+
throw new Error('Public session wallet is unavailable');
|
|
1675
|
+
const checkPair = async () => {
|
|
1676
|
+
const current = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1677
|
+
check();
|
|
1678
|
+
if (!current ||
|
|
1679
|
+
current.vault !== pair.vault ||
|
|
1680
|
+
current.salt !== pair.salt ||
|
|
1681
|
+
current.pairFingerprint !== pair.pairFingerprint)
|
|
1682
|
+
throw new Error('Public session wallet changed');
|
|
1683
|
+
};
|
|
1684
|
+
const digest = await publicSessionStateDigest(request.publicState, check);
|
|
1685
|
+
await withKey(check, (key) => {
|
|
1686
|
+
if (entered) {
|
|
1687
|
+
const rejected = Promise.reject(new Error('Public session credential was already consumed'));
|
|
1688
|
+
void rejected.catch(() => undefined);
|
|
1689
|
+
return rejected;
|
|
1690
|
+
}
|
|
1691
|
+
entered = true;
|
|
1692
|
+
pending = (async () => {
|
|
1693
|
+
let wrappingSecret;
|
|
1694
|
+
try {
|
|
1695
|
+
wrappingSecret = capturePublicSessionKey(key);
|
|
1696
|
+
check();
|
|
1697
|
+
restored = await restorePublicRecordCheckpoint(wrappingSecret, checkpoint, {
|
|
1698
|
+
walletBindingId: request.walletBindingId,
|
|
1699
|
+
pairFingerprint: pair.pairFingerprint,
|
|
1700
|
+
stateDigest: digest,
|
|
1701
|
+
domains: request.recordDomains,
|
|
1702
|
+
}, () => (publicGuard ? publicGuard() : check()), __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"));
|
|
1703
|
+
wrappingSecret.fill(0);
|
|
1704
|
+
wrappingSecret = undefined;
|
|
1705
|
+
await checkPair();
|
|
1706
|
+
this.clearRecordAccess();
|
|
1707
|
+
this.publicSession = true;
|
|
1708
|
+
this.publicSessionRevision++;
|
|
1709
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current =
|
|
1710
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token;
|
|
1711
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.epoch++;
|
|
1712
|
+
publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
1713
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").add(restored);
|
|
1714
|
+
check();
|
|
1715
|
+
// Recreate only the public transaction wrapper retired by lockWallet.
|
|
1716
|
+
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1717
|
+
result = await work(request.publicState);
|
|
1718
|
+
check();
|
|
1719
|
+
await checkPair();
|
|
1720
|
+
completed = true;
|
|
1721
|
+
}
|
|
1722
|
+
finally {
|
|
1723
|
+
wrappingSecret?.fill(0);
|
|
1724
|
+
}
|
|
1725
|
+
})();
|
|
1726
|
+
void pending.catch(() => undefined);
|
|
1727
|
+
return pending;
|
|
1728
|
+
});
|
|
1729
|
+
if (pending)
|
|
1730
|
+
await pending;
|
|
1731
|
+
check();
|
|
1732
|
+
if (!entered || !completed)
|
|
1733
|
+
throw new Error('Public session credential was not consumed');
|
|
1734
|
+
succeeded = true;
|
|
1735
|
+
return result;
|
|
1736
|
+
}
|
|
1737
|
+
finally {
|
|
1738
|
+
authority.signal.removeEventListener('abort', retire);
|
|
1739
|
+
request.publicState.fill(0);
|
|
1740
|
+
if (!succeeded) {
|
|
1741
|
+
restored?.dispose();
|
|
1742
|
+
retire();
|
|
1743
|
+
}
|
|
1744
|
+
else {
|
|
1745
|
+
operation.active = false;
|
|
1746
|
+
this.clearSecretMaterial(true);
|
|
1747
|
+
}
|
|
1748
|
+
if (this.secretOperation === operation)
|
|
1749
|
+
this.secretOperation = undefined;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
async unlock(password) {
|
|
1753
|
+
if (this.secretLifetime === 'session')
|
|
1754
|
+
return this.unlockSession(password);
|
|
1755
|
+
if (this.secretOperation)
|
|
1756
|
+
throw new Error('Secret operation is busy');
|
|
1757
|
+
const operation = {
|
|
1758
|
+
active: true,
|
|
1759
|
+
working: false,
|
|
1760
|
+
assertCurrent: __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this),
|
|
1761
|
+
};
|
|
1762
|
+
this.secretOperation = operation;
|
|
1763
|
+
try {
|
|
1764
|
+
const result = await this.unlockSession(password);
|
|
1765
|
+
if (result.canLogin) {
|
|
1766
|
+
this.clearRecordAccess();
|
|
1767
|
+
this.publicSession = true;
|
|
1768
|
+
this.publicSessionRevision++;
|
|
1769
|
+
}
|
|
1770
|
+
return result;
|
|
1771
|
+
}
|
|
1772
|
+
finally {
|
|
1773
|
+
operation.active = false;
|
|
1774
|
+
this.clearSecretMaterial(this.publicSession);
|
|
1775
|
+
if (this.secretOperation === operation)
|
|
1776
|
+
this.secretOperation = undefined;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1276
1779
|
/** Capture wallet-session authority without binding an account or network. */
|
|
1277
1780
|
captureUnlockedSessionGuard() {
|
|
1278
1781
|
const assertOwner = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
@@ -1294,7 +1797,7 @@ export class KeyringCore {
|
|
|
1294
1797
|
* Storage, platform and session restoration failures reject so callers do
|
|
1295
1798
|
* not charge a failed password attempt for an operational error.
|
|
1296
1799
|
*/
|
|
1297
|
-
async
|
|
1800
|
+
async unlockSession(password) {
|
|
1298
1801
|
let assertCurrent = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1299
1802
|
let authenticated = false;
|
|
1300
1803
|
try {
|
|
@@ -1513,6 +2016,51 @@ export class KeyringCore {
|
|
|
1513
2016
|
throw new Error('Invalid private record root');
|
|
1514
2017
|
return { secret: new TextEncoder().encode(root), assertCurrent };
|
|
1515
2018
|
}
|
|
2019
|
+
clearRecordAccess() {
|
|
2020
|
+
for (const access of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f"))
|
|
2021
|
+
access.dispose();
|
|
2022
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").clear();
|
|
2023
|
+
}
|
|
2024
|
+
/** Public transaction records may outlive a signer without retaining the spending root. */
|
|
2025
|
+
async createPrivateRecordAccess(domains, options) {
|
|
2026
|
+
this.assertSecretAccess();
|
|
2027
|
+
const session = this.privateRecordSession();
|
|
2028
|
+
const publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
2029
|
+
for (const previous of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")) {
|
|
2030
|
+
if (activePrivateRecordDomainSet(previous, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) === null)
|
|
2031
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").delete(previous);
|
|
2032
|
+
}
|
|
2033
|
+
let access;
|
|
2034
|
+
try {
|
|
2035
|
+
access = await createPrivateRecordAccess(session.secret, domains, options, publicGuard, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"));
|
|
2036
|
+
session.assertCurrent();
|
|
2037
|
+
publicGuard();
|
|
2038
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").add(access);
|
|
2039
|
+
return access;
|
|
2040
|
+
}
|
|
2041
|
+
catch (error) {
|
|
2042
|
+
access?.dispose();
|
|
2043
|
+
throw error;
|
|
2044
|
+
}
|
|
2045
|
+
finally {
|
|
2046
|
+
session.secret.fill(0);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
/** Retrieve only an exact domain set already derived under a secret operation. Never unlocks or derives. */
|
|
2050
|
+
getPrivateRecordAccess(domains) {
|
|
2051
|
+
const guard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
2052
|
+
const domainSet = privateRecordDomainSet(domains);
|
|
2053
|
+
guard();
|
|
2054
|
+
for (const access of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")) {
|
|
2055
|
+
if (privateRecordAccessCanTransfer(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) &&
|
|
2056
|
+
activePrivateRecordDomainSet(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) ===
|
|
2057
|
+
domainSet) {
|
|
2058
|
+
guard();
|
|
2059
|
+
return access;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
throw new Error('Record access is not initialized for these domains');
|
|
2063
|
+
}
|
|
1516
2064
|
/** Stable local profile identifier; never an account address or a key. */
|
|
1517
2065
|
async getPrivateRecordProfileId() {
|
|
1518
2066
|
const session = this.privateRecordSession();
|
|
@@ -2475,6 +3023,7 @@ export class KeyringCore {
|
|
|
2475
3023
|
}
|
|
2476
3024
|
// Helper methods for secure buffer operations
|
|
2477
3025
|
getSessionPasswordString() {
|
|
3026
|
+
this.assertSecretAccess();
|
|
2478
3027
|
if (!this.sessionPassword || this.sessionPassword.isCleared()) {
|
|
2479
3028
|
throw new Error('Session password not available');
|
|
2480
3029
|
}
|
|
@@ -2483,6 +3032,7 @@ export class KeyringCore {
|
|
|
2483
3032
|
return this.sessionPassword.toString();
|
|
2484
3033
|
}
|
|
2485
3034
|
getSessionMnemonicString() {
|
|
3035
|
+
this.assertSecretAccess();
|
|
2486
3036
|
if (!this.sessionMnemonic || this.sessionMnemonic.isCleared()) {
|
|
2487
3037
|
throw new Error('Session mnemonic not available');
|
|
2488
3038
|
}
|
|
@@ -2595,7 +3145,7 @@ export class KeyringCore {
|
|
|
2595
3145
|
this.syscoinTransaction = {};
|
|
2596
3146
|
}
|
|
2597
3147
|
}
|
|
2598
|
-
_KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership = new WeakMap(), _KeyringCore_instances = new WeakSet(), _KeyringCore_createDeviceUnlockControls = function _KeyringCore_createDeviceUnlockControls(port, readVaultPair) {
|
|
3148
|
+
_KeyringCore_recordAccess = new WeakMap(), _KeyringCore_recordAccessOwner = new WeakMap(), _KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership = new WeakMap(), _KeyringCore_instances = new WeakSet(), _KeyringCore_createDeviceUnlockControls = function _KeyringCore_createDeviceUnlockControls(port, readVaultPair) {
|
|
2599
3149
|
const readPair = () => readCurrentVaultPair(readVaultPair);
|
|
2600
3150
|
let pending = false;
|
|
2601
3151
|
const run = async (request, assertCurrent) => {
|
|
@@ -2686,7 +3236,13 @@ _KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership
|
|
|
2686
3236
|
else {
|
|
2687
3237
|
if (__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_finishUnlock).call(this).needsAccountCreation)
|
|
2688
3238
|
return false;
|
|
3239
|
+
this.clearRecordAccess();
|
|
2689
3240
|
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_installVaultSession).call(this, mnemonic, keyHex, check);
|
|
3241
|
+
if (this.secretLifetime === 'operation') {
|
|
3242
|
+
this.publicSession = true;
|
|
3243
|
+
this.publicSessionRevision++;
|
|
3244
|
+
this.clearSecretMaterial(true);
|
|
3245
|
+
}
|
|
2690
3246
|
}
|
|
2691
3247
|
return true;
|
|
2692
3248
|
}
|
|
@@ -2704,6 +3260,23 @@ _KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership
|
|
|
2704
3260
|
},
|
|
2705
3261
|
resume: (assertCurrent) => run(null, assertCurrent),
|
|
2706
3262
|
});
|
|
3263
|
+
}, _KeyringCore_capturePublicSessionGuard = function _KeyringCore_capturePublicSessionGuard() {
|
|
3264
|
+
const assertOwner = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
3265
|
+
const revision = this.publicSessionRevision;
|
|
3266
|
+
const check = () => {
|
|
3267
|
+
if (this.secretLifetime === 'session') {
|
|
3268
|
+
assertOwner();
|
|
3269
|
+
if (!this.sessionPassword || this.sessionPassword.isCleared())
|
|
3270
|
+
throw new Error('Wallet unlock session changed');
|
|
3271
|
+
}
|
|
3272
|
+
else if (!this.publicSession ||
|
|
3273
|
+
this.publicSessionRevision !== revision ||
|
|
3274
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current !== __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token) {
|
|
3275
|
+
throw new Error('Wallet public session changed');
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
check();
|
|
3279
|
+
return check;
|
|
2707
3280
|
}, _KeyringCore_acceptSessionFrom = function _KeyringCore_acceptSessionFrom(source) {
|
|
2708
3281
|
if (source.authority !== this.authority ||
|
|
2709
3282
|
source.storage !== this.storage ||
|