@ibgib/core-gib 0.1.64 → 0.1.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/keystone/keystone-helpers.d.mts +8 -0
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +74 -4
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +85 -18
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +178 -22
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +780 -340
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +15 -2
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/keystone-types.mjs +2 -0
- package/dist/keystone/keystone-types.mjs.map +1 -1
- package/dist/keystone/policy/profiles/profile-domain.json +2 -46
- package/dist/keystone/policy/profiles/profile-sync.json +2 -16
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +27 -4
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +230 -135
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/keystone-helpers.mts +89 -4
- package/src/keystone/keystone-service-v1.mts +262 -34
- package/src/keystone/keystone-service-v1.respec.mts +792 -376
- package/src/keystone/keystone-types.mts +16 -1
- package/src/keystone/policy/profiles/profile-domain.json +2 -46
- package/src/keystone/policy/profiles/profile-sync.json +2 -16
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +294 -131
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +1 -1
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
DelegateKeystoneInfo,
|
|
21
21
|
ClaimDetails,
|
|
22
22
|
ClaimDetails_AddPool,
|
|
23
|
+
ClaimDetails_RemovePool,
|
|
23
24
|
ClaimDetails_ReplacePool,
|
|
24
25
|
ClaimDetails_ChangePassword
|
|
25
26
|
} from "./keystone-types.mjs";
|
|
@@ -468,6 +469,13 @@ export async function solveAndReplenish({
|
|
|
468
469
|
|
|
469
470
|
const strategy = KeystoneStrategyFactory.create({ config: pool.config });
|
|
470
471
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
472
|
+
|
|
473
|
+
// Verify secret correctness before solving
|
|
474
|
+
const isSecretCorrect = await verifyPoolSecret({ pool, signingSecret: masterSecret });
|
|
475
|
+
if (!isSecretCorrect) {
|
|
476
|
+
throw new Error(`Crypto Violation: Invalid signing secret for pool: ${pool.id} (E: 8a4c2b1d3e5f6a7b8c9d0e1f2a3b4c5f)`);
|
|
477
|
+
}
|
|
478
|
+
|
|
471
479
|
const solutions: KeystoneSolution[] = [];
|
|
472
480
|
|
|
473
481
|
// 1. Solve
|
|
@@ -877,6 +885,8 @@ export async function validateKeystoneTransition({
|
|
|
877
885
|
}
|
|
878
886
|
|
|
879
887
|
// Claim details verification (transition validation)
|
|
888
|
+
const signingPoolIds = new Set(currData.proofs.map(p => p.solutions?.[0]?.poolId).filter(Boolean));
|
|
889
|
+
|
|
880
890
|
for (const proof of currData.proofs) {
|
|
881
891
|
const claim = proof.claim;
|
|
882
892
|
if (claim?.details) {
|
|
@@ -918,11 +928,12 @@ export async function validateKeystoneTransition({
|
|
|
918
928
|
errors.push(`Existing pool ${prevPool.id} was removed during add-pool transition. (E: 5fa2a8cb28c9b60e68994511a5825)`);
|
|
919
929
|
continue;
|
|
920
930
|
}
|
|
921
|
-
// Verify configuration
|
|
931
|
+
// Verify configuration did not change
|
|
922
932
|
if (JSON.stringify(prevPool.config) !== JSON.stringify(currPool.config)) {
|
|
923
933
|
errors.push(`Existing pool config ${prevPool.id} was mutated during add-pool transition. (E: 6fa2a8cb28c9b60e68994511a5825)`);
|
|
924
934
|
}
|
|
925
|
-
|
|
935
|
+
// Verify challenges did not change, unless this was the signing pool used to authorize the transition
|
|
936
|
+
if (!signingPoolIds.has(prevPool.id) && JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
926
937
|
errors.push(`Existing pool challenges ${prevPool.id} were mutated during add-pool transition. (E: 7fa2a8cb28c9b60e68994511a5825)`);
|
|
927
938
|
}
|
|
928
939
|
}
|
|
@@ -959,10 +970,52 @@ export async function validateKeystoneTransition({
|
|
|
959
970
|
if (JSON.stringify(prevPool.config) !== JSON.stringify(currPool.config)) {
|
|
960
971
|
errors.push(`Unrelated pool config ${prevPool.id} was mutated during replace-pool. (E: bfa2a8cb28c9b60e68994511a5825)`);
|
|
961
972
|
}
|
|
962
|
-
if (JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
973
|
+
if (!signingPoolIds.has(prevPool.id) && JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
963
974
|
errors.push(`Unrelated pool challenges ${prevPool.id} were mutated during replace-pool. (E: cfa2a8cb28c9b60e68994511a5825)`);
|
|
964
975
|
}
|
|
965
976
|
}
|
|
977
|
+
} else if (type === 'remove-pool') {
|
|
978
|
+
const removeInfo = info as ClaimDetails_RemovePool;
|
|
979
|
+
if (!removeInfo || !Array.isArray(removeInfo.remove) || removeInfo.remove.length === 0) {
|
|
980
|
+
errors.push(`details type is '${type}' but payload is invalid. (E: b7a2a8cb28c9b60e68994511a5825)`);
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const removeSet = new Set(removeInfo.remove);
|
|
985
|
+
|
|
986
|
+
// Verify that the set of pool IDs in current equals previous minus the removed pools
|
|
987
|
+
const prevIds = new Set(prevData.challengePools.map(p => p.id));
|
|
988
|
+
const currIds = new Set(currData.challengePools.map(p => p.id));
|
|
989
|
+
|
|
990
|
+
for (const id of removeInfo.remove) {
|
|
991
|
+
if (!prevIds.has(id)) {
|
|
992
|
+
errors.push(`ClaimDetails_RemovePool lists pool ${id} but it is missing in the previous keystone. (E: c7a2a8cb28c9b60e68994511a5825)`);
|
|
993
|
+
}
|
|
994
|
+
if (currIds.has(id)) {
|
|
995
|
+
errors.push(`ClaimDetails_RemovePool lists pool ${id} but it is still present in the evolved keystone. (E: d7a2a8cb28c9b60e68994511a5825)`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Verify size
|
|
1000
|
+
if (currData.challengePools.length !== prevData.challengePools.length - removeInfo.remove.length) {
|
|
1001
|
+
errors.push(`Structural mismatch: expected ${prevData.challengePools.length - removeInfo.remove.length} pools, found ${currData.challengePools.length}. (E: e7a2a8cb28c9b60e68994511a5825)`);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Verify all remaining pools are unmodified
|
|
1005
|
+
for (const prevPool of prevData.challengePools) {
|
|
1006
|
+
if (removeSet.has(prevPool.id)) continue;
|
|
1007
|
+
const currPool = currData.challengePools.find(p => p.id === prevPool.id);
|
|
1008
|
+
if (!currPool) {
|
|
1009
|
+
errors.push(`Pool ${prevPool.id} was unexpectedly removed. (E: f7a2a8cb28c9b60e68994511a5825)`);
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (JSON.stringify(prevPool.config) !== JSON.stringify(currPool.config)) {
|
|
1013
|
+
errors.push(`Remaining pool config ${prevPool.id} was mutated during remove-pool. (E: 07a2a8cb28c9b60e68994511a5825)`);
|
|
1014
|
+
}
|
|
1015
|
+
if (!signingPoolIds.has(prevPool.id) && JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
1016
|
+
errors.push(`Remaining pool challenges ${prevPool.id} were mutated during remove-pool. (E: 17a2a8cb28c9b60e68994511a5825)`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
966
1019
|
|
|
967
1020
|
} else if (type === 'change-password') {
|
|
968
1021
|
const changeInfo = info as ClaimDetails_ChangePassword;
|
|
@@ -1004,7 +1057,7 @@ export async function validateKeystoneTransition({
|
|
|
1004
1057
|
if (JSON.stringify(prevPool.config) !== JSON.stringify(currPool.config)) {
|
|
1005
1058
|
errors.push(`Unrelated pool config ${prevPool.id} was mutated during change-password. (E: 02b2a8cb28c9b60e68994511a5825)`);
|
|
1006
1059
|
}
|
|
1007
|
-
if (JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
1060
|
+
if (!signingPoolIds.has(prevPool.id) && JSON.stringify(prevPool.challenges) !== JSON.stringify(currPool.challenges)) {
|
|
1008
1061
|
errors.push(`Unrelated pool challenges ${prevPool.id} were mutated during change-password. (E: 03b2a8cb28c9b60e68994511a5825)`);
|
|
1009
1062
|
}
|
|
1010
1063
|
}
|
|
@@ -1488,4 +1541,36 @@ export async function rotatePoolChallenges({
|
|
|
1488
1541
|
...(prevPool.isForeign !== undefined ? { isForeign: prevPool.isForeign } : {}),
|
|
1489
1542
|
...(prevPool.metadata ? { metadata: prevPool.metadata } : {})
|
|
1490
1543
|
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* Pure helper to verify if a candidate signing secret (passphrase) is correct for a specific challenge pool.
|
|
1548
|
+
* Does not mutate any state.
|
|
1549
|
+
*/
|
|
1550
|
+
export async function verifyPoolSecret({
|
|
1551
|
+
pool,
|
|
1552
|
+
signingSecret
|
|
1553
|
+
}: {
|
|
1554
|
+
pool: KeystoneChallengePool;
|
|
1555
|
+
signingSecret: string;
|
|
1556
|
+
}): Promise<boolean> {
|
|
1557
|
+
const challengeIds = Object.keys(pool.challenges);
|
|
1558
|
+
if (challengeIds.length === 0) return false;
|
|
1559
|
+
|
|
1560
|
+
try {
|
|
1561
|
+
const firstId = challengeIds[0];
|
|
1562
|
+
const challenge = pool.challenges[firstId];
|
|
1563
|
+
|
|
1564
|
+
const strategy = KeystoneStrategyFactory.create({ config: pool.config });
|
|
1565
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: signingSecret });
|
|
1566
|
+
const solution = await strategy.generateSolution({
|
|
1567
|
+
poolSecret,
|
|
1568
|
+
poolId: pool.id,
|
|
1569
|
+
challengeId: firstId
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
return await strategy.validateSolution({ solution, challenge });
|
|
1573
|
+
} catch {
|
|
1574
|
+
return false;
|
|
1575
|
+
}
|
|
1491
1576
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
KeystoneData_V1, KeystoneIbGib_V1, KeystonePoolConfig, KeystoneClaim,
|
|
9
9
|
KeystoneChallengePool, KeystoneReplenishStrategy, KeystoneRevocationInfo,
|
|
10
10
|
DelegateKeystoneStatus, KeystoneClaimType,
|
|
11
|
-
ClaimDetails_AddPool, ClaimDetails_ReplacePool, ClaimDetails_ChangePassword
|
|
11
|
+
ClaimDetails_AddPool, ClaimDetails_RemovePool, ClaimDetails_ReplacePool, ClaimDetails_ChangePassword
|
|
12
12
|
} from './keystone-types.mjs';
|
|
13
13
|
import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
|
|
14
14
|
import { createStandardPoolConfig } from './keystone-config-builder.mjs';
|
|
@@ -24,13 +24,14 @@ import {
|
|
|
24
24
|
generateOpaqueChallengeId, resolveTargetPool, selectChallengeIds,
|
|
25
25
|
solveAndReplenish, validateKeystoneTransition,
|
|
26
26
|
validateGenesisKeystone, validateKeystoneGraph,
|
|
27
|
-
generatePoolChallenges, rotatePoolChallenges
|
|
27
|
+
generatePoolChallenges, rotatePoolChallenges, verifyPoolSecret
|
|
28
28
|
} from './keystone-helpers.mjs';
|
|
29
29
|
import { getLatestAddrs } from '../witness/space/space-helper.mjs';
|
|
30
30
|
import { IbGibSpaceAny } from '../witness/space/space-base-v1.mjs';
|
|
31
31
|
import { MetaspaceService } from '../witness/space/metaspace/metaspace-types.mjs';
|
|
32
32
|
import { FlatIbGibGraph } from '../common/other/graph-types.mjs';
|
|
33
33
|
import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
|
|
34
|
+
import { deriveDelegateSecret } from './strategy/hash-reveal-v1/hash-reveal-v1.mjs';
|
|
34
35
|
|
|
35
36
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
36
37
|
|
|
@@ -796,6 +797,128 @@ export class KeystoneService_V1 {
|
|
|
796
797
|
}
|
|
797
798
|
}
|
|
798
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Structural evolution: Removes challenge pools from the keystone.
|
|
802
|
+
*
|
|
803
|
+
* Use Case: Unlinking a custodian or delegate SSO pool.
|
|
804
|
+
*
|
|
805
|
+
* Requires the Master Secret to authorize the change via a pool containing
|
|
806
|
+
* the 'manage' verb (the administrative manage pool).
|
|
807
|
+
*/
|
|
808
|
+
async removePools({
|
|
809
|
+
latestKeystone,
|
|
810
|
+
masterSecret,
|
|
811
|
+
poolIds,
|
|
812
|
+
metaspace,
|
|
813
|
+
space,
|
|
814
|
+
}: {
|
|
815
|
+
latestKeystone: KeystoneIbGib_V1;
|
|
816
|
+
/**
|
|
817
|
+
* Alice's Master Secret.
|
|
818
|
+
* Required to solve challenges from the Admin/Manage pool to authorize this change.
|
|
819
|
+
*/
|
|
820
|
+
masterSecret: string;
|
|
821
|
+
/**
|
|
822
|
+
* The IDs of the pools to remove.
|
|
823
|
+
*/
|
|
824
|
+
poolIds: string[];
|
|
825
|
+
metaspace: MetaspaceService;
|
|
826
|
+
space: IbGibSpaceAny;
|
|
827
|
+
}): Promise<KeystoneIbGib_V1> {
|
|
828
|
+
const lc = `${this.lc}[${this.removePools.name}]`;
|
|
829
|
+
try {
|
|
830
|
+
if (logalot) { console.log(`${lc} starting...`); }
|
|
831
|
+
|
|
832
|
+
if (!latestKeystone.data) { throw new Error(`(UNEXPECTED) latestKeystone.data falsy? (E: 8334c8faed128166a999d428c7805b25)`); }
|
|
833
|
+
const prevData = latestKeystone.data;
|
|
834
|
+
|
|
835
|
+
if (prevData.revocationInfo) { throw new Error(`Keystone has been revoked. Cannot remove pools. (E: 9599f8f51c78d722252ddb2894fdbe25)`); }
|
|
836
|
+
|
|
837
|
+
if (poolIds.length === 0) { throw new Error(`No pool IDs provided to remove. (E: 7599f8f51c78d722252ddb2894fdbe25)`); }
|
|
838
|
+
|
|
839
|
+
// 1. Identify Authority (Resolve Admin Pool)
|
|
840
|
+
const adminPool = resolveTargetPool({
|
|
841
|
+
pools: prevData.challengePools,
|
|
842
|
+
verb: KEYSTONE_VERB_MANAGE,
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
if (logalot) { console.log(`${lc} Authorized via pool: ${adminPool.id}`); }
|
|
846
|
+
|
|
847
|
+
// 2. Construct the Management Claim with typed details
|
|
848
|
+
const target = getIbGibAddr({ ibGib: latestKeystone });
|
|
849
|
+
const claim: Partial<KeystoneClaim> = {
|
|
850
|
+
verb: KEYSTONE_VERB_MANAGE,
|
|
851
|
+
target,
|
|
852
|
+
details: {
|
|
853
|
+
type: KeystoneClaimType.remove_pool,
|
|
854
|
+
info: { remove: poolIds } as ClaimDetails_RemovePool
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// 3. Calculate Costs
|
|
859
|
+
const idsToSolve = await selectChallengeIds({
|
|
860
|
+
pool: adminPool,
|
|
861
|
+
targetAddr: target
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
// 4. Pay the Cost (Solve & Replenish)
|
|
865
|
+
const { proof, nextPools: replenishedExistingPools } = await solveAndReplenish({
|
|
866
|
+
targetPoolId: adminPool.id,
|
|
867
|
+
prevPools: prevData.challengePools,
|
|
868
|
+
masterSecret,
|
|
869
|
+
challengeIds: idsToSolve,
|
|
870
|
+
claim,
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// 5. Mutate Structure (Remove the pools)
|
|
874
|
+
const removeSet = new Set(poolIds);
|
|
875
|
+
const finalPools = replenishedExistingPools.filter(p => !removeSet.has(p.id));
|
|
876
|
+
|
|
877
|
+
// Verify that we actually removed the expected count of pools
|
|
878
|
+
if (finalPools.length !== replenishedExistingPools.length - poolIds.length) {
|
|
879
|
+
const missingIds = poolIds.filter(id => !replenishedExistingPools.some(p => p.id === id));
|
|
880
|
+
throw new Error(`Cannot remove pool. Pool IDs not found in keystone: ${missingIds.join(', ')} (E: 8a4c2b1d3e5f6a7b8c9d0e1f2a3b4c5e)`);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// 6. Construct New Data
|
|
884
|
+
const n = (prevData.n ?? 0) + 1;
|
|
885
|
+
let checkpointDetails: any | undefined;
|
|
886
|
+
if (n % KEYSTONE_CHECKPOINT_FREQUENCY === 0) {
|
|
887
|
+
const currentFrameView: KeystoneIbGib_V1 = {
|
|
888
|
+
...latestKeystone,
|
|
889
|
+
data: { ...prevData, challengePools: finalPools }
|
|
890
|
+
};
|
|
891
|
+
checkpointDetails = await this.getAggregateDetails({
|
|
892
|
+
latestKeystone: currentFrameView,
|
|
893
|
+
metaspace,
|
|
894
|
+
space
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const newData: KeystoneData_V1 = {
|
|
899
|
+
challengePools: finalPools,
|
|
900
|
+
proofs: [proof],
|
|
901
|
+
checkpointDetails,
|
|
902
|
+
...(prevData.delegates ? { delegates: prevData.delegates } : {}),
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
// 7. Commit
|
|
906
|
+
const newKeystone = await evolvePersistAndRegisterKeystone({
|
|
907
|
+
prevIbGib: latestKeystone,
|
|
908
|
+
newData,
|
|
909
|
+
metaspace,
|
|
910
|
+
space
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
return newKeystone;
|
|
914
|
+
} catch (error) {
|
|
915
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
916
|
+
throw error;
|
|
917
|
+
} finally {
|
|
918
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
799
922
|
/**
|
|
800
923
|
* Consolidates the state of a Keystone identity across its history.
|
|
801
924
|
* Walks back until it hits a checkpoint or genesis.
|
|
@@ -938,19 +1061,19 @@ export class KeystoneService_V1 {
|
|
|
938
1061
|
/**
|
|
939
1062
|
* Replaces a challenge pool (such as manage or custodian-manage) with a new pool,
|
|
940
1063
|
* authorizing the transition via the specified signing pool.
|
|
941
|
-
*
|
|
1064
|
+
*
|
|
942
1065
|
* @param latestKeystone The current tip frame of the keystone timeline.
|
|
943
1066
|
* - Visibility: Public (Public Merkle frame, safe for logs/ledger).
|
|
944
1067
|
* - Generator: Client/Server (Constructed locally and synced).
|
|
945
|
-
*
|
|
1068
|
+
*
|
|
946
1069
|
* @param newPool The fully constructed new pool to place in the next frame.
|
|
947
1070
|
* - Visibility: Public (Public configuration and public challenges, safe for ledger).
|
|
948
1071
|
* - Generator: Client/Server (Generated dynamically by the party proposing the update).
|
|
949
|
-
*
|
|
1072
|
+
*
|
|
950
1073
|
* @param signingSecret The master secret or derived KDF secret of the pool authorizing the change.
|
|
951
1074
|
* - Visibility: Private (Confidential secret credential. Must never go over the wire).
|
|
952
1075
|
* - Generator: Client/Server (Known only to the signing party).
|
|
953
|
-
*
|
|
1076
|
+
*
|
|
954
1077
|
* @param signingPoolId The ID of the pool used to authorize the transition (defaults to 'manage').
|
|
955
1078
|
* - Visibility: Public (Non-sensitive configuration string).
|
|
956
1079
|
* - Generator: Client/Server (From pool configuration).
|
|
@@ -1064,28 +1187,17 @@ export class KeystoneService_V1 {
|
|
|
1064
1187
|
/**
|
|
1065
1188
|
* Evolve the keystone by rotating the primary user manage pool (`manage`) to a new master secret (passphrase).
|
|
1066
1189
|
* This creates a new frame signed by the specified `signingPoolId` using `signingSecret`.
|
|
1067
|
-
*
|
|
1068
|
-
* Supports recovery (signing via `custodian-manage` using the custodian secret) and normal rotation
|
|
1069
|
-
* (signing via `manage` using the old password).
|
|
1070
|
-
*
|
|
1071
|
-
* @param
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
* @param newMasterSecret The new passphrase/master secret to set for the user's primary pools.
|
|
1076
|
-
* - Visibility: Private (Confidential secret credential. Must never go over the wire).
|
|
1077
|
-
* - Generator: Client (From user input in settings).
|
|
1078
|
-
*
|
|
1079
|
-
* @param signingSecret The secret of the pool authorizing this change (either old passphrase or derived custodian secret).
|
|
1080
|
-
* - Visibility: Private (Confidential secret. Must never go over the wire).
|
|
1081
|
-
* - Generator: Client/Server (Known only to the signing party).
|
|
1082
|
-
*
|
|
1083
|
-
* @param signingPoolId The ID of the pool used to authorize the rotation (defaults to 'manage').
|
|
1084
|
-
* - Visibility: Public (Non-sensitive ID string).
|
|
1085
|
-
* - Generator: Client/Server (From pool configuration).
|
|
1190
|
+
*
|
|
1191
|
+
* Supports recovery (signing via `custodian-manage` using the custodian secret) and normal rotation
|
|
1192
|
+
* (signing via `manage` using the old password). **I DONT THINK THIS IS POSSIBLE. WE WOULD NEED CUSTODIAN MANAGE POOLS ON ALL THE DELEGATES AS WELL**
|
|
1193
|
+
*
|
|
1194
|
+
* @param signingSecret
|
|
1195
|
+
*
|
|
1196
|
+
* @param signingPoolId
|
|
1086
1197
|
*/
|
|
1087
1198
|
async changePassword({
|
|
1088
|
-
|
|
1199
|
+
keystoneIbGib,
|
|
1200
|
+
keystoneAddr,
|
|
1089
1201
|
newMasterSecret,
|
|
1090
1202
|
signingSecret,
|
|
1091
1203
|
signingPoolId = POOL_ID_MANAGE,
|
|
@@ -1094,21 +1206,82 @@ export class KeystoneService_V1 {
|
|
|
1094
1206
|
metaspace,
|
|
1095
1207
|
space,
|
|
1096
1208
|
}: {
|
|
1097
|
-
|
|
1209
|
+
/**
|
|
1210
|
+
* The keystone IBGIB whose password we are changing. Does NOT have to
|
|
1211
|
+
* be the tip, as we will get the latest keystone in the implementation
|
|
1212
|
+
* of this function.
|
|
1213
|
+
* - Visibility: Public (Public Merkle frame, safe for ledger).
|
|
1214
|
+
* - Generator: Client/Server (Constructed locally and synced).
|
|
1215
|
+
*
|
|
1216
|
+
* NOTE: You can provide {@link keystoneIbGib} or {@link keystoneAddr},
|
|
1217
|
+
* but either way the latest keystone ibgib will be gotten first.
|
|
1218
|
+
*/
|
|
1219
|
+
keystoneIbGib?: KeystoneIbGib_V1;
|
|
1220
|
+
/**
|
|
1221
|
+
* The keystone ADDRESS whose password we are changing. Does NOT have to
|
|
1222
|
+
* be the tip, as we will get the latest keystone in the implementation
|
|
1223
|
+
* of this function.
|
|
1224
|
+
* - Visibility: Public (Public Merkle frame, safe for ledger).
|
|
1225
|
+
* - Generator: Client/Server (Constructed locally and synced).
|
|
1226
|
+
*
|
|
1227
|
+
* NOTE: You can provide {@link keystoneIbGib} or {@link keystoneAddr},
|
|
1228
|
+
* but either way the latest keystone ibgib will be gotten first.
|
|
1229
|
+
*/
|
|
1230
|
+
keystoneAddr?: IbGibAddr;
|
|
1231
|
+
/**
|
|
1232
|
+
* The new passphrase/master secret to set for the user's primary pools.
|
|
1233
|
+
* - Visibility: Private (Confidential secret credential. Must never go
|
|
1234
|
+
* over the wire).
|
|
1235
|
+
* - Generator: Client (From user input in settings).
|
|
1236
|
+
*/
|
|
1098
1237
|
newMasterSecret: string;
|
|
1238
|
+
/**
|
|
1239
|
+
* The secret of the pool authorizing this change (either old passphrase
|
|
1240
|
+
* or derived custodian secret).
|
|
1241
|
+
* - Visibility: Private (Confidential secret. Never over the wire).
|
|
1242
|
+
* - Generator: Client/Server (Known only to the signing party).
|
|
1243
|
+
*/
|
|
1099
1244
|
signingSecret: string;
|
|
1245
|
+
/**
|
|
1246
|
+
* The ID of the pool used to authorize the rotation. Defaults to
|
|
1247
|
+
* "manage" for user password changes, or if rotating custodian, should
|
|
1248
|
+
* be the custodian's corresponding manage pool.
|
|
1249
|
+
* - Visibility: Public (Non-sensitive ID string).
|
|
1250
|
+
* - Generator: Client/Server (From pool configuration).
|
|
1251
|
+
*/
|
|
1100
1252
|
signingPoolId?: string;
|
|
1253
|
+
/**
|
|
1254
|
+
* If provided, password will only be changed for these pools.
|
|
1255
|
+
* NOTE: **delegate passwords will NOT be changed if poolIds are
|
|
1256
|
+
* explicitly provided.**
|
|
1257
|
+
*/
|
|
1101
1258
|
poolIds?: string[];
|
|
1102
1259
|
frameDetails?: any;
|
|
1103
1260
|
metaspace: MetaspaceService;
|
|
1104
1261
|
space: IbGibSpaceAny;
|
|
1105
|
-
}): Promise<KeystoneIbGib_V1> {
|
|
1262
|
+
}): Promise<{ newKeystone: KeystoneIbGib_V1, newDelegates?: KeystoneIbGib_V1[] }> {
|
|
1106
1263
|
const lc = `${this.lc}[${this.changePassword.name}]`;
|
|
1107
1264
|
try {
|
|
1108
|
-
if (logalot) { console.log(`${lc} starting
|
|
1109
|
-
|
|
1110
|
-
if (!
|
|
1111
|
-
if (
|
|
1265
|
+
if (logalot) { console.log(`${lc} starting... (I: 9e3ff83ebd74511f9dad784a1020d926)`); }
|
|
1266
|
+
|
|
1267
|
+
if (!keystoneIbGib && !keystoneAddr) { throw new Error(`Either keystoneIbGib or keystoneAddr required. (E: d1b8c8cb75f2c022284e69b8c30af826)`); }
|
|
1268
|
+
if (keystoneIbGib && keystoneAddr) {
|
|
1269
|
+
// double check addr matches ibgib
|
|
1270
|
+
const checkAddr = getIbGibAddr({ ibGib: keystoneIbGib });
|
|
1271
|
+
if (checkAddr !== keystoneAddr) { throw new Error(`(UNEXPECTED) both keystoneIbGib (addr: ${checkAddr}) and keystoneAddr (${keystoneAddr}) provided, but the addrs are unequal? Note that only one is required for this function, but if both are provided the addrs must match exactly. (E: c5574f7e353a48b0bba94a1862af4e26)`); }
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
keystoneAddr ??= getIbGibAddr({ ibGib: keystoneIbGib });
|
|
1275
|
+
|
|
1276
|
+
const latestKeystone = await this.getLatestKeystone({
|
|
1277
|
+
addr: keystoneAddr,
|
|
1278
|
+
metaspace,
|
|
1279
|
+
space,
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
if (!latestKeystone.data) { throw new Error(`latestKeystone.data falsy (E: 22e498eb8308a33e68b812d8d3905826)`); }
|
|
1283
|
+
if (latestKeystone.data.revocationInfo) { throw new Error(`Keystone has been revoked. Cannot change password. (E: 726298617e8508b3c83b5e985a03e826)`); }
|
|
1284
|
+
|
|
1112
1285
|
|
|
1113
1286
|
const prevPools = latestKeystone.data.challengePools;
|
|
1114
1287
|
const signingPool = prevPools.find(p => p.id === signingPoolId);
|
|
@@ -1198,19 +1371,74 @@ export class KeystoneService_V1 {
|
|
|
1198
1371
|
proofs: [proof],
|
|
1199
1372
|
frameDetails,
|
|
1200
1373
|
checkpointDetails,
|
|
1201
|
-
...(latestKeystone.data.delegates ? { delegates: latestKeystone.data.delegates } :
|
|
1374
|
+
...(latestKeystone.data.delegates ? { delegates: latestKeystone.data.delegates } : undefined),
|
|
1202
1375
|
};
|
|
1203
1376
|
|
|
1204
1377
|
// 8. Commit
|
|
1205
|
-
|
|
1378
|
+
const newKeystoneIbGib = await evolvePersistAndRegisterKeystone({
|
|
1206
1379
|
prevIbGib: latestKeystone,
|
|
1207
1380
|
newData,
|
|
1208
1381
|
metaspace,
|
|
1209
1382
|
space
|
|
1210
1383
|
});
|
|
1384
|
+
|
|
1385
|
+
// update delegates' passwords also, **BUT ONLY IF NOT DOING
|
|
1386
|
+
// SPECIFIC POOLS**.
|
|
1387
|
+
const newDelegates: KeystoneIbGib_V1[] = [];
|
|
1388
|
+
if ((poolIds ?? []).length === 0) {
|
|
1389
|
+
const delegateSigningSecret = await deriveDelegateSecret({ masterSecret: signingSecret });
|
|
1390
|
+
const delegateNewMasterSecret = await deriveDelegateSecret({ masterSecret: newMasterSecret });
|
|
1391
|
+
const delegateTjpAddrs = Object.keys(latestKeystone.data.delegates ?? {});
|
|
1392
|
+
for (const delegateTjpAddr of delegateTjpAddrs) {
|
|
1393
|
+
const { newKeystone: newDelegate, newDelegates: newDelegatesSecondOrder } = await this.changePassword({
|
|
1394
|
+
keystoneAddr: delegateTjpAddr,
|
|
1395
|
+
metaspace,
|
|
1396
|
+
space,
|
|
1397
|
+
newMasterSecret: delegateNewMasterSecret,
|
|
1398
|
+
signingSecret: delegateSigningSecret,
|
|
1399
|
+
signingPoolId,
|
|
1400
|
+
poolIds: undefined,
|
|
1401
|
+
frameDetails,
|
|
1402
|
+
});
|
|
1403
|
+
if (newDelegatesSecondOrder && newDelegatesSecondOrder.length > 0) {
|
|
1404
|
+
throw new Error(`(UNEXPECTED) Delegate keystone has its own delegate(s)? ATOW (07/20/2026) second order delegates are not supported. (E: de45a7b7d9b8f61f6e58158269acba26)`);
|
|
1405
|
+
}
|
|
1406
|
+
newDelegates.push(newDelegate);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
return { newKeystone: newKeystoneIbGib, newDelegates: newDelegates.length > 0 ? newDelegates : undefined };
|
|
1211
1411
|
} catch (error) {
|
|
1212
1412
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1213
1413
|
throw error;
|
|
1414
|
+
} finally {
|
|
1415
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* Verifies if a candidate signing secret (passphrase) is correct for a specific challenge pool.
|
|
1421
|
+
* Does not mutate any state.
|
|
1422
|
+
*/
|
|
1423
|
+
async verifySigningSecret({
|
|
1424
|
+
keystoneIbGib,
|
|
1425
|
+
signingSecret,
|
|
1426
|
+
poolId
|
|
1427
|
+
}: {
|
|
1428
|
+
keystoneIbGib: KeystoneIbGib_V1;
|
|
1429
|
+
signingSecret: string;
|
|
1430
|
+
poolId: string;
|
|
1431
|
+
}): Promise<boolean> {
|
|
1432
|
+
const lc = `${this.lc}[${this.verifySigningSecret.name}]`;
|
|
1433
|
+
try {
|
|
1434
|
+
if (!keystoneIbGib.data) return false;
|
|
1435
|
+
const pool = keystoneIbGib.data.challengePools.find(p => p.id === poolId);
|
|
1436
|
+
if (!pool) return false;
|
|
1437
|
+
|
|
1438
|
+
return await verifyPoolSecret({ pool, signingSecret });
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
console.warn(`${lc} Exception during verification: ${extractErrorMsg(error)}`);
|
|
1441
|
+
return false;
|
|
1214
1442
|
}
|
|
1215
1443
|
}
|
|
1216
1444
|
}
|