@bsv/wallet-toolbox-mobile 2.1.9 → 2.1.11
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/out/src/CWIStyleWalletManager.d.ts +57 -5
- package/out/src/CWIStyleWalletManager.d.ts.map +1 -1
- package/out/src/CWIStyleWalletManager.js +282 -46
- package/out/src/CWIStyleWalletManager.js.map +1 -1
- package/out/src/SetupClient.d.ts.map +1 -1
- package/out/src/SetupClient.js +1 -2
- package/out/src/SetupClient.js.map +1 -1
- package/out/src/WalletAuthenticationManager.d.ts +1 -1
- package/out/src/WalletAuthenticationManager.d.ts.map +1 -1
- package/out/src/WalletAuthenticationManager.js +41 -31
- package/out/src/WalletAuthenticationManager.js.map +1 -1
- package/out/src/monitor/Monitor.d.ts +4 -3
- package/out/src/monitor/Monitor.d.ts.map +1 -1
- package/out/src/monitor/Monitor.js +39 -11
- package/out/src/monitor/Monitor.js.map +1 -1
- package/out/src/monitor/tasks/TaskReviewDoubleSpends.d.ts +26 -0
- package/out/src/monitor/tasks/TaskReviewDoubleSpends.d.ts.map +1 -0
- package/out/src/monitor/tasks/TaskReviewDoubleSpends.js +124 -0
- package/out/src/monitor/tasks/TaskReviewDoubleSpends.js.map +1 -0
- package/out/src/monitor/tasks/TaskReviewProvenTxs.d.ts +34 -0
- package/out/src/monitor/tasks/TaskReviewProvenTxs.d.ts.map +1 -0
- package/out/src/monitor/tasks/TaskReviewProvenTxs.js +131 -0
- package/out/src/monitor/tasks/TaskReviewProvenTxs.js.map +1 -0
- package/out/src/monitor/tasks/TaskReviewUtxos.d.ts +23 -0
- package/out/src/monitor/tasks/TaskReviewUtxos.d.ts.map +1 -0
- package/out/src/monitor/tasks/TaskReviewUtxos.js +71 -0
- package/out/src/monitor/tasks/TaskReviewUtxos.js.map +1 -0
- package/out/src/monitor/tasks/TaskSendWaiting.d.ts +14 -1
- package/out/src/monitor/tasks/TaskSendWaiting.d.ts.map +1 -1
- package/out/src/monitor/tasks/TaskSendWaiting.js +86 -20
- package/out/src/monitor/tasks/TaskSendWaiting.js.map +1 -1
- package/out/src/sdk/WalletStorage.interfaces.d.ts +9 -0
- package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
- package/out/src/services/Services.d.ts.map +1 -1
- package/out/src/services/Services.js +10 -2
- package/out/src/services/Services.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.js +1 -1
- package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.js.map +1 -1
- package/out/src/services/createDefaultWalletServicesOptions.js +1 -1
- package/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
- package/out/src/storage/StorageProvider.d.ts +6 -1
- package/out/src/storage/StorageProvider.d.ts.map +1 -1
- package/out/src/storage/StorageProvider.js +6 -0
- package/out/src/storage/StorageProvider.js.map +1 -1
- package/out/src/storage/StorageReaderWriter.d.ts +2 -1
- package/out/src/storage/StorageReaderWriter.d.ts.map +1 -1
- package/out/src/storage/StorageReaderWriter.js.map +1 -1
- package/out/src/storage/WalletStorageManager.d.ts +7 -0
- package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
- package/out/src/storage/WalletStorageManager.js +33 -2
- package/out/src/storage/WalletStorageManager.js.map +1 -1
- package/package.json +4 -2
- package/out/src/monitor/tasks/TaskSyncWhenIdle.d.ts +0 -12
- package/out/src/monitor/tasks/TaskSyncWhenIdle.d.ts.map +0 -1
- package/out/src/monitor/tasks/TaskSyncWhenIdle.js +0 -22
- package/out/src/monitor/tasks/TaskSyncWhenIdle.js.map +0 -1
|
@@ -1,15 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CWIStyleWalletManager = exports.OverlayUMPTokenInteractor = exports.DEFAULT_PROFILE_ID = exports.PBKDF2_NUM_ROUNDS = void 0;
|
|
3
|
+
exports.CWIStyleWalletManager = exports.OverlayUMPTokenInteractor = exports.DEFAULT_PROFILE_ID = exports.ARGON2ID_DEFAULT_HASH_LENGTH = exports.ARGON2ID_DEFAULT_PARALLELISM = exports.ARGON2ID_DEFAULT_MEMORY_KIB = exports.ARGON2ID_DEFAULT_ITERATIONS = exports.PBKDF2_NUM_ROUNDS = void 0;
|
|
4
4
|
const sdk_1 = require("@bsv/sdk");
|
|
5
|
+
const hash_wasm_1 = require("hash-wasm");
|
|
5
6
|
const PrivilegedKeyManager_1 = require("./sdk/PrivilegedKeyManager");
|
|
6
7
|
/**
|
|
7
8
|
* Number of rounds used in PBKDF2 for deriving password keys.
|
|
8
9
|
*/
|
|
9
10
|
exports.PBKDF2_NUM_ROUNDS = 7777;
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
|
|
12
|
+
* Default Argon2id parameters for password-key derivation (UMP v3).
|
|
13
|
+
*/
|
|
14
|
+
exports.ARGON2ID_DEFAULT_ITERATIONS = 7;
|
|
15
|
+
exports.ARGON2ID_DEFAULT_MEMORY_KIB = 131072;
|
|
16
|
+
exports.ARGON2ID_DEFAULT_PARALLELISM = 1;
|
|
17
|
+
exports.ARGON2ID_DEFAULT_HASH_LENGTH = 32;
|
|
18
|
+
function isLikelyDerSignatureField(field) {
|
|
19
|
+
if (!field || field.length < 8 || field.length > 80) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (field[0] !== 0x30) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const declaredLen = field[1];
|
|
26
|
+
return declaredLen === field.length - 2;
|
|
27
|
+
}
|
|
28
|
+
function stripVerifiedPushDropSignature(fields, lockingPublicKey) {
|
|
29
|
+
if (fields.length <= 11) {
|
|
30
|
+
return fields;
|
|
31
|
+
}
|
|
32
|
+
const protocolFieldCount = fields.length - 1;
|
|
33
|
+
const trailingField = fields[protocolFieldCount];
|
|
34
|
+
if (!trailingField || !isLikelyDerSignatureField(trailingField)) {
|
|
35
|
+
return fields;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
let dataLength = 0;
|
|
39
|
+
for (let i = 0; i < protocolFieldCount; i++) {
|
|
40
|
+
dataLength += fields[i].length;
|
|
41
|
+
}
|
|
42
|
+
const dataToVerify = new Array(dataLength);
|
|
43
|
+
let writeOffset = 0;
|
|
44
|
+
for (let i = 0; i < protocolFieldCount; i++) {
|
|
45
|
+
const field = fields[i];
|
|
46
|
+
for (let j = 0; j < field.length; j++) {
|
|
47
|
+
dataToVerify[writeOffset++] = field[j];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const signature = sdk_1.Signature.fromDER(trailingField);
|
|
51
|
+
if (signature.verify(dataToVerify, lockingPublicKey)) {
|
|
52
|
+
return fields.slice(0, protocolFieldCount);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
return fields;
|
|
57
|
+
}
|
|
58
|
+
return fields;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* PBKDF-2 that prefers WebCrypto and falls back to hash-wasm.
|
|
13
62
|
*
|
|
14
63
|
* @param passwordBytes Raw password bytes.
|
|
15
64
|
* @param salt Salt bytes.
|
|
@@ -18,7 +67,7 @@ exports.PBKDF2_NUM_ROUNDS = 7777;
|
|
|
18
67
|
* @param hash Digest algorithm (default "sha512").
|
|
19
68
|
* @returns Derived key bytes.
|
|
20
69
|
*/
|
|
21
|
-
async function
|
|
70
|
+
async function pbkdf2NativeOrWasm(passwordBytes, salt, iterations, keyLen, hash = 'sha512') {
|
|
22
71
|
var _a;
|
|
23
72
|
// ----- fast-path: WebCrypto (both browser & recent Node expose globalThis.crypto.subtle)
|
|
24
73
|
const subtle = (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto) === null || _a === void 0 ? void 0 : _a.subtle;
|
|
@@ -39,8 +88,52 @@ async function pbkdf2NativeOrJs(passwordBytes, salt, iterations, keyLen, hash =
|
|
|
39
88
|
/* fall through */
|
|
40
89
|
}
|
|
41
90
|
}
|
|
42
|
-
|
|
43
|
-
|
|
91
|
+
const hashFunction = hash === 'sha256' ? (0, hash_wasm_1.createSHA256)() : (0, hash_wasm_1.createSHA512)();
|
|
92
|
+
const derived = await (0, hash_wasm_1.pbkdf2)({
|
|
93
|
+
password: new Uint8Array(passwordBytes),
|
|
94
|
+
salt: new Uint8Array(salt),
|
|
95
|
+
iterations,
|
|
96
|
+
hashLength: keyLen,
|
|
97
|
+
hashFunction,
|
|
98
|
+
outputType: 'binary'
|
|
99
|
+
});
|
|
100
|
+
return Array.from(derived);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Derives the password key from a password using the KDF specified in the UMP token.
|
|
104
|
+
* For legacy tokens (no KDF metadata), uses PBKDF2 with fixed rounds (7777).
|
|
105
|
+
* For v3 tokens, uses the algorithm specified in passwordKdf metadata.
|
|
106
|
+
*
|
|
107
|
+
* @param token The UMP token containing KDF metadata and salt.
|
|
108
|
+
* @param passwordBytes Raw password bytes.
|
|
109
|
+
* @param overrideKdf Optional KDF config to override token/default settings.
|
|
110
|
+
* @returns Derived password key bytes.
|
|
111
|
+
*/
|
|
112
|
+
async function derivePasswordKey(token, passwordBytes, overrideKdf) {
|
|
113
|
+
var _a, _b, _c, _d, _e;
|
|
114
|
+
const kdf = overrideKdf || token.passwordKdf;
|
|
115
|
+
// Legacy token or explicit PBKDF2 request
|
|
116
|
+
if (!kdf || kdf.algorithm === 'pbkdf2-sha512') {
|
|
117
|
+
return pbkdf2NativeOrWasm(passwordBytes, token.passwordSalt, (_a = kdf === null || kdf === void 0 ? void 0 : kdf.iterations) !== null && _a !== void 0 ? _a : exports.PBKDF2_NUM_ROUNDS, 32, 'sha512');
|
|
118
|
+
}
|
|
119
|
+
// Argon2id path (UMP v3)
|
|
120
|
+
if (kdf.algorithm === 'argon2id') {
|
|
121
|
+
const iterations = (_b = kdf.iterations) !== null && _b !== void 0 ? _b : exports.ARGON2ID_DEFAULT_ITERATIONS;
|
|
122
|
+
const memorySize = (_c = kdf.memoryKiB) !== null && _c !== void 0 ? _c : exports.ARGON2ID_DEFAULT_MEMORY_KIB;
|
|
123
|
+
const parallelism = (_d = kdf.parallelism) !== null && _d !== void 0 ? _d : exports.ARGON2ID_DEFAULT_PARALLELISM;
|
|
124
|
+
const hashLength = (_e = kdf.hashLength) !== null && _e !== void 0 ? _e : exports.ARGON2ID_DEFAULT_HASH_LENGTH;
|
|
125
|
+
const hash = await (0, hash_wasm_1.argon2id)({
|
|
126
|
+
password: new Uint8Array(passwordBytes),
|
|
127
|
+
salt: new Uint8Array(token.passwordSalt),
|
|
128
|
+
iterations,
|
|
129
|
+
memorySize,
|
|
130
|
+
parallelism,
|
|
131
|
+
hashLength,
|
|
132
|
+
outputType: 'binary'
|
|
133
|
+
});
|
|
134
|
+
return Array.from(hash);
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Unsupported KDF algorithm: ${kdf.algorithm}`);
|
|
44
137
|
}
|
|
45
138
|
/**
|
|
46
139
|
* Unique Identifier for the default profile (16 zero bytes).
|
|
@@ -127,11 +220,32 @@ class OverlayUMPTokenInteractor {
|
|
|
127
220
|
fields[8] = token.presentationKeyEncrypted;
|
|
128
221
|
fields[9] = token.passwordKeyEncrypted;
|
|
129
222
|
fields[10] = token.recoveryKeyEncrypted;
|
|
130
|
-
// Optional field
|
|
223
|
+
// Optional field for encrypted profiles
|
|
131
224
|
if (token.profilesEncrypted) {
|
|
132
225
|
fields[11] = token.profilesEncrypted;
|
|
133
226
|
}
|
|
227
|
+
// V3 fields: umpVersion, kdfAlgorithm, kdfParams
|
|
228
|
+
if (token.umpVersion === 3 && token.passwordKdf) {
|
|
229
|
+
const versionFieldIndex = token.profilesEncrypted ? 12 : 11;
|
|
230
|
+
fields[versionFieldIndex] = [token.umpVersion]; // Single byte
|
|
231
|
+
fields[versionFieldIndex + 1] = sdk_1.Utils.toArray(token.passwordKdf.algorithm, 'utf8');
|
|
232
|
+
// Construct kdfParams JSON
|
|
233
|
+
const kdfParams = {
|
|
234
|
+
iterations: token.passwordKdf.iterations
|
|
235
|
+
};
|
|
236
|
+
if (token.passwordKdf.memoryKiB !== undefined) {
|
|
237
|
+
kdfParams.memoryKiB = token.passwordKdf.memoryKiB;
|
|
238
|
+
}
|
|
239
|
+
if (token.passwordKdf.parallelism !== undefined) {
|
|
240
|
+
kdfParams.parallelism = token.passwordKdf.parallelism;
|
|
241
|
+
}
|
|
242
|
+
if (token.passwordKdf.hashLength !== undefined) {
|
|
243
|
+
kdfParams.hashLength = token.passwordKdf.hashLength;
|
|
244
|
+
}
|
|
245
|
+
fields[versionFieldIndex + 2] = sdk_1.Utils.toArray(JSON.stringify(kdfParams), 'utf8');
|
|
246
|
+
}
|
|
134
247
|
// 2) Create a PushDrop script referencing these fields, locked with the admin key.
|
|
248
|
+
// The signature will be added as trailing metadata by PushDrop (not part of protocol fields).
|
|
135
249
|
const script = await new sdk_1.PushDrop(wallet, adminOriginator).lock(fields, [2, 'admin user management token'], // protocolID
|
|
136
250
|
'1', // keyID
|
|
137
251
|
'self', // counterparty
|
|
@@ -257,7 +371,7 @@ class OverlayUMPTokenInteractor {
|
|
|
257
371
|
* @returns The parsed UMPToken or `undefined` if none found/decodable.
|
|
258
372
|
*/
|
|
259
373
|
parseLookupAnswer(answer) {
|
|
260
|
-
var _a;
|
|
374
|
+
var _a, _b, _c;
|
|
261
375
|
if (answer.type !== 'output-list') {
|
|
262
376
|
return undefined;
|
|
263
377
|
}
|
|
@@ -274,23 +388,60 @@ class OverlayUMPTokenInteractor {
|
|
|
274
388
|
console.warn(`Unexpected number of fields in UMP token: ${(_a = decoded.fields) === null || _a === void 0 ? void 0 : _a.length}`);
|
|
275
389
|
return undefined;
|
|
276
390
|
}
|
|
391
|
+
// Parse protocol fields, excluding the trailing PushDrop signature only when
|
|
392
|
+
// it is cryptographically valid for the preceding field payload.
|
|
393
|
+
let protocolFields = stripVerifiedPushDropSignature(decoded.fields, decoded.lockingPublicKey);
|
|
394
|
+
// Detect v3 token metadata in either layout:
|
|
395
|
+
// - with profilesEncrypted present: [0..10]=core, [11]=profiles, [12]=version, [13]=algorithm, [14]=params
|
|
396
|
+
// - without profilesEncrypted: [0..10]=core, [11]=version, [12]=algorithm, [13]=params
|
|
397
|
+
const hasV3MetadataWithProfiles = protocolFields.length >= 15 && ((_b = protocolFields[12]) === null || _b === void 0 ? void 0 : _b.length) === 1 && protocolFields[12][0] === 3;
|
|
398
|
+
const hasV3MetadataWithoutProfiles = protocolFields.length >= 14 && ((_c = protocolFields[11]) === null || _c === void 0 ? void 0 : _c.length) === 1 && protocolFields[11][0] === 3;
|
|
399
|
+
const kdfVersionFieldIndex = hasV3MetadataWithProfiles ? 12 : hasV3MetadataWithoutProfiles ? 11 : -1;
|
|
277
400
|
// Build the UMP token from these fields, preserving outpoint
|
|
278
401
|
const t = {
|
|
279
|
-
//
|
|
280
|
-
passwordSalt:
|
|
281
|
-
passwordPresentationPrimary:
|
|
282
|
-
passwordRecoveryPrimary:
|
|
283
|
-
presentationRecoveryPrimary:
|
|
284
|
-
passwordPrimaryPrivileged:
|
|
285
|
-
presentationRecoveryPrivileged:
|
|
286
|
-
presentationHash:
|
|
287
|
-
recoveryHash:
|
|
288
|
-
presentationKeyEncrypted:
|
|
289
|
-
passwordKeyEncrypted:
|
|
290
|
-
recoveryKeyEncrypted:
|
|
291
|
-
profilesEncrypted: decoded.fields[12] ? decoded.fields[11] : undefined, // If there's a signature in field 12, use field 11
|
|
402
|
+
// Core fields (unchanged for all versions)
|
|
403
|
+
passwordSalt: protocolFields[0],
|
|
404
|
+
passwordPresentationPrimary: protocolFields[1],
|
|
405
|
+
passwordRecoveryPrimary: protocolFields[2],
|
|
406
|
+
presentationRecoveryPrimary: protocolFields[3],
|
|
407
|
+
passwordPrimaryPrivileged: protocolFields[4],
|
|
408
|
+
presentationRecoveryPrivileged: protocolFields[5],
|
|
409
|
+
presentationHash: protocolFields[6],
|
|
410
|
+
recoveryHash: protocolFields[7],
|
|
411
|
+
presentationKeyEncrypted: protocolFields[8],
|
|
412
|
+
passwordKeyEncrypted: protocolFields[9],
|
|
413
|
+
recoveryKeyEncrypted: protocolFields[10],
|
|
292
414
|
currentOutpoint: outpoint
|
|
293
415
|
};
|
|
416
|
+
// Encrypted profiles (optional, all versions)
|
|
417
|
+
const hasProfilesField = hasV3MetadataWithProfiles || (!hasV3MetadataWithoutProfiles && !!protocolFields[11]);
|
|
418
|
+
if (hasProfilesField && protocolFields[11] && protocolFields[11].length > 0) {
|
|
419
|
+
t.profilesEncrypted = protocolFields[11];
|
|
420
|
+
}
|
|
421
|
+
// V3 fields: umpVersion, kdfAlgorithm, kdfParams (fields 12, 13, 14)
|
|
422
|
+
if (kdfVersionFieldIndex !== -1) {
|
|
423
|
+
t.umpVersion = protocolFields[kdfVersionFieldIndex][0]; // Single byte value 3
|
|
424
|
+
const kdfAlgorithmField = protocolFields[kdfVersionFieldIndex + 1];
|
|
425
|
+
const kdfParamsField = protocolFields[kdfVersionFieldIndex + 2];
|
|
426
|
+
if (!kdfAlgorithmField || !kdfParamsField) {
|
|
427
|
+
return t;
|
|
428
|
+
}
|
|
429
|
+
const kdfAlgorithm = sdk_1.Utils.toUTF8(kdfAlgorithmField);
|
|
430
|
+
const kdfParamsJson = sdk_1.Utils.toUTF8(kdfParamsField);
|
|
431
|
+
try {
|
|
432
|
+
const kdfParams = JSON.parse(kdfParamsJson);
|
|
433
|
+
t.passwordKdf = {
|
|
434
|
+
algorithm: kdfAlgorithm,
|
|
435
|
+
iterations: kdfParams.iterations,
|
|
436
|
+
memoryKiB: kdfParams.memoryKiB,
|
|
437
|
+
parallelism: kdfParams.parallelism,
|
|
438
|
+
hashLength: kdfParams.hashLength
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
catch (e) {
|
|
442
|
+
console.warn('Failed to parse v3 KDF params:', e);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
294
445
|
return t;
|
|
295
446
|
}
|
|
296
447
|
catch (e) {
|
|
@@ -336,8 +487,10 @@ class CWIStyleWalletManager {
|
|
|
336
487
|
* @param passwordRetriever A function to request the user's password.
|
|
337
488
|
* @param newWalletFunder Optional function to fund a new wallet.
|
|
338
489
|
* @param stateSnapshot Optional previously saved state snapshot.
|
|
490
|
+
* @param kdfConfig Optional KDF configuration for new UMP tokens.
|
|
339
491
|
*/
|
|
340
|
-
constructor(adminOriginator, walletBuilder, interactor = new OverlayUMPTokenInteractor(), recoveryKeySaver, passwordRetriever, newWalletFunder, stateSnapshot) {
|
|
492
|
+
constructor(adminOriginator, walletBuilder, interactor = new OverlayUMPTokenInteractor(), recoveryKeySaver, passwordRetriever, newWalletFunder, stateSnapshot, kdfConfig) {
|
|
493
|
+
var _a, _b, _c, _d, _e;
|
|
341
494
|
/**
|
|
342
495
|
* Current mode of authentication.
|
|
343
496
|
*/
|
|
@@ -361,6 +514,14 @@ class CWIStyleWalletManager {
|
|
|
361
514
|
this.passwordRetriever = passwordRetriever;
|
|
362
515
|
this.authenticated = false;
|
|
363
516
|
this.newWalletFunder = newWalletFunder;
|
|
517
|
+
// Initialize KDF config with Argon2id defaults for v3 tokens
|
|
518
|
+
this.kdfConfig = {
|
|
519
|
+
algorithm: (_a = kdfConfig === null || kdfConfig === void 0 ? void 0 : kdfConfig.algorithm) !== null && _a !== void 0 ? _a : 'argon2id',
|
|
520
|
+
iterations: (_b = kdfConfig === null || kdfConfig === void 0 ? void 0 : kdfConfig.iterations) !== null && _b !== void 0 ? _b : exports.ARGON2ID_DEFAULT_ITERATIONS,
|
|
521
|
+
memoryKiB: (_c = kdfConfig === null || kdfConfig === void 0 ? void 0 : kdfConfig.memoryKiB) !== null && _c !== void 0 ? _c : exports.ARGON2ID_DEFAULT_MEMORY_KIB,
|
|
522
|
+
parallelism: (_d = kdfConfig === null || kdfConfig === void 0 ? void 0 : kdfConfig.parallelism) !== null && _d !== void 0 ? _d : exports.ARGON2ID_DEFAULT_PARALLELISM,
|
|
523
|
+
hashLength: (_e = kdfConfig === null || kdfConfig === void 0 ? void 0 : kdfConfig.hashLength) !== null && _e !== void 0 ? _e : exports.ARGON2ID_DEFAULT_HASH_LENGTH
|
|
524
|
+
};
|
|
364
525
|
// If a saved snapshot is provided, attempt to load it.
|
|
365
526
|
// Note: loadSnapshot now returns a promise. We don't await it here,
|
|
366
527
|
// as the constructor must be synchronous. The caller should check
|
|
@@ -413,7 +574,8 @@ class CWIStyleWalletManager {
|
|
|
413
574
|
if (!this.currentUMPToken) {
|
|
414
575
|
throw new Error('Provide presentation or recovery key first.');
|
|
415
576
|
}
|
|
416
|
-
|
|
577
|
+
// Use token-driven KDF (legacy PBKDF2 or v3 Argon2id)
|
|
578
|
+
const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, sdk_1.Utils.toArray(password, 'utf8'));
|
|
417
579
|
let rootPrimaryKey;
|
|
418
580
|
let rootPrivilegedKey; // Only needed for recovery mode
|
|
419
581
|
if (this.authenticationMode === 'presentation-key-and-password') {
|
|
@@ -447,7 +609,12 @@ class CWIStyleWalletManager {
|
|
|
447
609
|
const recoveryKey = (0, sdk_1.Random)(32);
|
|
448
610
|
await this.recoveryKeySaver(recoveryKey);
|
|
449
611
|
const passwordSalt = (0, sdk_1.Random)(32);
|
|
450
|
-
|
|
612
|
+
// Build temporary token with KDF config for password derivation
|
|
613
|
+
const tempTokenForKdf = {
|
|
614
|
+
passwordSalt,
|
|
615
|
+
passwordKdf: this.kdfConfig
|
|
616
|
+
};
|
|
617
|
+
const passwordKey = await derivePasswordKey(tempTokenForKdf, sdk_1.Utils.toArray(password, 'utf8'));
|
|
451
618
|
const rootPrimaryKey = (0, sdk_1.Random)(32);
|
|
452
619
|
const rootPrivilegedKey = (0, sdk_1.Random)(32);
|
|
453
620
|
// Build XOR keys
|
|
@@ -457,7 +624,7 @@ class CWIStyleWalletManager {
|
|
|
457
624
|
const primaryPassword = new sdk_1.SymmetricKey(this.XOR(rootPrimaryKey, passwordKey));
|
|
458
625
|
// Temp manager for encryption
|
|
459
626
|
const tempPrivilegedKeyManager = new PrivilegedKeyManager_1.PrivilegedKeyManager(async () => new sdk_1.PrivateKey(rootPrivilegedKey));
|
|
460
|
-
// Build new UMP token (no profiles initially)
|
|
627
|
+
// Build new UMP token (v3 with KDF metadata, no profiles initially)
|
|
461
628
|
const newToken = {
|
|
462
629
|
passwordSalt,
|
|
463
630
|
passwordPresentationPrimary: presentationPassword.encrypt(rootPrimaryKey),
|
|
@@ -482,7 +649,9 @@ class CWIStyleWalletManager {
|
|
|
482
649
|
protocolID: [2, 'admin key wrapping'],
|
|
483
650
|
keyID: '1'
|
|
484
651
|
})).ciphertext,
|
|
485
|
-
profilesEncrypted: undefined // No profiles yet
|
|
652
|
+
profilesEncrypted: undefined, // No profiles yet
|
|
653
|
+
umpVersion: 3, // UMP protocol version 3
|
|
654
|
+
passwordKdf: this.kdfConfig // KDF metadata for v3
|
|
486
655
|
};
|
|
487
656
|
this.currentUMPToken = newToken;
|
|
488
657
|
// Setup root infrastructure and switch to default profile
|
|
@@ -495,7 +664,8 @@ class CWIStyleWalletManager {
|
|
|
495
664
|
}
|
|
496
665
|
catch (e) {
|
|
497
666
|
console.error('Error funding new wallet:', e);
|
|
498
|
-
|
|
667
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
668
|
+
throw new Error(`Failed to fund new wallet before publishing UMP token: ${message}`);
|
|
499
669
|
}
|
|
500
670
|
}
|
|
501
671
|
// Publish the new UMP token *after* potentially funding
|
|
@@ -803,11 +973,18 @@ class CWIStyleWalletManager {
|
|
|
803
973
|
* Changes the user's password. Re-wraps keys and updates the UMP token.
|
|
804
974
|
*/
|
|
805
975
|
async changePassword(newPassword) {
|
|
976
|
+
var _a;
|
|
806
977
|
if (!this.authenticated || !this.currentUMPToken || !this.rootPrimaryKey || !this.rootPrivilegedKeyManager) {
|
|
807
978
|
throw new Error('Not authenticated or missing required data.');
|
|
808
979
|
}
|
|
809
980
|
const passwordSalt = (0, sdk_1.Random)(32);
|
|
810
|
-
|
|
981
|
+
// Preserve current token's KDF metadata (or use manager's kdfConfig for legacy tokens)
|
|
982
|
+
const kdfToUse = (_a = this.currentUMPToken.passwordKdf) !== null && _a !== void 0 ? _a : this.kdfConfig;
|
|
983
|
+
const tempTokenForKdf = {
|
|
984
|
+
passwordSalt,
|
|
985
|
+
passwordKdf: kdfToUse
|
|
986
|
+
};
|
|
987
|
+
const newPasswordKey = await derivePasswordKey(tempTokenForKdf, sdk_1.Utils.toArray(newPassword, 'utf8'));
|
|
811
988
|
// Decrypt existing factors needed for re-encryption, using the *root* privileged key manager
|
|
812
989
|
const recoveryKey = await this.getFactor('recoveryKey');
|
|
813
990
|
const presentationKey = await this.getFactor('presentationKey');
|
|
@@ -936,6 +1113,7 @@ class CWIStyleWalletManager {
|
|
|
936
1113
|
async updateAuthFactors(passwordSalt, passwordKey, presentationKey, recoveryKey, rootPrimaryKey, rootPrivilegedKey, // Explicitly pass the root key bytes
|
|
937
1114
|
profiles // Pass current/new profiles list
|
|
938
1115
|
) {
|
|
1116
|
+
var _a;
|
|
939
1117
|
if (!this.authenticated || !this.rootPrimaryKey || !this.currentUMPToken) {
|
|
940
1118
|
throw new Error('Wallet is not properly authenticated or missing data for update.');
|
|
941
1119
|
}
|
|
@@ -958,7 +1136,8 @@ class CWIStyleWalletManager {
|
|
|
958
1136
|
const profilesBytes = sdk_1.Utils.toArray(profilesJson, 'utf8');
|
|
959
1137
|
profilesEncrypted = new sdk_1.SymmetricKey(rootPrimaryKey).encrypt(profilesBytes);
|
|
960
1138
|
}
|
|
961
|
-
// Construct the new UMP token data
|
|
1139
|
+
// Construct the new UMP token data (preserve or upgrade to v3 with KDF metadata)
|
|
1140
|
+
const kdfMetadata = (_a = this.currentUMPToken.passwordKdf) !== null && _a !== void 0 ? _a : this.kdfConfig;
|
|
962
1141
|
const newTokenData = {
|
|
963
1142
|
passwordSalt,
|
|
964
1143
|
passwordPresentationPrimary: presentationPassword.encrypt(rootPrimaryKey),
|
|
@@ -983,7 +1162,9 @@ class CWIStyleWalletManager {
|
|
|
983
1162
|
protocolID: [2, 'admin key wrapping'],
|
|
984
1163
|
keyID: '1'
|
|
985
1164
|
})).ciphertext,
|
|
986
|
-
profilesEncrypted // Add encrypted profiles
|
|
1165
|
+
profilesEncrypted, // Add encrypted profiles
|
|
1166
|
+
umpVersion: 3, // UMP protocol version 3
|
|
1167
|
+
passwordKdf: kdfMetadata // Preserve or upgrade KDF metadata
|
|
987
1168
|
// currentOutpoint will be set after publishing
|
|
988
1169
|
};
|
|
989
1170
|
// We need the wallet built for the DEFAULT profile to publish the UMP token.
|
|
@@ -1015,15 +1196,16 @@ class CWIStyleWalletManager {
|
|
|
1015
1196
|
}
|
|
1016
1197
|
}
|
|
1017
1198
|
/**
|
|
1018
|
-
* Serializes a UMP token to binary format (Version 2 with
|
|
1019
|
-
* Layout: [1 byte version=
|
|
1199
|
+
* Serializes a UMP token to binary format (Version 3 with KDF metadata, Version 2 with profiles).
|
|
1200
|
+
* V3 Layout: [1 byte version=3] + [11 * (varint len + bytes) for standard fields] + [1 byte profile_flag] + [IF flag=1 THEN varint len + profile bytes] + [1 byte kdf_flag] + [IF flag=1 THEN kdf metadata] + [varint len + outpoint bytes]
|
|
1020
1201
|
*/
|
|
1021
1202
|
serializeUMPToken(token) {
|
|
1022
1203
|
if (!token.currentOutpoint) {
|
|
1023
1204
|
throw new Error('Token must have outpoint for serialization');
|
|
1024
1205
|
}
|
|
1025
1206
|
const writer = new sdk_1.Utils.Writer();
|
|
1026
|
-
|
|
1207
|
+
const hasKdfMetadata = token.umpVersion === 3 && token.passwordKdf;
|
|
1208
|
+
writer.writeUInt8(hasKdfMetadata ? 3 : 2); // Version 3 for KDF, 2 for legacy
|
|
1027
1209
|
const writeArray = (arr) => {
|
|
1028
1210
|
writer.writeVarIntNum(arr.length);
|
|
1029
1211
|
writer.write(arr);
|
|
@@ -1038,7 +1220,7 @@ class CWIStyleWalletManager {
|
|
|
1038
1220
|
writeArray(token.presentationHash); // 6
|
|
1039
1221
|
writeArray(token.recoveryHash); // 7
|
|
1040
1222
|
writeArray(token.presentationKeyEncrypted); // 8
|
|
1041
|
-
writeArray(token.passwordKeyEncrypted); // 9
|
|
1223
|
+
writeArray(token.passwordKeyEncrypted); // 9
|
|
1042
1224
|
writeArray(token.recoveryKeyEncrypted); // 10
|
|
1043
1225
|
// Write optional profiles field
|
|
1044
1226
|
if (token.profilesEncrypted && token.profilesEncrypted.length > 0) {
|
|
@@ -1048,6 +1230,32 @@ class CWIStyleWalletManager {
|
|
|
1048
1230
|
else {
|
|
1049
1231
|
writer.writeUInt8(0); // Flag indicating no profiles
|
|
1050
1232
|
}
|
|
1233
|
+
// V3: Write KDF metadata
|
|
1234
|
+
if (hasKdfMetadata) {
|
|
1235
|
+
writer.writeUInt8(1); // Flag indicating KDF metadata present
|
|
1236
|
+
writer.writeUInt8(token.umpVersion); // On-chain UMP version
|
|
1237
|
+
const algorithmBytes = sdk_1.Utils.toArray(token.passwordKdf.algorithm, 'utf8');
|
|
1238
|
+
writeArray(algorithmBytes);
|
|
1239
|
+
// Serialize KDF params as JSON
|
|
1240
|
+
const kdfParams = {
|
|
1241
|
+
iterations: token.passwordKdf.iterations
|
|
1242
|
+
};
|
|
1243
|
+
if (token.passwordKdf.memoryKiB !== undefined) {
|
|
1244
|
+
kdfParams.memoryKiB = token.passwordKdf.memoryKiB;
|
|
1245
|
+
}
|
|
1246
|
+
if (token.passwordKdf.parallelism !== undefined) {
|
|
1247
|
+
kdfParams.parallelism = token.passwordKdf.parallelism;
|
|
1248
|
+
}
|
|
1249
|
+
if (token.passwordKdf.hashLength !== undefined) {
|
|
1250
|
+
kdfParams.hashLength = token.passwordKdf.hashLength;
|
|
1251
|
+
}
|
|
1252
|
+
const kdfParamsBytes = sdk_1.Utils.toArray(JSON.stringify(kdfParams), 'utf8');
|
|
1253
|
+
writeArray(kdfParamsBytes);
|
|
1254
|
+
}
|
|
1255
|
+
else if (writer.toArray()[0] === 3) {
|
|
1256
|
+
// Version 3 without KDF metadata (shouldn't happen, but handle gracefully)
|
|
1257
|
+
writer.writeUInt8(0); // Flag indicating no KDF metadata
|
|
1258
|
+
}
|
|
1051
1259
|
// Write outpoint string
|
|
1052
1260
|
const outpointBytes = sdk_1.Utils.toArray(token.currentOutpoint, 'utf8');
|
|
1053
1261
|
writer.writeVarIntNum(outpointBytes.length);
|
|
@@ -1055,19 +1263,19 @@ class CWIStyleWalletManager {
|
|
|
1055
1263
|
return writer.toArray();
|
|
1056
1264
|
}
|
|
1057
1265
|
/**
|
|
1058
|
-
* Deserializes a UMP token from binary format (Handles Version 1 and
|
|
1266
|
+
* Deserializes a UMP token from binary format (Handles Version 1, 2, and 3).
|
|
1059
1267
|
*/
|
|
1060
1268
|
deserializeUMPToken(bin) {
|
|
1061
1269
|
const reader = new sdk_1.Utils.Reader(bin);
|
|
1062
1270
|
const version = reader.readUInt8();
|
|
1063
|
-
if (version !== 1 && version !== 2) {
|
|
1271
|
+
if (version !== 1 && version !== 2 && version !== 3) {
|
|
1064
1272
|
throw new Error(`Unsupported UMP token serialization version: ${version}`);
|
|
1065
1273
|
}
|
|
1066
1274
|
const readArray = () => {
|
|
1067
1275
|
const length = reader.readVarIntNum();
|
|
1068
1276
|
return reader.read(length);
|
|
1069
1277
|
};
|
|
1070
|
-
// Read standard fields (order matches serialization
|
|
1278
|
+
// Read standard fields (order matches serialization)
|
|
1071
1279
|
const passwordSalt = readArray(); // 0
|
|
1072
1280
|
const passwordPresentationPrimary = readArray(); // 1
|
|
1073
1281
|
const passwordRecoveryPrimary = readArray(); // 2
|
|
@@ -1079,14 +1287,40 @@ class CWIStyleWalletManager {
|
|
|
1079
1287
|
const presentationKeyEncrypted = readArray(); // 8
|
|
1080
1288
|
const passwordKeyEncrypted = readArray(); // 9
|
|
1081
1289
|
const recoveryKeyEncrypted = readArray(); // 10
|
|
1082
|
-
// Read optional profiles (
|
|
1290
|
+
// Read optional profiles (V2 and V3)
|
|
1083
1291
|
let profilesEncrypted;
|
|
1084
|
-
if (version
|
|
1292
|
+
if (version >= 2) {
|
|
1085
1293
|
const profilesFlag = reader.readUInt8();
|
|
1086
1294
|
if (profilesFlag === 1) {
|
|
1087
1295
|
profilesEncrypted = readArray();
|
|
1088
1296
|
}
|
|
1089
1297
|
}
|
|
1298
|
+
// Read KDF metadata (V3 only)
|
|
1299
|
+
let umpVersion;
|
|
1300
|
+
let passwordKdf;
|
|
1301
|
+
if (version === 3) {
|
|
1302
|
+
const kdfFlag = reader.readUInt8();
|
|
1303
|
+
if (kdfFlag === 1) {
|
|
1304
|
+
umpVersion = reader.readUInt8(); // On-chain UMP version
|
|
1305
|
+
const algorithmBytes = readArray();
|
|
1306
|
+
const algorithm = sdk_1.Utils.toUTF8(algorithmBytes);
|
|
1307
|
+
const kdfParamsBytes = readArray();
|
|
1308
|
+
const kdfParamsJson = sdk_1.Utils.toUTF8(kdfParamsBytes);
|
|
1309
|
+
try {
|
|
1310
|
+
const kdfParams = JSON.parse(kdfParamsJson);
|
|
1311
|
+
passwordKdf = {
|
|
1312
|
+
algorithm,
|
|
1313
|
+
iterations: kdfParams.iterations,
|
|
1314
|
+
memoryKiB: kdfParams.memoryKiB,
|
|
1315
|
+
parallelism: kdfParams.parallelism,
|
|
1316
|
+
hashLength: kdfParams.hashLength
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
catch (e) {
|
|
1320
|
+
console.warn('Failed to parse KDF params during deserialization:', e);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1090
1324
|
// Read outpoint string
|
|
1091
1325
|
const outpointLen = reader.readVarIntNum();
|
|
1092
1326
|
const outpointBytes = reader.read(outpointLen);
|
|
@@ -1101,10 +1335,12 @@ class CWIStyleWalletManager {
|
|
|
1101
1335
|
presentationHash,
|
|
1102
1336
|
recoveryHash,
|
|
1103
1337
|
presentationKeyEncrypted,
|
|
1104
|
-
passwordKeyEncrypted,
|
|
1338
|
+
passwordKeyEncrypted,
|
|
1105
1339
|
recoveryKeyEncrypted,
|
|
1106
|
-
profilesEncrypted,
|
|
1107
|
-
currentOutpoint
|
|
1340
|
+
profilesEncrypted,
|
|
1341
|
+
currentOutpoint,
|
|
1342
|
+
umpVersion,
|
|
1343
|
+
passwordKdf
|
|
1108
1344
|
};
|
|
1109
1345
|
return token;
|
|
1110
1346
|
}
|
|
@@ -1134,9 +1370,9 @@ class CWIStyleWalletManager {
|
|
|
1134
1370
|
return tempKey;
|
|
1135
1371
|
}
|
|
1136
1372
|
// 2. Otherwise, derive from password
|
|
1137
|
-
const password = await this.passwordRetriever(reason, (passwordCandidate) => {
|
|
1373
|
+
const password = await this.passwordRetriever(reason, async (passwordCandidate) => {
|
|
1138
1374
|
try {
|
|
1139
|
-
const derivedPasswordKey =
|
|
1375
|
+
const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, sdk_1.Utils.toArray(passwordCandidate, 'utf8'));
|
|
1140
1376
|
const privilegedDecryptor = this.XOR(this.rootPrimaryKey, derivedPasswordKey);
|
|
1141
1377
|
const decryptedPrivileged = new sdk_1.SymmetricKey(privilegedDecryptor).decrypt(this.currentUMPToken.passwordPrimaryPrivileged);
|
|
1142
1378
|
return !!decryptedPrivileged; // Test passes if decryption works
|
|
@@ -1145,8 +1381,8 @@ class CWIStyleWalletManager {
|
|
|
1145
1381
|
return false;
|
|
1146
1382
|
}
|
|
1147
1383
|
});
|
|
1148
|
-
// Decrypt the root privileged key using the confirmed password
|
|
1149
|
-
const derivedPasswordKey = await
|
|
1384
|
+
// Decrypt the root privileged key using the confirmed password (with token-driven KDF)
|
|
1385
|
+
const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, sdk_1.Utils.toArray(password, 'utf8'));
|
|
1150
1386
|
const privilegedDecryptor = this.XOR(this.rootPrimaryKey, derivedPasswordKey);
|
|
1151
1387
|
const rootPrivilegedBytes = new sdk_1.SymmetricKey(privilegedDecryptor).decrypt(this.currentUMPToken.passwordPrimaryPrivileged);
|
|
1152
1388
|
return new sdk_1.PrivateKey(rootPrivilegedBytes); // Return the ROOT key object
|