libp2r2p 0.0.1

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 (63) hide show
  1. package/AGENTS.md +20 -0
  2. package/README.md +129 -0
  3. package/base16/index.js +14 -0
  4. package/base64/index.js +29 -0
  5. package/content-key/event/index.js +85 -0
  6. package/content-key/index.js +1 -0
  7. package/content-key/services/iykc-proof.js +129 -0
  8. package/double-dh/index.js +166 -0
  9. package/ecdh/index.js +8 -0
  10. package/idb/index.js +64 -0
  11. package/idb-queue/index.js +793 -0
  12. package/index.js +21 -0
  13. package/key/index.js +139 -0
  14. package/network/index.js +68 -0
  15. package/nip44-v3/index.js +175 -0
  16. package/nip46/constants/index.js +5 -0
  17. package/nip46/helpers/frame.js +51 -0
  18. package/nip46/helpers/url.js +96 -0
  19. package/nip46/index.js +5 -0
  20. package/nip46/services/bunker-signer.js +49 -0
  21. package/nip46/services/client.js +252 -0
  22. package/nip46/services/server-session.js +225 -0
  23. package/nip46/services/transport.js +265 -0
  24. package/package.json +49 -0
  25. package/private-channel/constants/index.js +5 -0
  26. package/private-channel/helpers/chunk-size.js +61 -0
  27. package/private-channel/helpers/chunks.js +282 -0
  28. package/private-channel/helpers/event.js +68 -0
  29. package/private-channel/index.js +942 -0
  30. package/private-channel/services/received-chunks.js +398 -0
  31. package/private-message/index.js +672 -0
  32. package/private-messenger/constants/index.js +1 -0
  33. package/private-messenger/index.js +1431 -0
  34. package/private-messenger/recovery/index.js +316 -0
  35. package/relay/constants/index.js +18 -0
  36. package/relay/helpers/hll.js +62 -0
  37. package/relay/helpers/publish.js +100 -0
  38. package/relay/helpers/routing.js +36 -0
  39. package/relay/helpers/timer.js +4 -0
  40. package/relay/index.js +4 -0
  41. package/relay/services/query.js +224 -0
  42. package/relay/services/relay-connection.js +156 -0
  43. package/relay/services/relay-pool.js +908 -0
  44. package/temporary-storage/index.js +85 -0
  45. package/tests/base16.test.js +19 -0
  46. package/tests/base64.test.js +18 -0
  47. package/tests/content-key-event.test.js +48 -0
  48. package/tests/fixtures/nip44v3-vectors.json +1 -0
  49. package/tests/helpers/test-signer.js +185 -0
  50. package/tests/idb-queue.test.js +153 -0
  51. package/tests/idb.test.js +91 -0
  52. package/tests/nip44-v3.test.js +73 -0
  53. package/tests/nip46.test.js +668 -0
  54. package/tests/private-channel.test.js +1899 -0
  55. package/tests/private-message.test.js +460 -0
  56. package/tests/private-messenger.test.js +1715 -0
  57. package/tests/queries.test.js +101 -0
  58. package/tests/queue-parity.test.js +105 -0
  59. package/tests/relay-hll.test.js +32 -0
  60. package/tests/relay-pool.test.js +2067 -0
  61. package/tests/temporary-storage.test.js +89 -0
  62. package/tests/web-storage-queue.test.js +480 -0
  63. package/web-storage-queue/index.js +652 -0
@@ -0,0 +1,85 @@
1
+ export const TEMPORARY_STORAGE_KEYS_KEY = 'libp2r2p:temporary-storage:keys'
2
+
3
+ function normalizeKeys (keys) {
4
+ if (!Array.isArray(keys)) return []
5
+
6
+ const out = []
7
+ const seen = new Set()
8
+ for (const key of keys) {
9
+ if (typeof key !== 'string' || !key || key === TEMPORARY_STORAGE_KEYS_KEY || seen.has(key)) continue
10
+ seen.add(key)
11
+ out.push(key)
12
+ }
13
+ return out
14
+ }
15
+
16
+ export function createTemporaryStorage ({ storageArea = globalThis.sessionStorage } = {}) {
17
+ function storage () {
18
+ return storageArea
19
+ }
20
+
21
+ function readTrackedKeys () {
22
+ try {
23
+ return normalizeKeys(JSON.parse(storage().getItem(TEMPORARY_STORAGE_KEYS_KEY) || '[]'))
24
+ } catch {
25
+ return []
26
+ }
27
+ }
28
+
29
+ function writeTrackedKeys (keys) {
30
+ const normalized = normalizeKeys(keys)
31
+ if (normalized.length) storage().setItem(TEMPORARY_STORAGE_KEYS_KEY, JSON.stringify(normalized))
32
+ else storage().removeItem(TEMPORARY_STORAGE_KEYS_KEY)
33
+ }
34
+
35
+ function trackTemporaryKey (key) {
36
+ const tracked = readTrackedKeys()
37
+ if (tracked.includes(key)) return
38
+ writeTrackedKeys(tracked.concat(key))
39
+ }
40
+
41
+ function untrackTemporaryKeys (keys) {
42
+ const remove = new Set(normalizeKeys(keys))
43
+ if (!remove.size) return
44
+ writeTrackedKeys(readTrackedKeys().filter(key => !remove.has(key)))
45
+ }
46
+
47
+ function cleanup () {
48
+ for (const key of readTrackedKeys()) storage().removeItem(key)
49
+ storage().removeItem(TEMPORARY_STORAGE_KEYS_KEY)
50
+ }
51
+
52
+ function getItem (key) {
53
+ return storage().getItem(key)
54
+ }
55
+
56
+ function setItem (key, value) {
57
+ if (typeof key !== 'string' || !key || key === TEMPORARY_STORAGE_KEYS_KEY) throw new Error('INVALID_TEMPORARY_STORAGE_KEY')
58
+ trackTemporaryKey(key)
59
+ storage().setItem(key, value)
60
+ }
61
+
62
+ function removeItems (keys) {
63
+ const normalized = normalizeKeys(keys)
64
+ for (const key of normalized) storage().removeItem(key)
65
+ untrackTemporaryKeys(normalized)
66
+ }
67
+
68
+ return { cleanup, getItem, setItem, removeItems }
69
+ }
70
+
71
+ export function cleanupTemporaryStorage ({ storageArea = globalThis.sessionStorage } = {}) {
72
+ createTemporaryStorage({ storageArea }).cleanup()
73
+ }
74
+
75
+ export function getTemporaryItem (key, { storageArea = globalThis.sessionStorage } = {}) {
76
+ return createTemporaryStorage({ storageArea }).getItem(key)
77
+ }
78
+
79
+ export function setTemporaryItem (key, value, { storageArea = globalThis.sessionStorage } = {}) {
80
+ createTemporaryStorage({ storageArea }).setItem(key, value)
81
+ }
82
+
83
+ export function removeTemporaryItems (keys, { storageArea = globalThis.sessionStorage } = {}) {
84
+ createTemporaryStorage({ storageArea }).removeItems(keys)
85
+ }
@@ -0,0 +1,19 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import {
5
+ base16ToBytes,
6
+ bytesToBase16,
7
+ bytesToHex,
8
+ hexToBytes
9
+ } from '../base16/index.js'
10
+
11
+ test('base16 helpers encode bytes and expose hex aliases', () => {
12
+ const bytes = new Uint8Array([0, 1, 15, 16, 254, 255])
13
+ const encoded = '00010f10feff'
14
+
15
+ assert.equal(bytesToBase16(bytes), encoded)
16
+ assert.deepEqual(base16ToBytes(encoded), bytes)
17
+ assert.equal(bytesToHex, bytesToBase16)
18
+ assert.equal(hexToBytes, base16ToBytes)
19
+ })
@@ -0,0 +1,18 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import {
5
+ base64ToBytes,
6
+ base64UrlToBytes,
7
+ bytesToBase64,
8
+ bytesToBase64Url
9
+ } from '../base64/index.js'
10
+
11
+ test('base64 helpers encode bytes with plain and URL-safe alphabets', () => {
12
+ const bytes = new Uint8Array([0, 1, 2, 251, 252, 253, 254, 255])
13
+
14
+ assert.equal(bytesToBase64(bytes), 'AAEC+/z9/v8=')
15
+ assert.deepEqual(base64ToBytes('AAEC+/z9/v8='), bytes)
16
+ assert.equal(bytesToBase64Url(bytes), 'AAEC-_z9_v8')
17
+ assert.deepEqual(base64UrlToBytes('AAEC-_z9_v8'), bytes)
18
+ })
@@ -0,0 +1,48 @@
1
+ import { afterEach, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { generateSecretKey, verifyEvent } from 'nostr-tools'
4
+
5
+ import TestSigner from './helpers/test-signer.js'
6
+ import { bytesToHex } from '../base16/index.js'
7
+ import {
8
+ CONTENT_KEY_KIND,
9
+ makeContentKeyEvent,
10
+ parseContentKeyEvent,
11
+ verifyContentKeyProof,
12
+ verifyIykcProof
13
+ } from '../content-key/event/index.js'
14
+
15
+ afterEach(() => {
16
+ TestSigner.releaseAll()
17
+ })
18
+
19
+ function signer () {
20
+ return TestSigner.getOrCreate(bytesToHex(generateSecretKey()))
21
+ }
22
+
23
+ test('makeContentKeyEvent publishes a signed content pubkey proof', async () => {
24
+ const user = signer()
25
+ const contentKey = signer()
26
+ const userPubkey = await user.getPublicKey()
27
+ const contentPubkey = await contentKey.getPublicKey()
28
+ const event = await makeContentKeyEvent({ userSigner: user, contentKeySigner: contentKey, createdAt: 7 })
29
+ const parsed = parseContentKeyEvent(event)
30
+
31
+ assert.equal(event.kind, CONTENT_KEY_KIND)
32
+ assert.equal(event.pubkey, userPubkey)
33
+ assert.deepEqual(event.tags, [['cp', contentPubkey]])
34
+ assert.equal(parsed.iykcPubkey, contentPubkey)
35
+ assert.equal(parsed.iykcProof, `${event.created_at}:${event.sig}`)
36
+ assert.equal(verifyIykcProof({ receiverPubkey: userPubkey, iykcPubkey: contentPubkey, iykcProof: parsed.iykcProof }), true)
37
+ assert.equal(verifyContentKeyProof({ ownerPubkey: userPubkey, contentPubkey, proof: parsed.iykcProof }), true)
38
+ assert.equal(verifyEvent(event), true)
39
+ })
40
+
41
+ test('parseContentKeyEvent rejects malformed content-key events', async () => {
42
+ const event = await makeContentKeyEvent({ userSigner: signer(), contentKeySigner: signer(), createdAt: 7 })
43
+
44
+ assert.equal(parseContentKeyEvent({ ...event, tags: event.tags.concat([['x', 'nope']]) }), null)
45
+ assert.equal(parseContentKeyEvent({ ...event, tags: [['cp', event.tags[0][1], 'extra']] }), null)
46
+ assert.equal(parseContentKeyEvent({ ...event, content: 'nope' }), null)
47
+ assert.equal(parseContentKeyEvent({ ...event, tags: [['cp', 'f'.repeat(64)]] }), null)
48
+ })
@@ -0,0 +1 @@
1
+ {"encrypt_decrypt":[{"secret1":"1b7023bb70248d8edab44658c5e2677dd7e5d7093ec062eb204975df4255fddc","secret2":"827844538be12d1cfa0f7fa096668cc4f2c4a25c2c8f7e92ca6cb05c3c445d17","nonce":"b5451a6d90ec575b4cdcedf4987429eeab1bbaa192ea3db89eafa058826885a6","kind":1,"scope_hex":"","prk":"3520160171dc39d75e64768d4fb667647480d458fc4d5c26d000a7cb3c8805b1","encryption_key":"de94e4663af538351a9b75b8af31e968ed8b88241ddbce43ad1d4ae2b984327d","mac_key":"70e65d5ff8769e92fbdf163b00b1b317bd4d30fe82de6b00d05cd74fb576febd","plaintext_hex":"efbbbf48656c6c6f20776f726c6421","ciphertext":"A7VFGm2Q7FdbTNzt9Jh0Ke6rG7qhkuo9uJ6voFiCaIWmMJrEDBNRRCorotVxmP7ge14Y+UtDn1/Pn3uzAaNNzHUAAAABAAAAAPJgoFXpn6mjFE0hUZrnZljeaYwSdqBKbVDXcyLgVGC8"},{"secret1":"f9869a8237c9fffd3bc175d21cc144051de4889da28b462ca1e4557adc2d2275","secret2":"c4c53829b9ad83682873761b71d667457935eaa84159a206dea58f18be09d05d","nonce":"f99a4a4a84a4906d839b62861dcd54883cccabb3616d003f27250ac00e672c50","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","prk":"7eeee2eac804eae839f64c4f2204ba6c205a65ae895bea006a45afd2ff9afee0","encryption_key":"56c727b1f69ff6ecb29c6cfd6469c1908da5556b0c13123b3303d5068edf03b6","mac_key":"f6be43893bffe64c43d56ee2692014d3e5275a78a3cd8268e2e1e0cb707a6bad","plaintext_hex":"6e6f7374722e6c616e64206e697034347633","ciphertext":"A/maSkqEpJBtg5tihh3NVIg8zKuzYW0APyclCsAOZyxQfHiK7t6u8D4JR3dRUKMpBRQzoOYtunePezG3p65AXPEAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzvgOo5isSBI06S531Yb9j9l+LpL9dA0D9/LLtorb866Y="},{"secret1":"2f69dcb9891cf749ab0b4e07a718a9e364c44e7603d851c7c09e080b631534fb","secret2":"110ffc1f2ea8b15ffd5d24c59dc1b72c4b1f8180dd5ccb6a68097ff328f49e54","nonce":"ffffd9144f5fe48077ac672e1366d303dfebdf60b1abd07fce1ff762bb25a4aa","kind":1,"scope_hex":"e381afe4b896e7958c","prk":"dd23c1dad51c025ed632be8b8da198517eef83a86729ccf524382f6011c9500b","encryption_key":"44f5de03559045aeb509d670299e7eab7f12682a7d5cc6c9a1441fd35f9484a9","mac_key":"ab08e3aa28d40c9376a56056dea33b3e935402da5585607b081ad166cecd8432","plaintext_hex":"f09f9088f09fa694","ciphertext":"A///2RRPX+SAd6xnLhNm0wPf699gsavQf84f92K7JaSqrm0b+bxgKBqNS04QURAmEXZlYBY9Ed4neDw2uOAqkGcAAAABAAAACeOBr+S4lueVjIEUNKR4ekMqHUoWb/ks495G0c1lD6oPQ3ZFsa4LHvRE"},{"secret1":"e945941c87478b88c8af150219ed8055692f3f01543a3dec3cb40854fdf8545b","secret2":"11eefd6b9a1a4d4e4b71840aa77eb47d3821d825ca8d4e45065ff563bdc342d9","nonce":"726cab7f363afe8c0783dc1d2d6e4700ace52a26996a53ba3928ef3c865cc235","kind":1,"scope_hex":"efbbbfefbfbe","prk":"7fafc5865086ba6ef1d48c93fa5e8c84dc0fd73924f23a4560d8d0f31f9ab2db","encryption_key":"2b6ed20127afba197082b52159120042d0bdfec6df2e657944f79a62ec90a1d0","mac_key":"2e72490f486fd6f0ed2ca5e508dff16b6db6936df59b77406717e6aed645d2a0","plaintext_hex":"e69a97e58fb7e58c96e381aee3819fe38281e381aee382a2e382afe382bbe382b9e588b6e5bea1e6a99fe883bde3818ce799bbe5a0b4e38197e381bee38197e3819fefbc81","ciphertext":"A3Jsq382Ov6MB4PcHS1uRwCs5SommWpTujko7zyGXMI1d8FRsRgcGnjOo+Ifry8x/QC+vDDkPCHv7WDaem7tQ10AAAABAAAABu+7v++/vm+/pDQcUXHli2Do1EEoqYFmF/67UUcl31Ks9TRy9vCwc2IUY6Ev9T+oBanqVWGbPgAWysjisi5dIPAEcndMK2Ur4m2UqTo3WVTIqKmy30ad5VOwl4v1AHweiZvJU/w+lQ=="},{"secret1":"98c7c39a4abf5f923db71a3e2c0951fa020bc5ba1555c158ebc8663e1582bb01","secret2":"b775d4f4ef14b1a93cc34a534a64a1ec2cd1a64a5a7b45f837af5ea4595b37dc","nonce":"ec64f769d99bc3c6f5231145b546334275d910e11fe9a11351ee487e4dbfd4ec","kind":1,"scope_hex":"ef8080","prk":"98fca3e635b9478407385a8989fb78ddb115d992a92019852953a4cd139aeb69","encryption_key":"9e6bdba691401f02de1403f75ebcb3516f5ff3b77c8a3918d8a3393e73eb3188","mac_key":"b05399edc9e6eae42cc21ec5941a73e8b7387bf10f49dfcfd740131309d81c35","plaintext_hex":"9b8de973ddf42a02103de24d9b7a4f0c4f551abaf7cd88f08e7a9c4d41ec5f777b45c890c112968fee50dccd3287583e9a3a33f962d78054f36dcb6f1ea9a8aa3fcb80953e04f6a2b3c3c4e26909ef7c5e84da6df3fd423215015640b249c91b28b38b18499b615bf1e92635e1df15aeeba2063692ce7cc8296582ceed25ceda","ciphertext":"A+xk92nZm8PG9SMRRbVGM0J12RDhH+mhE1HuSH5Nv9Tsg6J943ljpXnIaVIuHaXrWfa99RkqZOW6NGy6oqm2HocAAAABAAAAA++AgNCkiGZgN5Uzx1HVpcoLQQisIwWD32PqBoQ4T598/KmHsxUAGEARiXh9ikGXtwKuH8a8EzTcobkr4OEXfPs0h5u0A1HUJ3M/Hc/orcqZgeA0RhfZe3IASVmQfU9/pge+nTPjJVK5ZHOlEBnt7tmYcT8vqv9bpxbyhCBGMO6nEFhUtrr2IKCW3Z6vljg7T3FDr7aVIY/cxniq4E+e5ec9pZ+wn3j9PAibWgEANCDK5nyiH6B348lnqxfmu8bvzzyPhA=="},{"secret1":"b0e73a57d65972a4276879cb8604f683dfd9197cc236f299ea55acb66bfa8ff0","secret2":"ebf87d9858227055ac9f789911edad1b55777edc99dd4b8634f52bb8c0922edc","nonce":"c027624d50656a34add75cec7e476e6287bc919cacf0ebbda6d3277c02b0a239","kind":1,"scope_hex":"","prk":"a76ecc57266a24238761cf79c9909e27af6adfa523fb914a1a54e17d15e26287","encryption_key":"3c5be4141db8d4e4bfd998b6a4f995922070b9dc4af41c5d50c89c7ccd437f0e","mac_key":"92704765cd32cbe3beb21f347541184fc0cff839c8d2077d198d1f91103bdd22","plaintext_hex":"6120646563656e7472616c697a65642070726f746f636f6c20666f7220616e797468696e67","ciphertext":"A8AnYk1QZWo0rddc7H5HbmKHvJGcrPDrvabTJ3wCsKI5ZMf+aMW7P7Iz5qDghY+87TL5pZjNiykm0xpMKlkwITgAAAABAAAAAE2F94qgXOR+co8R41Vu04wLtkrI3Y5QJbVmutA5v1MkCgrLCmZAwNXhQsUnzUOuAPloXVQQdgQL4gmVgIz0rqQ="},{"secret1":"0bac57d63af3e6650152577f7d5515062270b68cd2cda1250604ab70b7cdf091","secret2":"ddb09a891ef13bbf1b9ed8fb403afce4eea2197428da805dc85d90eee76e20f2","nonce":"0da18d3ebcc5f269f6415e3e3fcb5e1a8d76318fe439ec83cfdf99ef8eaacee9","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","prk":"d084d04cb7e61bb0c8fc7fbfaf48b58863fc01c1a4cebc7d48cad3b3853ac7a7","encryption_key":"be6eb2e4aa213dc2260abc2414e763057ab7df785e33338f1a3167ac280ee7fb","mac_key":"59f55d61316dee4f7f71c7b3d9704ef822d5d31bcfda30d02a267f29cb20d92e","plaintext_hex":"efbbbf48656c6c6f20776f726c6421","ciphertext":"Aw2hjT68xfJp9kFePj/LXhqNdjGP5Dnsg8/fme+Oqs7p+xyexXUdk8ZJ2rtLWT1xQ9lXxWSiagEVpRg35PndmKQAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzuKZ6xxWsljlgBA/i6yz7+dmE6dyszU9qkR7f2xDUQdg="},{"secret1":"b69f38d981ad22b1fd25473756b2dd9c69d1554c6d31ae2a64c0fc82aafd86ac","secret2":"c916f18fd08a90c1d20bdfe27f31c53d33ebefdbe28e3da8797632b4b474b9df","nonce":"8b3c3f3aaf575328259ac5e3c08191dde308c573e3f4e7cda7042f82133143fb","kind":1,"scope_hex":"e381afe4b896e7958c","prk":"93c97bd637c3fc60d9dbdb410df34c4a614c52db57ed0f2a218e8a973e125265","encryption_key":"e0a45a9306cc404aea91687e2b3c26abe23b0e12945799279e332c1880cacd78","mac_key":"b21748a15b40d53bbdaeb81c419c160994780d141324fcb1ac65bcd8be6bb6f4","plaintext_hex":"6e6f7374722e6c616e64206e697034347633","ciphertext":"A4s8PzqvV1MoJZrF48CBkd3jCMVz4/TnzacEL4ITMUP7b2QxXAKNEKp93ebvTrmrJ4aeJtLvqRokEeGXPBLE9UsAAAABAAAACeOBr+S4lueVjO04T51hx+sZw9n3gheEAyVOP0w/pWFvFtCuolpBkHvk"},{"secret1":"20d7e7e95a8e6376438182425c33c9445055fa4a8bd2c57e5c7902015433e18d","secret2":"d38139efe4118dd5862c2556600ef7914d1659cabbd1a3d5fd9f2a0abe9dcbb3","nonce":"20c635f2f795178ea0bbf9856dd99da02138ba79337d2511d887f2a065b917c9","kind":1,"scope_hex":"efbbbfefbfbe","prk":"4f9c75fe7c850a79f83000901ef8f020301c06e413a84de01784971ec249bb7b","encryption_key":"4345c818ddb2793427d8f5bb056e663cd941f910165601ff6806866cb7fb0fc3","mac_key":"4eb9ee0ea464574336446a8aae961f05b6a65cd5feb2087417eabf5344c554da","plaintext_hex":"f09f9088f09fa694","ciphertext":"AyDGNfL3lReOoLv5hW3ZnaAhOLp5M30lEdiH8qBluRfJTmsWPfIzALsx5OokjdKYAWkgDkES88FoC4k6wtgxUK8AAAABAAAABu+7v++/vmSE/qHW8+XDY97+8EQCRVPzORPYKrnLM6mNRp+zl2C6"},{"secret1":"1a2c6e81b5f1038fdda1f555d0431d1bd3efb22d57f608708fa46d7d7b96f1f5","secret2":"c18596eac499c94e04334021c1b6952757d83aeda2aa84f90ab47357cdd29fdb","nonce":"a05a11dcd50aa1e855b7e11a816158a1a4827d21a00b60105ed3c8e802770d77","kind":1,"scope_hex":"ef8080","prk":"c043b08590fcc2ef03e299633af842deffd1b5dbd2bf598606bf02abb898303d","encryption_key":"ba927e27a656a34369920ce7be028b6f6cb5878890123d1d3ba6b9f7ef4ab9c4","mac_key":"71163bde93ea8fa3b5574e81869416bcb8f6954a3b746e1b2ed24546949e208c","plaintext_hex":"e69a97e58fb7e58c96e381aee3819fe38281e381aee382a2e382afe382bbe382b9e588b6e5bea1e6a99fe883bde3818ce799bbe5a0b4e38197e381bee38197e3819fefbc81","ciphertext":"A6BaEdzVCqHoVbfhGoFhWKGkgn0hoAtgEF7TyOgCdw13O273WC9FSDyMtfOYNFvOlZQcaSrLdo6WBQ7ZI2UWn5MAAAABAAAAA++AgPPJWHFZya+M6arLz4wrWMHfL4Wyv4gYZBkicAvVBX0dMsr5tBcTP5xaM4lJZZnokEvMZRzYbjrfNTjT2gCWBapNdr/QrHxlTDa54nRmVR/2GBLkmQ5QeIiDm6OhfjXyYA=="}],"decrypt_only":[{"secret1":"fce6ddb6b9611964b37466e29f89f212f6905a70e5b0ea20b33ac9a4a74e60cb","secret2":"45d28e26e1072c9495857efc85bb56ba0833271d7cb183c6459b8ca17311ed7e","nonce":"d55b86093a16aabd228b9ac1724749e492fc3a81491c7374bd7a1d28a7b3b4a3","kind":1,"scope_hex":"","prk":"5f359143a097d9b66ec8e0cff7c111076efa06b5ec2f9185e13a304c4467a4e8","encryption_key":"cbb7467f7c3a6f04c5ac6e4554de2034b67f2ac32a94d58f44e7a14e80912b0b","mac_key":"784feeb31cf134baaa13d387f5102cf1f06a0e4d60cc737ba4c0311987401e9e","plaintext_hex":"efbbbf48656c6c6f20776f726c6421","ciphertext":"A9Vbhgk6Fqq9IouawXJHSeSS/DqBSRxzdL16HSins7Sji9VE1vdW4PQiqseqUsGZsaAvIe2yGmfWOXiimOZHRUUAAAABAAAAAHm5SMpSTmibFgS1CqDSU5sC6MEPKNyTHS7oxNAb/AAFwta2Xpcc","note":"non-standard padding (23 bytes instead of 17)"},{"secret1":"099f9dd917f81a515d587164ed21bfcc3897bf705a7c9b3de85abcea809831df","secret2":"9806d1447c80921a15ff6c737204d985fbee09a6fb123ec8c1ef8749a939ac6d","nonce":"382baacbba8cba0cc6e8a7b4444fb157186118a18b3dbf652fb6b1e8267bcac1","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","prk":"57dec8dfe66043f16b839513bffe2855984a725e50d8c63d4f5738d9dc6d0ed9","encryption_key":"c8ef9801a429de8526739c8d84c62eaf632b0d52023590c7449c8178885dcd57","mac_key":"7ad04de35b988b240f78285e597fdca8e963988664f4808878afc2ba0aa9a4c1","plaintext_hex":"6e6f7374722e6c616e64206e697034347633","ciphertext":"Azgrqsu6jLoMxuintERPsVcYYRihiz2/ZS+2segme8rBD0AqKypuSHff1x0FW+qO4lQlLltEjPWrvoMo7fbKOfwAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYznEO1UCJd2Ld0YV51u6kOkY3g22UhvNBAXY3sFnKmPu6fYx9s0rDWwNZcGxsMs+VydrONvM5F","note":"non-standard padding (36 bytes instead of 14)"},{"secret1":"a62c0eae8281f8997c6391889be1c39df8d06acbde7cbb5e23b8e6ba28c95328","secret2":"c51b92dfea8551090c2e17d22953f7124fdd6a59e7c67daadcb92819279801d4","nonce":"f08ea755450d9666cc122f2aa89794b170b8c69c6d7ff5f1d25bfae52164ca3a","kind":1,"scope_hex":"e381afe4b896e7958c","prk":"916e05d26ccb1d5f58698911c85789b89529baa9c4fb5fdcdac80f94afb525f8","encryption_key":"9110aa1049b70cdd93c38fa4888459d7286a78f5ca584d4dd658905660d4faea","mac_key":"a597dea28fc706883f9dcc4917209ffc10788c95e86f090a694ea51bc5fe7a11","plaintext_hex":"f09f9088f09fa694","ciphertext":"A/COp1VFDZZmzBIvKqiXlLFwuMacbX/18dJb+uUhZMo6uAhk3+WwQOcgQUgH4zhxaRzi80m70t5a9uV5B11EdpcAAAABAAAACeOBr+S4lueVjPeRbg5pf83dLf79+g1wPXb8mjm/rT9e","note":"non-standard padding (15 bytes instead of 24)"},{"secret1":"740853175b987393b113ac196ccf5152ed4a206b1a365291abacaf106c7a3bca","secret2":"da1a37af7852a7e5c0a7ce90a39950039bfaeab96297ef83b9d8817f5a2d26f9","nonce":"f7e0f4b83ebb87657001b8e47d5940a3d062dfebae66da5a2ad0f4e498fedf85","kind":1,"scope_hex":"efbbbfefbfbe","prk":"75c09260e4c8b8240514aee23723531df5f7816a25601e74e0ccbe87a965b309","encryption_key":"94cf5b47ccfd36dadee996e637ae2a11bd9ac53ff7a29573fe368adf8b1e4f20","mac_key":"a2e6d59755d7193c5cb9eba5965a42d2696062d455c2c7c535ad88b1c13d415b","plaintext_hex":"e69a97e58fb7e58c96e381aee3819fe38281e381aee382a2e382afe382bbe382b9e588b6e5bea1e6a99fe883bde3818ce799bbe5a0b4e38197e381bee38197e3819fefbc81","ciphertext":"A/fg9Lg+u4dlcAG45H1ZQKPQYt/rrmbaWirQ9OSY/t+Fhm1xiEi20bWbMLIDrNw7Gz6XU0bDmmmgYl3g4z68Z+4AAAABAAAABu+7v++/vhLMBXKVPi51crCqEwCQnuB0V13+nE22PpVe7jQiELzDMqdrE6intBrXyHJLrJ4VteEZoiU92jZDoG9ieltDh0NnKRKq7cuW/om3DIhB0DCb0Pq6C5g/VaFaz2+mVPXV0p2BMW2MpuKUJ7/VYAlJPNSlK/JSYpQq","note":"non-standard padding (50 bytes instead of 27)"},{"secret1":"28531e3c28be034f8bcca7bc25e5b9a6cbbcce8567f2a9016b3f1ad7707a724a","secret2":"96108e608b4231562b07d21021361e3efab87350021a9247ce410585c0adf757","nonce":"a82a808ca1a40368336f19e9d3f83bfaaa35e4b8bffc9b5d9426ae518b9f34d1","kind":1,"scope_hex":"ef8080","prk":"38846e1253b841b6960c3be79ec4b6bc1e0ce7c6e40a05f6ea2190ff0c53044b","encryption_key":"cbecc983553fb82f9f3f615dd959a451d66388d8632d2028a684c9f02c611f3f","mac_key":"5ed551ef02ba987cfdd4716c4a840a654501ea57aee9da055651669f85ac016e","plaintext_hex":"9b8de973ddf42a02103de24d9b7a4f0c4f551abaf7cd88f08e7a9c4d41ec5f777b45c890c112968fee50dccd3287583e9a3a33f962d78054f36dcb6f1ea9a8aa3fcb80953e04f6a2b3c3c4e26909ef7c5e84da6df3fd423215015640b249c91b28b38b18499b615bf1e92635e1df15aeeba2063692ce7cc8296582ceed25ceda","ciphertext":"A6gqgIyhpANoM28Z6dP4O/qqNeS4v/ybXZQmrlGLnzTRH0t5gnTV0ylQcxxkbLRHyKXnIagqYk5XMlG/85NYwuwAAAABAAAAA++AgM9zXmWWTYSixZDotwM+8HmJHW3aBt44KvZkhInVvt+Xmzh1YaPW8cbVr9kOQ38+cc5E285UfL164P71915Pr6mHNGOHtpKcX3P6TXCrch2MLux4m9xHf0BPcuv4+bwC0fLpNKVJLnNAnnGm7VOXdE2HhX4NP8ujzvH6cKTGfiyK0OlVpWX6Le+v/h2wm6m2SAHSJLbi6fIdA6MDilizVMqyaEFZ3n8eRYQ+To6eFKojRQw=","note":"non-standard padding (50 bytes instead of 64)"}],"long_encrypt_decrypt":[{"secret1":"e35f016acdf0bec26f9f0e97fd813aa042727cb1e5ac2adf1c7b8d18d393f455","secret2":"e3c47278057365a1007414224f54ee99e6198ac6b6a82917e635375a1f9afa8e","nonce":"0598a9aa024df86e1e532e8cd3ed412e5b8bc914ff0340aa8868f9fd2fe2871f","kind":1,"scope_hex":"ef8080","pattern_hex":"9b8de973ddf42a02103de24d9b7a4f0c4f551abaf7cd88f08e7a9c4d41ec5f777b45c890c112968fee50dccd3287583e9a3a33f962d78054f36dcb6f1ea9a8aa3fcb80953e04f6a2b3c3c4e26909ef7c5e84da6df3fd423215015640b249c91b28b38b18499b615bf1e92635e1df15aeeba2063692ce7cc8296582ceed25ceda","repeat":511,"ciphertext_sha256":"cf2c183f974c2601c0ec5d4fad0f0f98f18b0c83bc4e988c53ec1e496528deb1"},{"secret1":"69efe21ce6ffe00d4126a019542e61324ff59fef06c81798cf1ec9810bfa5566","secret2":"91749344a212c2168587ad46197ef2eb026e3fa6839cb4286599c4f24820431c","nonce":"8c9f02398c9c5c11260b9ec27292bd32f0127c3e5366b255e0878ecb82e81eeb","kind":1,"scope_hex":"efbbbfefbfbe","pattern_hex":"9b8de973ddf42a02103de24d9b7a4f0c4f551abaf7cd88f08e7a9c4d41ec5f777b45c890c112968fee50dccd3287583e9a3a33f962d78054f36dcb6f1ea9a8aa3fcb80953e04f6a2b3c3c4e26909ef7c5e84da6df3fd423215015640b249c91b28b38b18499b615bf1e92635e1df15aeeba2063692ce7cc8296582ceed25ceda","repeat":1023,"ciphertext_sha256":"8538f11d334dee64c561961ab7b371a90cb5ded3d1bf6c544d93aa4c72e5b1c0"},{"secret1":"9ed97778c4bcaf3b5c66c41d3b97ec62e89e2bb9ead5d27a980a1c268a24a2c9","secret2":"58cba97d2985b001a7367a088a2e868a85158bd218f2a5858eb23a43de4ea382","nonce":"d8212d54ca0a36a7a5ed9f33656aebcd995f64dc6c4551a54b0dad5b897e254c","kind":1,"scope_hex":"e381afe4b896e7958c","pattern_hex":"9b8de973ddf42a02103de24d9b7a4f0c4f551abaf7cd88f08e7a9c4d41ec5f777b45c890c112968fee50dccd3287583e9a3a33f962d78054f36dcb6f1ea9a8aa3fcb80953e04f6a2b3c3c4e26909ef7c5e84da6df3fd423215015640b249c91b28b38b18499b615bf1e92635e1df15aeeba2063692ce7cc8296582ceed25ceda","repeat":2047,"ciphertext_sha256":"66c5b453cc7123d9826115d437902db5226dd25f41aea9519f986938709c2901"},{"secret1":"77930acaf9d28607482f0d329d65eea04fd218a957f25004d6606414dcdea848","secret2":"c31571de3b9b8053d5477a8dd090d7ed7ffbed98f8e9c904b8034ba77f74e232","nonce":"b40a2f2ae51c8355ed8bce7f810628c5fd3a4c5d4fe9170c159b9c7e9d1d5f87","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","pattern_hex":"f09fa694","repeat":16383,"ciphertext_sha256":"ccd2942a398aaab22845d6d65599a82fa9ee5fc1caecb5a8cc358771b1b7ba7b"},{"secret1":"dc0ded78a0d133195b49429aadc6d424fde9b98a0e9ee12b4382bc0f08125a1c","secret2":"fac11ede5498f415f3a48cdf052a4e0d2f77fc4012baaf77de70a3f2cb4bc195","nonce":"7f2506e82ad6d97fa2cbbc2cf9f3a02bb61ce65bbe72a891c07c7bde23ade06b","kind":1,"scope_hex":"","pattern_hex":"f09fa694","repeat":32767,"ciphertext_sha256":"d84d643bda0d6f11dfc165eff69ab7c12c31f7f651603fe9e3397fb2ebb44a24"},{"secret1":"b585991901f19ca1353b4122a591c3ade793338174f70326ee351d54b2b4c9be","secret2":"cdbc4210a142109928087965a6e47922ed65763165cebdb54498770da448d5b0","nonce":"aef913a704ce90355a134dd5f4ea253115d9d426269f371f45a33de3c79a90d7","kind":1,"scope_hex":"ef8080","pattern_hex":"f09fa694","repeat":65535,"ciphertext_sha256":"75d4386e1e5bf39c2775486c066274fbceb9e0aff2880b513ec1593ce8a68f74"},{"secret1":"57420cd8c43b789a506e7dd1eb433b010e5323eb219de7c6a6a6f6976aa80693","secret2":"370091dce8dce4c3cf6b1a67ec8f41f9ae78a69ea902c67bdfe8930d90b2b7d6","nonce":"15ecf921c0e227f5199523de99193626087d506d998b8abd3c086e66fb25af0e","kind":1,"scope_hex":"efbbbfefbfbe","pattern_hex":"e38193e38293e381abe381a1e381afe4b896e7958c","repeat":3120,"ciphertext_sha256":"0577e05453f458e700740be3d849096f3d9d46924333ce3aa27c342a786a6cc4"},{"secret1":"aeda4666950455c2e038d6b7d9e000be92be6aaecbb57b7f0f980ba29da52453","secret2":"9d819437f4eb60290bfb9aa547d426516a3ea07a2149e46f3310acc85dd45a18","nonce":"56146d9c3caf5c118288754d2caabd142eb45e1f2d80f3caf5183888ca2ee416","kind":1,"scope_hex":"e381afe4b896e7958c","pattern_hex":"e38193e38293e381abe381a1e381afe4b896e7958c","repeat":6241,"ciphertext_sha256":"6a0c112e126aa897f80ac73227fbb062ac7ded96f641a05fa4634a9f4a8c702b"},{"secret1":"f153b2eadf9aa51bedf90ac8804dbe2bc4fed1ddd3861a857ae80a20d5c55f27","secret2":"22c42e7079ae57313d99c2f025c1be2f5dd999a3898d9aecbed7a207a1018510","nonce":"a7db4437442ca0ffbd836822c622115bc001de197c9f1a1d3d67a1e63c044d30","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","pattern_hex":"e38193e38293e381abe381a1e381afe4b896e7958c","repeat":12482,"ciphertext_sha256":"c066419c9ee88e7c8a77dbe25fac1dd8e9720fa4f481924058189062b12e3fe1"},{"secret1":"217d14ae21140584417aa9bbcab4bdf4adccb5e74b191d65d2b357794f7f7143","secret2":"cccdef797a083bea633fcff31f255b57b5d5c99b682eda8ff132066dc3cd9127","nonce":"143cb408e61cf7b4281b0ccf300284a68d7df282f71667df5740b5f282424880","kind":1,"scope_hex":"","pattern_hex":"21","repeat":65532,"ciphertext_sha256":"e5360cffeef8c31c88a3455b920d9d9a98c8669b77d38ac232e0f67420467e5d"},{"secret1":"625bbf8de97b71e8d70092bb6c576ee95895b7e6d2acf924790c80dd69785e8f","secret2":"13e7e6925ef02a644ea1c7bf8b68ff7de8c676ecf7f22d49a78a1802a6933189","nonce":"1bbbfd8803eac5c844e96ade2fdacf18fe3bda62312bdda102a29b52dd0c97bf","kind":1,"scope_hex":"ef8080","pattern_hex":"21","repeat":131068,"ciphertext_sha256":"8dd9a3157906d3a4e3de2b4d552cddae489a0996812bacca7728996a2605cae9"},{"secret1":"266503c3818aa70800280c53be6f7f8156273c6c606cfbb0803d8eb63dca65f9","secret2":"9846126fad398ea9b6fe7ebddd7e28c021691eaff5355847cca42a2caacacb68","nonce":"9815cf89d4ab86023b1a427166fd8db49681a077d5724d1b743a57cd6fb96b81","kind":1,"scope_hex":"efbbbfefbfbe","pattern_hex":"21","repeat":262140,"ciphertext_sha256":"0bd33e75b6bb9be42a1fce897801d064943945a930977551437031a413e07443"},{"secret1":"39506d419c1dede09cba910247457d66be7b213adbd981e3af0ef69b46c790f5","secret2":"5294e3ad19298a304753a32a42be2d44f3438f553677f30aab591f8a19ba4fd4","nonce":"8ccdf24876123afdbce42e59d4e03c53589150b4f23b087d3e3bfe89d96a54a6","kind":1,"scope_hex":"e381afe4b896e7958c","pattern_hex":"00","repeat":65532,"ciphertext_sha256":"c4c030de28d8b5fa9aafb1961aab296f078aced2988897c7907fa67cae073de1"},{"secret1":"86bbd84e3e9e2db2e7ec37fb16900723aac499f1074b66586b44a3ed63022a3e","secret2":"17d3ff292c4a65e6181ecc201b9c5ed0c7e8408f36183659181414ddbb17c3d5","nonce":"f7ce68c00fb546c23efb88f778819c131797705abfc222886406b8547a9c244c","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","pattern_hex":"00","repeat":131068,"ciphertext_sha256":"3992c52ad7811ba181f572d37a0e1c9a4dbc1d9b127caa171896eb861d1e818c"},{"secret1":"d38c5d7c0ba3b3e103318eb12952a481a204ab8dfd1d440b8882e9f0649ac0ee","secret2":"484475e3653226fd962fb9ca9ea8c8d4929473308eb53fd73a8ccd95e5eb8b98","nonce":"47fd0420348c7c4dead0c52874f2efe9ba9ecc9c4f9ee82319e1f33338d3ab86","kind":1,"scope_hex":"","pattern_hex":"00","repeat":262140,"ciphertext_sha256":"e03e2f16a5ee7e874d3cdc52e36ae8e73791d664540cdb7ac36f65687c7fc2a5"},{"secret1":"fd20231a23032bde7acc638eb7086784670dcf8bc90e57a65c1d03d68594d3f0","secret2":"b47f3ba965fbea51c927b6846543480aeb25275c0e01c5c4d286ea087e70ee92","nonce":"6c9820a69d021d86a695eb3d5cc11ec638a799aca10ecfb9819b1efa3b9731ff","kind":1,"scope_hex":"ef8080","pattern_hex":"ff","repeat":65532,"ciphertext_sha256":"6621fc89a79464d19cbef605d962c9f43af90568c8c352007829445d1886c9bf"},{"secret1":"38c961d0c8289789cec189e8db140f740abf34c8251eb16ad93cc5f9021abeb3","secret2":"e985cda5e08979c17a2c148532434c6f830bc2243e7a5592704972328f24d62d","nonce":"3957e9cc3be20433aff61993558572099e186312e714050cd3a589ff675f0e07","kind":1,"scope_hex":"efbbbfefbfbe","pattern_hex":"ff","repeat":131068,"ciphertext_sha256":"e41d7c737462f18b653683796d739c3b93f284c9bc15182bd94d278d13b84dea"},{"secret1":"0666935a88d5196746c799ce0593dede7a8e8044930e62e07793950b9dab9b4d","secret2":"e3505f4e3b8fd37da2a9b4f3cb67864819e6272cdcdcf198f422598fa1114d21","nonce":"56fef50b564913b040a9ee83c9c1eb36ac6553a9e5ad699ac036fb4338a38e35","kind":1,"scope_hex":"e381afe4b896e7958c","pattern_hex":"ff","repeat":262140,"ciphertext_sha256":"086aaa494e3a4ca5bc573d91fbed292d9abe05fb730c54660bf5813250744aab"}],"padded_length":[[0,32],[1,32],[32,32],[33,64],[34,64],[64,64],[65,96],[66,96],[96,96],[97,128],[98,128],[128,128],[129,192],[130,192],[192,192],[193,256],[194,256],[256,256],[257,384],[258,384],[384,384],[385,512],[386,512],[512,512],[513,768],[514,768],[768,768],[769,1024],[770,1024],[1024,1024],[1025,1536],[1026,1536],[1536,1536],[1537,2048],[1538,2048],[2048,2048],[2049,3072],[2050,3072],[3072,3072],[3073,4096],[3074,4096],[4096,4096],[4097,6144],[4098,6144],[6144,6144],[6145,8192],[6146,8192],[8192,8192],[8193,12288],[8194,12288],[12288,12288],[12289,16384],[12290,16384],[16384,16384],[16385,20480],[16386,20480],[20480,20480],[20481,24576],[20482,24576],[24576,24576],[24577,28672],[24578,28672],[28672,28672],[28673,32768],[28674,32768],[32768,32768],[32769,40960],[32770,40960],[40960,40960],[40961,49152],[40962,49152],[49152,49152],[49153,57344],[49154,57344],[57344,57344],[57345,65536],[57346,65536],[65536,65536],[65537,81920],[65538,81920],[81920,81920],[81921,98304],[81922,98304],[98304,98304],[98305,114688],[98306,114688],[114688,114688],[114689,131072],[114690,131072],[131072,131072],[131073,163840],[131074,163840],[163840,163840],[163841,196608],[163842,196608],[196608,196608],[196609,229376],[196610,229376],[229376,229376],[229377,262144],[229378,262144],[262144,262144],[262145,327680],[262146,327680],[327680,327680],[327681,393216],[327682,393216],[393216,393216],[393217,458752],[393218,458752],[458752,458752],[458753,524288],[458754,524288],[524288,524288],[524289,655360],[524290,655360],[655360,655360],[655361,786432],[655362,786432],[786432,786432],[786433,917504],[786434,917504],[917504,917504],[917505,1048576],[917506,1048576],[1048576,1048576],[1048577,1310720],[1048578,1310720],[1310720,1310720],[1310721,1572864],[1310722,1572864],[1572864,1572864],[1572865,1835008],[1572866,1835008],[1835008,1835008],[1835009,2097152],[1835010,2097152],[2097152,2097152],[2097153,2621440],[2097154,2621440],[2621440,2621440],[2621441,3145728],[2621442,3145728],[3145728,3145728],[3145729,3670016],[3145730,3670016],[3670016,3670016],[3670017,4194304],[3670018,4194304],[4194304,4194304],[4194305,5242880],[4194306,5242880],[5242880,5242880],[5242881,6291456],[5242882,6291456],[6291456,6291456],[6291457,7340032],[6291458,7340032],[7340032,7340032],[7340033,8388608],[7340034,8388608],[8388608,8388608],[8388609,10485760],[8388610,10485760],[10485760,10485760],[10485761,12582912],[10485762,12582912],[12582912,12582912],[12582913,14680064],[12582914,14680064],[14680064,14680064],[14680065,16777216],[14680066,16777216],[16777216,16777216],[16777217,20971520],[16777218,20971520]],"invalid_decryption":[{"secret":"b2a4cca9347992d235fe115382098e313f6eaa3680248443b90c64e4e2ab039e","public":"dc62907f84a35acecfc55b6d82961399f019981be0cd7d5e6a5a0620f9158870","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"Awx1nilOH4b0PT+ZszAS4TqOfADUQxWfAHAUVyJmy7c8EvrgFmKouWAVFZyjYN2XuuGSWHlKeuo9bF9t7MwMGfwAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzVeOxtyTClFO2/OPL6lpuSi3WFTdQgbhX6g/f1Iv2K6o=","why":"invalid MAC"},{"secret":"83ed5a7ae0494831e938a0a8226472954be9daffb4bf5d7641473b35e959cf90","public":"90ecf0dd8a793c74809735cc37cc3b9de20ffc9aae0eae7a1a0c740ecf09e395","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"A90ZzLN2HaQRTrzzLoobOtW+c9GyPxVp64fhIygpEpLaYYbCN6Pq1rjptBbN5S2vCFPCsE3wmU5u3Wx6L8oZxJoAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYz8Sxek35q19YWqmhyQNRHVZ+sNTtdgXO3MCnvjw0nanb233a39sc969Lm5DaUPN+yTKV0NbtYYN5hIWDOMOXj63XtzT7S7i/LAhv/l8y1zLTc0aUUoIjEg0EHi/FvlakK","why":"invalid MAC"},{"secret":"efd2ac18f500ac0fa1b9639149432ff2d309d1b49c7f683c9ca4613d14449dce","public":"84194beab56b44c426b866261772bc0a447ea34f94f2317ce1350cb714021a25","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"A5vkMZfJPQuWeOQsEiuZX1M4VJLY/k2G1mL8EKHK/yq7gifeC4V4zQ4L1iiQ1oVgmTmhwb/vd21Fm3YZrpYGEvYAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzQlapufh4trECMJjdb8m/TNFFcFJJSmiM/XQpnKXM/wc=","why":"invalid padding"},{"secret":"2926c352495ce986639ccbb263ad2221df731bae6ee8ec329cbb5d00c5b9ca87","public":"3c4b835fc7de0dd3a02971b559ccd5d3bcd2eb3cce7c1023b93892918effb71d","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"AzW5Wy/bvTYdcVOLIL8W26mfgTFG19S0H2BC9kyqgqK+TZ8oI00WJTNqwJIg8JE7DqDnOY+Q40Yd3G8Hi4GobQ8AAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYz3cI/rKzcQ22idVIBuFgLEVJaK1W5uxh6M6AdMoKKtdxO+lESYYWCEh/zzj35MFMwSJTn8z+XFvv9f2jYgQXHvPmY13wLOpIrpoS2W8luUbOVf9fip5mZXHw3neYvc7jA","why":"invalid padding"},{"secret":"47c04c4c6d385ddaaef691bd58dab94f81f4ba9eb1ab832339c3d0042f998dc5","public":"cb6a3aa6d94a58c9f03354a2a8723c3449e06b19a0ccfe19353195b28ddf7694","kind":1,"scope_hex":"e381afe4b896e7958c","ciphertext":"#A2A/tbfDDqn4qx267aPFZDwyH78j9zZV8g8ekZKonH8bDR7vYhp7zzh3oJAlJWem/Z5OVrRvUAJQrx8q289PqsEAAAABAAAACeOBr+S4lueVjASdOe8pTxevoZoYq1Y8rRarB6+yzRlquT4RZlmHH3jLEQmAbBjQGrOXi1uWPbaKC8j/VpjW5S9BAtyMSMpUcHg=","why":"unsupported future version"},{"secret":"751321afdaaf76aeb87851ca8f35eab87d0f50e1ec595c67e7e37ced7d6b7b9a","public":"a340bd34205bb0f3dc2f9aa3fcb70fc52c326cc4566c457bb9cfb9ae17c239af","kind":1,"scope_hex":"e381afe4b896e7958c","ciphertext":"AP9SHg4CFoD4fy22vSMNZV+efP7Ld7GCOpIKeZANqL+Kspe380sGxRQGKyy/liCuMi8DbcfQJypivkS+Y/bz3sIAAAABAAAACeOBr+S4lueVjMM0jKIQIfjKuEBBmjWPFmQsB20qe5pcJiLpnnXmp12z","why":"unsupported version 0"},{"secret":"c4ca1bf68b1f768bbc3670d554036b5c892303319ed6f7228e8dc2f6c99dd0b3","public":"537986db4ffe564eb7d565643e78dbc24e86650957e6e7c2c8d0124fd04fd68f","kind":1,"scope_hex":"","ciphertext":"Ap2Mv1HTQrVArE2UVevKq7rQ+a0FMw8OBuiAMnA81jJit7c0QzkEMr/o+5++t0/FbXFABfQaTRpF+dBuISyw3rEAAAABAAAAAC5e+Y9lfvgD1trmXL2Jv5H3Khi8ayWJJQMrVOEMd9tlJNj1b7k/ZzIG71f2GBzzoeBImA2fk+q6Iix4v5jUulo=","why":"unsupported version 2"},{"secret":"9460c5781801b35f01c1093434266cab5ba997014c52d9a16c3772f8580ac61b","public":"375cf44101e7d77699da6b8d7e633f507eaa82720d4d3cea1c0db85447975fa3","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"BAahtRKP0WL8luCz9m6TydiQtWUfoIWvkRlg2tatPVOCAwYrO8Dw3DZMgeTGjaehohfAmVyZ6SneuTQF3Ho+EUIAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzpHJCeyhgMaqIsPgWO635BaIwmRU4cfe9aA6gGqGI7SY=","why":"unsupported version 4"},{"secret":"b46bd96a998b00b55e48ad76c1fa0c68bc64b94174a189983b689fc05a0cec52","public":"797b06ba5dd8ca23a8c8cae4e9ee8963b25d9bf92618fa0b62db1ddc7b611453","kind":1,"scope_hex":"ef8080","ciphertext":"","why":"empty payload"},{"secret":"42602869f3d35e0bb04f72e3053d8de9cccf1e78dd6926c9311134784a5e70d6","public":"94a4ac3a4bf998dc64ab07cbb573ecb045e028f9b19c88f02af8f73d334ba0bd","kind":1,"scope_hex":"efbbbfefbfbe","ciphertext":"Aw==","why":"payload only has 1 byte"},{"secret":"d2dccc683567cd0cc6d9ec907703ddb362ecb61ef8e6c8ebb7411a293230935a","public":"294da03d650400e766953c9fb78600b788598a35e47b9755a891802de76f7a09","kind":1,"scope_hex":"ef8080","ciphertext":"A6oBqSSHPckKYt8Doymo2s1ku7LJxSNSfSuQdBoXaRe8q+5FQmvwbejIzJEaVKdUhbykNt5VFxno+sZtreNhrHgAAAABAAAVCO+AgAnTQMJrGJX7muna+wwIyM82vR398H+fhL6XxOam03jQErUC9W+klrNflk6oJ4mmyO88x6FJcf6n6LfDpGjMogNuMxoR1crWfknPMJuHggEUfOU6AjN7CgGiFPdnfcgbUPP487vxX9iw7U8WhQCfnh46vQTdwCDIm0C3aUp2/fJ0xNTVIZkFVKV1CyvyFpVicY9Zc7fmeEIDAsJvM1beK9sWqp9CI/sMU9OXmTfjdugSuisDRevchLkr6h5kB/rXDw==","why":"scope length out-of-bounds"},{"secret":"e1ef663aeb335452c9670d8a9f1f75cae5077f216314e9b49761610b4eb98538","public":"b729446e5cdbd2e4ac7967b056c17d5e4735035f2d2cf829722a0a85ca1fb6cb","kind":1,"scope_hex":"efbbbfefbfbe","ciphertext":"A/uzxqp0UwE6j7p8PIRKJDa0ah39GGyMbM0fOivlqESqbfFnp5OD2FSHR9TOTeJwiAfLXcXkoZPwiNKjB4ZgXV4AAAABAAAABu+7v++/vm7l8A==","why":"ciphertext too short"},{"secret":"327ffef01143a4dae30e201add671183424b39b1f33f64d78c19c22d3a7e790c","public":"039f6ce0144af03f0f3caf8a070a6a36519e2942286c2295a23785b0668ea621","kind":30078,"scope_hex":"737065632e6e6f7374722e6c616e642f6e697034347633","ciphertext":"A5jnLthci6SRC9V9Ak/AKGyB7xAGPLGZx+fW9wjfvOKQuug5cUUGX4R0mmxFHl5/TtcQ4syIiTtgXL3uVveIP3MAAHV+AAAAF3NwZWMubm9zdHIubGFuZC9uaXA0NHYzS3rEqFHqpL5Yqh/xY+a9i0XAyY960LDSfzQjSZh4UHzbuxmMEk92jAczsFkI9cWqd+xzahX59yD9l+UnCw3o9yXmzZIaA6UYPFI20f2VnH+G6F0Zt917fgt0bJwR3QUwblT63eOKLJGYhXqC11dweuKORW6oGJagRFo7P8r3UIFTHPkL5xMhUtZS7TS7GDKTB7kmEp5trwfzboiWxzp12LSlkUU9Nctyf6KE4iEQbk2jDndbC9npCn1rFpsFiHsd!","why":"invalid base64 (trailing)"},{"secret":"53d492157f17e6804bacdef3942fbb220a7239aab0e6bd6355eb3ff989ea0076","public":"ad2e1f00a4d5985810072cea336b0670f8e924368bca5b15c84ba8f3d7cae478","kind":1,"scope_hex":"e381afe4b896e7958c","ciphertext":"A3oG29gbEA%8QXjVLA5JeYOl1Hj5bJVaNcl2tAnfEHm5pzS0V+3eF8Tns8+A+TkfxrSc3DuAbkxc9SgWC+214cBIAAAABAAAACeOBr+S4lueVjGfB4CLk22vLao5NE6OH5KlgzSy++iyD7FZEmAkCVOfQkrnbj9kzyLF7HRygI5E2FJdeQkX6WDiHtwzP/UWwd+cnMaXTYS7vL0Zh6Lvz/PKicCecxB0NvkAdYM3hOpodhXEYd2nano+37mU1Cahp2uwyygJQTb427cHBucQiVpIadVoKqMeIA7EGvO9HTgTxgE93vyT26NqZFO9aniV1bFc7y9nq1OYHFfNQgzdxVMTQ88SwMbq2TSpU5uJc/cphNA==","why":"invalid base64 (middle)"},{"secret":"d4db7f6dcf6a45843739a806876a9da849f75f7a36702e7b4f7a10a986bf76cd","public":"0377961328c3bb0db459cd22033d7a7ca1e29d7f42fd3f1b038530a2402879c8","kind":1,"scope_hex":"","ciphertext":"Ay1pBSibePLV49S4vfkgB4GCHR0Xywd7acm1WoC1ZaX6Jg38sM0PQAshZtniNKpUjQWZGw4e7kSKEgFIGhT6SxQAAAADAAAAABuabdmj5F6vlFssb3CHu/ndTMpdcPSWXklapkGwxJRS","why":"context mismatch (kind)"},{"secret":"b00e9f068deb4b69c474109502839c981bd429075eeab9e5f41db9022c0cd869","public":"6de2d6a91eb75f5e6038d633da8695f35fad807d5bad6b9e25ae8f526e10c05f","kind":1,"scope_hex":"68656c6c6f20776f726c6421","ciphertext":"A49dkIXX4dVAn6A9ql4cQ3MQfoU7rPrsg9/8V8d5NL9+A7Ntb9VIbBQ4V2ORFXS5rzOkHhZkKmtnn3dpMUiQWwsAACcQAAAADGhlbGxvIHdvcmxkIRb3rRdfIPuJvtEnjn6dj7RgKg1OUvGmCQoXmp+32EN6XL+vEHnbIbvLWLqIV7eCAmOqrPVGngCJEppHzzFMuhM=","why":"context mismatch (kind)"},{"secret":"e150f3f5fd2eb47c49e8c1ee0ca51d3502d370108a98519d212731746fc513c2","public":"901f54e63d0d1df5bf120e12faf00d6e4ec3c6d9fe81e0dc0f35cf1d5a489ac2","kind":30078,"scope_hex":"6170706c69636174696f6e2d64617461","ciphertext":"A/JaJhlnuKYJhr6LdZ46lkK7cw0nxGvA4rp112e70q0O9jMnXxaWqqNE7nZnllwl7yuGWRtcLU2q6uWAUZ6wthIAAHV+AAAAD2luY29ycmVjdC1zY29wZZ++z1BEvaKntWN9G+9EIT0da0luVAV/hAvOoUg90pXE4C6nsAKmpZ3Q2YfAvtUDlkMpxPv4u10Rlz0VGlPo9GtcGJp4X2aq/5gdbTZJF8mxnkhZ4zQqwRibV0JJ0ZNZxg==","why":"context mismatch (scope)"},{"secret":"1c9c9ff5df8a9ff99e50b3dac27d987422567e9122075097edea2915e12580ca","public":"32178882654a70441f1c507902ae3a89888ecd2266930a45f8d6b2f578643307","kind":4,"scope_hex":"efbbbf","ciphertext":"A3c7VODzMr9mQXQNRtw3MQsntesGokh3g8nQwdAK4iWqL/nV6HPOhnz5xFf1UjyvTrHJC8BrGjRefguMrJLrfy8AAAAEAAAAD2luY29ycmVjdC1zY29wZfvq9zBo8LQrHyulrPg0swuPZs4W3Xy1TSQlmczZ643aOllm+9DveIcqoXoQaGQTs1RDLeekOMNquv9a5XMWZ431ysJ44L3ET4Ztw5Eevnxx3pgaob8bCikyuGT+5CyR2A==","why":"context mismatch (scope)"},{"secret":"18a8c52a7d94c36bf08ec04336247ab2c67014b964a3e33f3d6677e697f2008c","public":"d1069a731110484f212fa9fe2fa4dcd2e2c0857b5e0dda1bd5667901ce303d81","kind":4,"scope_hex":"ff","ciphertext":"A3PSjybYp19qos4LlMKEdlPQShJIBOJBGjjOI6etumpU4VaN0LYMB8qECxbwF7ebuv2Lxu5h0yXtNpdJtX4Z5fgAAAAEAAAAAf+AeTttkffcDMHUCDRxKYA3p7nnmK2hNvB04X0VP2H27Q==","why":"invalid scope (not valid utf8)"}]}
@@ -0,0 +1,185 @@
1
+ import { finalizeEvent, getPublicKey, nip04, nip44 } from 'nostr-tools'
2
+ import { extract as hkdfExtract, expand as hkdfExpand } from '@noble/hashes/hkdf.js'
3
+ import { sha256 } from '@noble/hashes/sha2.js'
4
+
5
+ import { deriveDoubleDhConversationKey } from '../../double-dh/index.js'
6
+ import { sharedXOnlySecret } from '../../ecdh/index.js'
7
+ import { bytesToHex, hexToBytes } from '../../base16/index.js'
8
+ import * as nip44v3 from '../../nip44-v3/index.js'
9
+
10
+ const textEncoder = new TextEncoder()
11
+ const SECP256K1_N = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141')
12
+ const SHARED_KEY_SALT = textEncoder.encode('nostr-shared-key-v1')
13
+ const secretKeys = new WeakMap()
14
+ const contentSignersByOwner = new WeakMap()
15
+ const signersByPubkey = {}
16
+
17
+ function bytesToBigInt (bytes) {
18
+ return BigInt(`0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}`)
19
+ }
20
+
21
+ function bigIntTo32Bytes (n) {
22
+ return hexToBytes(n.toString(16).padStart(64, '0'))
23
+ }
24
+
25
+ function deriveSecretKeySync (masterKeyBytes, info = '') {
26
+ const infoBytes = typeof info === 'string' ? textEncoder.encode(info) : info
27
+ const prk = hkdfExtract(sha256, masterKeyBytes, SHARED_KEY_SALT)
28
+ const wide = bytesToBigInt(hkdfExpand(sha256, prk, infoBytes, 48))
29
+ return bigIntTo32Bytes((wide % (SECP256K1_N - 1n)) + 1n)
30
+ }
31
+
32
+ function deriveSharedKeySync (mySeckey, theirPubkey, info = '') {
33
+ return deriveSecretKeySync(sharedXOnlySecret(mySeckey, theirPubkey), info)
34
+ }
35
+
36
+ export async function deriveSharedKey (mySeckey, theirPubkey, info = '') {
37
+ return deriveSharedKeySync(mySeckey, theirPubkey, info)
38
+ }
39
+
40
+ class SharedKeySigner {
41
+ constructor (signer, peerPubkey, info = '') {
42
+ this.signer = signer
43
+ this.peerPubkey = peerPubkey
44
+ this.info = info
45
+ this.shared = null
46
+ }
47
+
48
+ sharedSigner () {
49
+ this.shared ??= TestSigner.getOrCreate(bytesToHex(deriveSharedKeySync(secretKeys.get(this.signer), this.peerPubkey, this.info)))
50
+ return this.shared
51
+ }
52
+
53
+ getPublicKey () { return this.sharedSigner().getPublicKey() }
54
+ signEvent (event) { return this.sharedSigner().signEvent(event) }
55
+ nip04Encrypt (peerPubkey, plaintext) { return this.sharedSigner().nip04Encrypt(peerPubkey, plaintext) }
56
+ nip04Decrypt (peerPubkey, ciphertext) { return this.sharedSigner().nip04Decrypt(peerPubkey, ciphertext) }
57
+ nip44Encrypt (peerPubkey, plaintext) { return this.sharedSigner().nip44Encrypt(peerPubkey, plaintext) }
58
+ nip44Decrypt (peerPubkey, ciphertext) { return this.sharedSigner().nip44Decrypt(peerPubkey, ciphertext) }
59
+ nip44v3Encrypt (peerPubkey, kind, scope, plaintextB64) { return this.sharedSigner().nip44v3Encrypt(peerPubkey, kind, scope, plaintextB64) }
60
+ nip44v3Decrypt (peerPubkey, kind, scope, ciphertext) { return this.sharedSigner().nip44v3Decrypt(peerPubkey, kind, scope, ciphertext) }
61
+ nip44EncryptDoubleDH (...params) { return this.sharedSigner().nip44EncryptDoubleDH(...params) }
62
+ nip44DecryptDoubleDH (...params) { return this.sharedSigner().nip44DecryptDoubleDH(...params) }
63
+ withSharedKey (peerPubkey, info = this.info) { return new SharedKeySigner(this.signer, peerPubkey, info) }
64
+ }
65
+
66
+ export default class TestSigner {
67
+ static getOrCreate (seckey) {
68
+ const pubkey = getPublicKey(hexToBytes(seckey))
69
+ return (signersByPubkey[pubkey] ??= new TestSigner(seckey, pubkey))
70
+ }
71
+
72
+ static releaseAll () {
73
+ for (const pubkey of Object.keys(signersByPubkey)) delete signersByPubkey[pubkey]
74
+ }
75
+
76
+ static setContentSigners (ownerSigner, contentSigners = []) {
77
+ const signers = new Map()
78
+ for (const signer of contentSigners) signers.set(signer.getPublicKey(), signer)
79
+ if (signers.size) contentSignersByOwner.set(ownerSigner, signers)
80
+ else contentSignersByOwner.delete(ownerSigner)
81
+ }
82
+
83
+ constructor (seckey, pubkey) {
84
+ secretKeys.set(this, hexToBytes(seckey))
85
+ this.pubkey = pubkey
86
+ this.conversationKeys = {}
87
+ }
88
+
89
+ getPublicKey () {
90
+ return this.pubkey
91
+ }
92
+
93
+ signEvent (event) {
94
+ return finalizeEvent(event, secretKeys.get(this))
95
+ }
96
+
97
+ nip04Encrypt (peerPubkey, plaintext) {
98
+ return nip04.encrypt(secretKeys.get(this), peerPubkey, plaintext)
99
+ }
100
+
101
+ nip04Decrypt (peerPubkey, ciphertext) {
102
+ return nip04.decrypt(secretKeys.get(this), peerPubkey, ciphertext)
103
+ }
104
+
105
+ nip44Encrypt (peerPubkey, plaintext) {
106
+ const key = this.conversationKeys[peerPubkey] ??= nip44.getConversationKey(secretKeys.get(this), peerPubkey)
107
+ return nip44.encrypt(plaintext, key)
108
+ }
109
+
110
+ nip44Decrypt (peerPubkey, ciphertext) {
111
+ const key = this.conversationKeys[peerPubkey] ??= nip44.getConversationKey(secretKeys.get(this), peerPubkey)
112
+ return nip44.decrypt(ciphertext, key)
113
+ }
114
+
115
+ nip44v3Encrypt (peerPubkey, kind, scope, plaintextB64) {
116
+ return nip44v3.nip07Encrypt(secretKeys.get(this), peerPubkey, kind, scope, plaintextB64)
117
+ }
118
+
119
+ nip44v3Decrypt (peerPubkey, kind, scope, ciphertext) {
120
+ return nip44v3.nip07Decrypt(secretKeys.get(this), peerPubkey, kind, scope, ciphertext)
121
+ }
122
+
123
+ contentKeyMaterial (requestedContentPubkey = '') {
124
+ const contentSigners = contentSignersByOwner.get(this)
125
+ const signer = requestedContentPubkey ? contentSigners?.get(requestedContentPubkey) : null
126
+ if (!signer) return { contentPubkey: requestedContentPubkey || '', contentSecretKey: null }
127
+ return {
128
+ contentPubkey: signer.getPublicKey(),
129
+ contentSecretKey: secretKeys.get(signer)
130
+ }
131
+ }
132
+
133
+ latestContentKeyMaterial () {
134
+ const contentSigners = contentSignersByOwner.get(this)
135
+ const signer = contentSigners?.values?.().next().value
136
+ if (!signer) return { contentPubkey: '', contentSecretKey: null }
137
+ return {
138
+ contentPubkey: signer.getPublicKey(),
139
+ contentSecretKey: secretKeys.get(signer)
140
+ }
141
+ }
142
+
143
+ async nip44EncryptDoubleDH (peerPubkey, kind, scope = '', plaintextB64, peerContentPubkey = '') {
144
+ const normalizedKind = nip44v3.normalizeKind(kind)
145
+ const { contentPubkey, contentSecretKey } = this.latestContentKeyMaterial()
146
+ const { conversationKey } = deriveDoubleDhConversationKey({
147
+ role: 'sender',
148
+ identitySecretKey: secretKeys.get(this),
149
+ identityPubkey: this.pubkey,
150
+ contentSecretKey,
151
+ contentPubkey,
152
+ peerIdentityPubkey: peerPubkey,
153
+ peerContentPubkey,
154
+ kind: normalizedKind,
155
+ scope
156
+ })
157
+ const ciphertext = conversationKey
158
+ ? nip44v3.encryptWithConversationKeyBytes(conversationKey, normalizedKind, nip44v3.toBytes(scope || ''), nip44v3.b64decode(plaintextB64))
159
+ : nip44v3.nip07Encrypt(secretKeys.get(this), peerPubkey, normalizedKind, scope, plaintextB64)
160
+ return [ciphertext, contentPubkey]
161
+ }
162
+
163
+ async nip44DecryptDoubleDH (peerPubkey, kind, scope = '', ciphertext, peerContentPubkey = '', ownContentPubkey = '') {
164
+ const normalizedKind = nip44v3.normalizeKind(kind)
165
+ const { contentPubkey, contentSecretKey } = this.contentKeyMaterial(ownContentPubkey)
166
+ const { conversationKey } = deriveDoubleDhConversationKey({
167
+ role: 'receiver',
168
+ identitySecretKey: secretKeys.get(this),
169
+ identityPubkey: this.pubkey,
170
+ contentSecretKey,
171
+ contentPubkey,
172
+ peerIdentityPubkey: peerPubkey,
173
+ peerContentPubkey,
174
+ kind: normalizedKind,
175
+ scope
176
+ })
177
+ return conversationKey
178
+ ? nip44v3.b64encode(nip44v3.decryptWithConversationKeyBytes(conversationKey, normalizedKind, nip44v3.toBytes(scope || ''), ciphertext))
179
+ : nip44v3.nip07Decrypt(secretKeys.get(this), peerPubkey, normalizedKind, scope, ciphertext)
180
+ }
181
+
182
+ withSharedKey (peerPubkey, info) {
183
+ return new SharedKeySigner(this, peerPubkey, info)
184
+ }
185
+ }
@@ -0,0 +1,153 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
4
+ import { createQueue } from '../idb-queue/index.js'
5
+
6
+ function queueFor (factory, prefix, options = {}) {
7
+ return createQueue({ prefix, indexedDB: factory, ...options })
8
+ }
9
+
10
+ async function values (iterator) {
11
+ const out = []
12
+ for await (const item of iterator) out.push(item.value)
13
+ return out
14
+ }
15
+
16
+ test('idb queue supports ordered queue and positional operations', async () => {
17
+ const queue = await queueFor(new IDBFactory(), 'ordered')
18
+
19
+ await queue.push({ value: 'middle' })
20
+ await queue.unshift({ value: 'first' })
21
+ await queue.push({ value: 'last' })
22
+ assert.equal(await queue.setAt(1, { value: 'MIDDLE' }), 1)
23
+ assert.equal(await queue.insertAt(2, { value: 'between' }), 2)
24
+
25
+ assert.equal((await queue.shift()).value, 'first')
26
+ assert.equal((await queue.removeAt(1)).value, 'between')
27
+ assert.equal((await queue.shift()).value, 'MIDDLE')
28
+ assert.equal((await queue.pop()).value, 'last')
29
+ assert.equal(await queue.shift(), null)
30
+ })
31
+
32
+ test('idb queue keeps predicate parity and skips sparse removals', async () => {
33
+ const queue = await queueFor(new IDBFactory(), 'predicate')
34
+ await queue.push({ value: 'a' })
35
+ await queue.push({ value: 'c' })
36
+ assert.equal(await queue.insertWhere(item => item.value === 'c', { value: 'b' }), 1)
37
+ assert.equal(await queue.some(item => item.value === 'b'), true)
38
+ await queue.removeWhere(item => item.value === 'b')
39
+
40
+ assert.deepEqual(await values(queue.storedItems()), ['a', 'c'])
41
+ assert.equal((await queue.shift()).value, 'a')
42
+ assert.equal((await queue.shift()).value, 'c')
43
+ assert.equal(await queue.shift(), null)
44
+ })
45
+
46
+ test('idb queue streams live values and supports reverse snapshots', async () => {
47
+ const queue = await queueFor(new IDBFactory(), 'streams')
48
+ await queue.push({ value: 'first' })
49
+ await queue.push({ value: 'second' })
50
+
51
+ assert.deepEqual(await values(queue.reverseStoredItems()), ['second', 'first'])
52
+ const iterator = queue.items()
53
+ assert.equal((await iterator.next()).value.value, 'first')
54
+ assert.equal((await iterator.next()).value.value, 'second')
55
+ const pending = iterator.next()
56
+ await queue.push({ value: 'third' })
57
+ assert.equal((await pending).value.value, 'third')
58
+ await iterator.return()
59
+ })
60
+
61
+ test('idb queue enforces byte budgets and persists across queue instances', async () => {
62
+ const factory = new IDBFactory()
63
+ const queue = await queueFor(factory, 'capacity', { maxBytes: 450, evictionPolicy: 'fifo' })
64
+ await queue.push({ value: 'a'.repeat(120) })
65
+ await queue.push({ value: 'b'.repeat(120) })
66
+ await queue.push({ value: 'c'.repeat(120) })
67
+
68
+ assert.equal((await queue.shift()).value, 'b'.repeat(120))
69
+ assert.equal((await queue.shift()).value, 'c'.repeat(120))
70
+
71
+ const unbounded = await queueFor(factory, 'reopen')
72
+ await unbounded.push({ value: 'a'.repeat(120) })
73
+ await unbounded.push({ value: 'b'.repeat(120) })
74
+ await unbounded.push({ value: 'c'.repeat(120) })
75
+ const bounded = await queueFor(factory, 'reopen', { maxBytes: 450, evictionPolicy: 'fifo' })
76
+ assert.equal((await bounded.shift()).value, 'b'.repeat(120))
77
+ assert.equal((await bounded.shift()).value, 'c'.repeat(120))
78
+ })
79
+
80
+ test('idb queue exposes indexed equality, range, and deletion operations', async () => {
81
+ const queue = await queueFor(new IDBFactory(), 'indexed', {
82
+ indexes: {
83
+ byKind: 'kind',
84
+ byChannelTime: ['channel', 'time'],
85
+ byKey: { keyPath: 'key', unique: true }
86
+ }
87
+ })
88
+ await queue.push({ value: 'a', kind: 'message', channel: 'one', time: 10, key: 'a' })
89
+ await queue.push({ value: 'b', kind: 'message', channel: 'one', time: 20, key: 'b' })
90
+ await queue.push({ value: 'c', kind: 'seed', channel: 'two', time: 30, key: 'c' })
91
+
92
+ assert.equal((await queue.getBy('byKey', 'b')).value, 'b')
93
+ assert.equal(await queue.someBy('byKind', 'seed'), true)
94
+ assert.deepEqual(
95
+ await values(queue.storedItemsBy('byChannelTime', IDBKeyRange.bound(['one', 0], ['one', 20]))),
96
+ ['a', 'b']
97
+ )
98
+ assert.deepEqual((await queue.removeBy('byKind', 'message')).map(item => item.value), ['a', 'b'])
99
+ assert.deepEqual(await values(queue.storedItems()), ['c'])
100
+ })
101
+
102
+ test('idb queue evolves additive indexes and rejects incompatible definitions', async () => {
103
+ const factory = new IDBFactory()
104
+ const first = await queueFor(factory, 'schema', { indexes: { byKind: 'kind' } })
105
+ await first.push({ value: 'a', kind: 'message', channel: 'one' })
106
+ const second = await queueFor(factory, 'schema', {
107
+ indexes: { byKind: 'kind', byChannel: 'channel' }
108
+ })
109
+ assert.equal((await second.getBy('byChannel', 'one')).value, 'a')
110
+ await assert.rejects(
111
+ queueFor(factory, 'schema', { indexes: { byKind: 'channel' } }),
112
+ /QUEUE_INDEX_SCHEMA_MISMATCH/
113
+ )
114
+ })
115
+
116
+ test('idb queue supports unique compound indexes', async () => {
117
+ const queue = await queueFor(new IDBFactory(), 'compound', {
118
+ indexes: {
119
+ byChannelTypeEventId: {
120
+ keyPath: ['channel', 'type', 'event.id'],
121
+ unique: true
122
+ }
123
+ }
124
+ })
125
+ await queue.push({ value: 'first', channel: 'one', type: 'tell', event: { id: 'event' } })
126
+ await queue.push({ value: 'second', channel: 'one', type: 'reply', event: { id: 'event' } })
127
+ await queue.push({ value: 'third', channel: 'two', type: 'tell', event: { id: 'event' } })
128
+
129
+ assert.equal(await queue.someBy('byChannelTypeEventId', ['one', 'tell', 'event']), true)
130
+ await assert.rejects(
131
+ queue.push({ value: 'duplicate', channel: 'one', type: 'tell', event: { id: 'event' } }),
132
+ /ConstraintError/
133
+ )
134
+ })
135
+
136
+ test('idb queue rolls back failed writes without an operation journal', async () => {
137
+ const queue = await queueFor(new IDBFactory(), 'atomic')
138
+ await queue.push({ value: 'kept' })
139
+
140
+ await assert.rejects(queue.push({ value: 'bad', nonCloneable: () => {} }))
141
+ assert.equal((await queue.shift()).value, 'kept')
142
+ assert.equal(await queue.shift(), null)
143
+ })
144
+
145
+ test('idb queue rejects unavailable IndexedDB and clears atomically', async () => {
146
+ await assert.rejects(createQueue({ prefix: 'missing', indexedDB: null }), /IDB_UNAVAILABLE/)
147
+
148
+ const queue = await queueFor(new IDBFactory(), 'clear')
149
+ await queue.push({ value: 'first' })
150
+ await queue.push({ value: 'second' })
151
+ await queue.clear()
152
+ assert.equal(await queue.shift(), null)
153
+ })