@ibgib/core-gib 0.1.58 → 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.
Files changed (91) hide show
  1. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -1
  2. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +6 -2
  3. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -1
  4. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +1 -1
  5. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +1 -1
  6. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  7. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +6 -10
  8. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  9. package/dist/sync/sync-peer/sync-peer-types.d.mts +13 -1
  10. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  11. package/dist/sync/sync-peer/sync-peer-v1.d.mts +8 -4
  12. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  13. package/dist/sync/sync-peer/sync-peer-v1.mjs +78 -25
  14. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  15. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts +46 -0
  16. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts.map +1 -0
  17. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs +45 -0
  18. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs.map +1 -0
  19. package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-types.d.mts +2 -2
  20. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts.map +1 -0
  21. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs.map +1 -0
  22. package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-v1.d.mts +10 -8
  23. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -0
  24. package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-v1.mjs +102 -58
  25. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -0
  26. package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-websocket-peer-helpers.d.mts +3 -3
  27. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -0
  28. package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-websocket-peer-helpers.mjs +6 -6
  29. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -0
  30. package/dist/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-types.d.mts +1 -1
  31. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts.map +1 -0
  32. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs.map +1 -0
  33. package/dist/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-v1.d.mts +6 -4
  34. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +1 -0
  35. package/dist/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-v1.mjs +46 -25
  36. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -0
  37. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +22 -5
  38. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  39. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +223 -27
  40. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  41. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +9 -0
  42. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  43. package/dist/sync/sync-saga-coordinator.d.mts +6 -1
  44. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  45. package/dist/sync/sync-saga-coordinator.mjs +49 -11
  46. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  47. package/dist/sync/sync-types.d.mts +24 -0
  48. package/dist/sync/sync-types.d.mts.map +1 -1
  49. package/dist/sync/sync-types.mjs +0 -1
  50. package/dist/sync/sync-types.mjs.map +1 -1
  51. package/dist/sync/sync-withid.connect.respec.mjs +3 -3
  52. package/dist/sync/sync-withid.connect.respec.mjs.map +1 -1
  53. package/dist/sync/sync-withid.pingpong.respec.d.mts +11 -0
  54. package/dist/sync/sync-withid.pingpong.respec.d.mts.map +1 -0
  55. package/dist/sync/sync-withid.pingpong.respec.mjs +131 -0
  56. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -0
  57. package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
  58. package/dist/witness/space/inner-space/inner-space-v1.mjs +1 -1
  59. package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
  60. package/package.json +1 -1
  61. package/src/sync/docs/ping_pong_plan.md +147 -0
  62. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +6 -2
  63. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +1 -1
  64. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +10 -13
  65. package/src/sync/sync-peer/sync-peer-types.mts +18 -1
  66. package/src/sync/sync-peer/sync-peer-v1.mts +81 -35
  67. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mts +68 -0
  68. package/src/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-types.mts +2 -2
  69. package/src/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-v1.mts +116 -68
  70. package/src/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-websocket-peer-helpers.mts +8 -8
  71. package/src/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-types.mts +1 -1
  72. package/src/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-v1.mts +51 -27
  73. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +223 -34
  74. package/src/sync/sync-saga-context/sync-saga-context-types.mts +9 -0
  75. package/src/sync/sync-saga-coordinator.mts +79 -7
  76. package/src/sync/sync-types.mts +28 -4
  77. package/src/sync/sync-withid.connect.respec.mts +3 -3
  78. package/src/sync/sync-withid.pingpong.respec.mts +161 -0
  79. package/src/witness/space/inner-space/inner-space-v1.mts +4 -5
  80. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts.map +0 -1
  81. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs.map +0 -1
  82. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +0 -1
  83. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +0 -1
  84. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +0 -1
  85. package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +0 -1
  86. package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts.map +0 -1
  87. package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs.map +0 -1
  88. package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +0 -1
  89. package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +0 -1
  90. /package/dist/sync/sync-peer/{sync-peer-websocket-receiver → sync-peer-websocket/sync-peer-websocket-receiver}/sync-peer-websocket-receiver-types.mjs +0 -0
  91. /package/dist/sync/sync-peer/{sync-peer-websocket-sender → sync-peer-websocket/sync-peer-websocket-sender}/sync-peer-websocket-sender-types.mjs +0 -0
@@ -5,7 +5,7 @@ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
5
5
  import {
6
6
  SyncPeerData_V1, SyncPeerRel8ns_V1, SyncPeerWitness,
7
7
  InitializeSyncPeerOpts, ConnectSyncPeerOpts
8
- } from '../sync-peer-types.mjs';
8
+ } from '../../sync-peer-types.mjs';
9
9
 
10
10
  /**
11
11
  * Data for the SyncPeerWebSocketSender witness.
@@ -5,13 +5,13 @@
5
5
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
6
6
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
7
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';
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
15
  import {
16
16
  ConnectSyncPeerWebSocketSenderOpts,
17
17
  InitializeSyncPeerWebSocketSenderOpts,
@@ -23,6 +23,7 @@ import {
23
23
  SESSION_KEYSTONE_POLICY,
24
24
  getConnectChallenge
25
25
  } from '../sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
26
+ import { SyncWebSocketMsgType } from '../sync-peer-websocket-constants.mjs';
26
27
 
27
28
  const logalot = GLOBAL_LOG_A_LOT || true;
28
29
 
@@ -49,6 +50,7 @@ export class SyncPeerWebSocketSender_V1
49
50
  protected ws?: WebSocket;
50
51
  protected activeResolve?: (value: SyncSagaContextIbGib_V1 | undefined) => void;
51
52
  protected activeReject?: (reason: any) => void;
53
+ protected pendingPayloadsToSend: IbGib_V1[] = [];
52
54
 
53
55
  constructor(
54
56
  initialData: SyncPeerWebSocketSenderData_V1,
@@ -191,12 +193,12 @@ export class SyncPeerWebSocketSender_V1
191
193
  const msg = JSON.parse(ev.data);
192
194
  if (logalot) { console.log(`${lc} received frame: ${msg.type}`); }
193
195
 
194
- if (msg.type === 'auth-challenge-init') {
196
+ if (msg.type === SyncWebSocketMsgType.auth_challenge_init) {
195
197
  ws.send(JSON.stringify({
196
- type: 'auth-init',
198
+ type: SyncWebSocketMsgType.auth_init,
197
199
  sAddr: targetSAddr
198
200
  }));
199
- } else if (msg.type === 'auth-challenge') {
201
+ } else if (msg.type === SyncWebSocketMsgType.auth_challenge) {
200
202
  const { challengeUuid, demandedIds } = msg;
201
203
  if (logalot) { console.log(`${lc} solving demanded challenges: ${demandedIds.join(', ')}`); }
202
204
 
@@ -215,10 +217,10 @@ export class SyncPeerWebSocketSender_V1
215
217
  });
216
218
 
217
219
  ws.send(JSON.stringify({
218
- type: 'auth-proof',
220
+ type: SyncWebSocketMsgType.auth_proof,
219
221
  proofFrame
220
222
  }));
221
- } else if (msg.type === 'auth-ok') {
223
+ } else if (msg.type === SyncWebSocketMsgType.auth_ok) {
222
224
  if (logalot) { console.log(`${lc} WebSocket connect SUCCESS!`); }
223
225
  isResolved = true;
224
226
 
@@ -227,7 +229,7 @@ export class SyncPeerWebSocketSender_V1
227
229
  ws.addEventListener('message', (event) => this.handleRuntimeMessage(event));
228
230
 
229
231
  resolve();
230
- } else if (msg.type === 'auth-fail') {
232
+ } else if (msg.type === SyncWebSocketMsgType.auth_fail) {
231
233
  reject(new Error(`Connect failed: ${msg.message}`));
232
234
  }
233
235
  } catch (error) {
@@ -257,21 +259,44 @@ export class SyncPeerWebSocketSender_V1
257
259
  /**
258
260
  * Handles synchronizing messages and evolved context frames during active transaction turns.
259
261
  */
260
- protected handleRuntimeMessage(event: MessageEvent): void {
262
+ protected async handleRuntimeMessage(event: MessageEvent): Promise<void> {
261
263
  const lc = `${this.lc}[${this.handleRuntimeMessage.name}]`;
262
264
  try {
263
265
  const msg = JSON.parse(event.data);
264
266
  if (logalot) { console.log(`${lc} received runtime frame: ${msg.type}`); }
265
267
 
266
- if (msg.type === 'sync-frame-response') {
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
+
267
283
  if (this.activeResolve) {
268
- const responseContext = msg.context as SyncSagaContextIbGib_V1;
269
284
  const resolve = this.activeResolve;
270
285
  this.activeResolve = undefined;
271
286
  this.activeReject = undefined;
272
287
  resolve(responseContext);
273
288
  }
274
- } else if (msg.type === 'domain-payload') {
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) {
275
300
  const payload = msg.ibGib as IbGib_V1;
276
301
  this.payloadIbGibsDomainReceived$.next(payload);
277
302
  }
@@ -293,23 +318,22 @@ export class SyncPeerWebSocketSender_V1
293
318
  throw new Error(`WebSocket is not connected or open (E: a3b2c1d0e9f8e7d6c5b4a3f2e1d0c915)`);
294
319
  }
295
320
 
296
- // 1. Push any outgoing payload domain ibgibs down the pipe
321
+ // 1. Separate payloads to send later
297
322
  const domainPayloads = context.payloadIbGibsDomain ?? [];
298
- for (const ibGib of domainPayloads) {
299
- this.ws.send(JSON.stringify({
300
- type: 'domain-payload',
301
- ibGib
302
- }));
303
- }
323
+ this.pendingPayloadsToSend = [...domainPayloads];
324
+
325
+ // 2. Clone context without payloads for transport
326
+ const contextToSend = { ...context };
327
+ delete contextToSend.payloadIbGibsDomain;
304
328
 
305
- // 2. Transmit the synchronizing transaction context
329
+ // 3. Transmit the synchronizing transaction context
306
330
  return new Promise<SyncSagaContextIbGib_V1 | undefined>((resolve, reject) => {
307
331
  this.activeResolve = resolve;
308
332
  this.activeReject = reject;
309
333
 
310
334
  this.ws!.send(JSON.stringify({
311
- type: 'sync-frame',
312
- context
335
+ type: SyncWebSocketMsgType.sync_frame,
336
+ context: contextToSend
313
337
  }));
314
338
  });
315
339
 
@@ -2,11 +2,9 @@
2
2
  * @module sync saga context helpers
3
3
  */
4
4
 
5
- import { extractErrorMsg, getTimestamp } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
6
- import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
7
- import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
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, putInSpace, registerNewIbGib } from '../../witness/space/space-helper.mjs';
21
- import { SyncIbGib_V1, SyncSagaFrameDependencyGraph } from '../sync-types.mjs';
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
- * move to sync-peer-helpers.mts as a pure function?
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 authorizeContext({
216
+ export async function authenticateContextIntrinsically({
214
217
  context,
215
- fullSagaHistory
218
+ space,
219
+ // stageInProtocol,
216
220
  }: {
217
221
  context: SyncSagaContextIbGib_V1,
218
- fullSagaHistory: SyncSagaFrameDependencyGraph[],
222
+ space: IbGibSpaceAny,
223
+ // /**
224
+ // * @see {@link StageInProtocol}
225
+ // */
226
+ // stageInProtocol: StageInProtocol,
219
227
  }): Promise<string[]> {
220
- const lc = `[${authorizeContext.name}]`;
228
+ const lc = `[${authenticateContextIntrinsically.name}]`;
221
229
  try {
222
- if (logalot) { console.log(`${lc} starting... (I: 48c918b41ceec0cd489ca3b8819e6826)`); }
230
+ if (logalot) { console.log(`${lc} starting... (I: 3c34e8f1d6ef965f98725c88459ea926)`); }
231
+
232
+ const errors: string[] = [];
223
233
 
224
- console.error(`${lc} NAG ERROR (NOT THROWN): not implemented. authorize business logic (v1 must have this, but later when we are working on admin vs. student)(E: bc3a78f2dab18ab64c36d055a4b50526)`);
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
- return [];
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
- console.error(`${lc} ${extractErrorMsg(error)}`);
229
- throw error;
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
  }