6
6
TestVectorResult ,
7
7
parseIntegrationTestVectorsToTestVectorIterator ,
8
8
PositiveTestVectorInfo ,
9
+ DecryptManifestList ,
9
10
} from '@aws-crypto/integration-vectors'
10
11
import {
11
12
EncryptTestVectorInfo ,
@@ -28,6 +29,9 @@ import streamToPromise from 'stream-to-promise'
28
29
const { encrypt, decrypt, decryptUnsignedMessageStream } = buildClient (
29
30
CommitmentPolicy . FORBID_ENCRYPT_ALLOW_DECRYPT
30
31
)
32
+ import { ZipFile } from 'yazl'
33
+ import { createWriteStream } from 'fs'
34
+ import { v4 } from 'uuid'
31
35
import * as stream from 'stream'
32
36
import * as util from 'util'
33
37
const pipeline = util . promisify ( stream . pipeline )
@@ -111,11 +115,28 @@ async function testPositiveDecryptVector(
111
115
}
112
116
}
113
117
114
- // This is only viable for small streams, if we start get get larger streams, an stream equality should get written
118
+ interface ProcessEncryptResults {
119
+ handleEncryptResult : HandleEncryptResult
120
+ // We need to have a done step to close the ZipFile.
121
+ done ( ) : void
122
+ // The handleEncryptResult needs the ZipFile
123
+ // so that it can add ciphertexts and tests.
124
+ // But when we set up the encrypt manifest,
125
+ // we create plaintext files and have the keys manifest.
126
+ // This is a quick and dirty way to share the ZipFile
127
+ // between these two places.
128
+ manifestZip ?: ZipFile
129
+ }
130
+
131
+ interface HandleEncryptResult {
132
+ ( encryptResult : Buffer , info : EncryptTestVectorInfo ) : Promise < boolean >
133
+ }
134
+
115
135
export async function testEncryptVector (
116
- { name , keysInfo , encryptOp , plainTextData } : EncryptTestVectorInfo ,
117
- decryptOracle : string
136
+ info : EncryptTestVectorInfo ,
137
+ handleEncryptResult : HandleEncryptResult
118
138
) : Promise < TestVectorResult > {
139
+ const { name, keysInfo, encryptOp, plainTextData } = info
119
140
try {
120
141
const cmm = encryptMaterialsManagerNode ( keysInfo )
121
142
const { result : encryptResult } = await encrypt (
@@ -124,7 +145,68 @@ export async function testEncryptVector(
124
145
encryptOp
125
146
)
126
147
127
- const decryptResponse = await got . post ( decryptOracle , {
148
+ const result = await handleEncryptResult ( encryptResult , info )
149
+ return { result, name }
150
+ } catch ( err ) {
151
+ return { result : false , name, err }
152
+ }
153
+ }
154
+
155
+ // This isolates the logic on how to do both.
156
+ // Right now we only have 2 ways to handle results
157
+ // so this seems reasonable.
158
+ function composeEncryptResults (
159
+ decryptOracle ?: string ,
160
+ decryptManifest ?: string
161
+ ) : ProcessEncryptResults {
162
+ if ( ! ! decryptOracle && ! ! decryptManifest ) {
163
+ const oracle = decryptOracleEncryptResults ( decryptOracle )
164
+ const manifest = decryptionManifestEncryptResults ( decryptManifest )
165
+
166
+ return {
167
+ done ( ) {
168
+ manifest . done ( )
169
+ oracle . done ( )
170
+ } ,
171
+
172
+ async handleEncryptResult (
173
+ encryptResult : Buffer ,
174
+ info : EncryptTestVectorInfo
175
+ ) : Promise < boolean > {
176
+ return Promise . all ( [
177
+ oracle . handleEncryptResult ( encryptResult , info ) ,
178
+ manifest . handleEncryptResult ( encryptResult , info ) ,
179
+ ] ) . then ( ( results ) => {
180
+ const [ oracleResult , manifestResult ] = results
181
+ return oracleResult && manifestResult
182
+ } )
183
+ } ,
184
+ manifestZip : manifest . manifestZip ,
185
+ }
186
+ } else if ( ! ! decryptOracle ) {
187
+ return decryptOracleEncryptResults ( decryptOracle )
188
+ } else if ( ! ! decryptManifest ) {
189
+ return decryptionManifestEncryptResults ( decryptManifest )
190
+ }
191
+ needs ( false , 'unsupported' )
192
+ }
193
+
194
+ function decryptOracleEncryptResults (
195
+ decryptOracle : string
196
+ ) : ProcessEncryptResults {
197
+ const decryptOracleUrl = new URL ( decryptOracle ) . toString ( )
198
+ return {
199
+ handleEncryptResult,
200
+ // There is nothing to do when the oracle is done
201
+ // since nothing is saved.
202
+ done : ( ) => { } ,
203
+ }
204
+
205
+ async function handleEncryptResult (
206
+ encryptResult : Buffer ,
207
+ info : EncryptTestVectorInfo
208
+ ) : Promise < boolean > {
209
+ const decryptResponse = await got . post ( decryptOracleUrl , {
128
210
headers : {
129
211
'Content-Type' : 'application/octet-stream' ,
130
212
Accept : 'application/octet-stream' ,
@@ -134,10 +216,69 @@ export async function testEncryptVector(
134
216
} )
135
217
needs ( decryptResponse . statusCode === 200 , 'decrypt failure' )
136
218
const { body } = decryptResponse
137
- const result = plainTextData . equals ( body )
138
- return { result, name }
139
- } catch ( err ) {
140
- return { result : false , name, err }
219
+ // This is only viable for small streams,
220
+ // if we start get get larger streams,
221
+ // a stream equality should get written
222
+ return info . plainTextData . equals ( body )
223
+ }
224
+ }
225
+
226
+ function decryptionManifestEncryptResults (
227
+ manifestPath : string
228
+ ) : ProcessEncryptResults {
229
+ const manifestZip = new ZipFile ( )
230
+ const manifest : DecryptManifestList = {
231
+ manifest : {
232
+ type : 'awses-decrypt' ,
233
+ version : 1 ,
234
+ } ,
235
+ client : {
236
+ name : 'aws/aws-encryption-sdk-javascript' ,
237
+ //Get the right version
238
+ version : '2.2.0' ,
239
+ } ,
240
+ keys : 'file://keys.json' ,
241
+ tests : { } ,
242
+ }
243
+ manifestZip . outputStream . pipe ( createWriteStream ( manifestPath ) )
244
+
245
+ return {
246
+ handleEncryptResult,
247
+ done : ( ) => {
248
+ // All the tests have completed,
249
+ // so we write the manifest,
250
+ // as close the zip file.
251
+ manifestZip . addBuffer (
252
+ Buffer . from ( JSON . stringify ( manifest ) ) ,
253
+ `manifest.json`
254
+ )
255
+ manifestZip . end ( )
256
+ } ,
257
+ manifestZip,
258
+ }
259
+
260
+ async function handleEncryptResult (
261
+ encryptResult : Buffer ,
262
+ info : EncryptTestVectorInfo
263
+ ) : Promise < boolean > {
264
+ const testName = v4 ( )
265
+
266
+ manifestZip . addBuffer ( encryptResult , `ciphertexts/${ testName } ` )
267
+
268
+ manifest . tests [ testName ] = {
269
+ description : `Decrypt vector from ${ info . name } ` ,
270
+ ciphertext : `file://ciphertexts/${ testName } ` ,
271
+ 'master-keys' : info . keysInfo . map ( ( info ) => info [ 0 ] ) ,
272
+ result : {
273
+ output : {
274
+ plaintext : `file://plaintexts/${ info . plaintextName } ` ,
275
+ } ,
276
+ } ,
277
+ }
278
+
279
+ // These files are tested on decrypt
280
+ // so there is nothing to test at this point.
281
+ return true
141
282
}
142
283
}
143
284
@@ -196,22 +337,42 @@ export async function integrationDecryptTestVectors(
196
337
export async function integrationEncryptTestVectors (
197
338
manifestFile : string ,
198
339
keyFile : string ,
199
- decryptOracle : string ,
340
+ decryptOracle ?: string ,
341
+ decryptManifest ?: string ,
200
342
tolerateFailures = 0 ,
201
343
testName ?: string ,
202
344
concurrency = 1
203
345
) : Promise < number > {
204
- const decryptOracleUrl = new URL ( decryptOracle ) . toString ( )
205
- const tests = await getEncryptTestVectorIterator ( manifestFile , keyFile )
346
+ needs (
347
+ ! ! decryptOracle || ! ! decryptManifest ,
348
+ 'Need to pass an oracle or manifest path.'
349
+ )
206
350
207
- return parallelTests ( concurrency , tolerateFailures , runTest , tests )
351
+ const { done, handleEncryptResult, manifestZip } = composeEncryptResults (
352
+ decryptOracle ,
353
+ decryptManifest
354
+ )
355
+
356
+ const tests = await getEncryptTestVectorIterator (
357
+ manifestFile ,
358
+ keyFile ,
359
+ manifestZip
360
+ )
361
+
362
+ return parallelTests ( concurrency , tolerateFailures , runTest , tests ) . then (
363
+ ( num ) => {
364
+ // Do the output processing here
365
+ done ( )
366
+ return num
367
+ }
368
+ )
208
369
209
370
async function runTest ( test : EncryptTestVectorInfo ) : Promise < boolean > {
210
371
if ( testName ) {
211
372
if ( test . name !== testName ) return true
212
373
}
213
374
return handleTestResults (
214
- await testEncryptVector ( test , decryptOracleUrl ) ,
375
+ await testEncryptVector ( test , handleEncryptResult ) ,
215
376
notSupportedEncryptMessages
216
377
)
217
378
}
@@ -248,8 +409,12 @@ async function parallelTests<
248
409
* we just process the value and ask for another.
249
410
* Which will return done as true again.
250
411
*/
251
- if ( ! value && done ) return _resolve ( failureCount )
252
-
412
+ if ( ! value && done ) {
413
+ // We are done enqueueing work,
414
+ // but we need to wait until all that work is done
415
+ Promise . all ( [ ...queue ] ) . then ( ( ) => _resolve ( failureCount ) )
416
+ return
417
+ }
253
418
/* I need to define the work to be enqueue
254
419
* and a way to dequeue this work when complete.
255
420
* A Set of promises works nicely.
0 commit comments