@ibgib/core-gib 0.1.57 → 0.1.59
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-config-builder.d.mts +12 -1
- package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
- package/dist/keystone/keystone-config-builder.mjs +58 -4
- package/dist/keystone/keystone-config-builder.mjs.map +1 -1
- package/dist/keystone/keystone-constants.d.mts +40 -5
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +39 -5
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +11 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +37 -1
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-policy-types.d.mts +23 -0
- package/dist/keystone/keystone-policy-types.d.mts.map +1 -0
- package/dist/keystone/keystone-policy-types.mjs +2 -0
- package/dist/keystone/keystone-policy-types.mjs.map +1 -0
- package/dist/sync/graft-info/graft-info-helpers.respec.mjs +8 -8
- package/dist/sync/graft-info/graft-info-helpers.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +22 -22
- package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-divergence.respec.mjs +3 -3
- package/dist/sync/sync-conflict-basic-divergence.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +6 -6
- package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-text-merge.respec.mjs +26 -26
- package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +19 -0
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +51 -1
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +4 -4
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +3 -3
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +4 -4
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts +5 -0
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +24 -2
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts +5 -0
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs +21 -3
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +13 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +40 -10
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +81 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +37 -3
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +163 -23
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts +46 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs +45 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts +30 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +68 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +324 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +85 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +332 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts +29 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts +44 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +303 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +22 -5
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +223 -27
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +9 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +41 -2
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +110 -11
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-types.d.mts +24 -0
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +0 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/sync/sync-withid.connect.respec.d.mts +12 -0
- package/dist/sync/sync-withid.connect.respec.d.mts.map +1 -0
- package/dist/sync/sync-withid.connect.respec.mjs +205 -0
- package/dist/sync/sync-withid.connect.respec.mjs.map +1 -0
- package/dist/sync/sync-withid.establish.respec.d.mts +19 -0
- package/dist/sync/sync-withid.establish.respec.d.mts.map +1 -0
- package/dist/sync/sync-withid.establish.respec.mjs +322 -0
- package/dist/sync/sync-withid.establish.respec.mjs.map +1 -0
- package/dist/sync/sync-withid.pingpong.respec.d.mts +11 -0
- package/dist/sync/sync-withid.pingpong.respec.d.mts.map +1 -0
- package/dist/sync/sync-withid.pingpong.respec.mjs +131 -0
- package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -0
- package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/package.json +4 -4
- package/src/keystone/keystone-config-builder.mts +73 -4
- package/src/keystone/keystone-constants.mts +42 -6
- package/src/keystone/keystone-helpers.mts +44 -2
- package/src/keystone/keystone-policy-types.mts +25 -0
- package/src/keystone/keystone-policy.schema.json +51 -0
- package/src/keystone/keystone-service-v1.mts +3 -3
- package/src/sync/docs/architecture.md +20 -0
- package/src/sync/docs/ping_pong_plan.md +147 -0
- package/src/sync/docs/security.md +207 -3
- package/src/sync/graft-info/graft-info-helpers.respec.mts +7 -7
- package/src/sync/sync-conflict-adv-multitimelines.respec.mts +21 -21
- package/src/sync/sync-conflict-basic-divergence.respec.mts +2 -2
- package/src/sync/sync-conflict-basic-multitimelines.respec.mts +5 -5
- package/src/sync/sync-conflict-text-merge.respec.mts +25 -25
- package/src/sync/sync-helpers.mts +51 -1
- package/src/sync/sync-innerspace-constants.respec.mts +1 -1
- package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +3 -3
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
- package/src/sync/sync-innerspace-partial-update.respec.mts +2 -2
- package/src/sync/sync-innerspace.respec.mts +3 -3
- package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +26 -2
- package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mts +23 -3
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +1 -1
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +47 -13
- package/src/sync/sync-peer/sync-peer-types.mts +87 -1
- package/src/sync/sync-peer/sync-peer-v1.mts +171 -32
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mts +68 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mts +36 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +385 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +388 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mts +35 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +345 -0
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +223 -34
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +9 -0
- package/src/sync/sync-saga-coordinator.mts +162 -6
- package/src/sync/sync-types.mts +28 -4
- package/src/sync/sync-withid.connect.respec.mts +243 -0
- package/src/sync/sync-withid.establish.respec.mts +361 -0
- package/src/sync/sync-withid.pingpong.respec.mts +161 -0
- package/src/sync/unused-identity-backup.mts.md +1 -1
- package/src/witness/space/inner-space/inner-space-v1.mts +4 -5
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts +0 -2
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts.map +0 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +0 -310
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +0 -1
- package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +0 -364
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sync-peer-websocket-sender-v1
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
6
|
+
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
7
|
+
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
8
|
+
import { KeystoneIbGib_V1 } from '../../../../keystone/keystone-types.mjs';
|
|
9
|
+
import { KeystoneService_V1 } from '../../../../keystone/keystone-service-v1.mjs';
|
|
10
|
+
import { KeystoneStrategyFactory } from '../../../../keystone/strategy/keystone-strategy-factory.mjs';
|
|
11
|
+
import { deriveSessionSecret } from '../../../sync-helpers.mjs';
|
|
12
|
+
import { SyncPeer_V1 } from '../../sync-peer-v1.mjs';
|
|
13
|
+
import { SyncSagaContextIbGib_V1 } from '../../../sync-saga-context/sync-saga-context-types.mjs';
|
|
14
|
+
import { GLOBAL_LOG_A_LOT } from '../../../../core-constants.mjs';
|
|
15
|
+
import {
|
|
16
|
+
ConnectSyncPeerWebSocketSenderOpts,
|
|
17
|
+
InitializeSyncPeerWebSocketSenderOpts,
|
|
18
|
+
SyncPeerWebSocketSenderData_V1,
|
|
19
|
+
SyncPeerWebSocketSenderRel8ns_V1,
|
|
20
|
+
SyncPeerWebSocketSenderIbGib_V1
|
|
21
|
+
} from './sync-peer-websocket-sender-types.mjs';
|
|
22
|
+
import {
|
|
23
|
+
SESSION_KEYSTONE_POLICY,
|
|
24
|
+
getConnectChallenge
|
|
25
|
+
} from '../sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
|
|
26
|
+
import { SyncWebSocketMsgType } from '../sync-peer-websocket-constants.mjs';
|
|
27
|
+
|
|
28
|
+
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* WebSocket Sender Peer implementation running in browser/native environment.
|
|
32
|
+
*/
|
|
33
|
+
export class SyncPeerWebSocketSender_V1
|
|
34
|
+
extends SyncPeer_V1<ConnectSyncPeerWebSocketSenderOpts, InitializeSyncPeerWebSocketSenderOpts>
|
|
35
|
+
implements SyncPeerWebSocketSenderIbGib_V1 {
|
|
36
|
+
|
|
37
|
+
protected override lc: string = `[${SyncPeerWebSocketSender_V1.name}]`;
|
|
38
|
+
|
|
39
|
+
override get classname(): string {
|
|
40
|
+
return SyncPeerWebSocketSender_V1.name;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public get isSocketOpen(): boolean {
|
|
44
|
+
return this.ws !== undefined && this.ws.readyState === WebSocket.OPEN;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare data: SyncPeerWebSocketSenderData_V1 | undefined;
|
|
48
|
+
declare rel8ns: SyncPeerWebSocketSenderRel8ns_V1 | undefined;
|
|
49
|
+
|
|
50
|
+
protected ws?: WebSocket;
|
|
51
|
+
protected activeResolve?: (value: SyncSagaContextIbGib_V1 | undefined) => void;
|
|
52
|
+
protected activeReject?: (reason: any) => void;
|
|
53
|
+
protected pendingPayloadsToSend: IbGib_V1[] = [];
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
initialData: SyncPeerWebSocketSenderData_V1,
|
|
57
|
+
initialRel8ns?: SyncPeerWebSocketSenderRel8ns_V1,
|
|
58
|
+
) {
|
|
59
|
+
super(initialData, initialRel8ns);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected override async preConnectCheck(opts: ConnectSyncPeerWebSocketSenderOpts): Promise<void> {
|
|
63
|
+
const lc = `${this.lc}[${this.preConnectCheck.name}]`;
|
|
64
|
+
if (!this.data?.wsUrl) {
|
|
65
|
+
throw new Error(`Missing wsUrl in peer data (E: a3b2c1d0e9f8e7d6c5b4a3f2e1d0c910)`);
|
|
66
|
+
}
|
|
67
|
+
if (!this.data?.httpEvolveUrl) {
|
|
68
|
+
throw new Error(`Missing httpEvolveUrl in peer data (E: a3b2c1d0e9f8e7d6c5b4a3f2e1d0c911)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Submits the evolved master identity (I1) and new session keystone (S) to the server's HTTP registry.
|
|
74
|
+
*/
|
|
75
|
+
protected override async postEstablishToReceiver({
|
|
76
|
+
newSenderIdentity,
|
|
77
|
+
sessionIdentity,
|
|
78
|
+
}: {
|
|
79
|
+
newSenderIdentity: KeystoneIbGib_V1;
|
|
80
|
+
sessionIdentity: KeystoneIbGib_V1;
|
|
81
|
+
}): Promise<void> {
|
|
82
|
+
const lc = `${this.lc}[${this.postEstablishToReceiver.name}]`;
|
|
83
|
+
try {
|
|
84
|
+
if (logalot) { console.log(`${lc} posting evolved keystones to ${this.data!.httpEvolveUrl}...`); }
|
|
85
|
+
if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: d642e8a9af18b532c87c6f581aa53b26)`); }
|
|
86
|
+
|
|
87
|
+
if (!this.data.httpEvolveUrl) { throw new Error(`(UNEXPECTED) this.data.httpEvolveUrl falsy? (E: 8589ddbb155914d85c09658881da2c26)`); }
|
|
88
|
+
|
|
89
|
+
const response = await fetch(this.data.httpEvolveUrl, {
|
|
90
|
+
method: 'PUT',
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
keystoneIbGib: newSenderIdentity,
|
|
96
|
+
relatedIbGibs: [sessionIdentity]
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const data = await response.json().catch(() => ({}));
|
|
102
|
+
const errorMsg = data.message || data.error || 'Unknown error';
|
|
103
|
+
throw new Error(`HTTP ${response.status} evolution post rejected. errorMsg: ${errorMsg} (E: e8a478291b88d05c68cf6b385684b826)`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (logalot) { console.log(`${lc} evolve post accepted by server.`); }
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error(`${lc} establish post failed: ${extractErrorMsg(error)}`);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Establishes the stateful WebSocket connection and performs the multi-turn cryptographic challenge connect.
|
|
115
|
+
*/
|
|
116
|
+
protected override async connectImpl(opts: ConnectSyncPeerWebSocketSenderOpts): Promise<void> {
|
|
117
|
+
const lc = `${this.lc}[${this.connectImpl.name}]`;
|
|
118
|
+
try {
|
|
119
|
+
if (logalot) { console.log(`${lc} starting...`); }
|
|
120
|
+
|
|
121
|
+
const { senderIdentity, fnSenderSecret, sagaId, localMetaspace, localSpace } = this.opts!;
|
|
122
|
+
if (!senderIdentity || !fnSenderSecret || !sagaId) {
|
|
123
|
+
throw new Error(`Missing identity parameters in peer options (E: ed49b6711ac82efcdb42b8a28c5b6826)`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 1. Solve upfront pre-filter solution
|
|
127
|
+
const senderSecret = await fnSenderSecret();
|
|
128
|
+
const sessionSecret = await deriveSessionSecret({ senderSecret, sagaId });
|
|
129
|
+
|
|
130
|
+
// Fetch session identity S that establishSessionIdentity just created
|
|
131
|
+
const sAddr_tjp = getIbGibAddr({ ibGib: senderIdentity }); // wait, S's past contains I
|
|
132
|
+
// Actually, we can get the S keystone that was returned by establishSessionIdentity.
|
|
133
|
+
// Let's resolve S from the sender localSpace using its target claim or the targetAddrs
|
|
134
|
+
const sAddr = getIbGibAddr({ ibGib: senderIdentity.rel8ns?.past ? senderIdentity : senderIdentity }); // stub: get the actual generated S
|
|
135
|
+
|
|
136
|
+
// To make sure we have the exact session identity S, we can query localSpace.
|
|
137
|
+
// Since we know the sagaId, we can walk or locate it.
|
|
138
|
+
// But wait, the coordinator calls establishSessionIdentity first, which returns the created S keystone!
|
|
139
|
+
// Let's store a reference to the sessionIdentity S on this class during establish, or lookup from localSpace.
|
|
140
|
+
// Wait, does the coordinator let the peer store S? No, but let's query the latest keystone in localSpace.
|
|
141
|
+
const senderIdentityLatestAddr =
|
|
142
|
+
await localMetaspace.getLatestAddr({ ibGib: senderIdentity, space: localSpace });
|
|
143
|
+
if (!senderIdentityLatestAddr) { throw new Error(`senderIdentityLatestAddr falsy. we should have a latest addr that is different than the sender identity at this point (done in the peer establish phase) (E: 225607318bef485d7821f82de97b6826)`); }
|
|
144
|
+
|
|
145
|
+
const resGet = await localMetaspace.get({ addrs: [senderIdentityLatestAddr], space: localSpace });
|
|
146
|
+
const I_tip = resGet.ibGibs?.[0] as KeystoneIbGib_V1;
|
|
147
|
+
|
|
148
|
+
// const targetSAddr = I_tip?.rel8ns?.past?.[0] ?? I_tip?.data?.proofs?.[0]?.claim?.target;
|
|
149
|
+
const targetSAddr = I_tip.data.proofs.at(0)?.claim.target;
|
|
150
|
+
if (!targetSAddr) {
|
|
151
|
+
throw new Error(`Could not locate session keystone target from identity (E: 5ec4882f7e5e4c33fbd00ab8b3166726)`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const resGetS = await localMetaspace.get({ addrs: [targetSAddr], space: localSpace });
|
|
155
|
+
const sessionS = resGetS.ibGibs?.[0] as KeystoneIbGib_V1;
|
|
156
|
+
if (!sessionS) {
|
|
157
|
+
throw new Error(`Session keystone not found in local space: ${targetSAddr} (E: 9ba818e6622808bb4ae749be31f80b26)`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Solve dynamic upfront picket-fence challenge
|
|
161
|
+
const connectPool =
|
|
162
|
+
(sessionS.data?.challengePools ?? [])
|
|
163
|
+
.find(p => p.id === SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID);
|
|
164
|
+
if (!connectPool) {
|
|
165
|
+
throw new Error(`Session keystone missing connect pool (E: f50968afca04b6d38ec19824ea201826)`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const { challengeId } = getConnectChallenge(sessionS);
|
|
169
|
+
const strategy = KeystoneStrategyFactory.create({ config: connectPool.config });
|
|
170
|
+
const poolSecret = await strategy.derivePoolSecret({ masterSecret: sessionSecret });
|
|
171
|
+
const solution = await strategy.generateSolution({
|
|
172
|
+
poolSecret,
|
|
173
|
+
poolId: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
|
|
174
|
+
challengeId
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// 2. Open WebSocket connection
|
|
178
|
+
const wsUrl = `${this.data!.wsUrl}?sAddr=${encodeURIComponent(targetSAddr)}&solution=${encodeURIComponent(solution.value)}`;
|
|
179
|
+
if (logalot) { console.log(`${lc} connecting to WebSocket: ${wsUrl}`); }
|
|
180
|
+
|
|
181
|
+
const ws = new WebSocket(wsUrl);
|
|
182
|
+
this.ws = ws;
|
|
183
|
+
|
|
184
|
+
return new Promise<void>((resolve, reject) => {
|
|
185
|
+
let isResolved = false;
|
|
186
|
+
|
|
187
|
+
ws.addEventListener('open', () => {
|
|
188
|
+
if (logalot) { console.log(`${lc} WebSocket opened. Awaiting challenge...`); }
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
ws.addEventListener('message', async (ev) => {
|
|
192
|
+
try {
|
|
193
|
+
const msg = JSON.parse(ev.data);
|
|
194
|
+
if (logalot) { console.log(`${lc} received frame: ${msg.type}`); }
|
|
195
|
+
|
|
196
|
+
if (msg.type === SyncWebSocketMsgType.auth_challenge_init) {
|
|
197
|
+
ws.send(JSON.stringify({
|
|
198
|
+
type: SyncWebSocketMsgType.auth_init,
|
|
199
|
+
sAddr: targetSAddr
|
|
200
|
+
}));
|
|
201
|
+
} else if (msg.type === SyncWebSocketMsgType.auth_challenge) {
|
|
202
|
+
const { challengeUuid, demandedIds } = msg;
|
|
203
|
+
if (logalot) { console.log(`${lc} solving demanded challenges: ${demandedIds.join(', ')}`); }
|
|
204
|
+
|
|
205
|
+
const keystoneService = new KeystoneService_V1();
|
|
206
|
+
const proofFrame = await keystoneService.sign({
|
|
207
|
+
latestKeystone: sessionS,
|
|
208
|
+
masterSecret: sessionSecret,
|
|
209
|
+
poolId: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
|
|
210
|
+
requiredChallengeIds: demandedIds,
|
|
211
|
+
claim: {
|
|
212
|
+
verb: SESSION_KEYSTONE_POLICY.CONNECT_POOL.VERB,
|
|
213
|
+
target: challengeUuid
|
|
214
|
+
},
|
|
215
|
+
metaspace: localMetaspace,
|
|
216
|
+
space: localSpace
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
ws.send(JSON.stringify({
|
|
220
|
+
type: SyncWebSocketMsgType.auth_proof,
|
|
221
|
+
proofFrame
|
|
222
|
+
}));
|
|
223
|
+
} else if (msg.type === SyncWebSocketMsgType.auth_ok) {
|
|
224
|
+
if (logalot) { console.log(`${lc} WebSocket connect SUCCESS!`); }
|
|
225
|
+
isResolved = true;
|
|
226
|
+
|
|
227
|
+
// Setup persistent runtime listeners
|
|
228
|
+
ws.removeEventListener('message', (() => { }) as any);
|
|
229
|
+
ws.addEventListener('message', (event) => this.handleRuntimeMessage(event));
|
|
230
|
+
|
|
231
|
+
resolve();
|
|
232
|
+
} else if (msg.type === SyncWebSocketMsgType.auth_fail) {
|
|
233
|
+
reject(new Error(`Connect failed: ${msg.message}`));
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
reject(error);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
ws.addEventListener('close', (event) => {
|
|
241
|
+
if (!isResolved) {
|
|
242
|
+
reject(new Error(`WebSocket closed before connect completed (code: ${event.code})`));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
ws.addEventListener('error', (err) => {
|
|
247
|
+
if (!isResolved) {
|
|
248
|
+
reject(new Error(`WebSocket connection error`));
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error(`${lc} connect failed: ${extractErrorMsg(error)}`);
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Handles synchronizing messages and evolved context frames during active transaction turns.
|
|
261
|
+
*/
|
|
262
|
+
protected async handleRuntimeMessage(event: MessageEvent): Promise<void> {
|
|
263
|
+
const lc = `${this.lc}[${this.handleRuntimeMessage.name}]`;
|
|
264
|
+
try {
|
|
265
|
+
const msg = JSON.parse(event.data);
|
|
266
|
+
if (logalot) { console.log(`${lc} received runtime frame: ${msg.type}`); }
|
|
267
|
+
|
|
268
|
+
if (msg.type === SyncWebSocketMsgType.sync_frame_response) {
|
|
269
|
+
const responseContext = msg.context as SyncSagaContextIbGib_V1;
|
|
270
|
+
|
|
271
|
+
// Validate and authenticate Bob's response context first
|
|
272
|
+
await this.authenticateAndValidate({ context: responseContext });
|
|
273
|
+
|
|
274
|
+
// If response has expected payloads, authorize Bob to stream them
|
|
275
|
+
const expectedPayloadAddrs = responseContext.data?.['@payloadAddrsDomain'] || [];
|
|
276
|
+
if (expectedPayloadAddrs.length > 0) {
|
|
277
|
+
this.ws!.send(JSON.stringify({
|
|
278
|
+
type: SyncWebSocketMsgType.sync_frame_response_authenticated,
|
|
279
|
+
contextAddr: getIbGibAddr({ ibGib: responseContext })
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (this.activeResolve) {
|
|
284
|
+
const resolve = this.activeResolve;
|
|
285
|
+
this.activeResolve = undefined;
|
|
286
|
+
this.activeReject = undefined;
|
|
287
|
+
resolve(responseContext);
|
|
288
|
+
}
|
|
289
|
+
} else if (msg.type === SyncWebSocketMsgType.sync_frame_authenticated) {
|
|
290
|
+
// Bob authenticated our context, stream buffered payloads
|
|
291
|
+
const payloads = this.pendingPayloadsToSend || [];
|
|
292
|
+
this.pendingPayloadsToSend = [];
|
|
293
|
+
for (const ibGib of payloads) {
|
|
294
|
+
this.ws!.send(JSON.stringify({
|
|
295
|
+
type: SyncWebSocketMsgType.domain_payload,
|
|
296
|
+
ibGib
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
} else if (msg.type === SyncWebSocketMsgType.domain_payload) {
|
|
300
|
+
const payload = msg.ibGib as IbGib_V1;
|
|
301
|
+
this.payloadIbGibsDomainReceived$.next(payload);
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error(`${lc} failed parsing runtime frame: ${extractErrorMsg(error)}`);
|
|
305
|
+
if (this.activeReject) {
|
|
306
|
+
this.activeReject(error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Serializes the transaction context and payload down the active WebSocket connection.
|
|
313
|
+
*/
|
|
314
|
+
protected override async sendContextRequest(context: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1 | undefined> {
|
|
315
|
+
const lc = `${this.lc}[${this.sendContextRequest.name}]`;
|
|
316
|
+
try {
|
|
317
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
318
|
+
throw new Error(`WebSocket is not connected or open (E: a3b2c1d0e9f8e7d6c5b4a3f2e1d0c915)`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 1. Separate payloads to send later
|
|
322
|
+
const domainPayloads = context.payloadIbGibsDomain ?? [];
|
|
323
|
+
this.pendingPayloadsToSend = [...domainPayloads];
|
|
324
|
+
|
|
325
|
+
// 2. Clone context without payloads for transport
|
|
326
|
+
const contextToSend = { ...context };
|
|
327
|
+
delete contextToSend.payloadIbGibsDomain;
|
|
328
|
+
|
|
329
|
+
// 3. Transmit the synchronizing transaction context
|
|
330
|
+
return new Promise<SyncSagaContextIbGib_V1 | undefined>((resolve, reject) => {
|
|
331
|
+
this.activeResolve = resolve;
|
|
332
|
+
this.activeReject = reject;
|
|
333
|
+
|
|
334
|
+
this.ws!.send(JSON.stringify({
|
|
335
|
+
type: SyncWebSocketMsgType.sync_frame,
|
|
336
|
+
context: contextToSend
|
|
337
|
+
}));
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`${lc} sendContextRequest failed: ${extractErrorMsg(error)}`);
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* @module sync saga context helpers
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { extractErrorMsg,
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { IbGib_V1, } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
9
|
-
import { Ib } from '@ibgib/ts-gib/dist/types.mjs';
|
|
5
|
+
import { extractErrorMsg, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
6
|
+
import { getIbGibAddr, } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
7
|
+
import { Ib, } from '@ibgib/ts-gib/dist/types.mjs';
|
|
10
8
|
import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
|
|
11
9
|
|
|
12
10
|
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
|
|
@@ -14,13 +12,15 @@ import { SYNC_SAGA_CONTEXT_ATOM } from './sync-saga-context-constants.mjs';
|
|
|
14
12
|
import { SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from '../sync-constants.mjs';
|
|
15
13
|
import {
|
|
16
14
|
SyncSagaContextData_V1, SyncSagaContextIbGib_V1, SyncSagaContextIb_V1,
|
|
17
|
-
SyncSagaContextRel8ns_V1,
|
|
18
15
|
} from './sync-saga-context-types.mjs';
|
|
19
16
|
import { IbGibSpaceAny } from '../../witness/space/space-base-v1.mjs';
|
|
20
|
-
import { getFromSpace,
|
|
21
|
-
import {
|
|
17
|
+
import { getFromSpace, getLatestAddrs, getTjpIbGib } from '../../witness/space/space-helper.mjs';
|
|
18
|
+
import { SessionGenesisFrameDetails, } from '../sync-types.mjs';
|
|
22
19
|
import { validateSyncSagaFrame } from '../sync-helpers.mjs';
|
|
23
20
|
import { isIbGibWithAtom } from '../../common/other/ibgib-helper.mjs';
|
|
21
|
+
import { KeystoneService_V1 } from '../../keystone/keystone-service-v1.mjs';
|
|
22
|
+
import { KeystoneIbGib_V1 } from '../../keystone/keystone-types.mjs';
|
|
23
|
+
import { KEYSTONE_VERB_SYNC } from '../../keystone/keystone-constants.mjs';
|
|
24
24
|
|
|
25
25
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
26
26
|
|
|
@@ -192,42 +192,231 @@ export async function validateContextDomainPayloadIbGibs({ context }: { context:
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
export async function authenticateContext({
|
|
196
|
-
}): Promise<string[]> {
|
|
197
|
-
const lc = `[${authenticateContext.name}]`;
|
|
198
|
-
try {
|
|
199
|
-
if (logalot) { console.log(`${lc} starting... (I: 3c34e8f1d6ef965f98725c88459ea926)`); }
|
|
200
|
-
console.warn(`${lc}[NAG] not thrown. not implemented right now after removing all identity-related code. (W: e5fad31cfb49eef198a189a82dbcf726)`)
|
|
201
|
-
return [];
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
204
|
-
throw error;
|
|
205
|
-
} finally {
|
|
206
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
195
|
/**
|
|
211
|
-
*
|
|
196
|
+
* "Intrinsically": This authenticates assuming we have already established that
|
|
197
|
+
* this context is a valid continuation of previous sync contexts/saga state. It
|
|
198
|
+
* does not check that the session identity contained on this context is a valid
|
|
199
|
+
* continuation.
|
|
200
|
+
*
|
|
201
|
+
* ## notes
|
|
202
|
+
*
|
|
203
|
+
* Say an attacker tries to hijack a legit sync process by capturing previous
|
|
204
|
+
* context(s). Then the attacker just either creates its own sessionIdentity
|
|
205
|
+
*
|
|
206
|
+
* ## implementation notes
|
|
207
|
+
*
|
|
208
|
+
* This is a HUGE function right now, because there are just a lot of things to
|
|
209
|
+
* check. I'm basically going through and just taking every assumption that I
|
|
210
|
+
* can think of and encoding it.
|
|
211
|
+
*
|
|
212
|
+
* We will need to refactor this at some point to neaten it up, but we should
|
|
213
|
+
* not remove sections without EXTREMELY good reasoning, as this would reduce
|
|
214
|
+
* security.
|
|
212
215
|
*/
|
|
213
|
-
export async function
|
|
216
|
+
export async function authenticateContextIntrinsically({
|
|
214
217
|
context,
|
|
215
|
-
|
|
218
|
+
space,
|
|
219
|
+
// stageInProtocol,
|
|
216
220
|
}: {
|
|
217
221
|
context: SyncSagaContextIbGib_V1,
|
|
218
|
-
|
|
222
|
+
space: IbGibSpaceAny,
|
|
223
|
+
// /**
|
|
224
|
+
// * @see {@link StageInProtocol}
|
|
225
|
+
// */
|
|
226
|
+
// stageInProtocol: StageInProtocol,
|
|
219
227
|
}): Promise<string[]> {
|
|
220
|
-
const lc = `[${
|
|
228
|
+
const lc = `[${authenticateContextIntrinsically.name}]`;
|
|
221
229
|
try {
|
|
222
|
-
if (logalot) { console.log(`${lc} starting... (I:
|
|
230
|
+
if (logalot) { console.log(`${lc} starting... (I: 3c34e8f1d6ef965f98725c88459ea926)`); }
|
|
231
|
+
|
|
232
|
+
const errors: string[] = [];
|
|
223
233
|
|
|
224
|
-
|
|
234
|
+
if (!context.data) { throw new Error(`(UNEXPECTED) context.data falsy? (E: 3e4ddd0eb4b828ad489658d88d9a6326)`); }
|
|
235
|
+
if (!context.rel8ns) { throw new Error(`(UNEXPECTED) context.rel8ns falsy? (E: 8026589d4fed69c828334ee842074326)`); }
|
|
225
236
|
|
|
226
|
-
|
|
237
|
+
const { sagaFrame, signedSessionIdentity: currSessionIdentity } = context;
|
|
238
|
+
if (!sagaFrame.data) { throw new Error(`(UNEXPECTED) sagaFrame.data falsy? (E: b61cc82d25984c92f75db74a5a855b26)`); }
|
|
239
|
+
|
|
240
|
+
// We only sign at the context level.
|
|
241
|
+
// If context has no signedSessionIdentity, skip authentication (anonymous or broker response).
|
|
242
|
+
if (!currSessionIdentity) {
|
|
243
|
+
// check the sync saga to determine if there _should_ be a session
|
|
244
|
+
// identity according to the given sagaFrame (which could be
|
|
245
|
+
// malicious, remember!!)
|
|
246
|
+
// todo: add logic to SET this property when using identity, then (and only then) remove this todo.
|
|
247
|
+
console.error(`${lc}[NAG][not thrown] sagaFrame.data.sessionIdentityTjpAddr logic needs to be added in coordinator (E: 4fc47800a1086c917a47381824280826)`);
|
|
248
|
+
if (sagaFrame.data.sessionIdentityTjpAddr) {
|
|
249
|
+
errors.push('Context has no session identity, but sync saga frame shows a session identity (sagaFrame.data.sessionIdentityTjpAddr is truthy). (E: 69dd6cdc2e1859c0f3d62958c4339826)')
|
|
250
|
+
return errors; /* <<<< returns early */
|
|
251
|
+
} else if (context.rel8ns.sessionIdentity) {
|
|
252
|
+
errors.push('Context has no signed session identity, but context.rel8ns.sessionIdentity is truthy. (E: 96a04a8a6c88ea8bf88118f89ad8e326)')
|
|
253
|
+
return errors; /* <<<< returns early */
|
|
254
|
+
} else {
|
|
255
|
+
// nothing further to authenticate
|
|
256
|
+
if (logalot) { console.log(`${lc} context has no signedSessionIdentity and sync saga frame doesn't state there should be session identity. So nothing further to authenticate - returning early with no authentication errors. (I: d708735f9a2899ee98f762b8a09ed826)`); }
|
|
257
|
+
return []; /* <<<< returns early */
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const currSessionIdentityAddr = getIbGibAddr({ ibGib: currSessionIdentity });
|
|
261
|
+
|
|
262
|
+
// ensure the context rel8ns points to a session identity
|
|
263
|
+
const prevSessionIdentityAddrs_accordingToContextRel8ns = context.rel8ns?.sessionIdentity ?? [];
|
|
264
|
+
if (prevSessionIdentityAddrs_accordingToContextRel8ns.length === 0) {
|
|
265
|
+
errors.push(`context.rel8ns.sessionIdentity is falsy/empty but context.signedSessionIdentity is present. (E: 66f906421eb2468c0b33f908a3cf2826)`);
|
|
266
|
+
return errors; /* <<<< returns early */
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (prevSessionIdentityAddrs_accordingToContextRel8ns.length > 1) {
|
|
270
|
+
errors.push(`context.rel8ns.sessionIdentity has multiple identity addrs. (E: 489428bfe6fdaa4cd885b938dc4c5826)`);
|
|
271
|
+
return errors; /* <<<< returns early */
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ensure the context session identity is the immediate past of the
|
|
275
|
+
// current session identity
|
|
276
|
+
const prevSessionIdentityAddr = prevSessionIdentityAddrs_accordingToContextRel8ns[0];
|
|
277
|
+
|
|
278
|
+
// Confirm previous session identity addr exists in space and that it is
|
|
279
|
+
// the most recent in the session keystone's timeline
|
|
280
|
+
|
|
281
|
+
const resGetLatestAddr = await getLatestAddrs({ addrs: [prevSessionIdentityAddr], space });
|
|
282
|
+
if (!resGetLatestAddr) { throw new Error(`(UNEXPECTED) resGetLatestAddr for prevSessionIdentityAddr in space (${space.ib}) falsy? (E: 7b207e5cbcec9037ea5adbe822ead826)`); }
|
|
283
|
+
if (!resGetLatestAddr.data) { throw new Error(`(UNEXPECTED) resGetLatestAddr.data for prevSessionIdentityAddr in space (${space.ib}) falsy? (E: de4eb8d730c8c4dcb59c8b9c79277826)`); }
|
|
284
|
+
if (!resGetLatestAddr.data.success) {
|
|
285
|
+
throw new Error(`(UNEXPECTED) resGetLatestAddr.data.success falsy? (E: c94298dfd9684ad6a87eb748459aa826)`);
|
|
286
|
+
}
|
|
287
|
+
const { latestAddrsMap } = resGetLatestAddr.data;
|
|
288
|
+
if (!latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddr.data.latestAddrsMap falsy? (E: 19f1fd5fe798cf2e5fa923919169d826)`); }
|
|
289
|
+
if (Object.keys(latestAddrsMap).length !== 1) {
|
|
290
|
+
throw new Error(`(UNEXPECTED) Object.keys(latestAddrsMap).length !== 1? (E: fe526a0747589c6427a8bcc86da34a26)`);
|
|
291
|
+
}
|
|
292
|
+
const prevSessionIdentityAddr_latest = latestAddrsMap[prevSessionIdentityAddr];
|
|
293
|
+
if (!prevSessionIdentityAddr_latest) {
|
|
294
|
+
errors.push(`prevSessionIdentityAddr (${prevSessionIdentityAddr}) not found in space (${space.ib}). this should have been the incoming prevSessionIdentityAddr (E: f6d042bd6b54819998653228dee34226)`);
|
|
295
|
+
return errors; /* <<<< returns early */
|
|
296
|
+
}
|
|
297
|
+
if (prevSessionIdentityAddr !== prevSessionIdentityAddr_latest) {
|
|
298
|
+
if (prevSessionIdentityAddr_latest === currSessionIdentityAddr) {
|
|
299
|
+
// this is ok? if the sender peer is calling this just to validate **before sending**, then this will be the case. If the receiver is calling this code before continuing the sync/at the start of continuing the sync, then this will not hit.
|
|
300
|
+
// debugger; // in sync saga context auth, want to know if this hits...this does hit, so my thoughts on the innerspace/sender peer seem to be correct
|
|
301
|
+
} else {
|
|
302
|
+
// debugger; // in sync saga context auth, want to know if this hits...so far this does NOT hit
|
|
303
|
+
errors.push(`context.rel8ns.sessionIdentity does not point to the most recent in the space (${space.ib}). (E: 2f8288f53c87b6aa47bd2178d9df0c26)`)
|
|
304
|
+
return errors; /* <<<< returns early */
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const resGetPrevSessionIdentity = await getFromSpace({ addr: prevSessionIdentityAddr, space });
|
|
309
|
+
if (!resGetPrevSessionIdentity.success || resGetPrevSessionIdentity.ibGibs?.length !== 1) {
|
|
310
|
+
errors.push(`could not fetch latest sender identity ${prevSessionIdentityAddr} from space (${space.ib}). (E: fd48c3e64c9fa4efd8a1f8280af18226)`);
|
|
311
|
+
return errors;
|
|
312
|
+
}
|
|
313
|
+
const prevSessionIdentity = resGetPrevSessionIdentity.ibGibs[0] as KeystoneIbGib_V1;
|
|
314
|
+
|
|
315
|
+
// get the session identity tjp, which has frame details that link back
|
|
316
|
+
// to the identity that authorized the session
|
|
317
|
+
const sessionIdentityTjp =
|
|
318
|
+
await getTjpIbGib({ ibGib: prevSessionIdentity, naive: true, space }) as KeystoneIbGib_V1 | undefined;
|
|
319
|
+
if (!sessionIdentityTjp) { throw new Error(`(UNEXPECTED) couldn't get sessionIdentityTjp in space (${space.ib})? we have already gotten the identity itself in the space, so we would expect the entire timeline to exist in it. (E: 9be0382ff1c8a0e77645ea38c096f826)`); }
|
|
320
|
+
const sessionIdentityTjpAddr = getIbGibAddr({ ibGib: sessionIdentityTjp });
|
|
321
|
+
if (sessionIdentityTjpAddr !== sagaFrame.data.sessionIdentityTjpAddr) {
|
|
322
|
+
throw new Error(`(UNEXPECTED) sessionIdentityTjpAddr !== sagaFrame.data.sessionIdentityTjpAddr? (E: c9a4ad5c2728fe38e86afc58e4abaf26)`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const sessionGenesisFrameDetails = sessionIdentityTjp.data.frameDetails as SessionGenesisFrameDetails;
|
|
326
|
+
if (!sessionGenesisFrameDetails) {
|
|
327
|
+
errors.push(`Invalid session identity tjp: sessionIdentityTjp.data.frameDetails is falsy. (E: 0187f8f804a84256281720586620b826)`);
|
|
328
|
+
return errors; /* <<<< returns early */
|
|
329
|
+
}
|
|
330
|
+
const { senderIdentityAddr, senderIdentityTjpAddr } = sessionGenesisFrameDetails;
|
|
331
|
+
if (!senderIdentityAddr) { throw new Error(`sessionGenesisFrameDetails.senderIdentityAddr falsy (E: 02a0c80a3ead9e3af8af4cf3b156e826)`); }
|
|
332
|
+
if (!senderIdentityTjpAddr) { throw new Error(`sessionGenesisFrameDetails.senderIdentityTjpAddr falsy (E: 271928090ff5dc56d4bb63d8d5c68826)`); }
|
|
333
|
+
|
|
334
|
+
const resGetLatestAddr_senderIdentity =
|
|
335
|
+
await getLatestAddrs({ addrs: [senderIdentityTjpAddr, senderIdentityAddr], space });
|
|
336
|
+
if (!resGetLatestAddr_senderIdentity) { throw new Error(`(UNEXPECTED) resGetLatestAddr_senderIdentity for prevSessionIdentityAddr in space (${space.ib}) falsy? (E: 2e4ae8083b6fb7cbb8fae2a519062926)`); }
|
|
337
|
+
if (!resGetLatestAddr_senderIdentity.data) { throw new Error(`(UNEXPECTED) resGetLatestAddr_senderIdentity.data for prevSessionIdentityAddr in space (${space.ib}) falsy? (E: 2e231850c2a898cc282b4b2841056826)`); }
|
|
338
|
+
if (!resGetLatestAddr_senderIdentity.data.success) {
|
|
339
|
+
throw new Error(`(UNEXPECTED) resGetLatestAddr_senderIdentity.data.success falsy? (E: e93508f03e0475925875b00746ffd826)`);
|
|
340
|
+
}
|
|
341
|
+
const { latestAddrsMap: latestAddrsMap_senderIdentity } = resGetLatestAddr_senderIdentity.data;
|
|
342
|
+
if (!latestAddrsMap_senderIdentity) { throw new Error(`(UNEXPECTED) resGetLatestAddr_senderIdentity.data.latestAddrsMap falsy? (E: 87a91e3f9968ad9ba79cdfe8cd878326)`); }
|
|
343
|
+
if (Object.keys(latestAddrsMap_senderIdentity).length !== 2 && senderIdentityTjpAddr !== senderIdentityAddr) {
|
|
344
|
+
throw new Error(`(UNEXPECTED) Object.keys(latestAddrsMap_senderIdentity).length !== 2 && senderIdentityTjpAddr !== senderIdentityAddr? (E: fe46bd584853d8e1e8e2d11f52012826)`);
|
|
345
|
+
}
|
|
346
|
+
// these two should be the same, we're just confirming that they're both
|
|
347
|
+
// on the same timeline.
|
|
348
|
+
const senderIdentityTjpAddr_latest = latestAddrsMap_senderIdentity[senderIdentityTjpAddr];
|
|
349
|
+
const senderIdentityAddr_latest = latestAddrsMap_senderIdentity[senderIdentityAddr];
|
|
350
|
+
if (!senderIdentityAddr_latest) { throw new Error(`(UNEXPECTED) senderIdentityAddr_latest falsy? (E: e151798ae2e9241578d09948937c4b26)`); }
|
|
351
|
+
if (senderIdentityTjpAddr_latest !== senderIdentityAddr_latest) {
|
|
352
|
+
throw new Error(`senderIdentityTjpAddr_latest !== senderIdentityAddr_latest (E: 52478a1053589e72665031a853cc1826)`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ATOW, we're only allowing a single sync to occur on an identity at
|
|
356
|
+
// any given time (which makes sense). We also are assuming that the
|
|
357
|
+
// sender identity is not doing anything ELSE at this time, which in
|
|
358
|
+
// the (far) future may change. So the user couldn't edit their primary
|
|
359
|
+
// identity's profile, description, etc., while the sync is in progress.
|
|
360
|
+
// This may ultimately be asking too much though. But for now, we'll
|
|
361
|
+
// enforce that the latest senderIdentity addr should be that addr that
|
|
362
|
+
// authorized the session keystone.
|
|
363
|
+
// if (senderIdentityAddr_latest !== senderIdentityAddr) {
|
|
364
|
+
// errors.push(`The senderIdentityAddr referenced in the session keystone's genesis frameDetails (${senderIdentityAddr}) is DIFFERENT than the latest sender identity addr (${senderIdentityAddr_latest}). This means that the sender has done something besides the current sync operation, which isn't supported at this time. (E: a02598271b48cbeb584e45abde121826)`);
|
|
365
|
+
// return errors; /* <<<< returns early */
|
|
366
|
+
// }
|
|
367
|
+
|
|
368
|
+
// now we confirm the other direction: sender identity should have been
|
|
369
|
+
// signed with "sync" verb and targeting the sessionIdentity tjp
|
|
370
|
+
// (genesis) frame.
|
|
371
|
+
const resGetSenderIdentity_latest = await getFromSpace({
|
|
372
|
+
addr: senderIdentityAddr_latest,
|
|
373
|
+
space,
|
|
374
|
+
});
|
|
375
|
+
if (!resGetSenderIdentity_latest.success || resGetSenderIdentity_latest.ibGibs?.length !== 1) {
|
|
376
|
+
errors.push(`could not fetch latest sender identity ${prevSessionIdentityAddr} from space (${space.ib}). (E: 3565ff0ed458f5a2384c40b16e849826)`);
|
|
377
|
+
return errors; /* <<<< returns early */
|
|
378
|
+
}
|
|
379
|
+
const senderIdentity_latest = resGetSenderIdentity_latest.ibGibs[0] as KeystoneIbGib_V1;
|
|
380
|
+
if (!senderIdentity_latest.data.proofs) {
|
|
381
|
+
errors.push(`Invalid sender identity. Proofs empty/falsy. (E: ebf488853061614d2b5b137828119526)`);
|
|
382
|
+
return errors; /* <<<< returns early */
|
|
383
|
+
}
|
|
384
|
+
const syncClaim = senderIdentity_latest.data.proofs.find(p =>
|
|
385
|
+
p.claim.verb === KEYSTONE_VERB_SYNC
|
|
386
|
+
)?.claim;
|
|
387
|
+
if (!syncClaim) {
|
|
388
|
+
errors.push(`Most recent senderIdentity has no proof whose claim.verb === ${KEYSTONE_VERB_SYNC}. (E: b0f488ecccbbfe43d9a0b7c8a29d7826)`);
|
|
389
|
+
return errors; /* <<<< returns early */
|
|
390
|
+
}
|
|
391
|
+
if (syncClaim.target !== sessionIdentityTjpAddr) {
|
|
392
|
+
errors.push(`Most recent sender identity claim has claim.verb === ${KEYSTONE_VERB_SYNC} but DOES NOT target expected session identity addr ${prevSessionIdentityAddr}. (E: 3e7f18d99848969be8586423d5ccb826)`);
|
|
393
|
+
return errors;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const keystoneSvc = new KeystoneService_V1();
|
|
397
|
+
const transitionErrors = await keystoneSvc.validate({
|
|
398
|
+
currentIbGib: currSessionIdentity,
|
|
399
|
+
prevIbGib: prevSessionIdentity,
|
|
400
|
+
});
|
|
401
|
+
if (transitionErrors.length > 0) {
|
|
402
|
+
errors.push(`Invalid session identity transition: ${transitionErrors.join(', ')} (E: da1c81c6d3c86aec3254f48fe7514226)`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// we have a valid keystone evolution/signing, but was it specifically
|
|
406
|
+
// for this incoming context? verify that the signing targets context.
|
|
407
|
+
const contextAddr = getIbGibAddr({ ibGib: context });
|
|
408
|
+
const targetsThisContext =
|
|
409
|
+
currSessionIdentity.data?.proofs?.some(p => p.claim.target === contextAddr);
|
|
410
|
+
if (!targetsThisContext) {
|
|
411
|
+
errors.push(`Session identity signature does not target current context ibgib (${contextAddr}). (E: acae68938c287178c878d1b88bebb826)`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return errors;
|
|
227
415
|
} catch (error) {
|
|
228
|
-
|
|
229
|
-
|
|
416
|
+
const emsg = `${lc} ${extractErrorMsg(error)}`;
|
|
417
|
+
console.error(emsg);
|
|
418
|
+
return [`authentication produced an error: ${emsg} (E: 45e014b82af81993d936611ca6fc4d26)`];
|
|
230
419
|
} finally {
|
|
231
420
|
if (logalot) { console.log(`${lc} complete.`); }
|
|
232
421
|
}
|
|
233
|
-
}
|
|
422
|
+
}
|
|
@@ -8,6 +8,7 @@ import { IbGibData_V1, IbGibRel8ns_V1, IbGib_V1 } from '@ibgib/ts-gib/dist/V1/ty
|
|
|
8
8
|
import { SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from '../sync-constants.mjs';
|
|
9
9
|
import { SyncIbGib_V1 } from '../sync-types.mjs';
|
|
10
10
|
import { SYNC_SAGA_CONTEXT_ATOM } from './sync-saga-context-constants.mjs';
|
|
11
|
+
import { KeystoneIbGib_V1 } from '../../keystone/keystone-types.mjs';
|
|
11
12
|
|
|
12
13
|
export interface SyncSagaContextIb_V1 {
|
|
13
14
|
atom: typeof SYNC_SAGA_CONTEXT_ATOM;
|
|
@@ -48,6 +49,10 @@ export interface SyncSagaContextRel8ns_V1 extends IbGibRel8ns_V1 {
|
|
|
48
49
|
*/
|
|
49
50
|
sagaFrame: IbGibAddr[];
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Ephemeral session identity genesis address to confirm S_genesis.
|
|
54
|
+
*/
|
|
55
|
+
sessionIdentity?: IbGibAddr[];
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
/**
|
|
@@ -70,4 +75,8 @@ export interface SyncSagaContextIbGib_V1 extends IbGib_V1<SyncSagaContextData_V1
|
|
|
70
75
|
*/
|
|
71
76
|
sagaFrame: SyncIbGib_V1;
|
|
72
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Evolved session identity frame signed by Alice targeting this context.
|
|
80
|
+
*/
|
|
81
|
+
signedSessionIdentity?: KeystoneIbGib_V1;
|
|
73
82
|
}
|