@ibgib/core-gib 0.1.62 → 0.1.64
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/dist/keystone/keystone-constants.d.mts +1 -0
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +1 -0
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +19 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +191 -16
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +84 -2
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +324 -28
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +447 -2
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +31 -2
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/keystone-types.mjs +13 -0
- package/dist/keystone/keystone-types.mjs.map +1 -1
- package/dist/keystone/policy/keystone-profile-builder.d.mts +1 -1
- package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -1
- package/dist/keystone/policy/keystone-profile-builder.mjs +8 -0
- package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -1
- package/dist/keystone/policy/profiles/profile-domain.json +69 -0
- package/dist/keystone/policy/profiles/profile-sync.json +84 -0
- package/dist/keystone/policy/schemas/keystone.high.schema.json +1 -1
- package/dist/keystone/policy/schemas/keystone.medium.schema.json +1 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts +8 -0
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts.map +1 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs +15 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs.map +1 -1
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +16 -4
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +4 -1
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +4 -1
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +10 -4
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs +12 -3
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +4 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +4 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- 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 +90 -22
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +7 -10
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +14 -5
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-types.d.mts +5 -0
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/sync/sync-withid.connect.respec.mjs +4 -1
- package/dist/sync/sync-withid.connect.respec.mjs.map +1 -1
- package/dist/sync/sync-withid.establish.respec.mjs +4 -1
- package/dist/sync/sync-withid.establish.respec.mjs.map +1 -1
- package/dist/sync/sync-withid.pingpong.respec.mjs +4 -1
- package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
- package/package.json +2 -2
- package/src/keystone/keystone-constants.mts +1 -0
- package/src/keystone/keystone-helpers.mts +223 -15
- package/src/keystone/keystone-service-v1.mts +391 -28
- package/src/keystone/keystone-service-v1.respec.mts +1990 -1479
- package/src/keystone/keystone-types.mts +48 -2
- package/src/keystone/policy/keystone-profile-builder.mts +9 -1
- package/src/keystone/policy/profiles/profile-domain.json +69 -0
- package/src/keystone/policy/profiles/profile-sync.json +84 -0
- package/src/keystone/policy/schemas/keystone.high.schema.json +1 -1
- package/src/keystone/policy/schemas/keystone.medium.schema.json +1 -1
- package/src/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mts +20 -1
- package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +16 -4
- package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +4 -1
- package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +4 -1
- package/src/sync/sync-conflict-text-merge.withid.respec.mts +10 -4
- package/src/sync/sync-innerspace-constants.withid.respec.mts +12 -3
- package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-partial-update.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace.withid.respec.mts +4 -1
- package/src/sync/sync-peer/sync-peer-types.mts +4 -0
- package/src/sync/sync-peer/sync-peer-v1.mts +5 -1
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +96 -22
- package/src/sync/sync-saga-coordinator.mts +17 -12
- package/src/sync/sync-types.mts +6 -0
- package/src/sync/sync-withid.connect.respec.mts +5 -2
- package/src/sync/sync-withid.establish.respec.mts +4 -1
- package/src/sync/sync-withid.pingpong.respec.mts +4 -1
- package/src/sync/unused-identity-backup.mts.md +0 -311
|
@@ -4,12 +4,14 @@ import { clone, hash, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helpe
|
|
|
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';
|
|
7
|
+
import { KeystoneReplenishStrategy, KeystoneClaimType } from './keystone-types.mjs';
|
|
7
8
|
import { createRevocationPoolConfig, createStandardPoolConfig } from './keystone-config-builder.mjs';
|
|
8
|
-
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE } from './keystone-constants.mjs';
|
|
9
|
+
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE, KEYSTONE_VERB_SYNC, POOL_ID_CUSTODIAN_MANAGE } from './keystone-constants.mjs';
|
|
9
10
|
import { KeystoneService_V1, } from './keystone-service-v1.mjs';
|
|
10
|
-
import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone } from './keystone-helpers.mjs';
|
|
11
|
+
import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone, rotatePoolChallenges } from './keystone-helpers.mjs';
|
|
11
12
|
import { KeystoneProfileBuilder } from './policy/keystone-profile-builder.mjs';
|
|
12
13
|
import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
|
|
14
|
+
import { deriveDelegateSecret } from './strategy/hash-reveal-v1/hash-reveal-v1.mjs';
|
|
13
15
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
14
16
|
/**
|
|
15
17
|
* A simple in-memory map acting as a Space.
|
|
@@ -181,6 +183,14 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
181
183
|
const secretB = await strategyB.derivePoolSecret({ masterSecret });
|
|
182
184
|
iReckon(sir, secretA).asTo('salt affects secret').not.willEqual(secretB);
|
|
183
185
|
});
|
|
186
|
+
await ifWeMight(sir, 'deriveDelegateSecret derives a consistent secret using KDF', async () => {
|
|
187
|
+
const secretA = await deriveDelegateSecret({ masterSecret });
|
|
188
|
+
const secretB = await deriveDelegateSecret({ masterSecret });
|
|
189
|
+
iReckon(sir, secretA).asTo('consistency').willEqual(secretB);
|
|
190
|
+
iReckon(sir, secretA).asTo('truthy').isGonnaBeTruthy();
|
|
191
|
+
const secretC = await deriveDelegateSecret({ masterSecret: masterSecret + '_other' });
|
|
192
|
+
iReckon(sir, secretA).asTo('differ').not.willEqual(secretC);
|
|
193
|
+
});
|
|
184
194
|
});
|
|
185
195
|
await respecfully(sir, 'Challenge/Solution Logic', async () => {
|
|
186
196
|
await ifWeMight(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
|
|
@@ -460,6 +470,42 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
460
470
|
const remaining = Object.keys(revokePool.challenges);
|
|
461
471
|
iReckon(sir, remaining.length).asTo('pool depleted').willEqual(0);
|
|
462
472
|
});
|
|
473
|
+
await ifWeMight(sir, 'fails to sign any new claims after revocation', async () => {
|
|
474
|
+
let errorCaught = false;
|
|
475
|
+
try {
|
|
476
|
+
await service.sign({
|
|
477
|
+
latestKeystone: revokedKeystone,
|
|
478
|
+
masterSecret,
|
|
479
|
+
claim: { verb: "post", target: "some_target" },
|
|
480
|
+
metaspace: mockMetaspace,
|
|
481
|
+
space: mockSpace,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
errorCaught = true;
|
|
486
|
+
iReckon(sir, err.message).includes("has been revoked");
|
|
487
|
+
}
|
|
488
|
+
iReckon(sir, errorCaught).asTo('signing blocked after revocation').isGonnaBeTrue();
|
|
489
|
+
});
|
|
490
|
+
await ifWeMight(sir, 'fails to evolve or add pools after revocation', async () => {
|
|
491
|
+
let errorCaught = false;
|
|
492
|
+
try {
|
|
493
|
+
const dummyConfig = createStandardPoolConfig({ id: "dummy", salt: "dummy" });
|
|
494
|
+
const dummyPool = { id: "dummy", config: dummyConfig, challenges: {} };
|
|
495
|
+
await service.addPools({
|
|
496
|
+
latestKeystone: revokedKeystone,
|
|
497
|
+
masterSecret,
|
|
498
|
+
newPools: [dummyPool],
|
|
499
|
+
metaspace: mockMetaspace,
|
|
500
|
+
space: mockSpace,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
errorCaught = true;
|
|
505
|
+
iReckon(sir, err.message).includes("has been revoked");
|
|
506
|
+
}
|
|
507
|
+
iReckon(sir, errorCaught).asTo('adding pools blocked after revocation').isGonnaBeTrue();
|
|
508
|
+
});
|
|
463
509
|
});
|
|
464
510
|
});
|
|
465
511
|
// ===========================================================================
|
|
@@ -925,6 +971,16 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
925
971
|
iReckon(sir, details.description).asTo('description').willEqual('Alice test keystone');
|
|
926
972
|
iReckon(sir, details.extraInfo).asTo('extraInfo').willEqual('abc');
|
|
927
973
|
});
|
|
974
|
+
await ifWeMight(sir, 'compiles configs successfully with domain profile (omitting connect)', async () => {
|
|
975
|
+
const builder = KeystoneProfileBuilder.buildKeystone('domain')
|
|
976
|
+
.withUsername('alice')
|
|
977
|
+
.withEmail('alice@example.com')
|
|
978
|
+
.withDescription('Alice domain keystone');
|
|
979
|
+
const configs = await builder.compileConfigs();
|
|
980
|
+
iReckon(sir, configs.length).asTo('number of pools').willEqual(4);
|
|
981
|
+
const connectPool = configs.find(c => c.id === 'connect');
|
|
982
|
+
iReckon(sir, connectPool).asTo('connect pool omitted').isGonnaBeUndefined();
|
|
983
|
+
});
|
|
928
984
|
await ifWeMight(sir, 'supports customized overrides', async () => {
|
|
929
985
|
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
930
986
|
.customizePool('sync', {
|
|
@@ -1046,6 +1102,7 @@ await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () =>
|
|
|
1046
1102
|
masterSecret: parentSecret,
|
|
1047
1103
|
metaspace: mockMetaspace,
|
|
1048
1104
|
space: mockSpace,
|
|
1105
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1049
1106
|
});
|
|
1050
1107
|
iReckon(sir, evolvedParent).asTo('evolved parent exists').isGonnaBeTruthy();
|
|
1051
1108
|
iReckon(sir, evolvedParent.data?.delegates).asTo('delegates map exists').isGonnaBeTruthy();
|
|
@@ -1081,6 +1138,7 @@ await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () =>
|
|
|
1081
1138
|
masterSecret: parentSecret,
|
|
1082
1139
|
metaspace: mockMetaspace,
|
|
1083
1140
|
space: mockSpace,
|
|
1141
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1084
1142
|
});
|
|
1085
1143
|
// After registration
|
|
1086
1144
|
const statusAfter = await service.getDelegateStatus({
|
|
@@ -1100,6 +1158,7 @@ await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () =>
|
|
|
1100
1158
|
masterSecret: parentSecret,
|
|
1101
1159
|
metaspace: mockMetaspace,
|
|
1102
1160
|
space: mockSpace,
|
|
1161
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1103
1162
|
});
|
|
1104
1163
|
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1105
1164
|
if (!delegateTjpAddr) {
|
|
@@ -1135,6 +1194,7 @@ await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () =>
|
|
|
1135
1194
|
masterSecret: parentSecret,
|
|
1136
1195
|
metaspace: mockMetaspace,
|
|
1137
1196
|
space: mockSpace,
|
|
1197
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1138
1198
|
});
|
|
1139
1199
|
}
|
|
1140
1200
|
catch (e) {
|
|
@@ -1198,6 +1258,7 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1198
1258
|
masterSecret: parentSecret,
|
|
1199
1259
|
metaspace: mockMetaspace,
|
|
1200
1260
|
space: mockSpace,
|
|
1261
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1201
1262
|
});
|
|
1202
1263
|
const errors = await validateKeystoneTransition({
|
|
1203
1264
|
currentIbGib: evolvedParent,
|
|
@@ -1212,6 +1273,7 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1212
1273
|
masterSecret: parentSecret,
|
|
1213
1274
|
metaspace: mockMetaspace,
|
|
1214
1275
|
space: mockSpace,
|
|
1276
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1215
1277
|
});
|
|
1216
1278
|
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1217
1279
|
// Remove the manage verb proof by converting it to sign
|
|
@@ -1234,6 +1296,7 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1234
1296
|
masterSecret: parentSecret,
|
|
1235
1297
|
metaspace: mockMetaspace,
|
|
1236
1298
|
space: mockSpace,
|
|
1299
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1237
1300
|
});
|
|
1238
1301
|
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1239
1302
|
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
@@ -1257,6 +1320,7 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1257
1320
|
masterSecret: parentSecret,
|
|
1258
1321
|
metaspace: mockMetaspace,
|
|
1259
1322
|
space: mockSpace,
|
|
1323
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1260
1324
|
});
|
|
1261
1325
|
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1262
1326
|
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
@@ -1278,6 +1342,7 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1278
1342
|
masterSecret: parentSecret,
|
|
1279
1343
|
metaspace: mockMetaspace,
|
|
1280
1344
|
space: mockSpace,
|
|
1345
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC],
|
|
1281
1346
|
});
|
|
1282
1347
|
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1283
1348
|
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
@@ -1293,4 +1358,384 @@ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
|
1293
1358
|
iReckon(sir, errors.some(e => e.includes('not a valid historical address in the parent keystone timeline'))).isGonnaBeTrue();
|
|
1294
1359
|
});
|
|
1295
1360
|
});
|
|
1361
|
+
// ===========================================================================
|
|
1362
|
+
// SUITE G: COMBINED POOLS & PASSWORD ROTATION
|
|
1363
|
+
// ===========================================================================
|
|
1364
|
+
await respecfully(sir, 'Suite G: Combined Pools & Password Rotation', async () => {
|
|
1365
|
+
const service = new KeystoneService_V1();
|
|
1366
|
+
const userSecret = "UserMasterSecret_123";
|
|
1367
|
+
const custodianSecret = "CustodianSecret_456";
|
|
1368
|
+
let mockSpace;
|
|
1369
|
+
let mockMetaspace;
|
|
1370
|
+
firstOfEach(sir, async () => {
|
|
1371
|
+
mockSpace = new MockIbGibSpace();
|
|
1372
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1373
|
+
});
|
|
1374
|
+
await ifWeMight(sir, 'revokes a keystone using a separate revocation pool', async () => {
|
|
1375
|
+
const stdSalt = "std_salt";
|
|
1376
|
+
const revokeSalt = "revoke_salt";
|
|
1377
|
+
const stdConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: stdSalt });
|
|
1378
|
+
const revokeConfig = createStandardPoolConfig({ id: POOL_ID_REVOKE, salt: revokeSalt });
|
|
1379
|
+
revokeConfig.allowedVerbs = [KEYSTONE_VERB_REVOKE];
|
|
1380
|
+
revokeConfig.behavior.replenish = KeystoneReplenishStrategy.deleteAll;
|
|
1381
|
+
const keystone = await service.genesis({
|
|
1382
|
+
masterSecret: userSecret,
|
|
1383
|
+
configs: [stdConfig, revokeConfig],
|
|
1384
|
+
metaspace: mockMetaspace,
|
|
1385
|
+
space: mockSpace,
|
|
1386
|
+
});
|
|
1387
|
+
const revoked = await service.revoke({
|
|
1388
|
+
latestKeystone: keystone,
|
|
1389
|
+
masterSecret: userSecret,
|
|
1390
|
+
metaspace: mockMetaspace,
|
|
1391
|
+
space: mockSpace,
|
|
1392
|
+
});
|
|
1393
|
+
iReckon(sir, revoked.data?.revocationInfo).asTo('revocationInfo present').isGonnaBeTruthy();
|
|
1394
|
+
const revokePool = revoked.data?.challengePools.find(p => p.id === POOL_ID_REVOKE);
|
|
1395
|
+
iReckon(sir, Object.keys(revokePool.challenges).length).asTo('revoke pool depleted').willEqual(0);
|
|
1396
|
+
});
|
|
1397
|
+
await ifWeMight(sir, 'revokes a keystone using a combined manage/revoke pool and empties it', async () => {
|
|
1398
|
+
const salt = "combined_salt";
|
|
1399
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt });
|
|
1400
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1401
|
+
const keystone = await service.genesis({
|
|
1402
|
+
masterSecret: userSecret,
|
|
1403
|
+
configs: [manageConfig],
|
|
1404
|
+
metaspace: mockMetaspace,
|
|
1405
|
+
space: mockSpace,
|
|
1406
|
+
});
|
|
1407
|
+
const revoked = await service.revoke({
|
|
1408
|
+
latestKeystone: keystone,
|
|
1409
|
+
masterSecret: userSecret,
|
|
1410
|
+
metaspace: mockMetaspace,
|
|
1411
|
+
space: mockSpace,
|
|
1412
|
+
});
|
|
1413
|
+
iReckon(sir, revoked.data?.revocationInfo).asTo('revocationInfo present').isGonnaBeTruthy();
|
|
1414
|
+
const managePool = revoked.data?.challengePools.find(p => p.id === POOL_ID_MANAGE);
|
|
1415
|
+
iReckon(sir, Object.keys(managePool.challenges).length).asTo('combined manage pool depleted').willEqual(0);
|
|
1416
|
+
});
|
|
1417
|
+
await ifWeMight(sir, 'rotates user password successfully when signed by the user manage pool', async () => {
|
|
1418
|
+
const salt = "manage_salt";
|
|
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;
|
|
1450
|
+
try {
|
|
1451
|
+
await service.sign({
|
|
1452
|
+
latestKeystone: rotated,
|
|
1453
|
+
masterSecret: userSecret,
|
|
1454
|
+
claim,
|
|
1455
|
+
poolId: POOL_ID_MANAGE,
|
|
1456
|
+
metaspace: mockMetaspace,
|
|
1457
|
+
space: mockSpace,
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
catch {
|
|
1461
|
+
errorCaught = true;
|
|
1462
|
+
}
|
|
1463
|
+
iReckon(sir, errorCaught).asTo('old secret signing rejected').isGonnaBeTrue();
|
|
1464
|
+
});
|
|
1465
|
+
await ifWeMight(sir, 'recovers user password successfully when signed by the custodian manage pool', async () => {
|
|
1466
|
+
const userSalt = "user_salt";
|
|
1467
|
+
const custodianSalt = "custodian_salt";
|
|
1468
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1469
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1470
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1471
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1472
|
+
// We simulate constructing the custodian pool "offline" by the server
|
|
1473
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1474
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1475
|
+
const challenges = {};
|
|
1476
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1477
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1478
|
+
const challengeId = raw.substring(0, 16);
|
|
1479
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1480
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1481
|
+
}
|
|
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
|
+
});
|
|
1525
|
+
await ifWeMight(sir, 'rotates custodian manage pool successfully', async () => {
|
|
1526
|
+
const userSalt = "user_salt";
|
|
1527
|
+
const custodianSalt = "custodian_salt";
|
|
1528
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: userSalt });
|
|
1529
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1530
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: custodianSalt });
|
|
1531
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1532
|
+
// Custodian Pool genesis
|
|
1533
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1534
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1535
|
+
const challenges = {};
|
|
1536
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1537
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1538
|
+
const challengeId = raw.substring(0, 16);
|
|
1539
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1540
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1541
|
+
}
|
|
1542
|
+
const custodianPool = {
|
|
1543
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1544
|
+
config: custodianManageConfig,
|
|
1545
|
+
challenges,
|
|
1546
|
+
isForeign: true
|
|
1547
|
+
};
|
|
1548
|
+
const keystone = await service.genesis({
|
|
1549
|
+
masterSecret: userSecret,
|
|
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 });
|
|
1573
|
+
}
|
|
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
|
+
});
|
|
1602
|
+
await ifWeMight(sir, 'rotates all non-foreign pools during changePassword by default', async () => {
|
|
1603
|
+
const userSalt = "user_salt";
|
|
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
|
+
});
|
|
1670
|
+
await ifWeMight(sir, 'fails validation if password rotation details or constraints are violated', async () => {
|
|
1671
|
+
// Construct invalid change-password transition
|
|
1672
|
+
const userManageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: "salt1" });
|
|
1673
|
+
userManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1674
|
+
const custodianManageConfig = createStandardPoolConfig({ id: POOL_ID_CUSTODIAN_MANAGE, salt: "salt2" });
|
|
1675
|
+
custodianManageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
|
|
1676
|
+
// Custodian Pool genesis
|
|
1677
|
+
const strategy = KeystoneStrategyFactory.create({ config: custodianManageConfig });
|
|
1678
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
|
|
1679
|
+
const challenges = {};
|
|
1680
|
+
for (let i = 0; i < custodianManageConfig.behavior.size; i++) {
|
|
1681
|
+
const raw = await hash({ s: `${custodianManageConfig.salt}${Date.now()}${i}` });
|
|
1682
|
+
const challengeId = raw.substring(0, 16);
|
|
1683
|
+
const solution = await strategy.generateSolution({ poolSecret, poolId: custodianManageConfig.id, challengeId });
|
|
1684
|
+
challenges[challengeId] = await strategy.generateChallenge({ solution });
|
|
1685
|
+
}
|
|
1686
|
+
const custodianPool = {
|
|
1687
|
+
id: POOL_ID_CUSTODIAN_MANAGE,
|
|
1688
|
+
config: custodianManageConfig,
|
|
1689
|
+
challenges,
|
|
1690
|
+
isForeign: true
|
|
1691
|
+
};
|
|
1692
|
+
const keystone = await service.genesis({
|
|
1693
|
+
masterSecret: userSecret,
|
|
1694
|
+
configs: [userManageConfig],
|
|
1695
|
+
metaspace: mockMetaspace,
|
|
1696
|
+
space: mockSpace,
|
|
1697
|
+
});
|
|
1698
|
+
const keystoneWithCustodian = await service.addPools({
|
|
1699
|
+
latestKeystone: keystone,
|
|
1700
|
+
masterSecret: userSecret,
|
|
1701
|
+
newPools: [custodianPool],
|
|
1702
|
+
metaspace: mockMetaspace,
|
|
1703
|
+
space: mockSpace,
|
|
1704
|
+
});
|
|
1705
|
+
// Create an invalid frame manually where a foreign pool is rotated during change-password
|
|
1706
|
+
const badRotatedPool = await rotatePoolChallenges({
|
|
1707
|
+
prevPool: custodianPool,
|
|
1708
|
+
newMasterSecret: "bad_secret"
|
|
1709
|
+
});
|
|
1710
|
+
const evolved = await service.changePassword({
|
|
1711
|
+
latestKeystone: keystoneWithCustodian,
|
|
1712
|
+
newMasterSecret: "new_pass",
|
|
1713
|
+
signingSecret: userSecret,
|
|
1714
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
1715
|
+
metaspace: mockMetaspace,
|
|
1716
|
+
space: mockSpace,
|
|
1717
|
+
});
|
|
1718
|
+
// Inject the foreign pool rotation manually to simulate an attacker
|
|
1719
|
+
const attackerData = {
|
|
1720
|
+
...evolved.data,
|
|
1721
|
+
challengePools: evolved.data.challengePools.map(p => p.id === POOL_ID_CUSTODIAN_MANAGE ? badRotatedPool : p)
|
|
1722
|
+
};
|
|
1723
|
+
// Update details claim to say we rotated custodian-manage
|
|
1724
|
+
attackerData.proofs[0].claim.details = {
|
|
1725
|
+
type: KeystoneClaimType.change_password,
|
|
1726
|
+
info: { change: [POOL_ID_MANAGE, POOL_ID_CUSTODIAN_MANAGE] }
|
|
1727
|
+
};
|
|
1728
|
+
const attackerKeystone = {
|
|
1729
|
+
...evolved,
|
|
1730
|
+
data: attackerData
|
|
1731
|
+
};
|
|
1732
|
+
const errors = await service.validate({
|
|
1733
|
+
prevIbGib: keystoneWithCustodian,
|
|
1734
|
+
currentIbGib: attackerKeystone
|
|
1735
|
+
});
|
|
1736
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1737
|
+
const hasForeignViolation = errors.some(e => e.includes("Security Violation") && e.includes("foreign pool"));
|
|
1738
|
+
iReckon(sir, hasForeignViolation).isGonnaBeTrue();
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1296
1741
|
//# sourceMappingURL=keystone-service-v1.respec.mjs.map
|