@ibgib/core-gib 0.1.43 → 0.1.44
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/kdf/kdf-constants.d.mts +25 -0
- package/dist/keystone/kdf/kdf-constants.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-constants.mjs +28 -0
- package/dist/keystone/kdf/kdf-constants.mjs.map +1 -0
- package/dist/keystone/kdf/kdf-helpers.d.mts +45 -0
- package/dist/keystone/kdf/kdf-helpers.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-helpers.mjs +94 -0
- package/dist/keystone/kdf/kdf-helpers.mjs.map +1 -0
- package/dist/keystone/kdf/kdf-types.d.mts +49 -0
- package/dist/keystone/kdf/kdf-types.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-types.mjs +2 -0
- package/dist/keystone/kdf/kdf-types.mjs.map +1 -0
- package/dist/keystone/keystone-config-builder.d.mts +65 -12
- package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
- package/dist/keystone/keystone-config-builder.mjs +138 -46
- package/dist/keystone/keystone-config-builder.mjs.map +1 -1
- package/dist/keystone/keystone-config-builder.respec.mjs +21 -13
- package/dist/keystone/keystone-config-builder.respec.mjs.map +1 -1
- package/dist/keystone/keystone-constants.d.mts +15 -0
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +16 -0
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +4 -4
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +8 -5
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +1 -1
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +6 -5
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +72 -45
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +28 -18
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/keystone-types.mjs +26 -15
- package/dist/keystone/keystone-types.mjs.map +1 -1
- 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 +7 -10
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +9 -0
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +10 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +49 -19
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +22 -11
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +120 -27
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +1 -7
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +11 -0
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/README.md +4 -3
- package/src/keystone/docs/architecture.md +3 -1
- package/src/keystone/kdf/kdf-constants.mts +34 -0
- package/src/keystone/kdf/kdf-helpers.mts +105 -0
- package/src/keystone/kdf/kdf-types.mts +58 -0
- package/src/keystone/keystone-config-builder.mts +170 -47
- package/src/keystone/keystone-config-builder.respec.mts +21 -14
- package/src/keystone/keystone-constants.mts +21 -2
- package/src/keystone/keystone-helpers.mts +19 -14
- package/src/keystone/keystone-service-v1.mts +23 -22
- package/src/keystone/keystone-service-v1.respec.mts +71 -44
- package/src/keystone/keystone-types.mts +37 -23
- package/src/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mts +9 -13
- package/src/sync/sync-constants.mts +12 -0
- package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +53 -20
- package/src/sync/sync-saga-coordinator.mts +156 -31
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +1 -7
- package/src/sync/sync-types.mts +12 -0
- package/tmp.md +0 -274
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { HashAlgorithm } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
2
|
+
import { KeystoneReplenishStrategy } from "./keystone-types.mjs";
|
|
3
|
+
|
|
1
4
|
export const KEYSTONE_ATOM = "keystone";
|
|
2
5
|
|
|
3
6
|
// #region KeystoneVerb enum
|
|
@@ -25,9 +28,9 @@ export const KeystoneVerb = {
|
|
|
25
28
|
/**
|
|
26
29
|
* The meta-verb used to authorize structural changes to the Keystone,
|
|
27
30
|
* specifically adding or removing challenge pools.
|
|
28
|
-
*
|
|
31
|
+
*
|
|
29
32
|
* "Root access" to the identity.
|
|
30
|
-
*
|
|
33
|
+
*
|
|
31
34
|
* Basically, this is the verb for the "admin" pool.
|
|
32
35
|
*/
|
|
33
36
|
MANAGE: KEYSTONE_VERB_MANAGE,
|
|
@@ -44,3 +47,19 @@ export function isKeystoneVerb(value: string): value is KeystoneVerb {
|
|
|
44
47
|
export const POOL_ID_REVOKE = KEYSTONE_VERB_REVOKE;
|
|
45
48
|
export const POOL_ID_MANAGE = KEYSTONE_VERB_MANAGE;
|
|
46
49
|
export const POOL_ID_DEFAULT = "default";
|
|
50
|
+
|
|
51
|
+
export const KEYSTONE_CONFIG_DEFAULT_SIZE = 200;
|
|
52
|
+
export const KEYSTONE_CONFIG_DEFAULT_SEQUENTIAL = 2;
|
|
53
|
+
export const KEYSTONE_CONFIG_DEFAULT_RANDOM = 2;
|
|
54
|
+
export const KEYSTONE_CONFIG_DEFAULT_BINDING = 5;
|
|
55
|
+
export const KEYSTONE_CONFIG_DEFAULT_REPLENISH_STRATEGY = KeystoneReplenishStrategy.topUp;
|
|
56
|
+
export const KEYSTONE_CONFIG_DEFAULT_HASH_ALGORITHM = HashAlgorithm.sha_256;
|
|
57
|
+
export const KEYSTONE_CONFIG_DEFAULT_HASH_ROUNDS = 1;
|
|
58
|
+
|
|
59
|
+
export const KEYSTONE_CONFIG_DEFAULT_SIZE_HIGHSECURITY = 2000;
|
|
60
|
+
export const KEYSTONE_CONFIG_DEFAULT_SEQUENTIAL_HIGHSECURITY = 5;
|
|
61
|
+
export const KEYSTONE_CONFIG_DEFAULT_RANDOM_HIGHSECURITY = 5;
|
|
62
|
+
export const KEYSTONE_CONFIG_DEFAULT_BINDING_HIGHSECURITY = 16;
|
|
63
|
+
export const KEYSTONE_CONFIG_DEFAULT_REPLENISH_STRATEGY_HIGHSECURITY = KeystoneReplenishStrategy.replaceAll;
|
|
64
|
+
export const KEYSTONE_CONFIG_DEFAULT_HASH_ALGORITHM_HIGHSECURITY = HashAlgorithm.sha_256;
|
|
65
|
+
export const KEYSTONE_CONFIG_DEFAULT_HASH_ROUNDS_HIGHSECURITY = 10;
|
|
@@ -8,10 +8,11 @@ import { getGib } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
|
|
|
8
8
|
|
|
9
9
|
import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
|
|
10
10
|
import { KEYSTONE_ATOM } from "./keystone-constants.mjs";
|
|
11
|
-
import { KeystoneData_V1, KeystoneIbGib_V1,
|
|
11
|
+
import { KeystoneData_V1, KeystoneIbGib_V1, KeystoneIb_V1, KeystoneChallengePool, DeterministicResult, KeystoneProof, KeystonePoolConfig, KeystoneReplenishStrategy, KEYSTONE_REPLENISH_STRATEGY_VALID_VALUES, KeystoneClaim, KeystoneSolution } from "./keystone-types.mjs";
|
|
12
12
|
import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
|
|
13
13
|
import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
|
|
14
14
|
import { KeystoneStrategyFactory } from "./strategy/keystone-strategy-factory.mjs";
|
|
15
|
+
import { GIB } from "@ibgib/ts-gib/dist/V1/constants.mjs";
|
|
15
16
|
|
|
16
17
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ const logalot = GLOBAL_LOG_A_LOT;
|
|
|
19
20
|
* space-delimited keystone ib containing select keystone metadata.
|
|
20
21
|
*
|
|
21
22
|
* NOTE: This must match {@link parseKeystoneIb}
|
|
22
|
-
* @see {@link
|
|
23
|
+
* @see {@link KeystoneIb_V1}
|
|
23
24
|
*/
|
|
24
25
|
export async function getKeystoneIb({
|
|
25
26
|
keystoneData,
|
|
@@ -47,13 +48,13 @@ export async function getKeystoneIb({
|
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* NOTE: This must match {@link getKeystoneIb}
|
|
50
|
-
* @see {@link
|
|
51
|
+
* @see {@link KeystoneIb_V1}
|
|
51
52
|
*/
|
|
52
53
|
export async function parseKeystoneIb({
|
|
53
54
|
ib,
|
|
54
55
|
}: {
|
|
55
56
|
ib: Ib,
|
|
56
|
-
}): Promise<
|
|
57
|
+
}): Promise<KeystoneIb_V1> {
|
|
57
58
|
const lc = `[${parseKeystoneIb.name}]`;
|
|
58
59
|
try {
|
|
59
60
|
if (logalot) { console.log(`${lc} starting... (I: 73cb6832984255ed48b2f44db6a21e25)`); }
|
|
@@ -123,7 +124,9 @@ export function getDeterministicRequirements({
|
|
|
123
124
|
const { gib } = getIbAndGib({ ibGibAddr: targetAddr });
|
|
124
125
|
if (gib) {
|
|
125
126
|
// Get required hex prefixes (e.g. 'a', 'b', 'c', '1')
|
|
126
|
-
const prefixes = gib
|
|
127
|
+
const prefixes = gib !== GIB ?
|
|
128
|
+
gib.substring(0, behavior.targetBindingChars).toLowerCase() :
|
|
129
|
+
'abc'; // arbitrary for primitive binding target ibgib
|
|
127
130
|
|
|
128
131
|
for (const char of prefixes) {
|
|
129
132
|
// Look in the Explicit Bucket
|
|
@@ -200,7 +203,7 @@ export function removeFromBindingMap(
|
|
|
200
203
|
|
|
201
204
|
/**
|
|
202
205
|
* Selects the specific pool to use for an operation based on ID, filter criteria, or verb authorization.
|
|
203
|
-
*
|
|
206
|
+
*
|
|
204
207
|
* @returns The matching KeystoneChallengePool
|
|
205
208
|
* @throws If no pool matches or if multiple pools match but one was expected.
|
|
206
209
|
*/
|
|
@@ -216,7 +219,7 @@ export function resolveTargetPool({
|
|
|
216
219
|
*/
|
|
217
220
|
poolId?: string;
|
|
218
221
|
/**
|
|
219
|
-
* Optional predicate to find a pool.
|
|
222
|
+
* Optional predicate to find a pool.
|
|
220
223
|
* Useful for finding delegates via metadata.
|
|
221
224
|
*/
|
|
222
225
|
poolFilter?: (pool: KeystoneChallengePool) => boolean;
|
|
@@ -276,7 +279,7 @@ export function resolveTargetPool({
|
|
|
276
279
|
/**
|
|
277
280
|
* Calculates the complete list of Challenge IDs to solve for a given operation.
|
|
278
281
|
* Combines Deterministic requirements (Mandatory/Binding/FIFO) with Stochastic requirements.
|
|
279
|
-
*
|
|
282
|
+
*
|
|
280
283
|
* @returns Array of unique challenge IDs.
|
|
281
284
|
*/
|
|
282
285
|
export function selectChallengeIds({
|
|
@@ -312,7 +315,7 @@ export function selectChallengeIds({
|
|
|
312
315
|
throw new Error(`Insufficient challenges for random requirement. Need ${randomCount}, have ${availableIds.length} (E: 9f7a67428515c1e82813158581898125)`);
|
|
313
316
|
}
|
|
314
317
|
// Shuffle & Pick
|
|
315
|
-
// Note: simple Math.random sort is sufficient for V1 stochastic selection
|
|
318
|
+
// Note: simple Math.random sort is sufficient for V1 stochastic selection
|
|
316
319
|
// as we are just picking from valid available options.
|
|
317
320
|
const shuffled = [...availableIds].sort(() => 0.5 - Math.random());
|
|
318
321
|
randomIds.push(...shuffled.slice(0, randomCount));
|
|
@@ -347,7 +350,7 @@ export async function generateOpaqueChallengeId({
|
|
|
347
350
|
/**
|
|
348
351
|
* Calculates the NEXT state of the Challenge Pools given a specific consumption event.
|
|
349
352
|
* Handles TopUp, ReplaceAll, Consume, and ScorchedEarth strategies.
|
|
350
|
-
*
|
|
353
|
+
*
|
|
351
354
|
* @returns The new array of KeystoneChallengePools (including the modified one).
|
|
352
355
|
*/
|
|
353
356
|
export async function applyReplenishmentStrategy({
|
|
@@ -420,7 +423,7 @@ export async function applyReplenishmentStrategy({
|
|
|
420
423
|
}
|
|
421
424
|
} else if (strategyType === KeystoneReplenishStrategy.consume) {
|
|
422
425
|
consumedIds.forEach(id => delete pool.challenges[id]);
|
|
423
|
-
} else if (strategyType === KeystoneReplenishStrategy.
|
|
426
|
+
} else if (strategyType === KeystoneReplenishStrategy.deleteAll) {
|
|
424
427
|
pool.challenges = {};
|
|
425
428
|
pool.bindingMap = {};
|
|
426
429
|
} else {
|
|
@@ -441,7 +444,7 @@ export async function applyReplenishmentStrategy({
|
|
|
441
444
|
* 3. Solves Challenges (Generates Solutions).
|
|
442
445
|
* 4. Replenishes Pool (Calculates the next state of ALL pools).
|
|
443
446
|
* 5. Constructs Proof.
|
|
444
|
-
*
|
|
447
|
+
*
|
|
445
448
|
* @returns The resulting Proof and the full list of Next Pools.
|
|
446
449
|
*/
|
|
447
450
|
export async function solveAndReplenish({
|
|
@@ -512,7 +515,7 @@ export async function solveAndReplenish({
|
|
|
512
515
|
/**
|
|
513
516
|
* Validates the transition from Prev -> Curr.
|
|
514
517
|
* Enforces Cryptography AND Behavioral Policy.
|
|
515
|
-
*
|
|
518
|
+
*
|
|
516
519
|
* @returns Array of validation error strings. Empty array means Valid.
|
|
517
520
|
*/
|
|
518
521
|
export async function validateKeystoneTransition({
|
|
@@ -525,7 +528,9 @@ export async function validateKeystoneTransition({
|
|
|
525
528
|
const lc = `[${validateKeystoneTransition.name}]`;
|
|
526
529
|
const errors: string[] = [];
|
|
527
530
|
try {
|
|
528
|
-
if (!currentIbGib) {
|
|
531
|
+
if (!currentIbGib) {
|
|
532
|
+
throw new Error(`(UNEXPECTED) currentIbGib falsy? (E: 3c0f02655fa8279e386a079ebb604b25)`);
|
|
533
|
+
}
|
|
529
534
|
if (!prevIbGib) { throw new Error(`(UNEXPECTED) prevIbGib falsy? (E: 0d07c812634d839c784f31b8848ba825)`); }
|
|
530
535
|
|
|
531
536
|
// intrinsic validation
|
|
@@ -20,7 +20,7 @@ const logalot = GLOBAL_LOG_A_LOT || true;
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Facade for managing Keystone Identities.
|
|
23
|
-
*
|
|
23
|
+
*
|
|
24
24
|
* Handles Genesis, Authorized Evolution (Signing), and Validation.
|
|
25
25
|
*/
|
|
26
26
|
export class KeystoneService_V1 {
|
|
@@ -93,9 +93,9 @@ export class KeystoneService_V1 {
|
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* Signs a claim by solving challenges from a specific pool and evolving the Keystone timeline.
|
|
96
|
-
*
|
|
96
|
+
*
|
|
97
97
|
* Uses a hybrid selection strategy: Mandatory IDs (Alice) + Sequential (FIFO) + Random (Stochastic).
|
|
98
|
-
*
|
|
98
|
+
*
|
|
99
99
|
* Supports Delegation via `poolFilter` to find specific foreign pools.
|
|
100
100
|
*/
|
|
101
101
|
async sign({
|
|
@@ -122,7 +122,7 @@ export class KeystoneService_V1 {
|
|
|
122
122
|
*/
|
|
123
123
|
poolId?: string;
|
|
124
124
|
/**
|
|
125
|
-
* Optional predicate to find a pool.
|
|
125
|
+
* Optional predicate to find a pool.
|
|
126
126
|
* Useful for finding delegates via metadata without knowing the exact ID.
|
|
127
127
|
* e.g. (p) => p.metadata?.delegate === 'Bob'
|
|
128
128
|
*/
|
|
@@ -158,7 +158,7 @@ export class KeystoneService_V1 {
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
// 3. Pay the Cost (Solve & Replenish)
|
|
161
|
-
// This helper handles the Strategy creation, Secret derivation, Solving,
|
|
161
|
+
// This helper handles the Strategy creation, Secret derivation, Solving,
|
|
162
162
|
// and the calculation of the Next state for ALL pools.
|
|
163
163
|
const { proof, nextPools } = await solveAndReplenish({
|
|
164
164
|
targetPoolId: pool.id,
|
|
@@ -196,13 +196,13 @@ export class KeystoneService_V1 {
|
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
198
|
* Validates a keystone.
|
|
199
|
-
*
|
|
199
|
+
*
|
|
200
200
|
* ## NOTES
|
|
201
|
-
*
|
|
201
|
+
*
|
|
202
202
|
* Atow (12/22/2025) this only validates the transition from Prev -> Curr.
|
|
203
|
-
*
|
|
203
|
+
*
|
|
204
204
|
* @returns Array of validation error strings. Empty array means Valid.
|
|
205
|
-
*
|
|
205
|
+
*
|
|
206
206
|
* @see {@link validateKeystoneTransition}
|
|
207
207
|
*/
|
|
208
208
|
async validate({
|
|
@@ -220,11 +220,11 @@ export class KeystoneService_V1 {
|
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
222
|
* Permanently revokes the Identity.
|
|
223
|
-
*
|
|
223
|
+
*
|
|
224
224
|
* Logic:
|
|
225
225
|
* 1. Locates the 'revoke' pool.
|
|
226
226
|
* 2. Solves required challenges to prove ownership.
|
|
227
|
-
* 3. Wipes the pool (via
|
|
227
|
+
* 3. Wipes the pool (via the delete all strategy in solveAndReplenish).
|
|
228
228
|
* 4. Sets the revocationInfo on the new frame.
|
|
229
229
|
*/
|
|
230
230
|
async revoke({
|
|
@@ -270,8 +270,9 @@ export class KeystoneService_V1 {
|
|
|
270
270
|
if (idsToSolve.length === 0) { throw new Error(`Revocation policy selected 0 challenges? Check config for pool ${pool.id}. Revocation requires proof. (E: 97e5a8356d241ae7b882db791cb1f825)`); }
|
|
271
271
|
|
|
272
272
|
// 4. Pay the Cost & Scorched Earth
|
|
273
|
-
// The revoke pool config should have '
|
|
274
|
-
// causing solveAndReplenish
|
|
273
|
+
// The revoke pool config should have 'replenishStrategy:
|
|
274
|
+
// KeystoneReplenishStrategy.deleteAll', causing solveAndReplenish
|
|
275
|
+
// to return an empty pool in nextPools.
|
|
275
276
|
const { proof, nextPools } = await solveAndReplenish({
|
|
276
277
|
targetPoolId: pool.id,
|
|
277
278
|
prevPools: prevData.challengePools,
|
|
@@ -283,7 +284,7 @@ export class KeystoneService_V1 {
|
|
|
283
284
|
// warn if nextPools contains pool.id that isn't empty (we were
|
|
284
285
|
// supposed to do "scorched earth" which empties the pool)
|
|
285
286
|
if (nextPools.find(p => p.id === pool.id && Object.keys(p.challenges).length > 0)) {
|
|
286
|
-
console.warn(`${lc} revocation pool ${pool.id} is not empty after revocation. Is the revocation pool replenish strategy set to ${KeystoneReplenishStrategy.
|
|
287
|
+
console.warn(`${lc} revocation pool ${pool.id} is not empty after revocation. Is the revocation pool replenish strategy set to ${KeystoneReplenishStrategy.deleteAll}? (W: 300c28bc8b98fc3e3c0b0d988344f825)`);
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
// 5. Construct Revocation Info
|
|
@@ -316,11 +317,11 @@ export class KeystoneService_V1 {
|
|
|
316
317
|
|
|
317
318
|
/**
|
|
318
319
|
* Structural evolution: Adds new challenge pools to the keystone.
|
|
319
|
-
*
|
|
320
|
-
* Use Case: Adding a delegate (Server) for SSO, adding a recovery key,
|
|
320
|
+
*
|
|
321
|
+
* Use Case: Adding a delegate (Server) for SSO, adding a recovery key,
|
|
321
322
|
* or rotating to a new set of pools.
|
|
322
|
-
*
|
|
323
|
-
* Requires the Master Secret to authorize the change via a pool containing
|
|
323
|
+
*
|
|
324
|
+
* Requires the Master Secret to authorize the change via a pool containing
|
|
324
325
|
* the 'manage' verb.
|
|
325
326
|
*/
|
|
326
327
|
async addPools({
|
|
@@ -332,14 +333,14 @@ export class KeystoneService_V1 {
|
|
|
332
333
|
}: {
|
|
333
334
|
latestKeystone: KeystoneIbGib_V1;
|
|
334
335
|
/**
|
|
335
|
-
* Alice's Master Secret.
|
|
336
|
+
* Alice's Master Secret.
|
|
336
337
|
* Required to solve challenges from the Admin/Manage pool to authorize this change.
|
|
337
338
|
*/
|
|
338
339
|
masterSecret: string;
|
|
339
340
|
/**
|
|
340
|
-
* The pools to add.
|
|
341
|
-
* NOTE: These are fully constructed Pool objects.
|
|
342
|
-
* If they are foreign (Bob's), Alice must have constructed them
|
|
341
|
+
* The pools to add.
|
|
342
|
+
* NOTE: These are fully constructed Pool objects.
|
|
343
|
+
* If they are foreign (Bob's), Alice must have constructed them
|
|
343
344
|
* using Bob's challenges + Her config restrictions + isForeign=true.
|
|
344
345
|
*/
|
|
345
346
|
newPools: KeystoneChallengePool[];
|
|
@@ -195,12 +195,15 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
195
195
|
|
|
196
196
|
firstOfAll(sir, async () => {
|
|
197
197
|
// Use our standard builder to get a valid config object
|
|
198
|
-
config = createStandardPoolConfig(
|
|
198
|
+
config = createStandardPoolConfig({
|
|
199
|
+
id: salt,
|
|
200
|
+
salt,
|
|
201
|
+
}) as KeystonePoolConfig_HashV1;
|
|
199
202
|
});
|
|
200
203
|
|
|
201
204
|
await respecfully(sir, 'Derivation Logic', async () => {
|
|
202
205
|
|
|
203
|
-
await
|
|
206
|
+
await ifWeMight(sir, 'derivePoolSecret with same inputs returns same output', async () => {
|
|
204
207
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
205
208
|
|
|
206
209
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
@@ -210,7 +213,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
210
213
|
iReckon(sir, secretA).asTo('secret length').isGonnaBeTruthy();
|
|
211
214
|
});
|
|
212
215
|
|
|
213
|
-
await
|
|
216
|
+
await ifWeMight(sir, 'derivePoolSecret with different master secret returns different output', async () => {
|
|
214
217
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
215
218
|
|
|
216
219
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
@@ -219,7 +222,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
219
222
|
iReckon(sir, secretA).asTo('secrets differ').not.willEqual(secretB);
|
|
220
223
|
});
|
|
221
224
|
|
|
222
|
-
await
|
|
225
|
+
await ifWeMight(sir, 'derivePoolSecret with different salt returns different output', async () => {
|
|
223
226
|
// Modify salt in a copy of config
|
|
224
227
|
const configB = { ...config, salt: "OtherPool" };
|
|
225
228
|
const strategyA = KeystoneStrategyFactory.create({ config });
|
|
@@ -234,7 +237,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
234
237
|
|
|
235
238
|
await respecfully(sir, 'Challenge/Solution Logic', async () => {
|
|
236
239
|
|
|
237
|
-
await
|
|
240
|
+
await ifWeMight(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
|
|
238
241
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
239
242
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
240
243
|
const challengeId = "a3ff7843552870fc28bef2b"; // arbitrary random challengeId
|
|
@@ -253,7 +256,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
253
256
|
iReckon(sir, isValid).asTo('valid pair should pass').isGonnaBeTrue();
|
|
254
257
|
});
|
|
255
258
|
|
|
256
|
-
await
|
|
259
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched values', async () => {
|
|
257
260
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
258
261
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
259
262
|
const challengeId = "8c994f3ed598f150e25513"; // arbitrary random challengeId
|
|
@@ -269,7 +272,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
269
272
|
iReckon(sir, isValid).asTo('tampered solution should fail').isGonnaBeFalse();
|
|
270
273
|
});
|
|
271
274
|
|
|
272
|
-
await
|
|
275
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
|
|
273
276
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
274
277
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
275
278
|
|
|
@@ -310,8 +313,11 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
310
313
|
});
|
|
311
314
|
|
|
312
315
|
await respecfully(sir, 'Genesis', async () => {
|
|
313
|
-
await
|
|
314
|
-
const config = createStandardPoolConfig(
|
|
316
|
+
await ifWeMight(sir, 'creates a valid genesis frame and persists it', async () => {
|
|
317
|
+
const config = createStandardPoolConfig({
|
|
318
|
+
id: POOL_ID_DEFAULT,
|
|
319
|
+
salt: POOL_ID_DEFAULT,
|
|
320
|
+
});
|
|
315
321
|
|
|
316
322
|
genesisKeystone = await service.genesis({
|
|
317
323
|
masterSecret,
|
|
@@ -338,7 +344,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
338
344
|
});
|
|
339
345
|
|
|
340
346
|
await respecfully(sir, 'Signing (Evolution)', async () => {
|
|
341
|
-
await
|
|
347
|
+
await ifWeMight(sir, 'evolves the keystone with a valid proof', async () => {
|
|
342
348
|
const claim: Partial<KeystoneClaim> = {
|
|
343
349
|
target: "comment 123^gib",
|
|
344
350
|
verb: "post"
|
|
@@ -368,7 +374,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
368
374
|
});
|
|
369
375
|
|
|
370
376
|
await respecfully(sir, 'Validation', async () => {
|
|
371
|
-
await
|
|
377
|
+
await ifWeMight(sir, 'validates the genesis->signed transition', async () => {
|
|
372
378
|
const errors = await service.validate({
|
|
373
379
|
prevIbGib: genesisKeystone,
|
|
374
380
|
currentIbGib: signedKeystone,
|
|
@@ -400,8 +406,13 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
400
406
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
401
407
|
|
|
402
408
|
// Setup Alice's Identity
|
|
403
|
-
const config = createStandardPoolConfig(
|
|
404
|
-
|
|
409
|
+
const config = createStandardPoolConfig({
|
|
410
|
+
id: POOL_ID_DEFAULT,
|
|
411
|
+
salt: POOL_ID_DEFAULT,
|
|
412
|
+
targetBinding: 0,
|
|
413
|
+
size: 10,
|
|
414
|
+
});
|
|
415
|
+
// config.behavior.size = 10;
|
|
405
416
|
genesisKeystone = await service.genesis({
|
|
406
417
|
masterSecret: aliceSecret,
|
|
407
418
|
configs: [config],
|
|
@@ -411,7 +422,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
411
422
|
});
|
|
412
423
|
|
|
413
424
|
await respecfully(sir, 'Wrong Secret (Forgery)', async () => {
|
|
414
|
-
await
|
|
425
|
+
await ifWeMight(sir, 'prevents creation of forged frames', async () => {
|
|
415
426
|
const claim: Partial<KeystoneClaim> = { target: "comment 123^gib", verb: "post" };
|
|
416
427
|
|
|
417
428
|
let errorCaught = false;
|
|
@@ -440,10 +451,13 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
440
451
|
});
|
|
441
452
|
|
|
442
453
|
await respecfully(sir, 'Policy Violation (Restricted Verbs)', async () => {
|
|
443
|
-
await
|
|
454
|
+
await ifWeMight(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
|
|
444
455
|
// Create a specific restricted pool config manually
|
|
445
456
|
const restrictedPoolId = "read_only_pool";
|
|
446
|
-
const restrictedConfig = createStandardPoolConfig(
|
|
457
|
+
const restrictedConfig = createStandardPoolConfig({
|
|
458
|
+
id: restrictedPoolId,
|
|
459
|
+
salt: restrictedPoolId,
|
|
460
|
+
});
|
|
447
461
|
// Manually restrict it (since Builder defaults to undefined/allow-all)
|
|
448
462
|
restrictedConfig.allowedVerbs = ['read'];
|
|
449
463
|
|
|
@@ -495,8 +509,14 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
495
509
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
496
510
|
|
|
497
511
|
// Setup Identity WITH a Revocation Pool
|
|
498
|
-
const stdConfig = createStandardPoolConfig(
|
|
499
|
-
|
|
512
|
+
const stdConfig = createStandardPoolConfig({
|
|
513
|
+
id: POOL_ID_DEFAULT,
|
|
514
|
+
salt: POOL_ID_DEFAULT,
|
|
515
|
+
});
|
|
516
|
+
const revokeConfig = createRevocationPoolConfig({
|
|
517
|
+
id: POOL_ID_REVOKE,
|
|
518
|
+
salt: POOL_ID_REVOKE,
|
|
519
|
+
}); // Special Config
|
|
500
520
|
|
|
501
521
|
genesisKeystone = await service.genesis({
|
|
502
522
|
masterSecret,
|
|
@@ -509,7 +529,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
509
529
|
await respecfully(sir, 'Revoke Lifecycle', async () => {
|
|
510
530
|
let revokedKeystone: KeystoneIbGib_V1;
|
|
511
531
|
|
|
512
|
-
await
|
|
532
|
+
await ifWeMight(sir, 'successfully creates a revocation frame', async () => {
|
|
513
533
|
revokedKeystone = await service.revoke({
|
|
514
534
|
latestKeystone: genesisKeystone,
|
|
515
535
|
masterSecret,
|
|
@@ -527,7 +547,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
527
547
|
iReckon(sir, data.revocationInfo!.proof.claim.verb).willEqual(KEYSTONE_VERB_REVOKE);
|
|
528
548
|
});
|
|
529
549
|
|
|
530
|
-
await
|
|
550
|
+
await ifWeMight(sir, 'validates the revocation frame', async () => {
|
|
531
551
|
const errors = await service.validate({
|
|
532
552
|
prevIbGib: genesisKeystone,
|
|
533
553
|
currentIbGib: revokedKeystone!,
|
|
@@ -538,7 +558,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
538
558
|
iReckon(sir, errors.length).asTo('no validation errors').willEqual(0);
|
|
539
559
|
});
|
|
540
560
|
|
|
541
|
-
await
|
|
561
|
+
await ifWeMight(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
|
|
542
562
|
const data = revokedKeystone!.data!;
|
|
543
563
|
const revokePool = data.challengePools.find(p => p.id === POOL_ID_REVOKE);
|
|
544
564
|
|
|
@@ -568,7 +588,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
568
588
|
|
|
569
589
|
// Helper to generate a "Foreign" pool (e.g. from Bob)
|
|
570
590
|
const createForeignPool = async (id: string, verbs: string[] = []): Promise<KeystoneChallengePool> => {
|
|
571
|
-
const config = createStandardPoolConfig(id);
|
|
591
|
+
const config = createStandardPoolConfig({ id, salt: id });
|
|
572
592
|
config.allowedVerbs = verbs;
|
|
573
593
|
|
|
574
594
|
// We use the factory manually here to simulate Bob doing this offline
|
|
@@ -605,7 +625,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
605
625
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
606
626
|
|
|
607
627
|
// Alice Genesis: Standard pool (allows all verbs, including 'manage')
|
|
608
|
-
const config = createStandardPoolConfig(POOL_ID_DEFAULT);
|
|
628
|
+
const config = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: POOL_ID_DEFAULT });
|
|
609
629
|
aliceKeystone = await service.genesis({
|
|
610
630
|
masterSecret: aliceSecret,
|
|
611
631
|
configs: [config],
|
|
@@ -615,7 +635,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
615
635
|
});
|
|
616
636
|
|
|
617
637
|
await respecfully(sir, 'Happy Path', async () => {
|
|
618
|
-
await
|
|
638
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
619
639
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
620
640
|
|
|
621
641
|
const updatedKeystone = await service.addPools({
|
|
@@ -654,9 +674,10 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
654
674
|
});
|
|
655
675
|
|
|
656
676
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
657
|
-
await
|
|
677
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
658
678
|
// 1. Create a restricted keystone
|
|
659
|
-
|
|
679
|
+
let id = "read_only";
|
|
680
|
+
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
660
681
|
restrictedConfig.allowedVerbs = ['read']; // No 'manage'
|
|
661
682
|
|
|
662
683
|
const restrictedKeystone = await service.genesis({
|
|
@@ -686,7 +707,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
686
707
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
687
708
|
});
|
|
688
709
|
|
|
689
|
-
await
|
|
710
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
690
711
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
691
712
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
692
713
|
|
|
@@ -725,7 +746,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
725
746
|
|
|
726
747
|
// Helper to simulate Bob creating a pool "offline" to give to Alice
|
|
727
748
|
const createForeignPool = async (id: string, verbs: string[] = []): Promise<KeystoneChallengePool> => {
|
|
728
|
-
const config = createStandardPoolConfig(id);
|
|
749
|
+
const config = createStandardPoolConfig({ id, salt: id });
|
|
729
750
|
config.allowedVerbs = verbs;
|
|
730
751
|
|
|
731
752
|
// We use the factory manually here to simulate Bob doing this on his own machine
|
|
@@ -762,7 +783,10 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
762
783
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
763
784
|
|
|
764
785
|
// Alice Genesis: Standard pool (allows all verbs, including 'manage')
|
|
765
|
-
const config = createStandardPoolConfig(
|
|
786
|
+
const config = createStandardPoolConfig({
|
|
787
|
+
id: POOL_ID_DEFAULT,
|
|
788
|
+
salt: POOL_ID_DEFAULT,
|
|
789
|
+
});
|
|
766
790
|
aliceKeystone = await service.genesis({
|
|
767
791
|
masterSecret: aliceSecret,
|
|
768
792
|
configs: [config],
|
|
@@ -772,7 +796,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
772
796
|
});
|
|
773
797
|
|
|
774
798
|
await respecfully(sir, 'Happy Path', async () => {
|
|
775
|
-
await
|
|
799
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
776
800
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
777
801
|
|
|
778
802
|
const updatedKeystone = await service.addPools({
|
|
@@ -811,9 +835,10 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
811
835
|
});
|
|
812
836
|
|
|
813
837
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
814
|
-
await
|
|
838
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
815
839
|
// 1. Create a restricted keystone (read-only)
|
|
816
|
-
|
|
840
|
+
let id = "read_only";
|
|
841
|
+
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
817
842
|
restrictedConfig.allowedVerbs = ['read']; // No 'manage'
|
|
818
843
|
|
|
819
844
|
const restrictedKeystone = await service.genesis({
|
|
@@ -843,7 +868,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
843
868
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
844
869
|
});
|
|
845
870
|
|
|
846
|
-
await
|
|
871
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
847
872
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
848
873
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
849
874
|
|
|
@@ -883,11 +908,13 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
883
908
|
let signedKeystone: KeystoneIbGib_V1;
|
|
884
909
|
|
|
885
910
|
// We use a specific hybrid config to test exact selection logic
|
|
886
|
-
const hybridConfig = createStandardPoolConfig(
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
911
|
+
const hybridConfig = createStandardPoolConfig({
|
|
912
|
+
id: salt,
|
|
913
|
+
salt,
|
|
914
|
+
// 2 FIFO + 2 Random = 4 Total per sign
|
|
915
|
+
sequential: 2, random: 2, targetBinding: 0,
|
|
916
|
+
size: 20, // Small enough to track, large enough to be random
|
|
917
|
+
}) as KeystonePoolConfig_HashV1;
|
|
891
918
|
|
|
892
919
|
firstOfAll(sir, async () => {
|
|
893
920
|
mockSpace = new MockIbGibSpace();
|
|
@@ -903,7 +930,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
903
930
|
|
|
904
931
|
await respecfully(sir, 'Proof Granularity & Math', async () => {
|
|
905
932
|
|
|
906
|
-
await
|
|
933
|
+
await ifWeMight(sir, 'generates exactly the expected number of solutions', async () => {
|
|
907
934
|
signedKeystone = await service.sign({
|
|
908
935
|
latestKeystone: genesisKeystone,
|
|
909
936
|
masterSecret: aliceSecret,
|
|
@@ -920,7 +947,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
920
947
|
iReckon(sir, solutions.length).asTo('solution count').willEqual(4);
|
|
921
948
|
});
|
|
922
949
|
|
|
923
|
-
await
|
|
950
|
+
await ifWeMight(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
|
|
924
951
|
const proof = signedKeystone.data!.proofs[0];
|
|
925
952
|
const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === salt)!;
|
|
926
953
|
|
|
@@ -949,7 +976,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
949
976
|
}
|
|
950
977
|
});
|
|
951
978
|
|
|
952
|
-
await
|
|
979
|
+
await ifWeMight(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
|
|
953
980
|
const proof = signedKeystone.data!.proofs[0];
|
|
954
981
|
const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === salt)!;
|
|
955
982
|
|
|
@@ -971,7 +998,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
971
998
|
|
|
972
999
|
await respecfully(sir, 'DTO & Serialization', async () => {
|
|
973
1000
|
|
|
974
|
-
await
|
|
1001
|
+
await ifWeMight(sir, 'survives a clone/JSON-cycle without corruption', async () => {
|
|
975
1002
|
// 1. Create a DTO (simulate network transmission/storage)
|
|
976
1003
|
// 'clone' does a JSON stringify/parse under the hood (usually) or structured clone.
|
|
977
1004
|
const dto = clone(signedKeystone);
|
|
@@ -991,7 +1018,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
991
1018
|
iReckon(sir, errors.length).asTo('DTO validation errors').willEqual(0);
|
|
992
1019
|
});
|
|
993
1020
|
|
|
994
|
-
await
|
|
1021
|
+
await ifWeMight(sir, 'ensures data contains no functions or circular refs', async () => {
|
|
995
1022
|
// A crude but effective test: ensure JSON.stringify doesn't throw
|
|
996
1023
|
// and the result is equal to the object (if we parsed it back).
|
|
997
1024
|
|
|
@@ -1005,7 +1032,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
1005
1032
|
iReckon(sir, parsedSolution).asTo('deep property survives stringify').willEqual(originalSolution);
|
|
1006
1033
|
|
|
1007
1034
|
// Ensure no extra properties were lost
|
|
1008
|
-
// FIX: JSON.stringify removes keys with 'undefined' values.
|
|
1035
|
+
// FIX: JSON.stringify removes keys with 'undefined' values.
|
|
1009
1036
|
// We must filter the original keys to match this behavior for a fair comparison.
|
|
1010
1037
|
const origKeys = Object.keys(signedKeystone.data!)
|
|
1011
1038
|
.filter(k => (signedKeystone.data as any)[k] !== undefined);
|