|
3 | 3 | import { Button } from "attractions"; |
4 | 4 | import { Autocomplete } from "attractions"; |
5 | 5 |
|
6 | | -
|
7 | 6 | import { |
8 | 7 | username, |
9 | 8 | receivedScreenshots, |
10 | 9 | selectedScreenshot, |
11 | 10 | selectedContact, |
12 | | - db |
| 11 | + db, |
| 12 | + senderKeyPairs, |
| 13 | + receiverKeyPairs, |
13 | 14 | } from "./User"; |
14 | 15 |
|
15 | | - // on button click, set username |
16 | | - function setUsername() { |
17 | | - console.log({ msg: "updating username", username }); |
18 | | - startGun(); |
19 | | - } |
| 16 | + // Get the senders (logged in user) cryptographically secure public/private key pair |
| 17 | + // Contains the public key (pub), the private key (priv), the epub |
| 18 | + // (encryption public key) and the epriv (encryption private key) |
| 19 | + const senderPair = db.user()._.sea; |
| 20 | + senderKeyPairs.set({ ...$senderKeyPairs, ...senderPair }); |
20 | 21 |
|
21 | 22 | // Load screenshots received from Gun, listen for incoming |
22 | | - db |
23 | | - .get("screenshots") |
| 23 | + db.get("screenshots") |
24 | 24 | .get($username) |
25 | 25 | .map() |
26 | | - .once((data, id) => { |
27 | | - console.log({ data, id, message: "screenshots loaded" }); |
28 | | - receivedScreenshots.set([...$receivedScreenshots, data]); |
| 26 | + .once(async (data, id) => { |
| 27 | + if (data.data == null) return; |
| 28 | + const { data: encryptedImage, from: fromUser } = data; |
| 29 | +
|
| 30 | + const senderEPub = await getUserEPub(fromUser); |
| 31 | +
|
| 32 | + // Decrypt the image |
| 33 | + const decryptSecret = await SEA.secret(senderEPub, $senderKeyPairs); |
| 34 | + const decryptedMessage = await SEA.decrypt(encryptedImage, decryptSecret); |
| 35 | + receivedScreenshots.set([...$receivedScreenshots, decryptedMessage]); |
29 | 36 | }); |
30 | 37 |
|
| 38 | + const getUserEPub = async (alias) => { |
| 39 | + // check if alias starts with ~@ (database format for user alias) |
| 40 | + if (!alias.startsWith("~@")) alias = `~@${alias}`; |
| 41 | +
|
| 42 | + const userAccount = await db.get(alias).once(); |
| 43 | + if (!userAccount) return; |
| 44 | +
|
| 45 | + // Get the receivers public key (pub) |
| 46 | + const receiverPubKey = Object.keys(userAccount)[1].substring(1); |
| 47 | + // Get the receivers public key for encryption (epub) |
| 48 | + const receiverEPub = await db.user(receiverPubKey).get("epub"); |
| 49 | +
|
| 50 | + return receiverEPub; |
| 51 | + }; |
| 52 | +
|
31 | 53 | // Send screenshot to Gun user |
32 | | - function sendMessage() { |
| 54 | + const sendMessage = async () => { |
33 | 55 | console.log("sending message"); |
34 | 56 | const index = new Date().toISOString(); |
35 | 57 | console.log({ username, uusername: $username }); |
36 | 58 | console.log( |
37 | 59 | `Sending message in 'screenshots' to ${$selectedContact} at index ${index} from ${$username}` |
38 | 60 | ); |
39 | 61 |
|
40 | | - // const receivingContact = ($selectedContact).splice(2); |
| 62 | + // Encrypt screenshot and message |
| 63 | + const encryptSecret = await SEA.secret( |
| 64 | + $receiverKeyPairs.epub, |
| 65 | + $senderKeyPairs |
| 66 | + ); |
| 67 | + const message = { |
| 68 | + image: $selectedScreenshot, |
| 69 | + message: `Heres a screenshot from ${$username}`, |
| 70 | + }; |
| 71 | + const encrypted = await SEA.encrypt(message, encryptSecret); |
41 | 72 |
|
42 | | - db |
43 | | - .get("screenshots") |
| 73 | + db.get("screenshots") |
44 | 74 | .get($selectedContact) |
45 | 75 | .get(index) |
46 | 76 | .put({ |
47 | | - image: $selectedScreenshot, |
48 | | - message: `Heres a screenshot from ${$username}`, |
| 77 | + data: encrypted, |
49 | 78 | from: `${$username}`, |
50 | 79 | }); |
51 | | - } |
| 80 | +
|
| 81 | + }; |
52 | 82 |
|
53 | 83 | // AutoComplete component to search for user contact |
54 | 84 | let currentChosenContact = []; |
55 | 85 | async function* getOptions(selectedOption) { |
56 | 86 | let options = []; |
57 | 87 |
|
58 | | -
|
59 | | - await db.get(`~@${selectedOption}`).once((data, userId) => { |
| 88 | + // Find user in database (if it exists) |
| 89 | + await db.get(`~@${selectedOption}`).once(async (data, userId) => { |
60 | 90 | if (!data) return; // if no result, return |
61 | 91 |
|
62 | | - console.log({ data, key: userId }); |
63 | | -
|
64 | 92 | // Set the current chosen contact |
65 | | - // remove first 2 characters of userId |
66 | | -
|
67 | | -
|
68 | | - selectedContact.set(userId.substring(2)); |
69 | | - console.log({ selectedContact}) |
70 | | - console.log({ selectedContact: $selectedContact }); |
| 93 | + selectedContact.set(userId.substring(2)); // remove the ~@ |
71 | 94 |
|
72 | 95 | options.push({ |
73 | 96 | name: userId, |
74 | 97 | details: `Unique Identifier ${data._}`, |
75 | 98 | }); |
76 | 99 |
|
77 | | - console.log({ key: userId }); |
| 100 | +
|
| 101 | + /** |
| 102 | + * End-to-end encryption (E2EE) |
| 103 | + */ |
| 104 | +
|
| 105 | + // Get the receivers public key for encryption (epub) |
| 106 | + const receiverEPub = await getUserEPub(userId); |
| 107 | +
|
| 108 | + // Get the senders (logged in user) cryptographically secure public/private key pair |
| 109 | + // Contains the public key (pub), the private key (priv), the epub |
| 110 | + // (encryption public key) and the epriv (encryption private key) |
| 111 | + const senderPair = db.user()._.sea; |
| 112 | +
|
| 113 | + // Update state with key pairs |
| 114 | + senderKeyPairs.set({ ...$senderKeyPairs, ...senderPair }); |
| 115 | + receiverKeyPairs.set({ ...$receiverKeyPairs, epub: receiverEPub }); |
| 116 | +
|
78 | 117 | }); |
79 | 118 |
|
80 | 119 | yield options; |
|
0 commit comments