shogun-core 3.2.3 → 3.3.0

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 (32) hide show
  1. package/README.md +12 -0
  2. package/dist/browser/shogun-core.js +108102 -43579
  3. package/dist/browser/shogun-core.js.map +1 -1
  4. package/dist/ship/examples/messenger-cli.js +171 -55
  5. package/dist/ship/examples/wallet-cli.js +767 -0
  6. package/dist/ship/implementation/SHIP_00.js +478 -0
  7. package/dist/ship/implementation/SHIP_01.js +275 -492
  8. package/dist/ship/implementation/SHIP_02.js +1366 -0
  9. package/dist/ship/implementation/SHIP_03.js +855 -0
  10. package/dist/ship/interfaces/ISHIP_00.js +135 -0
  11. package/dist/ship/interfaces/ISHIP_01.js +81 -24
  12. package/dist/ship/interfaces/ISHIP_02.js +57 -0
  13. package/dist/ship/interfaces/ISHIP_03.js +61 -0
  14. package/dist/src/gundb/db.js +55 -11
  15. package/dist/src/index.js +10 -2
  16. package/dist/src/managers/CoreInitializer.js +41 -13
  17. package/dist/src/storage/storage.js +22 -9
  18. package/dist/types/ship/examples/messenger-cli.d.ts +7 -1
  19. package/dist/types/ship/examples/wallet-cli.d.ts +131 -0
  20. package/dist/types/ship/implementation/SHIP_00.d.ts +113 -0
  21. package/dist/types/ship/implementation/SHIP_01.d.ts +44 -77
  22. package/dist/types/ship/implementation/SHIP_02.d.ts +297 -0
  23. package/dist/types/ship/implementation/SHIP_03.d.ts +127 -0
  24. package/dist/types/ship/interfaces/ISHIP_00.d.ts +410 -0
  25. package/dist/types/ship/interfaces/ISHIP_01.d.ts +157 -119
  26. package/dist/types/ship/interfaces/ISHIP_02.d.ts +470 -0
  27. package/dist/types/ship/interfaces/ISHIP_03.d.ts +295 -0
  28. package/dist/types/src/gundb/db.d.ts +10 -3
  29. package/dist/types/src/index.d.ts +7 -0
  30. package/dist/types/src/interfaces/shogun.d.ts +2 -0
  31. package/dist/types/src/storage/storage.d.ts +2 -1
  32. package/package.json +22 -9
@@ -1,351 +1,168 @@
1
1
  "use strict";
2
2
  /**
3
- * Esempio Pratico: Messaggistica Decentralizzata con Shogun Core
3
+ * SHIP-01: Decentralized Encrypted Messaging Implementation
4
4
  *
5
- * Questo esempio mostra come creare un sistema di messaggistica sicuro usando:
6
- * - Shogun Core per autenticazione (username/password)
7
- * - GunDB per storage decentralizzato P2P
8
- * - SEA (Security, Encryption, Authorization) per crittografia
5
+ * Messaggistica decentralizzata E2E che dipende da SHIP-00 per l'identità.
9
6
  *
10
- * Vantaggi:
11
- * Completamente decentralizzato (no server centrale)
12
- * Zero costi (no blockchain, no gas fees)
13
- * Real-time messaging
14
- * ✅ Offline-first
15
- * ✅ End-to-end encryption
7
+ * Dipendenze:
8
+ * - SHIP-00 (Identity & Authentication) - per gestione utenti e chiavi
9
+ * - GunDB - per storage decentralizzato P2P
10
+ * - SEA - per crittografia ECDH + AES-GCM
16
11
  *
17
- * Note sull'ERC7627:
18
- * L'EIP era un template concettuale. Questo esempio implementa
19
- * solo la parte GunDB/Shogun Core senza interazione blockchain.
20
- * La funzione deriveEthereumAddress() rimane come utility per derivare
21
- * un address Ethereum dalla chiave GunDB se necessario in futuro.
12
+ * Vantaggi dell'architettura modulare:
13
+ * Separazione delle responsabilità (Identity vs Messaging)
14
+ * Riusabilità di SHIP-00 in altre applicazioni
15
+ * Testing più semplice e isolato
16
+ * Manutenibilità migliorata
22
17
  */
23
- var __importDefault = (this && this.__importDefault) || function (mod) {
24
- return (mod && mod.__esModule) ? mod : { "default": mod };
25
- };
26
18
  Object.defineProperty(exports, "__esModule", { value: true });
27
19
  exports.SHIP_01 = void 0;
28
- const core_1 = require("../../src/core");
29
20
  const ethers_1 = require("ethers");
30
- const derive_1 = __importDefault(require("../../src/gundb/derive"));
31
21
  // ============================================================================
32
- // 1. SETUP: Inizializzazione del Sistema Completo
22
+ // IMPLEMENTATION
33
23
  // ============================================================================
34
24
  /**
35
- * Classe per messaggistica sicura con Shogun Core
36
- * Implementa l'interfaccia ISHIP_01
37
- * Usa solo GunDB per storage decentralizzato (no blockchain)
25
+ * SHIP-01 Reference Implementation
26
+ *
27
+ * Questa implementazione dipende da ISHIP_00 per tutte le operazioni di identità.
28
+ * Si concentra esclusivamente sulla logica di messaggistica.
38
29
  */
39
30
  class SHIP_01 {
40
- constructor(shogunConfig) {
41
- // Inizializza Shogun Core
42
- this.shogun = new core_1.ShogunCore(shogunConfig);
43
- }
44
- // ========================================================================
45
- // 2. AUTENTICAZIONE: Username e Password
46
- // ========================================================================
47
- /**
48
- * Registra un nuovo utente
49
- */
50
- async signup(username, password) {
51
- try {
52
- // Registra con Shogun Core (crea SEA pair automaticamente)
53
- const signupResult = await this.shogun.signUp(username, password);
54
- if (!signupResult.success) {
55
- return {
56
- success: false,
57
- error: signupResult.error || "Signup failed",
58
- };
59
- }
60
- console.log("✅ Utente registrato");
61
- console.log(` Username: ${username}`);
62
- console.log(` GunDB Public Key: ${signupResult.pub}`);
63
- // Opzionale: deriva address Ethereum dalla chiave GunDB
64
- const derivedAddress = signupResult.pub
65
- ? await this.deriveEthereumAddress(signupResult.pub)
66
- : undefined;
67
- if (derivedAddress) {
68
- console.log(` Derived Address: ${derivedAddress}`);
69
- }
70
- return {
71
- success: true,
72
- userPub: signupResult.pub,
73
- derivedAddress,
74
- };
75
- }
76
- catch (error) {
77
- return {
78
- success: false,
79
- error: error.message,
80
- };
81
- }
82
- }
83
- /**
84
- * Login con username e password
85
- */
86
- async login(username, password) {
87
- try {
88
- // Login con Shogun Core
89
- const loginResult = await this.shogun.login(username, password);
90
- if (!loginResult.success) {
91
- return {
92
- success: false,
93
- error: loginResult.error || "Login failed",
94
- };
95
- }
96
- console.log("✅ Login effettuato");
97
- console.log(` Username: ${username}`);
98
- console.log(` GunDB Public Key: ${loginResult.userPub}`);
99
- // Opzionale: deriva address Ethereum dalla chiave GunDB
100
- const derivedAddress = loginResult.userPub
101
- ? await this.deriveEthereumAddress(loginResult.userPub)
102
- : undefined;
103
- if (derivedAddress) {
104
- console.log(` Derived Address: ${derivedAddress}`);
105
- }
106
- return {
107
- success: true,
108
- userPub: loginResult.userPub,
109
- derivedAddress,
110
- };
111
- }
112
- catch (error) {
113
- return {
114
- success: false,
115
- error: error.message,
116
- };
117
- }
118
- }
119
31
  /**
120
- * Login con SEA Key Pair
121
- *
122
- * Autenticazione diretta usando un key pair esportato.
123
- * Utile per:
124
- * - Recupero account senza password
125
- * - Portabilità tra dispositivi
126
- * - Backup dell'identità
32
+ * Constructor
33
+ * @param identity ISHIP_00 instance for identity operations
127
34
  */
128
- async loginWithPair(seaPair) {
129
- try {
130
- console.log("🔐 Login con key pair...");
131
- // Autentica con GunDB usando il pair
132
- const authResult = await new Promise((resolve) => {
133
- this.shogun.db.gun.user().auth(seaPair, (ack) => {
134
- if (ack.err) {
135
- resolve({
136
- success: false,
137
- error: ack.err,
138
- });
139
- }
140
- else {
141
- resolve({
142
- success: true,
143
- userPub: seaPair.pub,
144
- });
145
- }
146
- });
147
- });
148
- if (!authResult.success) {
149
- console.log("❌ Login fallito:", authResult.error);
150
- return authResult;
151
- }
152
- // Opzionale: deriva address Ethereum dalla chiave GunDB
153
- const derivedAddress = await this.deriveEthereumAddress(seaPair.pub);
154
- console.log("✅ Login effettuato");
155
- console.log(` GunDB Public Key: ${seaPair.pub}`);
156
- console.log(` Derived Address: ${derivedAddress}`);
157
- return {
158
- success: true,
159
- userPub: authResult.userPub,
160
- derivedAddress,
161
- };
162
- }
163
- catch (error) {
164
- console.error("❌ Errore login con pair:", error);
165
- return {
166
- success: false,
167
- error: error instanceof Error ? error.message : "Unknown error",
168
- };
35
+ constructor(identity) {
36
+ if (!identity.isLoggedIn()) {
37
+ throw new Error("User must be authenticated via SHIP-00 before using SHIP-01");
169
38
  }
39
+ this.identity = identity;
40
+ console.log("✅ SHIP-01 initialized with authenticated identity");
170
41
  }
171
42
  /**
172
- * Logout
173
- */
174
- logout() {
175
- this.shogun.logout();
176
- console.log("👋 Logout effettuato");
177
- }
178
- /**
179
- * Verifica se l'utente è autenticato
180
- */
181
- isLoggedIn() {
182
- return this.shogun.isLoggedIn();
183
- }
184
- // ========================================================================
185
- // 3. GESTIONE CHIAVI PUBBLICHE: Salva su GunDB
186
- // ========================================================================
187
- /**
188
- * Salva la chiave pubblica dell'utente su GunDB
189
- * Questo permette ad altri di trovare la tua chiave per criptare messaggi
43
+ * Get identity provider
190
44
  */
191
- async publishPublicKey() {
192
- try {
193
- if (!this.isLoggedIn()) {
194
- return { success: false, error: "Not logged in" };
195
- }
196
- // Ottieni il SEA pair dell'utente corrente
197
- const currentUser = this.shogun.db.user;
198
- if (!currentUser || !currentUser.is) {
199
- return { success: false, error: "No user session" };
200
- }
201
- // Salva chiave pubblica sul nodo globale usando il userPub come chiave
202
- // Questo permette ad altri di trovarla facilmente
203
- const userPub = currentUser.is.pub;
204
- await this.shogun.db.gun
205
- .get(userPub)
206
- .put({
207
- pub: currentUser.is.pub,
208
- epub: currentUser.is.epub,
209
- algorithm: "ECDSA",
210
- timestamp: Date.now().toString(),
211
- })
212
- .then();
213
- console.log(`📝 Chiave pubblica pubblicata su GunDB`);
214
- return { success: true };
215
- }
216
- catch (error) {
217
- console.error("❌ Errore pubblicazione chiave:", error);
218
- return { success: false, error: error.message };
219
- }
45
+ getIdentity() {
46
+ return this.identity;
220
47
  }
221
48
  // ========================================================================
222
- // 4. INVIO MESSAGGI: Solo GunDB
49
+ // MESSAGING - Send
223
50
  // ========================================================================
224
51
  /**
225
- * Invia un messaggio crittografato a un altro utente
52
+ * Send encrypted message to a user
53
+ * Uses identity provider (SHIP-00) to get keys
226
54
  */
227
55
  async sendMessage(recipientUsername, message) {
228
56
  try {
229
- if (!this.isLoggedIn()) {
230
- return { success: false, error: "Not logged in" };
231
- }
232
- const currentUser = this.shogun.db.user;
233
- if (!currentUser || !currentUser.is) {
234
- return { success: false, error: "No user session" };
57
+ // Verify authentication
58
+ if (!this.identity.isLoggedIn()) {
59
+ return { success: false, error: "Not authenticated" };
235
60
  }
236
- // 1. Ottieni la chiave pubblica del destinatario
237
- const recipientKey = await this.getRecipientPublicKey(recipientUsername);
61
+ // 1. Get recipient's public key from SHIP-00
62
+ const recipientKey = await this.identity.getPublicKey(recipientUsername);
238
63
  if (!recipientKey) {
239
64
  return {
240
65
  success: false,
241
66
  error: `Recipient ${recipientUsername} has not published their public key`,
242
67
  };
243
68
  }
244
- // 2. Ottieni il SEA pair completo (include chiavi private)
245
- const senderPair = this.shogun.db.gun.user()?._?.sea;
69
+ // 2. Get sender's key pair from SHIP-00
70
+ const senderPair = this.identity.getKeyPair();
246
71
  if (!senderPair) {
247
- return { success: false, error: "Cannot access SEA pair" };
72
+ return { success: false, error: "Cannot access sender key pair" };
73
+ }
74
+ // 3. Get current user from SHIP-00
75
+ const currentUser = this.identity.getCurrentUser();
76
+ if (!currentUser) {
77
+ return { success: false, error: "No current user" };
78
+ }
79
+ // 4. Access GunDB through identity provider
80
+ const shogun = this.identity.getShogun();
81
+ if (!shogun || !shogun.db) {
82
+ return { success: false, error: "Cannot access ShogunCore" };
83
+ }
84
+ const gun = shogun.db.gun;
85
+ const crypto = shogun.db.crypto;
86
+ if (!gun || !crypto) {
87
+ return { success: false, error: "Cannot access GunDB or crypto" };
248
88
  }
249
- // 3. Cripta il messaggio usando SEA.secret + SEA.encrypt (ECDH)
250
- const encryptedMessage = await this.shogun.db.crypto.encFor(message, senderPair, // sender pair completo
251
- { epub: recipientKey.epub } // recipient public encryption key
252
- );
253
- // 4. Genera ID messaggio
89
+ // 5. Encrypt message using ECDH
90
+ const encryptedMessage = await crypto.encFor(message, senderPair, { epub: recipientKey.epub });
91
+ // 6. Generate message ID
254
92
  const messageId = this.generateMessageId();
255
- const senderPub = currentUser.is.pub;
256
- // 5. Salva messaggio crittografato su GunDB
93
+ // 7. Save encrypted message on GunDB
257
94
  const messageData = {
258
- from: senderPub,
95
+ from: currentUser.pub,
259
96
  to: recipientUsername,
260
- content: encryptedMessage, // Contenuto crittografato!
97
+ content: encryptedMessage,
261
98
  timestamp: Date.now().toString(),
262
99
  messageId: messageId,
263
100
  };
264
- // Salva sul nodo globale 'messages' per permettere il listener
265
- await this.shogun.db.gun
101
+ await gun
266
102
  .get(SHIP_01.NODES.MESSAGES)
267
103
  .get(messageId)
268
104
  .put(messageData)
269
105
  .then();
270
- console.log(`✅ Messaggio salvato: ${messageId}`);
106
+ console.log(`✅ Message sent: ${messageId}`);
271
107
  return {
272
108
  success: true,
273
109
  messageId: messageId,
274
110
  };
275
111
  }
276
112
  catch (error) {
113
+ console.error("❌ Error sending message:", error);
277
114
  return {
278
115
  success: false,
279
116
  error: error.message,
280
117
  };
281
118
  }
282
119
  }
283
- /**
284
- * Ottiene la chiave pubblica di un utente da GunDB
285
- */
286
- async getRecipientPublicKey(username) {
287
- try {
288
- // Prima trova l'utente dal username
289
- const userData = await this.shogun.db.getUserByAlias(username);
290
- if (!userData || !userData.userPub) {
291
- console.error(`❌ User ${username} not found`);
292
- return null;
293
- }
294
- const userPub = userData.userPub;
295
- // Le chiavi sono salvate direttamente sul nodo userPub
296
- const publicKeyData = await this.shogun.db.gun.get(userPub).then();
297
- if (publicKeyData && publicKeyData.epub && publicKeyData.pub) {
298
- return {
299
- pub: publicKeyData.pub,
300
- epub: publicKeyData.epub,
301
- };
302
- }
303
- return null;
304
- }
305
- catch (error) {
306
- console.error("❌ Errore recupero chiave pubblica:", error);
307
- return null;
308
- }
309
- }
310
120
  // ========================================================================
311
- // 5. RICEZIONE MESSAGGI: Ascolta GunDB in real-time
121
+ // MESSAGING - Listen
312
122
  // ========================================================================
313
123
  /**
314
- * Ascolta messaggi crittografati in arrivo su GunDB
124
+ * Listen for incoming encrypted messages
125
+ * Uses identity provider (SHIP-00) to decrypt messages
315
126
  */
316
127
  async listenForMessages(onMessage) {
317
- if (!this.isLoggedIn()) {
318
- console.error("❌ Non autenticato");
128
+ if (!this.identity.isLoggedIn()) {
129
+ console.error("❌ Not authenticated");
319
130
  return;
320
131
  }
321
- const currentUser = this.shogun.db.user;
322
- if (!currentUser || !currentUser.is) {
323
- console.error("❌ Nessuna sessione utente");
132
+ // Get current user from SHIP-00
133
+ const currentUser = this.identity.getCurrentUser();
134
+ if (!currentUser || !currentUser.alias) {
135
+ console.error("❌ No current user");
324
136
  return;
325
137
  }
326
- const userPub = currentUser.is.pub;
327
- // Ottieni username dell'utente corrente
328
- const username = currentUser.is.alias;
329
- // Set per tracciare messaggi già ricevuti (evita duplicati)
138
+ const username = currentUser.alias;
139
+ // Access GunDB
140
+ const shogun = this.identity.getShogun();
141
+ const gun = shogun?.db?.gun;
142
+ if (!gun) {
143
+ console.error("❌ Cannot access GunDB");
144
+ return;
145
+ }
146
+ // Track received messages to avoid duplicates
330
147
  const receivedMessages = new Set();
331
- // Ascolta messaggi in tempo reale su GunDB
332
- this.shogun.db.gun
148
+ // Listen for messages in real-time
149
+ gun
333
150
  .get(SHIP_01.NODES.MESSAGES)
334
151
  .map()
335
152
  .on(async (data, key) => {
336
- // Filtra solo i messaggi destinati a questo utente (per username)
153
+ // Filter messages for this user
337
154
  if (data &&
338
155
  data.to === username &&
339
156
  data.from &&
340
157
  data.content &&
341
158
  data.messageId) {
342
- // Evita duplicati (GunDB può emettere più volte)
159
+ // Avoid duplicates
343
160
  if (receivedMessages.has(data.messageId)) {
344
161
  return;
345
162
  }
346
163
  receivedMessages.add(data.messageId);
347
164
  try {
348
- // Decripta il messaggio
165
+ // Decrypt message using SHIP-00
349
166
  const decryptedContent = await this.decryptMessage(data.content, data.from);
350
167
  onMessage({
351
168
  from: data.from,
@@ -354,90 +171,55 @@ class SHIP_01 {
354
171
  });
355
172
  }
356
173
  catch (error) {
357
- console.error("❌ Errore decrittazione messaggio:", error);
174
+ console.error("❌ Error decrypting message:", error);
358
175
  }
359
176
  }
360
177
  });
361
- console.log(`👂 In ascolto di messaggi per ${username} (${userPub.substring(0, 10)}...)`);
362
- }
363
- /**
364
- * Decripta un messaggio usando SEA.secret + SEA.decrypt (ECDH)
365
- */
366
- async decryptMessage(encryptedContent, senderPub) {
367
- // Ottieni il SEA pair completo del destinatario (noi)
368
- const receiverPair = this.shogun.db.gun.user()?._?.sea;
369
- if (!receiverPair) {
370
- throw new Error("Cannot access SEA pair");
371
- }
372
- // Ottieni epub del mittente
373
- const senderKey = await this.getPublicKeyByPub(senderPub);
374
- if (!senderKey) {
375
- throw new Error("Sender public key not found");
376
- }
377
- // Decripta usando SEA.secret + SEA.decrypt (ECDH)
378
- const decrypted = await this.shogun.db.crypto.decFrom(encryptedContent, { epub: senderKey.epub }, // sender's public encryption key
379
- receiverPair // receiver's pair completo
380
- );
381
- return decrypted;
382
- }
383
- /**
384
- * Ottiene la chiave pubblica di un utente dalla sua pub key
385
- */
386
- async getPublicKeyByPub(userPub) {
387
- try {
388
- // Le chiavi sono salvate direttamente sul nodo userPub
389
- const publicKeyData = await this.shogun.db.gun.get(userPub).then();
390
- if (publicKeyData && publicKeyData.epub && publicKeyData.pub) {
391
- return {
392
- pub: publicKeyData.pub,
393
- epub: publicKeyData.epub,
394
- };
395
- }
396
- return null;
397
- }
398
- catch (error) {
399
- console.error("❌ Errore recupero chiave:", error);
400
- return null;
401
- }
178
+ console.log(`👂 Listening for messages to ${username}...`);
402
179
  }
403
180
  // ========================================================================
404
- // 6. RECUPERO STORICO: Query messaggi passati
181
+ // MESSAGING - History
405
182
  // ========================================================================
406
183
  /**
407
- * Recupera lo storico dei messaggi crittografati con un utente
184
+ * Get message history with a user
185
+ * Uses identity provider (SHIP-00) to decrypt messages
408
186
  */
409
187
  async getMessageHistory(withUsername) {
410
- if (!this.isLoggedIn()) {
411
- console.error("❌ Non autenticato");
188
+ if (!this.identity.isLoggedIn()) {
189
+ console.error("❌ Not authenticated");
412
190
  return [];
413
191
  }
414
- const currentUser = this.shogun.db.user;
415
- if (!currentUser || !currentUser.is) {
416
- console.error("❌ Nessuna sessione utente");
192
+ // Get current user from SHIP-00
193
+ const currentUser = this.identity.getCurrentUser();
194
+ if (!currentUser || !currentUser.alias) {
195
+ console.error("❌ No current user");
417
196
  return [];
418
197
  }
419
- const userPub = currentUser.is.pub;
420
- const username = currentUser.is.alias;
421
- const allMessages = [];
422
- // Ottieni il userPub dell'altro utente
423
- const otherUserData = await this.shogun.db.getUserByAlias(withUsername);
198
+ const username = currentUser.alias;
199
+ const userPub = currentUser.pub;
200
+ // Get other user's public key from SHIP-00
201
+ const otherUserData = await this.identity.getUserByAlias(withUsername);
424
202
  const otherUserPub = otherUserData?.userPub;
425
- // Use .map() instead of .then() to iterate all messages
203
+ // Access GunDB
204
+ const shogun = this.identity.getShogun();
205
+ const gun = shogun?.db?.gun;
206
+ if (!gun) {
207
+ console.error("❌ Cannot access GunDB");
208
+ return [];
209
+ }
210
+ // Get all messages and filter
426
211
  return new Promise((resolve) => {
427
212
  const messages = [];
428
- let messageCount = 0;
429
- this.shogun.db.gun
213
+ gun
430
214
  .get(SHIP_01.NODES.MESSAGES)
431
215
  .map()
432
216
  .once(async (msgData, messageId) => {
433
- // Skip metadata fields
217
+ // Skip metadata
434
218
  if (!msgData || typeof msgData !== "object" || messageId === "_") {
435
219
  return;
436
220
  }
437
- messageCount++;
438
221
  try {
439
222
  // Check if message is part of this conversation
440
- // Fixed: Handle both username and userPub in matching
441
223
  const isSentToTarget = msgData.from === userPub &&
442
224
  (msgData.to === withUsername || msgData.to === otherUserPub);
443
225
  const isReceivedFromTarget = (msgData.to === username || msgData.to === userPub) &&
@@ -457,7 +239,7 @@ class SHIP_01 {
457
239
  // Silent error - message couldn't be decrypted
458
240
  }
459
241
  });
460
- // Wait a bit for GunDB to return all messages, then resolve
242
+ // Wait for GunDB to return all messages, then resolve
461
243
  setTimeout(() => {
462
244
  const sorted = messages.sort((a, b) => a.timestamp - b.timestamp);
463
245
  resolve(sorted);
@@ -465,186 +247,187 @@ class SHIP_01 {
465
247
  });
466
248
  }
467
249
  // ========================================================================
468
- // 7. UTILITY FUNCTIONS
250
+ // PRIVATE HELPERS
469
251
  // ========================================================================
470
252
  /**
471
- * Converte la chiave pubblica di GunDB in address Ethereum
472
- * Usa la chiave privata SEA come seed per derivazione deterministica
473
- *
474
- * Questo garantisce che:
475
- * - Stesso SEA pair stesso address Ethereum
476
- * - Derivazione cryptografica sicura
477
- * - Identità unificata tra GunDB e blockchain
253
+ * Decrypt a message using ECDH
254
+ * Uses identity provider (SHIP-00) to get keys
255
+ */
256
+ async decryptMessage(encryptedContent, senderPub) {
257
+ // Get receiver's key pair from SHIP-00
258
+ const receiverPair = this.identity.getKeyPair();
259
+ if (!receiverPair) {
260
+ throw new Error("Cannot access receiver key pair");
261
+ }
262
+ // Get sender's public key
263
+ const senderKeyData = await this.getPublicKeyByPub(senderPub);
264
+ if (!senderKeyData) {
265
+ throw new Error("Sender public key not found");
266
+ }
267
+ // Access crypto
268
+ const shogun = this.identity.getShogun();
269
+ const crypto = shogun?.db?.crypto;
270
+ if (!crypto) {
271
+ throw new Error("Cannot access crypto");
272
+ }
273
+ // Decrypt using ECDH
274
+ const decrypted = await crypto.decFrom(encryptedContent, { epub: senderKeyData.epub }, receiverPair);
275
+ return decrypted;
276
+ }
277
+ /**
278
+ * Get public key by pub key
279
+ * Uses identity provider internally
478
280
  */
479
- async deriveEthereumAddress(publicKey) {
281
+ async getPublicKeyByPub(userPub) {
480
282
  try {
481
- // Ottieni il SEA pair completo
482
- const seaPair = this.shogun.db.gun.user()?._?.sea;
483
- if (!seaPair || !seaPair.priv) {
484
- throw new Error("Cannot access SEA pair");
283
+ // Access GunDB
284
+ const shogun = this.identity.getShogun();
285
+ const gun = shogun?.db?.gun;
286
+ if (!gun) {
287
+ return null;
485
288
  }
486
- // Usa la chiave privata SEA come seed per derive
487
- // Questo è MOLTO più sicuro e deterministico del username!
488
- const derived = await (0, derive_1.default)(seaPair.priv, null, {
489
- includeSecp256k1Ethereum: true,
490
- includeP256: false,
491
- includeSecp256k1Bitcoin: false,
492
- });
493
- return derived.secp256k1Ethereum.address;
289
+ const publicKeyData = await gun.get(userPub).then();
290
+ if (publicKeyData && publicKeyData.epub && publicKeyData.pub) {
291
+ return {
292
+ pub: publicKeyData.pub,
293
+ epub: publicKeyData.epub,
294
+ };
295
+ }
296
+ return null;
494
297
  }
495
298
  catch (error) {
496
- console.error("❌ Errore derivazione address:", error);
497
- // Fallback: hash della chiave pubblica
498
- const fallbackKey = publicKey || "unknown";
499
- const hash = ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(fallbackKey));
500
- return ethers_1.ethers.getAddress("0x" + hash.slice(-40));
299
+ console.error("❌ Error getting public key:", error);
300
+ return null;
501
301
  }
502
302
  }
503
303
  /**
504
- * Genera ID messaggio univoco
304
+ * Generate unique message ID
505
305
  */
506
306
  generateMessageId() {
507
307
  return ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(16));
508
308
  }
309
+ // ========================================================================
310
+ // TOKEN-BASED MESSAGING (Channels/Groups)
311
+ // ========================================================================
312
+ /**
313
+ * Send message encrypted with shared token/password
314
+ * Useful for group chats, channels, broadcast messages
315
+ */
316
+ async sendMessageWithToken(token, message, channel) {
317
+ try {
318
+ // Verify authentication
319
+ if (!this.identity.isLoggedIn()) {
320
+ return { success: false, error: "Not authenticated" };
321
+ }
322
+ // Get current user
323
+ const currentUser = this.identity.getCurrentUser();
324
+ if (!currentUser) {
325
+ return { success: false, error: "No current user" };
326
+ }
327
+ // Access crypto and GunDB
328
+ const shogun = this.identity.getShogun();
329
+ const crypto = shogun?.db?.crypto;
330
+ const gun = shogun?.db?.gun;
331
+ if (!crypto || !gun) {
332
+ return { success: false, error: "Cannot access crypto or GunDB" };
333
+ }
334
+ // Hash token for key derivation (more secure)
335
+ const hashedToken = await crypto.hashText(token);
336
+ // Encrypt message with hashed token
337
+ const encryptedMessage = await crypto.encrypt(message, hashedToken);
338
+ // Generate message ID
339
+ const messageId = this.generateMessageId();
340
+ // Save encrypted message
341
+ const messageData = {
342
+ from: currentUser.pub,
343
+ content: encryptedMessage,
344
+ channel: channel || "default",
345
+ timestamp: Date.now().toString(),
346
+ messageId: messageId,
347
+ type: "token", // Mark as token-encrypted
348
+ };
349
+ await gun
350
+ .get(SHIP_01.NODES.TOKEN_MESSAGES)
351
+ .get(messageId)
352
+ .put(messageData)
353
+ .then();
354
+ console.log(`✅ Token message sent: ${messageId} (channel: ${channel || "default"})`);
355
+ return {
356
+ success: true,
357
+ messageId: messageId,
358
+ };
359
+ }
360
+ catch (error) {
361
+ console.error("❌ Error sending token message:", error);
362
+ return {
363
+ success: false,
364
+ error: error.message,
365
+ };
366
+ }
367
+ }
368
+ /**
369
+ * Listen for token-encrypted messages
370
+ * Automatically decrypts with provided token
371
+ */
372
+ async listenForTokenMessages(token, onMessage, channel) {
373
+ if (!this.identity.isLoggedIn()) {
374
+ console.error("❌ Not authenticated");
375
+ return;
376
+ }
377
+ // Access GunDB and crypto
378
+ const shogun = this.identity.getShogun();
379
+ const gun = shogun?.db?.gun;
380
+ const crypto = shogun?.db?.crypto;
381
+ if (!gun || !crypto) {
382
+ console.error("❌ Cannot access GunDB or crypto");
383
+ return;
384
+ }
385
+ // Hash token for decryption
386
+ const hashedToken = await crypto.hashText(token);
387
+ // Track received messages to avoid duplicates
388
+ const receivedMessages = new Set();
389
+ // Listen for token messages in real-time
390
+ gun
391
+ .get(SHIP_01.NODES.TOKEN_MESSAGES)
392
+ .map()
393
+ .on(async (data, key) => {
394
+ // Filter by channel if specified
395
+ if (channel && data?.channel !== channel) {
396
+ return;
397
+ }
398
+ // Validate data
399
+ if (data &&
400
+ data.type === "token" &&
401
+ data.from &&
402
+ data.content &&
403
+ data.messageId) {
404
+ // Avoid duplicates
405
+ if (receivedMessages.has(data.messageId)) {
406
+ return;
407
+ }
408
+ receivedMessages.add(data.messageId);
409
+ try {
410
+ // Decrypt message with hashed token
411
+ const decryptedContent = await crypto.decrypt(data.content, hashedToken);
412
+ onMessage({
413
+ from: data.from,
414
+ content: decryptedContent,
415
+ channel: data.channel,
416
+ timestamp: parseInt(data.timestamp),
417
+ });
418
+ }
419
+ catch (error) {
420
+ // Silently skip messages that can't be decrypted
421
+ // (wrong token or corrupted data)
422
+ }
423
+ }
424
+ });
425
+ console.log(`👂 Listening for token messages (channel: ${channel || "all"})...`);
426
+ }
509
427
  }
510
428
  exports.SHIP_01 = SHIP_01;
511
- // GunDB Node Names - Constants for clarity
429
+ // GunDB Node Names for messaging
512
430
  SHIP_01.NODES = {
513
431
  MESSAGES: "messages",
514
- USERS: "users",
515
- PUBLIC_KEYS: "publicKeys",
432
+ TOKEN_MESSAGES: "token_messages", // Token-encrypted messages (channels/groups)
516
433
  };
517
- async function testAliceAndBob() {
518
- console.log("🧪 TEST: Alice e Bob - Conversazione Crittografata E2E\n");
519
- console.log("=".repeat(60));
520
- // Setup Alice
521
- console.log("\n👩 ALICE - Setup");
522
- console.log("-".repeat(60));
523
- const alice = new SHIP_01({
524
- gunOptions: {
525
- peers: ["https://peer.wallie.io/gun"],
526
- radisk: true,
527
- },
528
- });
529
- // Alice signup/login
530
- await alice.signup("alice", "password123");
531
- const aliceLogin = await alice.login("alice", "password123");
532
- if (!aliceLogin.success) {
533
- console.error("❌ Alice login failed");
534
- return;
535
- }
536
- await alice.publishPublicKey();
537
- // Aspetta che la chiave sia sincronizzata
538
- await new Promise((resolve) => setTimeout(resolve, 1000));
539
- // Setup Bob
540
- console.log("\n👨 BOB - Setup");
541
- console.log("-".repeat(60));
542
- const bob = new SHIP_01({
543
- gunOptions: {
544
- peers: ["https://peer.wallie.io/gun"],
545
- radisk: true,
546
- },
547
- });
548
- // Bob signup/login
549
- await bob.signup("bob", "password456");
550
- const bobLogin = await bob.login("bob", "password456");
551
- if (!bobLogin.success) {
552
- console.error("❌ Bob login failed");
553
- return;
554
- }
555
- await bob.publishPublicKey();
556
- // Aspetta che la chiave sia sincronizzata
557
- await new Promise((resolve) => setTimeout(resolve, 1000));
558
- console.log("\n" + "=".repeat(60));
559
- console.log("💬 CONVERSAZIONE");
560
- console.log("=".repeat(60));
561
- // Alice e Bob ascoltano messaggi
562
- let aliceMessages = [];
563
- let bobMessages = [];
564
- await alice.listenForMessages((msg) => {
565
- aliceMessages.push(msg);
566
- console.log(`\n[Alice riceve] 📨`);
567
- console.log(` Da: ${msg.from.substring(0, 10)}...`);
568
- console.log(` Messaggio: "${msg.content}"`);
569
- console.log(` Timestamp: ${new Date(msg.timestamp).toLocaleTimeString()}`);
570
- });
571
- await bob.listenForMessages((msg) => {
572
- bobMessages.push(msg);
573
- console.log(`\n[Bob riceve] 📨`);
574
- console.log(` Da: ${msg.from.substring(0, 10)}...`);
575
- console.log(` Messaggio: "${msg.content}"`);
576
- console.log(` Timestamp: ${new Date(msg.timestamp).toLocaleTimeString()}`);
577
- });
578
- // Aspetta che i listener siano pronti
579
- await new Promise((resolve) => setTimeout(resolve, 2000));
580
- // Alice invia messaggio a Bob
581
- console.log("\n[Alice invia] 📤");
582
- const msg1 = await alice.sendMessage("bob", "Ciao Bob! Come stai? 👋");
583
- console.log(` ✅ Inviato: "${msg1.messageId}"`);
584
- // Aspetta che Bob riceva
585
- await new Promise((resolve) => setTimeout(resolve, 2000));
586
- // Bob risponde ad Alice
587
- console.log("\n[Bob invia] 📤");
588
- const msg2 = await bob.sendMessage("alice", "Ciao Alice! Tutto bene, grazie! Tu? 😊");
589
- console.log(` ✅ Inviato: "${msg2.messageId}"`);
590
- // Aspetta che Alice riceva
591
- await new Promise((resolve) => setTimeout(resolve, 2000));
592
- // Alice risponde
593
- console.log("\n[Alice invia] 📤");
594
- const msg3 = await alice.sendMessage("bob", "Anch'io benissimo! Questo sistema di messaggistica è fantastico! 🚀");
595
- console.log(` ✅ Inviato: "${msg3.messageId}"`);
596
- // Aspetta che Bob riceva
597
- await new Promise((resolve) => setTimeout(resolve, 2000));
598
- // Bob conclude
599
- console.log("\n[Bob invia] 📤");
600
- const msg4 = await bob.sendMessage("alice", "Vero! Zero costi, decentralizzato e veloce! 💪");
601
- console.log(` ✅ Inviato: "${msg4.messageId}"`);
602
- // Aspetta finale
603
- await new Promise((resolve) => setTimeout(resolve, 3000));
604
- // Mostra statistiche
605
- console.log("\n" + "=".repeat(60));
606
- console.log("📊 STATISTICHE FINALI");
607
- console.log("=".repeat(60));
608
- console.log(`\n👩 Alice:`);
609
- console.log(` - Messaggi inviati: 2`);
610
- console.log(` - Messaggi ricevuti: ${aliceMessages.length}`);
611
- console.log(` - Public Key: ${aliceLogin.userPub?.substring(0, 20)}...`);
612
- console.log(` - Derived Address: ${aliceLogin.derivedAddress}`);
613
- console.log(`\n👨 Bob:`);
614
- console.log(` - Messaggi inviati: 2`);
615
- console.log(` - Messaggi ricevuti: ${bobMessages.length}`);
616
- console.log(` - Public Key: ${bobLogin.userPub?.substring(0, 20)}...`);
617
- console.log(` - Derived Address: ${bobLogin.derivedAddress}`);
618
- // Recupera storico
619
- console.log("\n" + "=".repeat(60));
620
- console.log("📚 STORICO CONVERSAZIONE");
621
- console.log("=".repeat(60));
622
- const aliceHistory = await alice.getMessageHistory("bob");
623
- console.log(`\n👩 Storico di Alice con Bob (${aliceHistory.length} messaggi):`);
624
- aliceHistory.forEach((msg, i) => {
625
- const sender = msg.from === aliceLogin.userPub ? "Alice" : "Bob";
626
- console.log(` ${i + 1}. [${sender}]: ${msg.content}`);
627
- });
628
- const bobHistory = await bob.getMessageHistory("alice");
629
- console.log(`\n👨 Storico di Bob con Alice (${bobHistory.length} messaggi):`);
630
- bobHistory.forEach((msg, i) => {
631
- const sender = msg.from === bobLogin.userPub ? "Bob" : "Alice";
632
- console.log(` ${i + 1}. [${sender}]: ${msg.content}`);
633
- });
634
- // Riepilogo finale
635
- console.log("\n" + "=".repeat(60));
636
- console.log("✅ TEST COMPLETATO CON SUCCESSO!");
637
- console.log("=".repeat(60));
638
- console.log("\n📝 Riepilogo:");
639
- console.log(" ✅ 2 utenti registrati (Alice, Bob)");
640
- console.log(" ✅ 4 messaggi scambiati");
641
- console.log(" ✅ Messaggi ricevuti in real-time");
642
- console.log(" ✅ Storico recuperato correttamente");
643
- console.log(" ✅ Storage: GunDB P2P (gratis!)");
644
- console.log(" ✅ Costi: $0.00");
645
- console.log("\n🎉 Sistema di messaggistica decentralizzato funzionante!\n");
646
- }
647
- // Esegui esempio o test
648
- if (require.main === module) {
649
- testAliceAndBob().catch(console.error); // Test completo Alice & Bob
650
- }