wireweave 0.3.44 → 0.3.45

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/dm.js +39 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wireweave",
3
- "version": "0.3.44",
3
+ "version": "0.3.45",
4
4
  "description": "nostr + webrtc voice + data SDK. networking layer for 247420 projects.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/dm.js CHANGED
@@ -1,41 +1,53 @@
1
+ const GIFT_WRAP_KIND = 1059;
2
+
1
3
  export class DM extends EventTarget {
2
4
  constructor({ relayPool, auth, nostrTools }) {
3
5
  super();
4
6
  if (!relayPool || !auth || !nostrTools) throw new Error('DM: deps required');
5
7
  if (!nostrTools.nip44) throw new Error('nostr-tools nip44 missing');
8
+ if (!nostrTools.nip59) throw new Error('nostr-tools nip59 missing');
6
9
  this.pool = relayPool;
7
10
  this.auth = auth;
8
11
  this.NT = nostrTools;
9
12
  this.subId = null;
10
13
  }
11
14
 
12
- _convKey(peerPubkey) {
13
- if (!this.auth.privkey) throw new Error('DM: privkey required (extension signing not supported for nip44)');
14
- return this.NT.nip44.v2.utils.getConversationKey
15
- ? this.NT.nip44.v2.utils.getConversationKey(this.auth.privkey, peerPubkey)
16
- : this.NT.nip44.getConversationKey(this.auth.privkey, peerPubkey);
17
- }
18
-
15
+ // NIP-17: send is a real kind:14 rumor, gift-wrapped (NIP-59) once per
16
+ // recipient and once more for ourselves (self-copy), each wrap signed by
17
+ // a fresh single-use random key so no relay observer can attribute the
18
+ // wrap's outer envelope to either the sender or the recipient — only the
19
+ // holder of the recipient's (or our own) private key can even see it's a
20
+ // DM at all, let alone read it or learn who sent it.
19
21
  async send(peerPubkey, plaintext) {
20
- const key = this._convKey(peerPubkey);
21
- const ciphertext = this.NT.nip44.v2.encrypt(plaintext, key);
22
- const signed = await this.auth.sign({
22
+ if (!this.auth.privkey) throw new Error('DM: privkey required (extension signing not supported for nip17)');
23
+ const rumor = {
23
24
  kind: 14,
24
25
  created_at: Math.floor(Date.now() / 1000),
25
26
  tags: [['p', peerPubkey]],
26
- content: ciphertext
27
- });
28
- this.pool.publish(signed);
29
- return signed;
27
+ content: plaintext
28
+ };
29
+ const wrapForPeer = this.NT.nip59.wrapEvent(rumor, this.auth.privkey, peerPubkey);
30
+ const wrapForSelf = this.NT.nip59.wrapEvent(rumor, this.auth.privkey, this.auth.pubkey);
31
+ this.pool.publish(wrapForPeer);
32
+ this.pool.publish(wrapForSelf);
33
+ return wrapForPeer;
34
+ }
35
+
36
+ // Unwrap a gift-wrap (kind 1059) down to its rumor and return the
37
+ // plaintext. Requires our own privkey — only the wrap's addressed
38
+ // recipient (the 'p' tag on the outer event) can unwrap it.
39
+ decrypt(wrap) {
40
+ if (!this.auth.privkey) throw new Error('DM: privkey required (extension signing not supported for nip17)');
41
+ const rumor = this.NT.nip59.unwrapEvent(wrap, this.auth.privkey);
42
+ return rumor.content;
30
43
  }
31
44
 
32
- decrypt(event) {
33
- const peer = event.pubkey === this.auth.pubkey
34
- ? (event.tags.find(t => t[0] === 'p')?.[1] || '')
35
- : event.pubkey;
36
- if (!peer) throw new Error('DM: cannot resolve peer');
37
- const key = this._convKey(peer);
38
- return this.NT.nip44.v2.decrypt(event.content, key);
45
+ // Same as decrypt() but returns the full unwrapped rumor (sender pubkey,
46
+ // created_at, tags) alongside the plaintext, for callers that need the
47
+ // real (rumor-level) sender identity rather than the wrap's throwaway key.
48
+ unwrap(wrap) {
49
+ if (!this.auth.privkey) throw new Error('DM: privkey required (extension signing not supported for nip17)');
50
+ return this.NT.nip59.unwrapEvent(wrap, this.auth.privkey);
39
51
  }
40
52
 
41
53
  subscribe(onMessage) {
@@ -43,14 +55,14 @@ export class DM extends EventTarget {
43
55
  const subId = 'dm-' + this.auth.pubkey.slice(0, 16);
44
56
  this.subId = subId;
45
57
  this.pool.subscribe(subId, [
46
- { kinds: [14], '#p': [this.auth.pubkey] },
47
- { kinds: [14], authors: [this.auth.pubkey] }
58
+ { kinds: [GIFT_WRAP_KIND], '#p': [this.auth.pubkey] }
48
59
  ], (event) => {
49
60
  try {
50
- const plaintext = this.decrypt(event);
51
- onMessage({ event, plaintext, peer: event.pubkey === this.auth.pubkey
52
- ? (event.tags.find(t => t[0] === 'p')?.[1] || '')
53
- : event.pubkey });
61
+ const rumor = this.unwrap(event);
62
+ const peer = rumor.pubkey === this.auth.pubkey
63
+ ? (rumor.tags.find(t => t[0] === 'p')?.[1] || '')
64
+ : rumor.pubkey;
65
+ onMessage({ event, rumor, plaintext: rumor.content, peer });
54
66
  } catch (e) {
55
67
  this._emit('error', { event, error: e.message });
56
68
  }