@snaha/swarm-id 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.
- package/README.md +431 -0
- package/dist/chunk/bmt.d.ts +17 -0
- package/dist/chunk/bmt.d.ts.map +1 -0
- package/dist/chunk/cac.d.ts +18 -0
- package/dist/chunk/cac.d.ts.map +1 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/constants.d.ts.map +1 -0
- package/dist/chunk/encrypted-cac.d.ts +48 -0
- package/dist/chunk/encrypted-cac.d.ts.map +1 -0
- package/dist/chunk/encryption.d.ts +86 -0
- package/dist/chunk/encryption.d.ts.map +1 -0
- package/dist/chunk/index.d.ts +6 -0
- package/dist/chunk/index.d.ts.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/proxy/act/act.d.ts +78 -0
- package/dist/proxy/act/act.d.ts.map +1 -0
- package/dist/proxy/act/crypto.d.ts +44 -0
- package/dist/proxy/act/crypto.d.ts.map +1 -0
- package/dist/proxy/act/grantee-list.d.ts +82 -0
- package/dist/proxy/act/grantee-list.d.ts.map +1 -0
- package/dist/proxy/act/history.d.ts +183 -0
- package/dist/proxy/act/history.d.ts.map +1 -0
- package/dist/proxy/act/index.d.ts +104 -0
- package/dist/proxy/act/index.d.ts.map +1 -0
- package/dist/proxy/chunking-encrypted.d.ts +14 -0
- package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/chunking.d.ts.map +1 -0
- package/dist/proxy/download-data.d.ts +16 -0
- package/dist/proxy/download-data.d.ts.map +1 -0
- package/dist/proxy/feed-manifest.d.ts +62 -0
- package/dist/proxy/feed-manifest.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
- package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/index.d.ts +35 -0
- package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/types.d.ts +109 -0
- package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
- package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
- package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
- package/dist/proxy/feeds/index.d.ts +5 -0
- package/dist/proxy/feeds/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
- package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/index.d.ts +23 -0
- package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/types.d.ts +80 -0
- package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
- package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
- package/dist/proxy/index.d.ts +6 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/manifest-builder.d.ts +183 -0
- package/dist/proxy/manifest-builder.d.ts.map +1 -0
- package/dist/proxy/mantaray-encrypted.d.ts +27 -0
- package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
- package/dist/proxy/mantaray.d.ts +26 -0
- package/dist/proxy/mantaray.d.ts.map +1 -0
- package/dist/proxy/types.d.ts +29 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/upload-data.d.ts +17 -0
- package/dist/proxy/upload-data.d.ts.map +1 -0
- package/dist/proxy/upload-encrypted-data.d.ts +103 -0
- package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
- package/dist/schemas.d.ts +240 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/storage/debounced-uploader.d.ts +62 -0
- package/dist/storage/debounced-uploader.d.ts.map +1 -0
- package/dist/storage/utilization-store.d.ts +108 -0
- package/dist/storage/utilization-store.d.ts.map +1 -0
- package/dist/swarm-id-auth.d.ts +74 -0
- package/dist/swarm-id-auth.d.ts.map +1 -0
- package/dist/swarm-id-auth.js +2 -0
- package/dist/swarm-id-auth.js.map +1 -0
- package/dist/swarm-id-client.d.ts +878 -0
- package/dist/swarm-id-client.d.ts.map +1 -0
- package/dist/swarm-id-client.js +2 -0
- package/dist/swarm-id-client.js.map +1 -0
- package/dist/swarm-id-proxy.d.ts +236 -0
- package/dist/swarm-id-proxy.d.ts.map +1 -0
- package/dist/swarm-id-proxy.js +2 -0
- package/dist/swarm-id-proxy.js.map +1 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/swarm-id.umd.js +2 -0
- package/dist/swarm-id.umd.js.map +1 -0
- package/dist/sync/index.d.ts +9 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/key-derivation.d.ts +25 -0
- package/dist/sync/key-derivation.d.ts.map +1 -0
- package/dist/sync/restore-account.d.ts +28 -0
- package/dist/sync/restore-account.d.ts.map +1 -0
- package/dist/sync/serialization.d.ts +16 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/store-interfaces.d.ts +53 -0
- package/dist/sync/store-interfaces.d.ts.map +1 -0
- package/dist/sync/sync-account.d.ts +44 -0
- package/dist/sync/sync-account.d.ts.map +1 -0
- package/dist/sync/types.d.ts +13 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/test-fixtures.d.ts +17 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/types-BD_VkNn0.js +2 -0
- package/dist/types-BD_VkNn0.js.map +1 -0
- package/dist/types-lJCaT-50.js +2 -0
- package/dist/types-lJCaT-50.js.map +1 -0
- package/dist/types.d.ts +2157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/account-payload.d.ts +94 -0
- package/dist/utils/account-payload.d.ts.map +1 -0
- package/dist/utils/account-state-snapshot.d.ts +38 -0
- package/dist/utils/account-state-snapshot.d.ts.map +1 -0
- package/dist/utils/backup-encryption.d.ts +127 -0
- package/dist/utils/backup-encryption.d.ts.map +1 -0
- package/dist/utils/batch-utilization.d.ts +432 -0
- package/dist/utils/batch-utilization.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +11 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/hex.d.ts +17 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/key-derivation.d.ts +92 -0
- package/dist/utils/key-derivation.d.ts.map +1 -0
- package/dist/utils/storage-managers.d.ts +65 -0
- package/dist/utils/storage-managers.d.ts.map +1 -0
- package/dist/utils/swarm-id-export.d.ts +24 -0
- package/dist/utils/swarm-id-export.d.ts.map +1 -0
- package/dist/utils/ttl.d.ts +49 -0
- package/dist/utils/ttl.d.ts.map +1 -0
- package/dist/utils/url.d.ts +41 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/versioned-storage.d.ts +131 -0
- package/dist/utils/versioned-storage.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/chunk/bmt.test.ts +217 -0
- package/src/chunk/bmt.ts +57 -0
- package/src/chunk/cac.test.ts +214 -0
- package/src/chunk/cac.ts +65 -0
- package/src/chunk/constants.ts +18 -0
- package/src/chunk/encrypted-cac.test.ts +385 -0
- package/src/chunk/encrypted-cac.ts +131 -0
- package/src/chunk/encryption.test.ts +352 -0
- package/src/chunk/encryption.ts +300 -0
- package/src/chunk/index.ts +47 -0
- package/src/index.ts +430 -0
- package/src/proxy/act/act.test.ts +278 -0
- package/src/proxy/act/act.ts +158 -0
- package/src/proxy/act/bee-compat.test.ts +948 -0
- package/src/proxy/act/crypto.test.ts +436 -0
- package/src/proxy/act/crypto.ts +376 -0
- package/src/proxy/act/grantee-list.test.ts +393 -0
- package/src/proxy/act/grantee-list.ts +239 -0
- package/src/proxy/act/history.test.ts +360 -0
- package/src/proxy/act/history.ts +413 -0
- package/src/proxy/act/index.test.ts +748 -0
- package/src/proxy/act/index.ts +853 -0
- package/src/proxy/chunking-encrypted.ts +95 -0
- package/src/proxy/chunking.ts +65 -0
- package/src/proxy/download-data.ts +448 -0
- package/src/proxy/feed-manifest.ts +174 -0
- package/src/proxy/feeds/epochs/async-finder.ts +372 -0
- package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
- package/src/proxy/feeds/epochs/epoch.ts +181 -0
- package/src/proxy/feeds/epochs/finder.ts +282 -0
- package/src/proxy/feeds/epochs/index.ts +73 -0
- package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
- package/src/proxy/feeds/epochs/test-utils.ts +274 -0
- package/src/proxy/feeds/epochs/types.ts +128 -0
- package/src/proxy/feeds/epochs/updater.ts +192 -0
- package/src/proxy/feeds/epochs/utils.ts +62 -0
- package/src/proxy/feeds/index.ts +5 -0
- package/src/proxy/feeds/sequence/async-finder.ts +31 -0
- package/src/proxy/feeds/sequence/finder.ts +73 -0
- package/src/proxy/feeds/sequence/index.ts +54 -0
- package/src/proxy/feeds/sequence/integration.test.ts +966 -0
- package/src/proxy/feeds/sequence/types.ts +103 -0
- package/src/proxy/feeds/sequence/updater.ts +71 -0
- package/src/proxy/index.ts +5 -0
- package/src/proxy/manifest-builder.test.ts +427 -0
- package/src/proxy/manifest-builder.ts +679 -0
- package/src/proxy/mantaray-encrypted.ts +78 -0
- package/src/proxy/mantaray.ts +104 -0
- package/src/proxy/types.ts +32 -0
- package/src/proxy/upload-data.ts +189 -0
- package/src/proxy/upload-encrypted-data.ts +658 -0
- package/src/schemas.ts +299 -0
- package/src/storage/debounced-uploader.ts +192 -0
- package/src/storage/utilization-store.ts +397 -0
- package/src/swarm-id-client.test.ts +99 -0
- package/src/swarm-id-client.ts +3095 -0
- package/src/swarm-id-proxy.ts +3891 -0
- package/src/sync/index.ts +28 -0
- package/src/sync/restore-account.ts +90 -0
- package/src/sync/serialization.ts +39 -0
- package/src/sync/store-interfaces.ts +62 -0
- package/src/sync/sync-account.test.ts +302 -0
- package/src/sync/sync-account.ts +396 -0
- package/src/sync/types.ts +11 -0
- package/src/test-fixtures.ts +109 -0
- package/src/types.ts +1651 -0
- package/src/utils/account-state-snapshot.test.ts +595 -0
- package/src/utils/account-state-snapshot.ts +94 -0
- package/src/utils/backup-encryption.test.ts +442 -0
- package/src/utils/backup-encryption.ts +352 -0
- package/src/utils/batch-utilization.ts +1309 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/hex.ts +27 -0
- package/src/utils/key-derivation.ts +197 -0
- package/src/utils/storage-managers.ts +365 -0
- package/src/utils/ttl.ts +129 -0
- package/src/utils/url.test.ts +136 -0
- package/src/utils/url.ts +71 -0
- package/src/utils/versioned-storage.ts +323 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for high-level ACT operations (Bee-compatible API)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from "vitest"
|
|
6
|
+
import type { Bee, Stamper } from "@ethersphere/bee-js"
|
|
7
|
+
import { MerkleTree } from "@ethersphere/bee-js"
|
|
8
|
+
import type { UploadContext } from "../types"
|
|
9
|
+
import {
|
|
10
|
+
createActForContent,
|
|
11
|
+
decryptActReference,
|
|
12
|
+
addGranteesToAct,
|
|
13
|
+
revokeGranteesFromAct,
|
|
14
|
+
getGranteesFromAct,
|
|
15
|
+
parseCompressedPublicKey,
|
|
16
|
+
} from "./index"
|
|
17
|
+
import {
|
|
18
|
+
publicKeyFromPrivate,
|
|
19
|
+
compressPublicKey,
|
|
20
|
+
deriveKeys,
|
|
21
|
+
counterModeDecrypt,
|
|
22
|
+
} from "./crypto"
|
|
23
|
+
import { collectActEntriesFromJson, findActEntryByKey } from "./act"
|
|
24
|
+
import { decryptAndDeserializeGranteeList } from "./grantee-list"
|
|
25
|
+
|
|
26
|
+
// Mock the upload/download functions
|
|
27
|
+
vi.mock("../upload-encrypted-data", () => ({
|
|
28
|
+
uploadEncryptedDataWithSigning: vi.fn(),
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
vi.mock("../download-data", () => ({
|
|
32
|
+
downloadDataWithChunkAPI: vi.fn(),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
import { uploadEncryptedDataWithSigning } from "../upload-encrypted-data"
|
|
36
|
+
import { downloadDataWithChunkAPI } from "../download-data"
|
|
37
|
+
|
|
38
|
+
// Helper to create a random 32-byte array
|
|
39
|
+
function randomBytes(length: number): Uint8Array {
|
|
40
|
+
const bytes = new Uint8Array(length)
|
|
41
|
+
crypto.getRandomValues(bytes)
|
|
42
|
+
return bytes
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Helper to convert bytes to hex
|
|
46
|
+
function toHex(bytes: Uint8Array): string {
|
|
47
|
+
return Array.from(bytes)
|
|
48
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
49
|
+
.join("")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Helper to convert hex to bytes
|
|
53
|
+
function fromHex(hex: string): Uint8Array {
|
|
54
|
+
const bytes = new Uint8Array(hex.length / 2)
|
|
55
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
56
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
|
|
57
|
+
}
|
|
58
|
+
return bytes
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Create mock context
|
|
62
|
+
function createMockContext(): UploadContext {
|
|
63
|
+
return {
|
|
64
|
+
bee: {} as Bee,
|
|
65
|
+
stamper: {} as Stamper,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create a valid 32-byte hex reference for mocking
|
|
70
|
+
function createMockReference(counter: number): string {
|
|
71
|
+
// Create a 32-byte array with the counter as the last byte
|
|
72
|
+
const bytes = new Uint8Array(32)
|
|
73
|
+
bytes[31] = counter
|
|
74
|
+
return toHex(bytes)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Compute content hash using Swarm's BMT algorithm (same as MantarayNode)
|
|
78
|
+
async function computeContentHash(data: Uint8Array): Promise<string> {
|
|
79
|
+
const rootNode = await MerkleTree.root(data)
|
|
80
|
+
return toHex(rootNode.hash())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Create a content-addressed storage mock for uploads
|
|
84
|
+
function createContentAddressedUploadMock() {
|
|
85
|
+
const storage: Map<string, Uint8Array> = new Map()
|
|
86
|
+
|
|
87
|
+
const uploadMock = async (
|
|
88
|
+
_ctx: UploadContext,
|
|
89
|
+
data: Uint8Array,
|
|
90
|
+
): Promise<{
|
|
91
|
+
reference: string
|
|
92
|
+
tagUid?: number
|
|
93
|
+
chunkAddresses: Uint8Array[]
|
|
94
|
+
}> => {
|
|
95
|
+
const address = await computeContentHash(data)
|
|
96
|
+
// Fake 64-byte encrypted reference: address + address
|
|
97
|
+
const ref = `${address}${address}`
|
|
98
|
+
storage.set(ref, data)
|
|
99
|
+
return { reference: ref, chunkAddresses: [] }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const downloadMock = async (_bee: Bee, ref: string): Promise<Uint8Array> => {
|
|
103
|
+
const data = storage.get(ref)
|
|
104
|
+
if (!data) {
|
|
105
|
+
throw new Error(`Data not found for reference: ${ref}`)
|
|
106
|
+
}
|
|
107
|
+
return data
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { storage, uploadMock, downloadMock }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create test key pair
|
|
114
|
+
function createTestKeyPair(seed: number): {
|
|
115
|
+
privateKey: Uint8Array
|
|
116
|
+
publicKey: { x: Uint8Array; y: Uint8Array }
|
|
117
|
+
compressedPublicKey: string
|
|
118
|
+
} {
|
|
119
|
+
const privateKey = new Uint8Array(32)
|
|
120
|
+
privateKey[31] = seed
|
|
121
|
+
const publicKey = publicKeyFromPrivate(privateKey)
|
|
122
|
+
const compressed = compressPublicKey(publicKey.x, publicKey.y)
|
|
123
|
+
return {
|
|
124
|
+
privateKey,
|
|
125
|
+
publicKey,
|
|
126
|
+
compressedPublicKey: toHex(compressed),
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Helper to load ACT JSON data from storage
|
|
131
|
+
function loadActDataFromStorage(
|
|
132
|
+
storage: Map<string, Uint8Array>,
|
|
133
|
+
actReference: string,
|
|
134
|
+
): Uint8Array {
|
|
135
|
+
const actData = storage.get(actReference)
|
|
136
|
+
if (!actData) {
|
|
137
|
+
throw new Error(`ACT data not found for reference: ${actReference}`)
|
|
138
|
+
}
|
|
139
|
+
return actData
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
describe("parseCompressedPublicKey", () => {
|
|
143
|
+
it("should parse compressed public key from hex string", () => {
|
|
144
|
+
const keyPair = createTestKeyPair(1)
|
|
145
|
+
const parsed = parseCompressedPublicKey(keyPair.compressedPublicKey)
|
|
146
|
+
|
|
147
|
+
expect(parsed.x).toEqual(keyPair.publicKey.x)
|
|
148
|
+
expect(parsed.y).toEqual(keyPair.publicKey.y)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("should throw for invalid hex string", () => {
|
|
152
|
+
expect(() => parseCompressedPublicKey("invalid")).toThrow()
|
|
153
|
+
expect(() => parseCompressedPublicKey("0102")).toThrow() // Too short
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe("createActForContent", () => {
|
|
158
|
+
beforeEach(() => {
|
|
159
|
+
vi.clearAllMocks()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("should create ACT with publisher and grantees", async () => {
|
|
163
|
+
const context = createMockContext()
|
|
164
|
+
const publisher = createTestKeyPair(10)
|
|
165
|
+
const grantee1 = createTestKeyPair(11)
|
|
166
|
+
const grantee2 = createTestKeyPair(12)
|
|
167
|
+
|
|
168
|
+
const contentRef = randomBytes(64)
|
|
169
|
+
|
|
170
|
+
// Use content-addressed storage mock
|
|
171
|
+
const { storage, uploadMock } = createContentAddressedUploadMock()
|
|
172
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
173
|
+
|
|
174
|
+
const result = await createActForContent(
|
|
175
|
+
context,
|
|
176
|
+
contentRef,
|
|
177
|
+
publisher.privateKey,
|
|
178
|
+
[grantee1.publicKey, grantee2.publicKey],
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Should have all references
|
|
182
|
+
expect(result.encryptedReference).toBeDefined()
|
|
183
|
+
expect(result.encryptedReference.length).toBe(128) // 64 bytes = 128 hex chars
|
|
184
|
+
expect(result.historyReference).toBeDefined()
|
|
185
|
+
expect(result.granteeListReference).toBeDefined()
|
|
186
|
+
expect(result.publisherPubKey).toBeDefined()
|
|
187
|
+
expect(result.actReference).toBeDefined()
|
|
188
|
+
|
|
189
|
+
// ACT manifest should have 3 entries (publisher + 2 grantees)
|
|
190
|
+
const actData = loadActDataFromStorage(storage, result.actReference)
|
|
191
|
+
const actEntries = collectActEntriesFromJson(actData)
|
|
192
|
+
expect(actEntries.length).toBe(3)
|
|
193
|
+
|
|
194
|
+
// Grantee list should have 2 grantees
|
|
195
|
+
const granteeListBlob = storage.get(result.granteeListReference)
|
|
196
|
+
expect(granteeListBlob).toBeDefined()
|
|
197
|
+
const grantees = decryptAndDeserializeGranteeList(
|
|
198
|
+
granteeListBlob!,
|
|
199
|
+
publisher.privateKey,
|
|
200
|
+
)
|
|
201
|
+
expect(grantees.length).toBe(2)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("should create ACT that publisher can decrypt", async () => {
|
|
205
|
+
const context = createMockContext()
|
|
206
|
+
const publisher = createTestKeyPair(20)
|
|
207
|
+
const grantee = createTestKeyPair(21)
|
|
208
|
+
|
|
209
|
+
const contentRef = randomBytes(32)
|
|
210
|
+
|
|
211
|
+
// Use content-addressed storage mock
|
|
212
|
+
const { storage, uploadMock } = createContentAddressedUploadMock()
|
|
213
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
214
|
+
|
|
215
|
+
const result = await createActForContent(
|
|
216
|
+
context,
|
|
217
|
+
contentRef,
|
|
218
|
+
publisher.privateKey,
|
|
219
|
+
[grantee.publicKey],
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
// Publisher should be able to decrypt
|
|
223
|
+
const actData = loadActDataFromStorage(storage, result.actReference)
|
|
224
|
+
|
|
225
|
+
// Derive publisher's lookup key (publisher uses their own pub key)
|
|
226
|
+
const publisherKeys = deriveKeys(
|
|
227
|
+
publisher.privateKey,
|
|
228
|
+
publisher.publicKey.x,
|
|
229
|
+
publisher.publicKey.y,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
// Find publisher's entry
|
|
233
|
+
const encryptedAccessKey = findActEntryByKey(
|
|
234
|
+
actData,
|
|
235
|
+
publisherKeys.lookupKey,
|
|
236
|
+
)
|
|
237
|
+
expect(encryptedAccessKey).toBeDefined()
|
|
238
|
+
|
|
239
|
+
// Decrypt access key
|
|
240
|
+
const accessKey = counterModeDecrypt(
|
|
241
|
+
encryptedAccessKey!,
|
|
242
|
+
publisherKeys.accessKeyDecryptionKey,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
// Decrypt the encrypted reference
|
|
246
|
+
const encryptedRefBytes = fromHex(result.encryptedReference)
|
|
247
|
+
const decryptedRef = counterModeDecrypt(encryptedRefBytes, accessKey)
|
|
248
|
+
|
|
249
|
+
// First 32 bytes should match original content ref
|
|
250
|
+
expect(decryptedRef.slice(0, 32)).toEqual(contentRef)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it("should create ACT that grantee can decrypt", async () => {
|
|
254
|
+
const context = createMockContext()
|
|
255
|
+
const publisher = createTestKeyPair(30)
|
|
256
|
+
const grantee = createTestKeyPair(31)
|
|
257
|
+
|
|
258
|
+
const contentRef = randomBytes(32)
|
|
259
|
+
|
|
260
|
+
// Use content-addressed storage mock
|
|
261
|
+
const { storage, uploadMock } = createContentAddressedUploadMock()
|
|
262
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
263
|
+
|
|
264
|
+
const result = await createActForContent(
|
|
265
|
+
context,
|
|
266
|
+
contentRef,
|
|
267
|
+
publisher.privateKey,
|
|
268
|
+
[grantee.publicKey],
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
// Grantee should be able to decrypt
|
|
272
|
+
const actData = loadActDataFromStorage(storage, result.actReference)
|
|
273
|
+
|
|
274
|
+
// Grantee derives keys using publisher's public key
|
|
275
|
+
const granteeKeys = deriveKeys(
|
|
276
|
+
grantee.privateKey,
|
|
277
|
+
publisher.publicKey.x,
|
|
278
|
+
publisher.publicKey.y,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
// Find grantee's entry
|
|
282
|
+
const encryptedAccessKey = findActEntryByKey(actData, granteeKeys.lookupKey)
|
|
283
|
+
expect(encryptedAccessKey).toBeDefined()
|
|
284
|
+
|
|
285
|
+
// Decrypt access key
|
|
286
|
+
const accessKey = counterModeDecrypt(
|
|
287
|
+
encryptedAccessKey!,
|
|
288
|
+
granteeKeys.accessKeyDecryptionKey,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
// Decrypt the encrypted reference
|
|
292
|
+
const encryptedRefBytes = fromHex(result.encryptedReference)
|
|
293
|
+
const decryptedRef = counterModeDecrypt(encryptedRefBytes, accessKey)
|
|
294
|
+
|
|
295
|
+
// First 32 bytes should match original content ref
|
|
296
|
+
expect(decryptedRef.slice(0, 32)).toEqual(contentRef)
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe("decryptActReference", () => {
|
|
301
|
+
beforeEach(() => {
|
|
302
|
+
vi.clearAllMocks()
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it("should decrypt reference when reader is a grantee", async () => {
|
|
306
|
+
const bee = {} as Bee
|
|
307
|
+
const publisher = createTestKeyPair(40)
|
|
308
|
+
const grantee = createTestKeyPair(41)
|
|
309
|
+
|
|
310
|
+
const originalContentRef = randomBytes(32)
|
|
311
|
+
const originalContentRefHex = toHex(originalContentRef)
|
|
312
|
+
|
|
313
|
+
// Use content-addressed storage mock
|
|
314
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
315
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
316
|
+
|
|
317
|
+
const context = createMockContext()
|
|
318
|
+
const createResult = await createActForContent(
|
|
319
|
+
context,
|
|
320
|
+
originalContentRef,
|
|
321
|
+
publisher.privateKey,
|
|
322
|
+
[grantee.publicKey],
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
// Mock download to return the appropriate blobs
|
|
326
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
327
|
+
|
|
328
|
+
// Grantee should be able to decrypt
|
|
329
|
+
const decryptedRef = await decryptActReference(
|
|
330
|
+
bee,
|
|
331
|
+
createResult.encryptedReference,
|
|
332
|
+
createResult.historyReference,
|
|
333
|
+
createResult.publisherPubKey,
|
|
334
|
+
grantee.privateKey,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
expect(decryptedRef).toBe(originalContentRefHex)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it("should decrypt reference when reader is the publisher", async () => {
|
|
341
|
+
const bee = {} as Bee
|
|
342
|
+
const publisher = createTestKeyPair(50)
|
|
343
|
+
const grantee = createTestKeyPair(51)
|
|
344
|
+
|
|
345
|
+
const originalContentRef = randomBytes(32)
|
|
346
|
+
const originalContentRefHex = toHex(originalContentRef)
|
|
347
|
+
|
|
348
|
+
// Use content-addressed storage mock
|
|
349
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
350
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
351
|
+
|
|
352
|
+
const context = createMockContext()
|
|
353
|
+
const createResult = await createActForContent(
|
|
354
|
+
context,
|
|
355
|
+
originalContentRef,
|
|
356
|
+
publisher.privateKey,
|
|
357
|
+
[grantee.publicKey],
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
361
|
+
|
|
362
|
+
// Publisher should be able to decrypt their own content
|
|
363
|
+
const decryptedRef = await decryptActReference(
|
|
364
|
+
bee,
|
|
365
|
+
createResult.encryptedReference,
|
|
366
|
+
createResult.historyReference,
|
|
367
|
+
createResult.publisherPubKey,
|
|
368
|
+
publisher.privateKey,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
expect(decryptedRef).toBe(originalContentRefHex)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it("should throw error when reader is not authorized", async () => {
|
|
375
|
+
const bee = {} as Bee
|
|
376
|
+
const publisher = createTestKeyPair(60)
|
|
377
|
+
const grantee = createTestKeyPair(61)
|
|
378
|
+
const unauthorized = createTestKeyPair(62)
|
|
379
|
+
|
|
380
|
+
const originalContentRef = randomBytes(32)
|
|
381
|
+
|
|
382
|
+
// Use content-addressed storage mock
|
|
383
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
384
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
385
|
+
|
|
386
|
+
const context = createMockContext()
|
|
387
|
+
const createResult = await createActForContent(
|
|
388
|
+
context,
|
|
389
|
+
originalContentRef,
|
|
390
|
+
publisher.privateKey,
|
|
391
|
+
[grantee.publicKey],
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
395
|
+
|
|
396
|
+
// Unauthorized user should not be able to decrypt
|
|
397
|
+
await expect(
|
|
398
|
+
decryptActReference(
|
|
399
|
+
bee,
|
|
400
|
+
createResult.encryptedReference,
|
|
401
|
+
createResult.historyReference,
|
|
402
|
+
createResult.publisherPubKey,
|
|
403
|
+
unauthorized.privateKey,
|
|
404
|
+
),
|
|
405
|
+
).rejects.toThrow("Access denied")
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
describe("addGranteesToAct", () => {
|
|
410
|
+
beforeEach(() => {
|
|
411
|
+
vi.clearAllMocks()
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
it("should add new grantees to existing ACT", async () => {
|
|
415
|
+
const publisher = createTestKeyPair(70)
|
|
416
|
+
const grantee1 = createTestKeyPair(71)
|
|
417
|
+
const grantee2 = createTestKeyPair(72) // New grantee to add
|
|
418
|
+
|
|
419
|
+
const originalContentRef = randomBytes(32)
|
|
420
|
+
|
|
421
|
+
// Use content-addressed storage mock
|
|
422
|
+
const { storage, uploadMock, downloadMock } =
|
|
423
|
+
createContentAddressedUploadMock()
|
|
424
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
425
|
+
|
|
426
|
+
const context = createMockContext()
|
|
427
|
+
const createResult = await createActForContent(
|
|
428
|
+
context,
|
|
429
|
+
originalContentRef,
|
|
430
|
+
publisher.privateKey,
|
|
431
|
+
[grantee1.publicKey],
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
// Verify original has 2 entries (publisher + grantee1)
|
|
435
|
+
const originalActData = loadActDataFromStorage(
|
|
436
|
+
storage,
|
|
437
|
+
createResult.actReference,
|
|
438
|
+
)
|
|
439
|
+
const originalEntries = collectActEntriesFromJson(originalActData)
|
|
440
|
+
expect(originalEntries.length).toBe(2)
|
|
441
|
+
|
|
442
|
+
// Mock download to return the uploaded blobs
|
|
443
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
444
|
+
|
|
445
|
+
// Add new grantee
|
|
446
|
+
const result = await addGranteesToAct(
|
|
447
|
+
context,
|
|
448
|
+
createResult.historyReference,
|
|
449
|
+
publisher.privateKey,
|
|
450
|
+
[grantee2.publicKey],
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
expect(result.actReference).toBeDefined()
|
|
454
|
+
expect(result.historyReference).toBeDefined()
|
|
455
|
+
expect(result.granteeListReference).toBeDefined()
|
|
456
|
+
|
|
457
|
+
// Verify new ACT has 3 entries
|
|
458
|
+
const newActData = loadActDataFromStorage(storage, result.actReference)
|
|
459
|
+
const newEntries = collectActEntriesFromJson(newActData)
|
|
460
|
+
expect(newEntries.length).toBe(3)
|
|
461
|
+
|
|
462
|
+
// Verify grantee list has 2 grantees
|
|
463
|
+
const newGranteeListBlob = storage.get(result.granteeListReference)!
|
|
464
|
+
const grantees = decryptAndDeserializeGranteeList(
|
|
465
|
+
newGranteeListBlob,
|
|
466
|
+
publisher.privateKey,
|
|
467
|
+
)
|
|
468
|
+
expect(grantees.length).toBe(2)
|
|
469
|
+
|
|
470
|
+
// New grantee should be able to find their entry
|
|
471
|
+
const grantee2Keys = deriveKeys(
|
|
472
|
+
grantee2.privateKey,
|
|
473
|
+
publisher.publicKey.x,
|
|
474
|
+
publisher.publicKey.y,
|
|
475
|
+
)
|
|
476
|
+
const grantee2Entry = findActEntryByKey(newActData, grantee2Keys.lookupKey)
|
|
477
|
+
expect(grantee2Entry).toBeDefined()
|
|
478
|
+
})
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
describe("revokeGranteesFromAct", () => {
|
|
482
|
+
beforeEach(() => {
|
|
483
|
+
vi.clearAllMocks()
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it("should revoke grantees and rotate keys", async () => {
|
|
487
|
+
const publisher = createTestKeyPair(80)
|
|
488
|
+
const grantee1 = createTestKeyPair(81)
|
|
489
|
+
const grantee2 = createTestKeyPair(82) // Will be revoked
|
|
490
|
+
|
|
491
|
+
const originalContentRef = randomBytes(32)
|
|
492
|
+
|
|
493
|
+
// Use content-addressed storage mock
|
|
494
|
+
const { storage, uploadMock, downloadMock } =
|
|
495
|
+
createContentAddressedUploadMock()
|
|
496
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
497
|
+
|
|
498
|
+
const context = createMockContext()
|
|
499
|
+
const createResult = await createActForContent(
|
|
500
|
+
context,
|
|
501
|
+
originalContentRef,
|
|
502
|
+
publisher.privateKey,
|
|
503
|
+
[grantee1.publicKey, grantee2.publicKey],
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
// Verify original has 3 entries
|
|
507
|
+
const originalActData = loadActDataFromStorage(
|
|
508
|
+
storage,
|
|
509
|
+
createResult.actReference,
|
|
510
|
+
)
|
|
511
|
+
const originalEntries = collectActEntriesFromJson(originalActData)
|
|
512
|
+
expect(originalEntries.length).toBe(3)
|
|
513
|
+
|
|
514
|
+
// Mock download to return the uploaded blobs
|
|
515
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
516
|
+
|
|
517
|
+
// Revoke grantee2
|
|
518
|
+
const result = await revokeGranteesFromAct(
|
|
519
|
+
context,
|
|
520
|
+
createResult.historyReference,
|
|
521
|
+
createResult.encryptedReference,
|
|
522
|
+
publisher.privateKey,
|
|
523
|
+
[grantee2.publicKey],
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
// Should have new encrypted reference (key rotation)
|
|
527
|
+
expect(result.encryptedReference).not.toBe(createResult.encryptedReference)
|
|
528
|
+
expect(result.actReference).toBeDefined()
|
|
529
|
+
|
|
530
|
+
// New ACT should have 2 entries (publisher + grantee1)
|
|
531
|
+
const newActData = loadActDataFromStorage(storage, result.actReference)
|
|
532
|
+
const newEntries = collectActEntriesFromJson(newActData)
|
|
533
|
+
expect(newEntries.length).toBe(2)
|
|
534
|
+
|
|
535
|
+
// Verify grantee list has 1 grantee
|
|
536
|
+
const newGranteeListBlob = storage.get(result.granteeListReference)!
|
|
537
|
+
const grantees = decryptAndDeserializeGranteeList(
|
|
538
|
+
newGranteeListBlob,
|
|
539
|
+
publisher.privateKey,
|
|
540
|
+
)
|
|
541
|
+
expect(grantees.length).toBe(1)
|
|
542
|
+
|
|
543
|
+
// Grantee2 should NOT be able to find their entry in new ACT
|
|
544
|
+
const grantee2Keys = deriveKeys(
|
|
545
|
+
grantee2.privateKey,
|
|
546
|
+
publisher.publicKey.x,
|
|
547
|
+
publisher.publicKey.y,
|
|
548
|
+
)
|
|
549
|
+
const grantee2Entry = findActEntryByKey(newActData, grantee2Keys.lookupKey)
|
|
550
|
+
expect(grantee2Entry).toBeUndefined()
|
|
551
|
+
|
|
552
|
+
// Grantee1 should still be able to find their entry
|
|
553
|
+
const grantee1Keys = deriveKeys(
|
|
554
|
+
grantee1.privateKey,
|
|
555
|
+
publisher.publicKey.x,
|
|
556
|
+
publisher.publicKey.y,
|
|
557
|
+
)
|
|
558
|
+
const grantee1Entry = findActEntryByKey(newActData, grantee1Keys.lookupKey)
|
|
559
|
+
expect(grantee1Entry).toBeDefined()
|
|
560
|
+
})
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
describe("getGranteesFromAct", () => {
|
|
564
|
+
beforeEach(() => {
|
|
565
|
+
vi.clearAllMocks()
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
it("should return list of grantees as compressed hex strings", async () => {
|
|
569
|
+
const bee = {} as Bee
|
|
570
|
+
const publisher = createTestKeyPair(90)
|
|
571
|
+
const grantee1 = createTestKeyPair(91)
|
|
572
|
+
const grantee2 = createTestKeyPair(92)
|
|
573
|
+
|
|
574
|
+
// Use content-addressed storage mock
|
|
575
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
576
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
577
|
+
|
|
578
|
+
const context = createMockContext()
|
|
579
|
+
const createResult = await createActForContent(
|
|
580
|
+
context,
|
|
581
|
+
randomBytes(32),
|
|
582
|
+
publisher.privateKey,
|
|
583
|
+
[grantee1.publicKey, grantee2.publicKey],
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
// Mock download to return the uploaded blobs
|
|
587
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
588
|
+
|
|
589
|
+
// Get grantees
|
|
590
|
+
const grantees = await getGranteesFromAct(
|
|
591
|
+
bee,
|
|
592
|
+
createResult.historyReference,
|
|
593
|
+
publisher.privateKey,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
expect(grantees.length).toBe(2)
|
|
597
|
+
expect(grantees).toContain(grantee1.compressedPublicKey)
|
|
598
|
+
expect(grantees).toContain(grantee2.compressedPublicKey)
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
it("should return empty array for ACT with no grantees", async () => {
|
|
602
|
+
const bee = {} as Bee
|
|
603
|
+
const publisher = createTestKeyPair(100)
|
|
604
|
+
|
|
605
|
+
// Use content-addressed storage mock
|
|
606
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
607
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
608
|
+
|
|
609
|
+
const context = createMockContext()
|
|
610
|
+
const createResult = await createActForContent(
|
|
611
|
+
context,
|
|
612
|
+
randomBytes(32),
|
|
613
|
+
publisher.privateKey,
|
|
614
|
+
[],
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
618
|
+
|
|
619
|
+
const grantees = await getGranteesFromAct(
|
|
620
|
+
bee,
|
|
621
|
+
createResult.historyReference,
|
|
622
|
+
publisher.privateKey,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
expect(grantees.length).toBe(0)
|
|
626
|
+
})
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
describe("ACT end-to-end flow", () => {
|
|
630
|
+
beforeEach(() => {
|
|
631
|
+
vi.clearAllMocks()
|
|
632
|
+
})
|
|
633
|
+
|
|
634
|
+
it("should support full upload/download/manage lifecycle", async () => {
|
|
635
|
+
const bee = {} as Bee
|
|
636
|
+
const context = createMockContext()
|
|
637
|
+
|
|
638
|
+
// Setup participants
|
|
639
|
+
const publisher = createTestKeyPair(110)
|
|
640
|
+
const alice = createTestKeyPair(111)
|
|
641
|
+
const bob = createTestKeyPair(112)
|
|
642
|
+
const charlie = createTestKeyPair(113) // Will be added later
|
|
643
|
+
const eve = createTestKeyPair(114) // Unauthorized
|
|
644
|
+
|
|
645
|
+
const secretData = new TextEncoder().encode("Top secret message!")
|
|
646
|
+
|
|
647
|
+
// Use content-addressed storage mock
|
|
648
|
+
const { uploadMock, downloadMock } = createContentAddressedUploadMock()
|
|
649
|
+
vi.mocked(uploadEncryptedDataWithSigning).mockImplementation(uploadMock)
|
|
650
|
+
|
|
651
|
+
// Step 1: Publisher creates ACT with Alice and Bob
|
|
652
|
+
const createResult = await createActForContent(
|
|
653
|
+
context,
|
|
654
|
+
secretData,
|
|
655
|
+
publisher.privateKey,
|
|
656
|
+
[alice.publicKey, bob.publicKey],
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
// Step 2: Verify Alice can decrypt
|
|
660
|
+
vi.mocked(downloadDataWithChunkAPI).mockImplementation(downloadMock)
|
|
661
|
+
|
|
662
|
+
const aliceDecrypted = await decryptActReference(
|
|
663
|
+
bee,
|
|
664
|
+
createResult.encryptedReference,
|
|
665
|
+
createResult.historyReference,
|
|
666
|
+
createResult.publisherPubKey,
|
|
667
|
+
alice.privateKey,
|
|
668
|
+
)
|
|
669
|
+
expect(fromHex(aliceDecrypted).slice(0, secretData.length)).toEqual(
|
|
670
|
+
secretData,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
// Step 3: Verify Eve cannot decrypt
|
|
674
|
+
await expect(
|
|
675
|
+
decryptActReference(
|
|
676
|
+
bee,
|
|
677
|
+
createResult.encryptedReference,
|
|
678
|
+
createResult.historyReference,
|
|
679
|
+
createResult.publisherPubKey,
|
|
680
|
+
eve.privateKey,
|
|
681
|
+
),
|
|
682
|
+
).rejects.toThrow("Access denied")
|
|
683
|
+
|
|
684
|
+
// Step 4: Publisher adds Charlie
|
|
685
|
+
const addResult = await addGranteesToAct(
|
|
686
|
+
context,
|
|
687
|
+
createResult.historyReference,
|
|
688
|
+
publisher.privateKey,
|
|
689
|
+
[charlie.publicKey],
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
// Charlie should now be able to decrypt (using same encrypted ref)
|
|
693
|
+
const charlieDecrypted = await decryptActReference(
|
|
694
|
+
bee,
|
|
695
|
+
createResult.encryptedReference,
|
|
696
|
+
addResult.historyReference,
|
|
697
|
+
createResult.publisherPubKey,
|
|
698
|
+
charlie.privateKey,
|
|
699
|
+
)
|
|
700
|
+
expect(fromHex(charlieDecrypted).slice(0, secretData.length)).toEqual(
|
|
701
|
+
secretData,
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
// Step 5: Publisher revokes Bob
|
|
705
|
+
const revokeResult = await revokeGranteesFromAct(
|
|
706
|
+
context,
|
|
707
|
+
addResult.historyReference,
|
|
708
|
+
createResult.encryptedReference,
|
|
709
|
+
publisher.privateKey,
|
|
710
|
+
[bob.publicKey],
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
// Bob should NOT be able to decrypt the new ACT
|
|
714
|
+
await expect(
|
|
715
|
+
decryptActReference(
|
|
716
|
+
bee,
|
|
717
|
+
revokeResult.encryptedReference,
|
|
718
|
+
revokeResult.historyReference,
|
|
719
|
+
createResult.publisherPubKey,
|
|
720
|
+
bob.privateKey,
|
|
721
|
+
),
|
|
722
|
+
).rejects.toThrow("Access denied")
|
|
723
|
+
|
|
724
|
+
// Alice and Charlie should still be able to decrypt
|
|
725
|
+
const aliceStillDecrypted = await decryptActReference(
|
|
726
|
+
bee,
|
|
727
|
+
revokeResult.encryptedReference,
|
|
728
|
+
revokeResult.historyReference,
|
|
729
|
+
createResult.publisherPubKey,
|
|
730
|
+
alice.privateKey,
|
|
731
|
+
)
|
|
732
|
+
expect(fromHex(aliceStillDecrypted).slice(0, secretData.length)).toEqual(
|
|
733
|
+
secretData,
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
// Step 6: Verify grantee list
|
|
737
|
+
const finalGrantees = await getGranteesFromAct(
|
|
738
|
+
bee,
|
|
739
|
+
revokeResult.historyReference,
|
|
740
|
+
publisher.privateKey,
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
expect(finalGrantees.length).toBe(2)
|
|
744
|
+
expect(finalGrantees).toContain(alice.compressedPublicKey)
|
|
745
|
+
expect(finalGrantees).toContain(charlie.compressedPublicKey)
|
|
746
|
+
expect(finalGrantees).not.toContain(bob.compressedPublicKey)
|
|
747
|
+
})
|
|
748
|
+
})
|