core-3nweb-client-lib 0.49.6 → 0.49.8

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 (87) hide show
  1. package/build/api-defs/asmail.d.ts +1 -0
  2. package/build/api-defs/files.d.ts +11 -5
  3. package/build/core/asmail/delivery/common.d.ts +8 -28
  4. package/build/core/asmail/delivery/common.js +1 -47
  5. package/build/core/asmail/delivery/index.js +3 -3
  6. package/build/core/asmail/delivery/per-recipient-wip.js +3 -2
  7. package/build/core/asmail/inbox/index.d.ts +8 -32
  8. package/build/core/asmail/inbox/index.js +120 -170
  9. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.d.ts +11 -0
  10. package/build/{lib-client/xsp-fs/util/for-arrays.js → core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.js} +7 -13
  11. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.d.ts +33 -0
  12. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.js +72 -0
  13. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.d.ts +28 -0
  14. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.js +477 -0
  15. package/build/core/asmail/inbox/msg-indexing/index.d.ts +12 -26
  16. package/build/core/asmail/inbox/msg-indexing/index.js +47 -68
  17. package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
  18. package/build/core/asmail/inbox/msg-on-disk.js +2 -24
  19. package/build/core/asmail/index.js +8 -7
  20. package/build/core/asmail/msg/common.d.ts +0 -1
  21. package/build/core/asmail/msg/common.js +1 -1
  22. package/build/core/index.js +7 -4
  23. package/build/core/keyring/common.d.ts +1 -0
  24. package/build/core/keyring/common.js +2 -1
  25. package/build/core/keyring/index.d.ts +7 -44
  26. package/build/core/keyring/index.js +88 -222
  27. package/build/core/keyring/introductory-keys/published-intro-key.d.ts +17 -0
  28. package/build/core/keyring/introductory-keys/published-intro-key.js +190 -0
  29. package/build/core/keyring/peer-keys/dataset/v1/json-files.d.ts +105 -0
  30. package/build/core/keyring/peer-keys/dataset/v1/json-files.js +89 -0
  31. package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +43 -0
  32. package/build/core/keyring/peer-keys/dataset/v2/logs.js +116 -0
  33. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +73 -0
  34. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +543 -0
  35. package/build/core/keyring/peer-keys/index.d.ts +24 -0
  36. package/build/core/keyring/peer-keys/index.js +208 -0
  37. package/build/core/storage/synced/obj-files.js +10 -3
  38. package/build/core/storage/synced/obj-status.d.ts +1 -1
  39. package/build/core/storage/synced/obj-status.js +8 -2
  40. package/build/core/storage/synced/storage.d.ts +4 -4
  41. package/build/core/storage/system-folders/apps-data.d.ts +0 -1
  42. package/build/core/storage/system-folders/apps-data.js +2 -6
  43. package/build/lib-client/asmail/recipient.d.ts +1 -0
  44. package/build/lib-client/asmail/recipient.js +11 -0
  45. package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
  46. package/build/lib-client/fs-utils/fs-sync-utils.js +70 -13
  47. package/build/lib-client/xsp-fs/folder-node.js +13 -3
  48. package/build/lib-client/xsp-fs/fs.js +3 -1
  49. package/build/lib-client/xsp-fs/node-in-fs.d.ts +1 -0
  50. package/build/lib-client/xsp-fs/node-in-fs.js +7 -2
  51. package/build/lib-client/xsp-fs/node-persistence.js +1 -3
  52. package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -2
  53. package/build/lib-common/dataset-sync/per-device-json-array-logs.d.ts +58 -0
  54. package/build/lib-common/dataset-sync/per-device-json-array-logs.js +432 -0
  55. package/build/lib-common/dataset-sync/single-file.d.ts +10 -0
  56. package/build/lib-common/dataset-sync/single-file.js +77 -0
  57. package/build/lib-common/number-line.d.ts +10 -0
  58. package/build/lib-common/number-line.js +66 -0
  59. package/build/lib-common/objs-on-disk/obj-version-file.d.ts +1 -1
  60. package/build/lib-common/objs-on-disk/obj-version-file.js +3 -3
  61. package/build/lib-common/rng-def.d.ts +1 -0
  62. package/build/lib-common/rng-def.js +8 -0
  63. package/build/lib-sqlite-on-3nstorage/for-sqlite.d.ts +38 -0
  64. package/build/lib-sqlite-on-3nstorage/for-sqlite.js +162 -0
  65. package/build/lib-sqlite-on-3nstorage/index.d.ts +18 -5
  66. package/build/lib-sqlite-on-3nstorage/index.js +49 -26
  67. package/build/tests/caps-api/file-sink-checks/splice.js +72 -1
  68. package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +27 -1
  69. package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +12 -3
  70. package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +16 -12
  71. package/build/tests/computer.3nweb.core/keyrings.js +113 -28
  72. package/build/tests/jasmine.js +2 -2
  73. package/build/tests/units/number-line.js +9 -9
  74. package/package.json +2 -1
  75. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.d.ts +0 -52
  76. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.js +0 -333
  77. package/build/core/asmail/inbox/msg-indexing/sql-indexing.d.ts +0 -4
  78. package/build/core/asmail/inbox/msg-indexing/sql-indexing.js +0 -319
  79. package/build/core/keyring/correspondent-keys.d.ts +0 -149
  80. package/build/core/keyring/correspondent-keys.js +0 -403
  81. package/build/core/keyring/id-to-email-map.d.ts +0 -38
  82. package/build/core/keyring/id-to-email-map.js +0 -105
  83. package/build/core/keyring/keyring-storage.d.ts +0 -9
  84. package/build/core/keyring/keyring-storage.js +0 -54
  85. package/build/core/keyring/published-intro-key.d.ts +0 -45
  86. package/build/core/keyring/published-intro-key.js +0 -200
  87. package/build/lib-client/xsp-fs/util/for-arrays.d.ts +0 -1
@@ -0,0 +1,543 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2026 3NSoft Inc.
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU General Public License as published by the Free Software
7
+ Foundation, either version 3 of the License, or (at your option) any later
8
+ version.
9
+
10
+ This program is distributed in the hope that it will be useful, but
11
+ WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ See the GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License along with
16
+ this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.makePeerKeysDB = makePeerKeysDB;
20
+ const canonical_address_1 = require("../../../../../lib-common/canonical-address");
21
+ const for_sqlite_1 = require("../../../../../lib-sqlite-on-3nstorage/for-sqlite");
22
+ const lib_sqlite_on_3nstorage_1 = require("../../../../../lib-sqlite-on-3nstorage");
23
+ const json_files_1 = require("../v1/json-files");
24
+ const common_1 = require("../../../common");
25
+ const rng_def_1 = require("../../../../../lib-common/rng-def");
26
+ const buffer_utils_1 = require("../../../../../lib-common/buffer-utils");
27
+ const ecma_nacl_1 = require("ecma-nacl");
28
+ const number_line_1 = require("../../../../../lib-common/number-line");
29
+ const logs_1 = require("./logs");
30
+ const queryToCreatePeerKeysDbV2 = [
31
+ // XXX although we put foreign key into create clauses, current library binary is ignoring it even with pragma on
32
+ // yet, we follow connection accordingly
33
+ // `--sql
34
+ // PRAGMA foreign_keys = ON
35
+ // `,
36
+ `--sql
37
+ CREATE TABLE peers (
38
+ peerCAddr TEXT NOT NULL PRIMARY KEY,
39
+ peerAddr TEXT NOT NULL
40
+ )
41
+ `,
42
+ // sending_key_pairs contains material for sending messages.
43
+ // Sending pair is suggested by peer.
44
+ `--sql
45
+ CREATE TABLE sending_key_pairs (
46
+ peerCAddr TEXT NOT NULL PRIMARY KEY REFERENCES peers ON DELETE CASCADE,
47
+ --
48
+ pairAlg TEXT NOT NULL,
49
+ pairTS INTEGER NOT NULL,
50
+ --
51
+ sentMsgsCount INTEGER,
52
+ sentMsgLastTS INTEGER,
53
+ --
54
+ peerPKey BLOB NOT NULL,
55
+ peerKId TEXT NOT NULL,
56
+ --
57
+ pids TEXT NOT NULL,
58
+ --
59
+ senderSKey BLOB NOT NULL,
60
+ senderPKey TEXT NOT NULL,
61
+ senderKId TEXT NOT NULL,
62
+ --
63
+ msgMasterKeyAlg TEXT NOT NULL,
64
+ msgMasterKey BLOB NOT NULL
65
+ )
66
+ `,
67
+ // reception_key_pairs are used for reading incoming messages.
68
+ // Reception pairs are suggested by this side to respective peers, ratcheting/updating keys of both sides.
69
+ // When peer encrypts message to a new pair, it implies that pair is ratcheted to be in use, making current
70
+ // pair old, and allowing to suggest next new pair, advancing key ratcheting.
71
+ // In a scenario when this side starts messaging, it suggests pairs with peer's introductory key, known at the
72
+ // time. Introductory key may change in time, resulting in different suggested pairs before peer ever replies.
73
+ // These pairs should be kept till peer replies, implying which pair is used, allowing to drop other ones.
74
+ `--sql
75
+ CREATE TABLE reception_key_pairs (
76
+ peerCAddr TEXT NOT NULL REFERENCES peers ON DELETE CASCADE,
77
+ ratchetStage TEXT NOT NULL,
78
+ pids TEXT NOT NULL,
79
+ --
80
+ pairAlg TEXT NOT NULL,
81
+ pairTS INTEGER NOT NULL,
82
+ --
83
+ receivedMsgsCounts TEXT,
84
+ receivedMsgLastTS INTEGER,
85
+ --
86
+ peerPKey BLOB NOT NULL,
87
+ peerKId TEXT NOT NULL,
88
+ isPeerIntroKey INTEGER NOT NULL,
89
+ --
90
+ recipientSKey BLOB NOT NULL,
91
+ recipientPKey TEXT NOT NULL,
92
+ recipientKId TEXT NOT NULL,
93
+ --
94
+ msgMasterKeyAlg TEXT NOT NULL,
95
+ msgMasterKey BLOB NOT NULL,
96
+ --
97
+ PRIMARY KEY (peerCAddr, recipientKId, peerKId),
98
+ UNIQUE (peerCAddr, recipientKId) ON CONFLICT FAIL
99
+ )
100
+ `,
101
+ `--sql
102
+ CREATE INDEX reception_pair_stage ON reception_key_pairs (peerCAddr, ratchetStage)
103
+ `,
104
+ `--sql
105
+ CREATE TABLE reception_key_pairs_pids (
106
+ peerCAddr TEXT NOT NULL,
107
+ peerKId TEXT NOT NULL,
108
+ recipientKId TEXT NOT NULL,
109
+ pid TEXT NOT NULL,
110
+ PRIMARY KEY (peerCAddr, recipientKId, peerKId, pid),
111
+ FOREIGN KEY (peerCAddr, recipientKId, peerKId) REFERENCES reception_key_pairs ON DELETE CASCADE
112
+ )
113
+ `,
114
+ `--sql
115
+ CREATE INDEX pid_to_reception_pair ON reception_key_pairs_pids (pid)
116
+ `
117
+ ].join(';\n');
118
+ const sendingKeyPairsTabFields = {
119
+ peerCAddr: 'as-is',
120
+ pairAlg: 'as-is',
121
+ pairTS: 'as-is',
122
+ sentMsgsCount: 'as-is',
123
+ sentMsgLastTS: 'as-is',
124
+ peerPKey: 'as-is',
125
+ peerKId: 'as-is',
126
+ pids: for_sqlite_1.jsonTransform,
127
+ senderSKey: 'as-is',
128
+ senderPKey: 'as-is',
129
+ senderKId: 'as-is',
130
+ msgMasterKey: 'as-is',
131
+ msgMasterKeyAlg: 'as-is'
132
+ };
133
+ const receptionKeyPairsTabFields = {
134
+ peerCAddr: 'as-is',
135
+ ratchetStage: 'as-is',
136
+ pids: for_sqlite_1.jsonTransform,
137
+ pairTS: 'as-is',
138
+ pairAlg: 'as-is',
139
+ receivedMsgsCounts: for_sqlite_1.optJsonTransform,
140
+ receivedMsgLastTS: 'as-is',
141
+ peerPKey: 'as-is',
142
+ peerKId: 'as-is',
143
+ isPeerIntroKey: for_sqlite_1.booleanTransform,
144
+ recipientSKey: 'as-is',
145
+ recipientPKey: 'as-is',
146
+ recipientKId: 'as-is',
147
+ msgMasterKey: 'as-is',
148
+ msgMasterKeyAlg: 'as-is'
149
+ };
150
+ const pidsIndexTabFields = {
151
+ peerCAddr: 'as-is',
152
+ peerKId: 'as-is',
153
+ recipientKId: 'as-is',
154
+ pid: 'as-is'
155
+ };
156
+ const PEER_KEYS_DB = `peer-keys.sqlite`;
157
+ const PEER_KEYS_DB_ATTR = `msg-index-db`;
158
+ async function setMsgIndexInfoIntoAttrOf(dbFile, info) {
159
+ const completeInfo = {
160
+ dbVersion: 1,
161
+ ...info
162
+ };
163
+ await dbFile.updateXAttrs({
164
+ set: { [PEER_KEYS_DB_ATTR]: completeInfo }
165
+ });
166
+ }
167
+ async function readOrInitializeDB(dbsFS) {
168
+ if (await dbsFS.checkFilePresence(PEER_KEYS_DB)) {
169
+ const dbFile = await dbsFS.writableFile(PEER_KEYS_DB, { create: false });
170
+ return await lib_sqlite_on_3nstorage_1.SQLiteOn3NStorage.makeSynced(dbFile);
171
+ }
172
+ else {
173
+ const dbFile = await dbsFS.writableFile(PEER_KEYS_DB, { create: true, exclusive: true });
174
+ const peerKeys = await lib_sqlite_on_3nstorage_1.SQLiteOn3NStorage.makeSynced(dbFile);
175
+ peerKeys.db.run(queryToCreatePeerKeysDbV2);
176
+ await setMsgIndexInfoIntoAttrOf(peerKeys.dbFile, {});
177
+ await peerKeys.saveToFile();
178
+ await peerKeys.dbFileSync.upload();
179
+ await dbsFS.v.sync.upload('');
180
+ return peerKeys;
181
+ }
182
+ }
183
+ async function generatePids(random) {
184
+ const pids = [];
185
+ for (let i = 0; i < 5; i += 1) {
186
+ pids[i] = await (0, rng_def_1.stringOfB64UrlSafeChars)(common_1.PID_LENGTH, random);
187
+ if (i > 0) {
188
+ if (pids.slice(0, i).includes(pids[i])) {
189
+ i -= 1;
190
+ }
191
+ }
192
+ }
193
+ return pids;
194
+ }
195
+ async function makePeerKeysDB(peerKeysFS, random) {
196
+ const keys = await readOrInitializeDB(peerKeysFS);
197
+ let needToSave = false;
198
+ function noteDbMotificationForFileSaving() {
199
+ if (!needToSave && (keys.db.getRowsModified() > 0)) {
200
+ needToSave = true;
201
+ }
202
+ }
203
+ async function saveIfNeeded() {
204
+ if (needToSave) {
205
+ await keys.saveToFile();
206
+ }
207
+ }
208
+ function addPeer(peerAddr) {
209
+ const peerCAddr = (0, canonical_address_1.toCanonicalAddress)(peerAddr);
210
+ keys.db.exec(`--sql
211
+ INSERT INTO peers (peerCAddr, peerAddr) VALUES ($peerCAddr, $peerAddr) ON CONFLICT DO NOTHING`, { $peerCAddr: peerCAddr, $peerAddr: peerAddr });
212
+ noteDbMotificationForFileSaving();
213
+ }
214
+ function getSendingPair(peerCAddr) {
215
+ const pair = (0, for_sqlite_1.selectFromTable)(keys.db, 'sending_key_pairs', '*', { peerCAddr }, sendingKeyPairsTabFields);
216
+ return (pair ? pair[0] : undefined);
217
+ }
218
+ function addSendingPair(pair) {
219
+ (0, for_sqlite_1.performTableInsert)(keys.db, 'sending_key_pairs', pair, sendingKeyPairsTabFields);
220
+ noteDbMotificationForFileSaving();
221
+ }
222
+ function updateSendingPair(pair) {
223
+ (0, for_sqlite_1.updateInTable)(keys.db, 'sending_key_pairs', (0, for_sqlite_1.passOmitting)(pair, 'peerCAddr'), { peerCAddr: pair.peerCAddr }, sendingKeyPairsTabFields);
224
+ noteDbMotificationForFileSaving();
225
+ }
226
+ function updateSentMsgCountInSendingPair(peerCAddr, sentMsgsCount) {
227
+ updateSendingPair({ peerCAddr, sentMsgLastTS: Date.now(), sentMsgsCount });
228
+ }
229
+ function updateSendingPairFromSuggested(peerCAddr, pair, cryptoMaterial) {
230
+ updateSendingPair({
231
+ ...cryptoMaterial,
232
+ peerCAddr,
233
+ pids: pair.pids,
234
+ pairTS: pair.timestamp,
235
+ peerKId: pair.recipientPKey.kid,
236
+ sentMsgsCount: 0,
237
+ sentMsgLastTS: 0
238
+ });
239
+ }
240
+ function addSendingPairFromSuggested(peerCAddr, pair, cryptoMaterial) {
241
+ addSendingPair({
242
+ ...cryptoMaterial,
243
+ peerCAddr,
244
+ pids: pair.pids,
245
+ pairTS: pair.timestamp,
246
+ peerKId: pair.recipientPKey.kid,
247
+ sentMsgsCount: 0,
248
+ sentMsgLastTS: 0
249
+ });
250
+ }
251
+ function findKeyToMatchSuggestedForSendingPair(peerCAddr, thisSideKid) {
252
+ const pairs = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['ratchetStage', 'recipientSKey', 'recipientPKey', 'pairAlg'], { peerCAddr, recipientKId: thisSideKid }, receptionKeyPairsTabFields);
253
+ if (pairs) {
254
+ return {
255
+ pkey: pairs[0].recipientPKey,
256
+ skey: pairs[0].recipientSKey,
257
+ pairAlg: pairs[0].pairAlg
258
+ };
259
+ }
260
+ }
261
+ function getPeerAddressForCanonical(peerCAddr) {
262
+ const res = keys.db.exec(`--sql
263
+ SELECT peerAddr FROM peers WHERE peerCAddr=$peerCAddr`, { '$peerCAddr': peerCAddr });
264
+ return ((res.length > 0) ? (0, lib_sqlite_on_3nstorage_1.objectFromQueryExecResult)(res[0])[0].peerAddr : undefined);
265
+ }
266
+ function addReceptionPair(pair) {
267
+ (0, for_sqlite_1.performTableInsert)(keys.db, 'reception_key_pairs', pair, receptionKeyPairsTabFields);
268
+ const { peerCAddr, peerKId, pids, recipientKId } = pair;
269
+ for (const pid of pids) {
270
+ (0, for_sqlite_1.performTableInsert)(keys.db, 'reception_key_pairs_pids', { peerCAddr, peerKId, pid, recipientKId }, pidsIndexTabFields);
271
+ }
272
+ noteDbMotificationForFileSaving();
273
+ }
274
+ function markPairAsInUse(peerCAddr, peerKId, recipientKId) {
275
+ var _a;
276
+ const suggested = getPairSuggestedToPeer(peerCAddr);
277
+ if (((suggested === null || suggested === void 0 ? void 0 : suggested.senderKid) !== peerKId) || ((suggested === null || suggested === void 0 ? void 0 : suggested.recipientPKey.kid) !== recipientKId)) {
278
+ return false;
279
+ }
280
+ (0, for_sqlite_1.updateInTable)(keys.db, 'reception_key_pairs', { ratchetStage: 'old' }, { peerCAddr, ratchetStage: 'in_use' }, receptionKeyPairsTabFields);
281
+ (0, for_sqlite_1.updateInTable)(keys.db, 'reception_key_pairs', { ratchetStage: 'in_use' }, { peerCAddr, peerKId, recipientKId }, receptionKeyPairsTabFields);
282
+ const veryOld = (_a = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['peerKId', 'recipientKId', 'pairTS'], { peerCAddr, ratchetStage: 'old' }, receptionKeyPairsTabFields)) === null || _a === void 0 ? void 0 : _a.sort((a, b) => (b.pairTS - a.pairTS)).slice(1);
283
+ if (veryOld && (veryOld.length > 1)) {
284
+ for (const { recipientKId, peerKId } of veryOld) {
285
+ const whereParams = (0, for_sqlite_1.queryParamsFrom)({ peerCAddr, recipientKId, peerKId }, receptionKeyPairsTabFields);
286
+ const whereClause = (0, for_sqlite_1.andEqualExprFor)(whereParams);
287
+ keys.db.exec(`--sql
288
+ DELETE FROM reception_key_pairs WHERE ${whereClause}`, whereParams);
289
+ }
290
+ }
291
+ noteDbMotificationForFileSaving();
292
+ return true;
293
+ }
294
+ function updateReceivedMsgCountIn(peerCAddr, peerKId, recipientKId, msgCount, msgTS) {
295
+ const pair = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['receivedMsgLastTS', 'receivedMsgsCounts'], { peerCAddr, peerKId, recipientKId }, receptionKeyPairsTabFields);
296
+ if (!pair) {
297
+ return;
298
+ }
299
+ let { receivedMsgLastTS, receivedMsgsCounts } = pair[0];
300
+ if (!(0, number_line_1.addToNumberLineSegments)(receivedMsgsCounts, msgCount)) {
301
+ return;
302
+ }
303
+ (0, for_sqlite_1.updateInTable)(keys.db, 'reception_key_pairs', { receivedMsgsCounts, receivedMsgLastTS: Math.max(receivedMsgLastTS, msgTS) }, { peerCAddr, peerKId, recipientKId }, receptionKeyPairsTabFields);
304
+ noteDbMotificationForFileSaving();
305
+ }
306
+ function findReceptionPairsForPId(pid) {
307
+ const keyIds = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs_pids', ['peerCAddr', 'peerKId', 'recipientKId'], { pid }, pidsIndexTabFields);
308
+ if (!keyIds) {
309
+ return;
310
+ }
311
+ const foundPairs = [];
312
+ for (const { peerCAddr, peerKId, recipientKId } of keyIds) {
313
+ const key = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['msgMasterKey', 'ratchetStage', 'pairAlg'], { peerCAddr, peerKId, recipientKId }, receptionKeyPairsTabFields);
314
+ const { msgMasterKey, ratchetStage, pairAlg } = key[0];
315
+ foundPairs.push({
316
+ msgMasterKey: msgMasterKey, pairAlg: pairAlg, peerCAddr: peerCAddr,
317
+ peerKId: peerKId, ratchetStage: ratchetStage, recipientKId: recipientKId
318
+ });
319
+ }
320
+ return ((foundPairs.length > 0) ? foundPairs : undefined);
321
+ }
322
+ async function absorbV1DataIfPresent(keyringFS) {
323
+ var _a;
324
+ const v1Entries = await (0, json_files_1.checkIfV1AndReadPeerKeys)(keyringFS);
325
+ if (v1Entries)
326
+ for (const peer of v1Entries) {
327
+ const peerCAddr = (0, canonical_address_1.toCanonicalAddress)(peer.correspondent);
328
+ addPeer(peer.correspondent);
329
+ if (peer.receptionPairs.suggested) {
330
+ addReceptionPair((0, json_files_1.v1ReceptionPairToV2RecipientKeyPairDbEntry)(peer.receptionPairs.suggested, 'suggested', peerCAddr));
331
+ }
332
+ if (peer.receptionPairs.inUse) {
333
+ addReceptionPair((0, json_files_1.v1ReceptionPairToV2RecipientKeyPairDbEntry)(peer.receptionPairs.inUse, 'in_use', peerCAddr));
334
+ }
335
+ if (peer.receptionPairs.old) {
336
+ addReceptionPair((0, json_files_1.v1ReceptionPairToV2RecipientKeyPairDbEntry)(peer.receptionPairs.old, 'old', peerCAddr));
337
+ }
338
+ if (((_a = peer.sendingPair) === null || _a === void 0 ? void 0 : _a.type) === 'ratcheted') {
339
+ addSendingPair((0, json_files_1.v1RatchetedSendingPairToV2SendingKeyPairDbEntry)(peer.sendingPair, peerCAddr));
340
+ }
341
+ }
342
+ await keys.saveToFile();
343
+ await keys.dbFileSync.upload();
344
+ }
345
+ function getPairSuggestedToPeer(peerCAddr) {
346
+ const pairs = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['pids', 'isPeerIntroKey', 'peerKId', 'pairTS', 'recipientKId', 'recipientPKey'], { peerCAddr, ratchetStage: 'suggested' }, receptionKeyPairsTabFields);
347
+ if (!pairs) {
348
+ return;
349
+ }
350
+ const { pids, peerKId, pairTS, recipientKId, recipientPKey } = pairs[0];
351
+ return {
352
+ pids: pids,
353
+ recipientPKey: {
354
+ kid: recipientKId,
355
+ k: recipientPKey
356
+ },
357
+ timestamp: pairTS,
358
+ senderKid: peerKId
359
+ };
360
+ }
361
+ async function generateSuggestedPairOnPeerIntroKey(peerCAddr, peerIntroPKey) {
362
+ const { kid: peerKId, k: peerPKey } = peerIntroPKey;
363
+ return await generateAndAddSuggestedPair(peerCAddr, peerKId, buffer_utils_1.base64.open(peerPKey), false);
364
+ }
365
+ async function generateAndAddSuggestedPair(peerCAddr, peerKId, peerPKey, isPeerIntroKey) {
366
+ const recipientKey = await (0, common_1.generateKeyPair)(random);
367
+ const recipientSKey = buffer_utils_1.base64.open(recipientKey.skey.k);
368
+ const msgMasterKey = ecma_nacl_1.box.calc_dhshared_key(peerPKey, recipientSKey);
369
+ const pids = await generatePids(random);
370
+ const pairTS = Date.now();
371
+ const dbEntry = {
372
+ peerCAddr,
373
+ ratchetStage: 'suggested',
374
+ pids,
375
+ pairTS,
376
+ pairAlg: recipientKey.skey.alg,
377
+ isPeerIntroKey,
378
+ peerKId,
379
+ peerPKey,
380
+ recipientKId: recipientKey.pkey.kid,
381
+ recipientPKey: recipientKey.pkey.k,
382
+ recipientSKey,
383
+ msgMasterKey,
384
+ msgMasterKeyAlg: ecma_nacl_1.secret_box.JWK_ALG_NAME,
385
+ receivedMsgLastTS: null,
386
+ receivedMsgsCounts: []
387
+ };
388
+ addReceptionPair(dbEntry);
389
+ noteDbMotificationForFileSaving();
390
+ return {
391
+ nextCrypto: {
392
+ pids,
393
+ recipientPKey: {
394
+ kid: recipientKey.pkey.kid,
395
+ k: recipientKey.pkey.k
396
+ },
397
+ senderKid: peerKId,
398
+ timestamp: pairTS
399
+ },
400
+ entryToLog: dbEntry
401
+ };
402
+ }
403
+ async function generateRegularSuggestedPairIfNeeded(peerCAddr) {
404
+ const pairsInUse = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', ['pairTS'], { peerCAddr, ratchetStage: 'in_use' }, receptionKeyPairsTabFields);
405
+ if (pairsInUse && ((pairsInUse[0].pairTS + common_1.MIN_PERIOD_FOR_PAIR) > Date.now())) {
406
+ return;
407
+ }
408
+ const sendingPair = getSendingPair(peerCAddr);
409
+ if (!sendingPair) {
410
+ throw new Error(`Regular sending pair should be present to use peer's key in next suggested crypto`);
411
+ }
412
+ const { peerKId, peerPKey } = sendingPair;
413
+ return await generateAndAddSuggestedPair(peerCAddr, peerKId, peerPKey, false);
414
+ }
415
+ function getPeerKeysInfo(peerAddr) {
416
+ var _a;
417
+ const peerCAddr = (0, canonical_address_1.toCanonicalAddress)(peerAddr);
418
+ const sp = getSendingPair(peerCAddr);
419
+ const rpSuggested = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', '*', { peerCAddr, ratchetStage: 'suggested' }, receptionKeyPairsTabFields);
420
+ const rpInUse = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', '*', { peerCAddr, ratchetStage: 'in_use' }, receptionKeyPairsTabFields);
421
+ const rpOld = (_a = (0, for_sqlite_1.selectFromTable)(keys.db, 'reception_key_pairs', '*', { peerCAddr, ratchetStage: 'old' }, receptionKeyPairsTabFields)) === null || _a === void 0 ? void 0 : _a.sort((a, b) => (b.pairTS - a.pairTS));
422
+ return {
423
+ sendingPair: (sp ? {
424
+ alg: sp.pairAlg,
425
+ pids: sp.pids,
426
+ type: 'ratcheted',
427
+ timestamp: sp.pairTS,
428
+ senderKId: sp.senderKId,
429
+ recipientKId: sp.peerKId,
430
+ sentMsgs: {
431
+ count: sp.sentMsgsCount,
432
+ lastTS: sp.sentMsgLastTS
433
+ }
434
+ } : null),
435
+ receptionPairs: {
436
+ suggested: (rpSuggested ? recipientKeyPairDbEntryIntoDisplayInfo(rpSuggested[0]) : null),
437
+ inUse: (rpInUse ? recipientKeyPairDbEntryIntoDisplayInfo(rpInUse[0]) : null),
438
+ old: (rpOld ? recipientKeyPairDbEntryIntoDisplayInfo(rpOld[0]) : null)
439
+ }
440
+ };
441
+ }
442
+ async function saveAndSync() {
443
+ await saveIfNeeded();
444
+ const syncedIndexDbVersion = await keys.dbFileSync.upload();
445
+ return syncedIndexDbVersion;
446
+ }
447
+ // XXX at a startup we should check if current local latest is too old
448
+ // XXX one should watch for reconnect event and run similar checks of possibly missed events
449
+ async function absorbOpsFromOtherDevices(changeProc, ops) {
450
+ await changeProc.startOrChain(async () => {
451
+ for (const op of ops) {
452
+ if (op.p) {
453
+ if (op.p.opType === 'pair-from-peer') {
454
+ const { peerAddr, sendingPairRecord: inJSON } = op.p;
455
+ const existingPair = getSendingPair(inJSON.peerCAddr);
456
+ if (existingPair) {
457
+ if (existingPair.pairTS < inJSON.pairTS) {
458
+ updateSendingPair((0, logs_1.sendingPairEntryFromJSON)(inJSON));
459
+ }
460
+ }
461
+ else {
462
+ if (!getPeerAddressForCanonical(inJSON.peerCAddr)) {
463
+ addPeer(peerAddr);
464
+ }
465
+ addSendingPair((0, logs_1.sendingPairEntryFromJSON)(inJSON));
466
+ }
467
+ }
468
+ else if (op.p.opType === 'pair-suggested-to-peer') {
469
+ const { peerAddr, receivingPairRecord: inJSON } = op.p;
470
+ const existingPair = getPairSuggestedToPeer(inJSON.peerCAddr);
471
+ if (existingPair) {
472
+ if (existingPair.timestamp < inJSON.pairTS) {
473
+ // XXX replace existingPair with one from inJSON
474
+ // let's note that in broken situation we may suggest fixing moves
475
+ }
476
+ }
477
+ else {
478
+ if (!getPeerAddressForCanonical(inJSON.peerCAddr)) {
479
+ addPeer(peerAddr);
480
+ }
481
+ addReceptionPair((0, logs_1.recipientPairEntryFromJSON)(inJSON));
482
+ }
483
+ }
484
+ else if (op.p.opType === 'peer-started-using-pair') {
485
+ const { peerCAddr, peerKId, recipientKId } = op.p;
486
+ markPairAsInUse(peerCAddr, peerKId, recipientKId);
487
+ }
488
+ }
489
+ else if (op.syncPoint) {
490
+ // XXX
491
+ }
492
+ }
493
+ await saveIfNeeded();
494
+ });
495
+ }
496
+ function watchAndApplyOpsFromOtherDevices(changeProc, resetSyncInterval) {
497
+ return keys.dbFile.watch({
498
+ next: fsEvent => changeProc.startOrChain(async () => {
499
+ if (fsEvent.type === 'remote-change') {
500
+ // XXX get remote, compare,add to current what is missing, vs remote
501
+ resetSyncInterval();
502
+ }
503
+ })
504
+ });
505
+ }
506
+ return {
507
+ saveIfNeeded,
508
+ generateSuggestedPairOnPeerIntroKey,
509
+ getPairSuggestedToPeer,
510
+ generateRegularSuggestedPairIfNeeded,
511
+ addPeer,
512
+ getSendingPair,
513
+ updateSentMsgCountInSendingPair,
514
+ addSendingPairFromSuggested,
515
+ updateSendingPairFromSuggested,
516
+ findKeyToMatchSuggestedForSendingPair,
517
+ addReceptionPair,
518
+ markPairAsInUse,
519
+ findReceptionPairsForPId,
520
+ absorbV1DataIfPresent,
521
+ getPeerAddressForCanonical,
522
+ updateReceivedMsgCountIn,
523
+ absorbOpsFromOtherDevices,
524
+ watchAndApplyOpsFromOtherDevices,
525
+ saveAndSync,
526
+ getPeerKeysInfo
527
+ };
528
+ }
529
+ function recipientKeyPairDbEntryIntoDisplayInfo(p) {
530
+ return {
531
+ alg: p.pairAlg,
532
+ pids: p.pids,
533
+ recipientKId: p.recipientKId,
534
+ senderKId: p.peerKId,
535
+ timestamp: p.pairTS,
536
+ isSenderIntroKey: p.isPeerIntroKey,
537
+ receivedMsgs: {
538
+ counts: p.receivedMsgsCounts,
539
+ lastTS: p.receivedMsgLastTS
540
+ }
541
+ };
542
+ }
543
+ Object.freeze(exports);
@@ -0,0 +1,24 @@
1
+ import { JWKeyPair, ASMailKeyPair } from '../common';
2
+ import { SuggestedNextKeyPair } from '../../asmail/msg/opener';
3
+ import { AsyncRNG } from '../../../lib-common/rng-def';
4
+ import { Logger } from '../../../lib-client/logging/log-to-file';
5
+ type WritableFS = web3n.files.WritableFS;
6
+ type JsonKey = web3n.keys.JsonKey;
7
+ export declare function makePeersKeyring(keyringFS: WritableFS, localFS: WritableFS, random: AsyncRNG, logger: Logger): Promise<{
8
+ getPeerAddressForCanonical: (peerCAddr: string) => string | undefined;
9
+ needIntroKeyFor: (peerAddr: string) => boolean;
10
+ suggestPairForPeerIntroKey: (peerAddr: string, peerIntroPKey: JsonKey) => Promise<SuggestedNextKeyPair>;
11
+ getSendingCryptoWithinEstablishedPair: (peerCAddr: string) => Promise<{
12
+ currentPair: ASMailKeyPair;
13
+ msgMasterKey: Uint8Array;
14
+ msgCount: number;
15
+ nextMsgCrypto?: SuggestedNextKeyPair;
16
+ }>;
17
+ absorbNextPairSuggestedByPeer: (peerAddr: string, pair: SuggestedNextKeyPair, introKey?: JWKeyPair) => Promise<void>;
18
+ findEstablishedReceptionPairs: (pid: string) => Pick<import("./dataset/v2/peer-keys-db").RecipientKeyPairDbEntry, "peerCAddr" | "peerKId" | "recipientKId" | "msgMasterKey" | "ratchetStage" | "pairAlg">[] | undefined;
19
+ markPairAsInUse: (peerCAddr: string, peerKId: string, recipientKId: string) => Promise<void>;
20
+ updateReceivedMsgCountIn: (peerCAddr: string, peerKId: string, recipientKId: string, msgCount: number, msgTS: number) => void;
21
+ close: () => Promise<void>;
22
+ getPeerKeysInfo: (peerAddr: string) => web3n.keys.CorrespondentKeysInfo | undefined;
23
+ }>;
24
+ export {};