@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,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for ACT cryptographic primitives
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest"
|
|
6
|
+
import {
|
|
7
|
+
publicKeyFromPrivate,
|
|
8
|
+
ecdhSharedSecret,
|
|
9
|
+
deriveKeys,
|
|
10
|
+
counterModeEncrypt,
|
|
11
|
+
counterModeDecrypt,
|
|
12
|
+
publicKeyFromCompressed,
|
|
13
|
+
compressPublicKey,
|
|
14
|
+
generateRandomKey,
|
|
15
|
+
} from "./crypto"
|
|
16
|
+
|
|
17
|
+
// Test vectors - known private key and expected public key
|
|
18
|
+
// Using a simple test vector that can be verified
|
|
19
|
+
const TEST_PRIVATE_KEY = new Uint8Array([
|
|
20
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
21
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
22
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
// Generator point G for secp256k1 (private key = 1 gives G)
|
|
26
|
+
const GENERATOR_X = new Uint8Array([
|
|
27
|
+
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce,
|
|
28
|
+
0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2,
|
|
29
|
+
0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
const GENERATOR_Y = new Uint8Array([
|
|
33
|
+
0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e,
|
|
34
|
+
0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47,
|
|
35
|
+
0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8,
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
describe("publicKeyFromPrivate", () => {
|
|
39
|
+
it("should derive correct public key for private key = 1 (generator point)", () => {
|
|
40
|
+
const pubKey = publicKeyFromPrivate(TEST_PRIVATE_KEY)
|
|
41
|
+
expect(pubKey.x).toEqual(GENERATOR_X)
|
|
42
|
+
expect(pubKey.y).toEqual(GENERATOR_Y)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("should throw error for invalid private key length", () => {
|
|
46
|
+
expect(() => publicKeyFromPrivate(new Uint8Array(16))).toThrow()
|
|
47
|
+
expect(() => publicKeyFromPrivate(new Uint8Array(64))).toThrow()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("should derive different public keys for different private keys", () => {
|
|
51
|
+
const privKey1 = new Uint8Array(32)
|
|
52
|
+
privKey1[31] = 1
|
|
53
|
+
const privKey2 = new Uint8Array(32)
|
|
54
|
+
privKey2[31] = 2
|
|
55
|
+
|
|
56
|
+
const pubKey1 = publicKeyFromPrivate(privKey1)
|
|
57
|
+
const pubKey2 = publicKeyFromPrivate(privKey2)
|
|
58
|
+
|
|
59
|
+
expect(pubKey1.x).not.toEqual(pubKey2.x)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe("ecdhSharedSecret", () => {
|
|
64
|
+
it("should compute same shared secret from both directions", () => {
|
|
65
|
+
// Alice's key pair
|
|
66
|
+
const alicePrivate = new Uint8Array(32)
|
|
67
|
+
alicePrivate[31] = 2
|
|
68
|
+
const alicePublic = publicKeyFromPrivate(alicePrivate)
|
|
69
|
+
|
|
70
|
+
// Bob's key pair
|
|
71
|
+
const bobPrivate = new Uint8Array(32)
|
|
72
|
+
bobPrivate[31] = 3
|
|
73
|
+
const bobPublic = publicKeyFromPrivate(bobPrivate)
|
|
74
|
+
|
|
75
|
+
// Alice computes shared secret with Bob's public key
|
|
76
|
+
const sharedAlice = ecdhSharedSecret(alicePrivate, bobPublic.x, bobPublic.y)
|
|
77
|
+
|
|
78
|
+
// Bob computes shared secret with Alice's public key
|
|
79
|
+
const sharedBob = ecdhSharedSecret(bobPrivate, alicePublic.x, alicePublic.y)
|
|
80
|
+
|
|
81
|
+
// Both should be equal
|
|
82
|
+
expect(sharedAlice).toEqual(sharedBob)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it("should return 32 bytes", () => {
|
|
86
|
+
const privKey = new Uint8Array(32)
|
|
87
|
+
privKey[31] = 5
|
|
88
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
89
|
+
|
|
90
|
+
const shared = ecdhSharedSecret(privKey, pubKey.x, pubKey.y)
|
|
91
|
+
expect(shared.length).toBe(32)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("should throw error for invalid key lengths", () => {
|
|
95
|
+
const validPriv = new Uint8Array(32)
|
|
96
|
+
validPriv[31] = 1
|
|
97
|
+
const pubKey = publicKeyFromPrivate(validPriv)
|
|
98
|
+
|
|
99
|
+
expect(() =>
|
|
100
|
+
ecdhSharedSecret(new Uint8Array(16), pubKey.x, pubKey.y),
|
|
101
|
+
).toThrow()
|
|
102
|
+
expect(() =>
|
|
103
|
+
ecdhSharedSecret(validPriv, new Uint8Array(16), pubKey.y),
|
|
104
|
+
).toThrow()
|
|
105
|
+
expect(() =>
|
|
106
|
+
ecdhSharedSecret(validPriv, pubKey.x, new Uint8Array(16)),
|
|
107
|
+
).toThrow()
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe("deriveKeys", () => {
|
|
112
|
+
it("should derive different lookup and access key decryption keys", () => {
|
|
113
|
+
const privKey = new Uint8Array(32)
|
|
114
|
+
privKey[31] = 7
|
|
115
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
116
|
+
|
|
117
|
+
const { lookupKey, accessKeyDecryptionKey } = deriveKeys(
|
|
118
|
+
privKey,
|
|
119
|
+
pubKey.x,
|
|
120
|
+
pubKey.y,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
expect(lookupKey.length).toBe(32)
|
|
124
|
+
expect(accessKeyDecryptionKey.length).toBe(32)
|
|
125
|
+
expect(lookupKey).not.toEqual(accessKeyDecryptionKey)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it("should produce deterministic results", () => {
|
|
129
|
+
const privKey = new Uint8Array(32)
|
|
130
|
+
privKey[31] = 11
|
|
131
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
132
|
+
|
|
133
|
+
const result1 = deriveKeys(privKey, pubKey.x, pubKey.y)
|
|
134
|
+
const result2 = deriveKeys(privKey, pubKey.x, pubKey.y)
|
|
135
|
+
|
|
136
|
+
expect(result1.lookupKey).toEqual(result2.lookupKey)
|
|
137
|
+
expect(result1.accessKeyDecryptionKey).toEqual(
|
|
138
|
+
result2.accessKeyDecryptionKey,
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it("should produce different keys for different inputs", () => {
|
|
143
|
+
const privKey1 = new Uint8Array(32)
|
|
144
|
+
privKey1[31] = 13
|
|
145
|
+
const pubKey1 = publicKeyFromPrivate(privKey1)
|
|
146
|
+
|
|
147
|
+
const privKey2 = new Uint8Array(32)
|
|
148
|
+
privKey2[31] = 17
|
|
149
|
+
const pubKey2 = publicKeyFromPrivate(privKey2)
|
|
150
|
+
|
|
151
|
+
const result1 = deriveKeys(privKey1, pubKey1.x, pubKey1.y)
|
|
152
|
+
const result2 = deriveKeys(privKey2, pubKey2.x, pubKey2.y)
|
|
153
|
+
|
|
154
|
+
expect(result1.lookupKey).not.toEqual(result2.lookupKey)
|
|
155
|
+
expect(result1.accessKeyDecryptionKey).not.toEqual(
|
|
156
|
+
result2.accessKeyDecryptionKey,
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe("counterModeEncrypt/counterModeDecrypt", () => {
|
|
162
|
+
it("should encrypt and decrypt small data correctly", () => {
|
|
163
|
+
const key = generateRandomKey()
|
|
164
|
+
const plaintext = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
|
|
165
|
+
|
|
166
|
+
const ciphertext = counterModeEncrypt(plaintext, key)
|
|
167
|
+
const decrypted = counterModeDecrypt(ciphertext, key)
|
|
168
|
+
|
|
169
|
+
expect(decrypted).toEqual(plaintext)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it("should encrypt and decrypt exactly 32 bytes correctly", () => {
|
|
173
|
+
const key = generateRandomKey()
|
|
174
|
+
const plaintext = new Uint8Array(32)
|
|
175
|
+
for (let i = 0; i < 32; i++) {
|
|
176
|
+
plaintext[i] = i
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ciphertext = counterModeEncrypt(plaintext, key)
|
|
180
|
+
const decrypted = counterModeDecrypt(ciphertext, key)
|
|
181
|
+
|
|
182
|
+
expect(decrypted).toEqual(plaintext)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it("should encrypt and decrypt multiple blocks correctly", () => {
|
|
186
|
+
const key = generateRandomKey()
|
|
187
|
+
const plaintext = new Uint8Array(100)
|
|
188
|
+
for (let i = 0; i < 100; i++) {
|
|
189
|
+
plaintext[i] = i % 256
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const ciphertext = counterModeEncrypt(plaintext, key)
|
|
193
|
+
const decrypted = counterModeDecrypt(ciphertext, key)
|
|
194
|
+
|
|
195
|
+
expect(decrypted).toEqual(plaintext)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it("should produce different ciphertext with different keys", () => {
|
|
199
|
+
const key1 = generateRandomKey()
|
|
200
|
+
const key2 = generateRandomKey()
|
|
201
|
+
const plaintext = new Uint8Array([1, 2, 3, 4, 5])
|
|
202
|
+
|
|
203
|
+
const ciphertext1 = counterModeEncrypt(plaintext, key1)
|
|
204
|
+
const ciphertext2 = counterModeEncrypt(plaintext, key2)
|
|
205
|
+
|
|
206
|
+
expect(ciphertext1).not.toEqual(ciphertext2)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it("should produce ciphertext different from plaintext", () => {
|
|
210
|
+
const key = generateRandomKey()
|
|
211
|
+
const plaintext = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
212
|
+
|
|
213
|
+
const ciphertext = counterModeEncrypt(plaintext, key)
|
|
214
|
+
|
|
215
|
+
expect(ciphertext).not.toEqual(plaintext)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it("should throw error for invalid key length", () => {
|
|
219
|
+
const plaintext = new Uint8Array([1, 2, 3])
|
|
220
|
+
|
|
221
|
+
expect(() => counterModeEncrypt(plaintext, new Uint8Array(16))).toThrow()
|
|
222
|
+
expect(() => counterModeEncrypt(plaintext, new Uint8Array(64))).toThrow()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it("should handle empty data", () => {
|
|
226
|
+
const key = generateRandomKey()
|
|
227
|
+
const plaintext = new Uint8Array(0)
|
|
228
|
+
|
|
229
|
+
const ciphertext = counterModeEncrypt(plaintext, key)
|
|
230
|
+
const decrypted = counterModeDecrypt(ciphertext, key)
|
|
231
|
+
|
|
232
|
+
expect(decrypted).toEqual(plaintext)
|
|
233
|
+
expect(decrypted.length).toBe(0)
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it("should be symmetric (encrypt === decrypt)", () => {
|
|
237
|
+
const key = generateRandomKey()
|
|
238
|
+
const data = new Uint8Array([42, 43, 44, 45])
|
|
239
|
+
|
|
240
|
+
// Double encryption should return original
|
|
241
|
+
const encrypted = counterModeEncrypt(data, key)
|
|
242
|
+
const doubleEncrypted = counterModeEncrypt(encrypted, key)
|
|
243
|
+
|
|
244
|
+
expect(doubleEncrypted).toEqual(data)
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe("compressPublicKey", () => {
|
|
249
|
+
it("should compress to 33 bytes", () => {
|
|
250
|
+
const privKey = new Uint8Array(32)
|
|
251
|
+
privKey[31] = 1
|
|
252
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
253
|
+
|
|
254
|
+
const compressed = compressPublicKey(pubKey.x, pubKey.y)
|
|
255
|
+
expect(compressed.length).toBe(33)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it("should use prefix 0x02 for even y", () => {
|
|
259
|
+
// Generator point has even y
|
|
260
|
+
const compressed = compressPublicKey(GENERATOR_X, GENERATOR_Y)
|
|
261
|
+
expect(compressed[0]).toBe(0x02)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it("should use prefix 0x03 for odd y", () => {
|
|
265
|
+
// Find a private key that gives odd y
|
|
266
|
+
// Private key = 3 gives a point with odd y
|
|
267
|
+
const privKey = new Uint8Array(32)
|
|
268
|
+
privKey[31] = 3
|
|
269
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
270
|
+
|
|
271
|
+
const compressed = compressPublicKey(pubKey.x, pubKey.y)
|
|
272
|
+
// The y coordinate's parity determines the prefix
|
|
273
|
+
// Either 0x02 (even) or 0x03 (odd) - verify it's valid
|
|
274
|
+
expect([0x02, 0x03]).toContain(compressed[0])
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it("should include x coordinate after prefix", () => {
|
|
278
|
+
const compressed = compressPublicKey(GENERATOR_X, GENERATOR_Y)
|
|
279
|
+
expect(compressed.slice(1)).toEqual(GENERATOR_X)
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe("publicKeyFromCompressed", () => {
|
|
284
|
+
it("should decompress to original coordinates", () => {
|
|
285
|
+
const privKey = new Uint8Array(32)
|
|
286
|
+
privKey[31] = 1
|
|
287
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
288
|
+
|
|
289
|
+
const compressed = compressPublicKey(pubKey.x, pubKey.y)
|
|
290
|
+
const decompressed = publicKeyFromCompressed(compressed)
|
|
291
|
+
|
|
292
|
+
expect(decompressed.x).toEqual(pubKey.x)
|
|
293
|
+
expect(decompressed.y).toEqual(pubKey.y)
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it("should handle prefix 0x02 (even y)", () => {
|
|
297
|
+
const compressed = compressPublicKey(GENERATOR_X, GENERATOR_Y)
|
|
298
|
+
expect(compressed[0]).toBe(0x02)
|
|
299
|
+
|
|
300
|
+
const decompressed = publicKeyFromCompressed(compressed)
|
|
301
|
+
expect(decompressed.x).toEqual(GENERATOR_X)
|
|
302
|
+
expect(decompressed.y).toEqual(GENERATOR_Y)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it("should handle any valid compressed key (roundtrip)", () => {
|
|
306
|
+
// Test multiple private keys to cover both even and odd y cases
|
|
307
|
+
const privKey = new Uint8Array(32)
|
|
308
|
+
privKey[31] = 5
|
|
309
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
310
|
+
|
|
311
|
+
const compressed = compressPublicKey(pubKey.x, pubKey.y)
|
|
312
|
+
// Prefix should be 0x02 or 0x03
|
|
313
|
+
expect([0x02, 0x03]).toContain(compressed[0])
|
|
314
|
+
|
|
315
|
+
const decompressed = publicKeyFromCompressed(compressed)
|
|
316
|
+
expect(decompressed.x).toEqual(pubKey.x)
|
|
317
|
+
expect(decompressed.y).toEqual(pubKey.y)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it("should throw error for invalid prefix", () => {
|
|
321
|
+
const invalid = new Uint8Array(33)
|
|
322
|
+
invalid[0] = 0x04 // Uncompressed prefix
|
|
323
|
+
expect(() => publicKeyFromCompressed(invalid)).toThrow()
|
|
324
|
+
|
|
325
|
+
invalid[0] = 0x00
|
|
326
|
+
expect(() => publicKeyFromCompressed(invalid)).toThrow()
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it("should throw error for invalid length", () => {
|
|
330
|
+
expect(() => publicKeyFromCompressed(new Uint8Array(32))).toThrow()
|
|
331
|
+
expect(() => publicKeyFromCompressed(new Uint8Array(34))).toThrow()
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it("should roundtrip through compress/decompress", () => {
|
|
335
|
+
// Test multiple keys
|
|
336
|
+
for (let i = 1; i <= 10; i++) {
|
|
337
|
+
const privKey = new Uint8Array(32)
|
|
338
|
+
privKey[31] = i
|
|
339
|
+
const pubKey = publicKeyFromPrivate(privKey)
|
|
340
|
+
|
|
341
|
+
const compressed = compressPublicKey(pubKey.x, pubKey.y)
|
|
342
|
+
const decompressed = publicKeyFromCompressed(compressed)
|
|
343
|
+
|
|
344
|
+
expect(decompressed.x).toEqual(pubKey.x)
|
|
345
|
+
expect(decompressed.y).toEqual(pubKey.y)
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
describe("generateRandomKey", () => {
|
|
351
|
+
it("should return 32 bytes", () => {
|
|
352
|
+
const key = generateRandomKey()
|
|
353
|
+
expect(key.length).toBe(32)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it("should generate different keys each time", () => {
|
|
357
|
+
const key1 = generateRandomKey()
|
|
358
|
+
const key2 = generateRandomKey()
|
|
359
|
+
const key3 = generateRandomKey()
|
|
360
|
+
|
|
361
|
+
expect(key1).not.toEqual(key2)
|
|
362
|
+
expect(key2).not.toEqual(key3)
|
|
363
|
+
expect(key1).not.toEqual(key3)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it("should return Uint8Array", () => {
|
|
367
|
+
const key = generateRandomKey()
|
|
368
|
+
expect(key).toBeInstanceOf(Uint8Array)
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
describe("ECDH key agreement integration", () => {
|
|
373
|
+
it("should allow two parties to derive same lookup key", () => {
|
|
374
|
+
// Publisher generates key pair
|
|
375
|
+
const publisherPrivate = new Uint8Array(32)
|
|
376
|
+
crypto.getRandomValues(publisherPrivate)
|
|
377
|
+
const publisherPublic = publicKeyFromPrivate(publisherPrivate)
|
|
378
|
+
|
|
379
|
+
// Grantee generates key pair
|
|
380
|
+
const granteePrivate = new Uint8Array(32)
|
|
381
|
+
crypto.getRandomValues(granteePrivate)
|
|
382
|
+
const granteePublic = publicKeyFromPrivate(granteePrivate)
|
|
383
|
+
|
|
384
|
+
// Publisher derives keys using grantee's public key
|
|
385
|
+
const publisherKeys = deriveKeys(
|
|
386
|
+
publisherPrivate,
|
|
387
|
+
granteePublic.x,
|
|
388
|
+
granteePublic.y,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
// Grantee derives keys using publisher's public key
|
|
392
|
+
const granteeKeys = deriveKeys(
|
|
393
|
+
granteePrivate,
|
|
394
|
+
publisherPublic.x,
|
|
395
|
+
publisherPublic.y,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
// Both should derive the same lookup key and access key decryption key
|
|
399
|
+
expect(publisherKeys.lookupKey).toEqual(granteeKeys.lookupKey)
|
|
400
|
+
expect(publisherKeys.accessKeyDecryptionKey).toEqual(
|
|
401
|
+
granteeKeys.accessKeyDecryptionKey,
|
|
402
|
+
)
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it("should produce unique lookup keys for different grantees", () => {
|
|
406
|
+
// Publisher key pair
|
|
407
|
+
const publisherPrivate = new Uint8Array(32)
|
|
408
|
+
publisherPrivate[31] = 100
|
|
409
|
+
const publisherPublic = publicKeyFromPrivate(publisherPrivate)
|
|
410
|
+
|
|
411
|
+
// Grantee 1
|
|
412
|
+
const grantee1Private = new Uint8Array(32)
|
|
413
|
+
grantee1Private[31] = 101
|
|
414
|
+
const grantee1Public = publicKeyFromPrivate(grantee1Private)
|
|
415
|
+
|
|
416
|
+
// Grantee 2
|
|
417
|
+
const grantee2Private = new Uint8Array(32)
|
|
418
|
+
grantee2Private[31] = 102
|
|
419
|
+
const grantee2Public = publicKeyFromPrivate(grantee2Private)
|
|
420
|
+
|
|
421
|
+
// Publisher derives keys for each grantee
|
|
422
|
+
const keys1 = deriveKeys(
|
|
423
|
+
publisherPrivate,
|
|
424
|
+
grantee1Public.x,
|
|
425
|
+
grantee1Public.y,
|
|
426
|
+
)
|
|
427
|
+
const keys2 = deriveKeys(
|
|
428
|
+
publisherPrivate,
|
|
429
|
+
grantee2Public.x,
|
|
430
|
+
grantee2Public.y,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
// Lookup keys should be different
|
|
434
|
+
expect(keys1.lookupKey).not.toEqual(keys2.lookupKey)
|
|
435
|
+
})
|
|
436
|
+
})
|