@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.
Files changed (223) hide show
  1. package/README.md +431 -0
  2. package/dist/chunk/bmt.d.ts +17 -0
  3. package/dist/chunk/bmt.d.ts.map +1 -0
  4. package/dist/chunk/cac.d.ts +18 -0
  5. package/dist/chunk/cac.d.ts.map +1 -0
  6. package/dist/chunk/constants.d.ts +10 -0
  7. package/dist/chunk/constants.d.ts.map +1 -0
  8. package/dist/chunk/encrypted-cac.d.ts +48 -0
  9. package/dist/chunk/encrypted-cac.d.ts.map +1 -0
  10. package/dist/chunk/encryption.d.ts +86 -0
  11. package/dist/chunk/encryption.d.ts.map +1 -0
  12. package/dist/chunk/index.d.ts +6 -0
  13. package/dist/chunk/index.d.ts.map +1 -0
  14. package/dist/index.d.ts +46 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/proxy/act/act.d.ts +78 -0
  17. package/dist/proxy/act/act.d.ts.map +1 -0
  18. package/dist/proxy/act/crypto.d.ts +44 -0
  19. package/dist/proxy/act/crypto.d.ts.map +1 -0
  20. package/dist/proxy/act/grantee-list.d.ts +82 -0
  21. package/dist/proxy/act/grantee-list.d.ts.map +1 -0
  22. package/dist/proxy/act/history.d.ts +183 -0
  23. package/dist/proxy/act/history.d.ts.map +1 -0
  24. package/dist/proxy/act/index.d.ts +104 -0
  25. package/dist/proxy/act/index.d.ts.map +1 -0
  26. package/dist/proxy/chunking-encrypted.d.ts +14 -0
  27. package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
  28. package/dist/proxy/chunking.d.ts +15 -0
  29. package/dist/proxy/chunking.d.ts.map +1 -0
  30. package/dist/proxy/download-data.d.ts +16 -0
  31. package/dist/proxy/download-data.d.ts.map +1 -0
  32. package/dist/proxy/feed-manifest.d.ts +62 -0
  33. package/dist/proxy/feed-manifest.d.ts.map +1 -0
  34. package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
  35. package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
  36. package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
  37. package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
  38. package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
  39. package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
  40. package/dist/proxy/feeds/epochs/index.d.ts +35 -0
  41. package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
  42. package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
  43. package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
  44. package/dist/proxy/feeds/epochs/types.d.ts +109 -0
  45. package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
  46. package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
  47. package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
  48. package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
  49. package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
  50. package/dist/proxy/feeds/index.d.ts +5 -0
  51. package/dist/proxy/feeds/index.d.ts.map +1 -0
  52. package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
  53. package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
  54. package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
  55. package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
  56. package/dist/proxy/feeds/sequence/index.d.ts +23 -0
  57. package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
  58. package/dist/proxy/feeds/sequence/types.d.ts +80 -0
  59. package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
  60. package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
  61. package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
  62. package/dist/proxy/index.d.ts +6 -0
  63. package/dist/proxy/index.d.ts.map +1 -0
  64. package/dist/proxy/manifest-builder.d.ts +183 -0
  65. package/dist/proxy/manifest-builder.d.ts.map +1 -0
  66. package/dist/proxy/mantaray-encrypted.d.ts +27 -0
  67. package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
  68. package/dist/proxy/mantaray.d.ts +26 -0
  69. package/dist/proxy/mantaray.d.ts.map +1 -0
  70. package/dist/proxy/types.d.ts +29 -0
  71. package/dist/proxy/types.d.ts.map +1 -0
  72. package/dist/proxy/upload-data.d.ts +17 -0
  73. package/dist/proxy/upload-data.d.ts.map +1 -0
  74. package/dist/proxy/upload-encrypted-data.d.ts +103 -0
  75. package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
  76. package/dist/schemas.d.ts +240 -0
  77. package/dist/schemas.d.ts.map +1 -0
  78. package/dist/storage/debounced-uploader.d.ts +62 -0
  79. package/dist/storage/debounced-uploader.d.ts.map +1 -0
  80. package/dist/storage/utilization-store.d.ts +108 -0
  81. package/dist/storage/utilization-store.d.ts.map +1 -0
  82. package/dist/swarm-id-auth.d.ts +74 -0
  83. package/dist/swarm-id-auth.d.ts.map +1 -0
  84. package/dist/swarm-id-auth.js +2 -0
  85. package/dist/swarm-id-auth.js.map +1 -0
  86. package/dist/swarm-id-client.d.ts +878 -0
  87. package/dist/swarm-id-client.d.ts.map +1 -0
  88. package/dist/swarm-id-client.js +2 -0
  89. package/dist/swarm-id-client.js.map +1 -0
  90. package/dist/swarm-id-proxy.d.ts +236 -0
  91. package/dist/swarm-id-proxy.d.ts.map +1 -0
  92. package/dist/swarm-id-proxy.js +2 -0
  93. package/dist/swarm-id-proxy.js.map +1 -0
  94. package/dist/swarm-id.esm.js +2 -0
  95. package/dist/swarm-id.esm.js.map +1 -0
  96. package/dist/swarm-id.umd.js +2 -0
  97. package/dist/swarm-id.umd.js.map +1 -0
  98. package/dist/sync/index.d.ts +9 -0
  99. package/dist/sync/index.d.ts.map +1 -0
  100. package/dist/sync/key-derivation.d.ts +25 -0
  101. package/dist/sync/key-derivation.d.ts.map +1 -0
  102. package/dist/sync/restore-account.d.ts +28 -0
  103. package/dist/sync/restore-account.d.ts.map +1 -0
  104. package/dist/sync/serialization.d.ts +16 -0
  105. package/dist/sync/serialization.d.ts.map +1 -0
  106. package/dist/sync/store-interfaces.d.ts +53 -0
  107. package/dist/sync/store-interfaces.d.ts.map +1 -0
  108. package/dist/sync/sync-account.d.ts +44 -0
  109. package/dist/sync/sync-account.d.ts.map +1 -0
  110. package/dist/sync/types.d.ts +13 -0
  111. package/dist/sync/types.d.ts.map +1 -0
  112. package/dist/test-fixtures.d.ts +17 -0
  113. package/dist/test-fixtures.d.ts.map +1 -0
  114. package/dist/types-BD_VkNn0.js +2 -0
  115. package/dist/types-BD_VkNn0.js.map +1 -0
  116. package/dist/types-lJCaT-50.js +2 -0
  117. package/dist/types-lJCaT-50.js.map +1 -0
  118. package/dist/types.d.ts +2157 -0
  119. package/dist/types.d.ts.map +1 -0
  120. package/dist/utils/account-payload.d.ts +94 -0
  121. package/dist/utils/account-payload.d.ts.map +1 -0
  122. package/dist/utils/account-state-snapshot.d.ts +38 -0
  123. package/dist/utils/account-state-snapshot.d.ts.map +1 -0
  124. package/dist/utils/backup-encryption.d.ts +127 -0
  125. package/dist/utils/backup-encryption.d.ts.map +1 -0
  126. package/dist/utils/batch-utilization.d.ts +432 -0
  127. package/dist/utils/batch-utilization.d.ts.map +1 -0
  128. package/dist/utils/constants.d.ts +11 -0
  129. package/dist/utils/constants.d.ts.map +1 -0
  130. package/dist/utils/hex.d.ts +17 -0
  131. package/dist/utils/hex.d.ts.map +1 -0
  132. package/dist/utils/key-derivation.d.ts +92 -0
  133. package/dist/utils/key-derivation.d.ts.map +1 -0
  134. package/dist/utils/storage-managers.d.ts +65 -0
  135. package/dist/utils/storage-managers.d.ts.map +1 -0
  136. package/dist/utils/swarm-id-export.d.ts +24 -0
  137. package/dist/utils/swarm-id-export.d.ts.map +1 -0
  138. package/dist/utils/ttl.d.ts +49 -0
  139. package/dist/utils/ttl.d.ts.map +1 -0
  140. package/dist/utils/url.d.ts +41 -0
  141. package/dist/utils/url.d.ts.map +1 -0
  142. package/dist/utils/versioned-storage.d.ts +131 -0
  143. package/dist/utils/versioned-storage.d.ts.map +1 -0
  144. package/package.json +78 -0
  145. package/src/chunk/bmt.test.ts +217 -0
  146. package/src/chunk/bmt.ts +57 -0
  147. package/src/chunk/cac.test.ts +214 -0
  148. package/src/chunk/cac.ts +65 -0
  149. package/src/chunk/constants.ts +18 -0
  150. package/src/chunk/encrypted-cac.test.ts +385 -0
  151. package/src/chunk/encrypted-cac.ts +131 -0
  152. package/src/chunk/encryption.test.ts +352 -0
  153. package/src/chunk/encryption.ts +300 -0
  154. package/src/chunk/index.ts +47 -0
  155. package/src/index.ts +430 -0
  156. package/src/proxy/act/act.test.ts +278 -0
  157. package/src/proxy/act/act.ts +158 -0
  158. package/src/proxy/act/bee-compat.test.ts +948 -0
  159. package/src/proxy/act/crypto.test.ts +436 -0
  160. package/src/proxy/act/crypto.ts +376 -0
  161. package/src/proxy/act/grantee-list.test.ts +393 -0
  162. package/src/proxy/act/grantee-list.ts +239 -0
  163. package/src/proxy/act/history.test.ts +360 -0
  164. package/src/proxy/act/history.ts +413 -0
  165. package/src/proxy/act/index.test.ts +748 -0
  166. package/src/proxy/act/index.ts +853 -0
  167. package/src/proxy/chunking-encrypted.ts +95 -0
  168. package/src/proxy/chunking.ts +65 -0
  169. package/src/proxy/download-data.ts +448 -0
  170. package/src/proxy/feed-manifest.ts +174 -0
  171. package/src/proxy/feeds/epochs/async-finder.ts +372 -0
  172. package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
  173. package/src/proxy/feeds/epochs/epoch.ts +181 -0
  174. package/src/proxy/feeds/epochs/finder.ts +282 -0
  175. package/src/proxy/feeds/epochs/index.ts +73 -0
  176. package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
  177. package/src/proxy/feeds/epochs/test-utils.ts +274 -0
  178. package/src/proxy/feeds/epochs/types.ts +128 -0
  179. package/src/proxy/feeds/epochs/updater.ts +192 -0
  180. package/src/proxy/feeds/epochs/utils.ts +62 -0
  181. package/src/proxy/feeds/index.ts +5 -0
  182. package/src/proxy/feeds/sequence/async-finder.ts +31 -0
  183. package/src/proxy/feeds/sequence/finder.ts +73 -0
  184. package/src/proxy/feeds/sequence/index.ts +54 -0
  185. package/src/proxy/feeds/sequence/integration.test.ts +966 -0
  186. package/src/proxy/feeds/sequence/types.ts +103 -0
  187. package/src/proxy/feeds/sequence/updater.ts +71 -0
  188. package/src/proxy/index.ts +5 -0
  189. package/src/proxy/manifest-builder.test.ts +427 -0
  190. package/src/proxy/manifest-builder.ts +679 -0
  191. package/src/proxy/mantaray-encrypted.ts +78 -0
  192. package/src/proxy/mantaray.ts +104 -0
  193. package/src/proxy/types.ts +32 -0
  194. package/src/proxy/upload-data.ts +189 -0
  195. package/src/proxy/upload-encrypted-data.ts +658 -0
  196. package/src/schemas.ts +299 -0
  197. package/src/storage/debounced-uploader.ts +192 -0
  198. package/src/storage/utilization-store.ts +397 -0
  199. package/src/swarm-id-client.test.ts +99 -0
  200. package/src/swarm-id-client.ts +3095 -0
  201. package/src/swarm-id-proxy.ts +3891 -0
  202. package/src/sync/index.ts +28 -0
  203. package/src/sync/restore-account.ts +90 -0
  204. package/src/sync/serialization.ts +39 -0
  205. package/src/sync/store-interfaces.ts +62 -0
  206. package/src/sync/sync-account.test.ts +302 -0
  207. package/src/sync/sync-account.ts +396 -0
  208. package/src/sync/types.ts +11 -0
  209. package/src/test-fixtures.ts +109 -0
  210. package/src/types.ts +1651 -0
  211. package/src/utils/account-state-snapshot.test.ts +595 -0
  212. package/src/utils/account-state-snapshot.ts +94 -0
  213. package/src/utils/backup-encryption.test.ts +442 -0
  214. package/src/utils/backup-encryption.ts +352 -0
  215. package/src/utils/batch-utilization.ts +1309 -0
  216. package/src/utils/constants.ts +20 -0
  217. package/src/utils/hex.ts +27 -0
  218. package/src/utils/key-derivation.ts +197 -0
  219. package/src/utils/storage-managers.ts +365 -0
  220. package/src/utils/ttl.ts +129 -0
  221. package/src/utils/url.test.ts +136 -0
  222. package/src/utils/url.ts +71 -0
  223. package/src/utils/versioned-storage.ts +323 -0
@@ -0,0 +1,376 @@
1
+ import { Binary } from "cafe-utility"
2
+
3
+ // secp256k1 curve parameters
4
+ const SECP256K1_P = BigInt(
5
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
6
+ )
7
+ const SECP256K1_N = BigInt(
8
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
9
+ )
10
+ const SECP256K1_GX = BigInt(
11
+ "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
12
+ )
13
+ const SECP256K1_GY = BigInt(
14
+ "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
15
+ )
16
+
17
+ // Key derivation nonces
18
+ const LOOKUP_KEY_NONCE = 0x00
19
+ const ACCESS_KEY_DECRYPTION_NONCE = 0x01
20
+
21
+ // Size constants
22
+ const PRIVATE_KEY_SIZE = 32
23
+ const PUBLIC_KEY_COORD_SIZE = 32
24
+ const COMPRESSED_PUBLIC_KEY_SIZE = 33
25
+ const KEY_SIZE = 32
26
+ const COUNTER_SIZE = 4
27
+
28
+ /**
29
+ * Modular arithmetic: a mod p (always positive)
30
+ */
31
+ function mod(a: bigint, p: bigint): bigint {
32
+ const result = a % p
33
+ return result >= 0n ? result : result + p
34
+ }
35
+
36
+ /**
37
+ * Extended Euclidean algorithm for modular inverse
38
+ */
39
+ function modInverse(a: bigint, p: bigint): bigint {
40
+ let [oldR, r] = [mod(a, p), p]
41
+ let [oldS, s] = [1n, 0n]
42
+
43
+ while (r !== 0n) {
44
+ const quotient = oldR / r
45
+ ;[oldR, r] = [r, oldR - quotient * r]
46
+ ;[oldS, s] = [s, oldS - quotient * s]
47
+ }
48
+
49
+ if (oldR !== 1n) {
50
+ throw new Error("Modular inverse does not exist")
51
+ }
52
+
53
+ return mod(oldS, p)
54
+ }
55
+
56
+ /**
57
+ * Elliptic curve point addition on secp256k1
58
+ * Returns null for point at infinity
59
+ */
60
+ function ellipticAdd(
61
+ x1: bigint,
62
+ y1: bigint,
63
+ x2: bigint,
64
+ y2: bigint,
65
+ ): [bigint, bigint] | null {
66
+ // Handle point at infinity cases
67
+ if (x1 === 0n && y1 === 0n) return [x2, y2]
68
+ if (x2 === 0n && y2 === 0n) return [x1, y1]
69
+
70
+ // If points are inverses, return point at infinity
71
+ if (x1 === x2 && mod(y1 + y2, SECP256K1_P) === 0n) {
72
+ return null
73
+ }
74
+
75
+ let slope: bigint
76
+ if (x1 === x2 && y1 === y2) {
77
+ // Point doubling
78
+ const numerator = mod(3n * x1 * x1, SECP256K1_P)
79
+ const denominator = mod(2n * y1, SECP256K1_P)
80
+ slope = mod(numerator * modInverse(denominator, SECP256K1_P), SECP256K1_P)
81
+ } else {
82
+ // Point addition
83
+ const numerator = mod(y2 - y1, SECP256K1_P)
84
+ const denominator = mod(x2 - x1, SECP256K1_P)
85
+ slope = mod(numerator * modInverse(denominator, SECP256K1_P), SECP256K1_P)
86
+ }
87
+
88
+ const x3 = mod(slope * slope - x1 - x2, SECP256K1_P)
89
+ const y3 = mod(slope * (x1 - x3) - y1, SECP256K1_P)
90
+
91
+ return [x3, y3]
92
+ }
93
+
94
+ /**
95
+ * Elliptic curve point doubling (special case of addition)
96
+ */
97
+ function ellipticDouble(x: bigint, y: bigint): [bigint, bigint] | null {
98
+ return ellipticAdd(x, y, x, y)
99
+ }
100
+
101
+ /**
102
+ * Scalar multiplication using double-and-add algorithm
103
+ */
104
+ function scalarMultiply(
105
+ px: bigint,
106
+ py: bigint,
107
+ scalar: bigint,
108
+ ): [bigint, bigint] | null {
109
+ if (scalar === 0n) return null
110
+
111
+ let result: [bigint, bigint] | null = null
112
+ let current: [bigint, bigint] | null = [px, py]
113
+
114
+ let s = mod(scalar, SECP256K1_N)
115
+ while (s > 0n) {
116
+ if (s & 1n) {
117
+ if (result === null) {
118
+ result = current
119
+ } else if (current !== null) {
120
+ result = ellipticAdd(result[0], result[1], current[0], current[1])
121
+ }
122
+ }
123
+ if (current !== null) {
124
+ current = ellipticDouble(current[0], current[1])
125
+ }
126
+ s >>= 1n
127
+ }
128
+
129
+ return result
130
+ }
131
+
132
+ /**
133
+ * Convert Uint8Array to bigint (big-endian)
134
+ */
135
+ function uint8ArrayToBigInt(bytes: Uint8Array): bigint {
136
+ let result = 0n
137
+ for (const byte of bytes) {
138
+ result = (result << 8n) | BigInt(byte)
139
+ }
140
+ return result
141
+ }
142
+
143
+ /**
144
+ * Convert bigint to Uint8Array (big-endian, fixed size)
145
+ */
146
+ function bigIntToUint8Array(value: bigint, size: number): Uint8Array {
147
+ const result = new Uint8Array(size)
148
+ let v = value
149
+ for (let i = size - 1; i >= 0; i--) {
150
+ result[i] = Number(v & 0xffn)
151
+ v >>= 8n
152
+ }
153
+ return result
154
+ }
155
+
156
+ /**
157
+ * Derive public key from private key
158
+ */
159
+ export function publicKeyFromPrivate(privKey: Uint8Array): {
160
+ x: Uint8Array
161
+ y: Uint8Array
162
+ } {
163
+ if (privKey.length !== PRIVATE_KEY_SIZE) {
164
+ throw new Error(`Private key must be ${PRIVATE_KEY_SIZE} bytes`)
165
+ }
166
+
167
+ const scalar = uint8ArrayToBigInt(privKey)
168
+ const point = scalarMultiply(SECP256K1_GX, SECP256K1_GY, scalar)
169
+
170
+ if (point === null) {
171
+ throw new Error("Invalid private key")
172
+ }
173
+
174
+ return {
175
+ x: bigIntToUint8Array(point[0], PUBLIC_KEY_COORD_SIZE),
176
+ y: bigIntToUint8Array(point[1], PUBLIC_KEY_COORD_SIZE),
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Compute ECDH shared secret (x-coordinate of shared point)
182
+ */
183
+ export function ecdhSharedSecret(
184
+ privKey: Uint8Array,
185
+ pubX: Uint8Array,
186
+ pubY: Uint8Array,
187
+ ): Uint8Array {
188
+ if (privKey.length !== PRIVATE_KEY_SIZE) {
189
+ throw new Error(`Private key must be ${PRIVATE_KEY_SIZE} bytes`)
190
+ }
191
+ if (
192
+ pubX.length !== PUBLIC_KEY_COORD_SIZE ||
193
+ pubY.length !== PUBLIC_KEY_COORD_SIZE
194
+ ) {
195
+ throw new Error(
196
+ `Public key coordinates must be ${PUBLIC_KEY_COORD_SIZE} bytes each`,
197
+ )
198
+ }
199
+
200
+ const scalar = uint8ArrayToBigInt(privKey)
201
+ const px = uint8ArrayToBigInt(pubX)
202
+ const py = uint8ArrayToBigInt(pubY)
203
+
204
+ const point = scalarMultiply(px, py, scalar)
205
+ if (point === null) {
206
+ throw new Error("ECDH computation resulted in point at infinity")
207
+ }
208
+
209
+ return bigIntToUint8Array(point[0], PUBLIC_KEY_COORD_SIZE)
210
+ }
211
+
212
+ /**
213
+ * Derive lookup key and access key decryption key from ECDH shared secret
214
+ */
215
+ export function deriveKeys(
216
+ privKey: Uint8Array,
217
+ pubX: Uint8Array,
218
+ pubY: Uint8Array,
219
+ ): { lookupKey: Uint8Array; accessKeyDecryptionKey: Uint8Array } {
220
+ const sharedSecret = ecdhSharedSecret(privKey, pubX, pubY)
221
+
222
+ // lookupKey = keccak256(sharedX || 0x00)
223
+ const lookupKeyInput = new Uint8Array(sharedSecret.length + 1)
224
+ lookupKeyInput.set(sharedSecret)
225
+ lookupKeyInput[sharedSecret.length] = LOOKUP_KEY_NONCE
226
+ const lookupKey = Binary.keccak256(lookupKeyInput)
227
+
228
+ // accessKeyDecryptionKey = keccak256(sharedX || 0x01)
229
+ const akdKeyInput = new Uint8Array(sharedSecret.length + 1)
230
+ akdKeyInput.set(sharedSecret)
231
+ akdKeyInput[sharedSecret.length] = ACCESS_KEY_DECRYPTION_NONCE
232
+ const accessKeyDecryptionKey = Binary.keccak256(akdKeyInput)
233
+
234
+ return { lookupKey, accessKeyDecryptionKey }
235
+ }
236
+
237
+ /**
238
+ * Counter-mode encryption/decryption
239
+ * Matches Bee's Go implementation (bee/pkg/encryption/encryption.go:134-168)
240
+ * For each 32-byte block i: data[i] XOR keccak256(keccak256(key || uint32LE(i)))
241
+ */
242
+ export function counterModeEncrypt(
243
+ data: Uint8Array,
244
+ key: Uint8Array,
245
+ ): Uint8Array {
246
+ if (key.length !== KEY_SIZE) {
247
+ throw new Error(`Key must be ${KEY_SIZE} bytes`)
248
+ }
249
+
250
+ const result = new Uint8Array(data.length)
251
+ const numBlocks = Math.ceil(data.length / KEY_SIZE)
252
+
253
+ for (let i = 0; i < numBlocks; i++) {
254
+ // Create counter input: key || uint32LE(i)
255
+ // Must match Bee's Go implementation which uses binary.LittleEndian.PutUint32
256
+ const counterInput = new Uint8Array(key.length + COUNTER_SIZE)
257
+ counterInput.set(key)
258
+ // LITTLE ENDIAN counter (matches Bee's binary.LittleEndian.PutUint32)
259
+ counterInput[key.length] = i & 0xff
260
+ counterInput[key.length + 1] = (i >> 8) & 0xff
261
+ counterInput[key.length + 2] = (i >> 16) & 0xff
262
+ counterInput[key.length + 3] = (i >> 24) & 0xff
263
+
264
+ // First hash: keccak256(key || counter)
265
+ const ctrHash = Binary.keccak256(counterInput)
266
+
267
+ // Second hash for "selective disclosure" (matches Bee's implementation)
268
+ const keyStream = Binary.keccak256(ctrHash)
269
+
270
+ // XOR data block with keystream
271
+ const blockStart = i * KEY_SIZE
272
+ const blockEnd = Math.min(blockStart + KEY_SIZE, data.length)
273
+
274
+ for (let j = blockStart; j < blockEnd; j++) {
275
+ result[j] = data[j] ^ keyStream[j - blockStart]
276
+ }
277
+ }
278
+
279
+ return result
280
+ }
281
+
282
+ /**
283
+ * Counter-mode decryption (symmetric with encryption)
284
+ */
285
+ export const counterModeDecrypt = counterModeEncrypt
286
+
287
+ /**
288
+ * Parse compressed public key (33 bytes) to uncompressed coordinates
289
+ */
290
+ export function publicKeyFromCompressed(compressed: Uint8Array): {
291
+ x: Uint8Array
292
+ y: Uint8Array
293
+ } {
294
+ if (compressed.length !== COMPRESSED_PUBLIC_KEY_SIZE) {
295
+ throw new Error(
296
+ `Compressed public key must be ${COMPRESSED_PUBLIC_KEY_SIZE} bytes`,
297
+ )
298
+ }
299
+
300
+ const prefix = compressed[0]
301
+ if (prefix !== 0x02 && prefix !== 0x03) {
302
+ throw new Error("Invalid compressed public key prefix")
303
+ }
304
+
305
+ const x = uint8ArrayToBigInt(compressed.slice(1))
306
+
307
+ // y² = x³ + 7 (mod p)
308
+ const ySquared = mod(x * x * x + 7n, SECP256K1_P)
309
+
310
+ // Compute modular square root using Tonelli-Shanks
311
+ // For secp256k1, p ≡ 3 (mod 4), so y = (y²)^((p+1)/4) mod p
312
+ const exponent = (SECP256K1_P + 1n) / 4n
313
+ let y = modPow(ySquared, exponent, SECP256K1_P)
314
+
315
+ // Check parity and adjust if needed
316
+ const isEven = (y & 1n) === 0n
317
+ const shouldBeEven = prefix === 0x02
318
+ if (isEven !== shouldBeEven) {
319
+ y = SECP256K1_P - y
320
+ }
321
+
322
+ return {
323
+ x: bigIntToUint8Array(x, PUBLIC_KEY_COORD_SIZE),
324
+ y: bigIntToUint8Array(y, PUBLIC_KEY_COORD_SIZE),
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Compress public key to 33 bytes
330
+ */
331
+ export function compressPublicKey(x: Uint8Array, y: Uint8Array): Uint8Array {
332
+ if (
333
+ x.length !== PUBLIC_KEY_COORD_SIZE ||
334
+ y.length !== PUBLIC_KEY_COORD_SIZE
335
+ ) {
336
+ throw new Error(
337
+ `Public key coordinates must be ${PUBLIC_KEY_COORD_SIZE} bytes each`,
338
+ )
339
+ }
340
+
341
+ const yBigInt = uint8ArrayToBigInt(y)
342
+ const prefix = (yBigInt & 1n) === 0n ? 0x02 : 0x03
343
+
344
+ const result = new Uint8Array(COMPRESSED_PUBLIC_KEY_SIZE)
345
+ result[0] = prefix
346
+ result.set(x, 1)
347
+
348
+ return result
349
+ }
350
+
351
+ /**
352
+ * Modular exponentiation using square-and-multiply
353
+ */
354
+ function modPow(base: bigint, exponent: bigint, modulus: bigint): bigint {
355
+ let result = 1n
356
+ base = mod(base, modulus)
357
+
358
+ while (exponent > 0n) {
359
+ if (exponent & 1n) {
360
+ result = mod(result * base, modulus)
361
+ }
362
+ exponent >>= 1n
363
+ base = mod(base * base, modulus)
364
+ }
365
+
366
+ return result
367
+ }
368
+
369
+ /**
370
+ * Generate a random 32-byte key using crypto.getRandomValues
371
+ */
372
+ export function generateRandomKey(): Uint8Array {
373
+ const key = new Uint8Array(KEY_SIZE)
374
+ crypto.getRandomValues(key)
375
+ return key
376
+ }