@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/cjs/keyring-core.js
CHANGED
|
@@ -46,7 +46,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
46
46
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
47
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
48
|
};
|
|
49
|
-
var _KeyringCore_instances, _KeyringCore_deviceUnlockRetired, _KeyringCore_sessionOwnership, _KeyringCore_createDeviceUnlockControls, _KeyringCore_acceptSessionFrom, _KeyringCore_assertSessionOwner, _KeyringCore_captureSessionGuard, _KeyringCore_replaceSessionBuffers, _KeyringCore_finishUnlock, _KeyringCore_installVaultSession, _KeyringCore_recreateSessionFromVault;
|
|
49
|
+
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;
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
51
|
exports.KeyringCore = void 0;
|
|
52
52
|
const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1"));
|
|
@@ -74,6 +74,7 @@ const hardware_transaction_authority_1 = require("./hardware-transaction-authori
|
|
|
74
74
|
const initial_state_1 = require("./initial-state");
|
|
75
75
|
const keyring_host_1 = require("./keyring-host");
|
|
76
76
|
const private_records_1 = require("./private-records");
|
|
77
|
+
const public_session_1 = require("./public-session");
|
|
77
78
|
const signers_1 = require("./signers");
|
|
78
79
|
const software_hd_account_1 = require("./software-hd-account");
|
|
79
80
|
const storage_core_1 = require("./storage-core");
|
|
@@ -199,9 +200,16 @@ class KeyringCore {
|
|
|
199
200
|
this.sessionMnemonic = null; // can be a mnemonic or a zprv, can be changed to a zprv when using an imported wallet
|
|
200
201
|
this.privateRecordSessionRevision = 0;
|
|
201
202
|
this.sessionLifetime = new AbortController();
|
|
203
|
+
this.publicSession = false;
|
|
204
|
+
this.publicSessionRevision = 0;
|
|
205
|
+
_KeyringCore_recordAccess.set(this, new Set());
|
|
206
|
+
_KeyringCore_recordAccessOwner.set(this, Object.freeze({}));
|
|
202
207
|
_KeyringCore_deviceUnlockRetired.set(this, false);
|
|
203
208
|
_KeyringCore_sessionOwnership.set(this, void 0);
|
|
204
|
-
this.createSyscoinTransactions = () => new syscoin_core_1.SyscoinTransactionsCore(this.getSigner, this.getReadOnlySigner, this.getAccountsState, this.syscoinHardwareSigners, this.
|
|
209
|
+
this.createSyscoinTransactions = () => new syscoin_core_1.SyscoinTransactionsCore(this.getSigner, this.getReadOnlySigner, this.getAccountsState, this.syscoinHardwareSigners, this.capturePublicWalletContext, this.secretLifetime, () => {
|
|
210
|
+
if (this.secretLifetime === 'operation' && this.isUnlocked())
|
|
211
|
+
throw new Error('Prepare PSBT before the secret operation');
|
|
212
|
+
});
|
|
205
213
|
this.requireLedgerSigner = () => {
|
|
206
214
|
if (!this.ledgerSigner)
|
|
207
215
|
throw (0, syscoin_core_1.createHardwareSignerUnavailableError)('Ledger');
|
|
@@ -218,23 +226,25 @@ class KeyringCore {
|
|
|
218
226
|
? types_1.KeyringAccountType.Imported
|
|
219
227
|
: types_1.KeyringAccountType.HDAccount;
|
|
220
228
|
};
|
|
221
|
-
this.isUnlocked = () => !!this.sessionPassword &&
|
|
229
|
+
this.isUnlocked = () => !!this.sessionPassword &&
|
|
230
|
+
!this.sessionPassword.isCleared() &&
|
|
231
|
+
(this.secretLifetime === 'session' ||
|
|
232
|
+
(!!this.secretOperation?.active && this.secretOperation.working));
|
|
233
|
+
/** Public authenticated account context is independent of decrypted key availability. */
|
|
234
|
+
this.hasPublicSession = () => {
|
|
235
|
+
return this.secretLifetime === 'session'
|
|
236
|
+
? this.isUnlocked()
|
|
237
|
+
: this.publicSession &&
|
|
238
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current ===
|
|
239
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token;
|
|
240
|
+
};
|
|
222
241
|
this.lockWallet = async () => {
|
|
223
|
-
this.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
|
|
229
|
-
if (this.sessionMnemonic) {
|
|
230
|
-
this.sessionMnemonic.clear();
|
|
231
|
-
this.sessionMnemonic = null;
|
|
232
|
-
}
|
|
233
|
-
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
234
|
-
if (ownership.tracker.current === ownership.token) {
|
|
235
|
-
ownership.tracker.current = undefined;
|
|
236
|
-
ownership.tracker.epoch++;
|
|
237
|
-
}
|
|
242
|
+
this.clearRecordAccess();
|
|
243
|
+
this.publicSession = false;
|
|
244
|
+
this.publicSessionRevision++;
|
|
245
|
+
if (this.secretOperation)
|
|
246
|
+
this.secretOperation.active = false;
|
|
247
|
+
this.clearSecretMaterial(false);
|
|
238
248
|
// Clear transaction handlers that may hold HD signers
|
|
239
249
|
if (this.syscoinTransaction) {
|
|
240
250
|
// Replace with empty object to clear references
|
|
@@ -262,26 +272,35 @@ class KeyringCore {
|
|
|
262
272
|
};
|
|
263
273
|
/** Retire hardware before preparing a synchronous, one-use session move. */
|
|
264
274
|
this.prepareSessionTransfer = async (targetKeyring, assertCurrent) => {
|
|
265
|
-
|
|
275
|
+
const publicOnly = this.secretLifetime === 'operation';
|
|
276
|
+
if (publicOnly && this.secretOperation)
|
|
277
|
+
throw new Error('Secret operation is busy');
|
|
278
|
+
if (publicOnly ? !this.hasPublicSession() : !this.isUnlocked()) {
|
|
266
279
|
throw new Error('Source keyring must be unlocked to transfer session');
|
|
267
280
|
}
|
|
268
281
|
// Cross-wallet transfers must fail before either keyring's buffers change.
|
|
269
282
|
if (!(targetKeyring instanceof KeyringCore) ||
|
|
270
283
|
targetKeyring.authority !== this.authority ||
|
|
284
|
+
targetKeyring.secretLifetime !== this.secretLifetime ||
|
|
271
285
|
targetKeyring.storage !== this.storage ||
|
|
272
286
|
targetKeyring.vaultPersistence !== this.vaultPersistence ||
|
|
273
287
|
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").tracker !== __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker) {
|
|
274
288
|
throw new Error('Cannot transfer session between different wallet authorities');
|
|
275
289
|
}
|
|
276
|
-
const sourceGuard =
|
|
290
|
+
const sourceGuard = publicOnly
|
|
291
|
+
? __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)
|
|
292
|
+
: __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
277
293
|
const targetRevision = targetKeyring.privateRecordSessionRevision;
|
|
278
294
|
const ledger = this.ledgerSigner;
|
|
279
295
|
const trezor = this.trezorSigner;
|
|
280
296
|
const retirementChecks = [];
|
|
281
297
|
const checkOwnership = () => {
|
|
282
298
|
sourceGuard();
|
|
283
|
-
if (
|
|
284
|
-
|
|
299
|
+
if ((publicOnly &&
|
|
300
|
+
(this.secretOperation || targetKeyring.secretOperation)) ||
|
|
301
|
+
(publicOnly
|
|
302
|
+
? !this.hasPublicSession()
|
|
303
|
+
: !this.isUnlocked() || !this.sessionMnemonic) ||
|
|
285
304
|
this.ledgerSigner !== ledger ||
|
|
286
305
|
this.trezorSigner !== trezor ||
|
|
287
306
|
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").destroyed ||
|
|
@@ -333,12 +352,39 @@ class KeyringCore {
|
|
|
333
352
|
check();
|
|
334
353
|
if (targetKeyring === this)
|
|
335
354
|
return;
|
|
336
|
-
|
|
337
|
-
|
|
355
|
+
if (publicOnly) {
|
|
356
|
+
const sourceRecords = [...__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")].filter((access) => (0, private_records_1.privateRecordAccessCanTransfer)(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")));
|
|
357
|
+
const retiringRecords = [...__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")].filter((access) => !sourceRecords.includes(access));
|
|
358
|
+
targetKeyring.clearRecordAccess();
|
|
359
|
+
// No facade is delivered until the synchronous authority move is complete.
|
|
360
|
+
const targetOwner = {};
|
|
361
|
+
const movedRecords = sourceRecords.map((access) => (0, private_records_1.transferPrivateRecordAccess)(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"), __classPrivateFieldGet(targetKeyring, _KeyringCore_recordAccessOwner, "f"), () => {
|
|
362
|
+
if (!targetOwner.guard)
|
|
363
|
+
throw new Error('Record access transfer is incomplete');
|
|
364
|
+
targetOwner.guard();
|
|
365
|
+
}));
|
|
366
|
+
for (const access of retiringRecords)
|
|
367
|
+
access.dispose();
|
|
368
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").clear();
|
|
369
|
+
this.publicSession = false;
|
|
370
|
+
this.publicSessionRevision++;
|
|
371
|
+
targetKeyring.publicSession = true;
|
|
372
|
+
targetKeyring.publicSessionRevision++;
|
|
373
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current =
|
|
374
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_sessionOwnership, "f").token;
|
|
375
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.epoch++;
|
|
376
|
+
targetOwner.guard = __classPrivateFieldGet(targetKeyring, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(targetKeyring);
|
|
377
|
+
for (const access of movedRecords)
|
|
378
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_recordAccess, "f").add(access);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_instances, "m", _KeyringCore_acceptSessionFrom).call(targetKeyring, this);
|
|
382
|
+
this.retireSessionLifetime();
|
|
383
|
+
// Move the original SecureBuffers without copying their contents.
|
|
384
|
+
this.sessionPassword = null;
|
|
385
|
+
this.sessionMnemonic = null;
|
|
386
|
+
}
|
|
338
387
|
__classPrivateFieldSet(this, _KeyringCore_deviceUnlockRetired, true, "f");
|
|
339
|
-
// Move the original SecureBuffers without copying their contents.
|
|
340
|
-
this.sessionPassword = null;
|
|
341
|
-
this.sessionMnemonic = null;
|
|
342
388
|
},
|
|
343
389
|
});
|
|
344
390
|
};
|
|
@@ -389,7 +435,7 @@ class KeyringCore {
|
|
|
389
435
|
};
|
|
390
436
|
};
|
|
391
437
|
this.getActiveUtxoAddressAccount = (id, ownedRead) => {
|
|
392
|
-
const captured = (0, syscoin_core_1.captureOwnedSyscoinRead)(ownedRead, this.getAccountsState, this.
|
|
438
|
+
const captured = (0, syscoin_core_1.captureOwnedSyscoinRead)(ownedRead, this.getAccountsState, this.capturePublicWalletContext);
|
|
393
439
|
if (id !== captured.accountId)
|
|
394
440
|
throw new Error('UTXO account is not active');
|
|
395
441
|
const account = this.getVault().accounts[captured.accountType]?.[id];
|
|
@@ -768,8 +814,6 @@ class KeyringCore {
|
|
|
768
814
|
const isLedger = activeAccountType === types_1.KeyringAccountType.Ledger;
|
|
769
815
|
const isTrezor = activeAccountType === types_1.KeyringAccountType.Trezor;
|
|
770
816
|
const isHardwareWallet = isTrezor || isLedger;
|
|
771
|
-
if (!this.sessionPassword)
|
|
772
|
-
throw new Error('Wallet is locked cant proceed with transaction');
|
|
773
817
|
const activeAccountData = accounts[activeAccountType][activeAccountId];
|
|
774
818
|
if (!activeAccountData) {
|
|
775
819
|
throw new Error(`Active account (${activeAccountType}:${activeAccountId}) not found. Account switching may be in progress.`);
|
|
@@ -780,11 +824,14 @@ class KeyringCore {
|
|
|
780
824
|
if (isTrezor || activeAccountData.isTrezorWallet)
|
|
781
825
|
this.requireTrezorSigner();
|
|
782
826
|
if (isHardwareWallet) {
|
|
827
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)();
|
|
783
828
|
return {
|
|
784
829
|
address,
|
|
785
830
|
decryptedPrivateKey: '',
|
|
786
831
|
};
|
|
787
832
|
}
|
|
833
|
+
if (!this.sessionPassword)
|
|
834
|
+
throw new Error('Wallet is locked cant proceed with transaction');
|
|
788
835
|
if (!xprv) {
|
|
789
836
|
throw new Error(`Private key not found for account ${activeAccountType}:${activeAccountId}. Account may not be fully initialized.`);
|
|
790
837
|
}
|
|
@@ -835,7 +882,40 @@ class KeyringCore {
|
|
|
835
882
|
throw error;
|
|
836
883
|
}
|
|
837
884
|
};
|
|
885
|
+
this.captureEthereumSigningContext = () => {
|
|
886
|
+
const { activeAccount } = this.getVault();
|
|
887
|
+
if (activeAccount.type === types_1.KeyringAccountType.Ledger ||
|
|
888
|
+
activeAccount.type === types_1.KeyringAccountType.Trezor)
|
|
889
|
+
return this.capturePublicWalletContext();
|
|
890
|
+
return this.capturePsbtSigningContext();
|
|
891
|
+
};
|
|
892
|
+
this.capturePublicWalletContext = () => {
|
|
893
|
+
if (this.secretLifetime === 'session')
|
|
894
|
+
return this.capturePsbtSigningContext();
|
|
895
|
+
const publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
896
|
+
const vault = this.getVault();
|
|
897
|
+
const selected = { ...vault.activeAccount };
|
|
898
|
+
const account = vault.accounts[selected.type]?.[selected.id];
|
|
899
|
+
const identity = { address: account?.address, xpub: account?.xpub };
|
|
900
|
+
const network = { ...vault.activeNetwork };
|
|
901
|
+
const check = () => {
|
|
902
|
+
publicGuard();
|
|
903
|
+
const current = this.getVault(), currentAccount = current.accounts[selected.type]?.[selected.id];
|
|
904
|
+
if (current.activeAccount.id !== selected.id ||
|
|
905
|
+
current.activeAccount.type !== selected.type ||
|
|
906
|
+
currentAccount?.address !== identity.address ||
|
|
907
|
+
currentAccount?.xpub !== identity.xpub ||
|
|
908
|
+
current.activeNetwork.kind !== network.kind ||
|
|
909
|
+
current.activeNetwork.chainId !== network.chainId ||
|
|
910
|
+
current.activeNetwork.slip44 !== network.slip44 ||
|
|
911
|
+
current.activeNetwork.url !== network.url)
|
|
912
|
+
throw new Error('Wallet public context changed');
|
|
913
|
+
};
|
|
914
|
+
check();
|
|
915
|
+
return check;
|
|
916
|
+
};
|
|
838
917
|
this.capturePsbtSigningContext = (bindSessionRevision = true) => {
|
|
918
|
+
this.assertSecretAccess();
|
|
839
919
|
const session = this.sessionPassword;
|
|
840
920
|
const revision = this.privateRecordSessionRevision;
|
|
841
921
|
const vault = this.getVault();
|
|
@@ -1166,6 +1246,7 @@ class KeyringCore {
|
|
|
1166
1246
|
};
|
|
1167
1247
|
// NEW: Separate session initialization from account creation
|
|
1168
1248
|
this.initializeSession = async (seedPhrase, password) => {
|
|
1249
|
+
this.assertSecretAccess();
|
|
1169
1250
|
// Validate inputs first
|
|
1170
1251
|
if (!BIP84.validateMnemonic(seedPhrase)) {
|
|
1171
1252
|
throw new Error('Invalid Seed');
|
|
@@ -1271,12 +1352,41 @@ class KeyringCore {
|
|
|
1271
1352
|
}
|
|
1272
1353
|
};
|
|
1273
1354
|
this.initializeWalletSecurely = async (seedPhrase, password) => {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1355
|
+
if (this.secretLifetime === 'session') {
|
|
1356
|
+
await this.initializeSession(seedPhrase, password);
|
|
1357
|
+
return await this.createFirstAccount();
|
|
1358
|
+
}
|
|
1359
|
+
if (this.secretOperation)
|
|
1360
|
+
throw new Error('Secret operation is busy');
|
|
1361
|
+
const operation = {
|
|
1362
|
+
active: true,
|
|
1363
|
+
working: true,
|
|
1364
|
+
assertCurrent: () => {
|
|
1365
|
+
if (!operation.active || this.secretOperation !== operation)
|
|
1366
|
+
throw new Error('Secret operation cancelled');
|
|
1367
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this);
|
|
1368
|
+
},
|
|
1369
|
+
};
|
|
1370
|
+
this.secretOperation = operation;
|
|
1371
|
+
try {
|
|
1372
|
+
await this.initializeSession(seedPhrase, password);
|
|
1373
|
+
operation.assertCurrent();
|
|
1374
|
+
const result = await this.createFirstAccount();
|
|
1375
|
+
operation.assertCurrent();
|
|
1376
|
+
this.publicSession = true;
|
|
1377
|
+
this.publicSessionRevision++;
|
|
1378
|
+
return result;
|
|
1379
|
+
}
|
|
1380
|
+
finally {
|
|
1381
|
+
operation.active = false;
|
|
1382
|
+
this.clearSecretMaterial(this.publicSession);
|
|
1383
|
+
if (this.secretOperation === operation)
|
|
1384
|
+
this.secretOperation = undefined;
|
|
1385
|
+
}
|
|
1277
1386
|
};
|
|
1278
1387
|
const dependencies = (0, keyring_host_1.takeKeyringHostBinding)(binding);
|
|
1279
1388
|
this.authority = dependencies.authority;
|
|
1389
|
+
this.secretLifetime = dependencies.secretLifetime;
|
|
1280
1390
|
__classPrivateFieldSet(this, _KeyringCore_sessionOwnership, {
|
|
1281
1391
|
tracker: dependencies.sessionOwner,
|
|
1282
1392
|
token: Symbol(),
|
|
@@ -1291,7 +1401,7 @@ class KeyringCore {
|
|
|
1291
1401
|
this.initialTrezorAccountState = initial_state_1.initialActiveTrezorAccountState;
|
|
1292
1402
|
this.initialLedgerAccountState = initial_state_1.initialActiveLedgerAccountState;
|
|
1293
1403
|
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1294
|
-
this.ethereumTransaction = new ethereum_1.EthereumTransactions(this.getNetwork, this.getDecryptedPrivateKey, this.getAccountsState, this.ledgerSigner, this.trezorSigner, this.
|
|
1404
|
+
this.ethereumTransaction = new ethereum_1.EthereumTransactions(this.getNetwork, this.getDecryptedPrivateKey, this.getAccountsState, this.ledgerSigner, this.trezorSigner, this.captureEthereumSigningContext, dependencies.captureEvmSubmission);
|
|
1295
1405
|
if (dependencies.deviceUnlock) {
|
|
1296
1406
|
const { port, receive } = dependencies.deviceUnlock;
|
|
1297
1407
|
const readVaultPair = this.storage.readDeviceUnlockVaultPair;
|
|
@@ -1312,6 +1422,399 @@ class KeyringCore {
|
|
|
1312
1422
|
// No need to explicitly set active account - it's managed externally
|
|
1313
1423
|
return account;
|
|
1314
1424
|
}
|
|
1425
|
+
capturePublicSessionGuard() {
|
|
1426
|
+
return __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
1427
|
+
}
|
|
1428
|
+
assertSecretAccess() {
|
|
1429
|
+
if (this.secretLifetime === 'session')
|
|
1430
|
+
return;
|
|
1431
|
+
const operation = this.secretOperation;
|
|
1432
|
+
if (!operation?.active || !operation.working)
|
|
1433
|
+
throw new Error('A secret operation is required');
|
|
1434
|
+
operation.assertCurrent();
|
|
1435
|
+
}
|
|
1436
|
+
clearSecretMaterial(keepPublicSession) {
|
|
1437
|
+
this.retireSessionLifetime();
|
|
1438
|
+
this.sessionPassword?.clear();
|
|
1439
|
+
this.sessionPassword = null;
|
|
1440
|
+
this.sessionMnemonic?.clear();
|
|
1441
|
+
this.sessionMnemonic = null;
|
|
1442
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
1443
|
+
if (!keepPublicSession && ownership.tracker.current === ownership.token) {
|
|
1444
|
+
ownership.tracker.current = undefined;
|
|
1445
|
+
ownership.tracker.epoch++;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
/** The SDK returns only the work result, never its credential or secret lease. */
|
|
1449
|
+
async withSecretOperation(credential, options, work) {
|
|
1450
|
+
if (this.secretLifetime !== 'operation')
|
|
1451
|
+
throw new Error('Operation secret policy is required');
|
|
1452
|
+
if (this.secretOperation)
|
|
1453
|
+
throw new Error('Secret operation is busy');
|
|
1454
|
+
if (!credential ||
|
|
1455
|
+
typeof credential.withSecret !== 'function' ||
|
|
1456
|
+
typeof work !== 'function' ||
|
|
1457
|
+
!options ||
|
|
1458
|
+
typeof options.assertCurrent !== 'function' ||
|
|
1459
|
+
!options.signal ||
|
|
1460
|
+
typeof options.signal.addEventListener !== 'function')
|
|
1461
|
+
throw new TypeError('Invalid secret operation');
|
|
1462
|
+
let publicGuard = this.hasPublicSession()
|
|
1463
|
+
? __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this)
|
|
1464
|
+
: undefined;
|
|
1465
|
+
const initialGuard = publicGuard ? undefined : __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1466
|
+
const authority = (0, hardware_transaction_authority_1.captureHardwareTransactionAuthority)(options);
|
|
1467
|
+
const withSecret = credential.withSecret.bind(credential);
|
|
1468
|
+
if (this.secretOperation)
|
|
1469
|
+
throw new Error('Secret operation is busy');
|
|
1470
|
+
const operation = {
|
|
1471
|
+
active: true,
|
|
1472
|
+
working: false,
|
|
1473
|
+
assertCurrent: () => {
|
|
1474
|
+
if (!operation.active ||
|
|
1475
|
+
this.secretOperation !== operation ||
|
|
1476
|
+
authority.signal.aborted)
|
|
1477
|
+
throw new Error('Secret operation cancelled');
|
|
1478
|
+
(publicGuard ?? initialGuard)();
|
|
1479
|
+
authority.assertCurrent();
|
|
1480
|
+
(publicGuard ?? initialGuard)();
|
|
1481
|
+
},
|
|
1482
|
+
};
|
|
1483
|
+
this.secretOperation = operation;
|
|
1484
|
+
let entered = false, completed = false, succeeded = false, createdPublicRevision, result;
|
|
1485
|
+
let pending;
|
|
1486
|
+
const retire = () => {
|
|
1487
|
+
operation.active = false;
|
|
1488
|
+
if (this.secretOperation === operation)
|
|
1489
|
+
this.clearSecretMaterial(this.hasPublicSession());
|
|
1490
|
+
};
|
|
1491
|
+
authority.signal.addEventListener('abort', retire, { once: true });
|
|
1492
|
+
try {
|
|
1493
|
+
operation.assertCurrent();
|
|
1494
|
+
await withSecret(operation.assertCurrent, (secret) => {
|
|
1495
|
+
if (entered) {
|
|
1496
|
+
const rejected = Promise.reject(new Error('Secret credential was already consumed'));
|
|
1497
|
+
void rejected.catch(() => undefined);
|
|
1498
|
+
return rejected;
|
|
1499
|
+
}
|
|
1500
|
+
entered = true;
|
|
1501
|
+
pending = (async () => {
|
|
1502
|
+
try {
|
|
1503
|
+
operation.assertCurrent();
|
|
1504
|
+
if (typeof secret !== 'string' || !secret)
|
|
1505
|
+
throw new TypeError('Invalid secret credential');
|
|
1506
|
+
const authenticated = await this.unlockSession(secret);
|
|
1507
|
+
if (!operation.active ||
|
|
1508
|
+
this.secretOperation !== operation ||
|
|
1509
|
+
authority.signal.aborted)
|
|
1510
|
+
throw new Error('Secret operation cancelled');
|
|
1511
|
+
authority.assertCurrent();
|
|
1512
|
+
if (!authenticated.canLogin)
|
|
1513
|
+
throw new Error('Invalid secret credential');
|
|
1514
|
+
if (!publicGuard) {
|
|
1515
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this);
|
|
1516
|
+
if (!this.sessionPassword || this.sessionPassword.isCleared())
|
|
1517
|
+
throw new Error('Secret operation cancelled');
|
|
1518
|
+
this.publicSession = true;
|
|
1519
|
+
createdPublicRevision = ++this.publicSessionRevision;
|
|
1520
|
+
publicGuard = this.capturePublicSessionGuard();
|
|
1521
|
+
}
|
|
1522
|
+
operation.assertCurrent();
|
|
1523
|
+
operation.working = true;
|
|
1524
|
+
result = await work(secret);
|
|
1525
|
+
operation.assertCurrent();
|
|
1526
|
+
completed = true;
|
|
1527
|
+
}
|
|
1528
|
+
finally {
|
|
1529
|
+
operation.working = false;
|
|
1530
|
+
if (this.secretOperation === operation)
|
|
1531
|
+
this.clearSecretMaterial(this.hasPublicSession());
|
|
1532
|
+
}
|
|
1533
|
+
})();
|
|
1534
|
+
void pending.catch(() => undefined);
|
|
1535
|
+
return pending;
|
|
1536
|
+
});
|
|
1537
|
+
if (pending)
|
|
1538
|
+
await pending;
|
|
1539
|
+
operation.assertCurrent();
|
|
1540
|
+
if (!entered || !completed)
|
|
1541
|
+
throw new Error('Secret credential was not consumed');
|
|
1542
|
+
succeeded = true;
|
|
1543
|
+
return result;
|
|
1544
|
+
}
|
|
1545
|
+
finally {
|
|
1546
|
+
if (!succeeded &&
|
|
1547
|
+
createdPublicRevision !== undefined &&
|
|
1548
|
+
this.publicSessionRevision === createdPublicRevision &&
|
|
1549
|
+
this.hasPublicSession()) {
|
|
1550
|
+
this.clearRecordAccess();
|
|
1551
|
+
this.publicSession = false;
|
|
1552
|
+
this.publicSessionRevision++;
|
|
1553
|
+
}
|
|
1554
|
+
retire();
|
|
1555
|
+
authority.signal.removeEventListener('abort', retire);
|
|
1556
|
+
if (this.secretOperation === operation)
|
|
1557
|
+
this.secretOperation = undefined;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
/** Seal a host-validated immutable public manifest from a currently authorized secret session. */
|
|
1561
|
+
async createPublicSessionCheckpoint(input, wrappingKey, assertCurrent) {
|
|
1562
|
+
this.assertSecretAccess();
|
|
1563
|
+
const request = (0, public_session_1.capturePublicSessionRequest)(input);
|
|
1564
|
+
const session = this.privateRecordSession();
|
|
1565
|
+
const password = this.sessionPassword;
|
|
1566
|
+
const readPair = this.storage.readDeviceUnlockVaultPair;
|
|
1567
|
+
if (!password || typeof readPair !== 'function') {
|
|
1568
|
+
session.secret.fill(0);
|
|
1569
|
+
throw new Error('Public session requires atomic wallet storage');
|
|
1570
|
+
}
|
|
1571
|
+
const check = () => {
|
|
1572
|
+
this.assertSecretAccess();
|
|
1573
|
+
session.assertCurrent();
|
|
1574
|
+
(0, hardware_transaction_authority_1.assertHardwareOperationCurrent)(assertCurrent);
|
|
1575
|
+
this.assertSecretAccess();
|
|
1576
|
+
session.assertCurrent();
|
|
1577
|
+
};
|
|
1578
|
+
let wrappingSecret, vaultKey, passwordKey;
|
|
1579
|
+
try {
|
|
1580
|
+
wrappingSecret = (0, public_session_1.capturePublicSessionKey)(wrappingKey);
|
|
1581
|
+
check();
|
|
1582
|
+
const pair = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1583
|
+
check();
|
|
1584
|
+
if (!pair)
|
|
1585
|
+
throw new Error('Public session wallet is unavailable');
|
|
1586
|
+
vaultKey = this.hexToBytes(password.toString());
|
|
1587
|
+
let difference = vaultKey.length ^ wrappingSecret.length;
|
|
1588
|
+
for (let index = 0; index < wrappingSecret.length; index++)
|
|
1589
|
+
difference |= wrappingSecret[index] ^ (vaultKey[index] ?? 0);
|
|
1590
|
+
if (difference === 0)
|
|
1591
|
+
throw new Error('Public session key must be independent of the vault key');
|
|
1592
|
+
// Catch direct reuse of a 32-byte UTF-8 wallet password as well. Random
|
|
1593
|
+
// wrapping bytes normally fail strict decoding and need no extra KDF.
|
|
1594
|
+
// This detects known credential reuse, not arbitrary key provenance.
|
|
1595
|
+
let candidatePassword;
|
|
1596
|
+
try {
|
|
1597
|
+
candidatePassword = new TextDecoder('utf-8', {
|
|
1598
|
+
fatal: true,
|
|
1599
|
+
ignoreBOM: true,
|
|
1600
|
+
}).decode(wrappingSecret);
|
|
1601
|
+
}
|
|
1602
|
+
catch {
|
|
1603
|
+
// A binary public-view key is expected and does not need decoding.
|
|
1604
|
+
}
|
|
1605
|
+
if (candidatePassword !== undefined) {
|
|
1606
|
+
passwordKey = this.hexToBytes(await this.deriveStoredSessionKey(candidatePassword, {
|
|
1607
|
+
salt: pair.salt,
|
|
1608
|
+
}));
|
|
1609
|
+
check();
|
|
1610
|
+
let passwordDifference = vaultKey.length ^ passwordKey.length;
|
|
1611
|
+
for (let index = 0; index < passwordKey.length; index++)
|
|
1612
|
+
passwordDifference |= passwordKey[index] ^ (vaultKey[index] ?? 0);
|
|
1613
|
+
passwordKey.fill(0);
|
|
1614
|
+
passwordKey = undefined;
|
|
1615
|
+
if (passwordDifference === 0)
|
|
1616
|
+
throw new Error('Public session key must be independent of the vault password');
|
|
1617
|
+
}
|
|
1618
|
+
vaultKey.fill(0);
|
|
1619
|
+
vaultKey = undefined;
|
|
1620
|
+
// A checkpoint must never attest a different pair using an old live root.
|
|
1621
|
+
const payload = await (0, storage_core_1.decryptCurrentVault)(pair.vault, password.toString());
|
|
1622
|
+
check();
|
|
1623
|
+
if (!payload ||
|
|
1624
|
+
typeof payload !== 'object' ||
|
|
1625
|
+
Object.getOwnPropertyDescriptor(payload, 'mnemonic')?.value?.normalize('NFKD') !== new TextDecoder().decode(session.secret))
|
|
1626
|
+
throw new Error('Public session wallet changed');
|
|
1627
|
+
const digest = await (0, public_session_1.publicSessionStateDigest)(request.publicState, check);
|
|
1628
|
+
const checkpoint = await (0, private_records_1.sealPublicRecordCheckpoint)(session.secret, wrappingSecret, {
|
|
1629
|
+
walletBindingId: request.walletBindingId,
|
|
1630
|
+
pairFingerprint: pair.pairFingerprint,
|
|
1631
|
+
stateDigest: digest,
|
|
1632
|
+
domains: request.recordDomains,
|
|
1633
|
+
}, check);
|
|
1634
|
+
const current = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1635
|
+
check();
|
|
1636
|
+
if (!current ||
|
|
1637
|
+
current.vault !== pair.vault ||
|
|
1638
|
+
current.salt !== pair.salt ||
|
|
1639
|
+
current.pairFingerprint !== pair.pairFingerprint)
|
|
1640
|
+
throw new Error('Public session wallet changed');
|
|
1641
|
+
return checkpoint;
|
|
1642
|
+
}
|
|
1643
|
+
finally {
|
|
1644
|
+
session.secret.fill(0);
|
|
1645
|
+
wrappingSecret?.fill(0);
|
|
1646
|
+
vaultKey?.fill(0);
|
|
1647
|
+
passwordKey?.fill(0);
|
|
1648
|
+
request.publicState.fill(0);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
/** Restore only authenticated public state and record keys. Never decrypts the vault. */
|
|
1652
|
+
async restorePublicSession(credential, input, options, work) {
|
|
1653
|
+
if (this.secretLifetime !== 'operation')
|
|
1654
|
+
throw new Error('Operation secret policy is required');
|
|
1655
|
+
// Capture cancellation before any caller-controlled property or guard runs.
|
|
1656
|
+
const initialGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1657
|
+
initialGuard();
|
|
1658
|
+
if (this.secretOperation || this.hasPublicSession() || this.isUnlocked())
|
|
1659
|
+
throw new Error('Public session restore requires a locked wallet');
|
|
1660
|
+
if (!credential ||
|
|
1661
|
+
typeof credential.withKey !== 'function' ||
|
|
1662
|
+
typeof work !== 'function')
|
|
1663
|
+
throw new TypeError('Invalid public session restore');
|
|
1664
|
+
const readPair = this.storage.readDeviceUnlockVaultPair;
|
|
1665
|
+
if (typeof readPair !== 'function')
|
|
1666
|
+
throw new Error('Public session requires atomic wallet storage');
|
|
1667
|
+
const withKey = credential.withKey.bind(credential);
|
|
1668
|
+
const request = (0, public_session_1.capturePublicSessionRequest)(input);
|
|
1669
|
+
const checkpoint = (0, public_session_1.capturePublicSessionCheckpoint)(input);
|
|
1670
|
+
const authority = (0, hardware_transaction_authority_1.captureHardwareTransactionAuthority)(options);
|
|
1671
|
+
initialGuard();
|
|
1672
|
+
// A reentrant host guard may have admitted a different operation.
|
|
1673
|
+
if (this.secretOperation || this.hasPublicSession() || this.isUnlocked())
|
|
1674
|
+
throw new Error('Public session restore requires a locked wallet');
|
|
1675
|
+
let publicGuard;
|
|
1676
|
+
const operation = {
|
|
1677
|
+
active: true,
|
|
1678
|
+
working: false,
|
|
1679
|
+
assertCurrent: () => {
|
|
1680
|
+
if (!operation.active ||
|
|
1681
|
+
this.secretOperation !== operation ||
|
|
1682
|
+
authority.signal.aborted)
|
|
1683
|
+
throw new Error('Public session restore cancelled');
|
|
1684
|
+
(publicGuard ?? initialGuard)();
|
|
1685
|
+
authority.assertCurrent();
|
|
1686
|
+
if (!operation.active ||
|
|
1687
|
+
this.secretOperation !== operation ||
|
|
1688
|
+
authority.signal.aborted)
|
|
1689
|
+
throw new Error('Public session restore cancelled');
|
|
1690
|
+
(publicGuard ?? initialGuard)();
|
|
1691
|
+
},
|
|
1692
|
+
};
|
|
1693
|
+
const check = operation.assertCurrent;
|
|
1694
|
+
let restored;
|
|
1695
|
+
let succeeded = false, entered = false, completed = false;
|
|
1696
|
+
let pending, result;
|
|
1697
|
+
const retire = () => {
|
|
1698
|
+
operation.active = false;
|
|
1699
|
+
if (this.secretOperation !== operation)
|
|
1700
|
+
return;
|
|
1701
|
+
this.clearRecordAccess();
|
|
1702
|
+
this.publicSession = false;
|
|
1703
|
+
this.publicSessionRevision++;
|
|
1704
|
+
this.clearSecretMaterial(false);
|
|
1705
|
+
};
|
|
1706
|
+
this.secretOperation = operation;
|
|
1707
|
+
authority.signal.addEventListener('abort', retire, { once: true });
|
|
1708
|
+
try {
|
|
1709
|
+
check();
|
|
1710
|
+
const pair = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1711
|
+
check();
|
|
1712
|
+
if (!pair)
|
|
1713
|
+
throw new Error('Public session wallet is unavailable');
|
|
1714
|
+
const checkPair = async () => {
|
|
1715
|
+
const current = await readCurrentVaultPair(readPair.bind(this.storage));
|
|
1716
|
+
check();
|
|
1717
|
+
if (!current ||
|
|
1718
|
+
current.vault !== pair.vault ||
|
|
1719
|
+
current.salt !== pair.salt ||
|
|
1720
|
+
current.pairFingerprint !== pair.pairFingerprint)
|
|
1721
|
+
throw new Error('Public session wallet changed');
|
|
1722
|
+
};
|
|
1723
|
+
const digest = await (0, public_session_1.publicSessionStateDigest)(request.publicState, check);
|
|
1724
|
+
await withKey(check, (key) => {
|
|
1725
|
+
if (entered) {
|
|
1726
|
+
const rejected = Promise.reject(new Error('Public session credential was already consumed'));
|
|
1727
|
+
void rejected.catch(() => undefined);
|
|
1728
|
+
return rejected;
|
|
1729
|
+
}
|
|
1730
|
+
entered = true;
|
|
1731
|
+
pending = (async () => {
|
|
1732
|
+
let wrappingSecret;
|
|
1733
|
+
try {
|
|
1734
|
+
wrappingSecret = (0, public_session_1.capturePublicSessionKey)(key);
|
|
1735
|
+
check();
|
|
1736
|
+
restored = await (0, private_records_1.restorePublicRecordCheckpoint)(wrappingSecret, checkpoint, {
|
|
1737
|
+
walletBindingId: request.walletBindingId,
|
|
1738
|
+
pairFingerprint: pair.pairFingerprint,
|
|
1739
|
+
stateDigest: digest,
|
|
1740
|
+
domains: request.recordDomains,
|
|
1741
|
+
}, () => (publicGuard ? publicGuard() : check()), __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"));
|
|
1742
|
+
wrappingSecret.fill(0);
|
|
1743
|
+
wrappingSecret = undefined;
|
|
1744
|
+
await checkPair();
|
|
1745
|
+
this.clearRecordAccess();
|
|
1746
|
+
this.publicSession = true;
|
|
1747
|
+
this.publicSessionRevision++;
|
|
1748
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current =
|
|
1749
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token;
|
|
1750
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.epoch++;
|
|
1751
|
+
publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
1752
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").add(restored);
|
|
1753
|
+
check();
|
|
1754
|
+
// Recreate only the public transaction wrapper retired by lockWallet.
|
|
1755
|
+
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1756
|
+
result = await work(request.publicState);
|
|
1757
|
+
check();
|
|
1758
|
+
await checkPair();
|
|
1759
|
+
completed = true;
|
|
1760
|
+
}
|
|
1761
|
+
finally {
|
|
1762
|
+
wrappingSecret?.fill(0);
|
|
1763
|
+
}
|
|
1764
|
+
})();
|
|
1765
|
+
void pending.catch(() => undefined);
|
|
1766
|
+
return pending;
|
|
1767
|
+
});
|
|
1768
|
+
if (pending)
|
|
1769
|
+
await pending;
|
|
1770
|
+
check();
|
|
1771
|
+
if (!entered || !completed)
|
|
1772
|
+
throw new Error('Public session credential was not consumed');
|
|
1773
|
+
succeeded = true;
|
|
1774
|
+
return result;
|
|
1775
|
+
}
|
|
1776
|
+
finally {
|
|
1777
|
+
authority.signal.removeEventListener('abort', retire);
|
|
1778
|
+
request.publicState.fill(0);
|
|
1779
|
+
if (!succeeded) {
|
|
1780
|
+
restored?.dispose();
|
|
1781
|
+
retire();
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
operation.active = false;
|
|
1785
|
+
this.clearSecretMaterial(true);
|
|
1786
|
+
}
|
|
1787
|
+
if (this.secretOperation === operation)
|
|
1788
|
+
this.secretOperation = undefined;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
async unlock(password) {
|
|
1792
|
+
if (this.secretLifetime === 'session')
|
|
1793
|
+
return this.unlockSession(password);
|
|
1794
|
+
if (this.secretOperation)
|
|
1795
|
+
throw new Error('Secret operation is busy');
|
|
1796
|
+
const operation = {
|
|
1797
|
+
active: true,
|
|
1798
|
+
working: false,
|
|
1799
|
+
assertCurrent: __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this),
|
|
1800
|
+
};
|
|
1801
|
+
this.secretOperation = operation;
|
|
1802
|
+
try {
|
|
1803
|
+
const result = await this.unlockSession(password);
|
|
1804
|
+
if (result.canLogin) {
|
|
1805
|
+
this.clearRecordAccess();
|
|
1806
|
+
this.publicSession = true;
|
|
1807
|
+
this.publicSessionRevision++;
|
|
1808
|
+
}
|
|
1809
|
+
return result;
|
|
1810
|
+
}
|
|
1811
|
+
finally {
|
|
1812
|
+
operation.active = false;
|
|
1813
|
+
this.clearSecretMaterial(this.publicSession);
|
|
1814
|
+
if (this.secretOperation === operation)
|
|
1815
|
+
this.secretOperation = undefined;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1315
1818
|
/** Capture wallet-session authority without binding an account or network. */
|
|
1316
1819
|
captureUnlockedSessionGuard() {
|
|
1317
1820
|
const assertOwner = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
@@ -1333,7 +1836,7 @@ class KeyringCore {
|
|
|
1333
1836
|
* Storage, platform and session restoration failures reject so callers do
|
|
1334
1837
|
* not charge a failed password attempt for an operational error.
|
|
1335
1838
|
*/
|
|
1336
|
-
async
|
|
1839
|
+
async unlockSession(password) {
|
|
1337
1840
|
let assertCurrent = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1338
1841
|
let authenticated = false;
|
|
1339
1842
|
try {
|
|
@@ -1552,6 +2055,51 @@ class KeyringCore {
|
|
|
1552
2055
|
throw new Error('Invalid private record root');
|
|
1553
2056
|
return { secret: new TextEncoder().encode(root), assertCurrent };
|
|
1554
2057
|
}
|
|
2058
|
+
clearRecordAccess() {
|
|
2059
|
+
for (const access of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f"))
|
|
2060
|
+
access.dispose();
|
|
2061
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").clear();
|
|
2062
|
+
}
|
|
2063
|
+
/** Public transaction records may outlive a signer without retaining the spending root. */
|
|
2064
|
+
async createPrivateRecordAccess(domains, options) {
|
|
2065
|
+
this.assertSecretAccess();
|
|
2066
|
+
const session = this.privateRecordSession();
|
|
2067
|
+
const publicGuard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
2068
|
+
for (const previous of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")) {
|
|
2069
|
+
if ((0, private_records_1.activePrivateRecordDomainSet)(previous, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) === null)
|
|
2070
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").delete(previous);
|
|
2071
|
+
}
|
|
2072
|
+
let access;
|
|
2073
|
+
try {
|
|
2074
|
+
access = await (0, private_records_1.createPrivateRecordAccess)(session.secret, domains, options, publicGuard, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f"));
|
|
2075
|
+
session.assertCurrent();
|
|
2076
|
+
publicGuard();
|
|
2077
|
+
__classPrivateFieldGet(this, _KeyringCore_recordAccess, "f").add(access);
|
|
2078
|
+
return access;
|
|
2079
|
+
}
|
|
2080
|
+
catch (error) {
|
|
2081
|
+
access?.dispose();
|
|
2082
|
+
throw error;
|
|
2083
|
+
}
|
|
2084
|
+
finally {
|
|
2085
|
+
session.secret.fill(0);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
/** Retrieve only an exact domain set already derived under a secret operation. Never unlocks or derives. */
|
|
2089
|
+
getPrivateRecordAccess(domains) {
|
|
2090
|
+
const guard = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_capturePublicSessionGuard).call(this);
|
|
2091
|
+
const domainSet = (0, private_records_1.privateRecordDomainSet)(domains);
|
|
2092
|
+
guard();
|
|
2093
|
+
for (const access of __classPrivateFieldGet(this, _KeyringCore_recordAccess, "f")) {
|
|
2094
|
+
if ((0, private_records_1.privateRecordAccessCanTransfer)(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) &&
|
|
2095
|
+
(0, private_records_1.activePrivateRecordDomainSet)(access, __classPrivateFieldGet(this, _KeyringCore_recordAccessOwner, "f")) ===
|
|
2096
|
+
domainSet) {
|
|
2097
|
+
guard();
|
|
2098
|
+
return access;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
throw new Error('Record access is not initialized for these domains');
|
|
2102
|
+
}
|
|
1555
2103
|
/** Stable local profile identifier; never an account address or a key. */
|
|
1556
2104
|
async getPrivateRecordProfileId() {
|
|
1557
2105
|
const session = this.privateRecordSession();
|
|
@@ -2514,6 +3062,7 @@ class KeyringCore {
|
|
|
2514
3062
|
}
|
|
2515
3063
|
// Helper methods for secure buffer operations
|
|
2516
3064
|
getSessionPasswordString() {
|
|
3065
|
+
this.assertSecretAccess();
|
|
2517
3066
|
if (!this.sessionPassword || this.sessionPassword.isCleared()) {
|
|
2518
3067
|
throw new Error('Session password not available');
|
|
2519
3068
|
}
|
|
@@ -2522,6 +3071,7 @@ class KeyringCore {
|
|
|
2522
3071
|
return this.sessionPassword.toString();
|
|
2523
3072
|
}
|
|
2524
3073
|
getSessionMnemonicString() {
|
|
3074
|
+
this.assertSecretAccess();
|
|
2525
3075
|
if (!this.sessionMnemonic || this.sessionMnemonic.isCleared()) {
|
|
2526
3076
|
throw new Error('Session mnemonic not available');
|
|
2527
3077
|
}
|
|
@@ -2635,7 +3185,7 @@ class KeyringCore {
|
|
|
2635
3185
|
}
|
|
2636
3186
|
}
|
|
2637
3187
|
exports.KeyringCore = KeyringCore;
|
|
2638
|
-
_KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership = new WeakMap(), _KeyringCore_instances = new WeakSet(), _KeyringCore_createDeviceUnlockControls = function _KeyringCore_createDeviceUnlockControls(port, readVaultPair) {
|
|
3188
|
+
_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) {
|
|
2639
3189
|
const readPair = () => readCurrentVaultPair(readVaultPair);
|
|
2640
3190
|
let pending = false;
|
|
2641
3191
|
const run = async (request, assertCurrent) => {
|
|
@@ -2726,7 +3276,13 @@ _KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership
|
|
|
2726
3276
|
else {
|
|
2727
3277
|
if (__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_finishUnlock).call(this).needsAccountCreation)
|
|
2728
3278
|
return false;
|
|
3279
|
+
this.clearRecordAccess();
|
|
2729
3280
|
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_installVaultSession).call(this, mnemonic, keyHex, check);
|
|
3281
|
+
if (this.secretLifetime === 'operation') {
|
|
3282
|
+
this.publicSession = true;
|
|
3283
|
+
this.publicSessionRevision++;
|
|
3284
|
+
this.clearSecretMaterial(true);
|
|
3285
|
+
}
|
|
2730
3286
|
}
|
|
2731
3287
|
return true;
|
|
2732
3288
|
}
|
|
@@ -2744,6 +3300,23 @@ _KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership
|
|
|
2744
3300
|
},
|
|
2745
3301
|
resume: (assertCurrent) => run(null, assertCurrent),
|
|
2746
3302
|
});
|
|
3303
|
+
}, _KeyringCore_capturePublicSessionGuard = function _KeyringCore_capturePublicSessionGuard() {
|
|
3304
|
+
const assertOwner = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
3305
|
+
const revision = this.publicSessionRevision;
|
|
3306
|
+
const check = () => {
|
|
3307
|
+
if (this.secretLifetime === 'session') {
|
|
3308
|
+
assertOwner();
|
|
3309
|
+
if (!this.sessionPassword || this.sessionPassword.isCleared())
|
|
3310
|
+
throw new Error('Wallet unlock session changed');
|
|
3311
|
+
}
|
|
3312
|
+
else if (!this.publicSession ||
|
|
3313
|
+
this.publicSessionRevision !== revision ||
|
|
3314
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker.current !== __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").token) {
|
|
3315
|
+
throw new Error('Wallet public session changed');
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
check();
|
|
3319
|
+
return check;
|
|
2747
3320
|
}, _KeyringCore_acceptSessionFrom = function _KeyringCore_acceptSessionFrom(source) {
|
|
2748
3321
|
if (source.authority !== this.authority ||
|
|
2749
3322
|
source.storage !== this.storage ||
|