@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,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for content-addressed chunk (CAC) creation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest"
|
|
6
|
+
import { Span } from "@ethersphere/bee-js"
|
|
7
|
+
import { makeContentAddressedChunk } from "./cac"
|
|
8
|
+
import { calculateChunkAddress } from "./bmt"
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// makeContentAddressedChunk Tests
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
describe("makeContentAddressedChunk", () => {
|
|
15
|
+
describe("chunk structure", () => {
|
|
16
|
+
it("should create chunk with correct structure (data = span + payload)", () => {
|
|
17
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5])
|
|
18
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
19
|
+
|
|
20
|
+
// data should be span (8 bytes) + payload
|
|
21
|
+
expect(chunk.data.length).toBe(8 + payload.length)
|
|
22
|
+
expect(chunk.data.slice(8)).toEqual(payload)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("should set span reflecting payload length", () => {
|
|
26
|
+
const payload = new Uint8Array([10, 20, 30])
|
|
27
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
28
|
+
|
|
29
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(payload.length))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("should calculate address matching calculateChunkAddress result", () => {
|
|
33
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
|
|
34
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
35
|
+
|
|
36
|
+
const expectedAddress = calculateChunkAddress(chunk.data)
|
|
37
|
+
|
|
38
|
+
expect(chunk.address.toUint8Array()).toEqual(
|
|
39
|
+
expectedAddress.toUint8Array(),
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe("string input handling", () => {
|
|
45
|
+
it("should accept string input and encode as UTF-8", () => {
|
|
46
|
+
const text = "Hello, World!"
|
|
47
|
+
const chunk = makeContentAddressedChunk(text)
|
|
48
|
+
|
|
49
|
+
const encoder = new TextEncoder()
|
|
50
|
+
const expectedPayload = encoder.encode(text)
|
|
51
|
+
|
|
52
|
+
expect(chunk.payload.toUint8Array()).toEqual(expectedPayload)
|
|
53
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(expectedPayload.length))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("should handle Unicode strings", () => {
|
|
57
|
+
const text = "Hello, 世界! 🌍"
|
|
58
|
+
const chunk = makeContentAddressedChunk(text)
|
|
59
|
+
|
|
60
|
+
const encoder = new TextEncoder()
|
|
61
|
+
const expectedPayload = encoder.encode(text)
|
|
62
|
+
|
|
63
|
+
expect(chunk.payload.toUint8Array()).toEqual(expectedPayload)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it("should handle empty-like strings (single char)", () => {
|
|
67
|
+
const text = "a"
|
|
68
|
+
const chunk = makeContentAddressedChunk(text)
|
|
69
|
+
|
|
70
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(1))
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe("spanOverride parameter", () => {
|
|
75
|
+
it("should use Span override when provided", () => {
|
|
76
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
77
|
+
const customSpan = Span.fromBigInt(BigInt(1000))
|
|
78
|
+
const chunk = makeContentAddressedChunk(payload, customSpan)
|
|
79
|
+
|
|
80
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(1000))
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it("should use bigint override when provided", () => {
|
|
84
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
85
|
+
const chunk = makeContentAddressedChunk(payload, BigInt(2000))
|
|
86
|
+
|
|
87
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(2000))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it("should produce different address with different spanOverride", () => {
|
|
91
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
92
|
+
|
|
93
|
+
const chunk1 = makeContentAddressedChunk(payload)
|
|
94
|
+
const chunk2 = makeContentAddressedChunk(payload, BigInt(100))
|
|
95
|
+
|
|
96
|
+
expect(chunk1.address.toUint8Array()).not.toEqual(
|
|
97
|
+
chunk2.address.toUint8Array(),
|
|
98
|
+
)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe("error handling", () => {
|
|
103
|
+
it("should throw error on empty payload", () => {
|
|
104
|
+
const emptyPayload = new Uint8Array(0)
|
|
105
|
+
|
|
106
|
+
expect(() => makeContentAddressedChunk(emptyPayload)).toThrow(
|
|
107
|
+
/payload size .* exceeds limits/,
|
|
108
|
+
)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it("should throw error on empty string", () => {
|
|
112
|
+
expect(() => makeContentAddressedChunk("")).toThrow(
|
|
113
|
+
/payload size .* exceeds limits/,
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("should throw error on payload larger than 4096 bytes", () => {
|
|
118
|
+
const largePayload = new Uint8Array(4097)
|
|
119
|
+
|
|
120
|
+
expect(() => makeContentAddressedChunk(largePayload)).toThrow(
|
|
121
|
+
/payload size .* exceeds limits/,
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe("determinism", () => {
|
|
127
|
+
it("should produce same chunk for same payload", () => {
|
|
128
|
+
const payload = new Uint8Array([5, 10, 15, 20, 25])
|
|
129
|
+
|
|
130
|
+
const chunk1 = makeContentAddressedChunk(payload)
|
|
131
|
+
const chunk2 = makeContentAddressedChunk(payload)
|
|
132
|
+
|
|
133
|
+
expect(chunk1.data).toEqual(chunk2.data)
|
|
134
|
+
expect(chunk1.address.toUint8Array()).toEqual(
|
|
135
|
+
chunk2.address.toUint8Array(),
|
|
136
|
+
)
|
|
137
|
+
expect(chunk1.span.toBigInt()).toBe(chunk2.span.toBigInt())
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it("should produce same chunk for same string", () => {
|
|
141
|
+
const text = "Test string for determinism"
|
|
142
|
+
|
|
143
|
+
const chunk1 = makeContentAddressedChunk(text)
|
|
144
|
+
const chunk2 = makeContentAddressedChunk(text)
|
|
145
|
+
|
|
146
|
+
expect(chunk1.data).toEqual(chunk2.data)
|
|
147
|
+
expect(chunk1.address.toUint8Array()).toEqual(
|
|
148
|
+
chunk2.address.toUint8Array(),
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe("payload size variations", () => {
|
|
154
|
+
it("should handle minimum payload size (1 byte)", () => {
|
|
155
|
+
const payload = new Uint8Array([42])
|
|
156
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
157
|
+
|
|
158
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(1))
|
|
159
|
+
expect(chunk.data.length).toBe(9) // 8 span + 1 payload
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("should handle maximum payload size (4096 bytes)", () => {
|
|
163
|
+
const payload = new Uint8Array(4096)
|
|
164
|
+
for (let i = 0; i < 4096; i++) {
|
|
165
|
+
payload[i] = i % 256
|
|
166
|
+
}
|
|
167
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
168
|
+
|
|
169
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(4096))
|
|
170
|
+
expect(chunk.data.length).toBe(8 + 4096)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it("should handle power-of-two sizes correctly", () => {
|
|
174
|
+
const sizes = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
|
|
175
|
+
|
|
176
|
+
for (const size of sizes) {
|
|
177
|
+
const payload = new Uint8Array(size)
|
|
178
|
+
payload.fill(42)
|
|
179
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
180
|
+
|
|
181
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(size))
|
|
182
|
+
expect(chunk.address.toUint8Array().length).toBe(32)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe("edge cases", () => {
|
|
188
|
+
it("should handle payload with all zeros", () => {
|
|
189
|
+
const payload = new Uint8Array(100)
|
|
190
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
191
|
+
|
|
192
|
+
expect(chunk.address.toUint8Array().length).toBe(32)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it("should handle payload with all 0xFF", () => {
|
|
196
|
+
const payload = new Uint8Array(100).fill(0xff)
|
|
197
|
+
const chunk = makeContentAddressedChunk(payload)
|
|
198
|
+
|
|
199
|
+
expect(chunk.address.toUint8Array().length).toBe(32)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it("should produce different chunks for different payloads", () => {
|
|
203
|
+
const payload1 = new Uint8Array([1, 2, 3])
|
|
204
|
+
const payload2 = new Uint8Array([4, 5, 6])
|
|
205
|
+
|
|
206
|
+
const chunk1 = makeContentAddressedChunk(payload1)
|
|
207
|
+
const chunk2 = makeContentAddressedChunk(payload2)
|
|
208
|
+
|
|
209
|
+
expect(chunk1.address.toUint8Array()).not.toEqual(
|
|
210
|
+
chunk2.address.toUint8Array(),
|
|
211
|
+
)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
})
|
package/src/chunk/cac.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Content-addressed chunk (CAC) utilities
|
|
2
|
+
|
|
3
|
+
import { Binary } from "cafe-utility"
|
|
4
|
+
import { Bytes, Reference, Span } from "@ethersphere/bee-js"
|
|
5
|
+
import { calculateChunkAddress } from "./bmt"
|
|
6
|
+
import { MAX_PAYLOAD_SIZE, MIN_PAYLOAD_SIZE } from "./constants"
|
|
7
|
+
|
|
8
|
+
const ENCODER = new TextEncoder()
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Content addressed chunk interface
|
|
12
|
+
*/
|
|
13
|
+
export interface ContentAddressedChunk {
|
|
14
|
+
readonly data: Uint8Array // span + payload
|
|
15
|
+
readonly span: Span
|
|
16
|
+
readonly payload: Bytes
|
|
17
|
+
readonly address: Reference // BMT hash
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a content addressed chunk from payload data
|
|
22
|
+
*
|
|
23
|
+
* @param payloadBytes the data to be stored in the chunk (1-4096 bytes)
|
|
24
|
+
* @param spanOverride optional span value to use instead of payload length
|
|
25
|
+
*/
|
|
26
|
+
export function makeContentAddressedChunk(
|
|
27
|
+
payloadBytes: Uint8Array | string,
|
|
28
|
+
spanOverride?: Span | bigint,
|
|
29
|
+
): ContentAddressedChunk {
|
|
30
|
+
if (!(payloadBytes instanceof Uint8Array)) {
|
|
31
|
+
payloadBytes = ENCODER.encode(payloadBytes)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
payloadBytes.length < MIN_PAYLOAD_SIZE ||
|
|
36
|
+
payloadBytes.length > MAX_PAYLOAD_SIZE
|
|
37
|
+
) {
|
|
38
|
+
throw new RangeError(
|
|
39
|
+
`payload size ${payloadBytes.length} exceeds limits [${MIN_PAYLOAD_SIZE}, ${MAX_PAYLOAD_SIZE}]`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Determine span value
|
|
44
|
+
let span: Span
|
|
45
|
+
if (spanOverride instanceof Span) {
|
|
46
|
+
span = spanOverride
|
|
47
|
+
} else if (typeof spanOverride === "bigint") {
|
|
48
|
+
span = Span.fromBigInt(spanOverride)
|
|
49
|
+
} else {
|
|
50
|
+
span = Span.fromBigInt(BigInt(payloadBytes.length))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create chunk data (span + payload)
|
|
54
|
+
const chunkData = Binary.concatBytes(span.toUint8Array(), payloadBytes)
|
|
55
|
+
|
|
56
|
+
// Calculate BMT address
|
|
57
|
+
const address = calculateChunkAddress(chunkData)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
data: chunkData,
|
|
61
|
+
span,
|
|
62
|
+
payload: new Bytes(payloadBytes),
|
|
63
|
+
address,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Chunk size constants
|
|
2
|
+
export const MIN_PAYLOAD_SIZE = 1
|
|
3
|
+
export const MAX_PAYLOAD_SIZE = 4096
|
|
4
|
+
|
|
5
|
+
// Span size (8 bytes for uint64 little-endian)
|
|
6
|
+
export const SPAN_SIZE = 8
|
|
7
|
+
|
|
8
|
+
// Reference sizes
|
|
9
|
+
export const UNENCRYPTED_REF_SIZE = 32
|
|
10
|
+
export const ENCRYPTED_REF_SIZE = 64
|
|
11
|
+
|
|
12
|
+
// SOC (Single Owner Chunk) constants
|
|
13
|
+
export const IDENTIFIER_SIZE = 32
|
|
14
|
+
export const SIGNATURE_SIZE = 65
|
|
15
|
+
export const SOC_HEADER_SIZE = IDENTIFIER_SIZE + SIGNATURE_SIZE
|
|
16
|
+
|
|
17
|
+
// Download concurrency
|
|
18
|
+
export const DEFAULT_DOWNLOAD_CONCURRENCY = 64
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for encrypted content-addressed chunk (CAC) creation and decryption
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest"
|
|
6
|
+
import { Reference } from "@ethersphere/bee-js"
|
|
7
|
+
import {
|
|
8
|
+
makeEncryptedContentAddressedChunk,
|
|
9
|
+
decryptEncryptedChunk,
|
|
10
|
+
extractEncryptionKey,
|
|
11
|
+
extractChunkAddress,
|
|
12
|
+
} from "./encrypted-cac"
|
|
13
|
+
import { generateRandomKey } from "./encryption"
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// makeEncryptedContentAddressedChunk Tests
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
describe("makeEncryptedContentAddressedChunk", () => {
|
|
20
|
+
describe("chunk structure", () => {
|
|
21
|
+
it("should create encrypted chunk with correct structure", () => {
|
|
22
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5])
|
|
23
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
24
|
+
|
|
25
|
+
// data should be encrypted span (8 bytes) + encrypted data (4096 bytes)
|
|
26
|
+
expect(chunk.data.length).toBe(4104)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("should produce encrypted data of 4104 bytes (8 span + 4096 data)", () => {
|
|
30
|
+
const payload = new Uint8Array([10, 20, 30])
|
|
31
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
32
|
+
|
|
33
|
+
expect(chunk.data.length).toBe(4104)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it("should produce reference of 64 bytes (address + key)", () => {
|
|
37
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
38
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
39
|
+
|
|
40
|
+
expect(chunk.reference.toUint8Array().length).toBe(64)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("should include 32-byte encryption key", () => {
|
|
44
|
+
const payload = new Uint8Array([5, 6, 7, 8])
|
|
45
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
46
|
+
|
|
47
|
+
expect(chunk.encryptionKey.length).toBe(32)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("should include 32-byte address", () => {
|
|
51
|
+
const payload = new Uint8Array([9, 10, 11, 12])
|
|
52
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
53
|
+
|
|
54
|
+
expect(chunk.address.toUint8Array().length).toBe(32)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe("encryption key handling", () => {
|
|
59
|
+
it("should use custom encryption key when provided", () => {
|
|
60
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
61
|
+
const customKey = generateRandomKey()
|
|
62
|
+
const chunk = makeEncryptedContentAddressedChunk(payload, customKey)
|
|
63
|
+
|
|
64
|
+
expect(chunk.encryptionKey).toBe(customKey)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("should generate random key when not provided", () => {
|
|
68
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
69
|
+
|
|
70
|
+
const chunk1 = makeEncryptedContentAddressedChunk(payload)
|
|
71
|
+
const chunk2 = makeEncryptedContentAddressedChunk(payload)
|
|
72
|
+
|
|
73
|
+
expect(chunk1.encryptionKey).not.toEqual(chunk2.encryptionKey)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("should produce same encrypted data with same key", () => {
|
|
77
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5])
|
|
78
|
+
const key = generateRandomKey()
|
|
79
|
+
|
|
80
|
+
const chunk1 = makeEncryptedContentAddressedChunk(payload, key)
|
|
81
|
+
const chunk2 = makeEncryptedContentAddressedChunk(payload, key)
|
|
82
|
+
|
|
83
|
+
expect(chunk1.data).toEqual(chunk2.data)
|
|
84
|
+
expect(chunk1.address.toUint8Array()).toEqual(
|
|
85
|
+
chunk2.address.toUint8Array(),
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("should produce different encrypted data with different keys", () => {
|
|
90
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5])
|
|
91
|
+
const key1 = generateRandomKey()
|
|
92
|
+
const key2 = generateRandomKey()
|
|
93
|
+
|
|
94
|
+
const chunk1 = makeEncryptedContentAddressedChunk(payload, key1)
|
|
95
|
+
const chunk2 = makeEncryptedContentAddressedChunk(payload, key2)
|
|
96
|
+
|
|
97
|
+
expect(chunk1.data).not.toEqual(chunk2.data)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe("string input handling", () => {
|
|
102
|
+
it("should accept string input and encode as UTF-8", () => {
|
|
103
|
+
const text = "Hello, Encrypted World!"
|
|
104
|
+
const chunk = makeEncryptedContentAddressedChunk(text)
|
|
105
|
+
|
|
106
|
+
expect(chunk.data.length).toBe(4104)
|
|
107
|
+
expect(chunk.reference.toUint8Array().length).toBe(64)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("should handle Unicode strings", () => {
|
|
111
|
+
const text = "Encrypted 世界! 🔐"
|
|
112
|
+
const chunk = makeEncryptedContentAddressedChunk(text)
|
|
113
|
+
|
|
114
|
+
expect(chunk.data.length).toBe(4104)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe("error handling", () => {
|
|
119
|
+
it("should throw error on empty payload", () => {
|
|
120
|
+
const emptyPayload = new Uint8Array(0)
|
|
121
|
+
|
|
122
|
+
expect(() => makeEncryptedContentAddressedChunk(emptyPayload)).toThrow(
|
|
123
|
+
/payload size .* exceeds limits/,
|
|
124
|
+
)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it("should throw error on empty string", () => {
|
|
128
|
+
expect(() => makeEncryptedContentAddressedChunk("")).toThrow(
|
|
129
|
+
/payload size .* exceeds limits/,
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("should throw error on payload larger than 4096 bytes", () => {
|
|
134
|
+
const largePayload = new Uint8Array(4097)
|
|
135
|
+
|
|
136
|
+
expect(() => makeEncryptedContentAddressedChunk(largePayload)).toThrow(
|
|
137
|
+
/payload size .* exceeds limits/,
|
|
138
|
+
)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe("payload size variations", () => {
|
|
143
|
+
it("should handle minimum payload size (1 byte)", () => {
|
|
144
|
+
const payload = new Uint8Array([42])
|
|
145
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
146
|
+
|
|
147
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(1))
|
|
148
|
+
expect(chunk.data.length).toBe(4104)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("should handle maximum payload size (4096 bytes)", () => {
|
|
152
|
+
const payload = new Uint8Array(4096)
|
|
153
|
+
for (let i = 0; i < 4096; i++) {
|
|
154
|
+
payload[i] = i % 256
|
|
155
|
+
}
|
|
156
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
157
|
+
|
|
158
|
+
expect(chunk.span.toBigInt()).toBe(BigInt(4096))
|
|
159
|
+
expect(chunk.data.length).toBe(4104)
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// decryptEncryptedChunk Tests
|
|
166
|
+
// ============================================================================
|
|
167
|
+
|
|
168
|
+
describe("decryptEncryptedChunk", () => {
|
|
169
|
+
describe("round-trip encryption/decryption", () => {
|
|
170
|
+
it("should recover original data after encrypt then decrypt", () => {
|
|
171
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
|
|
172
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
173
|
+
|
|
174
|
+
const decrypted = decryptEncryptedChunk(chunk.data, chunk.encryptionKey)
|
|
175
|
+
|
|
176
|
+
// First 8 bytes are span, rest is payload (padded to 4096)
|
|
177
|
+
const decryptedPayload = decrypted.slice(8, 8 + payload.length)
|
|
178
|
+
expect(decryptedPayload).toEqual(payload)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it("should preserve original span value after decryption", () => {
|
|
182
|
+
const payload = new Uint8Array([10, 20, 30, 40, 50])
|
|
183
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
184
|
+
|
|
185
|
+
const decrypted = decryptEncryptedChunk(chunk.data, chunk.encryptionKey)
|
|
186
|
+
|
|
187
|
+
// Extract span from decrypted data (first 8 bytes, little-endian)
|
|
188
|
+
const decryptedSpanView = new DataView(decrypted.buffer, 0, 8)
|
|
189
|
+
const decryptedSpan = decryptedSpanView.getBigUint64(0, true)
|
|
190
|
+
|
|
191
|
+
expect(decryptedSpan).toBe(BigInt(payload.length))
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it("should round-trip string content", () => {
|
|
195
|
+
const text = "Hello, World! This is encrypted."
|
|
196
|
+
const encoder = new TextEncoder()
|
|
197
|
+
const expectedPayload = encoder.encode(text)
|
|
198
|
+
|
|
199
|
+
const chunk = makeEncryptedContentAddressedChunk(text)
|
|
200
|
+
const decrypted = decryptEncryptedChunk(chunk.data, chunk.encryptionKey)
|
|
201
|
+
|
|
202
|
+
const decryptedPayload = decrypted.slice(8, 8 + expectedPayload.length)
|
|
203
|
+
expect(decryptedPayload).toEqual(expectedPayload)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it("should round-trip maximum size payload", () => {
|
|
207
|
+
const payload = new Uint8Array(4096)
|
|
208
|
+
for (let i = 0; i < 4096; i++) {
|
|
209
|
+
payload[i] = i % 256
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
213
|
+
const decrypted = decryptEncryptedChunk(chunk.data, chunk.encryptionKey)
|
|
214
|
+
|
|
215
|
+
// For max size, decrypted should match exactly (no extra padding)
|
|
216
|
+
const decryptedPayload = decrypted.slice(8)
|
|
217
|
+
expect(decryptedPayload).toEqual(payload)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it("should round-trip various payload sizes", () => {
|
|
221
|
+
const sizes = [1, 10, 32, 64, 100, 256, 512, 1024, 2048, 4096]
|
|
222
|
+
|
|
223
|
+
for (const size of sizes) {
|
|
224
|
+
const payload = new Uint8Array(size)
|
|
225
|
+
for (let i = 0; i < size; i++) {
|
|
226
|
+
payload[i] = i % 256
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
230
|
+
const decrypted = decryptEncryptedChunk(chunk.data, chunk.encryptionKey)
|
|
231
|
+
|
|
232
|
+
const decryptedPayload = decrypted.slice(8, 8 + size)
|
|
233
|
+
expect(decryptedPayload).toEqual(payload)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
describe("key sensitivity", () => {
|
|
239
|
+
it("should fail to decrypt with wrong key", () => {
|
|
240
|
+
const payload = new Uint8Array([1, 2, 3, 4, 5])
|
|
241
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
242
|
+
const wrongKey = generateRandomKey()
|
|
243
|
+
|
|
244
|
+
const decrypted = decryptEncryptedChunk(chunk.data, wrongKey)
|
|
245
|
+
|
|
246
|
+
// Decryption doesn't throw but produces wrong data
|
|
247
|
+
const decryptedPayload = decrypted.slice(8, 8 + payload.length)
|
|
248
|
+
expect(decryptedPayload).not.toEqual(payload)
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// extractEncryptionKey Tests
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
describe("extractEncryptionKey", () => {
|
|
258
|
+
it("should return correct 32-byte key from 64-byte reference", () => {
|
|
259
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
260
|
+
const customKey = generateRandomKey()
|
|
261
|
+
const chunk = makeEncryptedContentAddressedChunk(payload, customKey)
|
|
262
|
+
|
|
263
|
+
const extractedKey = extractEncryptionKey(chunk.reference)
|
|
264
|
+
|
|
265
|
+
expect(extractedKey).toEqual(customKey)
|
|
266
|
+
expect(extractedKey.length).toBe(32)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it("should extract key that can decrypt the chunk", () => {
|
|
270
|
+
const payload = new Uint8Array([5, 10, 15, 20])
|
|
271
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
272
|
+
|
|
273
|
+
const extractedKey = extractEncryptionKey(chunk.reference)
|
|
274
|
+
const decrypted = decryptEncryptedChunk(chunk.data, extractedKey)
|
|
275
|
+
|
|
276
|
+
const decryptedPayload = decrypted.slice(8, 8 + payload.length)
|
|
277
|
+
expect(decryptedPayload).toEqual(payload)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it("should throw error on non-64-byte reference", () => {
|
|
281
|
+
// 32-byte reference (non-encrypted)
|
|
282
|
+
const shortRef = new Reference(new Uint8Array(32))
|
|
283
|
+
expect(() => extractEncryptionKey(shortRef)).toThrow(
|
|
284
|
+
/Invalid encrypted reference length/,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
// Note: Reference constructor validates that bytes are 32 or 64, so we can't test
|
|
288
|
+
// with lengths like 128. The 32-byte case is sufficient to test our validation.
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// extractChunkAddress Tests
|
|
294
|
+
// ============================================================================
|
|
295
|
+
|
|
296
|
+
describe("extractChunkAddress", () => {
|
|
297
|
+
it("should return correct 32-byte address from 64-byte reference", () => {
|
|
298
|
+
const payload = new Uint8Array([1, 2, 3, 4])
|
|
299
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
300
|
+
|
|
301
|
+
const extractedAddress = extractChunkAddress(chunk.reference)
|
|
302
|
+
|
|
303
|
+
expect(extractedAddress.toUint8Array()).toEqual(
|
|
304
|
+
chunk.address.toUint8Array(),
|
|
305
|
+
)
|
|
306
|
+
expect(extractedAddress.toUint8Array().length).toBe(32)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it("should extract address that matches chunk address", () => {
|
|
310
|
+
const payload = new Uint8Array([100, 200, 50, 25])
|
|
311
|
+
const key = generateRandomKey()
|
|
312
|
+
const chunk = makeEncryptedContentAddressedChunk(payload, key)
|
|
313
|
+
|
|
314
|
+
const extractedAddress = extractChunkAddress(chunk.reference)
|
|
315
|
+
|
|
316
|
+
expect(extractedAddress.toUint8Array()).toEqual(
|
|
317
|
+
chunk.address.toUint8Array(),
|
|
318
|
+
)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
it("should throw error on non-64-byte reference", () => {
|
|
322
|
+
// 32-byte reference (non-encrypted)
|
|
323
|
+
const shortRef = new Reference(new Uint8Array(32))
|
|
324
|
+
expect(() => extractChunkAddress(shortRef)).toThrow(
|
|
325
|
+
/Invalid encrypted reference length/,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
// Note: Reference constructor validates that bytes are 32 or 64, so we can't test
|
|
329
|
+
// with lengths like 128. The 32-byte case is sufficient to test our validation.
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it("should extract address that differs from encryption key", () => {
|
|
333
|
+
const payload = new Uint8Array([1, 2, 3])
|
|
334
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
335
|
+
|
|
336
|
+
const address = extractChunkAddress(chunk.reference)
|
|
337
|
+
const key = extractEncryptionKey(chunk.reference)
|
|
338
|
+
|
|
339
|
+
expect(address.toUint8Array()).not.toEqual(key)
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// Integration Tests
|
|
345
|
+
// ============================================================================
|
|
346
|
+
|
|
347
|
+
describe("encrypted-cac integration", () => {
|
|
348
|
+
it("should support complete workflow: create → extract → decrypt", () => {
|
|
349
|
+
const originalPayload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
350
|
+
|
|
351
|
+
// Create encrypted chunk
|
|
352
|
+
const chunk = makeEncryptedContentAddressedChunk(originalPayload)
|
|
353
|
+
|
|
354
|
+
// Extract address and key from reference
|
|
355
|
+
const address = extractChunkAddress(chunk.reference)
|
|
356
|
+
const key = extractEncryptionKey(chunk.reference)
|
|
357
|
+
|
|
358
|
+
// Verify address matches
|
|
359
|
+
expect(address.toUint8Array()).toEqual(chunk.address.toUint8Array())
|
|
360
|
+
|
|
361
|
+
// Decrypt using extracted key
|
|
362
|
+
const decrypted = decryptEncryptedChunk(chunk.data, key)
|
|
363
|
+
const decryptedPayload = decrypted.slice(8, 8 + originalPayload.length)
|
|
364
|
+
|
|
365
|
+
expect(decryptedPayload).toEqual(originalPayload)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it("should allow reference to be serialized and deserialized", () => {
|
|
369
|
+
const payload = new Uint8Array([42, 43, 44, 45])
|
|
370
|
+
const chunk = makeEncryptedContentAddressedChunk(payload)
|
|
371
|
+
|
|
372
|
+
// Serialize reference to hex
|
|
373
|
+
const referenceHex = chunk.reference.toHex()
|
|
374
|
+
|
|
375
|
+
// Deserialize reference
|
|
376
|
+
const deserializedRef = new Reference(referenceHex)
|
|
377
|
+
|
|
378
|
+
// Extract and decrypt
|
|
379
|
+
const key = extractEncryptionKey(deserializedRef)
|
|
380
|
+
const decrypted = decryptEncryptedChunk(chunk.data, key)
|
|
381
|
+
const decryptedPayload = decrypted.slice(8, 8 + payload.length)
|
|
382
|
+
|
|
383
|
+
expect(decryptedPayload).toEqual(payload)
|
|
384
|
+
})
|
|
385
|
+
})
|