@ibgib/core-gib 0.1.61 → 0.1.62
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 +13 -0
- package/dist/keystone/keystone-constants.d.mts +0 -1
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +0 -1
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +23 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +98 -5
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +38 -2
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +173 -3
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +347 -10
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +49 -0
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/keystone/README.md +15 -2
- package/src/keystone/docs/architecture.md +21 -2
- package/src/keystone/docs/delegation.md +57 -0
- package/src/keystone/keystone-constants.mts +0 -1
- package/src/keystone/keystone-helpers.mts +131 -5
- package/src/keystone/keystone-service-v1.mts +219 -2
- package/src/keystone/keystone-service-v1.respec.mts +383 -8
- package/src/keystone/keystone-types.mts +50 -0
|
@@ -10,10 +10,11 @@ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
|
10
10
|
import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
|
|
11
11
|
import { KeystoneChallengePool, KeystoneClaim, KeystoneIbGib_V1, KeystonePoolConfig_HashV1 } from './keystone-types.mjs';
|
|
12
12
|
import { createRevocationPoolConfig, createStandardPoolConfig } from './keystone-config-builder.mjs';
|
|
13
|
-
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE } from './keystone-constants.mjs';
|
|
14
|
-
import { KeystoneService_V1 } from './keystone-service-v1.mjs';
|
|
15
|
-
import { selectChallengeIds, validateChallengePool } from './keystone-helpers.mjs';
|
|
13
|
+
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE } from './keystone-constants.mjs';
|
|
14
|
+
import { KeystoneService_V1, } from './keystone-service-v1.mjs';
|
|
15
|
+
import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone } from './keystone-helpers.mjs';
|
|
16
16
|
import { KeystoneProfileBuilder } from './policy/keystone-profile-builder.mjs';
|
|
17
|
+
import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
|
|
17
18
|
|
|
18
19
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
19
20
|
|
|
@@ -37,16 +38,55 @@ class MockIbGibSpace {
|
|
|
37
38
|
return data ? JSON.parse(JSON.stringify(data)) : null;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
async argy({ argData, ibGibs }: { argData: any; ibGibs?: IbGib_V1[] }): Promise<any> {
|
|
42
|
+
return {
|
|
43
|
+
ib: 'arg^gib',
|
|
44
|
+
gib: 'arg_gib',
|
|
45
|
+
data: argData,
|
|
46
|
+
ibGibs,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
async witness(arg: any): Promise<any> {
|
|
41
51
|
const cmd = arg.data?.cmd;
|
|
52
|
+
const cmdModifiers = arg.data?.cmdModifiers || [];
|
|
42
53
|
if (cmd === 'get') {
|
|
43
54
|
const addrs = arg.data.ibGibAddrs || [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
55
|
+
if (cmdModifiers.includes('latest') && cmdModifiers.includes('addrs')) {
|
|
56
|
+
const latestAddrsMap: { [addr: string]: string } = {};
|
|
57
|
+
for (const queryAddr of addrs) {
|
|
58
|
+
const queryTjpGib = queryAddr.includes('.') ? queryAddr.split('.')[1] : queryAddr.split('^')[1];
|
|
59
|
+
let latestAddr = queryAddr;
|
|
60
|
+
let maxN = -1;
|
|
61
|
+
for (const [addr, ibGib] of this.store.entries()) {
|
|
62
|
+
const tjpGib = addr.includes('.') ? addr.split('.')[1] : addr.split('^')[1];
|
|
63
|
+
if (tjpGib === queryTjpGib) {
|
|
64
|
+
const n = ibGib.data?.n ?? 0;
|
|
65
|
+
if (n > maxN) {
|
|
66
|
+
maxN = n;
|
|
67
|
+
latestAddr = addr;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
latestAddrsMap[queryAddr] = latestAddr;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
data: {
|
|
75
|
+
success: true,
|
|
76
|
+
latestAddrsMap,
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
const ibGibs: IbGib_V1[] = [];
|
|
81
|
+
for (const addr of addrs) {
|
|
82
|
+
const x = await this.get({ addr });
|
|
83
|
+
if (x) { ibGibs.push(x); }
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
data: { success: true },
|
|
87
|
+
ibGibs,
|
|
88
|
+
};
|
|
48
89
|
}
|
|
49
|
-
return { ibGibs };
|
|
50
90
|
}
|
|
51
91
|
return undefined;
|
|
52
92
|
}
|
|
@@ -81,6 +121,15 @@ class MockMetaspaceService {
|
|
|
81
121
|
return target.put(args);
|
|
82
122
|
}
|
|
83
123
|
|
|
124
|
+
async get(args: { addr: string, space?: any }): Promise<{ success: boolean, ibGibs?: IbGib_V1[] }> {
|
|
125
|
+
const target = args.space || this.space;
|
|
126
|
+
const x = await target.get({ addr: args.addr });
|
|
127
|
+
return {
|
|
128
|
+
success: !!x,
|
|
129
|
+
ibGibs: x ? [x] : [],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
84
133
|
/**
|
|
85
134
|
* Tracks the latest version of an ibGib timeline.
|
|
86
135
|
*/
|
|
@@ -1160,4 +1209,330 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
1160
1209
|
});
|
|
1161
1210
|
|
|
1162
1211
|
});
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
// ===========================================================================
|
|
1216
|
+
// SUITE E: DELEGATION (Register/Unregister)
|
|
1217
|
+
// ===========================================================================
|
|
1218
|
+
|
|
1219
|
+
await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () => {
|
|
1220
|
+
|
|
1221
|
+
const service = new KeystoneService_V1();
|
|
1222
|
+
const parentSecret = "ParentMasterSecret_123";
|
|
1223
|
+
const delegateSecret = "DelegateMasterSecret_456";
|
|
1224
|
+
|
|
1225
|
+
let mockSpace: MockIbGibSpace;
|
|
1226
|
+
let mockMetaspace: any;
|
|
1227
|
+
|
|
1228
|
+
let parentKeystone: KeystoneIbGib_V1;
|
|
1229
|
+
let delegateKeystone: KeystoneIbGib_V1;
|
|
1230
|
+
|
|
1231
|
+
firstOfEach(sir, async () => {
|
|
1232
|
+
mockSpace = new MockIbGibSpace();
|
|
1233
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1234
|
+
|
|
1235
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1236
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1237
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt + "_manage" });
|
|
1238
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1239
|
+
|
|
1240
|
+
// 1. Create Parent Keystone (with manage pool)
|
|
1241
|
+
parentKeystone = await service.genesis({
|
|
1242
|
+
masterSecret: parentSecret,
|
|
1243
|
+
configs: [defaultConfig, manageConfig],
|
|
1244
|
+
metaspace: mockMetaspace,
|
|
1245
|
+
space: mockSpace as any,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// 2. Create Delegate Keystone
|
|
1249
|
+
const delegateConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt + "_delegate" });
|
|
1250
|
+
delegateKeystone = await service.genesis({
|
|
1251
|
+
masterSecret: delegateSecret,
|
|
1252
|
+
configs: [delegateConfig],
|
|
1253
|
+
metaspace: mockMetaspace,
|
|
1254
|
+
space: mockSpace as any,
|
|
1255
|
+
});
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
await ifWeMight(sir, 'registers a delegate keystone and evolves parent using manage pool', async () => {
|
|
1259
|
+
// Register the delegate
|
|
1260
|
+
const evolvedParent = await service.registerDelegate({
|
|
1261
|
+
parentKeystone,
|
|
1262
|
+
delegateKeystone,
|
|
1263
|
+
masterSecret: parentSecret,
|
|
1264
|
+
metaspace: mockMetaspace,
|
|
1265
|
+
space: mockSpace as any,
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
iReckon(sir, evolvedParent).asTo('evolved parent exists').isGonnaBeTruthy();
|
|
1269
|
+
iReckon(sir, evolvedParent.data?.delegates).asTo('delegates map exists').isGonnaBeTruthy();
|
|
1270
|
+
|
|
1271
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1272
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 312e986ce4b2ed6c084c03e8f45fa826)`); }
|
|
1273
|
+
const delegateInfo = evolvedParent.data?.delegates?.[delegateTjpAddr];
|
|
1274
|
+
iReckon(sir, delegateInfo).asTo('delegate info registered').isGonnaBeTruthy();
|
|
1275
|
+
iReckon(sir, delegateInfo?.delegateTjpAddr).asTo('delegate tjp matches').willEqual(delegateTjpAddr);
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
await ifWeMight(sir, 'gets correct delegate status via parent TJP address', async () => {
|
|
1279
|
+
const parentTjpAddr = getTjpAddr({ ibGib: parentKeystone });
|
|
1280
|
+
if (!parentTjpAddr) { throw new Error(`(UNEXPECTED) parentTjpAddr falsy? (E: b7ce2835ccd951d8c99257328be3b826)`); }
|
|
1281
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1282
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 3e4878ea19dd8034e827466a8bf09526)`); }
|
|
1283
|
+
|
|
1284
|
+
// Before registration
|
|
1285
|
+
const statusBefore = await service.getDelegateStatus({
|
|
1286
|
+
parentTjpAddr: parentTjpAddr,
|
|
1287
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1288
|
+
metaspace: mockMetaspace,
|
|
1289
|
+
space: mockSpace as any,
|
|
1290
|
+
});
|
|
1291
|
+
iReckon(sir, statusBefore.registered).asTo('initially unregistered').isGonnaBeFalse();
|
|
1292
|
+
|
|
1293
|
+
// Register
|
|
1294
|
+
const evolvedParent = await service.registerDelegate({
|
|
1295
|
+
parentKeystone,
|
|
1296
|
+
delegateKeystone,
|
|
1297
|
+
masterSecret: parentSecret,
|
|
1298
|
+
metaspace: mockMetaspace,
|
|
1299
|
+
space: mockSpace as any,
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
// After registration
|
|
1303
|
+
const statusAfter = await service.getDelegateStatus({
|
|
1304
|
+
parentTjpAddr: parentTjpAddr,
|
|
1305
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1306
|
+
metaspace: mockMetaspace,
|
|
1307
|
+
space: mockSpace as any,
|
|
1308
|
+
});
|
|
1309
|
+
iReckon(sir, statusAfter.registered).asTo('registered status').isGonnaBeTrue();
|
|
1310
|
+
iReckon(sir, statusAfter.delegateInfo?.delegateTjpAddr).asTo('status tjp matches').willEqual(delegateTjpAddr);
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
await ifWeMight(sir, 'unregisters a registered delegate keystone', async () => {
|
|
1314
|
+
// Register first
|
|
1315
|
+
const evolvedParent = await service.registerDelegate({
|
|
1316
|
+
parentKeystone,
|
|
1317
|
+
delegateKeystone,
|
|
1318
|
+
masterSecret: parentSecret,
|
|
1319
|
+
metaspace: mockMetaspace,
|
|
1320
|
+
space: mockSpace as any,
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1324
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 486b486a8c984a52b8d2b22418c20d26)`); }
|
|
1325
|
+
|
|
1326
|
+
// Unregister
|
|
1327
|
+
const finalParent = await service.unregisterDelegate({
|
|
1328
|
+
parentKeystone: evolvedParent,
|
|
1329
|
+
delegateTjpAddr,
|
|
1330
|
+
masterSecret: parentSecret,
|
|
1331
|
+
metaspace: mockMetaspace,
|
|
1332
|
+
space: mockSpace as any,
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
iReckon(sir, finalParent).asTo('final evolved parent exists').isGonnaBeTruthy();
|
|
1336
|
+
const info = finalParent.data?.delegates?.[delegateTjpAddr];
|
|
1337
|
+
iReckon(sir, info).asTo('delegate info removed').isGonnaBeUndefined();
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
await ifWeMight(sir, 'throws if parent does not have a manage pool', async () => {
|
|
1341
|
+
// Create a parent WITHOUT a manage pool
|
|
1342
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1343
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1344
|
+
const restrictedParent = await service.genesis({
|
|
1345
|
+
masterSecret: parentSecret,
|
|
1346
|
+
configs: [defaultConfig],
|
|
1347
|
+
metaspace: mockMetaspace,
|
|
1348
|
+
space: mockSpace as any,
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
let errorCaught = false;
|
|
1352
|
+
try {
|
|
1353
|
+
await service.registerDelegate({
|
|
1354
|
+
parentKeystone: restrictedParent,
|
|
1355
|
+
delegateKeystone,
|
|
1356
|
+
masterSecret: parentSecret,
|
|
1357
|
+
metaspace: mockMetaspace,
|
|
1358
|
+
space: mockSpace as any,
|
|
1359
|
+
});
|
|
1360
|
+
} catch (e: any) {
|
|
1361
|
+
errorCaught = true;
|
|
1362
|
+
iReckon(sir, e.message).includes('Manage pool is required');
|
|
1363
|
+
}
|
|
1364
|
+
iReckon(sir, errorCaught).asTo('delegation rejected').isGonnaBeTrue();
|
|
1365
|
+
});
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
1369
|
+
const service = new KeystoneService_V1();
|
|
1370
|
+
const parentSecret = "ParentMasterSecret_SuiteG";
|
|
1371
|
+
const delegateSecret = "DelegateMasterSecret_SuiteG";
|
|
1372
|
+
|
|
1373
|
+
let mockSpace: MockIbGibSpace;
|
|
1374
|
+
let mockMetaspace: any;
|
|
1375
|
+
|
|
1376
|
+
let parentKeystone: KeystoneIbGib_V1;
|
|
1377
|
+
let delegateKeystone: KeystoneIbGib_V1;
|
|
1378
|
+
|
|
1379
|
+
firstOfEach(sir, async () => {
|
|
1380
|
+
mockSpace = new MockIbGibSpace();
|
|
1381
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1382
|
+
|
|
1383
|
+
const salt1 = (await getUUID()).substring(0, 16);
|
|
1384
|
+
const salt2 = (await getUUID()).substring(0, 16);
|
|
1385
|
+
|
|
1386
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt2 });
|
|
1387
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1388
|
+
|
|
1389
|
+
const configs = [
|
|
1390
|
+
createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 }),
|
|
1391
|
+
manageConfig
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
parentKeystone = await service.genesis({
|
|
1395
|
+
masterSecret: parentSecret,
|
|
1396
|
+
configs,
|
|
1397
|
+
metaspace: mockMetaspace,
|
|
1398
|
+
space: mockSpace as any,
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
delegateKeystone = await service.genesis({
|
|
1402
|
+
masterSecret: delegateSecret,
|
|
1403
|
+
configs: [createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 })],
|
|
1404
|
+
metaspace: mockMetaspace,
|
|
1405
|
+
space: mockSpace as any,
|
|
1406
|
+
});
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
await ifWeMight(sir, 'validateKeystoneIb returns true for valid keystone ib and false for others', async () => {
|
|
1410
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone' })).isGonnaBeTrue();
|
|
1411
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone user info' })).isGonnaBeTrue();
|
|
1412
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'invalidAtom' })).isGonnaBeFalse();
|
|
1413
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'not_keystone sub_atom' })).isGonnaBeFalse();
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
await ifWeMight(sir, 'validateGenesisKeystone fails validation if ib is invalid', async () => {
|
|
1417
|
+
const invalidGenesis = {
|
|
1418
|
+
...parentKeystone,
|
|
1419
|
+
ib: 'not_a_keystone^' + parentKeystone.gib,
|
|
1420
|
+
};
|
|
1421
|
+
const errors = await validateGenesisKeystone({ keystoneIbGib: invalidGenesis });
|
|
1422
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1423
|
+
iReckon(sir, errors[0]).includes('invalid keystone ib');
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
await ifWeMight(sir, 'validateKeystoneTransition passes for valid delegate registration', async () => {
|
|
1427
|
+
const evolvedParent = await service.registerDelegate({
|
|
1428
|
+
parentKeystone,
|
|
1429
|
+
delegateKeystone,
|
|
1430
|
+
masterSecret: parentSecret,
|
|
1431
|
+
metaspace: mockMetaspace,
|
|
1432
|
+
space: mockSpace as any,
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
const errors = await validateKeystoneTransition({
|
|
1436
|
+
currentIbGib: evolvedParent,
|
|
1437
|
+
prevIbGib: parentKeystone,
|
|
1438
|
+
});
|
|
1439
|
+
iReckon(sir, errors.length).willEqual(0);
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegates map is mutated without manage proof', async () => {
|
|
1443
|
+
const evolvedParent = await service.registerDelegate({
|
|
1444
|
+
parentKeystone,
|
|
1445
|
+
delegateKeystone,
|
|
1446
|
+
masterSecret: parentSecret,
|
|
1447
|
+
metaspace: mockMetaspace,
|
|
1448
|
+
space: mockSpace as any,
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1451
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1452
|
+
|
|
1453
|
+
// Remove the manage verb proof by converting it to sign
|
|
1454
|
+
for (const proof of tamperedParent.data.proofs) {
|
|
1455
|
+
if (proof.claim) {
|
|
1456
|
+
proof.claim.verb = 'sign';
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
const errors = await validateKeystoneTransition({
|
|
1461
|
+
currentIbGib: tamperedParent,
|
|
1462
|
+
prevIbGib: parentKeystone,
|
|
1463
|
+
});
|
|
1464
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1465
|
+
iReckon(sir, errors.some(e => e.includes('Policy Violation: Delegates map was mutated'))).isGonnaBeTrue();
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate key does not match delegateTjpAddr', async () => {
|
|
1469
|
+
const evolvedParent = await service.registerDelegate({
|
|
1470
|
+
parentKeystone,
|
|
1471
|
+
delegateKeystone,
|
|
1472
|
+
masterSecret: parentSecret,
|
|
1473
|
+
metaspace: mockMetaspace,
|
|
1474
|
+
space: mockSpace as any,
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1478
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1479
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1480
|
+
|
|
1481
|
+
const info = tamperedParent.data.delegates![delegateTjpAddr];
|
|
1482
|
+
delete tamperedParent.data.delegates![delegateTjpAddr];
|
|
1483
|
+
tamperedParent.data.delegates!['mismatched_key'] = info;
|
|
1484
|
+
|
|
1485
|
+
const errors = await validateKeystoneTransition({
|
|
1486
|
+
currentIbGib: tamperedParent,
|
|
1487
|
+
prevIbGib: parentKeystone,
|
|
1488
|
+
});
|
|
1489
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1490
|
+
iReckon(sir, errors.some(e => e.includes('does not match delegateTjpAddr'))).isGonnaBeTrue();
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate addresses have invalid keystone ib', async () => {
|
|
1494
|
+
const evolvedParent = await service.registerDelegate({
|
|
1495
|
+
parentKeystone,
|
|
1496
|
+
delegateKeystone,
|
|
1497
|
+
masterSecret: parentSecret,
|
|
1498
|
+
metaspace: mockMetaspace,
|
|
1499
|
+
space: mockSpace as any,
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1503
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1504
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1505
|
+
|
|
1506
|
+
tamperedParent.data.delegates![delegateTjpAddr].delegateAddr = 'invalid_ib^12345';
|
|
1507
|
+
|
|
1508
|
+
const errors = await validateKeystoneTransition({
|
|
1509
|
+
currentIbGib: tamperedParent,
|
|
1510
|
+
prevIbGib: parentKeystone,
|
|
1511
|
+
});
|
|
1512
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1513
|
+
iReckon(sir, errors.some(e => e.includes('not a valid keystone address'))).isGonnaBeTrue();
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if thisAddr is not in the parent timeline history', async () => {
|
|
1517
|
+
const evolvedParent = await service.registerDelegate({
|
|
1518
|
+
parentKeystone,
|
|
1519
|
+
delegateKeystone,
|
|
1520
|
+
masterSecret: parentSecret,
|
|
1521
|
+
metaspace: mockMetaspace,
|
|
1522
|
+
space: mockSpace as any,
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1526
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1527
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1528
|
+
|
|
1529
|
+
tamperedParent.data.delegates![delegateTjpAddr].thisAddr = 'keystone^randomgib';
|
|
1530
|
+
|
|
1531
|
+
const errors = await validateKeystoneTransition({
|
|
1532
|
+
currentIbGib: tamperedParent,
|
|
1533
|
+
prevIbGib: parentKeystone,
|
|
1534
|
+
});
|
|
1535
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1536
|
+
iReckon(sir, errors.some(e => e.includes('not a valid historical address in the parent keystone timeline'))).isGonnaBeTrue();
|
|
1537
|
+
});
|
|
1163
1538
|
});
|
|
@@ -314,6 +314,50 @@ export interface KeystoneRevocationInfo {
|
|
|
314
314
|
proof: KeystoneProof;
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Shape of info we collect about delegated keystones. We can check
|
|
319
|
+
* against this info later when validating any identity delegation requests.
|
|
320
|
+
*/
|
|
321
|
+
export interface DelegateKeystoneInfo {
|
|
322
|
+
/**
|
|
323
|
+
* The Timeline Junction Point (TJP) address of the delegate keystone.
|
|
324
|
+
* Serves as its immutable, unique identifier.
|
|
325
|
+
*/
|
|
326
|
+
delegateTjpAddr: IbGibAddr;
|
|
327
|
+
/**
|
|
328
|
+
* concrete addr of **DELEGATE** keystone **at the time when the
|
|
329
|
+
* delegate was added.**
|
|
330
|
+
*
|
|
331
|
+
* So if we have a delegate keystone at D3, then this value would be the
|
|
332
|
+
* addr of that D3 keystone. This will have both a `punctiliarHash` and a
|
|
333
|
+
* `tjpGib` in its `GibInfo`.
|
|
334
|
+
*/
|
|
335
|
+
delegateAddr: IbGibAddr;
|
|
336
|
+
/**
|
|
337
|
+
* concrete keystone addr of **THIS** parent keystone **at the time
|
|
338
|
+
* when the delegate was added.**
|
|
339
|
+
*
|
|
340
|
+
* So if we have a delegate keystone at D3, then this value would be the
|
|
341
|
+
* addr of that D3 keystone. This will have both a `punctiliarHash` and a
|
|
342
|
+
* `tjpGib` in its `GibInfo`.
|
|
343
|
+
*/
|
|
344
|
+
thisAddr: IbGibAddr;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Status response when checking if a delegate keystone is registered.
|
|
349
|
+
*/
|
|
350
|
+
export interface DelegateKeystoneStatus {
|
|
351
|
+
/**
|
|
352
|
+
* True if the delegate is currently registered on the parent.
|
|
353
|
+
*/
|
|
354
|
+
registered: boolean;
|
|
355
|
+
/**
|
|
356
|
+
* Truthy only if registered is true.
|
|
357
|
+
*/
|
|
358
|
+
delegateInfo?: DelegateKeystoneInfo;
|
|
359
|
+
}
|
|
360
|
+
|
|
317
361
|
// ===========================================================================
|
|
318
362
|
// TOP LEVEL IBGIB DATA
|
|
319
363
|
// ===========================================================================
|
|
@@ -351,6 +395,12 @@ export interface KeystoneData_V1 extends IbGibData_V1 {
|
|
|
351
395
|
* Acts as a snapshot to avoid walking the entire timeline.
|
|
352
396
|
*/
|
|
353
397
|
checkpointDetails?: any;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Map of delegated keystones soft-linked to this parent keystone.
|
|
401
|
+
* Keyed by the delegate keystone's tjp address.
|
|
402
|
+
*/
|
|
403
|
+
delegates?: { [delegateTjpAddr: IbGibAddr]: DelegateKeystoneInfo };
|
|
354
404
|
}
|
|
355
405
|
|
|
356
406
|
export interface KeystoneRel8ns_V1 extends IbGibRel8ns_V1 {
|