Skip to content

Commit 70255dc

Browse files
committed
feat: Encryption tests for integration-browser
https://github.com/awslabs/aws-crypto-tools-test-vector-framework defines an encryption manifest. This manifest should be used to maintain cross compatibility of language implementations. Then integrate this into the CI integration tests. Also update package.json to better organize parameters.
1 parent a2f2ed9 commit 70255dc

10 files changed

+371
-74
lines changed

modules/integration-browser/karma.conf.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ module.exports = function (config) {
66
basePath: '',
77
frameworks: ['jasmine'],
88
files: [
9-
'fixtures/tests.json',
9+
'fixtures/decrypt_tests.json',
10+
'fixtures/encrypt_tests.json',
11+
'fixtures/decrypt_oracle.json',
1012
{ pattern: 'fixtures/*.json', included: false, served: true, watched: false, nocache: true },
11-
'build/module/integration.test.js'
13+
'build/module/integration.decrypt.test.js',
14+
'build/module/integration.encrypt.test.js',
1215
],
1316
preprocessors: {
14-
'build/module/integration.test.js': ['webpack', 'credentials'],
15-
'./fixtures/tests.json': ['json_fixtures']
17+
'build/module/integration.decrypt.test.js': ['webpack', 'credentials'],
18+
'build/module/integration.encrypt.test.js': ['webpack', 'credentials'],
19+
'./fixtures/decrypt_tests.json': ['json_fixtures'],
20+
'./fixtures/encrypt_tests.json': ['json_fixtures'],
21+
'./fixtures/decrypt_oracle.json': ['json_fixtures']
22+
1623
},
1724
webpack: {
1825
mode: 'development',

modules/integration-browser/package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,25 @@
1818
"@aws-crypto/client-browser": "^0.1.0-preview.2",
1919
"@aws-sdk/karma-credential-loader": "0.1.0-preview.2",
2020
"@aws-sdk/util-base64-browser": "0.1.0-preview.1",
21+
"@aws-sdk/util-utf8-browser": "0.1.0-preview.1",
2122
"@trust/keyto": "^0.3.7",
23+
"@types/got": "^9.6.2",
24+
"@types/stream-to-promise": "^2.2.0",
2225
"@types/unzipper": "^0.9.1",
2326
"@types/yargs": "^13.0.0",
24-
"stream-to-promise": "^2.2.0",
25-
"tslib": "^1.9.3",
26-
"unzipper": "^0.9.11",
27-
"yargs": "^13.2.2",
27+
"got": "^9.6.0",
2828
"jasmine-core": "^3.4.0",
2929
"karma": "^4.1.0",
3030
"karma-chrome-launcher": "^2.2.0",
3131
"karma-jasmine": "^2.0.1",
3232
"karma-json-fixtures-preprocessor": "0.0.6",
3333
"karma-webpack": "^3.0.5",
34+
"puppeteer": "^1.14.0",
35+
"stream-to-promise": "^2.2.0",
36+
"tslib": "^1.9.3",
37+
"unzipper": "^0.9.11",
3438
"webpack": "^4.30.0",
35-
"puppeteer": "^1.14.0"
39+
"yargs": "^13.2.2"
3640
},
3741
"devDependencies": {
3842
"@types/node": "^11.11.4",
@@ -46,10 +50,9 @@
4650
"main": "./build/main/index.js",
4751
"module": "./build/module/index.js",
4852
"types": "./build/main/index.d.ts",
49-
"bin": "./build_fixtures",
53+
"bin": "./build/main/cli.js",
5054
"files": [
5155
"build/**/*",
52-
"build_fixtures",
5356
"karma.conf.js"
5457
],
5558
"standard": {

modules/integration-browser/build_fixtures renamed to modules/integration-browser/src/build_decrypt_fixtures.ts

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17-
const argv = require('yargs')
18-
.option('vectorFile', {
19-
alias: 'v',
20-
describe: 'a vector zip file from aws-encryption-sdk-test-vectors',
21-
demandOption: true,
22-
type: 'string'
23-
})
24-
.option('testName', {
25-
alias: 't',
26-
describe: 'an optional test name to execute',
27-
type: 'string'
28-
})
29-
.option('slice', {
30-
alias: 's',
31-
describe: 'an optional range start:end e.g. 100:200',
32-
type: 'string'
33-
})
34-
.options('karma', {
35-
alias: 'k',
36-
describe: 'start karma and run the tests',
37-
type: 'boolean'
38-
})
39-
.argv
40-
41-
const { vectorFile, testName, slice, karma } = argv
42-
const {Open} = require('unzipper')
43-
const streamToPromise = require('stream-to-promise')
44-
const fs = require('fs')
45-
const path = require('path')
46-
const { spawnSync } = require('child_process')
47-
const fixtures = path.join(__dirname, './fixtures')
48-
49-
const [start=0, end=9999] = (slice || '').split(':').map(n => parseInt(n, 10))
50-
51-
if (!fs.existsSync(fixtures)){
52-
fs.mkdirSync(fixtures)
53-
}
17+
import { Open } from 'unzipper'
18+
import streamToPromise from 'stream-to-promise'
19+
import { writeFileSync } from 'fs'
20+
21+
import { DecryptManifestList } from './types' // eslint-disable-line no-unused-vars
22+
23+
export async function buildDecryptFixtures (fixtures: string, vectorFile: string, testName: string, slice: string) {
24+
const [start = 0, end = 9999] = (slice || '').split(':').map(n => parseInt(n, 10))
5425

55-
;(async () => {
5626
const centralDirectory = await Open.file(vectorFile)
5727
const filesMap = new Map(centralDirectory.files.map(file => [file.path, file]))
5828

5929
const readUriOnce = (() => {
6030
const cache = new Map()
61-
return async (uri) => {
31+
return async (uri: string) => {
6232
const has = cache.get(uri)
6333
if (has) return has
6434
const fileInfo = filesMap.get(testUri2Path(uri))
@@ -70,7 +40,7 @@ if (!fs.existsSync(fixtures)){
7040
})()
7141

7242
const manifestBuffer = await readUriOnce('manifest.json')
73-
const { keys: keysFile, tests } = JSON.parse(manifestBuffer.toString('utf8'))
43+
const { keys: keysFile, tests }: DecryptManifestList = JSON.parse(manifestBuffer.toString('utf8'))
7444
const keysBuffer = await readUriOnce(keysFile)
7545
const { keys } = JSON.parse(keysBuffer.toString('utf8'))
7646
const testNames = []
@@ -94,8 +64,8 @@ if (!fs.existsSync(fixtures)){
9464
const plainTextInfo = filesMap.get(testUri2Path(plaintextFile))
9565
const cipherInfo = filesMap.get(testUri2Path(ciphertext))
9666
if (!cipherInfo || !plainTextInfo) throw new Error(`no file for ${name}: ${ciphertext} | ${plaintextFile}`)
97-
98-
const cipherText = await streamToPromise(cipherInfo.stream())
67+
68+
const cipherText = await streamToPromise(<NodeJS.ReadableStream>cipherInfo.stream())
9969
const plainText = await readUriOnce(plainTextInfo.path)
10070
const keysInfo = masterKeys.map(keyInfo => {
10171
const key = keys[keyInfo.key]
@@ -111,19 +81,12 @@ if (!fs.existsSync(fixtures)){
11181
plainText: plainText.toString('base64')
11282
})
11383

114-
fs.writeFileSync(`${fixtures}/${name}.json`, test)
84+
writeFileSync(`${fixtures}/${name}.json`, test)
11585
}
11686

117-
fs.writeFileSync(`${fixtures}/tests.json`, JSON.stringify(testNames))
118-
119-
if (karma) {
120-
spawnSync('npm', ['run', 'karma'], {
121-
cwd: __dirname,
122-
stdio: 'inherit'
123-
})
124-
}
125-
})()
87+
writeFileSync(`${fixtures}/decrypt_tests.json`, JSON.stringify(testNames))
88+
}
12689

127-
function testUri2Path (uri) {
90+
function testUri2Path (uri: string) {
12891
return uri.replace('file://', '')
12992
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. A copy of the License is
6+
* located at
7+
*
8+
* http://aws.amazon.com/apache2.0/
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
* implied. See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import {
17+
EncryptManifestList, // eslint-disable-line no-unused-vars
18+
KeyList, // eslint-disable-line no-unused-vars
19+
KeyInfoTuple // eslint-disable-line no-unused-vars
20+
} from './types'
21+
import { randomBytes } from 'crypto'
22+
import {
23+
AlgorithmSuiteIdentifier, // eslint-disable-line no-unused-vars
24+
EncryptionContext // eslint-disable-line no-unused-vars
25+
} from '@aws-crypto/client-browser'
26+
import { URL } from 'url'
27+
import { readFileSync, writeFileSync } from 'fs'
28+
import got from 'got'
29+
30+
export async function buildEncryptFixtures (fixtures: string, manifestFile: string, keyFile: string, testName: string, slice: string) {
31+
const [start = 0, end = 9999] = (slice || '').split(':').map(n => parseInt(n, 10))
32+
const { tests, plaintexts }: EncryptManifestList = await getParsedJSON(manifestFile)
33+
const { keys }: KeyList = await getParsedJSON(keyFile)
34+
35+
const plaintextBytes: {[name: string]: string} = {}
36+
37+
Object
38+
.keys(plaintexts)
39+
.forEach(name => {
40+
plaintextBytes[name] = randomBytes(10).toString('base64')
41+
})
42+
43+
const testNames = []
44+
let count = 0
45+
46+
for (const [name, testInfo] of Object.entries(tests)) {
47+
count += 1
48+
49+
if (testName) {
50+
if (name !== testName) continue
51+
}
52+
53+
if (slice) {
54+
if (start >= count) continue
55+
if (count > end) continue
56+
}
57+
58+
testNames.push(name)
59+
60+
const {
61+
plaintext,
62+
'master-keys': masterKeys,
63+
algorithm,
64+
'frame-size': frameLength,
65+
'encryption-context': encryptionContext
66+
} = testInfo
67+
68+
const keysInfo = <KeyInfoTuple[]>masterKeys.map(keyInfo => {
69+
const key = keys[keyInfo.key]
70+
if (!key) throw new Error(`no key for ${name}`)
71+
return [keyInfo, key]
72+
})
73+
74+
/* I'm expecting that the encrypt function will throw if this is not a supported AlgorithmSuiteIdentifier */
75+
const suiteId = <AlgorithmSuiteIdentifier>parseInt(algorithm, 16)
76+
77+
const test: EncryptTestVectorInfo = {
78+
name,
79+
keysInfo,
80+
plainTextData: plaintextBytes[plaintext],
81+
encryptOp: { suiteId, frameLength, encryptionContext }
82+
}
83+
84+
writeFileSync(`${fixtures}/${name}.json`, JSON.stringify(test))
85+
}
86+
87+
writeFileSync(`${fixtures}/encrypt_tests.json`, JSON.stringify(testNames))
88+
}
89+
90+
export interface EncryptTestVectorInfo {
91+
name: string,
92+
keysInfo: KeyInfoTuple[],
93+
plainTextData: string,
94+
encryptOp: {
95+
suiteId: AlgorithmSuiteIdentifier,
96+
frameLength: number,
97+
encryptionContext: EncryptionContext
98+
}
99+
}
100+
101+
async function getParsedJSON (thing: string) {
102+
try {
103+
const url = new URL(thing)
104+
if (url.protocol === 'file:') {
105+
return jsonAtPath(thing)
106+
} else {
107+
return jsonAtUrl(url)
108+
}
109+
} catch (ex) {
110+
return jsonAtPath(thing)
111+
}
112+
}
113+
async function jsonAtUrl (url: URL) {
114+
const { body } = await got(url)
115+
return JSON.parse(body)
116+
}
117+
118+
function jsonAtPath (path: string) {
119+
const json = readFileSync(path, { encoding: 'utf-8' })
120+
return JSON.parse(json)
121+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/env node
2+
/*
3+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
6+
* this file except in compliance with the License. A copy of the License is
7+
* located at
8+
*
9+
* http://aws.amazon.com/apache2.0/
10+
*
11+
* or in the "license" file accompanying this file. This file is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13+
* implied. See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import yargs from 'yargs'
18+
import { spawnSync } from 'child_process'
19+
20+
import { join } from 'path'
21+
import { existsSync, mkdirSync, writeFileSync } from 'fs'
22+
import { buildDecryptFixtures } from './build_decrypt_fixtures'
23+
import { buildEncryptFixtures } from './build_encrypt_fixtures'
24+
25+
const cli = yargs
26+
.command('decrypt', 'verify decrypt vectors', y => y
27+
.option('vectorFile', {
28+
alias: 'v',
29+
describe: 'a vector zip file from aws-encryption-sdk-test-vectors',
30+
demandOption: true,
31+
type: 'string'
32+
})
33+
)
34+
.command('encrypt', 'verify encrypt manifest', y => y
35+
.option('manifestFile', {
36+
alias: 'm',
37+
describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest',
38+
demandOption: true,
39+
type: 'string'
40+
})
41+
.option('keyFile', {
42+
alias: 'k',
43+
describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical key list',
44+
demandOption: true,
45+
type: 'string'
46+
})
47+
.option('decryptOracle', {
48+
alias: 'o',
49+
describe: 'a url to the decrypt oracle',
50+
demandOption: true,
51+
type: 'string'
52+
})
53+
)
54+
.option('testName', {
55+
alias: 't',
56+
describe: 'an optional test name to execute',
57+
type: 'string'
58+
})
59+
.option('slice', {
60+
alias: 's',
61+
describe: 'an optional range start:end e.g. 100:200',
62+
type: 'string'
63+
})
64+
.options('karma', {
65+
describe: 'start karma and run the tests',
66+
type: 'boolean'
67+
})
68+
.demandCommand()
69+
const fixtures = join(__dirname, '../../fixtures')
70+
/* Sad side effect. */
71+
if (!existsSync(fixtures)) {
72+
mkdirSync(fixtures)
73+
}
74+
75+
;(async (argv) => {
76+
const { _: [ command ], testName, slice, karma, decryptOracle = '' } = argv
77+
78+
writeFileSync(`${fixtures}/decrypt_tests.json`, JSON.stringify([]))
79+
writeFileSync(`${fixtures}/encrypt_tests.json`, JSON.stringify([]))
80+
writeFileSync(`${fixtures}/decrypt_oracle.json`, JSON.stringify(decryptOracle))
81+
82+
if (command === 'decrypt') {
83+
const { vectorFile } = argv
84+
// @ts-ignore
85+
await buildDecryptFixtures(fixtures, vectorFile, testName, slice)
86+
} else if (command === 'encrypt') {
87+
const { manifestFile, keyFile } = argv
88+
// @ts-ignore
89+
await buildEncryptFixtures(fixtures, manifestFile, keyFile, testName, slice)
90+
} else {
91+
console.log(`Unknown command ${command}`)
92+
cli.showHelp()
93+
}
94+
95+
if (karma) {
96+
spawnSync('npm', ['run', 'karma'], {
97+
cwd: __dirname,
98+
stdio: 'inherit'
99+
})
100+
}
101+
})(cli.argv)

0 commit comments

Comments
 (0)