libp2r2p 0.8.0 → 0.10.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.
- package/README.md +72 -45
- package/base16/index.js +6 -3
- package/base36/index.js +12 -8
- package/base62/index.js +15 -11
- package/base64/index.js +18 -2
- package/base93/index.js +19 -7
- package/content-key/event/index.js +40 -12
- package/double-dh/index.js +21 -6
- package/ecdh/index.js +12 -1
- package/error/index.js +32 -0
- package/event/helpers/serialize.js +31 -0
- package/event/index.js +116 -0
- package/idb/index.js +5 -3
- package/idb-queue/index.js +21 -20
- package/index.js +10 -1
- package/key/index.js +31 -18
- package/kind/index.js +244 -0
- package/nip04/index.js +47 -0
- package/nip05/index.js +61 -0
- package/nip19/index.js +176 -43
- package/nip44/helpers.js +95 -0
- package/nip44/index.js +14 -0
- package/nip44-v3/index.js +35 -16
- package/nip46/helpers/frame.js +6 -6
- package/nip46/helpers/url.js +8 -6
- package/nip46/services/bunker-signer.js +7 -6
- package/nip46/services/client.js +8 -7
- package/nip46/services/server-session.js +8 -7
- package/nip46/services/transport.js +9 -8
- package/nip96/index.js +285 -0
- package/nip98/index.js +56 -0
- package/nwt/index.js +241 -0
- package/package.json +22 -5
- package/private-channel/helpers/chunks.js +7 -6
- package/private-channel/helpers/event.js +5 -4
- package/private-channel/index.js +63 -61
- package/private-channel/services/received-chunks.js +4 -3
- package/private-message/index.js +32 -31
- package/private-messenger/index.js +23 -22
- package/private-messenger/recovery/index.js +15 -14
- package/private-messenger/services/channel-state.js +2 -1
- package/relay/helpers/hll.js +3 -1
- package/relay/services/query.js +3 -3
- package/relay/services/relay-connection.js +222 -121
- package/relay/services/relay-pool.js +13 -10
- package/temporary-storage/index.js +3 -1
- package/url/index.js +131 -0
- package/web-storage-queue/index.js +8 -6
- package/i18n/index.js +0 -235
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { generateSecretKey, getPublicKey } from '
|
|
1
|
+
import { generateSecretKey, getPublicKey } from '../../key/index.js'
|
|
2
2
|
import { bytesToBase64, base64ToBytes } from '../../base64/index.js'
|
|
3
3
|
import { bytesToHex } from '../../base16/index.js'
|
|
4
|
-
import {
|
|
4
|
+
import { isValidIykcProof } from '../../content-key/event/index.js'
|
|
5
|
+
import { ValidationError } from '../../error/index.js'
|
|
5
6
|
import * as nip44v3 from '../../nip44-v3/index.js'
|
|
6
7
|
import { JSONL_CHUNK_BYTES } from './chunk-size.js'
|
|
7
8
|
import { ROUTER_KIND } from '../constants/index.js'
|
|
@@ -28,7 +29,7 @@ function rowTempKey (id, index) {
|
|
|
28
29
|
|
|
29
30
|
function normalizeContentKey ({ receiverPubkey, iykcPubkey = '', iykcProof = '' } = {}) {
|
|
30
31
|
if (!iykcPubkey) return { iykcPubkey: '', iykcProof: '' }
|
|
31
|
-
if (!
|
|
32
|
+
if (!isValidIykcProof({ receiverPubkey, iykcPubkey, iykcProof })) throw new ValidationError('INVALID_IYKC_PROOF')
|
|
32
33
|
return { iykcPubkey, iykcProof }
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -157,7 +158,7 @@ function setPreparedRow (id, index, row, temporaryStorage) {
|
|
|
157
158
|
|
|
158
159
|
function readPreparedRow (preparedRows, index) {
|
|
159
160
|
const row = storageFor(preparedRows.temporaryStorage).getItem(rowTempKey(preparedRows.id, index))
|
|
160
|
-
if (typeof row !== 'string') throw new
|
|
161
|
+
if (typeof row !== 'string') throw new ValidationError('MISSING_PREPARED_ROW')
|
|
161
162
|
return row
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -187,7 +188,7 @@ async function prepareEnvelopeRowsOnce ({ id, senderSigner, receivers, receiverC
|
|
|
187
188
|
ciphertext = encrypted[0]
|
|
188
189
|
const nextContentPubkey = encrypted[1] || ''
|
|
189
190
|
if (foundOwnContentPubkey && nextContentPubkey !== usedOwnContentPubkey) {
|
|
190
|
-
throw new
|
|
191
|
+
throw new ValidationError('INCONSISTENT_IMKC_PUBKEY')
|
|
191
192
|
}
|
|
192
193
|
foundOwnContentPubkey = true
|
|
193
194
|
if (nextContentPubkey) usedOwnContentPubkey = nextContentPubkey
|
|
@@ -240,7 +241,7 @@ export function preparedRowIndexesForReceivers (preparedRows, receivers) {
|
|
|
240
241
|
for (const receiver of receivers || []) {
|
|
241
242
|
const pubkey = receiverRecord(receiver, {}).receiverPubkey
|
|
242
243
|
const index = preparedRows?.receiverRowIndexesByPubkey?.[pubkey]
|
|
243
|
-
if (!pubkey || index === undefined) throw new
|
|
244
|
+
if (!pubkey || index === undefined) throw new ValidationError('MISSING_PREPARED_RECEIVER')
|
|
244
245
|
if (seen.has(index)) continue
|
|
245
246
|
seen.add(index)
|
|
246
247
|
indexes.push(index)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NYM_CARRIER_KIND, ROUTER_KIND } from '../constants/index.js'
|
|
2
|
+
import { ValidationError } from '../../error/index.js'
|
|
2
3
|
|
|
3
4
|
const encoder = new TextEncoder()
|
|
4
5
|
|
|
@@ -16,7 +17,7 @@ export function readReceiverTag (event) {
|
|
|
16
17
|
|
|
17
18
|
export function readSenderTag (event) {
|
|
18
19
|
const senderPubkey = event.tags?.find(t => t[0] === 'f')?.[1]
|
|
19
|
-
if (!senderPubkey) throw new
|
|
20
|
+
if (!senderPubkey) throw new ValidationError('MISSING_SENDER_TAG')
|
|
20
21
|
return senderPubkey
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -41,7 +42,7 @@ export function readChunkTag (event) {
|
|
|
41
42
|
const index = Number(tag?.[1])
|
|
42
43
|
const total = Number(tag?.[2])
|
|
43
44
|
if (!Number.isInteger(index) || !Number.isInteger(total) || index < 0 || total < 1) {
|
|
44
|
-
throw new
|
|
45
|
+
throw new ValidationError('INVALID_CHUNK_TAG')
|
|
45
46
|
}
|
|
46
47
|
return { index, total }
|
|
47
48
|
}
|
|
@@ -49,7 +50,7 @@ export function readChunkTag (event) {
|
|
|
49
50
|
export function makeRouterEvent ({ pubkey, senderPubkey, imkcPubkey, imkcProof, receiverPubkey, chunkIndex, chunkTotal, content }) {
|
|
50
51
|
const tags = [['f', senderPubkey]]
|
|
51
52
|
if (imkcPubkey) {
|
|
52
|
-
if (!imkcProof) throw new
|
|
53
|
+
if (!imkcProof) throw new ValidationError('INVALID_IMKC_PROOF')
|
|
53
54
|
tags.push(['imkc', imkcPubkey, imkcProof])
|
|
54
55
|
}
|
|
55
56
|
tags.push(['c', String(chunkIndex), String(chunkTotal)])
|
|
@@ -58,7 +59,7 @@ export function makeRouterEvent ({ pubkey, senderPubkey, imkcPubkey, imkcProof,
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
export function makeNymCarrierEvent ({ innerId, chunkIndex, chunkTotal, content, createdAt = nowSeconds() }) {
|
|
61
|
-
if (!innerId) throw new
|
|
62
|
+
if (!innerId) throw new ValidationError('INNER_EVENT_ID_REQUIRED')
|
|
62
63
|
return {
|
|
63
64
|
kind: NYM_CARRIER_KIND,
|
|
64
65
|
created_at: createdAt,
|
package/private-channel/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { finalizeEvent, getEventHash, isSerializableEvent, isValidEvent } from '../event/index.js'
|
|
2
|
+
import { generateSecretKey, getPublicKey } from '../key/index.js'
|
|
2
3
|
import { bytesToBase64, base64ToBytes } from '../base64/index.js'
|
|
3
4
|
import { hexToBytes } from '../base16/index.js'
|
|
4
|
-
import {
|
|
5
|
+
import { isValidContentKeyProof, isValidIykcProof, makeContentKeyEventForPubkey, parseContentKeyEvent } from '../content-key/event/index.js'
|
|
5
6
|
import { getIykcProofs } from '../content-key/index.js'
|
|
7
|
+
import { ValidationError } from '../error/index.js'
|
|
6
8
|
import * as nip44v3 from '../nip44-v3/index.js'
|
|
7
9
|
import { relayPool } from '../relay/index.js'
|
|
8
10
|
import { JSONL_CHUNK_BYTES, NYM_CARRIER_CHUNK_CHARS } from './helpers/chunk-size.js'
|
|
@@ -43,7 +45,7 @@ function uniq (values) {
|
|
|
43
45
|
function normalizeDeletionPubkey (deletionPubkey) {
|
|
44
46
|
if (deletionPubkey === undefined) return undefined
|
|
45
47
|
if (typeof deletionPubkey !== 'string' || !HEX_PUBKEY.test(deletionPubkey)) {
|
|
46
|
-
throw new
|
|
48
|
+
throw new ValidationError('INVALID_DELETION_PUBKEY')
|
|
47
49
|
}
|
|
48
50
|
return deletionPubkey.toLowerCase()
|
|
49
51
|
}
|
|
@@ -79,7 +81,7 @@ async function nip44v3DecryptText (signer, peerPubkey, kind, ciphertext) {
|
|
|
79
81
|
return base64ToText(await signer.nip44v3Decrypt(peerPubkey, kind, NIP44_V3_SCOPE, ciphertext))
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
function
|
|
84
|
+
function doesModeStoreRecoverySeeds (mode) {
|
|
83
85
|
return mode === 'seeder' || mode === 'watchtower'
|
|
84
86
|
}
|
|
85
87
|
|
|
@@ -89,16 +91,16 @@ async function makeImkcProof ({ senderSigner, senderPubkey, imkcPubkey }) {
|
|
|
89
91
|
if (
|
|
90
92
|
event.pubkey !== senderPubkey ||
|
|
91
93
|
parsed?.iykcPubkey !== imkcPubkey ||
|
|
92
|
-
!
|
|
93
|
-
) throw new
|
|
94
|
+
!isValidContentKeyProof({ ownerPubkey: senderPubkey, contentPubkey: imkcPubkey, proof: parsed?.iykcProof })
|
|
95
|
+
) throw new ValidationError('INVALID_IMKC_PROOF')
|
|
94
96
|
return parsed.iykcProof
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
async function prepareRoutedMessage ({ senderSigner, imkcSigner, privateChannelSigner = senderSigner, privateChannelReaderPubkey, receivers, event, temporaryStorageArea, _getIykcProofs = getIykcProofs }) {
|
|
98
|
-
if (!senderSigner?.getPublicKey) throw new
|
|
99
|
-
if (!senderSigner?.nip44EncryptDoubleDH && !senderSigner?.nip44v3Encrypt) throw new
|
|
100
|
-
if (!privateChannelSigner?.getPublicKey || !privateChannelSigner?.nip44v3Encrypt || !privateChannelSigner?.signEvent) throw new
|
|
101
|
-
if (!Array.isArray(receivers) || !receivers.length) throw new
|
|
100
|
+
if (!senderSigner?.getPublicKey) throw new ValidationError('SENDER_SIGNER_REQUIRED')
|
|
101
|
+
if (!senderSigner?.nip44EncryptDoubleDH && !senderSigner?.nip44v3Encrypt) throw new ValidationError('SIGNER_NIP44V3_ENCRYPT_UNSUPPORTED')
|
|
102
|
+
if (!privateChannelSigner?.getPublicKey || !privateChannelSigner?.nip44v3Encrypt || !privateChannelSigner?.signEvent) throw new ValidationError('PRIVATE_CHANNEL_SIGNER_REQUIRED')
|
|
103
|
+
if (!Array.isArray(receivers) || !receivers.length) throw new ValidationError('NO_RECEIVERS')
|
|
102
104
|
|
|
103
105
|
const senderPubkey = await senderSigner.getPublicKey()
|
|
104
106
|
const useDoubleDh = typeof senderSigner.nip44EncryptDoubleDH === 'function'
|
|
@@ -158,7 +160,7 @@ async function * wrapPreparedEvents ({ privateChannelSigner, receivers, receiver
|
|
|
158
160
|
tags: privateBroadcastTags({ deletionPubkey, createdAt, expirationSeconds }),
|
|
159
161
|
content: await nip44v3EncryptText(privateChannelSigner, context.channelReaderPubkey, PRIVATE_BROADCAST_KIND, JSON.stringify(router))
|
|
160
162
|
})
|
|
161
|
-
if (eventByteLength(outer) > MAX_EVENT_BYTES) throw new
|
|
163
|
+
if (eventByteLength(outer) > MAX_EVENT_BYTES) throw new ValidationError('EVENT_TOO_LARGE')
|
|
162
164
|
yield outer
|
|
163
165
|
}
|
|
164
166
|
} finally {
|
|
@@ -193,17 +195,17 @@ export async function wrapEvent (options) {
|
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
export async function * wrapNymEvents ({ nymSigner, privateChannelSigner, privateChannelReaderPubkey, deletionPubkey, event, expirationSeconds = EXPIRATION_SECONDS }) {
|
|
196
|
-
if (!nymSigner?.getPublicKey || !nymSigner?.signEvent) throw new
|
|
197
|
-
if (!privateChannelSigner?.getPublicKey || !privateChannelSigner?.nip44v3Encrypt || !privateChannelSigner?.signEvent) throw new
|
|
198
|
+
if (!nymSigner?.getPublicKey || !nymSigner?.signEvent) throw new ValidationError('NYM_SIGNER_REQUIRED')
|
|
199
|
+
if (!privateChannelSigner?.getPublicKey || !privateChannelSigner?.nip44v3Encrypt || !privateChannelSigner?.signEvent) throw new ValidationError('PRIVATE_CHANNEL_SIGNER_REQUIRED')
|
|
198
200
|
const normalizedDeletionPubkey = normalizeDeletionPubkey(deletionPubkey)
|
|
199
201
|
|
|
200
202
|
const nymPubkey = await nymSigner.getPublicKey()
|
|
201
203
|
const channelPubkey = await privateChannelSigner.getPublicKey()
|
|
202
204
|
const channelReaderPubkey = privateChannelReaderPubkey || channelPubkey
|
|
203
|
-
const wireEvent =
|
|
205
|
+
const wireEvent = hasEventSignature(event)
|
|
204
206
|
? assertValidSignedInnerEvent(event)
|
|
205
207
|
: wireNymRumor({ ...event, created_at: event?.created_at !== undefined ? event.created_at : nowSeconds() })
|
|
206
|
-
const innerEvent =
|
|
208
|
+
const innerEvent = hasEventSignature(wireEvent) ? wireEvent : normalizeNymRumor(wireEvent, nymPubkey)
|
|
207
209
|
const encoded = bytesToBase64(encoder.encode(JSON.stringify(wireEvent)))
|
|
208
210
|
const total = Math.max(1, Math.ceil(encoded.length / NYM_CARRIER_CHUNK_CHARS))
|
|
209
211
|
const carrierCreatedAt = nowSeconds()
|
|
@@ -223,7 +225,7 @@ export async function * wrapNymEvents ({ nymSigner, privateChannelSigner, privat
|
|
|
223
225
|
tags: privateBroadcastTags({ deletionPubkey: normalizedDeletionPubkey, createdAt, expirationSeconds }),
|
|
224
226
|
content: await nip44v3EncryptText(privateChannelSigner, channelReaderPubkey, PRIVATE_BROADCAST_KIND, JSON.stringify(carrier))
|
|
225
227
|
})
|
|
226
|
-
if (eventByteLength(outer) > MAX_EVENT_BYTES) throw new
|
|
228
|
+
if (eventByteLength(outer) > MAX_EVENT_BYTES) throw new ValidationError('EVENT_TOO_LARGE')
|
|
227
229
|
yield outer
|
|
228
230
|
}
|
|
229
231
|
}
|
|
@@ -244,31 +246,31 @@ function joinedRouter (router, content = '') {
|
|
|
244
246
|
|
|
245
247
|
function parsePayloadEnvelope (line, index = 0) {
|
|
246
248
|
const record = JSON.parse(line)
|
|
247
|
-
if (!Array.isArray(record) || record.length !== 1 || typeof record[0] !== 'string') throw new
|
|
249
|
+
if (!Array.isArray(record) || record.length !== 1 || typeof record[0] !== 'string') throw new ValidationError('INVALID_PAYLOAD_ENVELOPE')
|
|
248
250
|
return { index, type: 'payload', ciphertext: record[0] }
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
function parseRecipientEnvelope (line, index = 0) {
|
|
252
254
|
const record = JSON.parse(line)
|
|
253
|
-
if (!Array.isArray(record) || (record.length !== 2 && record.length !== 4)) throw new
|
|
255
|
+
if (!Array.isArray(record) || (record.length !== 2 && record.length !== 4)) throw new ValidationError('INVALID_RECIPIENT_ENVELOPE')
|
|
254
256
|
const [receiverPubkey, ciphertext, iykcPubkey = '', iykcProof = ''] = record
|
|
255
257
|
return { index, receiverPubkey, ciphertext, iykcPubkey, iykcProof }
|
|
256
258
|
}
|
|
257
259
|
|
|
258
|
-
function
|
|
260
|
+
function hasEventSignature (event) {
|
|
259
261
|
return Object.prototype.hasOwnProperty.call(event || {}, 'sig')
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
function assertValidSignedInnerEvent (event) {
|
|
263
|
-
if (!
|
|
264
|
-
throw new
|
|
265
|
+
if (!isValidEvent(event)) {
|
|
266
|
+
throw new ValidationError('INVALID_SIGNED_INNER_EVENT')
|
|
265
267
|
}
|
|
266
268
|
return event
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
function normalizeNymRumor (event, pubkey) {
|
|
270
272
|
const normalized = { ...event, pubkey }
|
|
271
|
-
if (!
|
|
273
|
+
if (!isSerializableEvent(normalized)) throw new ValidationError('INVALID_NYM_RUMOR')
|
|
272
274
|
return { ...normalized, id: getEventHash(normalized) }
|
|
273
275
|
}
|
|
274
276
|
|
|
@@ -282,11 +284,11 @@ function wireNymRumor (event = {}) {
|
|
|
282
284
|
}
|
|
283
285
|
|
|
284
286
|
function assertValidNymCarrierEvent (carrier) {
|
|
285
|
-
if (!
|
|
286
|
-
throw new
|
|
287
|
+
if (!isValidEvent(carrier)) {
|
|
288
|
+
throw new ValidationError('INVALID_NYM_CARRIER')
|
|
287
289
|
}
|
|
288
|
-
if (carrier.kind !== NYM_CARRIER_KIND) throw new
|
|
289
|
-
if (!readIdTag(carrier)) throw new
|
|
290
|
+
if (carrier.kind !== NYM_CARRIER_KIND) throw new ValidationError('INVALID_NYM_CARRIER_KIND')
|
|
291
|
+
if (!readIdTag(carrier)) throw new ValidationError('MISSING_NYM_CARRIER_ID')
|
|
290
292
|
readChunkTag(carrier)
|
|
291
293
|
return carrier
|
|
292
294
|
}
|
|
@@ -296,7 +298,7 @@ function nymCarrierGroupId (carrier) {
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
function validateNymCarriers (carriers) {
|
|
299
|
-
if (!Array.isArray(carriers) || !carriers.length) throw new
|
|
301
|
+
if (!Array.isArray(carriers) || !carriers.length) throw new ValidationError('NYM_CARRIERS_REQUIRED')
|
|
300
302
|
const chunks = []
|
|
301
303
|
let nymPubkey = ''
|
|
302
304
|
let innerId = ''
|
|
@@ -312,15 +314,15 @@ function validateNymCarriers (carriers) {
|
|
|
312
314
|
total = nextTotal
|
|
313
315
|
}
|
|
314
316
|
if (carrier.pubkey !== nymPubkey || nextInnerId !== innerId || nextTotal !== total) {
|
|
315
|
-
throw new
|
|
317
|
+
throw new ValidationError('MISMATCHED_NYM_CARRIER_CHUNKS')
|
|
316
318
|
}
|
|
317
|
-
if (chunks[index] !== undefined) throw new
|
|
319
|
+
if (chunks[index] !== undefined) throw new ValidationError('DUPLICATE_NYM_CARRIER_CHUNK')
|
|
318
320
|
chunks[index] = carrier.content
|
|
319
321
|
}
|
|
320
322
|
|
|
321
|
-
if (chunks.length !== total) throw new
|
|
323
|
+
if (chunks.length !== total) throw new ValidationError('MISSING_NYM_CARRIER_CHUNK')
|
|
322
324
|
for (let index = 0; index < total; index++) {
|
|
323
|
-
if (chunks[index] == null) throw new
|
|
325
|
+
if (chunks[index] == null) throw new ValidationError('MISSING_NYM_CARRIER_CHUNK')
|
|
324
326
|
}
|
|
325
327
|
return { nymPubkey, innerId, content: chunks.join('') }
|
|
326
328
|
}
|
|
@@ -331,44 +333,44 @@ export function eventFromNymCarriers (carriers) {
|
|
|
331
333
|
try {
|
|
332
334
|
parsed = JSON.parse(decoder.decode(base64ToBytes(content)))
|
|
333
335
|
} catch {
|
|
334
|
-
throw new
|
|
336
|
+
throw new ValidationError('INVALID_NYM_CARRIER_PAYLOAD')
|
|
335
337
|
}
|
|
336
338
|
|
|
337
|
-
if (
|
|
339
|
+
if (hasEventSignature(parsed)) {
|
|
338
340
|
const event = assertValidSignedInnerEvent(parsed)
|
|
339
|
-
if (event.id !== innerId) throw new
|
|
341
|
+
if (event.id !== innerId) throw new ValidationError('INVALID_NYM_CARRIER_INNER_ID')
|
|
340
342
|
return event
|
|
341
343
|
}
|
|
342
344
|
|
|
343
345
|
const event = normalizeNymRumor(parsed, nymPubkey)
|
|
344
|
-
if (event.id !== innerId) throw new
|
|
346
|
+
if (event.id !== innerId) throw new ValidationError('INVALID_NYM_CARRIER_INNER_ID')
|
|
345
347
|
return event
|
|
346
348
|
}
|
|
347
349
|
|
|
348
350
|
function assertValidEnvelopeIykcProof (envelope) {
|
|
349
351
|
if (!envelope.iykcPubkey) return
|
|
350
|
-
if (!
|
|
352
|
+
if (!isValidIykcProof({
|
|
351
353
|
receiverPubkey: envelope.receiverPubkey,
|
|
352
354
|
iykcPubkey: envelope.iykcPubkey,
|
|
353
355
|
iykcProof: envelope.iykcProof
|
|
354
|
-
})) throw new
|
|
356
|
+
})) throw new ValidationError('INVALID_IYKC_PROOF')
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
function assertValidRouterImkcProof ({ router, senderPubkey, imkcPubkey, imkcProof }) {
|
|
358
360
|
if (!hasImkcTag(router)) return
|
|
359
|
-
if (!
|
|
361
|
+
if (!isValidContentKeyProof({
|
|
360
362
|
ownerPubkey: senderPubkey,
|
|
361
363
|
contentPubkey: imkcPubkey,
|
|
362
364
|
proof: imkcProof
|
|
363
|
-
})) throw new
|
|
365
|
+
})) throw new ValidationError('INVALID_IMKC_PROOF')
|
|
364
366
|
}
|
|
365
367
|
|
|
366
368
|
function eventFromPayload ({ payloadCiphertext, messageSeckey, senderPubkey }) {
|
|
367
|
-
if (!HEX_SECKEY.test(messageSeckey || '')) throw new
|
|
369
|
+
if (!HEX_SECKEY.test(messageSeckey || '')) throw new ValidationError('INVALID_MESSAGE_SECKEY')
|
|
368
370
|
const messageSecretKey = hexToBytes(messageSeckey)
|
|
369
371
|
const messagePubkey = getPublicKey(messageSecretKey)
|
|
370
372
|
const decrypted = JSON.parse(nip44v3.decrypt(messageSecretKey, messagePubkey, ROUTER_KIND, NIP44_V3_SCOPE, payloadCiphertext))
|
|
371
|
-
if (
|
|
373
|
+
if (hasEventSignature(decrypted)) return assertValidSignedInnerEvent(decrypted)
|
|
372
374
|
const normalized = { ...decrypted, pubkey: senderPubkey }
|
|
373
375
|
return { ...normalized, id: getEventHash(normalized) }
|
|
374
376
|
}
|
|
@@ -377,7 +379,7 @@ async function unwrapRecipientEnvelope ({ payloadCiphertext, envelope, receiverS
|
|
|
377
379
|
if (receiverPubkey && envelope.receiverPubkey !== receiverPubkey) return null
|
|
378
380
|
let messageSeckey
|
|
379
381
|
if (envelope.iykcPubkey || imkcPubkey) {
|
|
380
|
-
if (!receiverSigner?.nip44DecryptDoubleDH) throw new
|
|
382
|
+
if (!receiverSigner?.nip44DecryptDoubleDH) throw new ValidationError('RECEIVER_DOUBLE_DH_UNSUPPORTED')
|
|
381
383
|
if (envelope.iykcPubkey) {
|
|
382
384
|
assertValidEnvelopeIykcProof(envelope)
|
|
383
385
|
}
|
|
@@ -390,7 +392,7 @@ async function unwrapRecipientEnvelope ({ payloadCiphertext, envelope, receiverS
|
|
|
390
392
|
envelope.iykcPubkey || ''
|
|
391
393
|
))
|
|
392
394
|
} else {
|
|
393
|
-
if (!receiverSigner?.nip44v3Decrypt) throw new
|
|
395
|
+
if (!receiverSigner?.nip44v3Decrypt) throw new ValidationError('RECEIVER_SIGNER_NIP44V3_DECRYPT_UNSUPPORTED')
|
|
394
396
|
messageSeckey = base64ToText(await receiverSigner.nip44v3Decrypt(senderPubkey, ROUTER_KIND, rowScope, envelope.ciphertext))
|
|
395
397
|
}
|
|
396
398
|
return eventFromPayload({ payloadCiphertext, messageSeckey, senderPubkey })
|
|
@@ -398,12 +400,12 @@ async function unwrapRecipientEnvelope ({ payloadCiphertext, envelope, receiverS
|
|
|
398
400
|
|
|
399
401
|
export async function unwrapEvent ({ receiverSigner, privateChannelSigner = receiverSigner, privateChannelReaderSigner = privateChannelSigner, privateChannelReaderPubkey, event, receiverPubkey }) {
|
|
400
402
|
if (!event || event.kind !== PRIVATE_BROADCAST_KIND) return null
|
|
401
|
-
if (!receiverSigner?.nip44DecryptDoubleDH && !receiverSigner?.nip44v3Decrypt) throw new
|
|
403
|
+
if (!receiverSigner?.nip44DecryptDoubleDH && !receiverSigner?.nip44v3Decrypt) throw new ValidationError('RECEIVER_SIGNER_NIP44V3_DECRYPT_UNSUPPORTED')
|
|
402
404
|
const channelReaderSigner = privateChannelReaderSigner || privateChannelSigner
|
|
403
|
-
if (!channelReaderSigner?.nip44v3Decrypt) throw new
|
|
405
|
+
if (!channelReaderSigner?.nip44v3Decrypt) throw new ValidationError('PRIVATE_CHANNEL_READER_REQUIRED')
|
|
404
406
|
|
|
405
407
|
const channelPubkey = event.pubkey || await privateChannelSigner?.getPublicKey?.()
|
|
406
|
-
if (!channelPubkey) throw new
|
|
408
|
+
if (!channelPubkey) throw new ValidationError('PRIVATE_CHANNEL_PUBKEY_REQUIRED')
|
|
407
409
|
const router = await decryptRouter({
|
|
408
410
|
content: event.content,
|
|
409
411
|
channelPubkey,
|
|
@@ -411,14 +413,14 @@ export async function unwrapEvent ({ receiverSigner, privateChannelSigner = rece
|
|
|
411
413
|
channelReaderSigner,
|
|
412
414
|
channelReaderPubkey: privateChannelReaderPubkey
|
|
413
415
|
})
|
|
414
|
-
if (router.kind !== ROUTER_KIND) throw new
|
|
416
|
+
if (router.kind !== ROUTER_KIND) throw new ValidationError('INVALID_ROUTER_KIND')
|
|
415
417
|
if (receiverPubkey && readReceiverTag(router) && readReceiverTag(router) !== receiverPubkey) return null
|
|
416
418
|
|
|
417
419
|
const senderPubkey = readSenderTag(router)
|
|
418
420
|
const imkcPubkey = readImkcTag(router)
|
|
419
421
|
assertValidRouterImkcProof({ router, senderPubkey, imkcPubkey, imkcProof: readImkcProof(router) })
|
|
420
422
|
const lines = decodeChunkLines(router.content)
|
|
421
|
-
if (!lines.length) throw new
|
|
423
|
+
if (!lines.length) throw new ValidationError('MISSING_PAYLOAD_ENVELOPE')
|
|
422
424
|
const payload = parsePayloadEnvelope(lines[0], 0)
|
|
423
425
|
for (let index = 1; index < lines.length; index++) {
|
|
424
426
|
const event = await unwrapRecipientEnvelope({
|
|
@@ -443,7 +445,7 @@ function relayReceiverEntries (relayToReceivers) {
|
|
|
443
445
|
if (!relayToReceivers) return []
|
|
444
446
|
if (relayToReceivers instanceof Map) return [...relayToReceivers.entries()]
|
|
445
447
|
if (typeof relayToReceivers === 'object') return Object.entries(relayToReceivers)
|
|
446
|
-
throw new
|
|
448
|
+
throw new ValidationError('INVALID_RELAY_RECEIVERS')
|
|
447
449
|
}
|
|
448
450
|
|
|
449
451
|
function groupedRelayReceivers ({ relayToReceivers, receivers }) {
|
|
@@ -467,7 +469,7 @@ function groupedRelayReceivers ({ relayToReceivers, receivers }) {
|
|
|
467
469
|
const pubkeys = uniq(Array.isArray(value) ? value : [value])
|
|
468
470
|
if (!pubkeys.length) continue
|
|
469
471
|
for (const pubkey of pubkeys) {
|
|
470
|
-
if (!wanted.has(pubkey)) throw new
|
|
472
|
+
if (!wanted.has(pubkey)) throw new ValidationError('RELAY_RECEIVER_NOT_REQUESTED')
|
|
471
473
|
covered.add(pubkey)
|
|
472
474
|
}
|
|
473
475
|
const key = [...pubkeys].sort().join(',')
|
|
@@ -481,9 +483,9 @@ function groupedRelayReceivers ({ relayToReceivers, receivers }) {
|
|
|
481
483
|
groupsByKey.get(key).relays.push(relay)
|
|
482
484
|
}
|
|
483
485
|
|
|
484
|
-
if (!groupsByKey.size) throw new
|
|
486
|
+
if (!groupsByKey.size) throw new ValidationError('NO_RELAYS')
|
|
485
487
|
for (const pubkey of orderedPubkeys) {
|
|
486
|
-
if (!covered.has(pubkey)) throw new
|
|
488
|
+
if (!covered.has(pubkey)) throw new ValidationError('RELAY_RECEIVER_MISSING')
|
|
487
489
|
}
|
|
488
490
|
|
|
489
491
|
return [...groupsByKey.values()].map(group => ({
|
|
@@ -554,7 +556,7 @@ function readSignerFromMap (signersByPubkey, pubkey) {
|
|
|
554
556
|
|
|
555
557
|
async function decryptRouter ({ content, channelPubkey, channelSigner, channelReaderSigner, channelReaderPubkey }) {
|
|
556
558
|
const signer = channelReaderSigner || channelSigner
|
|
557
|
-
if (!signer?.nip44v3Decrypt) throw new
|
|
559
|
+
if (!signer?.nip44v3Decrypt) throw new ValidationError('PRIVATE_CHANNEL_READER_REQUIRED')
|
|
558
560
|
|
|
559
561
|
const readerPubkey = channelReaderPubkey || channelPubkey
|
|
560
562
|
const signerPubkey = await signer.getPublicKey?.()
|
|
@@ -697,7 +699,7 @@ function createProcessor ({
|
|
|
697
699
|
const channelReaderPubkey = readValueFromMap(privateChannelReaderPubkeysByPubkey, channelPubkey) || privateChannelReaderPubkey || channelPubkey
|
|
698
700
|
const channelMode = readValueFromMap(modeByPubkey, channelPubkey) || mode
|
|
699
701
|
const channelReceivedChunkTtlMs = readValueFromMap(receivedChunkTtlMsByPubkey, channelPubkey) ?? receivedChunkTtlMs
|
|
700
|
-
if (!channelPubkey) throw new
|
|
702
|
+
if (!channelPubkey) throw new ValidationError('PRIVATE_CHANNEL_PUBKEY_REQUIRED')
|
|
701
703
|
|
|
702
704
|
const decrypted = await decryptRouter({
|
|
703
705
|
content: outer.content,
|
|
@@ -734,7 +736,7 @@ function createProcessor ({
|
|
|
734
736
|
|
|
735
737
|
const carriers = (await receivedChunks.readChunkContents(groupKey)).map(raw => JSON.parse(raw))
|
|
736
738
|
const event = eventFromNymCarriers(carriers)
|
|
737
|
-
const shouldSeed =
|
|
739
|
+
const shouldSeed = doesModeStoreRecoverySeeds(channelMode)
|
|
738
740
|
if (shouldSeed) {
|
|
739
741
|
await onSeedEvent?.({
|
|
740
742
|
recordType: 'nymCarrier_v1',
|
|
@@ -782,7 +784,7 @@ function createProcessor ({
|
|
|
782
784
|
missing: status.missing
|
|
783
785
|
})
|
|
784
786
|
|
|
785
|
-
const shouldSeed =
|
|
787
|
+
const shouldSeed = doesModeStoreRecoverySeeds(channelMode)
|
|
786
788
|
const sentByReceiver = receiverPubkey && senderPubkey === receiverPubkey
|
|
787
789
|
// Recovery seeders and own-sent messages need the full recipient list;
|
|
788
790
|
// regular leechers can stop as soon as their envelope is decrypted.
|
|
@@ -796,7 +798,7 @@ function createProcessor ({
|
|
|
796
798
|
helpers.rememberPayloadCiphertext(groupMeta, parsePayloadEnvelope(line, rowIndex).ciphertext)
|
|
797
799
|
return
|
|
798
800
|
}
|
|
799
|
-
if (!groupMeta.payloadCiphertext) throw new
|
|
801
|
+
if (!groupMeta.payloadCiphertext) throw new ValidationError('MISSING_PAYLOAD_ENVELOPE')
|
|
800
802
|
const envelope = parseRecipientEnvelope(line, rowIndex)
|
|
801
803
|
helpers.rememberReceiverPubkey(groupMeta, envelope.receiverPubkey)
|
|
802
804
|
|
|
@@ -893,7 +895,7 @@ function shouldIgnoreGroupError (err) {
|
|
|
893
895
|
}
|
|
894
896
|
|
|
895
897
|
export async function fetch ({ receiverSigner, iykcSigner, privateChannelSigner = receiverSigner, privateChannelSignersByPubkey, privateChannelReaderSigner = privateChannelSigner, privateChannelReaderSignersByPubkey, privateChannelReaderPubkey, privateChannelReaderPubkeysByPubkey, privateChannelPubkey, privateChannelPubkeys, receiverPubkey, relays, onChunk, onEvent, onNymEvent, onSeedEvent, onContentKeyUsage, onError, since, until, limit, mode = 'leecher', modeByPubkey, receivedChunkTtlMs = DEFAULT_RECEIVED_CHUNK_TTL_MS, receivedChunkTtlMsByPubkey, receivedChunkMaxBytes = DEFAULT_RECEIVED_CHUNK_MAX_BYTES, receivedChunkIndexedDB = globalThis.indexedDB, ignoredGroupTtlMs = DEFAULT_IGNORED_GROUP_TTL_MS, ignoredGroupMaxEntries = DEFAULT_IGNORED_GROUP_MAX_ENTRIES, _getEvents = getEvents }) {
|
|
896
|
-
if (!relays?.length) throw new
|
|
898
|
+
if (!relays?.length) throw new ValidationError('NO_RELAYS')
|
|
897
899
|
const authors = privateChannelPubkeyList({ privateChannelPubkey, privateChannelPubkeys })
|
|
898
900
|
const filter = { kinds: [PRIVATE_BROADCAST_KIND] }
|
|
899
901
|
if (authors.length) filter.authors = authors
|
|
@@ -916,9 +918,9 @@ export async function fetch ({ receiverSigner, iykcSigner, privateChannelSigner
|
|
|
916
918
|
}
|
|
917
919
|
|
|
918
920
|
export function subscribe ({ receiverSigner, iykcSigner, privateChannelSigner = receiverSigner, privateChannelSignersByPubkey, privateChannelReaderSigner = privateChannelSigner, privateChannelReaderSignersByPubkey, privateChannelReaderPubkey, privateChannelReaderPubkeysByPubkey, privateChannelPubkey, privateChannelPubkeys, receiverPubkey, relays, onChunk, onEvent, onNymEvent, onSeedEvent, onContentKeyUsage, onError, since = nowSeconds() - 5, limit, liveOnly = false, mode = 'leecher', modeByPubkey, receivedChunkTtlMs = DEFAULT_RECEIVED_CHUNK_TTL_MS, receivedChunkTtlMsByPubkey, receivedChunkMaxBytes = DEFAULT_RECEIVED_CHUNK_MAX_BYTES, receivedChunkIndexedDB = globalThis.indexedDB, ignoredGroupTtlMs = DEFAULT_IGNORED_GROUP_TTL_MS, ignoredGroupMaxEntries = DEFAULT_IGNORED_GROUP_MAX_ENTRIES, _liveEventsGenerator = getLiveEventsGenerator, _eventsFeedGenerator = getEventsFeedGenerator }) {
|
|
919
|
-
if (!relays?.length) throw new
|
|
920
|
-
if (receiverSigner && !receiverSigner?.nip44DecryptDoubleDH && !receiverSigner?.nip44v3Decrypt) throw new
|
|
921
|
-
if (!privateChannelReaderSigner && !privateChannelReaderSignersByPubkey && !privateChannelSigner && !privateChannelSignersByPubkey) throw new
|
|
921
|
+
if (!relays?.length) throw new ValidationError('NO_RELAYS')
|
|
922
|
+
if (receiverSigner && !receiverSigner?.nip44DecryptDoubleDH && !receiverSigner?.nip44v3Decrypt) throw new ValidationError('RECEIVER_SIGNER_NIP44V3_DECRYPT_UNSUPPORTED')
|
|
923
|
+
if (!privateChannelReaderSigner && !privateChannelReaderSignersByPubkey && !privateChannelSigner && !privateChannelSignersByPubkey) throw new ValidationError('PRIVATE_CHANNEL_READER_REQUIRED')
|
|
922
924
|
|
|
923
925
|
const authors = privateChannelPubkeyList({ privateChannelPubkey, privateChannelPubkeys })
|
|
924
926
|
const filter = { kinds: [PRIVATE_BROADCAST_KIND], since }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { bytesToBase64 } from '../../base64/index.js'
|
|
2
|
+
import { ValidationError } from '../../error/index.js'
|
|
2
3
|
import { run } from '../../idb/index.js'
|
|
3
4
|
|
|
4
5
|
export const DEFAULT_RECEIVED_CHUNK_TTL_MS = 60 * 60 * 1000 // 1 hour
|
|
@@ -106,7 +107,7 @@ function normalizeBytes (value) {
|
|
|
106
107
|
if (value instanceof Uint8Array) return new Uint8Array(value)
|
|
107
108
|
if (value instanceof ArrayBuffer) return new Uint8Array(value)
|
|
108
109
|
if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
|
|
109
|
-
throw new
|
|
110
|
+
throw new ValidationError('RECEIVED_CHUNK_BYTES_REQUIRED')
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
function uniq (values) {
|
|
@@ -362,9 +363,9 @@ export function createReceivedChunkStore ({
|
|
|
362
363
|
}
|
|
363
364
|
|
|
364
365
|
async function put ({ channelPubkey, routerPubkey, index, total, contentBytes, ttlMs }) {
|
|
365
|
-
if (!channelPubkey || !routerPubkey) throw new
|
|
366
|
+
if (!channelPubkey || !routerPubkey) throw new ValidationError('RECEIVED_CHUNK_GROUP_REQUIRED')
|
|
366
367
|
if (!Number.isSafeInteger(index) || !Number.isSafeInteger(total) || index < 0 || total < 1 || index >= total) {
|
|
367
|
-
throw new
|
|
368
|
+
throw new ValidationError('INVALID_RECEIVED_CHUNK_INDEX')
|
|
368
369
|
}
|
|
369
370
|
const bytes = normalizeBytes(contentBytes)
|
|
370
371
|
await ready()
|