Skip to content

fix: aws sdk version dependencies pollution #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/kms-keyring-node/src/kms_keyring_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ export class KmsKeyringNode extends KmsKeyringClass(KeyringNode as KeyRingConstr
}
immutableClass(KmsKeyringNode)

export { getKmsClient, cacheKmsClients, limitRegions, excludeRegions, cacheClients }
export { getKmsClient, cacheKmsClients, getClient, limitRegions, excludeRegions, cacheClients, KMS }
4 changes: 0 additions & 4 deletions modules/kms-keyring/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,15 @@
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/material-management": "^0.2.0-preview.1",
"@aws-sdk/types": "0.1.0-preview.1",
"tslib": "^1.9.3"
},
"devDependencies": {
"@aws-sdk/client-kms-browser": "^0.1.0-preview.2",
"@aws-sdk/client-kms-node": "^0.1.0-preview.2",
"@types/chai": "^4.1.4",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^5.2.5",
"@types/node": "^11.11.4",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"@typescript-eslint/parser": "^1.9.0",
"aws-sdk": "^2.443.0",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"mocha": "^5.2.0",
Expand Down
90 changes: 42 additions & 48 deletions modules/kms-keyring/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@

import { KmsClientSupplier } from './kms_client_supplier' // eslint-disable-line no-unused-vars
import {
KMS, // eslint-disable-line no-unused-vars
GenerateDataKeyInput, // eslint-disable-line no-unused-vars
EncryptInput, // eslint-disable-line no-unused-vars
DecryptInput, // eslint-disable-line no-unused-vars
GenerateDataKeyOutput, // eslint-disable-line no-unused-vars
RequiredGenerateDataKeyOutput, // eslint-disable-line no-unused-vars
EncryptOutput, // eslint-disable-line no-unused-vars
RequiredEncryptOutput, // eslint-disable-line no-unused-vars
DecryptOutput, // eslint-disable-line no-unused-vars
RequiredDecryptOutput // eslint-disable-line no-unused-vars
AwsEsdkKMSInterface, // eslint-disable-line no-unused-vars
GenerateDataKeyResponse, // eslint-disable-line no-unused-vars
RequiredGenerateDataKeyResponse, // eslint-disable-line no-unused-vars
EncryptResponse, // eslint-disable-line no-unused-vars
RequiredEncryptResponse, // eslint-disable-line no-unused-vars
DecryptResponse, // eslint-disable-line no-unused-vars
RequiredDecryptResponse // eslint-disable-line no-unused-vars
} from './kms_types'
import { regionFromKmsKeyArn } from './region_from_kms_key_arn'
import {
Expand All @@ -35,113 +32,110 @@ import {

export const KMS_PROVIDER_ID = 'aws-kms'

export async function generateDataKey<Client extends KMS> (
export async function generateDataKey<Client extends AwsEsdkKMSInterface> (
clientProvider: KmsClientSupplier<Client>,
NumberOfBytes: number,
KeyId: string,
EncryptionContext?: EncryptionContext,
GrantTokens?: string[]
): Promise<RequiredGenerateDataKeyOutput|false> {
): Promise<RequiredGenerateDataKeyResponse|false> {
const region = regionFromKmsKeyArn(KeyId)
const client = clientProvider(region)

/* Check for early return (Postcondition): Client region was not provided. */
if (!client) return false
const request: GenerateDataKeyInput<Client> = { KeyId, GrantTokens, NumberOfBytes, EncryptionContext } as GenerateDataKeyInput<Client>
// @ts-ignore
const v2vsV3Response = client.generateDataKey(request)
const v2vsV3Promise = (v2vsV3Response.promise ? v2vsV3Response.promise() : v2vsV3Response)
const dataKey: GenerateDataKeyOutput<Client> = await v2vsV3Promise
const v2vsV3Response = client.generateDataKey({ KeyId, GrantTokens, NumberOfBytes, EncryptionContext })
const v2vsV3Promise = 'promise' in v2vsV3Response
? v2vsV3Response.promise()
: v2vsV3Response
const dataKey = await v2vsV3Promise

return safeGenerateDataKey(dataKey)
}

export async function encrypt<Client extends KMS> (
export async function encrypt<Client extends AwsEsdkKMSInterface> (
clientProvider: KmsClientSupplier<Client>,
Plaintext: Uint8Array,
KeyId: string,
EncryptionContext?: EncryptionContext,
GrantTokens?: string[]
): Promise<RequiredEncryptOutput|false> {
): Promise<RequiredEncryptResponse|false> {
const region = regionFromKmsKeyArn(KeyId)
const client = clientProvider(region)

/* Check for early return (Postcondition): Client region was not provided. */
if (!client) return false

const request: EncryptInput<Client> = { KeyId, Plaintext, EncryptionContext, GrantTokens } as EncryptInput<Client>
// @ts-ignore
const v2vsV3Response = client.encrypt(request)
const v2vsV3Promise = (v2vsV3Response.promise ? v2vsV3Response.promise() : v2vsV3Response)
const kmsEDK: EncryptOutput<Client> = await v2vsV3Promise
const v2vsV3Response = client.encrypt({ KeyId, Plaintext, EncryptionContext, GrantTokens })
const v2vsV3Promise = 'promise' in v2vsV3Response
? v2vsV3Response.promise()
: v2vsV3Response
const kmsEDK = await v2vsV3Promise

return safeEncryptOutput(kmsEDK)
}

export async function decrypt<Client extends KMS> (
export async function decrypt<Client extends AwsEsdkKMSInterface> (
clientProvider: KmsClientSupplier<Client>,
{ providerId, providerInfo, encryptedDataKey }: EncryptedDataKey,
EncryptionContext?: EncryptionContext,
GrantTokens?: string[]
): Promise<RequiredDecryptOutput|false> {
): Promise<RequiredDecryptResponse|false> {
/* Precondition: The EDK must be a KMS edk. */
needs(providerId === KMS_PROVIDER_ID, 'Unsupported providerId')
const region = regionFromKmsKeyArn(providerInfo)
const client = clientProvider(region)
/* Check for early return (Postcondition): Client region was not provided. */
if (!client) return false

const request: DecryptInput<Client> = {
CiphertextBlob: encryptedDataKey,
EncryptionContext,
GrantTokens } as DecryptInput<Client>
// @ts-ignore
const v2vsV3Response = client.decrypt(request)
const v2vsV3Promise = (v2vsV3Response.promise ? v2vsV3Response.promise() : v2vsV3Response)
const dataKey: DecryptOutput<Client> = await v2vsV3Promise
const v2vsV3Response = client.decrypt({ CiphertextBlob: encryptedDataKey, EncryptionContext, GrantTokens })
const v2vsV3Promise = 'promise' in v2vsV3Response
? v2vsV3Response.promise()
: v2vsV3Response
const dataKey = await v2vsV3Promise

return safeDecryptOutput(dataKey)
}

export function kmsResponseToEncryptedDataKey ({
KeyId: providerInfo,
CiphertextBlob: encryptedDataKey
}: RequiredEncryptOutput) {
}: RequiredEncryptResponse) {
return new EncryptedDataKey({ providerId: KMS_PROVIDER_ID, providerInfo, encryptedDataKey })
}

function safeGenerateDataKey<Client extends KMS> (
dataKey: GenerateDataKeyOutput<Client>
): RequiredGenerateDataKeyOutput {
function safeGenerateDataKey (
dataKey: GenerateDataKeyResponse
): RequiredGenerateDataKeyResponse {
/* Postcondition: KMS must return serializable generate data key. */
needs(typeof dataKey.KeyId === 'string' &&
dataKey.Plaintext instanceof Uint8Array &&
dataKey.CiphertextBlob instanceof Uint8Array, 'Malformed KMS response.')

return <RequiredGenerateDataKeyOutput>safePlaintext(<RequiredGenerateDataKeyOutput>dataKey)
return <RequiredGenerateDataKeyResponse>safePlaintext(<RequiredGenerateDataKeyResponse>dataKey)
}

function safeEncryptOutput<Client extends KMS> (
dataKey: EncryptOutput<Client>
): RequiredEncryptOutput {
function safeEncryptOutput (
dataKey: EncryptResponse
): RequiredEncryptResponse {
/* Postcondition: KMS must return serializable encrypted data key. */
needs(typeof dataKey.KeyId === 'string' &&
dataKey.CiphertextBlob instanceof Uint8Array, 'Malformed KMS response.')

return <RequiredEncryptOutput>dataKey
return <RequiredEncryptResponse>dataKey
}

function safeDecryptOutput<Client extends KMS> (
dataKey: DecryptOutput<Client>
): RequiredDecryptOutput {
function safeDecryptOutput (
dataKey: DecryptResponse
): RequiredDecryptResponse {
/* Postcondition: KMS must return usable decrypted key. */
needs(typeof dataKey.KeyId === 'string' &&
dataKey.Plaintext instanceof Uint8Array, 'Malformed KMS response.')

return <RequiredDecryptOutput>safePlaintext(<RequiredDecryptOutput>dataKey)
return <RequiredDecryptResponse>safePlaintext(<RequiredDecryptResponse>dataKey)
}

function safePlaintext (dataKey: RequiredDecryptOutput | RequiredGenerateDataKeyOutput): RequiredDecryptOutput | RequiredGenerateDataKeyOutput {
function safePlaintext (dataKey: RequiredDecryptResponse | RequiredGenerateDataKeyResponse): RequiredDecryptResponse | RequiredGenerateDataKeyResponse {
/* The KMS Client *may* return a Buffer that is not isolated.
* i.e. the byteOffset !== 0.
* This means that the unencrypted data key is possibly accessible to someone else.
Expand Down
36 changes: 17 additions & 19 deletions modules/kms-keyring/src/kms_client_supplier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,25 @@

import { needs } from '@aws-crypto/material-management'
import {
KMS, // eslint-disable-line no-unused-vars
KMSConfiguration, // eslint-disable-line no-unused-vars
KMSOperations // eslint-disable-line no-unused-vars
AwsEsdkKMSInterface // eslint-disable-line no-unused-vars
} from './kms_types'

interface KMSConstructibleNonOption<Client extends KMS, Config extends KMSConfiguration> {
interface KMSConstructibleNonOption<Client extends AwsEsdkKMSInterface, Config> {
new(config: Config) : Client
}

interface KMSConstructibleOption<Client extends KMS, Config extends KMSConfiguration> {
interface KMSConstructibleOption<Client extends AwsEsdkKMSInterface, Config> {
new(config?: Config) : Client
}

export type KMSConstructible<Client extends KMS, Config extends KMSConfiguration> = KMSConstructibleNonOption<Client, Config> | KMSConstructibleOption<Client, Config>
export type KMSConstructible<Client extends AwsEsdkKMSInterface, Config> = KMSConstructibleNonOption<Client, Config> | KMSConstructibleOption<Client, Config>

export interface KmsClientSupplier<Client extends KMS> {
export interface KmsClientSupplier<Client extends AwsEsdkKMSInterface> {
/* KmsClientProvider is allowed to return undefined if, for example, user wants to exclude particular regions. */
(region: string): Client | false
}

export function getClient<Client extends KMS, Config extends KMSConfiguration> (
export function getClient<Client extends AwsEsdkKMSInterface, Config> (
KMSClient: KMSConstructible<Client, Config>,
defaultConfig?: Config
): KmsClientSupplier<Client> {
Expand All @@ -58,7 +56,7 @@ export function getClient<Client extends KMS, Config extends KMSConfiguration> (
}
}

export function limitRegions<Client extends KMS> (
export function limitRegions<Client extends AwsEsdkKMSInterface> (
regions: string[],
getClient: KmsClientSupplier<Client>
): KmsClientSupplier<Client> {
Expand All @@ -71,7 +69,7 @@ export function limitRegions<Client extends KMS> (
}
}

export function excludeRegions<Client extends KMS> (
export function excludeRegions<Client extends AwsEsdkKMSInterface> (
regions: string[],
getClient: KmsClientSupplier<Client>
): KmsClientSupplier<Client> {
Expand All @@ -84,7 +82,7 @@ export function excludeRegions<Client extends KMS> (
}
}

export function cacheClients<Client extends KMS> (
export function cacheClients<Client extends AwsEsdkKMSInterface> (
getClient: KmsClientSupplier<Client>
): KmsClientSupplier<Client> {
const clientsCache: {[key: string]: Client|false} = {}
Expand All @@ -103,7 +101,7 @@ export function cacheClients<Client extends KMS> (
* This does *not* mean that this call is successful,
* only that the region is backed by a functional KMS service.
*/
function deferCache<Client extends KMS> (
function deferCache<Client extends AwsEsdkKMSInterface> (
clientsCache: {[key: string]: Client|false},
region: string,
client: Client|false
Expand All @@ -115,17 +113,17 @@ function deferCache<Client extends KMS> (
}
const { encrypt, decrypt, generateDataKey } = client

return (<KMSOperations[]>['encrypt', 'decrypt', 'generateDataKey']).reduce(wrapOperation, client)
return (<(keyof AwsEsdkKMSInterface)[]>['encrypt', 'decrypt', 'generateDataKey']).reduce(wrapOperation, client)

/* Wrap each of the operations to cache the client on response */
function wrapOperation (client: Client, name: KMSOperations): Client {
// type params = Parameters<KMS[typeof name]>
// type retValue = ReturnType<KMS[typeof name]>
function wrapOperation (client: Client, name: keyof AwsEsdkKMSInterface): Client {
const original = client[name]
client[name] = function (...args: any): Promise<any> {
client[name] = function wrappedOperation (this: Client, args: any): Promise<any> {
// @ts-ignore (there should be a TypeScript solution for this)
const v2vsV3Response = original.apply(client, args)
const v2vsV3Promise = (v2vsV3Response.promise ? v2vsV3Response.promise() : v2vsV3Response)
const v2vsV3Response = original.call(client, args)
const v2vsV3Promise = 'promise' in v2vsV3Response
? v2vsV3Response.promise()
: v2vsV3Response
return v2vsV3Promise
.then((response: any) => {
clientsCache[region] = Object.assign(client, { encrypt, decrypt, generateDataKey })
Expand Down
14 changes: 7 additions & 7 deletions modules/kms-keyring/src/kms_keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

import { KmsClientSupplier } from './kms_client_supplier' // eslint-disable-line no-unused-vars
import {
KMS, // eslint-disable-line no-unused-vars
RequiredDecryptOutput // eslint-disable-line no-unused-vars
AwsEsdkKMSInterface, // eslint-disable-line no-unused-vars
RequiredDecryptResponse // eslint-disable-line no-unused-vars
} from './kms_types'
import {
needs,
Expand All @@ -34,15 +34,15 @@ import {
import { KMS_PROVIDER_ID, generateDataKey, encrypt, decrypt, kmsResponseToEncryptedDataKey } from './helpers'
import { regionFromKmsKeyArn } from './region_from_kms_key_arn'

export interface KmsKeyringInput<Client extends KMS> {
export interface KmsKeyringInput<Client extends AwsEsdkKMSInterface> {
clientProvider: KmsClientSupplier<Client>
keyIds?: string[]
generatorKeyId?: string
grantTokens?: string[]
discovery?: boolean
}

export interface KeyRing<S extends SupportedAlgorithmSuites, Client extends KMS> extends Keyring<S> {
export interface KeyRing<S extends SupportedAlgorithmSuites, Client extends AwsEsdkKMSInterface> extends Keyring<S> {
keyIds: ReadonlyArray<string>
generatorKeyId?: string
clientProvider: KmsClientSupplier<Client>
Expand All @@ -52,15 +52,15 @@ export interface KeyRing<S extends SupportedAlgorithmSuites, Client extends KMS>
_onDecrypt(material: DecryptionMaterial<S>, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext): Promise<DecryptionMaterial<S>>
}

export interface KmsKeyRingConstructible<S extends SupportedAlgorithmSuites, Client extends KMS> {
export interface KmsKeyRingConstructible<S extends SupportedAlgorithmSuites, Client extends AwsEsdkKMSInterface> {
new(input: KmsKeyringInput<Client>): KeyRing<S, Client>
}

export interface KeyRingConstructible<S extends SupportedAlgorithmSuites> {
new(): Keyring<S>
}

export function KmsKeyringClass<S extends SupportedAlgorithmSuites, Client extends KMS> (
export function KmsKeyringClass<S extends SupportedAlgorithmSuites, Client extends AwsEsdkKMSInterface> (
BaseKeyring: KeyRingConstructible<S>
): KmsKeyRingConstructible<S, Client> {
class KmsKeyring extends BaseKeyring implements KeyRing<S, Client> {
Expand Down Expand Up @@ -160,7 +160,7 @@ export function KmsKeyringClass<S extends SupportedAlgorithmSuites, Client exten
})

for (const edk of decryptableEDKs) {
let dataKey: RequiredDecryptOutput|false = false
let dataKey: RequiredDecryptResponse|false = false
try {
dataKey = await decrypt(clientProvider, edk, context, grantTokens)
} catch (e) {
Expand Down
Loading