@storacha/encrypt-upload-client 1.1.56 → 1.1.58
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/dist/config/constants.d.ts +3 -3
- package/dist/config/constants.js +4 -3
- package/dist/config/env.d.ts +9 -6
- package/dist/config/service.d.ts +13 -13
- package/dist/core/client.d.ts +54 -41
- package/dist/core/client.js +68 -56
- package/dist/core/errors.d.ts +6 -6
- package/dist/core/metadata/encrypted-metadata.d.ts +13 -8
- package/dist/core/metadata/kms-metadata.d.ts +68 -36
- package/dist/core/metadata/lit-metadata.d.ts +63 -28
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts +172 -137
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts +107 -86
- package/dist/crypto/factories.browser.d.ts +9 -5
- package/dist/crypto/factories.browser.js +15 -7
- package/dist/crypto/factories.node.d.ts +13 -6
- package/dist/crypto/factories.node.js +19 -13
- package/dist/crypto/index.d.ts +5 -5
- package/dist/crypto/index.js +5 -5
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts +58 -54
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +174 -146
- package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +36 -32
- package/dist/crypto/symmetric/node-aes-cbc-crypto.js +101 -95
- package/dist/examples/decrypt-test.d.ts +2 -2
- package/dist/examples/decrypt-test.js +78 -69
- package/dist/examples/encrypt-test.d.ts +5 -3
- package/dist/examples/encrypt-test.js +58 -55
- package/dist/handlers/decrypt-handler.d.ts +19 -5
- package/dist/handlers/encrypt-handler.d.ts +9 -3
- package/dist/handlers/encrypt-handler.js +93 -57
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/protocols/lit.d.ts +33 -9
- package/dist/protocols/lit.js +134 -98
- package/dist/test/cid-verification.spec.d.ts +2 -2
- package/dist/test/cid-verification.spec.js +341 -313
- package/dist/test/crypto-compatibility.spec.d.ts +2 -2
- package/dist/test/crypto-compatibility.spec.js +184 -120
- package/dist/test/crypto-counter-security.spec.d.ts +2 -2
- package/dist/test/crypto-counter-security.spec.js +177 -138
- package/dist/test/crypto-streaming.spec.d.ts +2 -2
- package/dist/test/crypto-streaming.spec.js +208 -126
- package/dist/test/encrypted-metadata.spec.d.ts +2 -2
- package/dist/test/encrypted-metadata.spec.js +89 -62
- package/dist/test/factories.spec.d.ts +2 -2
- package/dist/test/factories.spec.js +275 -139
- package/dist/test/file-metadata.spec.d.ts +2 -2
- package/dist/test/file-metadata.spec.js +472 -416
- package/dist/test/fixtures/test-fixtures.d.ts +25 -20
- package/dist/test/fixtures/test-fixtures.js +61 -53
- package/dist/test/helpers/test-file-utils.d.ts +19 -14
- package/dist/test/helpers/test-file-utils.js +78 -76
- package/dist/test/https-enforcement.spec.d.ts +2 -2
- package/dist/test/https-enforcement.spec.js +278 -124
- package/dist/test/kms-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/kms-crypto-adapter.spec.js +473 -304
- package/dist/test/lit-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/lit-crypto-adapter.spec.js +206 -118
- package/dist/test/memory-efficiency.spec.d.ts +2 -2
- package/dist/test/memory-efficiency.spec.js +100 -87
- package/dist/test/mocks/key-manager.d.ts +71 -38
- package/dist/test/mocks/key-manager.js +129 -113
- package/dist/test/node-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/node-crypto-adapter.spec.js +155 -102
- package/dist/test/node-generic-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/node-generic-crypto-adapter.spec.js +134 -94
- package/dist/test/setup.d.ts +2 -2
- package/dist/test/setup.js +8 -9
- package/dist/tsconfig.spec.tsbuildinfo +1 -1
- package/dist/types.d.ts +219 -181
- package/dist/utils/file-metadata.d.ts +19 -13
- package/dist/utils.d.ts +14 -5
- package/package.json +4 -4
|
@@ -1,305 +1,474 @@
|
|
|
1
|
-
import './setup.js'
|
|
2
|
-
import { test, describe } from 'node:test'
|
|
3
|
-
import assert from 'node:assert'
|
|
4
|
-
import * as Server from '@ucanto/server'
|
|
5
|
-
import { base64 } from 'multiformats/bases/base64'
|
|
6
|
-
import * as Space from '@storacha/capabilities/space'
|
|
7
|
-
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js'
|
|
8
|
-
import { KMSCryptoAdapter } from '../src/crypto/adapters/kms-crypto-adapter.js'
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import './setup.js'
|
|
2
|
+
import { test, describe } from 'node:test'
|
|
3
|
+
import assert from 'node:assert'
|
|
4
|
+
import * as Server from '@ucanto/server'
|
|
5
|
+
import { base64 } from 'multiformats/bases/base64'
|
|
6
|
+
import * as Space from '@storacha/capabilities/space'
|
|
7
|
+
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js'
|
|
8
|
+
import { KMSCryptoAdapter } from '../src/crypto/adapters/kms-crypto-adapter.js'
|
|
9
|
+
import {
|
|
10
|
+
createMockKeyManagerService,
|
|
11
|
+
createMockKeyManagerServer,
|
|
12
|
+
} from './mocks/key-manager.js'
|
|
13
|
+
import { createTestFixtures } from './fixtures/test-fixtures.js'
|
|
14
|
+
import {
|
|
15
|
+
stringToUint8Array,
|
|
16
|
+
streamToUint8Array,
|
|
17
|
+
uint8ArrayToString,
|
|
18
|
+
} from './helpers/test-file-utils.js'
|
|
12
19
|
await describe('KMSCryptoAdapter', async () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
20
|
+
await describe('Unit Tests', async () => {
|
|
21
|
+
await test('should delegate symmetric crypto operations to the injected implementation', async () => {
|
|
22
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
23
|
+
const adapter = new KMSCryptoAdapter(
|
|
24
|
+
symmetricCrypto,
|
|
25
|
+
'https://private.storacha.link',
|
|
26
|
+
'did:web:private.storacha.link'
|
|
27
|
+
)
|
|
28
|
+
const originalText =
|
|
29
|
+
'Op, this is a test for KMS strategy-based encryption!'
|
|
30
|
+
const blob = new Blob([stringToUint8Array(originalText)])
|
|
31
|
+
// Test that it delegates to the symmetric crypto implementation
|
|
32
|
+
const { key, iv, encryptedStream } = await adapter.encryptStream(blob)
|
|
33
|
+
assert(key instanceof Uint8Array, 'Key should be a Uint8Array')
|
|
34
|
+
assert(iv instanceof Uint8Array, 'IV should be a Uint8Array')
|
|
35
|
+
assert(
|
|
36
|
+
encryptedStream instanceof ReadableStream,
|
|
37
|
+
'Encrypted stream should be a ReadableStream'
|
|
38
|
+
)
|
|
39
|
+
// Test decryption delegation
|
|
40
|
+
const decryptedStream = await adapter.decryptStream(
|
|
41
|
+
encryptedStream,
|
|
42
|
+
key,
|
|
43
|
+
iv
|
|
44
|
+
)
|
|
45
|
+
const decryptedBytes = await streamToUint8Array(decryptedStream)
|
|
46
|
+
const decryptedText = uint8ArrayToString(decryptedBytes)
|
|
47
|
+
assert.strictEqual(
|
|
48
|
+
decryptedText,
|
|
49
|
+
originalText,
|
|
50
|
+
'Decrypted text should match original'
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
await test('should initialize KMS adapter with correct configuration', async () => {
|
|
54
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
55
|
+
const adapter = new KMSCryptoAdapter(
|
|
56
|
+
symmetricCrypto,
|
|
57
|
+
'https://private.storacha.link',
|
|
58
|
+
'did:web:private.storacha.link'
|
|
59
|
+
)
|
|
60
|
+
// Test that the adapter can handle encryption options directly
|
|
61
|
+
assert(
|
|
62
|
+
typeof adapter.encryptSymmetricKey === 'function',
|
|
63
|
+
'encryptSymmetricKey should be a function'
|
|
64
|
+
)
|
|
65
|
+
// Verify adapter constructor sets properties correctly
|
|
66
|
+
assert(
|
|
67
|
+
typeof adapter.keyManagerServiceDID === 'object',
|
|
68
|
+
'Adapter should have gateway DID object'
|
|
69
|
+
)
|
|
70
|
+
assert.strictEqual(
|
|
71
|
+
adapter.keyManagerServiceDID.did(),
|
|
72
|
+
'did:web:private.storacha.link',
|
|
73
|
+
'Adapter should have correct gateway DID'
|
|
74
|
+
)
|
|
75
|
+
assert(
|
|
76
|
+
adapter.keyManagerServiceURL instanceof URL,
|
|
77
|
+
'Adapter should have gateway URL'
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
await test('should handle metadata extraction with invalid CAR data', async () => {
|
|
81
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
82
|
+
const adapter = new KMSCryptoAdapter(
|
|
83
|
+
symmetricCrypto,
|
|
84
|
+
'https://private.storacha.link',
|
|
85
|
+
'did:web:private.storacha.link'
|
|
86
|
+
)
|
|
87
|
+
// Test that the method exists
|
|
88
|
+
assert(
|
|
89
|
+
typeof adapter.extractEncryptedMetadata === 'function',
|
|
90
|
+
'extractEncryptedMetadata should be a function'
|
|
91
|
+
)
|
|
92
|
+
assert(
|
|
93
|
+
typeof adapter.getEncryptedKey === 'function',
|
|
94
|
+
'getEncryptedKey should be a function'
|
|
95
|
+
)
|
|
96
|
+
// Should throw error for invalid CAR data
|
|
97
|
+
const mockCar = new Uint8Array([1, 2, 3])
|
|
98
|
+
assert.throws(
|
|
99
|
+
() => {
|
|
100
|
+
adapter.extractEncryptedMetadata(mockCar)
|
|
101
|
+
},
|
|
102
|
+
/Invalid CAR header format/,
|
|
103
|
+
'Should throw error for invalid CAR data'
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
await test('should sanitize space DID for KMS key ID', async () => {
|
|
107
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
108
|
+
const adapter = new KMSCryptoAdapter(
|
|
109
|
+
symmetricCrypto,
|
|
110
|
+
'https://mock-gateway.example.com',
|
|
111
|
+
'did:web:mock'
|
|
112
|
+
)
|
|
113
|
+
const spaceDID =
|
|
114
|
+
'did:key:z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1'
|
|
115
|
+
const sanitized = adapter.sanitizeSpaceDIDForKMSKeyId(spaceDID)
|
|
116
|
+
assert.strictEqual(
|
|
117
|
+
sanitized,
|
|
118
|
+
'z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1'
|
|
119
|
+
)
|
|
120
|
+
assert(!sanitized.includes('did:key:'))
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
await describe('Integration Tests', async () => {
|
|
124
|
+
await test('should complete full encryption workflow with mocked private gateway', async () => {
|
|
125
|
+
const fixtures = await createTestFixtures()
|
|
126
|
+
const {
|
|
127
|
+
keyManagerServiceDID,
|
|
128
|
+
spaceDID,
|
|
129
|
+
issuer,
|
|
130
|
+
publicKeyPem,
|
|
131
|
+
keyPair,
|
|
132
|
+
delegationProof,
|
|
133
|
+
} = fixtures
|
|
134
|
+
let setupCalled = false
|
|
135
|
+
let decryptCalled = false
|
|
136
|
+
let actualEncryptedKey = ''
|
|
137
|
+
// Create mock gateway service that performs real RSA encryption/decryption
|
|
138
|
+
const service = createMockKeyManagerService({
|
|
139
|
+
mockPublicKey: publicKeyPem,
|
|
140
|
+
onEncryptionSetup: (/** @type {any} */ input) => {
|
|
141
|
+
setupCalled = true
|
|
142
|
+
assert.strictEqual(input.capability.with, spaceDID)
|
|
143
|
+
assert.strictEqual(input.capability.can, 'space/encryption/setup')
|
|
144
|
+
},
|
|
145
|
+
onKeyDecrypt: (/** @type {any} */ input) => {
|
|
146
|
+
decryptCalled = true
|
|
147
|
+
assert.strictEqual(input.capability.with, spaceDID)
|
|
148
|
+
assert.strictEqual(
|
|
149
|
+
input.capability.can,
|
|
150
|
+
'space/encryption/key/decrypt'
|
|
151
|
+
)
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
// Override the decrypt handler to actually decrypt with the private key
|
|
155
|
+
service.space.encryption.key.decrypt = Server.provide(
|
|
156
|
+
Space.EncryptionKeyDecrypt,
|
|
157
|
+
async (input) => {
|
|
158
|
+
decryptCalled = true
|
|
159
|
+
assert.strictEqual(input.capability.with, spaceDID)
|
|
160
|
+
assert.strictEqual(
|
|
161
|
+
input.capability.can,
|
|
162
|
+
'space/encryption/key/decrypt'
|
|
163
|
+
)
|
|
164
|
+
// Get the encrypted key from the request
|
|
165
|
+
const encryptedKeyBytes = input.capability.nb.key
|
|
166
|
+
const encryptedSymmetricKey = base64.encode(encryptedKeyBytes)
|
|
167
|
+
actualEncryptedKey = encryptedSymmetricKey
|
|
168
|
+
// Decrypt with RSA private key (simulate real KMS decryption)
|
|
169
|
+
const encryptedBytes = encryptedKeyBytes
|
|
170
|
+
const decryptedBytes = await globalThis.crypto.subtle.decrypt(
|
|
171
|
+
{ name: 'RSA-OAEP' },
|
|
172
|
+
keyPair.privateKey,
|
|
173
|
+
encryptedBytes
|
|
174
|
+
)
|
|
175
|
+
// Return the decrypted symmetric key as base64
|
|
176
|
+
const decryptedKey = base64.encode(new Uint8Array(decryptedBytes))
|
|
177
|
+
return Server.ok({
|
|
178
|
+
decryptedSymmetricKey: decryptedKey,
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
// Create mock gateway server (HTTPS by default)
|
|
183
|
+
const keyManagerServiceServer = await createMockKeyManagerServer(
|
|
184
|
+
service,
|
|
185
|
+
keyManagerServiceDID,
|
|
186
|
+
5555
|
|
187
|
+
)
|
|
188
|
+
// Create KMS adapter with HTTP allowed for testing
|
|
189
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
190
|
+
const adapter = new KMSCryptoAdapter(
|
|
191
|
+
symmetricCrypto,
|
|
192
|
+
keyManagerServiceServer.url,
|
|
193
|
+
keyManagerServiceDID.did(),
|
|
194
|
+
{ allowInsecureHttp: true } // Allow HTTP for testing
|
|
195
|
+
)
|
|
196
|
+
try {
|
|
197
|
+
// Create test file and encrypt it to get real symmetric keys
|
|
198
|
+
const testFile = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
199
|
+
const testBlob = new Blob([testFile], {
|
|
200
|
+
type: 'application/octet-stream',
|
|
201
|
+
})
|
|
202
|
+
// Encrypt the file to get real symmetric keys
|
|
203
|
+
const { key, iv } = await adapter.encryptStream(testBlob)
|
|
204
|
+
const encryptionConfig = {
|
|
205
|
+
issuer,
|
|
206
|
+
spaceDID,
|
|
207
|
+
location: 'us-central1',
|
|
208
|
+
keyring: 'test-keyring',
|
|
209
|
+
}
|
|
210
|
+
// Test key encryption with real symmetric keys - this will call the mock setup
|
|
211
|
+
const encryptResult = await adapter.encryptSymmetricKey(
|
|
212
|
+
key,
|
|
213
|
+
iv,
|
|
214
|
+
encryptionConfig
|
|
215
|
+
)
|
|
216
|
+
assert(setupCalled, 'EncryptionSetup should have been called')
|
|
217
|
+
assert.strictEqual(encryptResult.strategy, 'kms')
|
|
218
|
+
const kmsMetadata =
|
|
219
|
+
/** @type {import('../src/types.js').KMSKeyMetadata} */ (
|
|
220
|
+
encryptResult.metadata
|
|
221
|
+
)
|
|
222
|
+
assert.strictEqual(kmsMetadata.space, spaceDID)
|
|
223
|
+
assert.strictEqual(kmsMetadata.kms.provider, 'google-kms')
|
|
224
|
+
assert.strictEqual(kmsMetadata.kms.algorithm, 'RSA-OAEP-2048-SHA256')
|
|
225
|
+
assert(typeof encryptResult.encryptedKey === 'string')
|
|
226
|
+
// Test key decryption - this will call the mock decrypt
|
|
227
|
+
const decryptionConfig = {
|
|
228
|
+
spaceDID,
|
|
229
|
+
decryptDelegation: delegationProof,
|
|
230
|
+
proofs: [],
|
|
231
|
+
}
|
|
232
|
+
const mockMetadata = {
|
|
233
|
+
strategy: /** @type {'kms'} */ ('kms'),
|
|
234
|
+
encryptedDataCID: 'bafybeid',
|
|
235
|
+
encryptedSymmetricKey: encryptResult.encryptedKey,
|
|
236
|
+
space: spaceDID,
|
|
237
|
+
kms: kmsMetadata.kms,
|
|
238
|
+
}
|
|
239
|
+
const decryptResult = await adapter.decryptSymmetricKey(
|
|
240
|
+
encryptResult.encryptedKey,
|
|
241
|
+
{
|
|
242
|
+
decryptionConfig,
|
|
243
|
+
metadata: mockMetadata,
|
|
244
|
+
resourceCID:
|
|
245
|
+
/** @type {import('@storacha/upload-client/types').AnyLink} */ (
|
|
246
|
+
/** @type {any} */ ('bafybeid')
|
|
247
|
+
),
|
|
248
|
+
issuer,
|
|
249
|
+
audience: keyManagerServiceDID.did(),
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
// Verify the round-trip worked
|
|
253
|
+
assert(setupCalled, 'EncryptionSetup should have been called')
|
|
254
|
+
assert(decryptCalled, 'KeyDecrypt should have been called')
|
|
255
|
+
assert(
|
|
256
|
+
decryptResult.key instanceof Uint8Array,
|
|
257
|
+
'Decrypted key should be Uint8Array'
|
|
258
|
+
)
|
|
259
|
+
assert(
|
|
260
|
+
decryptResult.iv instanceof Uint8Array,
|
|
261
|
+
'Decrypted IV should be Uint8Array'
|
|
262
|
+
)
|
|
263
|
+
// Most importantly: verify the decrypted keys match the original
|
|
264
|
+
assert.deepStrictEqual(
|
|
265
|
+
decryptResult.key,
|
|
266
|
+
key,
|
|
267
|
+
'Decrypted key should match original key'
|
|
268
|
+
)
|
|
269
|
+
assert.deepStrictEqual(
|
|
270
|
+
decryptResult.iv,
|
|
271
|
+
iv,
|
|
272
|
+
'Decrypted IV should match original IV'
|
|
273
|
+
)
|
|
274
|
+
// Verify the encrypted key was actually encrypted (different from original)
|
|
275
|
+
const originalCombined = adapter.symmetricCrypto.combineKeyAndIV(
|
|
276
|
+
key,
|
|
277
|
+
iv
|
|
278
|
+
)
|
|
279
|
+
const originalBase64 = base64.encode(originalCombined)
|
|
280
|
+
assert.notStrictEqual(
|
|
281
|
+
actualEncryptedKey,
|
|
282
|
+
originalBase64,
|
|
283
|
+
'Encrypted key should be different from original'
|
|
284
|
+
)
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('Test failed with error:', error)
|
|
287
|
+
throw error
|
|
288
|
+
} finally {
|
|
289
|
+
// Clean up server
|
|
290
|
+
await keyManagerServiceServer.close()
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
await test('should handle encryption setup errors gracefully', async () => {
|
|
294
|
+
const fixtures = await createTestFixtures()
|
|
295
|
+
const { keyManagerServiceDID, spaceDID, issuer } = fixtures
|
|
296
|
+
// Create service that returns errors
|
|
297
|
+
const service = createMockKeyManagerService({
|
|
298
|
+
mockPublicKey: 'invalid',
|
|
299
|
+
onEncryptionSetup: () => {
|
|
300
|
+
// This will be called but service will return error
|
|
301
|
+
},
|
|
302
|
+
})
|
|
303
|
+
// Override service to return error
|
|
304
|
+
service.space.encryption.setup = Server.provide(
|
|
305
|
+
Space.EncryptionSetup,
|
|
306
|
+
async () => {
|
|
307
|
+
return Server.error({
|
|
308
|
+
name: 'SpaceNotProvisioned',
|
|
309
|
+
message: 'Space is not provisioned for encryption',
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
const keyManagerServiceServer = await createMockKeyManagerServer(
|
|
314
|
+
service,
|
|
315
|
+
keyManagerServiceDID,
|
|
316
|
+
5556
|
|
317
|
+
)
|
|
318
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
319
|
+
const adapter = new KMSCryptoAdapter(
|
|
320
|
+
symmetricCrypto,
|
|
321
|
+
keyManagerServiceServer.url,
|
|
322
|
+
keyManagerServiceDID.did(),
|
|
323
|
+
{ allowInsecureHttp: true } // Allow HTTP for testing
|
|
324
|
+
)
|
|
325
|
+
try {
|
|
326
|
+
const testKey = new Uint8Array(32).fill(1)
|
|
327
|
+
const testIV = new Uint8Array(16).fill(2)
|
|
328
|
+
const encryptionConfig = {
|
|
329
|
+
issuer,
|
|
330
|
+
spaceDID,
|
|
331
|
+
}
|
|
332
|
+
// Should throw error
|
|
333
|
+
await assert.rejects(
|
|
334
|
+
() => adapter.encryptSymmetricKey(testKey, testIV, encryptionConfig),
|
|
335
|
+
/Space is not provisioned for encryption/
|
|
336
|
+
)
|
|
337
|
+
} finally {
|
|
338
|
+
await keyManagerServiceServer.close()
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
await test('should handle key decryption errors gracefully', async () => {
|
|
342
|
+
const fixtures = await createTestFixtures()
|
|
343
|
+
const { keyManagerServiceDID, spaceDID, issuer, delegationProof } =
|
|
344
|
+
fixtures
|
|
345
|
+
// Create service that returns errors for decrypt
|
|
346
|
+
const service = createMockKeyManagerService({
|
|
347
|
+
mockPublicKey: 'mock-key',
|
|
348
|
+
})
|
|
349
|
+
// Override decrypt service to return error
|
|
350
|
+
service.space.encryption.key.decrypt = Server.provide(
|
|
351
|
+
Space.EncryptionKeyDecrypt,
|
|
352
|
+
async () => {
|
|
353
|
+
return Server.error({
|
|
354
|
+
name: 'KeyNotFound',
|
|
355
|
+
message: 'KMS key not found',
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
const keyManagerServiceServer = await createMockKeyManagerServer(
|
|
360
|
+
service,
|
|
361
|
+
keyManagerServiceDID,
|
|
362
|
+
5557
|
|
363
|
+
)
|
|
364
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
365
|
+
const adapter = new KMSCryptoAdapter(
|
|
366
|
+
symmetricCrypto,
|
|
367
|
+
keyManagerServiceServer.url,
|
|
368
|
+
keyManagerServiceDID.did(),
|
|
369
|
+
{ allowInsecureHttp: true } // Allow HTTP for testing
|
|
370
|
+
)
|
|
371
|
+
const decryptionOptions = {
|
|
372
|
+
spaceDID,
|
|
373
|
+
decryptDelegation: delegationProof,
|
|
374
|
+
}
|
|
375
|
+
const mockKey = new Uint8Array([1, 2, 3]) // test value as bytes
|
|
376
|
+
const mockKeyString = base64.encode(mockKey)
|
|
377
|
+
const mockMetadata = {
|
|
378
|
+
strategy: /** @type {'kms'} */ ('kms'),
|
|
379
|
+
encryptedDataCID: 'bafybeid',
|
|
380
|
+
key: mockKey, // use bytes, not string
|
|
381
|
+
space: spaceDID,
|
|
382
|
+
kms: {
|
|
383
|
+
provider: /** @type {'google-kms'} */ ('google-kms'),
|
|
384
|
+
keyId: 'test-key',
|
|
385
|
+
algorithm: /** @type {'RSA-OAEP-2048-SHA256'} */ (
|
|
386
|
+
'RSA-OAEP-2048-SHA256'
|
|
387
|
+
),
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
const decryptConfigs = /** @type {any} */ ({
|
|
391
|
+
decryptionConfig: decryptionOptions,
|
|
392
|
+
metadata: mockMetadata,
|
|
393
|
+
delegationCAR: new Uint8Array(),
|
|
394
|
+
resourceCID: 'bafybeid',
|
|
395
|
+
issuer,
|
|
396
|
+
audience: keyManagerServiceDID.did(),
|
|
397
|
+
})
|
|
398
|
+
try {
|
|
399
|
+
// Should throw error
|
|
400
|
+
await assert.rejects(
|
|
401
|
+
() => adapter.decryptSymmetricKey(mockKeyString, decryptConfigs),
|
|
402
|
+
/KMS key not found/
|
|
403
|
+
)
|
|
404
|
+
} finally {
|
|
405
|
+
await keyManagerServiceServer.close()
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
await describe('Validation Tests', async () => {
|
|
410
|
+
await test('should validate required decryption parameters', async () => {
|
|
411
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
412
|
+
const adapter = new KMSCryptoAdapter(
|
|
413
|
+
symmetricCrypto,
|
|
414
|
+
'https://mock-gateway.example.com',
|
|
415
|
+
'did:web:mock'
|
|
416
|
+
)
|
|
417
|
+
const invalidConfigs = /** @type {any} */ ({
|
|
418
|
+
decryptionConfig: {}, // Missing spaceDID and decryptDelegation
|
|
419
|
+
metadata: { strategy: 'kms' },
|
|
420
|
+
delegationCAR: new Uint8Array(),
|
|
421
|
+
resourceCID: 'bafybeid',
|
|
422
|
+
issuer: null,
|
|
423
|
+
audience: 'did:web:mock',
|
|
424
|
+
})
|
|
425
|
+
await assert.rejects(
|
|
426
|
+
() => adapter.decryptSymmetricKey('key', invalidConfigs),
|
|
427
|
+
/SpaceDID and decryptDelegation are required/
|
|
428
|
+
)
|
|
429
|
+
})
|
|
430
|
+
await test('should validate issuer is provided', async () => {
|
|
431
|
+
const fixtures = await createTestFixtures()
|
|
432
|
+
const { spaceDID, delegationProof } = fixtures
|
|
433
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
434
|
+
const adapter = new KMSCryptoAdapter(
|
|
435
|
+
symmetricCrypto,
|
|
436
|
+
'https://mock-gateway.example.com',
|
|
437
|
+
'did:web:mock'
|
|
438
|
+
)
|
|
439
|
+
const invalidConfigs = /** @type {any} */ ({
|
|
440
|
+
decryptionConfig: { spaceDID, decryptDelegation: delegationProof },
|
|
441
|
+
metadata: { strategy: 'kms' },
|
|
442
|
+
delegationCAR: new Uint8Array(),
|
|
443
|
+
resourceCID: 'bafybeid',
|
|
444
|
+
issuer: null, // Missing issuer
|
|
445
|
+
audience: 'did:web:mock',
|
|
446
|
+
})
|
|
447
|
+
await assert.rejects(
|
|
448
|
+
() => adapter.decryptSymmetricKey('key', invalidConfigs),
|
|
449
|
+
/Issuer is required/
|
|
450
|
+
)
|
|
451
|
+
})
|
|
452
|
+
await test('should reject non-KMS metadata', async () => {
|
|
453
|
+
const symmetricCrypto = new GenericAesCtrStreamingCrypto()
|
|
454
|
+
const adapter = new KMSCryptoAdapter(
|
|
455
|
+
symmetricCrypto,
|
|
456
|
+
'https://mock-gateway.example.com',
|
|
457
|
+
'did:web:mock'
|
|
458
|
+
)
|
|
459
|
+
const invalidConfigs = /** @type {any} */ ({
|
|
460
|
+
decryptionOptions: { spaceDID: 'did:key:test', delegationProof: {} },
|
|
461
|
+
metadata: { strategy: 'lit' }, // Wrong strategy
|
|
462
|
+
delegationCAR: new Uint8Array(),
|
|
463
|
+
resourceCID: 'bafybeid',
|
|
464
|
+
issuer: {},
|
|
465
|
+
audience: 'did:web:mock',
|
|
466
|
+
})
|
|
467
|
+
await assert.rejects(
|
|
468
|
+
() => adapter.decryptSymmetricKey('key', invalidConfigs),
|
|
469
|
+
/KMSCryptoAdapter can only handle KMS metadata/
|
|
470
|
+
)
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
})
|
|
474
|
+
//# sourceMappingURL=kms-crypto-adapter.spec.js.map
|