Skip to content

Commit 6f94c29

Browse files
authored
Merge branch 'master' into sequence-number-order
2 parents cdcc2f0 + d7b5e73 commit 6f94c29

32 files changed

+714
-231
lines changed

modules/decrypt-node/src/decipher_stream.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
// @ts-ignore
1717
import { Transform as PortableTransform } from 'readable-stream'
1818
import { Transform } from 'stream' // eslint-disable-line no-unused-vars
19-
import { DecipherGCM } from 'crypto' // eslint-disable-line no-unused-vars
20-
import { needs } from '@aws-crypto/material-management-node'
19+
import {
20+
needs,
21+
GetDecipher, // eslint-disable-line no-unused-vars
22+
AwsEsdkJsDecipherGCM // eslint-disable-line no-unused-vars
23+
} from '@aws-crypto/material-management-node'
2124
import {
2225
aadFactory,
2326
ContentType // eslint-disable-line no-unused-vars
@@ -31,12 +34,12 @@ const PortableTransformWithType = (<new (...args: any[]) => Transform>PortableTr
3134
export interface DecipherInfo {
3235
messageId: Buffer
3336
contentType: ContentType
34-
getDecipher: (iv: Uint8Array) => DecipherGCM
37+
getDecipher: GetDecipher
3538
dispose: () => void
3639
}
3740

3841
interface DecipherState {
39-
decipher: DecipherGCM
42+
decipher: AwsEsdkJsDecipherGCM
4043
content: Buffer[]
4144
contentLength: number
4245
}

modules/decrypt-node/src/verify_stream.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
// @ts-ignore
1717
import { Transform as PortableTransform } from 'readable-stream'
1818
import { Transform } from 'stream' // eslint-disable-line no-unused-vars
19-
import { DecipherGCM } from 'crypto' // eslint-disable-line no-unused-vars
2019
import {
2120
needs,
22-
GetVerify // eslint-disable-line no-unused-vars
21+
GetVerify, // eslint-disable-line no-unused-vars
22+
GetDecipher // eslint-disable-line no-unused-vars
2323
} from '@aws-crypto/material-management-node'
2424
import {
2525
deserializeSignature,
@@ -35,7 +35,7 @@ const PortableTransformWithType = (<new (...args: any[]) => Transform>PortableTr
3535

3636
export interface VerifyInfo {
3737
headerInfo: HeaderInfo
38-
getDecipher: (iv: Uint8Array) => DecipherGCM
38+
getDecipher: GetDecipher
3939
dispose: () => void
4040
verify?: AWSVerify
4141
}

modules/encrypt-browser/src/encrypt.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
AlgorithmSuiteIdentifier,
2222
getEncryptHelper,
2323
KeyringWebCrypto,
24+
needs,
2425
WebCryptoMaterialsManager // eslint-disable-line no-unused-vars
2526
} from '@aws-crypto/material-management-browser'
2627
import {
@@ -35,7 +36,8 @@ import {
3536
serializeSignatureInfo,
3637
FRAME_LENGTH,
3738
MESSAGE_ID_LENGTH,
38-
raw2der
39+
raw2der,
40+
Maximum
3941
} from '@aws-crypto/serialize'
4042
import { fromUtf8 } from '@aws-sdk/util-utf8-browser'
4143
import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend'
@@ -60,6 +62,9 @@ export async function encrypt (
6062
plaintext: Uint8Array,
6163
{ suiteId, encryptionContext, frameLength = FRAME_LENGTH }: EncryptInput = {}
6264
): Promise<EncryptResult> {
65+
/* Precondition: The frameLength must be less than the maximum frame size for browser encryption. */
66+
needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`)
67+
6368
const backend = await getWebCryptoBackend()
6469
if (!backend) throw new Error('No supported crypto backend')
6570

modules/encrypt-browser/test/encrypt.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,9 @@ describe('encrypt structural testing', () => {
8585

8686
expect(messageHeader).to.deep.equal(messageInfo.messageHeader)
8787
})
88+
89+
it('Precondition: The frameLength must be less than the maximum frame size for browser encryption.', async () => {
90+
const frameLength = 0
91+
expect(encrypt(keyRing, 'asdf', { frameLength })).to.rejectedWith(Error)
92+
})
8893
})

modules/encrypt-node/src/encrypt_stream.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
import {
1717
NodeDefaultCryptographicMaterialsManager, NodeAlgorithmSuite, AlgorithmSuiteIdentifier, // eslint-disable-line no-unused-vars
1818
KeyringNode, NodeEncryptionMaterial, getEncryptHelper, EncryptionContext, // eslint-disable-line no-unused-vars
19-
NodeMaterialsManager // eslint-disable-line no-unused-vars
19+
NodeMaterialsManager, // eslint-disable-line no-unused-vars
20+
needs
2021
} from '@aws-crypto/material-management-node'
2122
import { getFramedEncryptStream } from './framed_encrypt_stream'
2223
import { SignatureStream } from './signature_stream'
@@ -26,7 +27,8 @@ import {
2627
MessageHeader, // eslint-disable-line no-unused-vars
2728
serializeFactory, kdfInfo, ContentType, SerializationVersion, ObjectType,
2829
FRAME_LENGTH,
29-
MESSAGE_ID_LENGTH
30+
MESSAGE_ID_LENGTH,
31+
Maximum
3032
} from '@aws-crypto/serialize'
3133

3234
// @ts-ignore
@@ -56,6 +58,9 @@ export function encryptStream (
5658
): Duplex {
5759
const { suiteId, context, frameLength = FRAME_LENGTH } = op
5860

61+
/* Precondition: The frameLength must be less than the maximum frame size Node.js stream. */
62+
needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`)
63+
5964
/* If the cmm is a Keyring, wrap it with NodeDefaultCryptographicMaterialsManager. */
6065
cmm = cmm instanceof KeyringNode
6166
? new NodeDefaultCryptographicMaterialsManager(cmm)

modules/encrypt-node/src/framed_encrypt_stream.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ import {
1919
} from '@aws-crypto/serialize'
2020
// @ts-ignore
2121
import { Transform as PortableTransform } from 'readable-stream'
22-
import { CipherGCM } from 'crypto' // eslint-disable-line no-unused-vars
2322
import { Transform } from 'stream' // eslint-disable-line no-unused-vars
24-
import { needs } from '@aws-crypto/material-management-node'
23+
import {
24+
GetCipher, // eslint-disable-line no-unused-vars
25+
AwsEsdkJsCipherGCM, // eslint-disable-line no-unused-vars
26+
needs
27+
} from '@aws-crypto/material-management-node'
2528

2629
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
2730
const serialize = serializeFactory(fromUtf8)
@@ -38,7 +41,7 @@ interface EncryptFrame {
3841
content: Buffer[]
3942
bodyHeader: Buffer
4043
headerSent?: boolean
41-
cipher: CipherGCM,
44+
cipher: AwsEsdkJsCipherGCM,
4245
isFinalFrame: boolean
4346
}
4447

@@ -165,8 +168,6 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes
165168
})()
166169
}
167170

168-
type GetCipher = (iv: Uint8Array) => CipherGCM
169-
170171
type EncryptFrameInput = {
171172
pendingFrame: AccumulatingFrame,
172173
messageHeader: MessageHeader,

modules/encrypt-node/test/encrypt.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ describe('encrypt structural testing', () => {
184184

185185
expect(messageHeader).to.deep.equal(messageInfo.messageHeader)
186186
})
187+
188+
it('Precondition: The frameLength must be less than the maximum frame size Node.js stream.', async () => {
189+
const frameLength = 0
190+
expect(encrypt(keyRing, 'asdf', { frameLength })).to.rejectedWith(Error)
191+
})
187192
})
188193

189194
function finishedAsync (stream: any) {

modules/integration-node/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
"license": "Apache-2.0",
1616
"dependencies": {
1717
"@aws-crypto/client-node": "^0.1.0-preview.2",
18+
"@types/got": "^9.6.2",
1819
"@types/unzipper": "^0.9.1",
1920
"@types/yargs": "^13.0.0",
21+
"got": "^9.6.0",
2022
"tslib": "^1.9.3",
2123
"unzipper": "^0.9.11",
2224
"yargs": "^13.2.2"

modules/integration-node/src/cli.ts

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,37 @@
1515
*/
1616

1717
import yargs from 'yargs'
18-
import { integrationTestVectors } from './integration_tests'
18+
import { integrationDecryptTestVectors, integrationEncryptTestVectors } from './integration_tests'
1919

20-
const argv = yargs
21-
.option('vectorFile', {
22-
alias: 'v',
23-
describe: 'a vector zip file from aws-encryption-sdk-test-vectors',
24-
demandOption: true,
25-
type: 'string'
26-
})
20+
const cli = yargs
21+
.command('decrypt', 'verify decrypt vectors', y => y
22+
.option('vectorFile', {
23+
alias: 'v',
24+
describe: 'a vector zip file from aws-encryption-sdk-test-vectors',
25+
demandOption: true,
26+
type: 'string'
27+
})
28+
)
29+
.command('encrypt', 'verify encrypt manifest', y => y
30+
.option('manifestFile', {
31+
alias: 'm',
32+
describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest',
33+
demandOption: true,
34+
type: 'string'
35+
})
36+
.option('keyFile', {
37+
alias: 'k',
38+
describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical key list',
39+
demandOption: true,
40+
type: 'string'
41+
})
42+
.option('decryptOracle', {
43+
alias: 'o',
44+
describe: 'a url to the decrypt oracle',
45+
demandOption: true,
46+
type: 'string'
47+
})
48+
)
2749
.option('tolerateFailures', {
2850
alias: 'f',
2951
describe: 'an optional number of failures to tolerate before exiting',
@@ -35,7 +57,24 @@ const argv = yargs
3557
describe: 'an optional test name to execute',
3658
type: 'string'
3759
})
38-
.argv
60+
.demandCommand()
61+
62+
;(async (argv) => {
63+
const { _: [ command ], tolerateFailures, testName } = argv
64+
/* I set the result to 1 so that if I fall through the exit condition is a failure */
65+
let result = 1
66+
if (command === 'decrypt') {
67+
const { vectorFile } = argv
68+
// @ts-ignore
69+
result = await integrationDecryptTestVectors(vectorFile, tolerateFailures, testName)
70+
} else if (command === 'encrypt') {
71+
const { manifestFile, keyFile, decryptOracle } = argv
72+
// @ts-ignore
73+
result = await integrationEncryptTestVectors(manifestFile, keyFile, decryptOracle, tolerateFailures, testName)
74+
} else {
75+
console.log(`Unknown command ${command}`)
76+
cli.showHelp()
77+
}
3978

40-
const { vectorFile, tolerateFailures, testName } = argv
41-
integrationTestVectors(vectorFile, tolerateFailures, testName)
79+
if (result) process.exit(result)
80+
})(cli.argv)

modules/integration-node/src/decrypt_materials_manager_node.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ const Bits2RawAesWrappingSuiteIdentifier: {[key: number]: WrappingSuiteIdentifie
3939
256: RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING
4040
}
4141

42+
export function encryptMaterialsManagerNode (keyInfos: KeyInfoTuple[]) {
43+
const [generator, ...children] = keyInfos.map(keyringNode)
44+
return new MultiKeyringNode({ generator, children })
45+
}
46+
4247
export function decryptMaterialsManagerNode (keyInfos: KeyInfoTuple[]) {
4348
const children = keyInfos.map(keyringNode)
4449
return new MultiKeyringNode({ children })
@@ -58,8 +63,8 @@ function keyringNode ([ info, key ]: KeyInfoTuple) {
5863
}
5964

6065
function kmsKeyring (_keyInfo: KmsKeyInfo, key: KMSKey) {
61-
const keyIds = [key['key-id']]
62-
return new KmsKeyringNode({ keyIds })
66+
const generatorKeyId = key['key-id']
67+
return new KmsKeyringNode({ generatorKeyId })
6368
}
6469

6570
function aesKeyring (keyInfo:AesKeyInfo, key: AESKey) {

modules/integration-node/src/get_test_iterator.ts renamed to modules/integration-node/src/get_decrypt_test_iterator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515

1616
import { Open } from 'unzipper'
1717
import {
18-
ManifestList, // eslint-disable-line no-unused-vars
18+
DecryptManifestList, // eslint-disable-line no-unused-vars
1919
KeyList, // eslint-disable-line no-unused-vars
2020
KeyInfoTuple // eslint-disable-line no-unused-vars
2121
} from './types'
2222
import { Readable } from 'stream' // eslint-disable-line no-unused-vars
2323

24-
export async function getTestVectorIterator (vectorFile: string) {
24+
export async function getDecryptTestVectorIterator (vectorFile: string) {
2525
const centralDirectory = await Open.file(vectorFile)
2626
// @ts-ignore
2727
const filesMap = new Map(centralDirectory.files.map(file => [file.path, file]))
@@ -40,7 +40,7 @@ export async function getTestVectorIterator (vectorFile: string) {
4040
})()
4141

4242
const manifestBuffer = await readUriOnce('manifest.json')
43-
const { keys: keysFile, tests }: ManifestList = JSON.parse(manifestBuffer.toString('utf8'))
43+
const { keys: keysFile, tests }: DecryptManifestList = JSON.parse(manifestBuffer.toString('utf8'))
4444
const keysBuffer = await readUriOnce(keysFile)
4545
const { keys }: KeyList = JSON.parse(keysBuffer.toString('utf8'))
4646

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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-node'
26+
import { URL } from 'url'
27+
import { readFileSync } from 'fs'
28+
import got from 'got'
29+
30+
export async function getEncryptTestVectorIterator (manifestFile: string, keyFile: string) {
31+
const { tests, plaintexts }: EncryptManifestList = await getParsedJSON(manifestFile)
32+
const { keys }: KeyList = await getParsedJSON(keyFile)
33+
34+
const plaintextBytes: {[name: string]: Buffer} = {}
35+
36+
Object
37+
.keys(plaintexts)
38+
.forEach(name => {
39+
plaintextBytes[name] = randomBytes(plaintexts[name])
40+
})
41+
42+
return (function * nextTest (): IterableIterator<EncryptTestVectorInfo> {
43+
for (const [name, testInfo] of Object.entries(tests)) {
44+
const {
45+
plaintext,
46+
'master-keys': masterKeys,
47+
algorithm,
48+
'frame-size': frameLength,
49+
'encryption-context': encryptionContext
50+
} = testInfo
51+
const keysInfo = <KeyInfoTuple[]>masterKeys.map(keyInfo => {
52+
const key = keys[keyInfo.key]
53+
if (!key) throw new Error(`no key for ${name}`)
54+
return [keyInfo, key]
55+
})
56+
57+
/* I'm expecting that the encrypt function will throw if this is not a supported AlgorithmSuiteIdentifier */
58+
const suiteId = <AlgorithmSuiteIdentifier>parseInt(algorithm, 16)
59+
60+
yield {
61+
name,
62+
keysInfo,
63+
plainTextData: plaintextBytes[plaintext],
64+
encryptOp: { suiteId, frameLength, encryptionContext }
65+
}
66+
}
67+
})()
68+
}
69+
70+
export interface EncryptTestVectorInfo {
71+
name: string,
72+
keysInfo: KeyInfoTuple[],
73+
plainTextData: Buffer,
74+
encryptOp: {
75+
suiteId: AlgorithmSuiteIdentifier,
76+
frameLength: number,
77+
encryptionContext: EncryptionContext
78+
}
79+
}
80+
81+
async function getParsedJSON (thing: string) {
82+
try {
83+
const url = new URL(thing)
84+
if (url.protocol === 'file:') {
85+
return jsonAtPath(thing)
86+
} else {
87+
return jsonAtUrl(url)
88+
}
89+
} catch (ex) {
90+
return jsonAtPath(thing)
91+
}
92+
}
93+
async function jsonAtUrl (url: URL) {
94+
const { body } = await got(url)
95+
return JSON.parse(body)
96+
}
97+
98+
function jsonAtPath (path: string) {
99+
const json = readFileSync(path, { encoding: 'utf-8' })
100+
return JSON.parse(json)
101+
}

0 commit comments

Comments
 (0)