@ibgib/core-gib 0.1.65 → 0.1.66
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/CHANGELOG.md +7 -0
- package/dist/keystone/keystone-service-v1.d.mts +54 -18
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +56 -21
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +744 -471
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +9 -1
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +27 -4
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +230 -135
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/keystone-service-v1.mts +112 -32
- package/src/keystone/keystone-service-v1.respec.mts +791 -556
- package/src/keystone/keystone-types.mts +9 -1
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +294 -131
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { respecfully, iReckon, firstOfAll, firstOfEach, ifWeMight } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
2
2
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
3
|
-
import { clone, hash, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
3
|
+
import { clone, hash, getUUID, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
4
4
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
5
5
|
import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
6
6
|
import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
|
|
@@ -143,6 +143,33 @@ class MockMetaspaceService {
|
|
|
143
143
|
const addr = getIbGibAddr({ ibGib });
|
|
144
144
|
this.timelineHeads.set(tjpGib, addr);
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Resolves the latest address for a timeline.
|
|
148
|
+
*/
|
|
149
|
+
async getLatestAddr({ addr, tjpAddr, space }) {
|
|
150
|
+
const targetAddr = tjpAddr || addr;
|
|
151
|
+
if (!targetAddr)
|
|
152
|
+
return undefined;
|
|
153
|
+
const targetSpace = space || this.space;
|
|
154
|
+
const gib = targetAddr.split('^')[1] || '';
|
|
155
|
+
let tjpGib = gib.includes('.') ? gib.split('.').slice(1).join('.') : gib;
|
|
156
|
+
if (this.timelineHeads.has(tjpGib)) {
|
|
157
|
+
return this.timelineHeads.get(tjpGib);
|
|
158
|
+
}
|
|
159
|
+
if (targetSpace && typeof targetSpace.witness === 'function') {
|
|
160
|
+
const res = await targetSpace.witness({
|
|
161
|
+
data: {
|
|
162
|
+
cmd: 'get',
|
|
163
|
+
cmdModifiers: ['latest', 'addrs'],
|
|
164
|
+
ibGibAddrs: [targetAddr]
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (res?.data?.success && res?.data?.latestAddrsMap?.[targetAddr]) {
|
|
168
|
+
return res.data.latestAddrsMap[targetAddr];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return targetAddr;
|
|
172
|
+
}
|
|
146
173
|
}
|
|
147
174
|
// ===========================================================================
|
|
148
175
|
// SUITE A: STRATEGY VECTORS (The Math)
|
|
@@ -1372,537 +1399,783 @@ await respecfully(sir, 'Suite G: Combined Pools & Password Rotation', async () =
|
|
|
1372
1399
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1373
1400
|
});
|
|
1374
1401
|
await ifWeMight(sir, 'revokes a keystone using a separate revocation pool', async () => {
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1402
|
+
const lc = `[revokes a keystone using a separate revocation pool]`;
|
|
1403
|
+
try {
|
|
1404
|
+
if (logalot) {
|
|
1405
|
+
console.log(`${lc} starting... (I: 8efbb3af6a483aa4c1205118bd481926)`);
|
|
1406
|
+
}
|
|
1407
|
+
const stdSalt = "std_salt";
|
|
1408
|
+
const revokeSalt = "revoke_salt";
|
|
1409
|
+
const stdConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: stdSalt });
|
|
1410
|
+
const revokeConfig = createStandardPoolConfig({ id: POOL_ID_REVOKE, salt: revokeSalt });
|
|
1411
|
+
revokeConfig.allowedVerbs = [KEYSTONE_VERB_REVOKE];
|
|
1412
|
+
revokeConfig.behavior.replenish = KeystoneReplenishStrategy.deleteAll;
|
|
1413
|
+
const keystone = await service.genesis({
|
|
1414
|
+
masterSecret: userSecret,
|
|
1415
|
+
configs: [stdConfig, revokeConfig],
|
|
1416
|
+
metaspace: mockMetaspace,
|
|
1417
|
+
space: mockSpace,
|
|
1418
|
+
});
|
|
1419
|
+
const revoked = await service.revoke({
|
|
1420
|
+
latestKeystone: keystone,
|
|
1421
|
+
masterSecret: userSecret,
|
|
1422
|
+
metaspace: mockMetaspace,
|
|
1423
|
+
space: mockSpace,
|
|
1424
|
+
});
|
|
1425
|
+
iReckon(sir, revoked.data?.revocationInfo).asTo('revocationInfo present').isGonnaBeTruthy();
|
|
1426
|
+
const revokePool = revoked.data?.challengePools.find(p => p.id === POOL_ID_REVOKE);
|
|
1427
|
+
iReckon(sir, Object.keys(revokePool.challenges).length).asTo('revoke pool depleted').willEqual(0);
|
|
1428
|
+
}
|
|
1429
|
+
catch (error) {
|
|
1430
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1431
|
+
throw error;
|
|
1432
|
+
}
|
|
1433
|
+
finally {
|
|
1434
|
+
if (logalot) {
|
|
1435
|
+
console.log(`${lc} complete.`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1396
1438
|
});
|
|
1397
1439
|
await ifWeMight(sir, 'revokes a keystone using a combined manage/revoke pool and empties it', async () => {
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1440
|
+
const lc = `[revokes a keystone using a combined manage/revoke pool and empties it]`;
|
|
1441
|
+
try {
|
|
1442
|
+
if (logalot) {
|
|
1443
|
+
console.log(`${lc} starting... (I: 31d8bc860b7cb09893ccaac3d2cf4826)`);
|
|
1444
|
+
}
|
|
1445
|
+
const salt = "combined_salt";
|
|
1446
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt });
|
|
1447
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1448
|
+
const keystone = await service.genesis({
|
|
1449
|
+
masterSecret: userSecret,
|
|
1450
|
+
configs: [manageConfig],
|
|
1451
|
+
metaspace: mockMetaspace,
|
|
1452
|
+
space: mockSpace,
|
|
1453
|
+
});
|
|
1454
|
+
const revoked = await service.revoke({
|
|
1455
|
+
latestKeystone: keystone,
|
|
1456
|
+
masterSecret: userSecret,
|
|
1457
|
+
metaspace: mockMetaspace,
|
|
1458
|
+
space: mockSpace,
|
|
1459
|
+
});
|
|
1460
|
+
iReckon(sir, revoked.data?.revocationInfo).asTo('revocationInfo present').isGonnaBeTruthy();
|
|
1461
|
+
const managePool = revoked.data?.challengePools.find(p => p.id === POOL_ID_MANAGE);
|
|
1462
|
+
iReckon(sir, Object.keys(managePool.challenges).length).asTo('combined manage pool depleted').willEqual(0);
|
|
1463
|
+
}
|
|
1464
|
+
catch (error) {
|
|
1465
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1466
|
+
throw error;
|
|
1467
|
+
}
|
|
1468
|
+
finally {
|
|
1469
|
+
if (logalot) {
|
|
1470
|
+
console.log(`${lc} complete.`);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1416
1473
|
});
|
|
1417
1474
|
await ifWeMight(sir, 'rotates user password successfully when signed by the user manage pool', async () => {
|
|
1418
|
-
const
|
|
1419
|
-
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt });
|
|
1420
|
-
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1421
|
-
const keystone = await service.genesis({
|
|
1422
|
-
masterSecret: userSecret,
|
|
1423
|
-
configs: [manageConfig],
|
|
1424
|
-
metaspace: mockMetaspace,
|
|
1425
|
-
space: mockSpace,
|
|
1426
|
-
});
|
|
1427
|
-
const newSecret = "NewUserSecret_999";
|
|
1428
|
-
const rotated = await service.changePassword({
|
|
1429
|
-
latestKeystone: keystone,
|
|
1430
|
-
newMasterSecret: newSecret,
|
|
1431
|
-
signingSecret: userSecret,
|
|
1432
|
-
signingPoolId: POOL_ID_MANAGE,
|
|
1433
|
-
metaspace: mockMetaspace,
|
|
1434
|
-
space: mockSpace,
|
|
1435
|
-
});
|
|
1436
|
-
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1437
|
-
// 1. Try to sign using the new secret (should succeed)
|
|
1438
|
-
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1439
|
-
const signedNew = await service.sign({
|
|
1440
|
-
latestKeystone: rotated,
|
|
1441
|
-
masterSecret: newSecret,
|
|
1442
|
-
claim,
|
|
1443
|
-
poolId: POOL_ID_MANAGE,
|
|
1444
|
-
metaspace: mockMetaspace,
|
|
1445
|
-
space: mockSpace,
|
|
1446
|
-
});
|
|
1447
|
-
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1448
|
-
// 2. Try to sign using the old secret (should fail)
|
|
1449
|
-
let errorCaught = false;
|
|
1475
|
+
const lc = `[rotates user password successfully when signed by the user manage pool]`;
|
|
1450
1476
|
try {
|
|
1451
|
-
|
|
1452
|
-
|
|
1477
|
+
if (logalot) {
|
|
1478
|
+
console.log(`${lc} starting... (I: 580b0bbdc7f80e35d8ee2339ac0eb526)`);
|
|
1479
|
+
}
|
|
1480
|
+
const salt = "manage_salt";
|
|
1481
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt });
|
|
1482
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1483
|
+
const keystone = await service.genesis({
|
|
1453
1484
|
masterSecret: userSecret,
|
|
1485
|
+
configs: [manageConfig],
|
|
1486
|
+
metaspace: mockMetaspace,
|
|
1487
|
+
space: mockSpace,
|
|
1488
|
+
});
|
|
1489
|
+
const newSecret = "NewUserSecret_999";
|
|
1490
|
+
const { newKeystone: rotated } = await service.changePassword({
|
|
1491
|
+
keystoneIbGib: keystone,
|
|
1492
|
+
newMasterSecret: newSecret,
|
|
1493
|
+
signingSecret: userSecret,
|
|
1494
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
1495
|
+
metaspace: mockMetaspace,
|
|
1496
|
+
space: mockSpace,
|
|
1497
|
+
});
|
|
1498
|
+
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1499
|
+
// 1. Try to sign using the new secret (should succeed)
|
|
1500
|
+
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1501
|
+
const signedNew = await service.sign({
|
|
1502
|
+
latestKeystone: rotated,
|
|
1503
|
+
masterSecret: newSecret,
|
|
1454
1504
|
claim,
|
|
1455
1505
|
poolId: POOL_ID_MANAGE,
|
|
1456
1506
|
metaspace: mockMetaspace,
|
|
1457
1507
|
space: mockSpace,
|
|
1458
1508
|
});
|
|
1509
|
+
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1510
|
+
// 2. Try to sign using the old secret (should fail)
|
|
1511
|
+
let errorCaught = false;
|
|
1512
|
+
try {
|
|
1513
|
+
await service.sign({
|
|
1514
|
+
latestKeystone: rotated,
|
|
1515
|
+
masterSecret: userSecret,
|
|
1516
|
+
claim,
|
|
1517
|
+
poolId: POOL_ID_MANAGE,
|
|
1518
|
+
metaspace: mockMetaspace,
|
|
1519
|
+
space: mockSpace,
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
catch {
|
|
1523
|
+
errorCaught = true;
|
|
1524
|
+
}
|
|
1525
|
+
iReckon(sir, errorCaught).asTo('old secret signing rejected').isGonnaBeTrue();
|
|
1459
1526
|
}
|
|
1460
|
-
catch {
|
|
1461
|
-
|
|
1527
|
+
catch (error) {
|
|
1528
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1529
|
+
throw error;
|
|
1530
|
+
}
|
|
1531
|
+
finally {
|
|
1532
|
+
if (logalot) {
|
|
1533
|
+
console.log(`${lc} complete.`);
|
|
1534
|
+
}
|
|
1462
1535
|
}
|
|
1463
|
-
iReckon(sir, errorCaught).asTo('old secret signing rejected').isGonnaBeTrue();
|
|
1464
1536
|
});
|
|
1465
1537
|
await ifWeMight(sir, 'recovers user password successfully when signed by the custodian manage pool', async () => {
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1479
|
-
const
|
|
1480
|
-
challenges
|
|
1538
|
+
const lc = `[recovers user password successfully when signed by the custodian manage pool]`;
|
|
1539
|
+
try {
|
|
1540
|
+
if (logalot) {
|
|
1541
|
+
console.log(`${lc} starting... (I: 409df8ee1ae81d8b45c4a9c98e6e7626)`);
|
|
1542
|
+
}
|
|
1543
|
+
const userSalt = "user_salt";
|
|
1544
|
+
const custodianSalt = "custodian_salt";
|
|
1545
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1546
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1547
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1548
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1549
|
+
// We simulate constructing the custodian pool "offline" by the server
|
|
1550
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1551
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1552
|
+
const challenges = {};
|
|
1553
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1554
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1555
|
+
const challengeId = raw.substring(0, 16);
|
|
1556
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1557
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1558
|
+
}
|
|
1559
|
+
const custodianPool = {
|
|
1560
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1561
|
+
config: custodianManageConfig,
|
|
1562
|
+
challenges,
|
|
1563
|
+
isForeign: true
|
|
1564
|
+
};
|
|
1565
|
+
const keystone = await service.genesis({
|
|
1566
|
+
masterSecret: userSecret,
|
|
1567
|
+
configs: [userManageConfig],
|
|
1568
|
+
metaspace: mockMetaspace,
|
|
1569
|
+
space: mockSpace,
|
|
1570
|
+
});
|
|
1571
|
+
// Add the custodian pool to the keystone
|
|
1572
|
+
const keystoneWithCustodian = await service.addPools({
|
|
1573
|
+
latestKeystone: keystone,
|
|
1574
|
+
masterSecret: userSecret,
|
|
1575
|
+
newPools: [custodianPool],
|
|
1576
|
+
metaspace: mockMetaspace,
|
|
1577
|
+
space: mockSpace,
|
|
1578
|
+
});
|
|
1579
|
+
// The user forgets their secret. Recover using custodianSecret!
|
|
1580
|
+
const recoveredSecret = "RecoveredUserSecret_777";
|
|
1581
|
+
const { newKeystone: recoveredKeystone } = await service.changePassword({
|
|
1582
|
+
keystoneIbGib: keystoneWithCustodian,
|
|
1583
|
+
newMasterSecret: recoveredSecret,
|
|
1584
|
+
signingSecret: custodianSecret,
|
|
1585
|
+
signingPoolId: POOL_ID_CUSTODIAN_MANAGE,
|
|
1586
|
+
metaspace: mockMetaspace,
|
|
1587
|
+
space: mockSpace,
|
|
1588
|
+
});
|
|
1589
|
+
iReckon(sir, recoveredKeystone).isGonnaBeTruthy();
|
|
1590
|
+
// Verify we can sign with recovered secret
|
|
1591
|
+
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1592
|
+
const signedNew = await service.sign({
|
|
1593
|
+
latestKeystone: recoveredKeystone,
|
|
1594
|
+
masterSecret: recoveredSecret,
|
|
1595
|
+
claim,
|
|
1596
|
+
poolId: POOL_ID_MANAGE,
|
|
1597
|
+
metaspace: mockMetaspace,
|
|
1598
|
+
space: mockSpace,
|
|
1599
|
+
});
|
|
1600
|
+
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1601
|
+
}
|
|
1602
|
+
catch (error) {
|
|
1603
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1604
|
+
throw error;
|
|
1605
|
+
}
|
|
1606
|
+
finally {
|
|
1607
|
+
if (logalot) {
|
|
1608
|
+
console.log(`${lc} complete.`);
|
|
1609
|
+
}
|
|
1481
1610
|
}
|
|
1482
|
-
const custodianPool = {
|
|
1483
|
-
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1484
|
-
config: custodianManageConfig,
|
|
1485
|
-
challenges,
|
|
1486
|
-
isForeign: true
|
|
1487
|
-
};
|
|
1488
|
-
const keystone = await service.genesis({
|
|
1489
|
-
masterSecret: userSecret,
|
|
1490
|
-
configs: [userManageConfig],
|
|
1491
|
-
metaspace: mockMetaspace,
|
|
1492
|
-
space: mockSpace,
|
|
1493
|
-
});
|
|
1494
|
-
// Add the custodian pool to the keystone
|
|
1495
|
-
const keystoneWithCustodian = await service.addPools({
|
|
1496
|
-
latestKeystone: keystone,
|
|
1497
|
-
masterSecret: userSecret,
|
|
1498
|
-
newPools: [custodianPool],
|
|
1499
|
-
metaspace: mockMetaspace,
|
|
1500
|
-
space: mockSpace,
|
|
1501
|
-
});
|
|
1502
|
-
// The user forgets their secret. Recover using custodianSecret!
|
|
1503
|
-
const recoveredSecret = "RecoveredUserSecret_777";
|
|
1504
|
-
const recoveredKeystone = await service.changePassword({
|
|
1505
|
-
latestKeystone: keystoneWithCustodian,
|
|
1506
|
-
newMasterSecret: recoveredSecret,
|
|
1507
|
-
signingSecret: custodianSecret,
|
|
1508
|
-
signingPoolId: POOL_ID_CUSTODIAN_MANAGE,
|
|
1509
|
-
metaspace: mockMetaspace,
|
|
1510
|
-
space: mockSpace,
|
|
1511
|
-
});
|
|
1512
|
-
iReckon(sir, recoveredKeystone).isGonnaBeTruthy();
|
|
1513
|
-
// Verify we can sign with recovered secret
|
|
1514
|
-
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1515
|
-
const signedNew = await service.sign({
|
|
1516
|
-
latestKeystone: recoveredKeystone,
|
|
1517
|
-
masterSecret: recoveredSecret,
|
|
1518
|
-
claim,
|
|
1519
|
-
poolId: POOL_ID_MANAGE,
|
|
1520
|
-
metaspace: mockMetaspace,
|
|
1521
|
-
space: mockSpace,
|
|
1522
|
-
});
|
|
1523
|
-
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1524
1611
|
});
|
|
1525
1612
|
await ifWeMight(sir, 'rotates custodian manage pool successfully', async () => {
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
const
|
|
1539
|
-
const
|
|
1540
|
-
challenges
|
|
1613
|
+
const lc = `[rotates custodian manage pool successfully]`;
|
|
1614
|
+
try {
|
|
1615
|
+
if (logalot) {
|
|
1616
|
+
console.log(`${lc} starting... (I: 76d9a76dc2487949d7deb31696d3e126)`);
|
|
1617
|
+
}
|
|
1618
|
+
const userSalt = "user_salt";
|
|
1619
|
+
const custodianSalt = "custodian_salt";
|
|
1620
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1621
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1622
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1623
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1624
|
+
// Custodian Pool genesis
|
|
1625
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1626
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1627
|
+
const challenges = {};
|
|
1628
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1629
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1630
|
+
const challengeId = raw.substring(0, 16);
|
|
1631
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1632
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1633
|
+
}
|
|
1634
|
+
const custodianPool = {
|
|
1635
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1636
|
+
config: custodianManageConfig,
|
|
1637
|
+
challenges,
|
|
1638
|
+
isForeign: true
|
|
1639
|
+
};
|
|
1640
|
+
const keystone = await service.genesis({
|
|
1641
|
+
masterSecret: userSecret,
|
|
1642
|
+
configs: [userManageConfig],
|
|
1643
|
+
metaspace: mockMetaspace,
|
|
1644
|
+
space: mockSpace,
|
|
1645
|
+
});
|
|
1646
|
+
const keystoneWithCustodian = await service.addPools({
|
|
1647
|
+
latestKeystone: keystone,
|
|
1648
|
+
masterSecret: userSecret,
|
|
1649
|
+
newPools: [custodianPool],
|
|
1650
|
+
metaspace: mockMetaspace,
|
|
1651
|
+
space: mockSpace,
|
|
1652
|
+
});
|
|
1653
|
+
// Rotate custodian credentials to new secret
|
|
1654
|
+
const newCustodianSecret = "NewCustodianSecret_888";
|
|
1655
|
+
const newCustodianConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: "new_custodian_salt" });
|
|
1656
|
+
newCustodianConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1657
|
+
const newStrategy = KeystoneStrategyFactory.create({ config: newCustodianConfig });
|
|
1658
|
+
const newPoolSecret = await newStrategy.derivePoolSecret({ masterSecret: newCustodianSecret });
|
|
1659
|
+
const newChallenges = {};
|
|
1660
|
+
for (let i = 0; i < newCustodianConfig.behavior.size; i++) {
|
|
1661
|
+
const raw = await hash({ s: `${newCustodianConfig.salt}${Date.now()}${i}` });
|
|
1662
|
+
const challengeId = raw.substring(0, 16);
|
|
1663
|
+
const solution = await newStrategy.generateSolution({ poolSecret: newPoolSecret, poolId: newCustodianConfig.id, challengeId });
|
|
1664
|
+
newChallenges[challengeId] = await newStrategy.generateChallenge({ solution });
|
|
1665
|
+
}
|
|
1666
|
+
const newCustodianPool = {
|
|
1667
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1668
|
+
config: newCustodianConfig,
|
|
1669
|
+
challenges: newChallenges,
|
|
1670
|
+
isForeign: true
|
|
1671
|
+
};
|
|
1672
|
+
// Rotate signed by user manage pool
|
|
1673
|
+
const rotated = await service.replacePool({
|
|
1674
|
+
latestKeystone: keystoneWithCustodian,
|
|
1675
|
+
newPool: newCustodianPool,
|
|
1676
|
+
signingSecret: userSecret,
|
|
1677
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
1678
|
+
metaspace: mockMetaspace,
|
|
1679
|
+
space: mockSpace,
|
|
1680
|
+
});
|
|
1681
|
+
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1682
|
+
// Verify we can sign with new custodian secret
|
|
1683
|
+
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1684
|
+
const signedNew = await service.sign({
|
|
1685
|
+
latestKeystone: rotated,
|
|
1686
|
+
masterSecret: newCustodianSecret,
|
|
1687
|
+
claim,
|
|
1688
|
+
poolId: POOL_ID_CUSTODIAN_MANAGE,
|
|
1689
|
+
metaspace: mockMetaspace,
|
|
1690
|
+
space: mockSpace,
|
|
1691
|
+
});
|
|
1692
|
+
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1541
1693
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
configs: [userManageConfig],
|
|
1551
|
-
metaspace: mockMetaspace,
|
|
1552
|
-
space: mockSpace,
|
|
1553
|
-
});
|
|
1554
|
-
const keystoneWithCustodian = await service.addPools({
|
|
1555
|
-
latestKeystone: keystone,
|
|
1556
|
-
masterSecret: userSecret,
|
|
1557
|
-
newPools: [custodianPool],
|
|
1558
|
-
metaspace: mockMetaspace,
|
|
1559
|
-
space: mockSpace,
|
|
1560
|
-
});
|
|
1561
|
-
// Rotate custodian credentials to new secret
|
|
1562
|
-
const newCustodianSecret = "NewCustodianSecret_888";
|
|
1563
|
-
const newCustodianConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: "new_custodian_salt" });
|
|
1564
|
-
newCustodianConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1565
|
-
const newStrategy = KeystoneStrategyFactory.create({ config: newCustodianConfig });
|
|
1566
|
-
const newPoolSecret = await newStrategy.derivePoolSecret({ masterSecret: newCustodianSecret });
|
|
1567
|
-
const newChallenges = {};
|
|
1568
|
-
for (let i = 0; i < newCustodianConfig.behavior.size; i++) {
|
|
1569
|
-
const raw = await hash({ s: `${newCustodianConfig.salt}${Date.now()}${i}` });
|
|
1570
|
-
const challengeId = raw.substring(0, 16);
|
|
1571
|
-
const solution = await newStrategy.generateSolution({ poolSecret: newPoolSecret, poolId: newCustodianConfig.id, challengeId });
|
|
1572
|
-
newChallenges[challengeId] = await newStrategy.generateChallenge({ solution });
|
|
1694
|
+
catch (error) {
|
|
1695
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1696
|
+
throw error;
|
|
1697
|
+
}
|
|
1698
|
+
finally {
|
|
1699
|
+
if (logalot) {
|
|
1700
|
+
console.log(`${lc} complete.`);
|
|
1701
|
+
}
|
|
1573
1702
|
}
|
|
1574
|
-
const newCustodianPool = {
|
|
1575
|
-
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1576
|
-
config: newCustodianConfig,
|
|
1577
|
-
challenges: newChallenges,
|
|
1578
|
-
isForeign: true
|
|
1579
|
-
};
|
|
1580
|
-
// Rotate signed by user manage pool
|
|
1581
|
-
const rotated = await service.replacePool({
|
|
1582
|
-
latestKeystone: keystoneWithCustodian,
|
|
1583
|
-
newPool: newCustodianPool,
|
|
1584
|
-
signingSecret: userSecret,
|
|
1585
|
-
signingPoolId: POOL_ID_MANAGE,
|
|
1586
|
-
metaspace: mockMetaspace,
|
|
1587
|
-
space: mockSpace,
|
|
1588
|
-
});
|
|
1589
|
-
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1590
|
-
// Verify we can sign with new custodian secret
|
|
1591
|
-
const claim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target" };
|
|
1592
|
-
const signedNew = await service.sign({
|
|
1593
|
-
latestKeystone: rotated,
|
|
1594
|
-
masterSecret: newCustodianSecret,
|
|
1595
|
-
claim,
|
|
1596
|
-
poolId: POOL_ID_CUSTODIAN_MANAGE,
|
|
1597
|
-
metaspace: mockMetaspace,
|
|
1598
|
-
space: mockSpace,
|
|
1599
|
-
});
|
|
1600
|
-
iReckon(sir, signedNew).isGonnaBeTruthy();
|
|
1601
1703
|
});
|
|
1602
1704
|
await ifWeMight(sir, 'rotates all non-foreign pools during changePassword by default', async () => {
|
|
1603
|
-
const
|
|
1604
|
-
const custodianSalt = "custodian_salt";
|
|
1605
|
-
const secondarySalt = "secondary_salt";
|
|
1606
|
-
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1607
|
-
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1608
|
-
// Another native/non-foreign pool
|
|
1609
|
-
const secondaryNativeConfig = createStandardPoolConfig({ id: "secondary-native", salt: secondarySalt });
|
|
1610
|
-
secondaryNativeConfig.allowedVerbs = [KEYSTONE_VERB_SYNC];
|
|
1611
|
-
// A custodian/foreign pool
|
|
1612
|
-
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1613
|
-
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1614
|
-
// Custodian Pool genesis
|
|
1615
|
-
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1616
|
-
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1617
|
-
const challenges = {};
|
|
1618
|
-
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1619
|
-
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1620
|
-
const challengeId = raw.substring(0, 16);
|
|
1621
|
-
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1622
|
-
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1623
|
-
}
|
|
1624
|
-
const custodianPool = {
|
|
1625
|
-
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1626
|
-
config: custodianManageConfig,
|
|
1627
|
-
challenges,
|
|
1628
|
-
isForeign: true
|
|
1629
|
-
};
|
|
1630
|
-
const keystone = await service.genesis({
|
|
1631
|
-
masterSecret: userSecret,
|
|
1632
|
-
configs: [userManageConfig, secondaryNativeConfig],
|
|
1633
|
-
metaspace: mockMetaspace,
|
|
1634
|
-
space: mockSpace,
|
|
1635
|
-
});
|
|
1636
|
-
const keystoneWithCustodian = await service.addPools({
|
|
1637
|
-
latestKeystone: keystone,
|
|
1638
|
-
masterSecret: userSecret,
|
|
1639
|
-
newPools: [custodianPool],
|
|
1640
|
-
metaspace: mockMetaspace,
|
|
1641
|
-
space: mockSpace,
|
|
1642
|
-
});
|
|
1643
|
-
// Rotate passphrase/password
|
|
1644
|
-
const newPassphrase = "SuperNewSecretPassphrase_999";
|
|
1645
|
-
const rotated = await service.changePassword({
|
|
1646
|
-
latestKeystone: keystoneWithCustodian,
|
|
1647
|
-
newMasterSecret: newPassphrase,
|
|
1648
|
-
signingSecret: userSecret,
|
|
1649
|
-
signingPoolId: POOL_ID_MANAGE,
|
|
1650
|
-
metaspace: mockMetaspace,
|
|
1651
|
-
space: mockSpace,
|
|
1652
|
-
});
|
|
1653
|
-
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1654
|
-
const data = rotated.data;
|
|
1655
|
-
const rotatedManage = data.challengePools.find(p => p.id === POOL_ID_MANAGE);
|
|
1656
|
-
const rotatedSecondary = data.challengePools.find(p => p.id === "secondary-native");
|
|
1657
|
-
const rotatedCustodian = data.challengePools.find(p => p.id === POOL_ID_CUSTODIAN_MANAGE);
|
|
1658
|
-
// Verify native pools rotated (salt changed)
|
|
1659
|
-
iReckon(sir, rotatedManage.config.salt).not.willEqual(userSalt);
|
|
1660
|
-
iReckon(sir, rotatedSecondary.config.salt).not.willEqual(secondarySalt);
|
|
1661
|
-
// Verify foreign pool was not modified (salt identical)
|
|
1662
|
-
iReckon(sir, rotatedCustodian.config.salt).willEqual(custodianSalt);
|
|
1663
|
-
// Verify we can validate transition
|
|
1664
|
-
const errors = await service.validate({
|
|
1665
|
-
prevIbGib: keystoneWithCustodian,
|
|
1666
|
-
currentIbGib: rotated
|
|
1667
|
-
});
|
|
1668
|
-
iReckon(sir, errors.length).willEqual(0);
|
|
1669
|
-
// Verify we can sign using the rotated 'manage' pool with the new passphrase
|
|
1670
|
-
const manageClaim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target_manage" };
|
|
1671
|
-
const signedWithNewManage = await service.sign({
|
|
1672
|
-
latestKeystone: rotated,
|
|
1673
|
-
masterSecret: newPassphrase,
|
|
1674
|
-
claim: manageClaim,
|
|
1675
|
-
poolId: POOL_ID_MANAGE,
|
|
1676
|
-
metaspace: mockMetaspace,
|
|
1677
|
-
space: mockSpace,
|
|
1678
|
-
});
|
|
1679
|
-
iReckon(sir, signedWithNewManage).isGonnaBeTruthy();
|
|
1680
|
-
// Verify we can sign using the rotated 'secondary-native' pool with the new passphrase
|
|
1681
|
-
const syncClaim = { verb: KEYSTONE_VERB_SYNC, target: "test_target_sync" };
|
|
1682
|
-
const signedWithNewSync = await service.sign({
|
|
1683
|
-
latestKeystone: rotated,
|
|
1684
|
-
masterSecret: newPassphrase,
|
|
1685
|
-
claim: syncClaim,
|
|
1686
|
-
poolId: "secondary-native",
|
|
1687
|
-
metaspace: mockMetaspace,
|
|
1688
|
-
space: mockSpace,
|
|
1689
|
-
});
|
|
1690
|
-
iReckon(sir, signedWithNewSync).isGonnaBeTruthy();
|
|
1691
|
-
// Verify signing with the OLD master secret fails on both pools
|
|
1692
|
-
let oldManageFailed = false;
|
|
1705
|
+
const lc = `[rotates all non-foreign pools during changePassword by default]`;
|
|
1693
1706
|
try {
|
|
1694
|
-
|
|
1695
|
-
|
|
1707
|
+
if (logalot) {
|
|
1708
|
+
console.log(`${lc} starting... (I: 8449682eda6389e9d514888f9ce86f26)`);
|
|
1709
|
+
}
|
|
1710
|
+
const userSalt = "user_salt";
|
|
1711
|
+
const custodianSalt = "custodian_salt";
|
|
1712
|
+
const secondarySalt = "secondary_salt";
|
|
1713
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1714
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1715
|
+
// Another native/non-foreign pool
|
|
1716
|
+
const secondaryNativeConfig = createStandardPoolConfig({ id: "secondary-native", salt: secondarySalt });
|
|
1717
|
+
secondaryNativeConfig.allowedVerbs = [KEYSTONE_VERB_SYNC];
|
|
1718
|
+
// A custodian/foreign pool
|
|
1719
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1720
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1721
|
+
// Custodian Pool genesis
|
|
1722
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1723
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1724
|
+
const challenges = {};
|
|
1725
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1726
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1727
|
+
const challengeId = raw.substring(0, 16);
|
|
1728
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1729
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1730
|
+
}
|
|
1731
|
+
const custodianPool = {
|
|
1732
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1733
|
+
config: custodianManageConfig,
|
|
1734
|
+
challenges,
|
|
1735
|
+
isForeign: true
|
|
1736
|
+
};
|
|
1737
|
+
const keystone = await service.genesis({
|
|
1696
1738
|
masterSecret: userSecret,
|
|
1739
|
+
configs: [userManageConfig, secondaryNativeConfig],
|
|
1740
|
+
metaspace: mockMetaspace,
|
|
1741
|
+
space: mockSpace,
|
|
1742
|
+
});
|
|
1743
|
+
const keystoneWithCustodian = await service.addPools({
|
|
1744
|
+
latestKeystone: keystone,
|
|
1745
|
+
masterSecret: userSecret,
|
|
1746
|
+
newPools: [custodianPool],
|
|
1747
|
+
metaspace: mockMetaspace,
|
|
1748
|
+
space: mockSpace,
|
|
1749
|
+
});
|
|
1750
|
+
// Rotate passphrase/password
|
|
1751
|
+
const newPassphrase = "SuperNewSecretPassphrase_999";
|
|
1752
|
+
const { newKeystone: rotated } = await service.changePassword({
|
|
1753
|
+
keystoneIbGib: keystoneWithCustodian,
|
|
1754
|
+
newMasterSecret: newPassphrase,
|
|
1755
|
+
signingSecret: userSecret,
|
|
1756
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
1757
|
+
metaspace: mockMetaspace,
|
|
1758
|
+
space: mockSpace,
|
|
1759
|
+
});
|
|
1760
|
+
iReckon(sir, rotated).isGonnaBeTruthy();
|
|
1761
|
+
const data = rotated.data;
|
|
1762
|
+
const rotatedManage = data.challengePools.find(p => p.id === POOL_ID_MANAGE);
|
|
1763
|
+
const rotatedSecondary = data.challengePools.find(p => p.id === "secondary-native");
|
|
1764
|
+
const rotatedCustodian = data.challengePools.find(p => p.id === POOL_ID_CUSTODIAN_MANAGE);
|
|
1765
|
+
// Verify native pools rotated (salt changed)
|
|
1766
|
+
iReckon(sir, rotatedManage.config.salt).not.willEqual(userSalt);
|
|
1767
|
+
iReckon(sir, rotatedSecondary.config.salt).not.willEqual(secondarySalt);
|
|
1768
|
+
// Verify foreign pool was not modified (salt identical)
|
|
1769
|
+
iReckon(sir, rotatedCustodian.config.salt).willEqual(custodianSalt);
|
|
1770
|
+
// Verify we can validate transition
|
|
1771
|
+
const errors = await service.validate({
|
|
1772
|
+
prevIbGib: keystoneWithCustodian,
|
|
1773
|
+
currentIbGib: rotated
|
|
1774
|
+
});
|
|
1775
|
+
iReckon(sir, errors.length).willEqual(0);
|
|
1776
|
+
// Verify we can sign using the rotated 'manage' pool with the new passphrase
|
|
1777
|
+
const manageClaim = { verb: KEYSTONE_VERB_MANAGE, target: "test_target_manage" };
|
|
1778
|
+
const signedWithNewManage = await service.sign({
|
|
1779
|
+
latestKeystone: rotated,
|
|
1780
|
+
masterSecret: newPassphrase,
|
|
1697
1781
|
claim: manageClaim,
|
|
1698
1782
|
poolId: POOL_ID_MANAGE,
|
|
1699
1783
|
metaspace: mockMetaspace,
|
|
1700
1784
|
space: mockSpace,
|
|
1701
1785
|
});
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
iReckon(sir, oldManageFailed).isGonnaBeTrue();
|
|
1707
|
-
let oldSyncFailed = false;
|
|
1708
|
-
try {
|
|
1709
|
-
await service.sign({
|
|
1786
|
+
iReckon(sir, signedWithNewManage).isGonnaBeTruthy();
|
|
1787
|
+
// Verify we can sign using the rotated 'secondary-native' pool with the new passphrase
|
|
1788
|
+
const syncClaim = { verb: KEYSTONE_VERB_SYNC, target: "test_target_sync" };
|
|
1789
|
+
const signedWithNewSync = await service.sign({
|
|
1710
1790
|
latestKeystone: rotated,
|
|
1711
|
-
masterSecret:
|
|
1791
|
+
masterSecret: newPassphrase,
|
|
1712
1792
|
claim: syncClaim,
|
|
1713
1793
|
poolId: "secondary-native",
|
|
1714
1794
|
metaspace: mockMetaspace,
|
|
1715
1795
|
space: mockSpace,
|
|
1716
1796
|
});
|
|
1797
|
+
iReckon(sir, signedWithNewSync).isGonnaBeTruthy();
|
|
1798
|
+
// Verify signing with the OLD master secret fails on both pools
|
|
1799
|
+
let oldManageFailed = false;
|
|
1800
|
+
try {
|
|
1801
|
+
await service.sign({
|
|
1802
|
+
latestKeystone: rotated,
|
|
1803
|
+
masterSecret: userSecret,
|
|
1804
|
+
claim: manageClaim,
|
|
1805
|
+
poolId: POOL_ID_MANAGE,
|
|
1806
|
+
metaspace: mockMetaspace,
|
|
1807
|
+
space: mockSpace,
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
catch {
|
|
1811
|
+
oldManageFailed = true;
|
|
1812
|
+
}
|
|
1813
|
+
iReckon(sir, oldManageFailed).isGonnaBeTrue();
|
|
1814
|
+
let oldSyncFailed = false;
|
|
1815
|
+
try {
|
|
1816
|
+
await service.sign({
|
|
1817
|
+
latestKeystone: rotated,
|
|
1818
|
+
masterSecret: userSecret,
|
|
1819
|
+
claim: syncClaim,
|
|
1820
|
+
poolId: "secondary-native",
|
|
1821
|
+
metaspace: mockMetaspace,
|
|
1822
|
+
space: mockSpace,
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
catch {
|
|
1826
|
+
oldSyncFailed = true;
|
|
1827
|
+
}
|
|
1828
|
+
iReckon(sir, oldSyncFailed).isGonnaBeTrue();
|
|
1717
1829
|
}
|
|
1718
|
-
catch {
|
|
1719
|
-
|
|
1830
|
+
catch (error) {
|
|
1831
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1832
|
+
throw error;
|
|
1833
|
+
}
|
|
1834
|
+
finally {
|
|
1835
|
+
if (logalot) {
|
|
1836
|
+
console.log(`${lc} complete.`);
|
|
1837
|
+
}
|
|
1720
1838
|
}
|
|
1721
|
-
iReckon(sir, oldSyncFailed).isGonnaBeTrue();
|
|
1722
1839
|
});
|
|
1723
1840
|
await ifWeMight(sir, 'fails validation if password rotation details or constraints are violated', async () => {
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
const
|
|
1736
|
-
const
|
|
1737
|
-
challenges
|
|
1841
|
+
const lc = `[fails validation if password rotation details or constraints are violated]`;
|
|
1842
|
+
try {
|
|
1843
|
+
if (logalot) {
|
|
1844
|
+
console.log(`${lc} starting... (I: b497f8486c58b25922d28b5efbd24826)`);
|
|
1845
|
+
}
|
|
1846
|
+
// Construct invalid change-password transition
|
|
1847
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: "salt1" });
|
|
1848
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1849
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: "salt2" });
|
|
1850
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1851
|
+
// Custodian Pool genesis
|
|
1852
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1853
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1854
|
+
const challenges = {};
|
|
1855
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1856
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1857
|
+
const challengeId = raw.substring(0, 16);
|
|
1858
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1859
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1860
|
+
}
|
|
1861
|
+
const custodianPool = {
|
|
1862
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1863
|
+
config: custodianManageConfig,
|
|
1864
|
+
challenges,
|
|
1865
|
+
isForeign: true
|
|
1866
|
+
};
|
|
1867
|
+
const keystone = await service.genesis({
|
|
1868
|
+
masterSecret: userSecret,
|
|
1869
|
+
configs: [userManageConfig],
|
|
1870
|
+
metaspace: mockMetaspace,
|
|
1871
|
+
space: mockSpace,
|
|
1872
|
+
});
|
|
1873
|
+
const keystoneWithCustodian = await service.addPools({
|
|
1874
|
+
latestKeystone: keystone,
|
|
1875
|
+
masterSecret: userSecret,
|
|
1876
|
+
newPools: [custodianPool],
|
|
1877
|
+
metaspace: mockMetaspace,
|
|
1878
|
+
space: mockSpace,
|
|
1879
|
+
});
|
|
1880
|
+
// Create an invalid frame manually where a foreign pool is rotated during change-password
|
|
1881
|
+
const badRotatedPool = await rotatePoolChallenges({
|
|
1882
|
+
prevPool: custodianPool,
|
|
1883
|
+
newMasterSecret: "bad_secret"
|
|
1884
|
+
});
|
|
1885
|
+
const { newKeystone: evolved } = await service.changePassword({
|
|
1886
|
+
keystoneIbGib: keystoneWithCustodian,
|
|
1887
|
+
newMasterSecret: "new_pass",
|
|
1888
|
+
signingSecret: userSecret,
|
|
1889
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
1890
|
+
metaspace: mockMetaspace,
|
|
1891
|
+
space: mockSpace,
|
|
1892
|
+
});
|
|
1893
|
+
// Inject the foreign pool rotation manually to simulate an attacker
|
|
1894
|
+
const attackerData = {
|
|
1895
|
+
...evolved.data,
|
|
1896
|
+
challengePools: evolved.data.challengePools.map(p => p.id === POOL_ID_CUSTODIAN_MANAGE ? badRotatedPool : p)
|
|
1897
|
+
};
|
|
1898
|
+
// Update details claim to say we rotated custodian-manage
|
|
1899
|
+
attackerData.proofs[0].claim.details = {
|
|
1900
|
+
type: KeystoneClaimType.change_password,
|
|
1901
|
+
info: { change: [POOL_ID_MANAGE, POOL_ID_CUSTODIAN_MANAGE] }
|
|
1902
|
+
};
|
|
1903
|
+
const attackerKeystone = {
|
|
1904
|
+
...evolved,
|
|
1905
|
+
data: attackerData
|
|
1906
|
+
};
|
|
1907
|
+
const errors = await service.validate({
|
|
1908
|
+
prevIbGib: keystoneWithCustodian,
|
|
1909
|
+
currentIbGib: attackerKeystone
|
|
1910
|
+
});
|
|
1911
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1912
|
+
const hasForeignViolation = errors.some(e => e.includes("Security Violation") && e.includes("foreign pool"));
|
|
1913
|
+
iReckon(sir, hasForeignViolation).isGonnaBeTrue();
|
|
1914
|
+
}
|
|
1915
|
+
catch (error) {
|
|
1916
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1917
|
+
throw error;
|
|
1918
|
+
}
|
|
1919
|
+
finally {
|
|
1920
|
+
if (logalot) {
|
|
1921
|
+
console.log(`${lc} complete.`);
|
|
1922
|
+
}
|
|
1738
1923
|
}
|
|
1739
|
-
const custodianPool = {
|
|
1740
|
-
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1741
|
-
config: custodianManageConfig,
|
|
1742
|
-
challenges,
|
|
1743
|
-
isForeign: true
|
|
1744
|
-
};
|
|
1745
|
-
const keystone = await service.genesis({
|
|
1746
|
-
masterSecret: userSecret,
|
|
1747
|
-
configs: [userManageConfig],
|
|
1748
|
-
metaspace: mockMetaspace,
|
|
1749
|
-
space: mockSpace,
|
|
1750
|
-
});
|
|
1751
|
-
const keystoneWithCustodian = await service.addPools({
|
|
1752
|
-
latestKeystone: keystone,
|
|
1753
|
-
masterSecret: userSecret,
|
|
1754
|
-
newPools: [custodianPool],
|
|
1755
|
-
metaspace: mockMetaspace,
|
|
1756
|
-
space: mockSpace,
|
|
1757
|
-
});
|
|
1758
|
-
// Create an invalid frame manually where a foreign pool is rotated during change-password
|
|
1759
|
-
const badRotatedPool = await rotatePoolChallenges({
|
|
1760
|
-
prevPool: custodianPool,
|
|
1761
|
-
newMasterSecret: "bad_secret"
|
|
1762
|
-
});
|
|
1763
|
-
const evolved = await service.changePassword({
|
|
1764
|
-
latestKeystone: keystoneWithCustodian,
|
|
1765
|
-
newMasterSecret: "new_pass",
|
|
1766
|
-
signingSecret: userSecret,
|
|
1767
|
-
signingPoolId: POOL_ID_MANAGE,
|
|
1768
|
-
metaspace: mockMetaspace,
|
|
1769
|
-
space: mockSpace,
|
|
1770
|
-
});
|
|
1771
|
-
// Inject the foreign pool rotation manually to simulate an attacker
|
|
1772
|
-
const attackerData = {
|
|
1773
|
-
...evolved.data,
|
|
1774
|
-
challengePools: evolved.data.challengePools.map(p => p.id === POOL_ID_CUSTODIAN_MANAGE ? badRotatedPool : p)
|
|
1775
|
-
};
|
|
1776
|
-
// Update details claim to say we rotated custodian-manage
|
|
1777
|
-
attackerData.proofs[0].claim.details = {
|
|
1778
|
-
type: KeystoneClaimType.change_password,
|
|
1779
|
-
info: { change: [POOL_ID_MANAGE, POOL_ID_CUSTODIAN_MANAGE] }
|
|
1780
|
-
};
|
|
1781
|
-
const attackerKeystone = {
|
|
1782
|
-
...evolved,
|
|
1783
|
-
data: attackerData
|
|
1784
|
-
};
|
|
1785
|
-
const errors = await service.validate({
|
|
1786
|
-
prevIbGib: keystoneWithCustodian,
|
|
1787
|
-
currentIbGib: attackerKeystone
|
|
1788
|
-
});
|
|
1789
|
-
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1790
|
-
const hasForeignViolation = errors.some(e => e.includes("Security Violation") && e.includes("foreign pool"));
|
|
1791
|
-
iReckon(sir, hasForeignViolation).isGonnaBeTrue();
|
|
1792
1924
|
});
|
|
1793
1925
|
await ifWeMight(sir, 'verifies signing secrets correctly for a pool', async () => {
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1926
|
+
const lc = `[verifies signing secrets correctly for a pool]`;
|
|
1927
|
+
try {
|
|
1928
|
+
if (logalot) {
|
|
1929
|
+
console.log(`${lc} starting... (I: 343eb84f5768ff0adcf2308c68404826)`);
|
|
1930
|
+
}
|
|
1931
|
+
const userSecret = "alice_secret_123";
|
|
1932
|
+
const builder = KeystoneProfileBuilder.buildKeystone('low')
|
|
1933
|
+
.withUsername('alice')
|
|
1934
|
+
.withEmail('alice@example.com')
|
|
1935
|
+
.withDescription('Alice test keystone');
|
|
1936
|
+
const configs = await builder.compileConfigs();
|
|
1937
|
+
const keystone = await service.genesis({
|
|
1938
|
+
masterSecret: userSecret,
|
|
1939
|
+
configs,
|
|
1940
|
+
frameDetails: builder.getFrameDetails(),
|
|
1941
|
+
metaspace: mockMetaspace,
|
|
1942
|
+
space: mockSpace,
|
|
1943
|
+
});
|
|
1944
|
+
// 1. Correct secret on manage pool
|
|
1945
|
+
const isCorrectManage = await service.verifySigningSecret({
|
|
1946
|
+
keystoneIbGib: keystone,
|
|
1947
|
+
signingSecret: userSecret,
|
|
1948
|
+
poolId: POOL_ID_MANAGE
|
|
1949
|
+
});
|
|
1950
|
+
iReckon(sir, isCorrectManage).asTo('correct manage secret').isGonnaBeTrue();
|
|
1951
|
+
// 2. Incorrect secret on manage pool
|
|
1952
|
+
const isWrongManage = await service.verifySigningSecret({
|
|
1953
|
+
keystoneIbGib: keystone,
|
|
1954
|
+
signingSecret: "wrong_secret",
|
|
1955
|
+
poolId: POOL_ID_MANAGE
|
|
1956
|
+
});
|
|
1957
|
+
iReckon(sir, isWrongManage).asTo('incorrect manage secret').isGonnaBeFalse();
|
|
1958
|
+
// 3. Correct secret on default/sign pool
|
|
1959
|
+
const isCorrectDefault = await service.verifySigningSecret({
|
|
1960
|
+
keystoneIbGib: keystone,
|
|
1961
|
+
signingSecret: userSecret,
|
|
1962
|
+
poolId: 'default'
|
|
1963
|
+
});
|
|
1964
|
+
iReckon(sir, isCorrectDefault).asTo('correct default secret').isGonnaBeTrue();
|
|
1965
|
+
// 4. Invalid/non-existent pool ID
|
|
1966
|
+
const isInvalidPool = await service.verifySigningSecret({
|
|
1967
|
+
keystoneIbGib: keystone,
|
|
1968
|
+
signingSecret: userSecret,
|
|
1969
|
+
poolId: 'invalid_pool_id_123'
|
|
1970
|
+
});
|
|
1971
|
+
iReckon(sir, isInvalidPool).asTo('invalid pool id').isGonnaBeFalse();
|
|
1972
|
+
}
|
|
1973
|
+
catch (error) {
|
|
1974
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1975
|
+
throw error;
|
|
1976
|
+
}
|
|
1977
|
+
finally {
|
|
1978
|
+
if (logalot) {
|
|
1979
|
+
console.log(`${lc} complete.`);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1835
1982
|
});
|
|
1836
1983
|
await ifWeMight(sir, 'removes pools successfully, validating transitions and auth', async () => {
|
|
1837
|
-
const
|
|
1838
|
-
const customPoolId = "custodian-manage-google";
|
|
1839
|
-
const configs = [
|
|
1840
|
-
createStandardPoolConfig({
|
|
1841
|
-
id: POOL_ID_MANAGE,
|
|
1842
|
-
salt: "manage_salt",
|
|
1843
|
-
}),
|
|
1844
|
-
createStandardPoolConfig({
|
|
1845
|
-
id: customPoolId,
|
|
1846
|
-
salt: "custodian_salt",
|
|
1847
|
-
})
|
|
1848
|
-
];
|
|
1849
|
-
const keystone = await service.genesis({
|
|
1850
|
-
masterSecret: userSecret,
|
|
1851
|
-
configs,
|
|
1852
|
-
metaspace: mockMetaspace,
|
|
1853
|
-
space: mockSpace,
|
|
1854
|
-
});
|
|
1855
|
-
// 1. Success path: remove customPoolId using correct secret
|
|
1856
|
-
const evolvedKeystone = await service.removePools({
|
|
1857
|
-
latestKeystone: keystone,
|
|
1858
|
-
masterSecret: userSecret,
|
|
1859
|
-
poolIds: [customPoolId],
|
|
1860
|
-
metaspace: mockMetaspace,
|
|
1861
|
-
space: mockSpace,
|
|
1862
|
-
});
|
|
1863
|
-
iReckon(sir, evolvedKeystone).asTo('evolved keystone exists').isGonnaBeTruthy();
|
|
1864
|
-
iReckon(sir, evolvedKeystone.data?.challengePools.length).asTo('pool count reduced').willEqual(1);
|
|
1865
|
-
iReckon(sir, evolvedKeystone.data?.challengePools[0].id).asTo('manage pool remains').willEqual(POOL_ID_MANAGE);
|
|
1866
|
-
// Verify transition is fully valid
|
|
1867
|
-
const errors = await service.validate({
|
|
1868
|
-
prevIbGib: keystone,
|
|
1869
|
-
currentIbGib: evolvedKeystone
|
|
1870
|
-
});
|
|
1871
|
-
iReckon(sir, errors.length).asTo('transition validation succeeds').willEqual(0);
|
|
1872
|
-
// 2. Sad path: invalid master secret
|
|
1873
|
-
let cryptoErrorCaught = false;
|
|
1984
|
+
const lc = `[removes pools successfully, validating transitions and auth]`;
|
|
1874
1985
|
try {
|
|
1875
|
-
|
|
1986
|
+
if (logalot) {
|
|
1987
|
+
console.log(`${lc} starting... (I: 2e2e747d6808b05c1e452a680d94b726)`);
|
|
1988
|
+
}
|
|
1989
|
+
const userSecret = "alice_secret_123";
|
|
1990
|
+
const customPoolId = "custodian-manage-google";
|
|
1991
|
+
const configs = [
|
|
1992
|
+
createStandardPoolConfig({
|
|
1993
|
+
id: POOL_ID_MANAGE,
|
|
1994
|
+
salt: "manage_salt",
|
|
1995
|
+
}),
|
|
1996
|
+
createStandardPoolConfig({
|
|
1997
|
+
id: customPoolId,
|
|
1998
|
+
salt: "custodian_salt",
|
|
1999
|
+
})
|
|
2000
|
+
];
|
|
2001
|
+
const keystone = await service.genesis({
|
|
2002
|
+
masterSecret: userSecret,
|
|
2003
|
+
configs,
|
|
2004
|
+
metaspace: mockMetaspace,
|
|
2005
|
+
space: mockSpace,
|
|
2006
|
+
});
|
|
2007
|
+
// 1. Success path: remove customPoolId using correct secret
|
|
2008
|
+
const evolvedKeystone = await service.removePools({
|
|
1876
2009
|
latestKeystone: keystone,
|
|
1877
|
-
masterSecret:
|
|
2010
|
+
masterSecret: userSecret,
|
|
1878
2011
|
poolIds: [customPoolId],
|
|
1879
2012
|
metaspace: mockMetaspace,
|
|
1880
2013
|
space: mockSpace,
|
|
1881
2014
|
});
|
|
2015
|
+
iReckon(sir, evolvedKeystone).asTo('evolved keystone exists').isGonnaBeTruthy();
|
|
2016
|
+
iReckon(sir, evolvedKeystone.data?.challengePools.length).asTo('pool count reduced').willEqual(1);
|
|
2017
|
+
iReckon(sir, evolvedKeystone.data?.challengePools[0].id).asTo('manage pool remains').willEqual(POOL_ID_MANAGE);
|
|
2018
|
+
// Verify transition is fully valid
|
|
2019
|
+
const errors = await service.validate({
|
|
2020
|
+
prevIbGib: keystone,
|
|
2021
|
+
currentIbGib: evolvedKeystone
|
|
2022
|
+
});
|
|
2023
|
+
iReckon(sir, errors.length).asTo('transition validation succeeds').willEqual(0);
|
|
2024
|
+
// 2. Sad path: invalid master secret
|
|
2025
|
+
let cryptoErrorCaught = false;
|
|
2026
|
+
try {
|
|
2027
|
+
await service.removePools({
|
|
2028
|
+
latestKeystone: keystone,
|
|
2029
|
+
masterSecret: "wrong_secret",
|
|
2030
|
+
poolIds: [customPoolId],
|
|
2031
|
+
metaspace: mockMetaspace,
|
|
2032
|
+
space: mockSpace,
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
catch (e) {
|
|
2036
|
+
if (e.message.includes('Crypto Violation')) {
|
|
2037
|
+
cryptoErrorCaught = true;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
iReckon(sir, cryptoErrorCaught).asTo('forgery check prevents removal').isGonnaBeTrue();
|
|
2041
|
+
// 3. Sad path: non-existent pool ID
|
|
2042
|
+
let notFoundErrorCaught = false;
|
|
2043
|
+
try {
|
|
2044
|
+
await service.removePools({
|
|
2045
|
+
latestKeystone: keystone,
|
|
2046
|
+
masterSecret: userSecret,
|
|
2047
|
+
poolIds: ["non-existent-pool"],
|
|
2048
|
+
metaspace: mockMetaspace,
|
|
2049
|
+
space: mockSpace,
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
catch (e) {
|
|
2053
|
+
if (e.message.includes('not found in keystone')) {
|
|
2054
|
+
notFoundErrorCaught = true;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
iReckon(sir, notFoundErrorCaught).asTo('non-existent pool ID rejected').isGonnaBeTrue();
|
|
1882
2058
|
}
|
|
1883
|
-
catch (
|
|
1884
|
-
|
|
1885
|
-
|
|
2059
|
+
catch (error) {
|
|
2060
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
2061
|
+
throw error;
|
|
2062
|
+
}
|
|
2063
|
+
finally {
|
|
2064
|
+
if (logalot) {
|
|
2065
|
+
console.log(`${lc} complete.`);
|
|
1886
2066
|
}
|
|
1887
2067
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2068
|
+
});
|
|
2069
|
+
await ifWeMight(sir, 'rotates registered delegate keystone challenge pools when changing user password', async () => {
|
|
2070
|
+
const lc = `[rotates registered delegate keystone challenge pools when changing user password]`;
|
|
1891
2071
|
try {
|
|
1892
|
-
|
|
1893
|
-
|
|
2072
|
+
if (logalot) {
|
|
2073
|
+
console.log(`${lc} starting...`);
|
|
2074
|
+
}
|
|
2075
|
+
const userSecret = "alice_initial_pass_123";
|
|
2076
|
+
const newSecret = "alice_new_pass_456";
|
|
2077
|
+
// 1. Create primary keystone
|
|
2078
|
+
const parentConfigs = [
|
|
2079
|
+
createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: "parent_manage_salt" }),
|
|
2080
|
+
createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: "parent_default_salt" })
|
|
2081
|
+
];
|
|
2082
|
+
const parentKeystone = await service.genesis({
|
|
1894
2083
|
masterSecret: userSecret,
|
|
1895
|
-
|
|
2084
|
+
configs: parentConfigs,
|
|
1896
2085
|
metaspace: mockMetaspace,
|
|
1897
2086
|
space: mockSpace,
|
|
1898
2087
|
});
|
|
2088
|
+
// 2. Create delegate keystone using derived delegateSecret from userSecret (matching real app creation behavior)
|
|
2089
|
+
const delegateSecret = await deriveDelegateSecret({ masterSecret: userSecret });
|
|
2090
|
+
const delegateConfigs = [
|
|
2091
|
+
createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: "delegate_manage_salt" }),
|
|
2092
|
+
createStandardPoolConfig({ id: "delegate_sync_pool", salt: "delegate_sync_salt" })
|
|
2093
|
+
];
|
|
2094
|
+
const delegateKeystone = await service.genesis({
|
|
2095
|
+
masterSecret: delegateSecret,
|
|
2096
|
+
configs: delegateConfigs,
|
|
2097
|
+
metaspace: mockMetaspace,
|
|
2098
|
+
space: mockSpace,
|
|
2099
|
+
});
|
|
2100
|
+
const delegateTjp = getTjpAddr({ ibGib: delegateKeystone });
|
|
2101
|
+
if (!delegateTjp) {
|
|
2102
|
+
throw new Error('delegateTjp missing');
|
|
2103
|
+
}
|
|
2104
|
+
// 3. Register delegate under primary keystone
|
|
2105
|
+
const registeredParent = await service.registerDelegate({
|
|
2106
|
+
parentKeystone,
|
|
2107
|
+
delegateKeystone,
|
|
2108
|
+
masterSecret: userSecret,
|
|
2109
|
+
metaspace: mockMetaspace,
|
|
2110
|
+
space: mockSpace,
|
|
2111
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
2112
|
+
});
|
|
2113
|
+
// 4. Change user password on parent keystone.
|
|
2114
|
+
// Note: We explicitly set `poolIds: undefined` to reinforce that passing undefined
|
|
2115
|
+
// instructs changePassword to rotate all non-foreign pools on the primary keystone
|
|
2116
|
+
// AND all registered outward delegates. (In production caller code, poolIds parameter can simply be omitted).
|
|
2117
|
+
const { newKeystone: evolvedParent, newDelegates } = await service.changePassword({
|
|
2118
|
+
keystoneIbGib: registeredParent,
|
|
2119
|
+
newMasterSecret: newSecret,
|
|
2120
|
+
signingSecret: userSecret,
|
|
2121
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
2122
|
+
poolIds: undefined,
|
|
2123
|
+
metaspace: mockMetaspace,
|
|
2124
|
+
space: mockSpace,
|
|
2125
|
+
});
|
|
2126
|
+
// 5. Retrieve latest delegate keystone from metaspace/space
|
|
2127
|
+
if (newDelegates && newDelegates.length === 1) {
|
|
2128
|
+
const latestDelegate = newDelegates[0];
|
|
2129
|
+
// 6. Verify primary keystone challenge pools have rotated to new secret
|
|
2130
|
+
for (const pool of evolvedParent.data.challengePools) {
|
|
2131
|
+
if (!pool.isForeign) {
|
|
2132
|
+
const oldValid = await service.verifySigningSecret({
|
|
2133
|
+
keystoneIbGib: evolvedParent,
|
|
2134
|
+
signingSecret: userSecret,
|
|
2135
|
+
poolId: pool.id
|
|
2136
|
+
});
|
|
2137
|
+
iReckon(sir, oldValid).asTo(`parent pool ${pool.id} old secret invalid`).isGonnaBeFalse();
|
|
2138
|
+
const newValid = await service.verifySigningSecret({
|
|
2139
|
+
keystoneIbGib: evolvedParent,
|
|
2140
|
+
signingSecret: newSecret,
|
|
2141
|
+
poolId: pool.id
|
|
2142
|
+
});
|
|
2143
|
+
iReckon(sir, newValid).asTo(`parent pool ${pool.id} new secret valid`).isGonnaBeTrue();
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
// 7. Verify delegate keystone challenge pools have rotated to new secret
|
|
2147
|
+
const oldDelegateSecret = await deriveDelegateSecret({ masterSecret: userSecret });
|
|
2148
|
+
const newDelegateSecret = await deriveDelegateSecret({ masterSecret: newSecret });
|
|
2149
|
+
for (const pool of latestDelegate.data.challengePools) {
|
|
2150
|
+
if (!pool.isForeign) {
|
|
2151
|
+
const oldValid = await service.verifySigningSecret({
|
|
2152
|
+
keystoneIbGib: latestDelegate,
|
|
2153
|
+
signingSecret: oldDelegateSecret,
|
|
2154
|
+
poolId: pool.id
|
|
2155
|
+
});
|
|
2156
|
+
iReckon(sir, oldValid).asTo(`delegate pool ${pool.id} old secret invalid`).isGonnaBeFalse();
|
|
2157
|
+
const newValid = await service.verifySigningSecret({
|
|
2158
|
+
keystoneIbGib: latestDelegate,
|
|
2159
|
+
signingSecret: newDelegateSecret,
|
|
2160
|
+
poolId: pool.id
|
|
2161
|
+
});
|
|
2162
|
+
iReckon(sir, newValid).asTo(`delegate pool ${pool.id} new secret valid`).isGonnaBeTrue();
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
else {
|
|
2167
|
+
iReckon(sir, true).asTo('newDelegates should have had length 1 but is empty/falsy').isGonnaBeFalse();
|
|
2168
|
+
}
|
|
1899
2169
|
}
|
|
1900
|
-
catch (
|
|
1901
|
-
|
|
1902
|
-
|
|
2170
|
+
catch (error) {
|
|
2171
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
2172
|
+
throw error;
|
|
2173
|
+
}
|
|
2174
|
+
finally {
|
|
2175
|
+
if (logalot) {
|
|
2176
|
+
console.log(`${lc} complete.`);
|
|
1903
2177
|
}
|
|
1904
2178
|
}
|
|
1905
|
-
iReckon(sir, notFoundErrorCaught).asTo('non-existent pool ID rejected').isGonnaBeTrue();
|
|
1906
2179
|
});
|
|
1907
2180
|
});
|
|
1908
2181
|
//# sourceMappingURL=keystone-service-v1.respec.mjs.map
|