From 4cb47ac043c4760a730c93f251919d8802b5ac5f Mon Sep 17 00:00:00 2001 From: seebees Date: Tue, 21 Apr 2020 11:38:44 -0700 Subject: [PATCH 1/4] chore: move to eslint and prettier Typescript is moving to eslint and prettier is now the standard. This first PR updates all source files with these new linting/formatting rules. The test are NOT changed. This is to help the reviews confirm that all changes are format only. --- .eslintrc.js | 68 + .prettierrc.js | 9 + modules/cache-material/package.json | 4 +- ...yptographic_materials_cache_key_helpers.ts | 40 +- ...hing_cryptographic_materials_decorators.ts | 179 +- .../src/cryptographic_materials_cache.ts | 25 +- ...get_local_cryptographic_materials_cache.ts | 48 +- .../cache-material/src/portable_compare.ts | 6 +- .../package.json | 4 +- .../src/caching_materials_manager_browser.ts | 48 +- .../src/sha512.ts | 11 +- .../package.json | 4 +- .../src/caching_materials_manager_node.ts | 46 +- .../src/sha512.ts | 9 +- modules/client-browser/package.json | 4 +- modules/client-node/package.json | 4 +- modules/decrypt-browser/package.json | 4 +- modules/decrypt-browser/src/decrypt.ts | 101 +- modules/decrypt-node/package.json | 4 +- modules/decrypt-node/src/decipher_stream.ts | 73 +- modules/decrypt-node/src/decrypt.ts | 28 +- modules/decrypt-node/src/decrypt_stream.ts | 37 +- .../decrypt-node/src/parse_header_stream.ts | 60 +- modules/decrypt-node/src/verify_stream.ts | 87 +- modules/encrypt-browser/package.json | 4 +- modules/encrypt-browser/src/encrypt.ts | 77 +- modules/encrypt-node/package.json | 4 +- modules/encrypt-node/src/encrypt.ts | 31 +- modules/encrypt-node/src/encrypt_stream.ts | 80 +- .../encrypt-node/src/framed_encrypt_stream.ts | 113 +- modules/encrypt-node/src/signature_stream.ts | 10 +- modules/example-browser/package.json | 4 +- modules/example-browser/src/aes_simple.ts | 34 +- modules/example-browser/src/caching_cmm.ts | 40 +- modules/example-browser/src/fallback.ts | 45 +- modules/example-browser/src/index.ts | 6 +- modules/example-browser/src/kms_simple.ts | 42 +- modules/example-browser/src/multi_keyring.ts | 62 +- modules/example-browser/src/rsa_simple.ts | 68 +- modules/example-node/package.json | 4 +- modules/example-node/src/aes_simple.ts | 34 +- modules/example-node/src/caching_cmm.ts | 37 +- .../src/kms_regional_discovery.ts | 16 +- modules/example-node/src/kms_simple.ts | 24 +- modules/example-node/src/kms_stream.ts | 30 +- modules/example-node/src/multi_keyring.ts | 48 +- modules/example-node/src/rsa_simple.ts | 21 +- modules/hkdf-node/package.json | 4 +- modules/hkdf-node/src/errors.ts | 13 +- modules/hkdf-node/src/hkdf.ts | 49 +- modules/integration-browser/package.json | 4 +- .../src/build_decrypt_fixtures.ts | 80 +- .../src/build_encrypt_fixtures.ts | 68 +- modules/integration-browser/src/cli.ts | 108 +- .../decrypt_materials_manager_web_crypto.ts | 90 +- .../src/integration.decrypt.test.ts | 6 +- .../src/integration.encrypt.test.ts | 10 +- modules/integration-browser/src/types.ts | 39 +- modules/integration-node/package.json | 4 +- modules/integration-node/src/cli.ts | 108 +- .../src/decrypt_materials_manager_node.ts | 77 +- .../src/get_decrypt_test_iterator.ts | 85 +- .../src/get_encrypt_test_iterator.ts | 60 +- modules/integration-node/src/index.ts | 1 - .../integration-node/src/integration_tests.ts | 81 +- modules/integration-node/src/types.ts | 39 +- modules/kms-keyring-browser/package.json | 4 +- .../src/kms_keyring_browser.ts | 55 +- modules/kms-keyring-node/package.json | 4 +- .../kms-keyring-node/src/kms_keyring_node.ts | 44 +- modules/kms-keyring/package.json | 4 +- modules/kms-keyring/src/helpers.ts | 132 +- .../kms-keyring/src/kms_client_supplier.ts | 89 +- modules/kms-keyring/src/kms_keyring.ts | 192 +- modules/kms-keyring/src/kms_types.ts | 23 +- .../src/region_from_kms_key_arn.ts | 26 +- .../material-management-browser/package.json | 4 +- ...browser_cryptographic_materials_manager.ts | 151 +- .../src/bytes2_jwk.ts | 9 +- .../material-management-browser/src/index.ts | 27 +- .../src/keyring_helpers.ts | 40 +- .../src/material_helpers.ts | 190 +- modules/material-management-node/package.json | 4 +- modules/material-management-node/src/index.ts | 25 +- .../src/material_helpers.ts | 136 +- .../node_cryptographic_materials_manager.ts | 117 +- modules/material-management/package.json | 4 +- .../src/algorithm_suites.ts | 74 +- .../src/clone_cryptographic_material.ts | 43 +- .../src/cryptographic_material.ts | 429 ++- modules/material-management/src/ecc_decode.ts | 52 +- modules/material-management/src/ecc_encode.ts | 16 +- .../src/encrypted_data_key.ts | 33 +- .../src/immutable_class.ts | 26 +- modules/material-management/src/index.ts | 59 +- modules/material-management/src/keyring.ts | 54 +- .../material-management/src/keyring_trace.ts | 51 +- .../src/materials_manager.ts | 18 +- .../material-management/src/multi_keyring.ts | 88 +- modules/material-management/src/needs.ts | 24 +- .../src/node_algorithms.ts | 73 +- .../material-management/src/pem_helpers.ts | 80 +- .../material-management/src/signature_key.ts | 60 +- modules/material-management/src/types.ts | 54 +- .../src/web_crypto_algorithms.ts | 132 +- modules/raw-aes-keyring-browser/package.json | 4 +- modules/raw-aes-keyring-browser/src/index.ts | 5 +- .../src/raw_aes_keyring_browser.ts | 148 +- modules/raw-aes-keyring-node/package.json | 4 +- modules/raw-aes-keyring-node/src/index.ts | 5 +- .../src/raw_aes_keyring_node.ts | 111 +- modules/raw-keyring/package.json | 4 +- .../src/raw_aes_algorithm_suite.ts | 27 +- .../src/raw_aes_encrypted_data_keys.ts | 74 +- modules/raw-keyring/src/raw_aes_material.ts | 65 +- .../raw-keyring/src/raw_keyring_decorators.ts | 36 +- modules/raw-rsa-keyring-browser/package.json | 4 +- .../src/get_import_options.ts | 84 +- .../src/raw_rsa_keyring_web_crypto.ts | 117 +- modules/raw-rsa-keyring-browser/src/types.ts | 24 +- modules/raw-rsa-keyring-node/package.json | 4 +- .../src/oaep_hash_supported.ts | 22 +- .../src/raw_rsa_keyring_node.ts | 94 +- modules/serialize/package.json | 4 +- modules/serialize/src/aad_factory.ts | 23 +- modules/serialize/src/concat_buffers.ts | 30 +- modules/serialize/src/decode_body_header.ts | 100 +- modules/serialize/src/deserialize_factory.ts | 91 +- modules/serialize/src/ecdsa_signature.ts | 34 +- modules/serialize/src/identifiers.ts | 32 +- modules/serialize/src/kdf_info.ts | 14 +- modules/serialize/src/read_element.ts | 16 +- modules/serialize/src/serialize_factory.ts | 87 +- modules/serialize/src/signature_info.ts | 8 +- modules/serialize/src/types.ts | 50 +- modules/serialize/src/uint_util.ts | 6 +- modules/web-crypto-backend/package.json | 4 +- .../web-crypto-backend/src/backend-factory.ts | 76 +- modules/web-crypto-backend/src/index.ts | 9 +- .../src/promisify-ms-crypto.ts | 28 +- .../src/synchronous_random_values.ts | 2 +- package-lock.json | 2750 +++++++++++------ package.json | 24 +- 143 files changed, 6290 insertions(+), 3239 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .prettierrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..bb7d534c1 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,68 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + ignorePatterns: ['node_modules/'], + rules: { + // I disagree with this rule. + // Humans read from less specific to more specific. + // No on puts the outline at the end of the book. + // Since the exported functions should be composed + // of lower level functions, + // it is good for understanding + // for the source files to get more detailed + // as you read down from the top. + "no-use-before-define": ["error", { "functions": false }], + "@typescript-eslint/no-use-before-define": ["error", { "functions": false }], + // This is used in a few specific ways. + // It may be that adding this to overrides for the tests + // and then manual line overrides would be + // the best way to handle this later. + '@typescript-eslint/no-explicit-any': 'off', + // Minimize churn. + '@typescript-eslint/member-delimiter-style': ['error', { + 'multiline': { + 'delimiter': 'none', + 'requireLast': false + }, + 'singleline': { + 'delimiter': 'semi', + 'requireLast': false + } + }], + // The ESDK exports some interfaces + // that conflict with this rule. + // At a later date, this might be + // able to be turned on, + // but to ensure ZERO interface changes + // this rule is disabled. + // To be clear this would only impact + // Typescript use of some types/interfaces. + "@typescript-eslint/no-empty-interface": 'off', + // To minimize the source change, + // this is turned of. + "@typescript-eslint/ban-ts-ignore": 'off', + }, + // This is a good rule, + // but in many tests, + // we are just looking to mock specific functions. + "overrides": [ + { + "files": ["modules/**/test/**/*.ts"], + "rules": { + "@typescript-eslint/no-empty-function": "off" + } + } + ] +}; diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..83f388a03 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,9 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// I would prefer to use the default configuration +// but then the diff is out of control. +module.exports = { + semi: false, + singleQuote: true +} \ No newline at end of file diff --git a/modules/cache-material/package.json b/modules/cache-material/package.json index 29b5c3559..2da52cc92 100644 --- a/modules/cache-material/package.json +++ b/modules/cache-material/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts b/modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts index bbf508410..66d500ee5 100644 --- a/modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts +++ b/modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - DecryptionRequest, // eslint-disable-line no-unused-vars - EncryptionRequest, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars - EncryptionContext // eslint-disable-line no-unused-vars + SupportedAlgorithmSuites, + DecryptionRequest, + EncryptionRequest, + EncryptedDataKey, + EncryptionContext, } from '@aws-crypto/material-management' import { serializeFactory, uInt16BE } from '@aws-crypto/serialize' import { compare } from './portable_compare' @@ -14,24 +14,26 @@ import { compare } from './portable_compare' // 512 bits of 0 for padding between hashes in decryption materials cache ID generation. const BIT_PAD_512 = Buffer.alloc(64) -export function buildCryptographicMaterialsCacheKeyHelpers ( +export function buildCryptographicMaterialsCacheKeyHelpers< + S extends SupportedAlgorithmSuites +>( fromUtf8: (input: string) => Uint8Array, toUtf8: (input: Uint8Array) => string, - sha512: (...data: ((Uint8Array|string))[]) => Promise + sha512: (...data: (Uint8Array | string)[]) => Promise ): CryptographicMaterialsCacheKeyHelpersInterface { const { serializeEncryptionContext, - serializeEncryptedDataKey + serializeEncryptedDataKey, } = serializeFactory(fromUtf8) return { buildEncryptionMaterialCacheKey, buildDecryptionMaterialCacheKey, encryptedDataKeysHash, - encryptionContextHash + encryptionContextHash, } - async function buildEncryptionMaterialCacheKey ( + async function buildEncryptionMaterialCacheKey( partition: string, { suite, encryptionContext }: EncryptionRequest ) { @@ -47,7 +49,7 @@ export function buildCryptographicMaterialsCacheKeyHelpers ) { @@ -63,16 +65,18 @@ export function buildCryptographicMaterialsCacheKeyHelpers) { + async function encryptedDataKeysHash( + encryptedDataKeys: ReadonlyArray + ) { const hashes = await Promise.all( encryptedDataKeys .map(serializeEncryptedDataKey) - .map(edk => sha512(edk)) + .map(async (edk) => sha512(edk)) ) return hashes.sort(compare) } - function encryptionContextHash (context: EncryptionContext) { + async function encryptionContextHash(context: EncryptionContext) { /* The AAD section is uInt16BE(length) + AAD * see: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-aad * However, the RAW Keyring wants _only_ the ADD. @@ -83,7 +87,9 @@ export function buildCryptographicMaterialsCacheKeyHelpers { +export interface CryptographicMaterialsCacheKeyHelpersInterface< + S extends SupportedAlgorithmSuites +> { buildEncryptionMaterialCacheKey( partition: string, { suite, encryptionContext }: EncryptionRequest @@ -92,6 +98,8 @@ export interface CryptographicMaterialsCacheKeyHelpersInterface ): Promise - encryptedDataKeysHash(encryptedDataKeys: ReadonlyArray): Promise + encryptedDataKeysHash( + encryptedDataKeys: ReadonlyArray + ): Promise encryptionContextHash(context: EncryptionContext): Promise } diff --git a/modules/cache-material/src/caching_cryptographic_materials_decorators.ts b/modules/cache-material/src/caching_cryptographic_materials_decorators.ts index 32dd52b12..7235f8b97 100644 --- a/modules/cache-material/src/caching_cryptographic_materials_decorators.ts +++ b/modules/cache-material/src/caching_cryptographic_materials_decorators.ts @@ -2,33 +2,38 @@ // SPDX-License-Identifier: Apache-2.0 import { - GetEncryptionMaterials, // eslint-disable-line no-unused-vars - GetDecryptMaterials, // eslint-disable-line no-unused-vars - DecryptionMaterial, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - EncryptionRequest, // eslint-disable-line no-unused-vars - EncryptionMaterial, // eslint-disable-line no-unused-vars - MaterialsManager, // eslint-disable-line no-unused-vars - DecryptionRequest, // eslint-disable-line no-unused-vars + GetEncryptionMaterials, + GetDecryptMaterials, + DecryptionMaterial, + SupportedAlgorithmSuites, + EncryptionRequest, + EncryptionMaterial, + MaterialsManager, + DecryptionRequest, needs, readOnlyProperty, - Keyring, // eslint-disable-line no-unused-vars - cloneMaterial + Keyring, + cloneMaterial, } from '@aws-crypto/material-management' import { Maximum } from '@aws-crypto/serialize' import { - CryptographicMaterialsCache, // eslint-disable-line no-unused-vars - Entry // eslint-disable-line no-unused-vars + CryptographicMaterialsCache, + Entry, } from './cryptographic_materials_cache' -import { - CryptographicMaterialsCacheKeyHelpersInterface // eslint-disable-line no-unused-vars -} from './build_cryptographic_materials_cache_key_helpers' +import { CryptographicMaterialsCacheKeyHelpersInterface } from './build_cryptographic_materials_cache_key_helpers' -export function decorateProperties ( +export function decorateProperties( obj: CachingMaterialsManager, input: CachingMaterialsManagerDecorateInput ) { - const { cache, backingMaterialsManager, maxAge, maxBytesEncrypted, maxMessagesEncrypted, partition } = input + const { + cache, + backingMaterialsManager, + maxAge, + maxBytesEncrypted, + maxMessagesEncrypted, + partition, + } = input /* Precondition: A caching material manager needs a cache. */ needs(cache, 'You must provide a cache.') @@ -37,36 +42,64 @@ export function decorateProperties ( /* Precondition: You *can not* cache something forever. */ needs(maxAge > 0, 'You must configure a maxAge') /* Precondition: maxBytesEncrypted must be inside bounds. i.e. positive and not more than the maximum. */ - needs(!maxBytesEncrypted || (maxBytesEncrypted > 0 && Maximum.BYTES_PER_CACHED_KEY_LIMIT >= maxBytesEncrypted), 'maxBytesEncrypted is outside of bounds.') + needs( + !maxBytesEncrypted || + (maxBytesEncrypted > 0 && + Maximum.BYTES_PER_CACHED_KEY_LIMIT >= maxBytesEncrypted), + 'maxBytesEncrypted is outside of bounds.' + ) /* Precondition: maxMessagesEncrypted must be inside bounds. i.e. positive and not more than the maximum. */ - needs(!maxMessagesEncrypted || (maxMessagesEncrypted > 0 && Maximum.MESSAGES_PER_CACHED_KEY_LIMIT >= maxMessagesEncrypted), 'maxMessagesEncrypted is outside of bounds.') + needs( + !maxMessagesEncrypted || + (maxMessagesEncrypted > 0 && + Maximum.MESSAGES_PER_CACHED_KEY_LIMIT >= maxMessagesEncrypted), + 'maxMessagesEncrypted is outside of bounds.' + ) /* Precondition: partition must be a string. */ - needs(partition && typeof partition === 'string', 'partition must be a string.') + needs( + partition && typeof partition === 'string', + 'partition must be a string.' + ) readOnlyProperty(obj, '_cache', cache) readOnlyProperty(obj, '_backingMaterialsManager', backingMaterialsManager) readOnlyProperty(obj, '_maxAge', maxAge) - readOnlyProperty(obj, '_maxBytesEncrypted', maxBytesEncrypted || Maximum.BYTES_PER_CACHED_KEY_LIMIT) - readOnlyProperty(obj, '_maxMessagesEncrypted', maxMessagesEncrypted || Maximum.MESSAGES_PER_CACHED_KEY_LIMIT) + readOnlyProperty( + obj, + '_maxBytesEncrypted', + maxBytesEncrypted || Maximum.BYTES_PER_CACHED_KEY_LIMIT + ) + readOnlyProperty( + obj, + '_maxMessagesEncrypted', + maxMessagesEncrypted || Maximum.MESSAGES_PER_CACHED_KEY_LIMIT + ) readOnlyProperty(obj, '_partition', partition) } -export function getEncryptionMaterials ( - { buildEncryptionMaterialCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface -): GetEncryptionMaterials { - return async function getEncryptionMaterials ( +export function getEncryptionMaterials({ + buildEncryptionMaterialCacheKey, +}: CryptographicMaterialsCacheKeyHelpersInterface): GetEncryptionMaterials< + S +> { + return async function getEncryptionMaterials( this: CachingMaterialsManager, request: EncryptionRequest ): Promise> { const { suite, encryptionContext, plaintextLength } = request /* Check for early return (Postcondition): If I can not cache the EncryptionMaterial, do not even look. */ - if ((suite && !suite.cacheSafe) || typeof plaintextLength !== 'number' || plaintextLength < 0) { - return this - ._backingMaterialsManager - .getEncryptionMaterials(request) + if ( + (suite && !suite.cacheSafe) || + typeof plaintextLength !== 'number' || + plaintextLength < 0 + ) { + return this._backingMaterialsManager.getEncryptionMaterials(request) } - const cacheKey = await buildEncryptionMaterialCacheKey(this._partition, { suite, encryptionContext }) + const cacheKey = await buildEncryptionMaterialCacheKey(this._partition, { + suite, + encryptionContext, + }) const entry = this._cache.getEncryptionMaterial(cacheKey, plaintextLength) /* Check for early return (Postcondition): If I have a valid EncryptionMaterial, return it. */ if (entry && !this._cacheEntryHasExceededLimits(entry)) { @@ -75,8 +108,7 @@ export function getEncryptionMaterials ( this._cache.del(cacheKey) } - const material = await this - ._backingMaterialsManager + const material = await this._backingMaterialsManager /* Strip any information about the plaintext from the backing request, * because the resulting response may be used to encrypt multiple plaintexts. */ @@ -94,10 +126,15 @@ export function getEncryptionMaterials ( response: material, now: Date.now(), messagesEncrypted: 1, - bytesEncrypted: plaintextLength + bytesEncrypted: plaintextLength, } if (!this._cacheEntryHasExceededLimits(testEntry)) { - this._cache.putEncryptionMaterial(cacheKey, material, plaintextLength, this._maxAge) + this._cache.putEncryptionMaterial( + cacheKey, + material, + plaintextLength, + this._maxAge + ) return cloneResponse(material) } else { /* Postcondition: If the material has exceeded limits it MUST NOT be cloned. @@ -110,22 +147,23 @@ export function getEncryptionMaterials ( } } -export function decryptMaterials ( - { buildDecryptionMaterialCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface -): GetDecryptMaterials { - return async function decryptMaterials ( +export function decryptMaterials({ + buildDecryptionMaterialCacheKey, +}: CryptographicMaterialsCacheKeyHelpersInterface): GetDecryptMaterials { + return async function decryptMaterials( this: CachingMaterialsManager, request: DecryptionRequest ): Promise> { const { suite } = request /* Check for early return (Postcondition): If I can not cache the DecryptionMaterial, do not even look. */ if (!suite.cacheSafe) { - return this - ._backingMaterialsManager - .decryptMaterials(request) + return this._backingMaterialsManager.decryptMaterials(request) } - const cacheKey = await buildDecryptionMaterialCacheKey(this._partition, request) + const cacheKey = await buildDecryptionMaterialCacheKey( + this._partition, + request + ) const entry = this._cache.getDecryptionMaterial(cacheKey) /* Check for early return (Postcondition): If I have a valid DecryptionMaterial, return it. */ if (entry && !this._cacheEntryHasExceededLimits(entry)) { @@ -134,24 +172,28 @@ export function decryptMaterials ( this._cache.del(cacheKey) } - const material = await this - ._backingMaterialsManager - .decryptMaterials(request) + const material = await this._backingMaterialsManager.decryptMaterials( + request + ) this._cache.putDecryptionMaterial(cacheKey, material, this._maxAge) return cloneResponse(material) } } -export function cacheEntryHasExceededLimits (): CacheEntryHasExceededLimits { - return function cacheEntryHasExceededLimits ( +export function cacheEntryHasExceededLimits< + S extends SupportedAlgorithmSuites +>(): CacheEntryHasExceededLimits { + return function cacheEntryHasExceededLimits( this: CachingMaterialsManager, { now, messagesEncrypted, bytesEncrypted }: Entry ): boolean { const age = Date.now() - now - return age > this._maxAge || + return ( + age > this._maxAge || messagesEncrypted > this._maxMessagesEncrypted || bytesEncrypted > this._maxBytesEncrypted + ) } } @@ -163,27 +205,34 @@ export function cacheEntryHasExceededLimits * @param material EncryptionMaterial|DecryptionMaterial * @return EncryptionMaterial|DecryptionMaterial */ -function cloneResponse|DecryptionMaterial> ( - material: M -): M { +function cloneResponse< + S extends SupportedAlgorithmSuites, + M extends EncryptionMaterial | DecryptionMaterial +>(material: M): M { return cloneMaterial(material) } -export interface CachingMaterialsManagerInput extends Readonly<{ - cache: CryptographicMaterialsCache - backingMaterials: MaterialsManager|Keyring - partition?: string - maxBytesEncrypted?: number - maxMessagesEncrypted?: number - maxAge: number -}>{} - -export interface CachingMaterialsManagerDecorateInput extends CachingMaterialsManagerInput { +export interface CachingMaterialsManagerInput< + S extends SupportedAlgorithmSuites +> + extends Readonly<{ + cache: CryptographicMaterialsCache + backingMaterials: MaterialsManager | Keyring + partition?: string + maxBytesEncrypted?: number + maxMessagesEncrypted?: number + maxAge: number + }> {} + +export interface CachingMaterialsManagerDecorateInput< + S extends SupportedAlgorithmSuites +> extends CachingMaterialsManagerInput { backingMaterialsManager: MaterialsManager partition: string } -export interface CachingMaterialsManager extends MaterialsManager { +export interface CachingMaterialsManager + extends MaterialsManager { readonly _partition: string readonly _cache: CryptographicMaterialsCache readonly _backingMaterialsManager: MaterialsManager @@ -194,6 +243,8 @@ export interface CachingMaterialsManager ext _cacheEntryHasExceededLimits: CacheEntryHasExceededLimits } -export interface CacheEntryHasExceededLimits { +export interface CacheEntryHasExceededLimits< + S extends SupportedAlgorithmSuites +> { (entry: Entry): boolean } diff --git a/modules/cache-material/src/cryptographic_materials_cache.ts b/modules/cache-material/src/cryptographic_materials_cache.ts index 81a887675..068aa4d18 100644 --- a/modules/cache-material/src/cryptographic_materials_cache.ts +++ b/modules/cache-material/src/cryptographic_materials_cache.ts @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { - EncryptionMaterial, // eslint-disable-line no-unused-vars - DecryptionMaterial, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites // eslint-disable-line no-unused-vars + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, } from '@aws-crypto/material-management' -export interface CryptographicMaterialsCache { +export interface CryptographicMaterialsCache< + S extends SupportedAlgorithmSuites +> { putEncryptionMaterial( key: string, response: EncryptionMaterial, @@ -19,22 +21,27 @@ export interface CryptographicMaterialsCache response: DecryptionMaterial, maxAge?: number ): void - getEncryptionMaterial(key: string, plaintextLength: number): EncryptionMaterialEntry|false - getDecryptionMaterial(key: string): DecryptionMaterialEntry|false + getEncryptionMaterial( + key: string, + plaintextLength: number + ): EncryptionMaterialEntry | false + getDecryptionMaterial(key: string): DecryptionMaterialEntry | false del(key: string): void } export interface Entry { - response: EncryptionMaterial|DecryptionMaterial + response: EncryptionMaterial | DecryptionMaterial bytesEncrypted: number messagesEncrypted: number readonly now: number } -export interface EncryptionMaterialEntry extends Entry { +export interface EncryptionMaterialEntry + extends Entry { readonly response: EncryptionMaterial } -export interface DecryptionMaterialEntry extends Entry { +export interface DecryptionMaterialEntry + extends Entry { readonly response: DecryptionMaterial } diff --git a/modules/cache-material/src/get_local_cryptographic_materials_cache.ts b/modules/cache-material/src/get_local_cryptographic_materials_cache.ts index 316bf385f..8708b3a07 100644 --- a/modules/cache-material/src/get_local_cryptographic_materials_cache.ts +++ b/modules/cache-material/src/get_local_cryptographic_materials_cache.ts @@ -3,31 +3,33 @@ import LRU from 'lru-cache' import { - EncryptionMaterial, // eslint-disable-line no-unused-vars - DecryptionMaterial, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, needs, isEncryptionMaterial, - isDecryptionMaterial + isDecryptionMaterial, } from '@aws-crypto/material-management' import { - CryptographicMaterialsCache, // eslint-disable-line no-unused-vars - Entry, // eslint-disable-line no-unused-vars - EncryptionMaterialEntry, // eslint-disable-line no-unused-vars - DecryptionMaterialEntry // eslint-disable-line no-unused-vars + CryptographicMaterialsCache, + Entry, + EncryptionMaterialEntry, + DecryptionMaterialEntry, } from './cryptographic_materials_cache' -export function getLocalCryptographicMaterialsCache ( +export function getLocalCryptographicMaterialsCache< + S extends SupportedAlgorithmSuites +>( capacity: number, proactiveFrequency: number = 1000 * 60 ): CryptographicMaterialsCache { const cache = new LRU>({ max: capacity, - dispose (_key, value) { + dispose(_key, value) { /* Zero out the unencrypted dataKey, when the material is removed from the cache. */ value.response.zeroUnencryptedDataKey() - } + }, }) /* It is not a guarantee that the last item in the LRU will be the Oldest Item. @@ -43,7 +45,7 @@ export function getLocalCryptographicMaterialsCache { mayEvictTail() proactivelyTryAndEvictTail() @@ -59,7 +61,7 @@ export function getLocalCryptographicMaterialsCache, plaintextLength: number, @@ -75,12 +77,12 @@ export function getLocalCryptographicMaterialsCache, maxAge?: number @@ -93,12 +95,12 @@ export function getLocalCryptographicMaterialsCache= 0, 'Malformed plaintextLength') const entry = cache.get(key) @@ -110,23 +112,23 @@ export function getLocalCryptographicMaterialsCache>entry + return entry as EncryptionMaterialEntry }, - getDecryptionMaterial (key: string) { + getDecryptionMaterial(key: string) { const entry = cache.get(key) /* Check for early return (Postcondition): If this key does not have a DecryptionMaterial, return false. */ if (!entry) return false /* Postcondition: Only return DecryptionMaterial. */ needs(isDecryptionMaterial(entry.response), 'Malformed response.') - return >entry + return entry as DecryptionMaterialEntry }, - del (key: string) { + del(key: string) { cache.del(key) - } + }, } - function mayEvictTail () { + function mayEvictTail() { // @ts-ignore const { tail } = cache.dumpLru() /* Check for early return (Postcondition) UNTESTED: If there is no tail, then the cache is empty. */ diff --git a/modules/cache-material/src/portable_compare.ts b/modules/cache-material/src/portable_compare.ts index 6c1f79698..e87ebb054 100644 --- a/modules/cache-material/src/portable_compare.ts +++ b/modules/cache-material/src/portable_compare.ts @@ -6,10 +6,8 @@ * This is a simple compare function that is portable. * This function is *not* constant time. */ -export function compare (a: Uint8Array, b: Uint8Array) { - const length = a.byteLength > b.byteLength - ? b.byteLength - : a.byteLength +export function compare(a: Uint8Array, b: Uint8Array) { + const length = a.byteLength > b.byteLength ? b.byteLength : a.byteLength for (let i = 0; length > i; i += 1) { if (a[i] > b[i]) return 1 diff --git a/modules/caching-materials-manager-browser/package.json b/modules/caching-materials-manager-browser/package.json index 724370cc4..1cb39b2e7 100644 --- a/modules/caching-materials-manager-browser/package.json +++ b/modules/caching-materials-manager-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/caching-materials-manager-browser/src/caching_materials_manager_browser.ts b/modules/caching-materials-manager-browser/src/caching_materials_manager_browser.ts index 8bd6a3f07..f99f9b49c 100644 --- a/modules/caching-materials-manager-browser/src/caching_materials_manager_browser.ts +++ b/modules/caching-materials-manager-browser/src/caching_materials_manager_browser.ts @@ -2,31 +2,36 @@ // SPDX-License-Identifier: Apache-2.0 import { - CachingMaterialsManager, // eslint-disable-line no-unused-vars + CachingMaterialsManager, decorateProperties, getEncryptionMaterials, decryptMaterials, cacheEntryHasExceededLimits, buildCryptographicMaterialsCacheKeyHelpers, - CachingMaterialsManagerInput, // eslint-disable-line no-unused-vars - CryptographicMaterialsCache // eslint-disable-line no-unused-vars + CachingMaterialsManagerInput, + CryptographicMaterialsCache, } from '@aws-crypto/cache-material' import { - WebCryptoMaterialsManager, // eslint-disable-line no-unused-vars + WebCryptoMaterialsManager, WebCryptoDefaultCryptographicMaterialsManager, - WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars + WebCryptoAlgorithmSuite, KeyringWebCrypto, - WebCryptoGetEncryptionMaterials, // eslint-disable-line no-unused-vars - WebCryptoGetDecryptMaterials // eslint-disable-line no-unused-vars + WebCryptoGetEncryptionMaterials, + WebCryptoGetDecryptMaterials, } from '@aws-crypto/material-management-browser' import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' import { synchronousRandomValues } from '@aws-crypto/web-crypto-backend' import { sha512 } from './sha512' -const cacheKeyHelpers = buildCryptographicMaterialsCacheKeyHelpers(fromUtf8, toUtf8, sha512) +const cacheKeyHelpers = buildCryptographicMaterialsCacheKeyHelpers( + fromUtf8, + toUtf8, + sha512 +) -export class WebCryptoCachingMaterialsManager implements CachingMaterialsManager { +export class WebCryptoCachingMaterialsManager + implements CachingMaterialsManager { readonly _cache!: CryptographicMaterialsCache readonly _backingMaterialsManager!: WebCryptoMaterialsManager readonly _partition!: string @@ -34,10 +39,13 @@ export class WebCryptoCachingMaterialsManager implements CachingMaterialsManager readonly _maxMessagesEncrypted!: number readonly _maxAge!: number - constructor (input: CachingMaterialsManagerInput) { - const backingMaterialsManager = input.backingMaterials instanceof KeyringWebCrypto - ? new WebCryptoDefaultCryptographicMaterialsManager(input.backingMaterials) - : input.backingMaterials + constructor(input: CachingMaterialsManagerInput) { + const backingMaterialsManager = + input.backingMaterials instanceof KeyringWebCrypto + ? new WebCryptoDefaultCryptographicMaterialsManager( + input.backingMaterials + ) + : (input.backingMaterials as WebCryptoDefaultCryptographicMaterialsManager) /* Precondition: A partition value must exist for WebCryptoCachingMaterialsManager. * The maximum hash function at this time is 512. @@ -48,11 +56,17 @@ export class WebCryptoCachingMaterialsManager implements CachingMaterialsManager decorateProperties(this, { ...input, backingMaterialsManager, - partition + partition, }) } - getEncryptionMaterials: WebCryptoGetEncryptionMaterials = getEncryptionMaterials(cacheKeyHelpers) - decryptMaterials: WebCryptoGetDecryptMaterials = decryptMaterials(cacheKeyHelpers) - _cacheEntryHasExceededLimits = cacheEntryHasExceededLimits() + getEncryptionMaterials: WebCryptoGetEncryptionMaterials = getEncryptionMaterials< + WebCryptoAlgorithmSuite + >(cacheKeyHelpers) + decryptMaterials: WebCryptoGetDecryptMaterials = decryptMaterials< + WebCryptoAlgorithmSuite + >(cacheKeyHelpers) + _cacheEntryHasExceededLimits = cacheEntryHasExceededLimits< + WebCryptoAlgorithmSuite + >() } diff --git a/modules/caching-materials-manager-browser/src/sha512.ts b/modules/caching-materials-manager-browser/src/sha512.ts index 318c65b02..e367eae34 100644 --- a/modules/caching-materials-manager-browser/src/sha512.ts +++ b/modules/caching-materials-manager-browser/src/sha512.ts @@ -2,12 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import { fromUtf8 } from '@aws-sdk/util-utf8-browser' -import { getWebCryptoBackend, getNonZeroByteBackend } from '@aws-crypto/web-crypto-backend' +import { + getWebCryptoBackend, + getNonZeroByteBackend, +} from '@aws-crypto/web-crypto-backend' import { concatBuffers } from '@aws-crypto/serialize' -export const sha512 = async (...inputs: (Uint8Array|string)[]) => { +export const sha512 = async (...inputs: (Uint8Array | string)[]) => { // Normalize to Uint8Array and squash into a single value. - const data = concatBuffers(...inputs.map(u => typeof u === 'string' ? fromUtf8(u) : u)) + const data = concatBuffers( + ...inputs.map((u) => (typeof u === 'string' ? fromUtf8(u) : u)) + ) // Prefer the non-zero byte because this will always be the native implementation. const backend = getNonZeroByteBackend(await getWebCryptoBackend()) // Do the hash diff --git a/modules/caching-materials-manager-node/package.json b/modules/caching-materials-manager-node/package.json index ff9389b55..1851461ff 100644 --- a/modules/caching-materials-manager-node/package.json +++ b/modules/caching-materials-manager-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/caching-materials-manager-node/src/caching_materials_manager_node.ts b/modules/caching-materials-manager-node/src/caching_materials_manager_node.ts index f968fbace..bf5fe34a0 100644 --- a/modules/caching-materials-manager-node/src/caching_materials_manager_node.ts +++ b/modules/caching-materials-manager-node/src/caching_materials_manager_node.ts @@ -2,22 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 import { - CachingMaterialsManager, // eslint-disable-line no-unused-vars + CachingMaterialsManager, decorateProperties, getEncryptionMaterials, decryptMaterials, cacheEntryHasExceededLimits, buildCryptographicMaterialsCacheKeyHelpers, - CachingMaterialsManagerInput, // eslint-disable-line no-unused-vars - CryptographicMaterialsCache // eslint-disable-line no-unused-vars + CachingMaterialsManagerInput, + CryptographicMaterialsCache, } from '@aws-crypto/cache-material' import { - NodeMaterialsManager, // eslint-disable-line no-unused-vars + NodeMaterialsManager, NodeDefaultCryptographicMaterialsManager, - NodeAlgorithmSuite, // eslint-disable-line no-unused-vars + NodeAlgorithmSuite, KeyringNode, - NodeGetEncryptionMaterials, // eslint-disable-line no-unused-vars - NodeGetDecryptMaterials // eslint-disable-line no-unused-vars + NodeGetEncryptionMaterials, + NodeGetDecryptMaterials, } from '@aws-crypto/material-management-node' import { sha512 } from './sha512' import { randomBytes } from 'crypto' @@ -25,9 +25,14 @@ import { randomBytes } from 'crypto' const fromUtf8 = (input: string) => Buffer.from(input, 'utf8') const toUtf8 = (input: Uint8Array) => Buffer.from(input).toString('utf8') -const cacheKeyHelpers = buildCryptographicMaterialsCacheKeyHelpers(fromUtf8, toUtf8, sha512) +const cacheKeyHelpers = buildCryptographicMaterialsCacheKeyHelpers( + fromUtf8, + toUtf8, + sha512 +) -export class NodeCachingMaterialsManager implements CachingMaterialsManager { +export class NodeCachingMaterialsManager + implements CachingMaterialsManager { readonly _cache!: CryptographicMaterialsCache readonly _backingMaterialsManager!: NodeMaterialsManager readonly _partition!: string @@ -35,10 +40,11 @@ export class NodeCachingMaterialsManager implements CachingMaterialsManager) { - const backingMaterialsManager = input.backingMaterials instanceof KeyringNode - ? new NodeDefaultCryptographicMaterialsManager(input.backingMaterials) - : input.backingMaterials + constructor(input: CachingMaterialsManagerInput) { + const backingMaterialsManager = + input.backingMaterials instanceof KeyringNode + ? new NodeDefaultCryptographicMaterialsManager(input.backingMaterials) + : (input.backingMaterials as NodeDefaultCryptographicMaterialsManager) /* Precondition: A partition value must exist for NodeCachingMaterialsManager. * The maximum hash function at this time is 512. @@ -49,11 +55,17 @@ export class NodeCachingMaterialsManager implements CachingMaterialsManager(cacheKeyHelpers) - decryptMaterials: NodeGetDecryptMaterials = decryptMaterials(cacheKeyHelpers) - _cacheEntryHasExceededLimits = cacheEntryHasExceededLimits() + getEncryptionMaterials: NodeGetEncryptionMaterials = getEncryptionMaterials< + NodeAlgorithmSuite + >(cacheKeyHelpers) + decryptMaterials: NodeGetDecryptMaterials = decryptMaterials< + NodeAlgorithmSuite + >(cacheKeyHelpers) + _cacheEntryHasExceededLimits = cacheEntryHasExceededLimits< + NodeAlgorithmSuite + >() } diff --git a/modules/caching-materials-manager-node/src/sha512.ts b/modules/caching-materials-manager-node/src/sha512.ts index 1cef41ea3..903ee9411 100644 --- a/modules/caching-materials-manager-node/src/sha512.ts +++ b/modules/caching-materials-manager-node/src/sha512.ts @@ -3,7 +3,8 @@ import { createHash } from 'crypto' -export const sha512 = async (...data: (Uint8Array|string)[]) => data - .map(item => typeof item === 'string' ? Buffer.from(item) : item) - .reduce((hash, item) => hash.update(item), createHash('sha512')) - .digest() +export const sha512 = async (...data: (Uint8Array | string)[]) => + data + .map((item) => (typeof item === 'string' ? Buffer.from(item) : item)) + .reduce((hash, item) => hash.update(item), createHash('sha512')) + .digest() diff --git a/modules/client-browser/package.json b/modules/client-browser/package.json index dd87afb0b..ab70fcc30 100644 --- a/modules/client-browser/package.json +++ b/modules/client-browser/package.json @@ -12,7 +12,9 @@ "version": "1.0.5", "scripts": { "build": "tsc -b tsconfig.json", - "lint": "standard src/*.ts test/**/*.ts" + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts" }, "author": { "name": "AWS Crypto Tools Team", diff --git a/modules/client-node/package.json b/modules/client-node/package.json index 0d63ab248..3dcc49b52 100644 --- a/modules/client-node/package.json +++ b/modules/client-node/package.json @@ -12,7 +12,9 @@ "version": "1.0.4", "scripts": { "build": "tsc -b tsconfig.json", - "lint": "standard src/*.ts test/**/*.ts" + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts" }, "author": { "name": "AWS Crypto Tools Team", diff --git a/modules/decrypt-browser/package.json b/modules/decrypt-browser/package.json index d002039cd..8d1aeb3b2 100644 --- a/modules/decrypt-browser/package.json +++ b/modules/decrypt-browser/package.json @@ -3,7 +3,9 @@ "version": "1.1.1", "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/decrypt-browser/src/decrypt.ts b/modules/decrypt-browser/src/decrypt.ts index c1efbf288..917d5eb2c 100644 --- a/modules/decrypt-browser/src/decrypt.ts +++ b/modules/decrypt-browser/src/decrypt.ts @@ -6,20 +6,20 @@ import { KeyringWebCrypto, WebCryptoDefaultCryptographicMaterialsManager, getDecryptionHelper, - GetSubtleDecrypt, // eslint-disable-line no-unused-vars + GetSubtleDecrypt, needs, - WebCryptoMaterialsManager // eslint-disable-line no-unused-vars + WebCryptoMaterialsManager, } from '@aws-crypto/material-management-browser' import { deserializeSignature, - MessageHeader, // eslint-disable-line no-unused-vars + MessageHeader, deserializeFactory, kdfInfo, decodeBodyHeader, aadFactory, concatBuffers, der2raw, - HeaderInfo // eslint-disable-line no-unused-vars + HeaderInfo, } from '@aws-crypto/serialize' import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-browser' @@ -31,31 +31,49 @@ export interface DecryptResult { plaintext: Uint8Array } -export async function decrypt ( - cmm: KeyringWebCrypto|WebCryptoMaterialsManager, +export async function decrypt( + cmm: KeyringWebCrypto | WebCryptoMaterialsManager, ciphertext: Uint8Array ): Promise { /* If the cmm is a Keyring, wrap it with WebCryptoDefaultCryptographicMaterialsManager. */ - cmm = cmm instanceof KeyringWebCrypto - ? new WebCryptoDefaultCryptographicMaterialsManager(cmm) - : cmm + cmm = + cmm instanceof KeyringWebCrypto + ? new WebCryptoDefaultCryptographicMaterialsManager(cmm) + : cmm const headerInfo = deserialize.deserializeMessageHeader(ciphertext) if (headerInfo === false) throw new Error('Unable to parse Header') const { messageHeader } = headerInfo const { rawHeader, headerIv, headerAuthTag } = headerInfo - const { encryptionContext, encryptedDataKeys, suiteId, messageId } = messageHeader + const { + encryptionContext, + encryptedDataKeys, + suiteId, + messageId, + } = messageHeader const suite = new WebCryptoAlgorithmSuite(suiteId) - const material = await cmm.decryptMaterials({ suite, encryptionContext, encryptedDataKeys }) - const { kdfGetSubtleDecrypt, subtleVerify, dispose } = await getDecryptionHelper(material) + const material = await cmm.decryptMaterials({ + suite, + encryptionContext, + encryptedDataKeys, + }) + const { + kdfGetSubtleDecrypt, + subtleVerify, + dispose, + } = await getDecryptionHelper(material) const info = kdfInfo(suiteId, messageId) const getSubtleDecrypt = kdfGetSubtleDecrypt(info) // The tag is appended to the Data await getSubtleDecrypt(headerIv, rawHeader)(headerAuthTag) // will throw if invalid - const { plaintext, readPos } = await bodyDecrypt({ buffer: ciphertext, getSubtleDecrypt, headerInfo }) + const { plaintext, readPos } = await bodyDecrypt({ + buffer: ciphertext, + getSubtleDecrypt, + headerInfo, + }) dispose() @@ -85,15 +103,33 @@ interface FramedDecryptOptions extends BodyDecryptOptions { readPos: number } -async function bodyDecrypt ({ buffer, getSubtleDecrypt, headerInfo }: BodyDecryptOptions) { - let readPos = headerInfo.headerIv.byteLength + headerInfo.rawHeader.byteLength + headerInfo.headerAuthTag.byteLength +async function bodyDecrypt({ + buffer, + getSubtleDecrypt, + headerInfo, +}: BodyDecryptOptions) { + let readPos = + headerInfo.headerIv.byteLength + + headerInfo.rawHeader.byteLength + + headerInfo.headerAuthTag.byteLength const clearBuffers: ArrayBuffer[] = [] let sequenceNumber = 0 + // This is unfortunate, ideally the eslint no-constant-condition could be resolve + // but at this time, I'm just going to disable this line + // and leave a note to keep myself from replicating this kind of logic. + /* eslint-disable no-constant-condition */ while (true) { + /* eslint-enable no-constant-condition */ + /* Keeping track of the sequence number myself. */ sequenceNumber += 1 - const { clearBlob, frameInfo } = await framedDecrypt({ buffer, getSubtleDecrypt, headerInfo, readPos }) + const { clearBlob, frameInfo } = await framedDecrypt({ + buffer, + getSubtleDecrypt, + headerInfo, + readPos, + }) /* Precondition: The sequenceNumber is required to monotonically increase, starting from 1. * This is to avoid a bad actor from abusing the sequence number on un-signed algorithm suites. @@ -101,7 +137,10 @@ async function bodyDecrypt ({ buffer, getSubtleDecrypt, headerInfo }: BodyDecryp * then the data could be significantly altered just by rearranging the frames. * Non-framed data returns a sequenceNumber of 1. */ - needs(frameInfo.sequenceNumber === sequenceNumber, 'Encrypted body sequence out of order.') + needs( + frameInfo.sequenceNumber === sequenceNumber, + 'Encrypted body sequence out of order.' + ) clearBuffers.push(clearBlob) readPos = frameInfo.readPos @@ -116,15 +155,33 @@ async function bodyDecrypt ({ buffer, getSubtleDecrypt, headerInfo }: BodyDecryp * non-framed decrypt. The names not-withstanding, this supports non-framed decrypt * See decodeBodyHeader (it abstracts framed and non-framed body headers) */ -async function framedDecrypt ({ buffer, getSubtleDecrypt, headerInfo, readPos }: FramedDecryptOptions) { - const { messageHeader: { messageId } } = headerInfo +async function framedDecrypt({ + buffer, + getSubtleDecrypt, + headerInfo, + readPos, +}: FramedDecryptOptions) { + const { + messageHeader: { messageId }, + } = headerInfo const frameInfo = decodeBodyHeader(buffer, headerInfo, readPos) if (!frameInfo) throw new Error('Format Error') const cipherLength = frameInfo.contentLength + frameInfo.tagLength / 8 const contentString = messageAADContentString(frameInfo) - const messageAdditionalData = messageAAD(messageId, contentString, frameInfo.sequenceNumber, frameInfo.contentLength) - const cipherBlob = buffer.slice(frameInfo.readPos, frameInfo.readPos + cipherLength) - const clearBlob = await getSubtleDecrypt(frameInfo.iv, messageAdditionalData)(cipherBlob) + const messageAdditionalData = messageAAD( + messageId, + contentString, + frameInfo.sequenceNumber, + frameInfo.contentLength + ) + const cipherBlob = buffer.slice( + frameInfo.readPos, + frameInfo.readPos + cipherLength + ) + const clearBlob = await getSubtleDecrypt( + frameInfo.iv, + messageAdditionalData + )(cipherBlob) frameInfo.readPos += cipherLength return { clearBlob, frameInfo } } diff --git a/modules/decrypt-node/package.json b/modules/decrypt-node/package.json index f451a3efd..066961eaf 100644 --- a/modules/decrypt-node/package.json +++ b/modules/decrypt-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/decrypt-node/src/decipher_stream.ts b/modules/decrypt-node/src/decipher_stream.ts index b1e5cf1ab..81766bce4 100644 --- a/modules/decrypt-node/src/decipher_stream.ts +++ b/modules/decrypt-node/src/decipher_stream.ts @@ -3,21 +3,20 @@ // @ts-ignore import { Transform as PortableTransform } from 'readable-stream' -import { Transform } from 'stream' // eslint-disable-line no-unused-vars +import { Transform } from 'stream' import { needs, - GetDecipher, // eslint-disable-line no-unused-vars - AwsEsdkJsDecipherGCM // eslint-disable-line no-unused-vars + GetDecipher, + AwsEsdkJsDecipherGCM, } from '@aws-crypto/material-management-node' -import { - aadFactory, - ContentType // eslint-disable-line no-unused-vars -} from '@aws-crypto/serialize' +import { aadFactory, ContentType } from '@aws-crypto/serialize' import { VerifyStream } from './verify_stream' const fromUtf8 = (input: string) => Buffer.from(input, 'utf8') const aadUtility = aadFactory(fromUtf8) -const PortableTransformWithType = ( Transform>PortableTransform) +const PortableTransformWithType = PortableTransform as new ( + ...args: any[] +) => Transform export interface DecipherInfo { messageId: Buffer @@ -39,17 +38,17 @@ export interface BodyInfo { isFinalFrame: boolean } -const ioTick = () => new Promise(resolve => setImmediate(resolve)) -const noop = () => {} +const ioTick = async () => new Promise((resolve) => setImmediate(resolve)) +const noop = () => {} // eslint-disable-line @typescript-eslint/no-empty-function -export function getDecipherStream () { +export function getDecipherStream() { let decipherInfo: DecipherInfo let decipherState: DecipherState = {} as any let pathologicalDrain: Function = noop - let frameComplete: Function|false = false + let frameComplete: Function | false = false return new (class DecipherStream extends PortableTransformWithType { - constructor () { + constructor() { super() this.on('pipe', (source: VerifyStream) => { /* Precondition: The source must be a VerifyStream to emit the required events. */ @@ -59,32 +58,46 @@ export function getDecipherStream () { decipherInfo = info }) .on('BodyInfo', this._onBodyHeader) - .on('AuthTag', async (authTag: Buffer, next: Function) => { - try { - await this._onAuthTag(authTag, next) - } catch (e) { - this.emit('error', e) - } + .on('AuthTag', (authTag: Buffer, next: Function) => { + this._onAuthTag(authTag, next).catch((e) => this.emit('error', e)) }) }) } - _onBodyHeader = ({ iv, contentLength, sequenceNumber, isFinalFrame }: BodyInfo) => { + _onBodyHeader = ({ + iv, + contentLength, + sequenceNumber, + isFinalFrame, + }: BodyInfo) => { /* Precondition: decipherInfo must be set before BodyInfo is sent. */ needs(decipherInfo, 'Malformed State.') /* Precondition: Ciphertext must not be flowing before a BodyHeader is processed. */ needs(!decipherState.decipher, 'Malformed State.') const { messageId, contentType, getDecipher } = decipherInfo - const aadString = aadUtility.messageAADContentString({ contentType, isFinalFrame }) - const messageAAD = aadUtility.messageAAD(messageId, aadString, sequenceNumber, contentLength) - const decipher = getDecipher(iv) - .setAAD(Buffer.from(messageAAD.buffer, messageAAD.byteOffset, messageAAD.byteLength)) + const aadString = aadUtility.messageAADContentString({ + contentType, + isFinalFrame, + }) + const messageAAD = aadUtility.messageAAD( + messageId, + aadString, + sequenceNumber, + contentLength + ) + const decipher = getDecipher(iv).setAAD( + Buffer.from( + messageAAD.buffer, + messageAAD.byteOffset, + messageAAD.byteLength + ) + ) const content: Buffer[] = [] decipherState = { decipher, content, contentLength } } - _transform (chunk: any, _encoding: string, callback: Function) { + _transform(chunk: any, _encoding: string, callback: Function) { /* Precondition: BodyHeader must be parsed before frame data. */ needs(decipherState.decipher, 'Malformed State.') @@ -107,7 +120,7 @@ export function getDecipherStream () { } } - _read (size: number) { + _read(size: number) { /* The _onAuthTag decrypts and pushes the encrypted frame. * If this.push returns false then this stream * should wait until the destination stream calls read. @@ -125,7 +138,7 @@ export function getDecipherStream () { super._read(size) } - _onAuthTag = async (authTag: Buffer, next:Function) => { + _onAuthTag = async (authTag: Buffer, next: Function) => { const { decipher, content, contentLength } = decipherState /* Precondition: _onAuthTag must be called only after a frame has been accumulated. * However there is an edge case. The final frame _can_ be zero length. @@ -169,7 +182,9 @@ export function getDecipherStream () { /* back pressure: if push returns false, wait until _read * has been called. */ - await new Promise(resolve => { pathologicalDrain = resolve }) + await new Promise((resolve) => { + pathologicalDrain = resolve + }) } } @@ -190,7 +205,7 @@ export function getDecipherStream () { frameComplete = false } - _destroy () { + _destroy() { // It is possible to have to destroy the stream before // decipherInfo is set. Especially if the HeaderAuth // is not valid. diff --git a/modules/decrypt-node/src/decrypt.ts b/modules/decrypt-node/src/decrypt.ts index 1adeaf8b2..fb605b709 100644 --- a/modules/decrypt-node/src/decrypt.ts +++ b/modules/decrypt-node/src/decrypt.ts @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { - NodeMaterialsManager, // eslint-disable-line no-unused-vars - KeyringNode // eslint-disable-line no-unused-vars + NodeMaterialsManager, + KeyringNode, } from '@aws-crypto/material-management-node' import { decryptStream } from './decrypt_stream' // @ts-ignore import { finished } from 'readable-stream' -import { Readable, Duplex } from 'stream' // eslint-disable-line no-unused-vars -import { MessageHeader } from '@aws-crypto/serialize' // eslint-disable-line no-unused-vars +import { Readable, Duplex } from 'stream' +import { MessageHeader } from '@aws-crypto/serialize' export interface DecryptOutput { plaintext: Buffer @@ -22,17 +22,19 @@ export interface DecryptOptions { maxBodySize?: number } -export async function decrypt ( - cmm: NodeMaterialsManager|KeyringNode, - ciphertext: Buffer|Uint8Array|Readable|string|NodeJS.ReadableStream, - { encoding, maxBodySize } : DecryptOptions = {} +export async function decrypt( + cmm: NodeMaterialsManager | KeyringNode, + ciphertext: Buffer | Uint8Array | Readable | string | NodeJS.ReadableStream, + { encoding, maxBodySize }: DecryptOptions = {} ): Promise { const stream = decryptStream(cmm, { maxBodySize }) const plaintext: Buffer[] = [] - let messageHeader: MessageHeader|false = false + let messageHeader: MessageHeader | false = false stream - .once('MessageHeader', (header: MessageHeader) => { messageHeader = header }) + .once('MessageHeader', (header: MessageHeader) => { + messageHeader = header + }) .on('data', (chunk: Buffer) => plaintext.push(chunk)) // This will check both Uint8Array|Buffer @@ -51,12 +53,12 @@ export async function decrypt ( return { plaintext: Buffer.concat(plaintext), - messageHeader + messageHeader, } } -function finishedAsync (stream: Duplex) { +async function finishedAsync(stream: Duplex) { return new Promise((resolve, reject) => { - finished(stream, (err: Error) => err ? reject(err) : resolve()) + finished(stream, (err: Error) => (err ? reject(err) : resolve())) }) } diff --git a/modules/decrypt-node/src/decrypt_stream.ts b/modules/decrypt-node/src/decrypt_stream.ts index a6e047989..28a0c2d63 100644 --- a/modules/decrypt-node/src/decrypt_stream.ts +++ b/modules/decrypt-node/src/decrypt_stream.ts @@ -4,13 +4,13 @@ import { NodeDefaultCryptographicMaterialsManager, KeyringNode, - NodeMaterialsManager // eslint-disable-line no-unused-vars + NodeMaterialsManager, } from '@aws-crypto/material-management-node' import { ParseHeaderStream } from './parse_header_stream' import { VerifyStream } from './verify_stream' import { getDecipherStream } from './decipher_stream' import Duplexify from 'duplexify' -import { Duplex } from 'stream' // eslint-disable-line no-unused-vars +import { Duplex } from 'stream' // @ts-ignore import { pipeline, PassThrough } from 'readable-stream' @@ -19,32 +19,39 @@ export interface DecryptStreamOptions { maxBodySize?: number } -export function decryptStream ( - cmm: KeyringNode|NodeMaterialsManager, - { maxBodySize } : DecryptStreamOptions = {} +export function decryptStream( + cmm: KeyringNode | NodeMaterialsManager, + { maxBodySize }: DecryptStreamOptions = {} ): Duplex { /* If the cmm is a Keyring, wrap it with NodeDefaultCryptographicMaterialsManager. */ - cmm = cmm instanceof KeyringNode - ? new NodeDefaultCryptographicMaterialsManager(cmm) - : cmm + cmm = + cmm instanceof KeyringNode + ? new NodeDefaultCryptographicMaterialsManager(cmm) + : cmm const parseHeaderStream = new ParseHeaderStream(cmm) const verifyStream = new VerifyStream({ maxBodySize }) const decipherStream = getDecipherStream() + const stream = new Duplexify(parseHeaderStream, decipherStream) /* pipeline will _either_ stream.destroy or the callback. * decipherStream uses destroy to dispose the material. * So I tack a pass though stream onto the end. */ - pipeline(parseHeaderStream, verifyStream, decipherStream, new PassThrough(), (err: Error) => { - if (err) stream.emit('error', err) - }) - - const stream = new Duplexify(parseHeaderStream, decipherStream) + pipeline( + parseHeaderStream, + verifyStream, + decipherStream, + new PassThrough(), + (err: Error) => { + if (err) stream.emit('error', err) + } + ) // Forward header events - parseHeaderStream - .once('MessageHeader', header => stream.emit('MessageHeader', header)) + parseHeaderStream.once('MessageHeader', (header) => + stream.emit('MessageHeader', header) + ) return stream } diff --git a/modules/decrypt-node/src/parse_header_stream.ts b/modules/decrypt-node/src/parse_header_stream.ts index 9a416bf16..2987d015e 100644 --- a/modules/decrypt-node/src/parse_header_stream.ts +++ b/modules/decrypt-node/src/parse_header_stream.ts @@ -3,20 +3,21 @@ // @ts-ignore import { Transform as PortableTransform } from 'readable-stream' -import { Transform } from 'stream' // eslint-disable-line no-unused-vars +import { Transform } from 'stream' import { NodeAlgorithmSuite, - NodeMaterialsManager, // eslint-disable-line no-unused-vars - getDecryptionHelper + NodeMaterialsManager, + getDecryptionHelper, } from '@aws-crypto/material-management-node' import { deserializeFactory, kdfInfo } from '@aws-crypto/serialize' -import { VerifyInfo } from './verify_stream' // eslint-disable-line no-unused-vars +import { VerifyInfo } from './verify_stream' -const toUtf8 = (input: Uint8Array) => Buffer - .from(input.buffer, input.byteOffset, input.byteLength) - .toString('utf8') +const toUtf8 = (input: Uint8Array) => + Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString('utf8') const deserialize = deserializeFactory(toUtf8, NodeAlgorithmSuite) -const PortableTransformWithType = ( Transform>PortableTransform) +const PortableTransformWithType = PortableTransform as new ( + ...args: any[] +) => Transform interface HeaderState { buffer: Buffer @@ -25,15 +26,18 @@ interface HeaderState { export class ParseHeaderStream extends PortableTransformWithType { private materialsManager!: NodeMaterialsManager private _headerState: HeaderState - constructor (cmm: NodeMaterialsManager) { + constructor(cmm: NodeMaterialsManager) { super() - Object.defineProperty(this, 'materialsManager', { value: cmm, enumerable: true }) + Object.defineProperty(this, 'materialsManager', { + value: cmm, + enumerable: true, + }) this._headerState = { - buffer: Buffer.alloc(0) + buffer: Buffer.alloc(0), } } - _transform (chunk: any, encoding: string, callback: Function) { + _transform(chunk: any, encoding: string, callback: Function) { const { buffer } = this._headerState const headerBuffer = Buffer.concat([buffer, chunk]) const headerInfo = deserialize.deserializeMessageHeader(headerBuffer) @@ -53,24 +57,44 @@ export class ParseHeaderStream extends PortableTransformWithType { .then((material) => { this._headerState.buffer = Buffer.alloc(0) // clear the Buffer... - const { kdfGetDecipher, getVerify, dispose } = getDecryptionHelper(material) + const { kdfGetDecipher, getVerify, dispose } = getDecryptionHelper( + material + ) const info = kdfInfo(messageHeader.suiteId, messageHeader.messageId) const getDecipher = kdfGetDecipher(info) const headerAuth = getDecipher(headerIv) - headerAuth.setAAD(Buffer.from(rawHeader.buffer, rawHeader.byteOffset, rawHeader.byteLength)) - headerAuth.setAuthTag(Buffer.from(headerAuthTag.buffer, headerAuthTag.byteOffset, headerAuthTag.byteLength)) + headerAuth.setAAD( + Buffer.from( + rawHeader.buffer, + rawHeader.byteOffset, + rawHeader.byteLength + ) + ) + headerAuth.setAuthTag( + Buffer.from( + headerAuthTag.buffer, + headerAuthTag.byteOffset, + headerAuthTag.byteLength + ) + ) headerAuth.update(Buffer.alloc(0)) headerAuth.final() // will throw if invalid const verify = getVerify ? getVerify() : void 0 - const verifyInfo: VerifyInfo = { headerInfo, getDecipher, verify, dispose } + const verifyInfo: VerifyInfo = { + headerInfo, + getDecipher, + verify, + dispose, + } this.emit('VerifyInfo', verifyInfo) this.emit('MessageHeader', headerInfo.messageHeader) // The header is parsed, pass control - const readPos = rawHeader.byteLength + headerIv.byteLength + headerAuthTag.byteLength + const readPos = + rawHeader.byteLength + headerIv.byteLength + headerAuthTag.byteLength const tail = headerBuffer.slice(readPos) /* needs calls in downstream _transform streams will throw. * But streams are async. @@ -86,6 +110,6 @@ export class ParseHeaderStream extends PortableTransformWithType { // flush the tail. Stream control is now in the verify and decrypt streams return setImmediate(() => this._transform(tail, encoding, callback)) }) - .catch(err => callback(err)) + .catch((err) => callback(err)) } } diff --git a/modules/decrypt-node/src/verify_stream.ts b/modules/decrypt-node/src/verify_stream.ts index 521a6c31e..3bfbdc8be 100644 --- a/modules/decrypt-node/src/verify_stream.ts +++ b/modules/decrypt-node/src/verify_stream.ts @@ -3,23 +3,25 @@ // @ts-ignore import { Transform as PortableTransform } from 'readable-stream' -import { Transform } from 'stream' // eslint-disable-line no-unused-vars +import { Transform } from 'stream' import { needs, - GetVerify, // eslint-disable-line no-unused-vars - GetDecipher // eslint-disable-line no-unused-vars + GetVerify, + GetDecipher, } from '@aws-crypto/material-management-node' import { deserializeSignature, decodeBodyHeader, - BodyHeader, // eslint-disable-line no-unused-vars - HeaderInfo // eslint-disable-line no-unused-vars + BodyHeader, + HeaderInfo, } from '@aws-crypto/serialize' import { ParseHeaderStream } from './parse_header_stream' -import { DecipherInfo } from './decipher_stream' // eslint-disable-line no-unused-vars +import { DecipherInfo } from './decipher_stream' type AWSVerify = ReturnType -const PortableTransformWithType = ( Transform>PortableTransform) +const PortableTransformWithType = PortableTransform as new ( + ...args: any[] +) => Transform export interface VerifyInfo { headerInfo: HeaderInfo @@ -46,15 +48,21 @@ export class VerifyStream extends PortableTransformWithType { buffer: Buffer.alloc(0), authTagBuffer: Buffer.alloc(0), signatureInfo: Buffer.alloc(0), - sequenceNumber: 0 + sequenceNumber: 0, } private _verify?: AWSVerify private _maxBodySize?: number - constructor ({ maxBodySize }: VerifyStreamOptions) { + constructor({ maxBodySize }: VerifyStreamOptions) { super() /* Precondition: VerifyStream requires maxBodySize must be falsey or a number. */ - needs(!maxBodySize || typeof maxBodySize === 'number', 'Unsupported MaxBodySize.') - Object.defineProperty(this, '_maxBodySize', { value: maxBodySize, enumerable: true }) + needs( + !maxBodySize || typeof maxBodySize === 'number', + 'Unsupported MaxBodySize.' + ) + Object.defineProperty(this, '_maxBodySize', { + value: maxBodySize, + enumerable: true, + }) this.on('pipe', (source: ParseHeaderStream) => { /* Precondition: The source must a ParseHeaderStream emit the required events. */ @@ -68,26 +76,38 @@ export class VerifyStream extends PortableTransformWithType { */ if (verify) { const { rawHeader, headerIv, headerAuthTag } = headerInfo - ;[rawHeader, headerIv, headerAuthTag].forEach(e => verify.update(e)) + ;[rawHeader, headerIv, headerAuthTag].forEach((e) => verify.update(e)) } - Object.defineProperty(this, '_headerInfo', { value: headerInfo, enumerable: true }) - Object.defineProperty(this, '_verify', { value: verify, enumerable: true }) + Object.defineProperty(this, '_headerInfo', { + value: headerInfo, + enumerable: true, + }) + Object.defineProperty(this, '_verify', { + value: verify, + enumerable: true, + }) const decipherInfo: DecipherInfo = { - // @ts-ignore - messageId: Buffer.from(messageId.buffer, messageId.byteOffset, messageId.byteLength), + messageId: Buffer.from( + (messageId as Uint8Array).buffer || messageId, + (messageId as Uint8Array).byteOffset || 0, + messageId.byteLength + ), contentType, getDecipher, - dispose + dispose, } this.emit('DecipherInfo', decipherInfo) }) }) } - _transform (chunk: Buffer, enc: string, callback: Function): any { + _transform(chunk: Buffer, enc: string, callback: Function): any { /* Precondition: VerifyInfo must have initialized the stream. */ - needs(this._headerInfo, 'VerifyStream not configured, VerifyInfo event not yet received.') + needs( + this._headerInfo, + 'VerifyStream not configured, VerifyInfo event not yet received.' + ) // BodyHeader const state = this._verifyState @@ -106,7 +126,10 @@ export class VerifyStream extends PortableTransformWithType { * Before returning *any* cleartext, the stream **MUST** verify the decryption. * This means that I must buffer the message until the AuthTag is reached. */ - needs(!this._maxBodySize || this._maxBodySize >= frameHeader.contentLength, 'maxBodySize exceeded.') + needs( + !this._maxBodySize || this._maxBodySize >= frameHeader.contentLength, + 'maxBodySize exceeded.' + ) /* Keeping track of the sequence number myself. */ state.sequenceNumber += 1 @@ -117,7 +140,10 @@ export class VerifyStream extends PortableTransformWithType { * then the data could be significantly altered just by rearranging the frames. * Non-framed data returns a sequenceNumber of 1. */ - needs(frameHeader.sequenceNumber === state.sequenceNumber, 'Encrypted body sequence out of order.') + needs( + frameHeader.sequenceNumber === state.sequenceNumber, + 'Encrypted body sequence out of order.' + ) if (this._verify) { this._verify.update(frameBuffer.slice(0, frameHeader.readPos)) @@ -154,7 +180,10 @@ export class VerifyStream extends PortableTransformWithType { state.authTagBuffer = Buffer.concat([authTagBuffer, chunk]) return callback() } else { - const finalAuthTagBuffer = Buffer.concat([authTagBuffer, chunk], tagLengthBytes) + const finalAuthTagBuffer = Buffer.concat( + [authTagBuffer, chunk], + tagLengthBytes + ) if (this._verify) { this._verify.update(finalAuthTagBuffer) } @@ -172,7 +201,11 @@ export class VerifyStream extends PortableTransformWithType { /* Overwriting the _transform function. * Data flow control is not handled here. */ - this._transform = (chunk: Buffer, _enc: string, callback: Function) => { + this._transform = ( + chunk: Buffer, + _enc: string, + callback: Function + ) => { if (chunk.length) { state.signatureInfo = Buffer.concat([state.signatureInfo, chunk]) } @@ -202,7 +235,7 @@ export class VerifyStream extends PortableTransformWithType { callback() } - push (chunk: any, encoding?: string | undefined): boolean { + push(chunk: any, encoding?: string | undefined): boolean { // Typescript???? this._verify instanceof Verify is better.... if (this._verify && chunk) { this._verify.update(chunk) @@ -210,11 +243,13 @@ export class VerifyStream extends PortableTransformWithType { return super.push(chunk, encoding) } - _flush (callback: Function) { + _flush(callback: Function) { /* Check for early return (Postcondition): If there is no verify stream do not attempt to verify. */ if (!this._verify) return callback() const { signatureInfo } = this._verifyState - const { buffer, byteOffset, byteLength } = deserializeSignature(signatureInfo) + const { buffer, byteOffset, byteLength } = deserializeSignature( + signatureInfo + ) const signature = Buffer.from(buffer, byteOffset, byteLength) const isVerified = this._verify.awsCryptoVerify(signature) /* Postcondition: The signature must be valid. */ diff --git a/modules/encrypt-browser/package.json b/modules/encrypt-browser/package.json index b076c51c0..47cf2e554 100644 --- a/modules/encrypt-browser/package.json +++ b/modules/encrypt-browser/package.json @@ -3,7 +3,9 @@ "version": "1.1.1", "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/encrypt-browser/src/encrypt.ts b/modules/encrypt-browser/src/encrypt.ts index b0c8af710..49ab0625c 100644 --- a/modules/encrypt-browser/src/encrypt.ts +++ b/modules/encrypt-browser/src/encrypt.ts @@ -3,21 +3,21 @@ import { WebCryptoAlgorithmSuite, - WebCryptoDefaultCryptographicMaterialsManager, // eslint-disable-line no-unused-vars - WebCryptoEncryptionRequest, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars + WebCryptoDefaultCryptographicMaterialsManager, + WebCryptoEncryptionRequest, + EncryptionContext, AlgorithmSuiteIdentifier, getEncryptHelper, KeyringWebCrypto, needs, - WebCryptoMaterialsManager // eslint-disable-line no-unused-vars + WebCryptoMaterialsManager, } from '@aws-crypto/material-management-browser' import { serializeFactory, aadFactory, kdfInfo, concatBuffers, - MessageHeader, // eslint-disable-line no-unused-vars + MessageHeader, SerializationVersion, ObjectType, ContentType, @@ -25,7 +25,7 @@ import { FRAME_LENGTH, MESSAGE_ID_LENGTH, raw2der, - Maximum + Maximum, } from '@aws-crypto/serialize' import { fromUtf8 } from '@aws-sdk/util-utf8-browser' import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend' @@ -45,34 +45,48 @@ export interface EncryptResult { result: Uint8Array } -export async function encrypt ( - cmm: KeyringWebCrypto|WebCryptoMaterialsManager, +export async function encrypt( + cmm: KeyringWebCrypto | WebCryptoMaterialsManager, plaintext: Uint8Array, - { suiteId, encryptionContext = {}, frameLength = FRAME_LENGTH }: EncryptInput = {} + { + suiteId, + encryptionContext = {}, + frameLength = FRAME_LENGTH, + }: EncryptInput = {} ): Promise { /* Precondition: The frameLength must be less than the maximum frame size for browser encryption. */ - needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`) + needs( + frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, + `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}` + ) const backend = await getWebCryptoBackend() if (!backend) throw new Error('No supported crypto backend') /* If the cmm is a Keyring, wrap it with WebCryptoDefaultCryptographicMaterialsManager. */ - cmm = cmm instanceof KeyringWebCrypto - ? new WebCryptoDefaultCryptographicMaterialsManager(cmm) - : cmm + cmm = + cmm instanceof KeyringWebCrypto + ? new WebCryptoDefaultCryptographicMaterialsManager(cmm) + : cmm // Subtle Crypto functions are all one-shot so all the plaintext needs to be available. const plaintextLength = plaintext.byteLength - const suite = suiteId ? new WebCryptoAlgorithmSuite(suiteId) : new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) + const suite = suiteId + ? new WebCryptoAlgorithmSuite(suiteId) + : new WebCryptoAlgorithmSuite( + AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 + ) const encryptionRequest: WebCryptoEncryptionRequest = { suite, encryptionContext, - plaintextLength + plaintextLength, } const material = await cmm.getEncryptionMaterials(encryptionRequest) - const { kdfGetSubtleEncrypt, subtleSign, dispose } = await getEncryptHelper(material) + const { kdfGetSubtleEncrypt, subtleSign, dispose } = await getEncryptHelper( + material + ) const messageId = await backend.randomValues(MESSAGE_ID_LENGTH) @@ -87,7 +101,7 @@ export async function encrypt ( encryptedDataKeys: material.encryptedDataKeys, contentType: ContentType.FRAMED_DATA, headerIvLength: ivLength, - frameLength + frameLength, } const header = serialize.serializeMessageHeader(messageHeader) @@ -95,7 +109,10 @@ export async function encrypt ( const getSubtleEncrypt = kdfGetSubtleEncrypt(info) const headerAuthIv = serialize.headerAuthIv(ivLength) - const headerAuthTag = await getSubtleEncrypt(headerAuthIv, header)(new Uint8Array(0)) + const headerAuthTag = await getSubtleEncrypt( + headerAuthIv, + header + )(new Uint8Array(0)) const numberOfFrames = Math.ceil(plaintextLength / frameLength) /* The final frame has a variable length. @@ -103,16 +120,24 @@ export async function encrypt ( * So I calculate how much of a frame I should have at the end. * This value will NEVER be larger than the frameLength. */ - const finalFrameLength = frameLength - ((numberOfFrames * frameLength) - plaintextLength) + const finalFrameLength = + frameLength - (numberOfFrames * frameLength - plaintextLength) const bodyContent = [] - for (let sequenceNumber = 1; numberOfFrames >= sequenceNumber; sequenceNumber += 1) { + for ( + let sequenceNumber = 1; + numberOfFrames >= sequenceNumber; + sequenceNumber += 1 + ) { const frameIv = serialize.frameIv(ivLength, sequenceNumber) const isFinalFrame = sequenceNumber === numberOfFrames const frameHeader = isFinalFrame ? serialize.finalFrameHeader(sequenceNumber, frameIv, finalFrameLength) : serialize.frameHeader(sequenceNumber, frameIv) - const contentString = messageAADContentString({ contentType: messageHeader.contentType, isFinalFrame }) + const contentString = messageAADContentString({ + contentType: messageHeader.contentType, + isFinalFrame, + }) const messageAdditionalData = messageAAD( messageId, contentString, @@ -129,7 +154,10 @@ export async function encrypt ( (sequenceNumber - 1) * frameLength, isFinalFrame ? finalFrameLength : frameLength ) - const cipherBufferAndAuthTag = await getSubtleEncrypt(frameIv, messageAdditionalData)(framePlaintext) + const cipherBufferAndAuthTag = await getSubtleEncrypt( + frameIv, + messageAdditionalData + )(framePlaintext) bodyContent.push(frameHeader, cipherBufferAndAuthTag) } @@ -145,7 +173,10 @@ export async function encrypt ( if (typeof subtleSign === 'function') { const signatureArrayBuffer = await subtleSign(result) - const derSignature = raw2der(new Uint8Array(signatureArrayBuffer), material.suite) + const derSignature = raw2der( + new Uint8Array(signatureArrayBuffer), + material.suite + ) const signatureInfo = serializeSignatureInfo(derSignature) return { result: concatBuffers(result, signatureInfo), messageHeader } } else { diff --git a/modules/encrypt-node/package.json b/modules/encrypt-node/package.json index f81fc0563..8fe7501b4 100644 --- a/modules/encrypt-node/package.json +++ b/modules/encrypt-node/package.json @@ -3,7 +3,9 @@ "version": "1.0.4", "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/encrypt-node/src/encrypt.ts b/modules/encrypt-node/src/encrypt.ts index 70bb1396f..35e050516 100644 --- a/modules/encrypt-node/src/encrypt.ts +++ b/modules/encrypt-node/src/encrypt.ts @@ -1,16 +1,13 @@ import { - KeyringNode, // eslint-disable-line no-unused-vars - NodeMaterialsManager // eslint-disable-line no-unused-vars + KeyringNode, + NodeMaterialsManager, } from '@aws-crypto/material-management-node' -import { - encryptStream, - EncryptStreamInput // eslint-disable-line no-unused-vars -} from './encrypt_stream' +import { encryptStream, EncryptStreamInput } from './encrypt_stream' // @ts-ignore import { finished } from 'readable-stream' -import { Readable, Duplex } from 'stream' // eslint-disable-line no-unused-vars -import { MessageHeader } from '@aws-crypto/serialize' // eslint-disable-line no-unused-vars +import { Readable, Duplex } from 'stream' +import { MessageHeader } from '@aws-crypto/serialize' interface EncryptInput extends EncryptStreamInput { encoding?: BufferEncoding @@ -21,9 +18,9 @@ export interface EncryptOutput { messageHeader: MessageHeader } -export async function encrypt ( - cmm: KeyringNode|NodeMaterialsManager, - plaintext: Buffer|Uint8Array|Readable|string|NodeJS.ReadableStream, +export async function encrypt( + cmm: KeyringNode | NodeMaterialsManager, + plaintext: Buffer | Uint8Array | Readable | string | NodeJS.ReadableStream, op: EncryptInput = {} ): Promise { const { encoding } = op @@ -36,9 +33,11 @@ export async function encrypt ( const stream = encryptStream(cmm, op) const result: Buffer[] = [] - let messageHeader: MessageHeader|false = false + let messageHeader: MessageHeader | false = false stream - .once('MessageHeader', header => { messageHeader = header }) + .once('MessageHeader', (header) => { + messageHeader = header + }) .on('data', (chunk: Buffer) => result.push(chunk)) // This will check both Uint8Array|Buffer @@ -55,12 +54,12 @@ export async function encrypt ( return { result: Buffer.concat(result), - messageHeader + messageHeader, } } -function finishedAsync (stream: Duplex) { +async function finishedAsync(stream: Duplex) { return new Promise((resolve, reject) => { - finished(stream, (err: Error) => err ? reject(err) : resolve()) + finished(stream, (err: Error) => (err ? reject(err) : resolve())) }) } diff --git a/modules/encrypt-node/src/encrypt_stream.ts b/modules/encrypt-node/src/encrypt_stream.ts index c0716f883..18e7aeff5 100644 --- a/modules/encrypt-node/src/encrypt_stream.ts +++ b/modules/encrypt-node/src/encrypt_stream.ts @@ -2,26 +2,35 @@ // SPDX-License-Identifier: Apache-2.0 import { - NodeDefaultCryptographicMaterialsManager, NodeAlgorithmSuite, AlgorithmSuiteIdentifier, // eslint-disable-line no-unused-vars - KeyringNode, NodeEncryptionMaterial, getEncryptHelper, EncryptionContext, // eslint-disable-line no-unused-vars - NodeMaterialsManager, // eslint-disable-line no-unused-vars - needs + NodeDefaultCryptographicMaterialsManager, + NodeAlgorithmSuite, + AlgorithmSuiteIdentifier, + KeyringNode, + NodeEncryptionMaterial, + getEncryptHelper, + EncryptionContext, + NodeMaterialsManager, + needs, } from '@aws-crypto/material-management-node' import { getFramedEncryptStream } from './framed_encrypt_stream' import { SignatureStream } from './signature_stream' import Duplexify from 'duplexify' import { randomBytes } from 'crypto' import { - MessageHeader, // eslint-disable-line no-unused-vars - serializeFactory, kdfInfo, ContentType, SerializationVersion, ObjectType, + MessageHeader, + serializeFactory, + kdfInfo, + ContentType, + SerializationVersion, + ObjectType, FRAME_LENGTH, MESSAGE_ID_LENGTH, - Maximum + Maximum, } from '@aws-crypto/serialize' // @ts-ignore import { pipeline } from 'readable-stream' -import { Duplex } from 'stream' // eslint-disable-line no-unused-vars +import { Duplex } from 'stream' const fromUtf8 = (input: string) => Buffer.from(input, 'utf8') const { serializeMessageHeader, headerAuthIv } = serializeFactory(fromUtf8) @@ -40,50 +49,71 @@ export interface EncryptStreamInput { * @param cmm NodeMaterialsManager|KeyringNode * @param op EncryptStreamInput */ -export function encryptStream ( - cmm: KeyringNode|NodeMaterialsManager, +export function encryptStream( + cmm: KeyringNode | NodeMaterialsManager, op: EncryptStreamInput = {} ): Duplex { - const { suiteId, encryptionContext = {}, frameLength = FRAME_LENGTH, plaintextLength } = op + const { + suiteId, + encryptionContext = {}, + frameLength = FRAME_LENGTH, + plaintextLength, + } = op /* Precondition: The frameLength must be less than the maximum frame size Node.js stream. */ - needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`) + needs( + frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, + `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}` + ) /* If the cmm is a Keyring, wrap it with NodeDefaultCryptographicMaterialsManager. */ - cmm = cmm instanceof KeyringNode - ? new NodeDefaultCryptographicMaterialsManager(cmm) - : cmm + cmm = + cmm instanceof KeyringNode + ? new NodeDefaultCryptographicMaterialsManager(cmm) + : cmm const suite = suiteId && new NodeAlgorithmSuite(suiteId) const wrappingStream = new Duplexify() - cmm.getEncryptionMaterials({ suite, encryptionContext, plaintextLength }) + cmm + .getEncryptionMaterials({ suite, encryptionContext, plaintextLength }) .then(async (material) => { const { dispose, getSigner } = getEncryptHelper(material) - const { getCipher, messageHeader, rawHeader } = getEncryptionInfo(material, frameLength) + const { getCipher, messageHeader, rawHeader } = getEncryptionInfo( + material, + frameLength + ) wrappingStream.emit('MessageHeader', messageHeader) - const encryptStream = getFramedEncryptStream(getCipher, messageHeader, dispose, plaintextLength) + const encryptStream = getFramedEncryptStream( + getCipher, + messageHeader, + dispose, + plaintextLength + ) const signatureStream = new SignatureStream(getSigner) pipeline(encryptStream, signatureStream) wrappingStream.setReadable(signatureStream) // Flush the rawHeader through the signatureStream - rawHeader.forEach(buff => signatureStream.write(buff)) + rawHeader.forEach((buff) => signatureStream.write(buff)) // @ts-ignore until readable-stream exports v3 types... wrappingStream.setWritable(encryptStream) }) - .catch(err => wrappingStream.emit('error', err)) + .catch((err) => wrappingStream.emit('error', err)) return wrappingStream } -export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLength: number) { +export function getEncryptionInfo( + material: NodeEncryptionMaterial, + frameLength: number +) { const { kdfGetCipher } = getEncryptHelper(material) const { encryptionContext } = material @@ -98,10 +128,12 @@ export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLengt encryptedDataKeys: Object.freeze(material.encryptedDataKeys), // freeze me please contentType: ContentType.FRAMED_DATA, headerIvLength: ivLength, - frameLength + frameLength, }) - const { buffer, byteOffset, byteLength } = serializeMessageHeader(messageHeader) + const { buffer, byteOffset, byteLength } = serializeMessageHeader( + messageHeader + ) const headerBuffer = Buffer.from(buffer, byteOffset, byteLength) const info = kdfInfo(messageHeader.suiteId, messageHeader.messageId) const getCipher = kdfGetCipher(info) @@ -115,6 +147,6 @@ export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLengt return { getCipher, messageHeader, - rawHeader: [headerBuffer, headerIv, headerAuth] + rawHeader: [headerBuffer, headerIv, headerAuth], } } diff --git a/modules/encrypt-node/src/framed_encrypt_stream.ts b/modules/encrypt-node/src/framed_encrypt_stream.ts index ddc906bb4..f7d60acfb 100644 --- a/modules/encrypt-node/src/framed_encrypt_stream.ts +++ b/modules/encrypt-node/src/framed_encrypt_stream.ts @@ -2,17 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { - serializeFactory, aadFactory, - MessageHeader, // eslint-disable-line no-unused-vars - Maximum + serializeFactory, + aadFactory, + MessageHeader, + Maximum, } from '@aws-crypto/serialize' // @ts-ignore import { Transform as PortableTransform } from 'readable-stream' -import { Transform } from 'stream' // eslint-disable-line no-unused-vars +import { Transform } from 'stream' import { - GetCipher, // eslint-disable-line no-unused-vars - AwsEsdkJsCipherGCM, // eslint-disable-line no-unused-vars - needs + GetCipher, + AwsEsdkJsCipherGCM, + needs, } from '@aws-crypto/material-management-node' const fromUtf8 = (input: string) => Buffer.from(input, 'utf8') @@ -30,16 +31,28 @@ interface EncryptFrame { content: Buffer[] bodyHeader: Buffer headerSent?: boolean - cipher: AwsEsdkJsCipherGCM, + cipher: AwsEsdkJsCipherGCM isFinalFrame: boolean } +const PortableTransformWithType = PortableTransform as new ( + ...args: any[] +) => Transform -const ioTick = () => new Promise(resolve => setImmediate(resolve)) -const noop = () => {} +const ioTick = async () => new Promise((resolve) => setImmediate(resolve)) +const noop = () => {} // eslint-disable-line @typescript-eslint/no-empty-function type ErrBack = (err?: Error) => void -export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: MessageHeader, dispose: Function, plaintextLength?: number) { - let accumulatingFrame: AccumulatingFrame = { contentLength: 0, content: [], sequenceNumber: 1 } +export function getFramedEncryptStream( + getCipher: GetCipher, + messageHeader: MessageHeader, + dispose: Function, + plaintextLength?: number +) { + let accumulatingFrame: AccumulatingFrame = { + contentLength: 0, + content: [], + sequenceNumber: 1, + } let pathologicalDrain: Function = noop const { frameLength } = messageHeader @@ -47,21 +60,28 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes * The Maximum.BYTES_PER_MESSAGE is set to be within Number.MAX_SAFE_INTEGER * See serialize/identifiers.ts enum Maximum for more details. */ - needs(!plaintextLength || (plaintextLength >= 0 && Maximum.BYTES_PER_MESSAGE >= plaintextLength), 'plaintextLength out of bounds.') + needs( + !plaintextLength || + (plaintextLength >= 0 && Maximum.BYTES_PER_MESSAGE >= plaintextLength), + 'plaintextLength out of bounds.' + ) /* Keeping the messageHeader, accumulatingFrame and pathologicalDrain private is the intention here. * It is already unlikely that these values could be touched in the current composition of streams, * but a different composition may change this. * Since we are handling the plain text here, it seems prudent to take extra measures. */ - return new (class FramedEncryptStream extends ( Transform>PortableTransform) { - _transform (chunk: Buffer, encoding: string, callback: ErrBack) { + return new (class FramedEncryptStream extends PortableTransformWithType { + _transform(chunk: Buffer, encoding: string, callback: ErrBack) { const contentLeft = frameLength - accumulatingFrame.contentLength /* Precondition: Must not process more than plaintextLength. * The plaintextLength is the MAXIMUM value that can be encrypted. */ - needs(!plaintextLength || (plaintextLength -= chunk.length) >= 0, 'Encrypted data exceeded plaintextLength.') + needs( + !plaintextLength || (plaintextLength -= chunk.length) >= 0, + 'Encrypted data exceeded plaintextLength.' + ) /* Check for early return (Postcondition): Have not accumulated a frame. */ if (contentLeft > chunk.length) { @@ -81,7 +101,7 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes pendingFrame: accumulatingFrame, messageHeader, getCipher, - isFinalFrame: false + isFinalFrame: false, }) // Reset frame state for next frame @@ -89,7 +109,7 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes accumulatingFrame = { contentLength: 0, content: [], - sequenceNumber: sequenceNumber + 1 + sequenceNumber: sequenceNumber + 1, } this._flushEncryptFrame(encryptFrame) @@ -97,12 +117,12 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes .catch(callback) } - _flush (callback: ErrBack) { + _flush(callback: ErrBack) { const encryptFrame = getEncryptFrame({ pendingFrame: accumulatingFrame, messageHeader, getCipher, - isFinalFrame: true + isFinalFrame: true, }) this._flushEncryptFrame(encryptFrame) @@ -110,11 +130,11 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes .catch(callback) } - _destroy () { + _destroy() { dispose() } - _read (size: number) { + _read(size: number) { super._read(size) /* The _flushEncryptFrame encrypts and pushes the frame. * If this.push returns false then this stream @@ -131,7 +151,7 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes pathologicalDrain = noop } - async _flushEncryptFrame (encryptingFrame: EncryptFrame) { + async _flushEncryptFrame(encryptingFrame: EncryptFrame) { const { content, cipher, bodyHeader, isFinalFrame } = encryptingFrame this.push(bodyHeader) @@ -152,14 +172,19 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes /* Push the authTag onto the end. Yes, I am abusing the name. */ cipherContent.push(cipher.getAuthTag()) - needs(frameSize === frameLength || (isFinalFrame && frameLength >= frameSize), 'Malformed frame') + needs( + frameSize === frameLength || (isFinalFrame && frameLength >= frameSize), + 'Malformed frame' + ) for (const cipherText of cipherContent) { if (!this.push(cipherText)) { /* back pressure: if push returns false, wait until _read * has been called. */ - await new Promise(resolve => { pathologicalDrain = resolve }) + await new Promise((resolve) => { + pathologicalDrain = resolve + }) } } @@ -169,13 +194,13 @@ export function getFramedEncryptStream (getCipher: GetCipher, messageHeader: Mes } type EncryptFrameInput = { - pendingFrame: AccumulatingFrame, - messageHeader: MessageHeader, - getCipher: GetCipher, + pendingFrame: AccumulatingFrame + messageHeader: MessageHeader + getCipher: GetCipher isFinalFrame: boolean } -export function getEncryptFrame (input: EncryptFrameInput): EncryptFrame { +export function getEncryptFrame(input: EncryptFrameInput): EncryptFrame { const { pendingFrame, messageHeader, getCipher, isFinalFrame } = input const { sequenceNumber, contentLength, content } = pendingFrame const { frameLength, contentType, messageId, headerIvLength } = messageHeader @@ -185,13 +210,31 @@ export function getEncryptFrame (input: EncryptFrameInput): EncryptFrame { * In the case of the final frame, * it MUST NOT be larger than the frame length. */ - needs(frameLength === contentLength || (isFinalFrame && frameLength >= contentLength), `Malformed frame length and content length: ${JSON.stringify({ frameLength, contentLength, isFinalFrame })}`) + needs( + frameLength === contentLength || + (isFinalFrame && frameLength >= contentLength), + `Malformed frame length and content length: ${JSON.stringify({ + frameLength, + contentLength, + isFinalFrame, + })}` + ) const frameIv = serialize.frameIv(headerIvLength, sequenceNumber) - const bodyHeader = Buffer.from(isFinalFrame - ? finalFrameHeader(sequenceNumber, frameIv, contentLength) - : frameHeader(sequenceNumber, frameIv)) - const contentString = aadUtility.messageAADContentString({ contentType, isFinalFrame }) - const { buffer, byteOffset, byteLength } = aadUtility.messageAAD(messageId, contentString, sequenceNumber, contentLength) + const bodyHeader = Buffer.from( + isFinalFrame + ? finalFrameHeader(sequenceNumber, frameIv, contentLength) + : frameHeader(sequenceNumber, frameIv) + ) + const contentString = aadUtility.messageAADContentString({ + contentType, + isFinalFrame, + }) + const { buffer, byteOffset, byteLength } = aadUtility.messageAAD( + messageId, + contentString, + sequenceNumber, + contentLength + ) const cipher = getCipher(frameIv) cipher.setAAD(Buffer.from(buffer, byteOffset, byteLength)) diff --git a/modules/encrypt-node/src/signature_stream.ts b/modules/encrypt-node/src/signature_stream.ts index 548260402..796e5d9d0 100644 --- a/modules/encrypt-node/src/signature_stream.ts +++ b/modules/encrypt-node/src/signature_stream.ts @@ -2,27 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 import { Transform } from 'stream' -import { GetSigner } from '@aws-crypto/material-management-node' // eslint-disable-line no-unused-vars +import { GetSigner } from '@aws-crypto/material-management-node' import { serializeSignatureInfo } from '@aws-crypto/serialize' type AWSSigner = ReturnType export class SignatureStream extends Transform { - private _signer!: AWSSigner|undefined - constructor (getSigner?: GetSigner) { + private _signer!: AWSSigner | undefined + constructor(getSigner?: GetSigner) { super() const value = getSigner && getSigner() Object.defineProperty(this, '_signer', { value, enumerable: true }) } - _transform (chunk: any, _encoding: string, callback: Function) { + _transform(chunk: any, _encoding: string, callback: Function) { // If we have a signer, push the data to it this._signer && this._signer.update(chunk) // forward the data on callback(null, chunk) } - _flush (callback: Function) { + _flush(callback: Function) { if (this._signer) { const signature = this._signer.awsCryptoSign() this.push(serializeSignatureInfo(signature)) diff --git a/modules/example-browser/package.json b/modules/example-browser/package.json index 6de83adf8..19b531068 100644 --- a/modules/example-browser/package.json +++ b/modules/example-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage", diff --git a/modules/example-browser/src/aes_simple.ts b/modules/example-browser/src/aes_simple.ts index 5b78f69d3..62ca939f7 100644 --- a/modules/example-browser/src/aes_simple.ts +++ b/modules/example-browser/src/aes_simple.ts @@ -11,12 +11,12 @@ import { RawAesKeyringWebCrypto, encrypt, decrypt, - synchronousRandomValues + synchronousRandomValues, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' /* This is done to facilitate testing. */ -export async function testAES () { +export async function testAES() { /* You need to specify a name * and a namespace for raw encryption key providers. * The name and namespace that you use in the decryption keyring *must* be an exact, @@ -26,16 +26,25 @@ export async function testAES () { const keyNamespace = 'aes-namespace' /* The wrapping suite defines the AES-GCM algorithm suite to use. */ - const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + const wrappingSuite = + RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING // Get your plaintext master key from wherever you store it. const unencryptedMasterKey = synchronousRandomValues(32) /* Import the plaintext master key into a WebCrypto CryptoKey. */ - const masterKey = await RawAesKeyringWebCrypto.importCryptoKey(unencryptedMasterKey, wrappingSuite) + const masterKey = await RawAesKeyringWebCrypto.importCryptoKey( + unencryptedMasterKey, + wrappingSuite + ) /* Configure the Raw AES keyring. */ - const keyring = new RawAesKeyringWebCrypto({ keyName, keyNamespace, wrappingSuite, masterKey }) + const keyring = new RawAesKeyringWebCrypto({ + keyName, + keyNamespace, + wrappingSuite, + masterKey, + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -49,14 +58,16 @@ export async function testAES () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ const plainText = new Uint8Array([1, 2, 3, 4, 5]) /* Encrypt the data. */ - const { result } = await encrypt(keyring, plainText, { encryptionContext: context }) + const { result } = await encrypt(keyring, plainText, { + encryptionContext: context, + }) /* Log the plain text * only for testing and to show that it works. @@ -83,11 +94,10 @@ export async function testAES () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. diff --git a/modules/example-browser/src/caching_cmm.ts b/modules/example-browser/src/caching_cmm.ts index 577c7e809..06f539f6f 100644 --- a/modules/example-browser/src/caching_cmm.ts +++ b/modules/example-browser/src/caching_cmm.ts @@ -12,7 +12,7 @@ import { encrypt, decrypt, WebCryptoCachingMaterialsManager, - getLocalCryptographicMaterialsCache + getLocalCryptographicMaterialsCache, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' @@ -22,14 +22,19 @@ import { toBase64 } from '@aws-sdk/util-base64-browser' * Use any method you like to get credentials into the browser. * See kms.webpack.config */ -declare const credentials: {accessKeyId: string, secretAccessKey:string, sessionToken:string } +declare const credentials: { + accessKeyId: string + secretAccessKey: string + sessionToken: string +} /* This is done to facilitate testing. */ -export async function testCachingCMMExample () { +export async function testCachingCMMExample() { /* This example uses a KMS keyring. The generator key in a KMS keyring generates and encrypts the data key. * The caller needs kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding additional KMS keys that can decrypt. * The caller must have kms:Encrypt permission for every CMK in keyIds. @@ -43,7 +48,9 @@ export async function testCachingCMMExample () { * or omit the `keyIds` parameter. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* Need a client provider that will inject correct credentials. * The credentials here are injected by webpack from your environment bundle is created @@ -63,12 +70,16 @@ export async function testCachingCMMExample () { credentials: { accessKeyId, secretAccessKey, - sessionToken - } + sessionToken, + }, }) /* You must configure the KMS keyring with your KMS CMKs */ - const keyring = new KmsKeyringBrowser({ clientProvider, generatorKeyId, keyIds }) + const keyring = new KmsKeyringBrowser({ + clientProvider, + generatorKeyId, + keyIds, + }) /* Create a cache to hold the data keys (and related cryptographic material). * This example uses the local cache provided by the Encryption SDK. @@ -116,7 +127,7 @@ export async function testCachingCMMExample () { partition, maxAge, maxBytesEncrypted, - maxMessagesEncrypted + maxMessagesEncrypted, }) /* Encryption context is a *very* powerful tool for controlling @@ -140,7 +151,7 @@ export async function testCachingCMMExample () { const encryptionContext = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ @@ -186,11 +197,10 @@ export async function testCachingCMMExample () { * do not include a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs that you supplied to the `encrypt` function are included in the encryption context that the `decrypt` function returns. */ - Object - .entries(encryptionContext) - .forEach(([key, value]) => { - if (decryptedContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(encryptionContext).forEach(([key, value]) => { + if (decryptedContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. diff --git a/modules/example-browser/src/fallback.ts b/modules/example-browser/src/fallback.ts index f976655e9..b06b4afde 100644 --- a/modules/example-browser/src/fallback.ts +++ b/modules/example-browser/src/fallback.ts @@ -13,7 +13,7 @@ import { decrypt, synchronousRandomValues, configureFallback, - AlgorithmSuiteIdentifier + AlgorithmSuiteIdentifier, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' @@ -30,7 +30,7 @@ import { subtle } from './msrcrypto' configureFallback(subtle) /* This is done to facilitate testing. */ -export async function testFallback () { +export async function testFallback() { /* You need to specify a name * and a namespace for raw encryption key providers. * The name and namespace that you use in the decryption keyring *must* be an exact, @@ -40,16 +40,25 @@ export async function testFallback () { const keyNamespace = 'aes-namespace' /* The wrapping suite defines the AES-GCM algorithm suite to use. */ - const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + const wrappingSuite = + RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING // Get your plaintext master key from wherever you store it. const unencryptedMasterKey = synchronousRandomValues(32) /* Import the plaintext master key into a WebCrypto CryptoKey. */ - const masterKey = await RawAesKeyringWebCrypto.importCryptoKey(unencryptedMasterKey, wrappingSuite) + const masterKey = await RawAesKeyringWebCrypto.importCryptoKey( + unencryptedMasterKey, + wrappingSuite + ) /* Configure the Raw AES keyring. */ - const keyring = new RawAesKeyringWebCrypto({ keyName, keyNamespace, wrappingSuite, masterKey }) + const keyring = new RawAesKeyringWebCrypto({ + keyName, + keyNamespace, + wrappingSuite, + masterKey, + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -63,20 +72,26 @@ export async function testFallback () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ const plainText = new Uint8Array([1, 2, 3, 4, 5]) /* Encrypt the data. */ - const { result } = await encrypt(keyring, plainText, { encryptionContext: context, suiteId: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16 }) + const { result } = await encrypt(keyring, plainText, { + encryptionContext: context, + suiteId: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16, + }) /* Log the plain text * only for testing and to show that it works. */ console.log('plainText:', plainText) - document.body.insertAdjacentHTML('beforeend', `

plainText:

${plainText}

`) + document.body.insertAdjacentHTML( + 'beforeend', + `

plainText:

${plainText}

` + ) /* Log the base64-encoded result * so that you can try decrypting it with another AWS Encryption SDK implementation. @@ -97,17 +112,19 @@ export async function testFallback () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. */ console.log(plaintext) - document.body.insertAdjacentHTML('beforeend', `

plainText:

${plaintext}

`) + document.body.insertAdjacentHTML( + 'beforeend', + `

plainText:

${plaintext}

` + ) /* Return the values to make testing easy. */ return { plainText, plaintext } diff --git a/modules/example-browser/src/index.ts b/modules/example-browser/src/index.ts index 66a57fbfd..924cf6717 100644 --- a/modules/example-browser/src/index.ts +++ b/modules/example-browser/src/index.ts @@ -2,6 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 /* - * This library has no exported implementation. - * It is intended to be used as a reference. - */ + * This library has no exported implementation. + * It is intended to be used as a reference. + */ diff --git a/modules/example-browser/src/kms_simple.ts b/modules/example-browser/src/kms_simple.ts index 52e972fff..039732575 100644 --- a/modules/example-browser/src/kms_simple.ts +++ b/modules/example-browser/src/kms_simple.ts @@ -10,7 +10,7 @@ import { KMS, getClient, encrypt, - decrypt + decrypt, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' @@ -20,14 +20,19 @@ import { toBase64 } from '@aws-sdk/util-base64-browser' * Use any method you like to get credentials into the browser. * See kms.webpack.config */ -declare const credentials: {accessKeyId: string, secretAccessKey:string, sessionToken:string } +declare const credentials: { + accessKeyId: string + secretAccessKey: string + sessionToken: string +} /* This is done to facilitate testing. */ -export async function testKmsSimpleExample () { +export async function testKmsSimpleExample() { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding alternate KMS keys that can decrypt. * Access to kms:Encrypt is required for every CMK in keyIds. @@ -36,7 +41,9 @@ export async function testKmsSimpleExample () { * In this example, I am using the same CMK. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* Need a client provider that will inject correct credentials. * The credentials here are injected by webpack from your environment bundle is created @@ -56,12 +63,16 @@ export async function testKmsSimpleExample () { credentials: { accessKeyId, secretAccessKey, - sessionToken - } + sessionToken, + }, }) /* The KMS keyring must be configured with the desired CMKs */ - const keyring = new KmsKeyringBrowser({ clientProvider, generatorKeyId, keyIds }) + const keyring = new KmsKeyringBrowser({ + clientProvider, + generatorKeyId, + keyIds, + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -75,14 +86,16 @@ export async function testKmsSimpleExample () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ const plainText = new Uint8Array([1, 2, 3, 4, 5]) /* Encrypt the data. */ - const { result } = await encrypt(keyring, plainText, { encryptionContext: context }) + const { result } = await encrypt(keyring, plainText, { + encryptionContext: context, + }) /* Log the plain text * only for testing and to show that it works. @@ -109,11 +122,10 @@ export async function testKmsSimpleExample () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. diff --git a/modules/example-browser/src/multi_keyring.ts b/modules/example-browser/src/multi_keyring.ts index b0ebc7d9f..0aec820eb 100644 --- a/modules/example-browser/src/multi_keyring.ts +++ b/modules/example-browser/src/multi_keyring.ts @@ -15,7 +15,7 @@ import { MultiKeyringWebCrypto, encrypt, decrypt, - synchronousRandomValues + synchronousRandomValues, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' @@ -25,14 +25,19 @@ import { toBase64 } from '@aws-sdk/util-base64-browser' * Use any method you like to get credentials into the browser. * See kms.webpack.config */ -declare const credentials: {accessKeyId: string, secretAccessKey:string, sessionToken:string } +declare const credentials: { + accessKeyId: string + secretAccessKey: string + sessionToken: string +} /* This is done to facilitate testing. */ -export async function testMultiKeyringExample () { +export async function testMultiKeyringExample() { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding alternate KMS keys that can decrypt. * Access to kms:Encrypt is required for every CMK in keyIds. @@ -41,7 +46,9 @@ export async function testMultiKeyringExample () { * In this example, I am using the same CMK. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* Need a client provider that will inject correct credentials. * The credentials here are injected by webpack @@ -63,12 +70,16 @@ export async function testMultiKeyringExample () { credentials: { accessKeyId, secretAccessKey, - sessionToken - } + sessionToken, + }, }) /* The KMS keyring must be configured with the desired CMKs */ - const kmsKeyring = new KmsKeyringBrowser({ clientProvider, generatorKeyId, keyIds }) + const kmsKeyring = new KmsKeyringBrowser({ + clientProvider, + generatorKeyId, + keyIds, + }) /* You need to specify a name * and a namespace for raw encryption key providers. @@ -79,19 +90,31 @@ export async function testMultiKeyringExample () { const keyNamespace = 'aes-namespace' /* The wrapping suite defines the AES-GCM algorithm suite to use. */ - const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + const wrappingSuite = + RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING // Get your plaintext master key from its storage location. const unencryptedMasterKey = synchronousRandomValues(32) /* The plaintext master key must be imported into a WebCrypto CryptoKey. */ - const masterKey = await RawAesKeyringWebCrypto.importCryptoKey(unencryptedMasterKey, wrappingSuite) + const masterKey = await RawAesKeyringWebCrypto.importCryptoKey( + unencryptedMasterKey, + wrappingSuite + ) /* Configure the Raw AES keyring. */ - const aesKeyring = new RawAesKeyringWebCrypto({ keyName, keyNamespace, wrappingSuite, masterKey }) + const aesKeyring = new RawAesKeyringWebCrypto({ + keyName, + keyNamespace, + wrappingSuite, + masterKey, + }) /* Combine the two keyrings into a multi-keyring. */ - const keyring = new MultiKeyringWebCrypto({ generator: kmsKeyring, children: [ aesKeyring ] }) + const keyring = new MultiKeyringWebCrypto({ + generator: kmsKeyring, + children: [aesKeyring], + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -105,14 +128,16 @@ export async function testMultiKeyringExample () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ const plainText = new Uint8Array([1, 2, 3, 4, 5]) /* Encrypt the data. */ - const { result } = await encrypt(keyring, plainText, { encryptionContext: context }) + const { result } = await encrypt(keyring, plainText, { + encryptionContext: context, + }) /* Log the plain text * only for testing and to show that it works. @@ -146,11 +171,10 @@ export async function testMultiKeyringExample () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. diff --git a/modules/example-browser/src/rsa_simple.ts b/modules/example-browser/src/rsa_simple.ts index e39d3cf10..ba0ea85ee 100644 --- a/modules/example-browser/src/rsa_simple.ts +++ b/modules/example-browser/src/rsa_simple.ts @@ -7,26 +7,58 @@ */ import { - RsaImportableKey, // eslint-disable-line no-unused-vars + RsaImportableKey, RawRsaKeyringWebCrypto, encrypt, - decrypt + decrypt, } from '@aws-crypto/client-browser' import { toBase64 } from '@aws-sdk/util-base64-browser' /* This is done to facilitate testing. */ -export async function testRSA () { +export async function testRSA() { /* JWK for the RSA Keys to use. * These keys are *Public*! * *DO NOT USE* */ - const privateRsaJwkKey: RsaImportableKey = { 'alg': 'RSA-OAEP-256', 'd': 'XcAlS3OYtZ5F3BFGRQH5B8soiqstUk9JkH6_sUhBUfM7yjFpn3MQACtGgOKsFIO01KWCVl7Cn6E3c-MuuT3QqNQrUx8n-WrJU8qNpDOGJ5CVpG9-xTSQVNzRV92gj8g7-BIgehtzMmirXXNsb1XeTg9zsm3iptt9VyhplGqcgOdmm72sT1Z8ZmkagaElHSg0dR1ZNGgzSfTtRg_J1tTh7cmFb1LVz069o6cRaa5ueOPNKxmEslBdVWsDo9naxd_keLiqOOMIQp-KlLuQ-Zhn5fZyqxkRPGjTKZZHitgurzfWG4ERjjrYCbZsOjEt9Tj8FXXUB8bd3qRPy5UkN-XLEQ', 'dp': 'V8QYdWm4OqWpfF_NPdCGr5eqztfHiQQn1NLmkvNO8c9dc2yNizZ4GxtNNEARYjgnLK0ROCoiK5yamtVDyjZ_zzZUvE0CG8iNRg1qvaOM8n_7B2YgmUs9rJ-QKK3HVEsi_M0x-hHeRl3ocAkNfby3__yt6s43FvyrccQh89WcAr0', 'dq': 'NT5lrYlvkOwXIHl8P9AQm1nNL0RkHSrWahYlagRkyU3ELySlWr2laDxXzPnngpuBvyA98iq6Z2JTn8ArtXXvTqQk6BF6np6qqg1QNQxsQeU4Aj3xOMV9EGh57Zpa8Rs0jVydxBdlRW03Fr0UChHKxmT2kS0622gdlGQAs3YxMck', 'e': 'AQAB', 'ext': true, 'key_ops': ['unwrapKey'], 'kty': 'RSA', 'n': '6k_jrxg7mpz7CzgAr6eRqJr1VlvjJ9uQY71hadkDZkLLZHiMl7hz73lqq3w2MfHCa3Pf3BVo5TCXGYuxKOlPb7bH0WWpMeAzOKR_X27UqfA8MBVGb4YO5HXqw0jup8-I-Zi3CQAmP87uE6GDuh7xzeAcwpGD5xE0N74-uWq3YS92PFHCavtryx-ad9VGTgfAbkV3k1-RSxIiZjzbAt3exBAn5EjMfF6FMI70_HYqO-5xGv_aAPSa1OMc_buK5QACN7gmFwqHBzw98v93iyGUc4_XJNL-jPzKNP4AT1zMc6p6RxF3SYytNq7iXIjUmm-oY8fvCSmT1F13XKdzv7DLOw', 'p': '9dGuBwEDeOHFwJ_AQXHBWu53bv_L1_9lh2X-NEBO1B7YMhYWu2nMqXEvLpwvPqyBXwWnuPdfGqu6BHv22RDAF7Lu_oUshq-9dzSwFxaC5PQ2NwtHnz0-zwhEzCE3Qw9t63_OXX87gjp5vy6c5bvb3B9EbZU33Xf9nqVEJhzFreU', 'q': '9AQ0oYhctBbFuIu4jt1HBmqQGGAECbhQAMw324MX8pVUg6GOtF0X822iEsq7aIfY8u5nTWu1kKl6s84US1yII0sJmW2Jj722r5VYDIrxk5x_mLQ6jXmfuH2kl-Lvzo6aHIVkDLIK-IaPt5teSwG71QfAPDgR6drIAuSFnJZ2Ap8', 'qi': 'mfoT9tmXPhLBanX5Mg76pO21NAXR1aAQ76tS1_hJZYxP8iZtmlEdvvAMIdSibvIt7Gfi60rBPnxqmmKuitJfzIVCd4sVLjIVEjT_njjLAzU-NTQdGugPCWWo8jB8NyeFy6nrZa_Hy52ijBn-Xt5G8pzvz5lF5gRfCe09y14oNeQ' } - const publicRsaJwkKey: RsaImportableKey = { 'alg': 'RSA-OAEP-256', 'e': 'AQAB', 'ext': true, 'key_ops': ['wrapKey'], 'kty': 'RSA', 'n': '6k_jrxg7mpz7CzgAr6eRqJr1VlvjJ9uQY71hadkDZkLLZHiMl7hz73lqq3w2MfHCa3Pf3BVo5TCXGYuxKOlPb7bH0WWpMeAzOKR_X27UqfA8MBVGb4YO5HXqw0jup8-I-Zi3CQAmP87uE6GDuh7xzeAcwpGD5xE0N74-uWq3YS92PFHCavtryx-ad9VGTgfAbkV3k1-RSxIiZjzbAt3exBAn5EjMfF6FMI70_HYqO-5xGv_aAPSa1OMc_buK5QACN7gmFwqHBzw98v93iyGUc4_XJNL-jPzKNP4AT1zMc6p6RxF3SYytNq7iXIjUmm-oY8fvCSmT1F13XKdzv7DLOw' } + const privateRsaJwkKey: RsaImportableKey = { + alg: 'RSA-OAEP-256', + d: + 'XcAlS3OYtZ5F3BFGRQH5B8soiqstUk9JkH6_sUhBUfM7yjFpn3MQACtGgOKsFIO01KWCVl7Cn6E3c-MuuT3QqNQrUx8n-WrJU8qNpDOGJ5CVpG9-xTSQVNzRV92gj8g7-BIgehtzMmirXXNsb1XeTg9zsm3iptt9VyhplGqcgOdmm72sT1Z8ZmkagaElHSg0dR1ZNGgzSfTtRg_J1tTh7cmFb1LVz069o6cRaa5ueOPNKxmEslBdVWsDo9naxd_keLiqOOMIQp-KlLuQ-Zhn5fZyqxkRPGjTKZZHitgurzfWG4ERjjrYCbZsOjEt9Tj8FXXUB8bd3qRPy5UkN-XLEQ', + dp: + 'V8QYdWm4OqWpfF_NPdCGr5eqztfHiQQn1NLmkvNO8c9dc2yNizZ4GxtNNEARYjgnLK0ROCoiK5yamtVDyjZ_zzZUvE0CG8iNRg1qvaOM8n_7B2YgmUs9rJ-QKK3HVEsi_M0x-hHeRl3ocAkNfby3__yt6s43FvyrccQh89WcAr0', + dq: + 'NT5lrYlvkOwXIHl8P9AQm1nNL0RkHSrWahYlagRkyU3ELySlWr2laDxXzPnngpuBvyA98iq6Z2JTn8ArtXXvTqQk6BF6np6qqg1QNQxsQeU4Aj3xOMV9EGh57Zpa8Rs0jVydxBdlRW03Fr0UChHKxmT2kS0622gdlGQAs3YxMck', + e: 'AQAB', + ext: true, + key_ops: ['unwrapKey'], // eslint-disable-line @typescript-eslint/camelcase + kty: 'RSA', + n: + '6k_jrxg7mpz7CzgAr6eRqJr1VlvjJ9uQY71hadkDZkLLZHiMl7hz73lqq3w2MfHCa3Pf3BVo5TCXGYuxKOlPb7bH0WWpMeAzOKR_X27UqfA8MBVGb4YO5HXqw0jup8-I-Zi3CQAmP87uE6GDuh7xzeAcwpGD5xE0N74-uWq3YS92PFHCavtryx-ad9VGTgfAbkV3k1-RSxIiZjzbAt3exBAn5EjMfF6FMI70_HYqO-5xGv_aAPSa1OMc_buK5QACN7gmFwqHBzw98v93iyGUc4_XJNL-jPzKNP4AT1zMc6p6RxF3SYytNq7iXIjUmm-oY8fvCSmT1F13XKdzv7DLOw', + p: + '9dGuBwEDeOHFwJ_AQXHBWu53bv_L1_9lh2X-NEBO1B7YMhYWu2nMqXEvLpwvPqyBXwWnuPdfGqu6BHv22RDAF7Lu_oUshq-9dzSwFxaC5PQ2NwtHnz0-zwhEzCE3Qw9t63_OXX87gjp5vy6c5bvb3B9EbZU33Xf9nqVEJhzFreU', + q: + '9AQ0oYhctBbFuIu4jt1HBmqQGGAECbhQAMw324MX8pVUg6GOtF0X822iEsq7aIfY8u5nTWu1kKl6s84US1yII0sJmW2Jj722r5VYDIrxk5x_mLQ6jXmfuH2kl-Lvzo6aHIVkDLIK-IaPt5teSwG71QfAPDgR6drIAuSFnJZ2Ap8', + qi: + 'mfoT9tmXPhLBanX5Mg76pO21NAXR1aAQ76tS1_hJZYxP8iZtmlEdvvAMIdSibvIt7Gfi60rBPnxqmmKuitJfzIVCd4sVLjIVEjT_njjLAzU-NTQdGugPCWWo8jB8NyeFy6nrZa_Hy52ijBn-Xt5G8pzvz5lF5gRfCe09y14oNeQ', + } + const publicRsaJwkKey: RsaImportableKey = { + alg: 'RSA-OAEP-256', + e: 'AQAB', + ext: true, + key_ops: ['wrapKey'], // eslint-disable-line @typescript-eslint/camelcase + kty: 'RSA', + n: + '6k_jrxg7mpz7CzgAr6eRqJr1VlvjJ9uQY71hadkDZkLLZHiMl7hz73lqq3w2MfHCa3Pf3BVo5TCXGYuxKOlPb7bH0WWpMeAzOKR_X27UqfA8MBVGb4YO5HXqw0jup8-I-Zi3CQAmP87uE6GDuh7xzeAcwpGD5xE0N74-uWq3YS92PFHCavtryx-ad9VGTgfAbkV3k1-RSxIiZjzbAt3exBAn5EjMfF6FMI70_HYqO-5xGv_aAPSa1OMc_buK5QACN7gmFwqHBzw98v93iyGUc4_XJNL-jPzKNP4AT1zMc6p6RxF3SYytNq7iXIjUmm-oY8fvCSmT1F13XKdzv7DLOw', + } /* The RSA private key needs to be imported to a CryptoKey. */ - const privateKey = await RawRsaKeyringWebCrypto.importPrivateKey(privateRsaJwkKey) + const privateKey = await RawRsaKeyringWebCrypto.importPrivateKey( + privateRsaJwkKey + ) /* The RSA public key needs to be imported to a CryptoKey. */ - const publicKey = await RawRsaKeyringWebCrypto.importPublicKey(publicRsaJwkKey) + const publicKey = await RawRsaKeyringWebCrypto.importPublicKey( + publicRsaJwkKey + ) /* You need to specify a name * and a namespace for raw encryption key providers. @@ -37,7 +69,12 @@ export async function testRSA () { const keyNamespace = 'Example RSA Provider' /* The Raw RSA Keyring must be configured with the desired CryptoKeys. */ - const keyring = new RawRsaKeyringWebCrypto({ keyName, keyNamespace, publicKey, privateKey }) + const keyring = new RawRsaKeyringWebCrypto({ + keyName, + keyNamespace, + publicKey, + privateKey, + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -51,14 +88,16 @@ export async function testRSA () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. */ const plainText = new Uint8Array([1, 2, 3, 4, 5]) /* Encrypt the data. */ - const { result } = await encrypt(keyring, plainText, { encryptionContext: context }) + const { result } = await encrypt(keyring, plainText, { + encryptionContext: context, + }) /* Log the plain text * only for testing and to show that it works. @@ -85,11 +124,10 @@ export async function testRSA () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Log the clear message * only for testing and to show that it works. diff --git a/modules/example-node/package.json b/modules/example-node/package.json index 0fd5c9519..a4a90540b 100644 --- a/modules/example-node/package.json +++ b/modules/example-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/example-node/src/aes_simple.ts b/modules/example-node/src/aes_simple.ts index 884685a84..f35346cd4 100644 --- a/modules/example-node/src/aes_simple.ts +++ b/modules/example-node/src/aes_simple.ts @@ -1,14 +1,19 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { RawAesKeyringNode, encrypt, decrypt, RawAesWrappingSuiteIdentifier } from '@aws-crypto/client-node' +import { + RawAesKeyringNode, + encrypt, + decrypt, + RawAesWrappingSuiteIdentifier, +} from '@aws-crypto/client-node' import { randomBytes } from 'crypto' /** * This function is an example of using the RawAesKeyringNode * to encrypt and decrypt a simple string */ -export async function aesTest () { +export async function aesTest() { /* You need to specify a name * and a namespace for raw encryption key providers. * The name and namespace that you use in the decryption keyring *must* be an exact, @@ -18,13 +23,19 @@ export async function aesTest () { const keyNamespace = 'aes-namespace' /* The wrapping suite defines the AES-GCM algorithm suite to use. */ - const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + const wrappingSuite = + RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING // Get your plaintext master key from wherever you store it. const unencryptedMasterKey = randomBytes(32) /* Configure the Raw AES keyring. */ - const keyring = new RawAesKeyringNode({ keyName, keyNamespace, unencryptedMasterKey, wrappingSuite }) + const keyring = new RawAesKeyringNode({ + keyName, + keyNamespace, + unencryptedMasterKey, + wrappingSuite, + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -38,14 +49,16 @@ export async function aesTest () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. A simple string. */ const cleartext = 'asdf' /* Encrypt the data. */ - const { result } = await encrypt(keyring, cleartext, { encryptionContext: context }) + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: context, + }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, result) @@ -59,11 +72,10 @@ export async function aesTest () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Return the values so the code can be tested. */ return { plaintext, result, cleartext } diff --git a/modules/example-node/src/caching_cmm.ts b/modules/example-node/src/caching_cmm.ts index ee0a14b4d..a73e3dbd3 100644 --- a/modules/example-node/src/caching_cmm.ts +++ b/modules/example-node/src/caching_cmm.ts @@ -1,13 +1,20 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KmsKeyringNode, encrypt, decrypt, NodeCachingMaterialsManager, getLocalCryptographicMaterialsCache } from '@aws-crypto/client-node' - -export async function cachingCMMNodeSimpleTest () { +import { + KmsKeyringNode, + encrypt, + decrypt, + NodeCachingMaterialsManager, + getLocalCryptographicMaterialsCache, +} from '@aws-crypto/client-node' + +export async function cachingCMMNodeSimpleTest() { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding alternate KMS keys that can decrypt. * Access to kms:Encrypt is required for every CMK in keyIds. @@ -21,7 +28,9 @@ export async function cachingCMMNodeSimpleTest () { * or omit the `keyIds` parameter. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* The KMS keyring must be configured with the desired CMKs * This example passes the keyring to the caching CMM @@ -75,7 +84,7 @@ export async function cachingCMMNodeSimpleTest () { partition, maxAge, maxBytesEncrypted, - maxMessagesEncrypted + maxMessagesEncrypted, }) /* Encryption context is a *very* powerful tool for controlling @@ -99,7 +108,7 @@ export async function cachingCMMNodeSimpleTest () { const encryptionContext = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. A simple string. */ @@ -119,7 +128,10 @@ export async function cachingCMMNodeSimpleTest () { * the AWS Encryption SDK uses the actual plaintext length * instead of any length you provide. */ - const { result } = await encrypt(cachingCMM, cleartext, { encryptionContext, plaintextLength: 4 }) + const { result } = await encrypt(cachingCMM, cleartext, { + encryptionContext, + plaintextLength: 4, + }) /* Decrypt the data. * NOTE: This decrypt request will not use the data key @@ -138,11 +150,10 @@ export async function cachingCMMNodeSimpleTest () { * do not include a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs that you supplied to the `encrypt` function are included in the encryption context that the `decrypt` function returns. */ - Object - .entries(encryptionContext) - .forEach(([key, value]) => { - if (decryptedContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(encryptionContext).forEach(([key, value]) => { + if (decryptedContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Return the values so the code can be tested. */ return { plaintext, result, cleartext, messageHeader } diff --git a/modules/example-node/src/kms_regional_discovery.ts b/modules/example-node/src/kms_regional_discovery.ts index 33dc83783..cb5134487 100644 --- a/modules/example-node/src/kms_regional_discovery.ts +++ b/modules/example-node/src/kms_regional_discovery.ts @@ -1,9 +1,17 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KmsKeyringNode, limitRegions, excludeRegions, getKmsClient, decrypt } from '@aws-crypto/client-node' +import { + KmsKeyringNode, + limitRegions, + excludeRegions, + getKmsClient, + decrypt, +} from '@aws-crypto/client-node' -export async function kmsRegionalDiscoveryLimitTest (ciphertext: string|Buffer) { +export async function kmsRegionalDiscoveryLimitTest( + ciphertext: string | Buffer +) { const discovery = true // This provider will *only* decrypt for keys in the us-east-1 region. const clientProvider = limitRegions(['us-east-1'], getKmsClient) @@ -14,7 +22,9 @@ export async function kmsRegionalDiscoveryLimitTest (ciphertext: string|Buffer) return { ciphertext, cleartext } } -export async function kmsRegionalDiscoveryExcludeTest (ciphertext: string|Buffer) { +export async function kmsRegionalDiscoveryExcludeTest( + ciphertext: string | Buffer +) { const discovery = true // This provider will decrypt for keys in any region except us-east-1. const clientProvider = excludeRegions(['us-east-1'], getKmsClient) diff --git a/modules/example-node/src/kms_simple.ts b/modules/example-node/src/kms_simple.ts index 3a1499ebf..3b5d7310a 100644 --- a/modules/example-node/src/kms_simple.ts +++ b/modules/example-node/src/kms_simple.ts @@ -3,11 +3,12 @@ import { KmsKeyringNode, encrypt, decrypt } from '@aws-crypto/client-node' -export async function kmsSimpleTest () { +export async function kmsSimpleTest() { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding alternate KMS keys that can decrypt. * Access to kms:Encrypt is required for every CMK in keyIds. @@ -16,7 +17,9 @@ export async function kmsSimpleTest () { * In this example, I am using the same CMK. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* The KMS keyring must be configured with the desired CMKs */ const keyring = new KmsKeyringNode({ generatorKeyId, keyIds }) @@ -33,14 +36,16 @@ export async function kmsSimpleTest () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. A simple string. */ const cleartext = 'asdf' /* Encrypt the data. */ - const { result } = await encrypt(keyring, cleartext, { encryptionContext: context }) + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: context, + }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, result) @@ -55,11 +60,10 @@ export async function kmsSimpleTest () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Return the values so the code can be tested. */ return { plaintext, result, cleartext, messageHeader } diff --git a/modules/example-node/src/kms_stream.ts b/modules/example-node/src/kms_stream.ts index 8047472f1..140306eee 100644 --- a/modules/example-node/src/kms_stream.ts +++ b/modules/example-node/src/kms_stream.ts @@ -5,7 +5,7 @@ import { KmsKeyringNode, decryptStream, encryptStream, - MessageHeader // eslint-disable-line no-unused-vars + MessageHeader, } from '@aws-crypto/client-node' import { finished } from 'stream' @@ -13,11 +13,12 @@ import { createReadStream } from 'fs' import { promisify } from 'util' const finishedAsync = promisify(finished) -export async function kmsStreamTest (filename: string) { +export async function kmsStreamTest(filename: string) { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* The KMS keyring must be configured with the desired CMKs */ const keyring = new KmsKeyringNode({ generatorKeyId }) @@ -34,7 +35,7 @@ export async function kmsStreamTest (filename: string) { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Create a simple pipeline to encrypt the package.json for this project. */ @@ -43,17 +44,16 @@ export async function kmsStreamTest (filename: string) { .pipe(decryptStream(new KmsKeyringNode({ discovery: true }))) .on('MessageHeader', ({ encryptionContext }: MessageHeader) => { /* Verify the encryption context. - * Depending on the Algorithm Suite, the `encryptionContext` _may_ contain additional values. - * In Signing Algorithm Suites the public verification key is serialized into the `encryptionContext`. - * Because the encryption context might contain additional key-value pairs, - * do not add a test that requires that all key-value pairs match. - * Instead, verify that the key-value pairs you expect match. - */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + * Depending on the Algorithm Suite, the `encryptionContext` _may_ contain additional values. + * In Signing Algorithm Suites the public verification key is serialized into the `encryptionContext`. + * Because the encryption context might contain additional key-value pairs, + * do not add a test that requires that all key-value pairs match. + * Instead, verify that the key-value pairs you expect match. + */ + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) }) /* This is not strictly speaking part of the example. diff --git a/modules/example-node/src/multi_keyring.ts b/modules/example-node/src/multi_keyring.ts index a2d39d94e..4a0ca084a 100644 --- a/modules/example-node/src/multi_keyring.ts +++ b/modules/example-node/src/multi_keyring.ts @@ -6,14 +6,22 @@ * to encrypt and decrypt using the AWS Encryption SDK for Javascript in Node.js. */ -import { MultiKeyringNode, KmsKeyringNode, RawAesKeyringNode, RawAesWrappingSuiteIdentifier, encrypt, decrypt } from '@aws-crypto/client-node' +import { + MultiKeyringNode, + KmsKeyringNode, + RawAesKeyringNode, + RawAesWrappingSuiteIdentifier, + encrypt, + decrypt, +} from '@aws-crypto/client-node' import { randomBytes } from 'crypto' -export async function multiKeyringTest () { +export async function multiKeyringTest() { /* A KMS CMK is required to generate the data key. * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. */ - const generatorKeyId = 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' + const generatorKeyId = + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' /* Adding alternate KMS keys that can decrypt. * Access to kms:Encrypt is required for every CMK in keyIds. @@ -22,7 +30,9 @@ export async function multiKeyringTest () { * In this example, I am using the same CMK. * This is *only* to demonstrate how the CMK ARNs are configured. */ - const keyIds = ['arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f'] + const keyIds = [ + 'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f', + ] /* The KMS keyring must be configured with the desired CMKs */ const kmsKeyring = new KmsKeyringNode({ generatorKeyId, keyIds }) @@ -35,15 +45,24 @@ export async function multiKeyringTest () { const keyName = 'aes-name' const keyNamespace = 'aes-namespace' /* The wrapping suite defines the AES-GCM algorithm suite to use. */ - const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + const wrappingSuite = + RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING // Get your plaintext master key from wherever you store it. const unencryptedMasterKey = randomBytes(32) /* Configure the Raw AES Keyring. */ - const aesKeyring = new RawAesKeyringNode({ keyName, keyNamespace, unencryptedMasterKey, wrappingSuite }) + const aesKeyring = new RawAesKeyringNode({ + keyName, + keyNamespace, + unencryptedMasterKey, + wrappingSuite, + }) /* Combine the two keyrings with a MultiKeyring. */ - const keyring = new MultiKeyringNode({ generator: kmsKeyring, children: [ aesKeyring ] }) + const keyring = new MultiKeyringNode({ + generator: kmsKeyring, + children: [aesKeyring], + }) /* Encryption context is a *very* powerful tool for controlling and managing access. * It is ***not*** secret! @@ -57,14 +76,16 @@ export async function multiKeyringTest () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. A simple string. */ const cleartext = 'asdf' /* Encrypt the data. */ - const { result } = await encrypt(keyring, cleartext, { encryptionContext: context }) + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: context, + }) /* Decrypt the data. * This decrypt call could be done with **any** of the 3 keyrings. @@ -85,11 +106,10 @@ export async function multiKeyringTest () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Return the values so the code can be tested. */ return { plaintext, result, cleartext, messageHeader } diff --git a/modules/example-node/src/rsa_simple.ts b/modules/example-node/src/rsa_simple.ts index 7a65820a3..1684c20c3 100644 --- a/modules/example-node/src/rsa_simple.ts +++ b/modules/example-node/src/rsa_simple.ts @@ -11,7 +11,7 @@ const generateKeyPairAsync = promisify(generateKeyPair) * This function is an example of using the RsaKeyringNode * to encrypt and decrypt a simple string */ -export async function rsaTest () { +export async function rsaTest() { /* You need to specify a name * and a namespace for raw encryption key providers. * The name and namespace that you use in the decryption keyring *must* be an exact, @@ -40,14 +40,16 @@ export async function rsaTest () { const context = { stage: 'demo', purpose: 'simple demonstration app', - origin: 'us-west-2' + origin: 'us-west-2', } /* Find data to encrypt. A simple string. */ const cleartext = 'asdf' /* Encrypt the data. */ - const { result } = await encrypt(keyring, cleartext, { encryptionContext: context }) + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: context, + }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, result) @@ -61,11 +63,10 @@ export async function rsaTest () { * do not add a test that requires that all key-value pairs match. * Instead, verify that the key-value pairs you expect match. */ - Object - .entries(context) - .forEach(([key, value]) => { - if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') - }) + Object.entries(context).forEach(([key, value]) => { + if (encryptionContext[key] !== value) + throw new Error('Encryption Context does not match expected values') + }) /* Return the values so the code can be tested. */ return { plaintext, result, cleartext } @@ -74,7 +75,7 @@ export async function rsaTest () { /** * This is a helper function to generate an RSA key pair for testing purposes only. */ -async function generateRsaKeys () { +async function generateRsaKeys() { const modulusLength = 3072 const publicKeyEncoding = { type: 'pkcs1', format: 'pem' } const privateKeyEncoding = { type: 'pkcs1', format: 'pem' } @@ -82,6 +83,6 @@ async function generateRsaKeys () { return generateKeyPairAsync('rsa', { modulusLength, publicKeyEncoding, - privateKeyEncoding + privateKeyEncoding, }) } diff --git a/modules/hkdf-node/package.json b/modules/hkdf-node/package.json index 014f3469c..c5177a81d 100644 --- a/modules/hkdf-node/package.json +++ b/modules/hkdf-node/package.json @@ -5,7 +5,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/hkdf-node/src/errors.ts b/modules/hkdf-node/src/errors.ts index 96aa4a10d..0b7d7f927 100644 --- a/modules/hkdf-node/src/errors.ts +++ b/modules/hkdf-node/src/errors.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 export class HKDFError extends Error { - constructor (message?: string) { + constructor(message?: string) { super(message) Object.setPrototypeOf(this, HKDFError.prototype) } @@ -10,15 +10,20 @@ export class HKDFError extends Error { export class KeyLengthError extends HKDFError { public name = 'KeyLengthError' - constructor (maxLength: number, algorithm: string) { - super('Can not derive keys larger than ' + maxLength + ' for algorithm:' + algorithm) + constructor(maxLength: number, algorithm: string) { + super( + 'Can not derive keys larger than ' + + maxLength + + ' for algorithm:' + + algorithm + ) Object.setPrototypeOf(this, KeyLengthError.prototype) } } export class UnsupportedAlgorithm extends HKDFError { public name = 'UnsupportedAlgorithm' - constructor (algorithm: string) { + constructor(algorithm: string) { super('Hash algorithm: ' + algorithm + ' is not an implemented algorithm') Object.setPrototypeOf(this, UnsupportedAlgorithm.prototype) } diff --git a/modules/hkdf-node/src/hkdf.ts b/modules/hkdf-node/src/hkdf.ts index a9d558c4c..f93f4f900 100644 --- a/modules/hkdf-node/src/hkdf.ts +++ b/modules/hkdf-node/src/hkdf.ts @@ -10,13 +10,15 @@ import { UnsupportedAlgorithm, KeyLengthError } from './errors' * @param algorithm [String] The name of the hash algorithm to use * @return [Function] The extract function decorated with expand and verify functions */ -export function HKDF (algorithm: string = 'sha256'): HKDFOutput { +export function HKDF(algorithm = 'sha256'): HKDFOutput { // Check the length and support - try { - var hashLength = createHash(algorithm).digest().length - } catch (ex) { - throw new UnsupportedAlgorithm(algorithm) - } + const hashLength = (function () { + try { + return createHash(algorithm).digest().length + } catch (ex) { + throw new UnsupportedAlgorithm(algorithm) + } + })() // (<= 255*HashLen) from https://tools.ietf.org/html/rfc5869 const maxLength = 255 * hashLength @@ -36,9 +38,12 @@ export function HKDF (algorithm: string = 'sha256'): HKDFOutput { * @param salt [String|Buffer] Optional salt for the extraction * @return [Function] expand function with the extracted key curried onto it */ - function extractExpand (ikm: string|Uint8Array, salt?: string|Uint8Array|false) { + function extractExpand( + ikm: string | Uint8Array, + salt?: string | Uint8Array | false + ) { const prk = extract(ikm, salt) - return (length:number, info?:Uint8Array) => expand(prk, length, info) + return (length: number, info?: Uint8Array) => expand(prk, length, info) } /** @@ -48,8 +53,11 @@ export function HKDF (algorithm: string = 'sha256'): HKDFOutput { * @param salt [String|Buffer] Optional salt for the extraction * @return [Buffer] the expanded key */ - function extract (ikm: string|Uint8Array, salt?: string|Uint8Array|false) { - var _salt = salt || Buffer.alloc(hashLength, 0).toString() + function extract( + ikm: string | Uint8Array, + salt?: string | Uint8Array | false + ) { + const _salt = salt || Buffer.alloc(hashLength, 0).toString() return createHmac(algorithm, _salt).update(ikm).digest() } @@ -61,21 +69,21 @@ export function HKDF (algorithm: string = 'sha256'): HKDFOutput { * @param info [Buffer] Data to bind the expanded key to application/context specific information * @return [Buffer] the expanded */ - function expand (prk:Uint8Array, length:number, info?:Uint8Array) { + function expand(prk: Uint8Array, length: number, info?: Uint8Array) { if (length > maxLength) { throw new KeyLengthError(maxLength, algorithm) } info = info || Buffer.alloc(0) - var N = Math.ceil(length / hashLength) - var memo: Buffer[] = [] + const N = Math.ceil(length / hashLength) + const memo: Buffer[] = [] /* L/length octets are returned from T(1)...T(N), and T(0) is definitionally empty/zero length. * Elide T(0) into the Buffer.alloc(0) case and then return L octets of T indexed 0...L-1. */ - for (var i = 0; i < N; i++) { + for (let i = 0; i < N; i++) { memo[i] = createHmac(algorithm, prk) - .update((memo[i - 1] || Buffer.alloc(0))) + .update(memo[i - 1] || Buffer.alloc(0)) .update(info) .update(Buffer.alloc(1, i + 1)) .digest() @@ -85,11 +93,11 @@ export function HKDF (algorithm: string = 'sha256'): HKDFOutput { } export interface Extract { - (ikm: string|Uint8Array, salt?: string|Uint8Array|false): Buffer + (ikm: string | Uint8Array, salt?: string | Uint8Array | false): Buffer } export interface Expand { - (prk:Uint8Array, length:number, info?:Uint8Array): Buffer + (prk: Uint8Array, length: number, info?: Uint8Array): Buffer } export interface HKDFOutput { @@ -98,4 +106,9 @@ export interface HKDFOutput { expand: Expand } -type Curry = ((...args: T) => void) extends (head: any, ...tail: infer U) => any ? U : never +type Curry = ((...args: T) => void) extends ( + head: any, + ...tail: infer U +) => any + ? U + : never diff --git a/modules/integration-browser/package.json b/modules/integration-browser/package.json index fe858cdae..c9283330c 100644 --- a/modules/integration-browser/package.json +++ b/modules/integration-browser/package.json @@ -3,7 +3,9 @@ "version": "1.2.0", "scripts": { "build": "tsc -b tsconfig.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "build_fixtures": "npx .", "test": "npm run lint && npm run karma" diff --git a/modules/integration-browser/src/build_decrypt_fixtures.ts b/modules/integration-browser/src/build_decrypt_fixtures.ts index 1f448b07f..9f3bb2249 100644 --- a/modules/integration-browser/src/build_decrypt_fixtures.ts +++ b/modules/integration-browser/src/build_decrypt_fixtures.ts @@ -2,16 +2,12 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - open, - Entry, // eslint-disable-line no-unused-vars - ZipFile // eslint-disable-line no-unused-vars -} from 'yauzl' +import { open, Entry, ZipFile } from 'yauzl' import streamToPromise from 'stream-to-promise' import { writeFileSync } from 'fs' -import { Readable } from 'stream' // eslint-disable-line no-unused-vars +import { Readable } from 'stream' -import { DecryptManifestList } from './types' // eslint-disable-line no-unused-vars +import { DecryptManifestList } from './types' /* This function interacts with manifest information * and produces the fixtures in the `fixtures` @@ -20,8 +16,15 @@ import { DecryptManifestList } from './types' // eslint-disable-line no-unused-v * 1. The code is not tied to a specific copy of the manifest information * 2. The tests can be run on a subset of tests for debugging. */ -export async function buildDecryptFixtures (fixtures: string, vectorFile: string, testName?: string, slice?: string) { - const [start = 0, end = 9999] = (slice || '').split(':').map(n => parseInt(n, 10)) +export async function buildDecryptFixtures( + fixtures: string, + vectorFile: string, + testName?: string, + slice?: string +) { + const [start = 0, end = 9999] = (slice || '') + .split(':') + .map((n) => parseInt(n, 10)) const filesMap = await centralDirectory(vectorFile) @@ -39,7 +42,9 @@ export async function buildDecryptFixtures (fixtures: string, vectorFile: string })() const manifestBuffer = await readUriOnce('file://manifest.json') - const { keys: keysFile, tests }: DecryptManifestList = JSON.parse(manifestBuffer.toString('utf8')) + const { keys: keysFile, tests }: DecryptManifestList = JSON.parse( + manifestBuffer.toString('utf8') + ) const keysBuffer = await readUriOnce(keysFile) const { keys } = JSON.parse(keysBuffer.toString('utf8')) const testNames = [] @@ -59,14 +64,19 @@ export async function buildDecryptFixtures (fixtures: string, vectorFile: string testNames.push(name) - const { plaintext: plaintextFile, ciphertext, 'master-keys': masterKeys } = testInfo + const { + plaintext: plaintextFile, + ciphertext, + 'master-keys': masterKeys, + } = testInfo const plainTextInfo = filesMap.get(plaintextFile) const cipherInfo = filesMap.get(ciphertext) - if (!cipherInfo || !plainTextInfo) throw new Error(`no file for ${name}: ${ciphertext} | ${plaintextFile}`) + if (!cipherInfo || !plainTextInfo) + throw new Error(`no file for ${name}: ${ciphertext} | ${plaintextFile}`) const cipherText = await streamToPromise(await cipherInfo.stream()) const plainText = await readUriOnce(`file://${plainTextInfo.fileName}`) - const keysInfo = masterKeys.map(keyInfo => { + const keysInfo = masterKeys.map((keyInfo) => { const key = keys[keyInfo.key] if (!key) throw new Error(`no key for ${name}`) return [keyInfo, key] @@ -77,7 +87,7 @@ export async function buildDecryptFixtures (fixtures: string, vectorFile: string keysInfo, cipherFile: cipherInfo.fileName, cipherText: cipherText.toString('base64'), - plainText: plainText.toString('base64') + plainText: plainText.toString('base64'), }) writeFileSync(`${fixtures}/${name}.json`, test) @@ -90,29 +100,35 @@ interface StreamEntry extends Entry { stream: () => Promise } -function centralDirectory (vectorFile: string): Promise> { +async function centralDirectory( + vectorFile: string +): Promise> { const filesMap = new Map() return new Promise((resolve, reject) => { - open(vectorFile, { lazyEntries: true, autoClose: false }, (err, zipfile) => { - if (err || !zipfile) return reject(err) - - zipfile - .on('entry', (entry: StreamEntry) => { - entry.stream = curryStream(zipfile, entry) - filesMap.set(`file://${entry.fileName}`, entry) - zipfile.readEntry() - }) - .on('end', () => { - resolve(filesMap) - }) - .on('error', (err) => reject(err)) - .readEntry() - }) + open( + vectorFile, + { lazyEntries: true, autoClose: false }, + (err, zipfile) => { + if (err || !zipfile) return reject(err) + + zipfile + .on('entry', (entry: StreamEntry) => { + entry.stream = curryStream(zipfile, entry) + filesMap.set(`file://${entry.fileName}`, entry) + zipfile.readEntry() + }) + .on('end', () => { + resolve(filesMap) + }) + .on('error', (err) => reject(err)) + .readEntry() + } + ) }) } -function curryStream (zipfile: ZipFile, entry: Entry) { - return function stream (): Promise { +function curryStream(zipfile: ZipFile, entry: Entry) { + return async function stream(): Promise { return new Promise((resolve, reject) => { zipfile.openReadStream(entry, (err, readStream) => { if (err || !readStream) return reject(err) diff --git a/modules/integration-browser/src/build_encrypt_fixtures.ts b/modules/integration-browser/src/build_encrypt_fixtures.ts index 7590695be..92b3c9bc9 100644 --- a/modules/integration-browser/src/build_encrypt_fixtures.ts +++ b/modules/integration-browser/src/build_encrypt_fixtures.ts @@ -1,15 +1,11 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - EncryptManifestList, // eslint-disable-line no-unused-vars - KeyList, // eslint-disable-line no-unused-vars - KeyInfoTuple // eslint-disable-line no-unused-vars -} from './types' +import { EncryptManifestList, KeyList, KeyInfoTuple } from './types' import { randomBytes } from 'crypto' import { - AlgorithmSuiteIdentifier, // eslint-disable-line no-unused-vars - EncryptionContext // eslint-disable-line no-unused-vars + AlgorithmSuiteIdentifier, + EncryptionContext, } from '@aws-crypto/client-browser' import { URL } from 'url' import { readFileSync, writeFileSync } from 'fs' @@ -22,21 +18,29 @@ import got from 'got' * 1. The code is not tied to a specific copy of the manifest information * 2. The tests can be run on a subset of tests for debugging. */ -export async function buildEncryptFixtures (fixtures: string, manifestFile: string, keyFile: string, testName?: string, slice?: string) { - const [start = 0, end = 9999] = (slice || '').split(':').map(n => parseInt(n, 10)) - const { tests, plaintexts }: EncryptManifestList = await getParsedJSON(manifestFile) +export async function buildEncryptFixtures( + fixtures: string, + manifestFile: string, + keyFile: string, + testName?: string, + slice?: string +) { + const [start = 0, end = 9999] = (slice || '') + .split(':') + .map((n) => parseInt(n, 10)) + const { tests, plaintexts }: EncryptManifestList = await getParsedJSON( + manifestFile + ) const { keys }: KeyList = await getParsedJSON(keyFile) - const plaintextBytes: {[name: string]: string} = {} + const plaintextBytes: { [name: string]: string } = {} - Object - .keys(plaintexts) - .forEach(name => { - /* Generate random bites as per spec. - * See: https://github.com/awslabs/aws-crypto-tools-test-vector-framework/blob/master/features/0003-awses-message-encryption.md#plaintexts - */ - plaintextBytes[name] = randomBytes(10).toString('base64') - }) + Object.keys(plaintexts).forEach((name) => { + /* Generate random bites as per spec. + * See: https://github.com/awslabs/aws-crypto-tools-test-vector-framework/blob/master/features/0003-awses-message-encryption.md#plaintexts + */ + plaintextBytes[name] = randomBytes(10).toString('base64') + }) const testNames = [] let count = 0 @@ -60,23 +64,23 @@ export async function buildEncryptFixtures (fixtures: string, manifestFile: stri 'master-keys': masterKeys, algorithm, 'frame-size': frameLength, - 'encryption-context': encryptionContext + 'encryption-context': encryptionContext, } = testInfo - const keysInfo = masterKeys.map(keyInfo => { + const keysInfo = masterKeys.map((keyInfo) => { const key = keys[keyInfo.key] if (!key) throw new Error(`no key for ${name}`) - return [keyInfo, key] + return [keyInfo, key] as KeyInfoTuple }) /* I'm expecting that the encrypt function will throw if this is not a supported AlgorithmSuiteIdentifier */ - const suiteId = parseInt(algorithm, 16) + const suiteId = parseInt(algorithm, 16) as AlgorithmSuiteIdentifier const test: EncryptTestVectorInfo = { name, keysInfo, plainTextData: plaintextBytes[plaintext], - encryptOp: { suiteId, frameLength, encryptionContext } + encryptOp: { suiteId, frameLength, encryptionContext }, } writeFileSync(`${fixtures}/${name}.json`, JSON.stringify(test)) @@ -86,17 +90,17 @@ export async function buildEncryptFixtures (fixtures: string, manifestFile: stri } export interface EncryptTestVectorInfo { - name: string, - keysInfo: KeyInfoTuple[], - plainTextData: string, + name: string + keysInfo: KeyInfoTuple[] + plainTextData: string encryptOp: { - suiteId: AlgorithmSuiteIdentifier, - frameLength: number, + suiteId: AlgorithmSuiteIdentifier + frameLength: number encryptionContext: EncryptionContext } } -async function getParsedJSON (thing: string) { +async function getParsedJSON(thing: string) { try { const url = new URL(thing) if (url.protocol === 'file:') { @@ -108,12 +112,12 @@ async function getParsedJSON (thing: string) { return jsonAtPath(thing) } } -async function jsonAtUrl (url: URL) { +async function jsonAtUrl(url: URL) { const { body } = await got(url) return JSON.parse(body) } -function jsonAtPath (path: string) { +function jsonAtPath(path: string) { const json = readFileSync(path, { encoding: 'utf-8' }) return JSON.parse(json) } diff --git a/modules/integration-browser/src/cli.ts b/modules/integration-browser/src/cli.ts index 40a223392..d3dcf96a4 100644 --- a/modules/integration-browser/src/cli.ts +++ b/modules/integration-browser/src/cli.ts @@ -13,47 +13,50 @@ import { buildDecryptFixtures } from './build_decrypt_fixtures' import { buildEncryptFixtures } from './build_encrypt_fixtures' const cli = yargs - .command('decrypt', 'verify decrypt vectors', y => y - .option('vectorFile', { + .command('decrypt', 'verify decrypt vectors', (y) => + y.option('vectorFile', { alias: 'v', describe: 'a vector zip file from aws-encryption-sdk-test-vectors', demandOption: true, - type: 'string' + type: 'string', }) ) - .command('encrypt', 'verify encrypt manifest', y => y - .option('manifestFile', { - alias: 'm', - describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest', - demandOption: true, - type: 'string' - }) - .option('keyFile', { - alias: 'k', - describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical key list', - demandOption: true, - type: 'string' - }) - .option('decryptOracle', { - alias: 'o', - describe: 'a url to the decrypt oracle', - demandOption: true, - type: 'string' - }) + .command('encrypt', 'verify encrypt manifest', (y) => + y + .option('manifestFile', { + alias: 'm', + describe: + 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest', + demandOption: true, + type: 'string', + }) + .option('keyFile', { + alias: 'k', + describe: + 'a path/url to aws-crypto-tools-test-vector-framework canonical key list', + demandOption: true, + type: 'string', + }) + .option('decryptOracle', { + alias: 'o', + describe: 'a url to the decrypt oracle', + demandOption: true, + type: 'string', + }) ) .option('testName', { alias: 't', describe: 'an optional test name to execute', - type: 'string' + type: 'string', }) .option('slice', { alias: 's', describe: 'an optional range start:end e.g. 100:200', - type: 'string' + type: 'string', }) .options('karma', { describe: 'start karma and run the tests', - type: 'boolean' + type: 'boolean', }) .option('concurrency', { alias: 'c', @@ -61,12 +64,18 @@ const cli = yargs default: 1, coerce: (value: any) => { if (typeof value === 'string') { - needs(value.toLowerCase() === 'cpu', `The only supported string is 'cpu'`) + needs( + value.toLowerCase() === 'cpu', + `The only supported string is 'cpu'` + ) return cpus().length - 1 } - needs(typeof value === 'number' && value > 0, `Must be a number greater than 0`) + needs( + typeof value === 'number' && value > 0, + `Must be a number greater than 0` + ) return value - } + }, }) .demandCommand() const fixtures = join(__dirname, '../../fixtures') @@ -76,7 +85,13 @@ if (!existsSync(fixtures)) { } ;(async (argv) => { - const { _: [ command ], testName, slice, karma, concurrency } = argv + const { + _: [command], + testName, + slice, + karma, + concurrency, + } = argv writeFileSync(`${fixtures}/decrypt_tests.json`, JSON.stringify([])) writeFileSync(`${fixtures}/encrypt_tests.json`, JSON.stringify([])) @@ -84,15 +99,29 @@ if (!existsSync(fixtures)) { if (command === 'decrypt') { // It is not clear how to get yargs/typescript to play nicely with sub commands - const { vectorFile } = argv as unknown as { vectorFile: string} - if (!existsSync(vectorFile)) throw new Error(`No file found at ${vectorFile}`) + const { vectorFile } = (argv as unknown) as { vectorFile: string } + if (!existsSync(vectorFile)) + throw new Error(`No file found at ${vectorFile}`) await buildDecryptFixtures(fixtures, vectorFile as string, testName, slice) } else if (command === 'encrypt') { // It is not clear how to get yargs/typescript to play nicely with sub commands - const { manifestFile, keyFile, decryptOracle } = argv as unknown as { manifestFile: string, keyFile: string, decryptOracle: string} - writeFileSync(`${fixtures}/decrypt_oracle.json`, JSON.stringify(decryptOracle)) + const { manifestFile, keyFile, decryptOracle } = (argv as unknown) as { + manifestFile: string + keyFile: string + decryptOracle: string + } + writeFileSync( + `${fixtures}/decrypt_oracle.json`, + JSON.stringify(decryptOracle) + ) - await buildEncryptFixtures(fixtures, manifestFile as string, keyFile as string, testName, slice) + await buildEncryptFixtures( + fixtures, + manifestFile as string, + keyFile as string, + testName, + slice + ) } else { console.log(`Unknown command ${command}`) cli.showHelp() @@ -101,13 +130,12 @@ if (!existsSync(fixtures)) { if (karma) { const { status } = spawnSync('npm', ['run', 'karma'], { cwd: __dirname, - stdio: 'inherit' + stdio: 'inherit', }) /* Forward the status to the parent. */ process.exit(status || 0) } -})(cli.argv) - .catch(err => { - console.log(err) - process.exit(1) - }) +})(cli.argv).catch((err) => { + console.log(err) + process.exit(1) +}) diff --git a/modules/integration-browser/src/decrypt_materials_manager_web_crypto.ts b/modules/integration-browser/src/decrypt_materials_manager_web_crypto.ts index 1a5a1876d..d19a95529 100644 --- a/modules/integration-browser/src/decrypt_materials_manager_web_crypto.ts +++ b/modules/integration-browser/src/decrypt_materials_manager_web_crypto.ts @@ -5,21 +5,21 @@ import { needs, MultiKeyringWebCrypto, KmsKeyringBrowser, - KmsWebCryptoClientSupplier, // eslint-disable-line no-unused-vars + KmsWebCryptoClientSupplier, KMS, RawAesKeyringWebCrypto, - WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars + WrappingSuiteIdentifier, RawAesWrappingSuiteIdentifier, - RawRsaKeyringWebCrypto + RawRsaKeyringWebCrypto, } from '@aws-crypto/client-browser' import { - RsaKeyInfo, // eslint-disable-line no-unused-vars - AesKeyInfo, // eslint-disable-line no-unused-vars - KmsKeyInfo, // eslint-disable-line no-unused-vars - RSAKey, // eslint-disable-line no-unused-vars - AESKey, // eslint-disable-line no-unused-vars - KMSKey, // eslint-disable-line no-unused-vars - KeyInfoTuple // eslint-disable-line no-unused-vars + RsaKeyInfo, + AesKeyInfo, + KmsKeyInfo, + RSAKey, + AESKey, + KMSKey, + KeyInfoTuple, } from './types' import { fromBase64 } from '@aws-sdk/util-base64-browser' @@ -27,39 +27,55 @@ import { fromBase64 } from '@aws-sdk/util-base64-browser' import keyto from '@trust/keyto' declare const credentials: any -const Bits2RawAesWrappingSuiteIdentifier: {[key: number]: WrappingSuiteIdentifier} = { +const Bits2RawAesWrappingSuiteIdentifier: { + [key: number]: WrappingSuiteIdentifier +} = { 128: RawAesWrappingSuiteIdentifier.AES128_GCM_IV12_TAG16_NO_PADDING, /* Browsers do not support 192 Bit keys. * Leaving this here to make sure this is clear. *192: RawAesWrappingSuiteIdentifier.AES192_GCM_IV12_TAG16_NO_PADDING, */ - 256: RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + 256: RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING, } -export async function encryptMaterialsManagerWebCrypto (keyInfos: KeyInfoTuple[]) { - const [generator, ...children] = await Promise.all(keyInfos.map(keyringWebCrypto)) +export async function encryptMaterialsManagerWebCrypto( + keyInfos: KeyInfoTuple[] +) { + const [generator, ...children] = await Promise.all( + keyInfos.map(keyringWebCrypto) + ) return new MultiKeyringWebCrypto({ generator, children }) } -export async function decryptMaterialsManagerWebCrypto (keyInfos: KeyInfoTuple[]) { +export async function decryptMaterialsManagerWebCrypto( + keyInfos: KeyInfoTuple[] +) { const children = await Promise.all(keyInfos.map(keyringWebCrypto)) return new MultiKeyringWebCrypto({ children }) } -async function keyringWebCrypto ([ info, key ]: KeyInfoTuple) { +async function keyringWebCrypto([info, key]: KeyInfoTuple) { if (info.type === 'aws-kms' && key.type === 'aws-kms') { return kmsKeyring(info, key) } - if (info.type === 'raw' && info['encryption-algorithm'] === 'aes' && key.type === 'symmetric') { + if ( + info.type === 'raw' && + info['encryption-algorithm'] === 'aes' && + key.type === 'symmetric' + ) { return aesKeyring(info, key) } - if (info.type === 'raw' && info['encryption-algorithm'] === 'rsa' && (key.type === 'public' || key.type === 'private')) { + if ( + info.type === 'raw' && + info['encryption-algorithm'] === 'rsa' && + (key.type === 'public' || key.type === 'private') + ) { return rsaKeyring(info, key) } throw new Error('Unsupported keyring type') } -function kmsKeyring (_keyInfo: KmsKeyInfo, key: KMSKey) { +function kmsKeyring(_keyInfo: KmsKeyInfo, key: KMSKey) { const generatorKeyId = key['key-id'] const clientProvider: KmsWebCryptoClientSupplier = (region: string) => { return new KMS({ region, credentials }) @@ -67,19 +83,28 @@ function kmsKeyring (_keyInfo: KmsKeyInfo, key: KMSKey) { return new KmsKeyringBrowser({ generatorKeyId, clientProvider }) } -async function aesKeyring (keyInfo:AesKeyInfo, key: AESKey) { +async function aesKeyring(keyInfo: AesKeyInfo, key: AESKey) { const keyName = key['key-id'] const keyNamespace = keyInfo['provider-id'] const { encoding, material } = key needs(encoding === 'base64', 'Unsupported encoding') const rawKey = fromBase64(material) - if (!Bits2RawAesWrappingSuiteIdentifier[key.bits]) throw new Error('Unsupported right now') + if (!Bits2RawAesWrappingSuiteIdentifier[key.bits]) + throw new Error('Unsupported right now') const wrappingSuite = Bits2RawAesWrappingSuiteIdentifier[key.bits] - const masterKey = await RawAesKeyringWebCrypto.importCryptoKey(rawKey, wrappingSuite) - return new RawAesKeyringWebCrypto({ keyName, keyNamespace, masterKey, wrappingSuite }) + const masterKey = await RawAesKeyringWebCrypto.importCryptoKey( + rawKey, + wrappingSuite + ) + return new RawAesKeyringWebCrypto({ + keyName, + keyNamespace, + masterKey, + wrappingSuite, + }) } -async function rsaKeyring (keyInfo: RsaKeyInfo, key: RSAKey) { +async function rsaKeyring(keyInfo: RsaKeyInfo, key: RSAKey) { const keyName = key['key-id'] const keyNamespace = keyInfo['provider-id'] @@ -87,7 +112,7 @@ async function rsaKeyring (keyInfo: RsaKeyInfo, key: RSAKey) { return new RawRsaKeyringWebCrypto({ keyName, keyNamespace, ...rsaKey }) } -async function pem2JWK (keyInfo: RsaKeyInfo, { material, type }: RSAKey) { +async function pem2JWK(keyInfo: RsaKeyInfo, { material, type }: RSAKey) { const OAEP_SHA1_MFG1 = 'RSA-OAEP' const OAEP_SHA256_MFG1 = 'RSA-OAEP-256' const OAEP_SHA384_MFG1 = 'RSA-OAEP-384' @@ -103,15 +128,16 @@ async function pem2JWK (keyInfo: RsaKeyInfo, { material, type }: RSAKey) { const paddingAlgorithm = keyInfo['padding-algorithm'] const paddingHash = keyInfo['padding-hash'] if (paddingAlgorithm === 'oaep-mgf1') { - jwk.alg = paddingHash === 'sha1' - ? OAEP_SHA1_MFG1 - : paddingHash === 'sha256' + jwk.alg = + paddingHash === 'sha1' + ? OAEP_SHA1_MFG1 + : paddingHash === 'sha256' ? OAEP_SHA256_MFG1 : paddingHash === 'sha384' - ? OAEP_SHA384_MFG1 - : paddingHash === 'sha512' - ? OAEP_SHA512_MFG1 - : false + ? OAEP_SHA384_MFG1 + : paddingHash === 'sha512' + ? OAEP_SHA512_MFG1 + : false } else if (paddingAlgorithm === 'pkcs1') { throw new Error('Unsupported right now') } diff --git a/modules/integration-browser/src/integration.decrypt.test.ts b/modules/integration-browser/src/integration.decrypt.test.ts index 61679c560..e6b3fe91c 100644 --- a/modules/integration-browser/src/integration.decrypt.test.ts +++ b/modules/integration-browser/src/integration.decrypt.test.ts @@ -13,7 +13,7 @@ declare const fetch: any const notSupportedMessages = [ '192-bit AES keys are not supported', - 'Unsupported right now' + 'Unsupported right now', ] const tests: string[] = __fixtures__['fixtures/decrypt_tests'] @@ -23,7 +23,7 @@ for (let i = 0, j = tests.length; i < j; i += chunk) { aGroup(chunk, tests.slice(i, i + chunk)) } -function aGroup (groupNumber: number, tests: string[]) { +function aGroup(groupNumber: number, tests: string[]) { describe(`browser decryption vectors: ${groupNumber}`, () => { for (const testName of tests) { aTest(testName) @@ -31,7 +31,7 @@ function aGroup (groupNumber: number, tests: string[]) { }) } -function aTest (testName: string) { +function aTest(testName: string) { it(testName, async () => { console.log(`start: ${testName}`) const response = await fetch(`base/fixtures/${testName}.json`) diff --git a/modules/integration-browser/src/integration.encrypt.test.ts b/modules/integration-browser/src/integration.encrypt.test.ts index d82ee934b..742249380 100644 --- a/modules/integration-browser/src/integration.encrypt.test.ts +++ b/modules/integration-browser/src/integration.encrypt.test.ts @@ -15,7 +15,7 @@ declare const fetch: any const notSupportedMessages = [ '192-bit AES keys are not supported', 'frameLength out of bounds: 0 > frameLength >= 4294967295', - 'Unsupported right now' + 'Unsupported right now', ] const tests = __fixtures__['fixtures/encrypt_tests'] @@ -26,7 +26,7 @@ for (let i = 0, j = tests.length; i < j; i += chunk) { aGroup(chunk, tests.slice(i, i + chunk), decryptOracle) } -function aGroup (groupNumber: number, tests: string[], decryptOracle: string) { +function aGroup(groupNumber: number, tests: string[], decryptOracle: string) { describe(`'browser encrypt tests': ${groupNumber}`, () => { for (const testName of tests) { aTest(testName, decryptOracle) @@ -34,7 +34,7 @@ function aGroup (groupNumber: number, tests: string[], decryptOracle: string) { }) } -function aTest (testName: string, decryptOracle: string) { +function aTest(testName: string, decryptOracle: string) { it(testName, async () => { console.log(`start: ${testName}`) const response = await fetch(`base/fixtures/${testName}.json`) @@ -48,9 +48,9 @@ function aTest (testName: string, decryptOracle: string) { method: 'POST', headers: { 'Content-Type': 'application/octet-stream', - 'Accept': 'application/octet-stream' + Accept: 'application/octet-stream', }, - body: result + body: result, }) const body = await response.arrayBuffer() needs(response.ok, `Failed to decrypt: ${toUtf8(body)}`) diff --git a/modules/integration-browser/src/types.ts b/modules/integration-browser/src/types.ts index eb29c054d..0c1aa3d3d 100644 --- a/modules/integration-browser/src/types.ts +++ b/modules/integration-browser/src/types.ts @@ -1,26 +1,26 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EncryptionContext } from '@aws-crypto/client-browser' // eslint-disable-line no-unused-vars +import { EncryptionContext } from '@aws-crypto/client-browser' export interface DecryptManifestList { manifest: DecryptManifest client: Client keys: string - tests: {[testName: string]: DecryptTest} + tests: { [testName: string]: DecryptTest } } export interface EncryptManifestList { manifest: EncryptManifest client: Client keys: string - plaintexts: {[name: string]: number} - tests: {[testName: string]: EncryptTest} + plaintexts: { [name: string]: number } + tests: { [testName: string]: EncryptTest } } export interface KeyList { manifest: Manifest - keys: {[key: string]: (KMSKey|AESKey|RSAKey)} + keys: { [key: string]: KMSKey | AESKey | RSAKey } } interface Manifest { @@ -42,21 +42,21 @@ interface Client { } interface KeyInfo { - type: 'aws-kms'|'raw' + type: 'aws-kms' | 'raw' key: string } interface RawKeyInfo extends KeyInfo { - type: 'raw', + type: 'raw' 'provider-id': string - 'encryption-algorithm': 'aes'|'rsa', - 'padding-algorithm': 'pkcs1'|'oaep-mgf1'|null + 'encryption-algorithm': 'aes' | 'rsa' + 'padding-algorithm': 'pkcs1' | 'oaep-mgf1' | null } export interface RsaKeyInfo extends RawKeyInfo { - 'encryption-algorithm': 'rsa', - 'padding-algorithm': 'pkcs1'|'oaep-mgf1' - 'padding-hash': 'sha1'|'sha256'|'sha384'|'sha512' + 'encryption-algorithm': 'rsa' + 'padding-algorithm': 'pkcs1' | 'oaep-mgf1' + 'padding-hash': 'sha1' | 'sha256' | 'sha384' | 'sha512' } export interface AesKeyInfo extends RawKeyInfo { @@ -74,18 +74,18 @@ interface EncryptTest { algorithm: string 'frame-size': number 'encryption-context': EncryptionContext - 'master-keys': (RsaKeyInfo|AesKeyInfo|KmsKeyInfo)[] + 'master-keys': (RsaKeyInfo | AesKeyInfo | KmsKeyInfo)[] } interface DecryptTest { plaintext: string ciphertext: string - 'master-keys': (RsaKeyInfo|AesKeyInfo|KmsKeyInfo)[] + 'master-keys': (RsaKeyInfo | AesKeyInfo | KmsKeyInfo)[] } interface Key { - 'encrypt': boolean - 'decrypt': boolean + encrypt: boolean + decrypt: boolean 'key-id': string } @@ -99,7 +99,7 @@ interface RawKey extends Key { export interface RSAKey extends RawKey { algorithm: 'rsa' - type: 'public'|'private' + type: 'public' | 'private' bits: number encoding: 'pem' material: string @@ -118,4 +118,7 @@ export interface KMSKey extends Key { 'key-id': string } -export type KeyInfoTuple = [RsaKeyInfo, RSAKey] | [AesKeyInfo, AESKey] | [KmsKeyInfo, KMSKey] +export type KeyInfoTuple = + | [RsaKeyInfo, RSAKey] + | [AesKeyInfo, AESKey] + | [KmsKeyInfo, KMSKey] diff --git a/modules/integration-node/package.json b/modules/integration-node/package.json index 3e2018e82..36ba6a315 100644 --- a/modules/integration-node/package.json +++ b/modules/integration-node/package.json @@ -3,7 +3,9 @@ "version": "1.2.0", "scripts": { "build": "tsc -b tsconfig.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "test": "npm run lint && npm run build && node ./build/main/index.js", "integration_node": "npx ." }, diff --git a/modules/integration-node/src/cli.ts b/modules/integration-node/src/cli.ts index d187df082..a94af4592 100644 --- a/modules/integration-node/src/cli.ts +++ b/modules/integration-node/src/cli.ts @@ -5,47 +5,53 @@ import yargs from 'yargs' import { needs } from '@aws-crypto/client-node' import { cpus } from 'os' -import { integrationDecryptTestVectors, integrationEncryptTestVectors } from './integration_tests' +import { + integrationDecryptTestVectors, + integrationEncryptTestVectors, +} from './integration_tests' const cli = yargs - .command('decrypt', 'verify decrypt vectors', y => y - .option('vectorFile', { + .command('decrypt', 'verify decrypt vectors', (y) => + y.option('vectorFile', { alias: 'v', describe: 'a vector zip file from aws-encryption-sdk-test-vectors', demandOption: true, - type: 'string' + type: 'string', }) ) - .command('encrypt', 'verify encrypt manifest', y => y - .option('manifestFile', { - alias: 'm', - describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest', - demandOption: true, - type: 'string' - }) - .option('keyFile', { - alias: 'k', - describe: 'a path/url to aws-crypto-tools-test-vector-framework canonical key list', - demandOption: true, - type: 'string' - }) - .option('decryptOracle', { - alias: 'o', - describe: 'a url to the decrypt oracle', - demandOption: true, - type: 'string' - }) + .command('encrypt', 'verify encrypt manifest', (y) => + y + .option('manifestFile', { + alias: 'm', + describe: + 'a path/url to aws-crypto-tools-test-vector-framework canonical manifest', + demandOption: true, + type: 'string', + }) + .option('keyFile', { + alias: 'k', + describe: + 'a path/url to aws-crypto-tools-test-vector-framework canonical key list', + demandOption: true, + type: 'string', + }) + .option('decryptOracle', { + alias: 'o', + describe: 'a url to the decrypt oracle', + demandOption: true, + type: 'string', + }) ) .option('tolerateFailures', { alias: 'f', describe: 'an optional number of failures to tolerate before exiting', type: 'number', - default: 0 + default: 0, }) .option('testName', { alias: 't', describe: 'an optional test name to execute', - type: 'string' + type: 'string', }) .option('concurrency', { alias: 'c', @@ -53,33 +59,59 @@ const cli = yargs default: 1, coerce: (value: any) => { if (typeof value === 'string') { - needs(value.toLowerCase() === 'cpu', `The only supported string is 'cpu'`) + needs( + value.toLowerCase() === 'cpu', + `The only supported string is 'cpu'` + ) return cpus().length - 1 } - needs(typeof value === 'number' && value > 0, `Must be a number greater than 0`) + needs( + typeof value === 'number' && value > 0, + `Must be a number greater than 0` + ) return value - } + }, }) .demandCommand() ;(async (argv) => { - const { _: [ command ], tolerateFailures, testName, concurrency } = argv + const { + _: [command], + tolerateFailures, + testName, + concurrency, + } = argv /* I set the result to 1 so that if I fall through the exit condition is a failure */ let result = 1 if (command === 'decrypt') { - const { vectorFile } = argv as unknown as { vectorFile: string} - result = await integrationDecryptTestVectors(vectorFile, tolerateFailures, testName, concurrency) + const { vectorFile } = (argv as unknown) as { vectorFile: string } + result = await integrationDecryptTestVectors( + vectorFile, + tolerateFailures, + testName, + concurrency + ) } else if (command === 'encrypt') { - const { manifestFile, keyFile, decryptOracle } = argv as unknown as { manifestFile: string, keyFile: string, decryptOracle: string} - result = await integrationEncryptTestVectors(manifestFile, keyFile, decryptOracle, tolerateFailures, testName, concurrency) + const { manifestFile, keyFile, decryptOracle } = (argv as unknown) as { + manifestFile: string + keyFile: string + decryptOracle: string + } + result = await integrationEncryptTestVectors( + manifestFile, + keyFile, + decryptOracle, + tolerateFailures, + testName, + concurrency + ) } else { console.log(`Unknown command ${command}`) cli.showHelp() } if (result) process.exit(result) -})(cli.argv) - .catch(err => { - console.log(err) - process.exit(1) - }) +})(cli.argv).catch((err) => { + console.log(err) + process.exit(1) +}) diff --git a/modules/integration-node/src/decrypt_materials_manager_node.ts b/modules/integration-node/src/decrypt_materials_manager_node.ts index 1cbc1a452..a79656eb9 100644 --- a/modules/integration-node/src/decrypt_materials_manager_node.ts +++ b/modules/integration-node/src/decrypt_materials_manager_node.ts @@ -6,77 +6,100 @@ import { MultiKeyringNode, KmsKeyringNode, RawAesKeyringNode, - WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars + WrappingSuiteIdentifier, RawAesWrappingSuiteIdentifier, RawRsaKeyringNode, - oaepHashSupported + oaepHashSupported, } from '@aws-crypto/client-node' import { - RsaKeyInfo, // eslint-disable-line no-unused-vars - AesKeyInfo, // eslint-disable-line no-unused-vars - KmsKeyInfo, // eslint-disable-line no-unused-vars - RSAKey, // eslint-disable-line no-unused-vars - AESKey, // eslint-disable-line no-unused-vars - KMSKey, // eslint-disable-line no-unused-vars - KeyInfoTuple // eslint-disable-line no-unused-vars + RsaKeyInfo, + AesKeyInfo, + KmsKeyInfo, + RSAKey, + AESKey, + KMSKey, + KeyInfoTuple, } from './types' import { constants } from 'crypto' -const Bits2RawAesWrappingSuiteIdentifier: {[key: number]: WrappingSuiteIdentifier} = { +const Bits2RawAesWrappingSuiteIdentifier: { + [key: number]: WrappingSuiteIdentifier +} = { 128: RawAesWrappingSuiteIdentifier.AES128_GCM_IV12_TAG16_NO_PADDING, 192: RawAesWrappingSuiteIdentifier.AES192_GCM_IV12_TAG16_NO_PADDING, - 256: RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING + 256: RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING, } -export function encryptMaterialsManagerNode (keyInfos: KeyInfoTuple[]) { +export function encryptMaterialsManagerNode(keyInfos: KeyInfoTuple[]) { const [generator, ...children] = keyInfos.map(keyringNode) return new MultiKeyringNode({ generator, children }) } -export function decryptMaterialsManagerNode (keyInfos: KeyInfoTuple[]) { +export function decryptMaterialsManagerNode(keyInfos: KeyInfoTuple[]) { const children = keyInfos.map(keyringNode) return new MultiKeyringNode({ children }) } -export function keyringNode ([ info, key ]: KeyInfoTuple) { +export function keyringNode([info, key]: KeyInfoTuple) { if (info.type === 'aws-kms' && key.type === 'aws-kms') { return kmsKeyring(info, key) } - if (info.type === 'raw' && info['encryption-algorithm'] === 'aes' && key.type === 'symmetric') { + if ( + info.type === 'raw' && + info['encryption-algorithm'] === 'aes' && + key.type === 'symmetric' + ) { return aesKeyring(info, key) } - if (info.type === 'raw' && info['encryption-algorithm'] === 'rsa' && (key.type === 'public' || key.type === 'private')) { + if ( + info.type === 'raw' && + info['encryption-algorithm'] === 'rsa' && + (key.type === 'public' || key.type === 'private') + ) { return rsaKeyring(info, key) } throw new Error('Unsupported keyring type') } -export function kmsKeyring (_keyInfo: KmsKeyInfo, key: KMSKey) { +export function kmsKeyring(_keyInfo: KmsKeyInfo, key: KMSKey) { const generatorKeyId = key['key-id'] return new KmsKeyringNode({ generatorKeyId }) } -export function aesKeyring (keyInfo:AesKeyInfo, key: AESKey) { +export function aesKeyring(keyInfo: AesKeyInfo, key: AESKey) { const keyName = key['key-id'] const keyNamespace = keyInfo['provider-id'] const { encoding, material } = key const unencryptedMasterKey = Buffer.alloc(key.bits / 8, material, encoding) const wrappingSuite = Bits2RawAesWrappingSuiteIdentifier[key.bits] - return new RawAesKeyringNode({ keyName, keyNamespace, unencryptedMasterKey, wrappingSuite }) + return new RawAesKeyringNode({ + keyName, + keyNamespace, + unencryptedMasterKey, + wrappingSuite, + }) } -export function rsaKeyring (keyInfo: RsaKeyInfo, key: RSAKey) { +export function rsaKeyring(keyInfo: RsaKeyInfo, key: RSAKey) { const keyName = key['key-id'] const keyNamespace = keyInfo['provider-id'] - const rsaKey = key.type === 'private' - ? { privateKey: key.material } - : { publicKey: key.material } + const rsaKey = + key.type === 'private' + ? { privateKey: key.material } + : { publicKey: key.material } const { padding, oaepHash } = rsaPadding(keyInfo) - return new RawRsaKeyringNode({ keyName, keyNamespace, rsaKey, padding, oaepHash }) + return new RawRsaKeyringNode({ + keyName, + keyNamespace, + rsaKey, + padding, + oaepHash, + }) } -export function rsaPadding (keyInfo: RsaKeyInfo) { - if (keyInfo['padding-algorithm'] === 'pkcs1') return { padding: constants.RSA_PKCS1_PADDING } +export function rsaPadding(keyInfo: RsaKeyInfo) { + if (keyInfo['padding-algorithm'] === 'pkcs1') + return { padding: constants.RSA_PKCS1_PADDING } const padding = constants.RSA_PKCS1_OAEP_PADDING const oaepHash = keyInfo['padding-hash'] needs(oaepHashSupported || oaepHash === 'sha1', 'Not supported at this time.') @@ -85,7 +108,7 @@ export function rsaPadding (keyInfo: RsaKeyInfo) { export class NotSupported extends Error { code: string - constructor (message?: string) { + constructor(message?: string) { super(message) Object.setPrototypeOf(this, NotSupported.prototype) this.code = 'NOT_SUPPORTED' diff --git a/modules/integration-node/src/get_decrypt_test_iterator.ts b/modules/integration-node/src/get_decrypt_test_iterator.ts index 7a774c0c4..2a1a1d47b 100644 --- a/modules/integration-node/src/get_decrypt_test_iterator.ts +++ b/modules/integration-node/src/get_decrypt_test_iterator.ts @@ -1,27 +1,21 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - open, - Entry, // eslint-disable-line no-unused-vars - ZipFile // eslint-disable-line no-unused-vars -} from 'yauzl' -import { - DecryptManifestList, // eslint-disable-line no-unused-vars - KeyList, // eslint-disable-line no-unused-vars - KeyInfoTuple // eslint-disable-line no-unused-vars -} from './types' -import { Readable } from 'stream' // eslint-disable-line no-unused-vars +import { open, Entry, ZipFile } from 'yauzl' +import { DecryptManifestList, KeyList, KeyInfoTuple } from './types' +import { Readable } from 'stream' import streamToPromise from 'stream-to-promise' -export async function getDecryptTestVectorIterator (vectorFile: string) { +export async function getDecryptTestVectorIterator(vectorFile: string) { const filesMap = await centralDirectory(vectorFile) return _getDecryptTestVectorIterator(filesMap) } /* Just a simple more testable function */ -export async function _getDecryptTestVectorIterator (filesMap: Map) { +export async function _getDecryptTestVectorIterator( + filesMap: Map +) { const readUriOnce = (() => { const cache: Map = new Map() return async (uri: string): Promise => { @@ -38,37 +32,44 @@ export async function _getDecryptTestVectorIterator (filesMap: Map { + return (function* nextTest(): IterableIterator { for (const [name, testInfo] of Object.entries(tests)) { - const { plaintext: plaintextFile, ciphertext, 'master-keys': masterKeys } = testInfo + const { + plaintext: plaintextFile, + ciphertext, + 'master-keys': masterKeys, + } = testInfo const plainTextInfo = filesMap.get(plaintextFile) const cipherInfo = filesMap.get(ciphertext) - if (!cipherInfo || !plainTextInfo) throw new Error(`no file for ${name}: ${ciphertext} | ${plaintextFile}`) + if (!cipherInfo || !plainTextInfo) + throw new Error(`no file for ${name}: ${ciphertext} | ${plaintextFile}`) const cipherStream = cipherInfo.stream const plainTextStream = plainTextInfo.stream - const keysInfo = masterKeys.map(keyInfo => { + const keysInfo = masterKeys.map((keyInfo) => { const key = keys[keyInfo.key] if (!key) throw new Error(`no key for ${name}`) - return [keyInfo, key] + return [keyInfo, key] as KeyInfoTuple }) yield { name, keysInfo, cipherStream, - plainTextStream + plainTextStream, } } })() } export interface TestVectorInfo { - name: string, - keysInfo: KeyInfoTuple[], + name: string + keysInfo: KeyInfoTuple[] cipherStream: () => Promise plainTextStream: () => Promise } @@ -77,29 +78,35 @@ interface StreamEntry extends Entry { stream: () => Promise } -function centralDirectory (vectorFile: string): Promise> { +async function centralDirectory( + vectorFile: string +): Promise> { const filesMap = new Map() return new Promise((resolve, reject) => { - open(vectorFile, { lazyEntries: true, autoClose: false }, (err, zipfile) => { - if (err || !zipfile) return reject(err) + open( + vectorFile, + { lazyEntries: true, autoClose: false }, + (err, zipfile) => { + if (err || !zipfile) return reject(err) - zipfile - .on('entry', (entry: StreamEntry) => { - entry.stream = curryStream(zipfile, entry) - filesMap.set('file://' + entry.fileName, entry) - zipfile.readEntry() - }) - .on('end', () => { - resolve(filesMap) - }) - .on('error', (err) => reject(err)) - .readEntry() - }) + zipfile + .on('entry', (entry: StreamEntry) => { + entry.stream = curryStream(zipfile, entry) + filesMap.set('file://' + entry.fileName, entry) + zipfile.readEntry() + }) + .on('end', () => { + resolve(filesMap) + }) + .on('error', (err) => reject(err)) + .readEntry() + } + ) }) } -function curryStream (zipfile: ZipFile, entry: Entry) { - return function stream (): Promise { +function curryStream(zipfile: ZipFile, entry: Entry) { + return async function stream(): Promise { return new Promise((resolve, reject) => { zipfile.openReadStream(entry, (err, readStream) => { if (err || !readStream) return reject(err) diff --git a/modules/integration-node/src/get_encrypt_test_iterator.ts b/modules/integration-node/src/get_encrypt_test_iterator.ts index 67e094dbb..fd3214583 100644 --- a/modules/integration-node/src/get_encrypt_test_iterator.ts +++ b/modules/integration-node/src/get_encrypt_test_iterator.ts @@ -1,79 +1,79 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - EncryptManifestList, // eslint-disable-line no-unused-vars - KeyList, // eslint-disable-line no-unused-vars - KeyInfoTuple // eslint-disable-line no-unused-vars -} from './types' +import { EncryptManifestList, KeyList, KeyInfoTuple } from './types' import { randomBytes } from 'crypto' import { - AlgorithmSuiteIdentifier, // eslint-disable-line no-unused-vars - EncryptionContext // eslint-disable-line no-unused-vars + AlgorithmSuiteIdentifier, + EncryptionContext, } from '@aws-crypto/client-node' import { URL } from 'url' import { readFileSync } from 'fs' import got from 'got' -export async function getEncryptTestVectorIterator (manifestFile: string, keyFile: string) { +export async function getEncryptTestVectorIterator( + manifestFile: string, + keyFile: string +) { const [manifest, keys]: [EncryptManifestList, KeyList] = await Promise.all([ getParsedJSON(manifestFile), - getParsedJSON(keyFile) + getParsedJSON(keyFile), ]) return _getEncryptTestVectorIterator(manifest, keys) } /* Just a simple more testable function */ -export function _getEncryptTestVectorIterator ({ tests, plaintexts }: EncryptManifestList, { keys }: KeyList) { - const plaintextBytes: {[name: string]: Buffer} = {} +export function _getEncryptTestVectorIterator( + { tests, plaintexts }: EncryptManifestList, + { keys }: KeyList +) { + const plaintextBytes: { [name: string]: Buffer } = {} - Object - .keys(plaintexts) - .forEach(name => { - plaintextBytes[name] = randomBytes(plaintexts[name]) - }) + Object.keys(plaintexts).forEach((name) => { + plaintextBytes[name] = randomBytes(plaintexts[name]) + }) - return (function * nextTest (): IterableIterator { + return (function* nextTest(): IterableIterator { for (const [name, testInfo] of Object.entries(tests)) { const { plaintext, 'master-keys': masterKeys, algorithm, 'frame-size': frameLength, - 'encryption-context': encryptionContext + 'encryption-context': encryptionContext, } = testInfo - const keysInfo = masterKeys.map(keyInfo => { + const keysInfo = masterKeys.map((keyInfo) => { const key = keys[keyInfo.key] if (!key) throw new Error(`no key for ${name}`) - return [keyInfo, key] + return [keyInfo, key] as KeyInfoTuple }) /* I'm expecting that the encrypt function will throw if this is not a supported AlgorithmSuiteIdentifier */ - const suiteId = parseInt(algorithm, 16) + const suiteId = parseInt(algorithm, 16) as AlgorithmSuiteIdentifier yield { name, keysInfo, plainTextData: plaintextBytes[plaintext], - encryptOp: { suiteId, frameLength, encryptionContext } + encryptOp: { suiteId, frameLength, encryptionContext }, } } })() } export interface EncryptTestVectorInfo { - name: string, - keysInfo: KeyInfoTuple[], - plainTextData: Buffer, + name: string + keysInfo: KeyInfoTuple[] + plainTextData: Buffer encryptOp: { - suiteId: AlgorithmSuiteIdentifier, - frameLength: number, + suiteId: AlgorithmSuiteIdentifier + frameLength: number encryptionContext: EncryptionContext } } -async function getParsedJSON (thing: string) { +async function getParsedJSON(thing: string) { try { const url = new URL(thing) if (url.protocol === 'file:') { @@ -85,12 +85,12 @@ async function getParsedJSON (thing: string) { return jsonAtPath(thing) } } -async function jsonAtUrl (url: URL) { +async function jsonAtUrl(url: URL) { const { body } = await got(url) return JSON.parse(body) } -function jsonAtPath (path: string) { +function jsonAtPath(path: string) { const json = readFileSync(path, { encoding: 'utf-8' }) return JSON.parse(json) } diff --git a/modules/integration-node/src/index.ts b/modules/integration-node/src/index.ts index cdd536778..20203d9e9 100644 --- a/modules/integration-node/src/index.ts +++ b/modules/integration-node/src/index.ts @@ -1,4 +1,3 @@ - // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 diff --git a/modules/integration-node/src/integration_tests.ts b/modules/integration-node/src/integration_tests.ts index 788688263..e0ff292e0 100644 --- a/modules/integration-node/src/integration_tests.ts +++ b/modules/integration-node/src/integration_tests.ts @@ -2,30 +2,36 @@ // SPDX-License-Identifier: Apache-2.0 import { - TestVectorInfo, // eslint-disable-line no-unused-vars - getDecryptTestVectorIterator + TestVectorInfo, + getDecryptTestVectorIterator, } from './get_decrypt_test_iterator' import { - EncryptTestVectorInfo, // eslint-disable-line no-unused-vars - getEncryptTestVectorIterator + EncryptTestVectorInfo, + getEncryptTestVectorIterator, } from './get_encrypt_test_iterator' -import { decryptMaterialsManagerNode, encryptMaterialsManagerNode } from './decrypt_materials_manager_node' +import { + decryptMaterialsManagerNode, + encryptMaterialsManagerNode, +} from './decrypt_materials_manager_node' import { decrypt, encrypt, needs } from '@aws-crypto/client-node' import { URL } from 'url' import got from 'got' import streamToPromise from 'stream-to-promise' -const notSupportedDecryptMessages = [ - 'Not supported at this time.' -] +const notSupportedDecryptMessages = ['Not supported at this time.'] const notSupportedEncryptMessages = [ 'frameLength out of bounds: 0 > frameLength >= 4294967295', - 'Not supported at this time.' + 'Not supported at this time.', ] // This is only viable for small streams, if we start get get larger streams, an stream equality should get written -export async function testDecryptVector ({ name, keysInfo, plainTextStream, cipherStream }: TestVectorInfo): Promise { +export async function testDecryptVector({ + name, + keysInfo, + plainTextStream, + cipherStream, +}: TestVectorInfo): Promise { try { const cmm = decryptMaterialsManagerNode(keysInfo) const knowGood = await streamToPromise(await plainTextStream()) @@ -38,18 +44,25 @@ export async function testDecryptVector ({ name, keysInfo, plainTextStream, ciph } // This is only viable for small streams, if we start get get larger streams, an stream equality should get written -export async function testEncryptVector ({ name, keysInfo, encryptOp, plainTextData }: EncryptTestVectorInfo, decryptOracle: string): Promise { +export async function testEncryptVector( + { name, keysInfo, encryptOp, plainTextData }: EncryptTestVectorInfo, + decryptOracle: string +): Promise { try { const cmm = encryptMaterialsManagerNode(keysInfo) - const { result: encryptResult } = await encrypt(cmm, plainTextData, encryptOp) + const { result: encryptResult } = await encrypt( + cmm, + plainTextData, + encryptOp + ) const decryptResponse = await got.post(decryptOracle, { headers: { 'Content-Type': 'application/octet-stream', - 'Accept': 'application/octet-stream' + Accept: 'application/octet-stream', }, body: encryptResult, - responseType: 'buffer' + responseType: 'buffer', }) needs(decryptResponse.statusCode === 200, 'decrypt failure') const { body } = decryptResponse @@ -60,12 +73,17 @@ export async function testEncryptVector ({ name, keysInfo, encryptOp, plainTextD } } -export async function integrationDecryptTestVectors (vectorFile: string, tolerateFailures: number = 0, testName?: string, concurrency: number = 1) { +export async function integrationDecryptTestVectors( + vectorFile: string, + tolerateFailures = 0, + testName?: string, + concurrency = 1 +) { const tests = await getDecryptTestVectorIterator(vectorFile) return parallelTests(concurrency, tolerateFailures, runTest, tests) - async function runTest (test: TestVectorInfo): Promise { + async function runTest(test: TestVectorInfo): Promise { if (testName) { if (test.name !== testName) return true } @@ -84,17 +102,27 @@ export async function integrationDecryptTestVectors (vectorFile: string, tolerat } } -export async function integrationEncryptTestVectors (manifestFile: string, keyFile: string, decryptOracle: string, tolerateFailures: number = 0, testName?: string, concurrency: number = 1) { +export async function integrationEncryptTestVectors( + manifestFile: string, + keyFile: string, + decryptOracle: string, + tolerateFailures = 0, + testName?: string, + concurrency = 1 +) { const decryptOracleUrl = new URL(decryptOracle).toString() const tests = await getEncryptTestVectorIterator(manifestFile, keyFile) return parallelTests(concurrency, tolerateFailures, runTest, tests) - async function runTest (test: EncryptTestVectorInfo): Promise { + async function runTest(test: EncryptTestVectorInfo): Promise { if (testName) { if (test.name !== testName) return true } - const { result, name, err } = await testEncryptVector(test, decryptOracleUrl) + const { result, name, err } = await testEncryptVector( + test, + decryptOracleUrl + ) if (result) { console.log({ name, result }) return true @@ -110,9 +138,14 @@ export async function integrationEncryptTestVectors (manifestFile: string, keyFi } async function parallelTests< - Test extends EncryptTestVectorInfo|TestVectorInfo, + Test extends EncryptTestVectorInfo | TestVectorInfo, work extends (test: Test) => Promise ->(max: number, tolerateFailures: number, runTest: work, tests: IterableIterator) { +>( + max: number, + tolerateFailures: number, + runTest: work, + tests: IterableIterator +) { let _resolve: (failureCount: number) => void const queue = new Set>() let failureCount = 0 @@ -122,7 +155,7 @@ async function parallelTests< enqueue() }) - function enqueue (): void { + function enqueue(): void { /* If there are more failures than I am willing to tolerate, stop. */ if (failureCount > tolerateFailures) return _resolve(failureCount) /* Do not over-fill the queue! */ @@ -181,7 +214,7 @@ async function parallelTests< } interface TestVectorResults { - name: string, - result: boolean, + name: string + result: boolean err?: Error } diff --git a/modules/integration-node/src/types.ts b/modules/integration-node/src/types.ts index e0e860540..2a73f2dac 100644 --- a/modules/integration-node/src/types.ts +++ b/modules/integration-node/src/types.ts @@ -1,25 +1,25 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EncryptionContext } from '@aws-crypto/client-node' // eslint-disable-line no-unused-vars +import { EncryptionContext } from '@aws-crypto/client-node' export interface DecryptManifestList { manifest: DecryptManifest client: Client keys: string - tests: {[testName: string]: DecryptTest} + tests: { [testName: string]: DecryptTest } } export interface EncryptManifestList { manifest: EncryptManifest keys: string - plaintexts: {[name: string]: number} - tests: {[testName: string]: EncryptTest} + plaintexts: { [name: string]: number } + tests: { [testName: string]: EncryptTest } } export interface KeyList { manifest: Manifest - keys: {[key: string]: (KMSKey|AESKey|RSAKey)} + keys: { [key: string]: KMSKey | AESKey | RSAKey } } interface Manifest { @@ -41,21 +41,21 @@ interface Client { } interface KeyInfo { - type: 'aws-kms'|'raw' + type: 'aws-kms' | 'raw' key: string } interface RawKeyInfo extends KeyInfo { - type: 'raw', + type: 'raw' 'provider-id': string - 'encryption-algorithm': 'aes'|'rsa', - 'padding-algorithm': 'pkcs1'|'oaep-mgf1'|null + 'encryption-algorithm': 'aes' | 'rsa' + 'padding-algorithm': 'pkcs1' | 'oaep-mgf1' | null } export interface RsaKeyInfo extends RawKeyInfo { - 'encryption-algorithm': 'rsa', - 'padding-algorithm': 'pkcs1'|'oaep-mgf1' - 'padding-hash'?: 'sha1'|'sha256'|'sha384'|'sha512' + 'encryption-algorithm': 'rsa' + 'padding-algorithm': 'pkcs1' | 'oaep-mgf1' + 'padding-hash'?: 'sha1' | 'sha256' | 'sha384' | 'sha512' } export interface AesKeyInfo extends RawKeyInfo { @@ -73,18 +73,18 @@ interface EncryptTest { algorithm: string 'frame-size': number 'encryption-context': EncryptionContext - 'master-keys': (RsaKeyInfo|AesKeyInfo|KmsKeyInfo)[] + 'master-keys': (RsaKeyInfo | AesKeyInfo | KmsKeyInfo)[] } interface DecryptTest { plaintext: string ciphertext: string - 'master-keys': (RsaKeyInfo|AesKeyInfo|KmsKeyInfo)[] + 'master-keys': (RsaKeyInfo | AesKeyInfo | KmsKeyInfo)[] } interface Key { - 'encrypt': boolean - 'decrypt': boolean + encrypt: boolean + decrypt: boolean 'key-id': string } @@ -98,7 +98,7 @@ interface RawKey extends Key { export interface RSAKey extends RawKey { algorithm: 'rsa' - type: 'public'|'private' + type: 'public' | 'private' bits: number encoding: 'pem' material: string @@ -117,4 +117,7 @@ export interface KMSKey extends Key { 'key-id': string } -export type KeyInfoTuple = [RsaKeyInfo, RSAKey] | [AesKeyInfo, AESKey] | [KmsKeyInfo, KMSKey] +export type KeyInfoTuple = + | [RsaKeyInfo, RSAKey] + | [AesKeyInfo, AESKey] + | [KmsKeyInfo, KMSKey] diff --git a/modules/kms-keyring-browser/package.json b/modules/kms-keyring-browser/package.json index 5465d1acc..98cbd268b 100644 --- a/modules/kms-keyring-browser/package.json +++ b/modules/kms-keyring-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/kms-keyring-browser/src/kms_keyring_browser.ts b/modules/kms-keyring-browser/src/kms_keyring_browser.ts index ee6743bd2..62e42eca7 100644 --- a/modules/kms-keyring-browser/src/kms_keyring_browser.ts +++ b/modules/kms-keyring-browser/src/kms_keyring_browser.ts @@ -3,52 +3,62 @@ import { KmsKeyringClass, - KeyRingConstructible, // eslint-disable-line no-unused-vars - KmsKeyringInput, // eslint-disable-line no-unused-vars - KMSConstructible, // eslint-disable-line no-unused-vars - KmsClientSupplier, // eslint-disable-line no-unused-vars + KeyRingConstructible, + KmsKeyringInput, + KMSConstructible, + KmsClientSupplier, getClient, limitRegions, excludeRegions, - cacheClients + cacheClients, } from '@aws-crypto/kms-keyring' import { - WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars - WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars + WebCryptoAlgorithmSuite, + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, + EncryptedDataKey, immutableClass, importForWebCryptoEncryptionMaterial, importForWebCryptoDecryptionMaterial, - KeyringWebCrypto // eslint-disable-line no-unused-vars + KeyringWebCrypto, } from '@aws-crypto/material-management-browser' -import { KMS } from 'aws-sdk' // eslint-disable-line no-unused-vars +import { KMS } from 'aws-sdk' -const getKmsClient = getClient(KMS, { customUserAgent: 'AwsEncryptionSdkJavascriptBrowser' }) +const getKmsClient = getClient(KMS, { + customUserAgent: 'AwsEncryptionSdkJavascriptBrowser', +}) const cacheKmsClients = cacheClients(getKmsClient) export type KmsKeyringWebCryptoInput = Partial> -export type KMSWebCryptoConstructible = KMSConstructible +export type KMSWebCryptoConstructible = KMSConstructible< + KMS, + KMS.ClientConfiguration +> export type KmsWebCryptoClientSupplier = KmsClientSupplier -export class KmsKeyringBrowser extends KmsKeyringClass(KeyringWebCrypto as KeyRingConstructible) { - constructor ({ +export class KmsKeyringBrowser extends KmsKeyringClass( + KeyringWebCrypto as KeyRingConstructible +) { + constructor({ clientProvider = cacheKmsClients, keyIds, generatorKeyId, grantTokens, - discovery + discovery, }: KmsKeyringWebCryptoInput = {}) { super({ clientProvider, keyIds, generatorKeyId, grantTokens, discovery }) } - async _onEncrypt (material: WebCryptoEncryptionMaterial) { + async _onEncrypt(material: WebCryptoEncryptionMaterial) { const _material = await super._onEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) } - async _onDecrypt (material: WebCryptoDecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]) { + async _onDecrypt( + material: WebCryptoDecryptionMaterial, + encryptedDataKeys: EncryptedDataKey[] + ) { const _material = await super._onDecrypt(material, encryptedDataKeys) return importForWebCryptoDecryptionMaterial(_material) @@ -56,4 +66,11 @@ export class KmsKeyringBrowser extends KmsKeyringClass(KeyringWebCrypto as KeyRi } immutableClass(KmsKeyringBrowser) -export { getClient, cacheKmsClients, limitRegions, excludeRegions, cacheClients, KMS } +export { + getClient, + cacheKmsClients, + limitRegions, + excludeRegions, + cacheClients, + KMS, +} diff --git a/modules/kms-keyring-node/package.json b/modules/kms-keyring-node/package.json index ea44c36a6..7d2376072 100644 --- a/modules/kms-keyring-node/package.json +++ b/modules/kms-keyring-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/kms-keyring-node/src/kms_keyring_node.ts b/modules/kms-keyring-node/src/kms_keyring_node.ts index 409f737c2..f1822ac34 100644 --- a/modules/kms-keyring-node/src/kms_keyring_node.ts +++ b/modules/kms-keyring-node/src/kms_keyring_node.ts @@ -3,38 +3,54 @@ import { KmsKeyringClass, - KeyRingConstructible, // eslint-disable-line no-unused-vars - KmsKeyringInput, // eslint-disable-line no-unused-vars - KMSConstructible, // eslint-disable-line no-unused-vars - KmsClientSupplier, // eslint-disable-line no-unused-vars + KeyRingConstructible, + KmsKeyringInput, + KMSConstructible, + KmsClientSupplier, getClient, limitRegions, excludeRegions, - cacheClients + cacheClients, } from '@aws-crypto/kms-keyring' import { - NodeAlgorithmSuite, // eslint-disable-line no-unused-vars - immutableClass, KeyringNode + NodeAlgorithmSuite, + immutableClass, + KeyringNode, } from '@aws-crypto/material-management-node' -import { KMS } from 'aws-sdk' // eslint-disable-line no-unused-vars -const getKmsClient = getClient(KMS, { customUserAgent: 'AwsEncryptionSdkJavascriptNodejs' }) +import { KMS } from 'aws-sdk' +const getKmsClient = getClient(KMS, { + customUserAgent: 'AwsEncryptionSdkJavascriptNodejs', +}) const cacheKmsClients = cacheClients(getKmsClient) export type KmsKeyringNodeInput = Partial> -export type KMSNodeConstructible = KMSConstructible +export type KMSNodeConstructible = KMSConstructible< + KMS, + KMS.ClientConfiguration +> export type KmsNodeClientSupplier = KmsClientSupplier -export class KmsKeyringNode extends KmsKeyringClass(KeyringNode as KeyRingConstructible) { - constructor ({ +export class KmsKeyringNode extends KmsKeyringClass( + KeyringNode as KeyRingConstructible +) { + constructor({ clientProvider = cacheKmsClients, keyIds, generatorKeyId, grantTokens, - discovery + discovery, }: KmsKeyringNodeInput = {}) { super({ clientProvider, keyIds, generatorKeyId, grantTokens, discovery }) } } immutableClass(KmsKeyringNode) -export { getKmsClient, cacheKmsClients, getClient, limitRegions, excludeRegions, cacheClients, KMS } +export { + getKmsClient, + cacheKmsClients, + getClient, + limitRegions, + excludeRegions, + cacheClients, + KMS, +} diff --git a/modules/kms-keyring/package.json b/modules/kms-keyring/package.json index af3436e8a..73419414f 100644 --- a/modules/kms-keyring/package.json +++ b/modules/kms-keyring/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/kms-keyring/src/helpers.ts b/modules/kms-keyring/src/helpers.ts index 3d2fe2e16..4071e355f 100644 --- a/modules/kms-keyring/src/helpers.ts +++ b/modules/kms-keyring/src/helpers.ts @@ -1,74 +1,82 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KmsClientSupplier } from './kms_client_supplier' // eslint-disable-line no-unused-vars +import { KmsClientSupplier } from './kms_client_supplier' import { - 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 + AwsEsdkKMSInterface, + GenerateDataKeyResponse, + RequiredGenerateDataKeyResponse, + EncryptResponse, + RequiredEncryptResponse, + DecryptResponse, + RequiredDecryptResponse, } from './kms_types' import { regionFromKmsKeyArn } from './region_from_kms_key_arn' import { - EncryptionContext, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars - needs -} from '@aws-crypto/material-management' // eslint-disable-line no-unused-vars + EncryptionContext, + EncryptedDataKey, + needs, +} from '@aws-crypto/material-management' export const KMS_PROVIDER_ID = 'aws-kms' -export async function generateDataKey ( +export async function generateDataKey( clientProvider: KmsClientSupplier, NumberOfBytes: number, KeyId: string, EncryptionContext: EncryptionContext, GrantTokens?: string[] -): Promise { +): Promise { const region = regionFromKmsKeyArn(KeyId) const client = clientProvider(region) /* Check for early return (Postcondition): clientProvider did not return a client for generateDataKey. */ if (!client) return false - const v2vsV3Response = client.generateDataKey({ KeyId, GrantTokens, NumberOfBytes, EncryptionContext }) - const v2vsV3Promise = 'promise' in v2vsV3Response - ? v2vsV3Response.promise() - : v2vsV3Response + 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 ( +export async function encrypt( clientProvider: KmsClientSupplier, Plaintext: Uint8Array, KeyId: string, EncryptionContext: EncryptionContext, GrantTokens?: string[] -): Promise { +): Promise { const region = regionFromKmsKeyArn(KeyId) const client = clientProvider(region) /* Check for early return (Postcondition): clientProvider did not return a client for encrypt. */ if (!client) return false - const v2vsV3Response = client.encrypt({ KeyId, Plaintext, EncryptionContext, GrantTokens }) - const v2vsV3Promise = 'promise' in v2vsV3Response - ? v2vsV3Response.promise() - : v2vsV3Response + 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 ( +export async function decrypt( clientProvider: KmsClientSupplier, { providerId, providerInfo, encryptedDataKey }: EncryptedDataKey, EncryptionContext: EncryptionContext, GrantTokens?: string[] -): Promise { +): Promise { /* Precondition: The EDK must be a KMS edk. */ needs(providerId === KMS_PROVIDER_ID, 'Unsupported providerId') const region = regionFromKmsKeyArn(providerInfo) @@ -76,54 +84,72 @@ export async function decrypt ( /* Check for early return (Postcondition): clientProvider did not return a client for decrypt. */ if (!client) return false - const v2vsV3Response = client.decrypt({ CiphertextBlob: encryptedDataKey, EncryptionContext, GrantTokens }) - const v2vsV3Promise = 'promise' in v2vsV3Response - ? v2vsV3Response.promise() - : v2vsV3Response + 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 ({ +export function kmsResponseToEncryptedDataKey({ KeyId: providerInfo, - CiphertextBlob: encryptedDataKey + CiphertextBlob: encryptedDataKey, }: RequiredEncryptResponse) { - return new EncryptedDataKey({ providerId: KMS_PROVIDER_ID, providerInfo, encryptedDataKey }) + return new EncryptedDataKey({ + providerId: KMS_PROVIDER_ID, + providerInfo, + encryptedDataKey, + }) } -function safeGenerateDataKey ( +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 safePlaintext(dataKey) + needs( + typeof dataKey.KeyId === 'string' && + dataKey.Plaintext instanceof Uint8Array && + dataKey.CiphertextBlob instanceof Uint8Array, + 'Malformed KMS response.' + ) + + return safePlaintext( + dataKey as RequiredGenerateDataKeyResponse + ) as RequiredGenerateDataKeyResponse } -function safeEncryptOutput ( - dataKey: EncryptResponse -): RequiredEncryptResponse { +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.') + needs( + typeof dataKey.KeyId === 'string' && + dataKey.CiphertextBlob instanceof Uint8Array, + 'Malformed KMS response.' + ) - return dataKey + return dataKey as RequiredEncryptResponse } -function safeDecryptOutput ( - dataKey: DecryptResponse -): RequiredDecryptResponse { +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 safePlaintext(dataKey) + needs( + typeof dataKey.KeyId === 'string' && + dataKey.Plaintext instanceof Uint8Array, + 'Malformed KMS response.' + ) + + return safePlaintext( + dataKey as RequiredDecryptResponse + ) as RequiredDecryptResponse } -function safePlaintext (dataKey: RequiredDecryptResponse | RequiredGenerateDataKeyResponse): RequiredDecryptResponse | RequiredGenerateDataKeyResponse { +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. diff --git a/modules/kms-keyring/src/kms_client_supplier.ts b/modules/kms-keyring/src/kms_client_supplier.ts index 5fd11e600..bfb9ed1bd 100644 --- a/modules/kms-keyring/src/kms_client_supplier.ts +++ b/modules/kms-keyring/src/kms_client_supplier.ts @@ -2,35 +2,40 @@ // SPDX-License-Identifier: Apache-2.0 import { needs } from '@aws-crypto/material-management' -import { - AwsEsdkKMSInterface // eslint-disable-line no-unused-vars -} from './kms_types' +import { AwsEsdkKMSInterface } from './kms_types' -interface KMSConstructibleNonOption { - new(config: Config) : Client +interface KMSConstructibleNonOption< + Client extends AwsEsdkKMSInterface, + Config +> { + new (config: Config): Client } interface KMSConstructibleOption { - new(config?: Config) : Client + new (config?: Config): Client } -export type KMSConstructible = KMSConstructibleNonOption | KMSConstructibleOption +export type KMSConstructible = + | KMSConstructibleNonOption + | KMSConstructibleOption export interface KmsClientSupplier { /* KmsClientProvider is allowed to return undefined if, for example, user wants to exclude particular regions. */ (region: string): Client | false } -export function getClient ( +export function getClient( KMSClient: KMSConstructible, defaultConfig?: Config ): KmsClientSupplier { - return function getKmsClient (region: string) { + return function getKmsClient(region: string) { /* a KMS alias is supported. These do not have a region * in this case, the Encryption SDK should find the default region * or the default region needs to be supplied to this function */ - const config = (region ? { ...defaultConfig, region } : { ...defaultConfig }) as Config + const config = (region + ? { ...defaultConfig, region } + : { ...defaultConfig }) as Config const client = new KMSClient(config) /* Postcondition: A region must be configured. @@ -44,12 +49,15 @@ export function getClient ( } } -export function limitRegions ( +export function limitRegions( regions: string[], getClient: KmsClientSupplier ): KmsClientSupplier { /* Precondition: limitRegions requires that region be a string. */ - needs(regions.every(r => !!r && typeof r === 'string'), 'Can only limit on region strings') + needs( + regions.every((r) => !!r && typeof r === 'string'), + 'Can only limit on region strings' + ) return (region: string) => { if (!regions.includes(region)) return false @@ -57,12 +65,15 @@ export function limitRegions ( } } -export function excludeRegions ( +export function excludeRegions( regions: string[], getClient: KmsClientSupplier ): KmsClientSupplier { /* Precondition: excludeRegions requires region be a string. */ - needs(regions.every(r => !!r && typeof r === 'string'), 'Can only exclude on region strings') + needs( + regions.every((r) => !!r && typeof r === 'string'), + 'Can only exclude on region strings' + ) return (region: string) => { if (regions.includes(region)) return false @@ -70,14 +81,15 @@ export function excludeRegions ( } } -export function cacheClients ( +export function cacheClients( getClient: KmsClientSupplier ): KmsClientSupplier { - const clientsCache: {[key: string]: Client|false} = {} + const clientsCache: { [key: string]: Client | false } = {} return (region: string) => { // Do not cache until KMS has been responded in the given region - if (!clientsCache.hasOwnProperty(region)) return deferCache(clientsCache, region, getClient(region)) + if (!Object.prototype.hasOwnProperty.call(clientsCache, region)) + return deferCache(clientsCache, region, getClient(region)) return clientsCache[region] } } @@ -89,11 +101,11 @@ export function cacheClients ( * This does *not* mean that this call is successful, * only that the region is backed by a functional KMS service. */ -export function deferCache ( - clientsCache: {[key: string]: Client|false}, +export function deferCache( + clientsCache: { [key: string]: Client | false }, region: string, - client: Client|false -): Client|false { + client: Client | false +): Client | false { /* Check for early return (Postcondition): No client, then I cache false and move on. */ if (!client) { clientsCache[region] = false @@ -101,29 +113,46 @@ export function deferCache ( } const { encrypt, decrypt, generateDataKey } = client - return (<(keyof AwsEsdkKMSInterface)[]>['encrypt', 'decrypt', 'generateDataKey']).reduce(wrapOperation, client) + return ([ + 'encrypt', + 'decrypt', + 'generateDataKey', + ] as (keyof AwsEsdkKMSInterface)[]).reduce(wrapOperation, client) /* Wrap each of the operations to cache the client on response */ - function wrapOperation (client: Client, name: keyof AwsEsdkKMSInterface): Client { + function wrapOperation( + client: Client, + name: keyof AwsEsdkKMSInterface + ): Client { const original = client[name] - client[name] = function wrappedOperation (this: Client, args: any): Promise { + client[name] = async function wrappedOperation( + this: Client, + args: any + ): Promise { // @ts-ignore (there should be a TypeScript solution for this) const v2vsV3Response = original.call(client, args) - const v2vsV3Promise = 'promise' in v2vsV3Response - ? v2vsV3Response.promise() - : v2vsV3Response + const v2vsV3Promise = + 'promise' in v2vsV3Response ? v2vsV3Response.promise() : v2vsV3Response return v2vsV3Promise .then((response: any) => { - clientsCache[region] = Object.assign(client, { encrypt, decrypt, generateDataKey }) + clientsCache[region] = Object.assign(client, { + encrypt, + decrypt, + generateDataKey, + }) return response }) - .catch((e: any) => { + .catch(async (e: any) => { /* Errors from a KMS contact mean that the region is "live". * As such the client can be cached because the problem is not with the client per se, * but with the request made. */ if (e.$metadata && e.$metadata.httpStatusCode) { - clientsCache[region] = Object.assign(client, { encrypt, decrypt, generateDataKey }) + clientsCache[region] = Object.assign(client, { + encrypt, + decrypt, + generateDataKey, + }) } // The request was not successful return Promise.reject(e) diff --git a/modules/kms-keyring/src/kms_keyring.ts b/modules/kms-keyring/src/kms_keyring.ts index 8bfc8a680..5740f6e31 100644 --- a/modules/kms-keyring/src/kms_keyring.ts +++ b/modules/kms-keyring/src/kms_keyring.ts @@ -1,25 +1,28 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KmsClientSupplier } from './kms_client_supplier' // eslint-disable-line no-unused-vars -import { - AwsEsdkKMSInterface, // eslint-disable-line no-unused-vars - RequiredDecryptResponse // eslint-disable-line no-unused-vars -} from './kms_types' +import { KmsClientSupplier } from './kms_client_supplier' +import { AwsEsdkKMSInterface, RequiredDecryptResponse } from './kms_types' import { needs, - Keyring, // eslint-disable-line no-unused-vars - EncryptionMaterial, // eslint-disable-line no-unused-vars - DecryptionMaterial, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - KeyringTrace, // eslint-disable-line no-unused-vars + Keyring, + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, + KeyringTrace, KeyringTraceFlag, - EncryptedDataKey, // eslint-disable-line no-unused-vars + EncryptedDataKey, immutableClass, readOnlyProperty, - unwrapDataKey + unwrapDataKey, } from '@aws-crypto/material-management' -import { KMS_PROVIDER_ID, generateDataKey, encrypt, decrypt, kmsResponseToEncryptedDataKey } from './helpers' +import { + KMS_PROVIDER_ID, + generateDataKey, + encrypt, + decrypt, + kmsResponseToEncryptedDataKey, +} from './helpers' import { regionFromKmsKeyArn } from './region_from_kms_key_arn' export interface KmsKeyringInput { @@ -30,27 +33,37 @@ export interface KmsKeyringInput { discovery?: boolean } -export interface KeyRing extends Keyring { +export interface KeyRing< + S extends SupportedAlgorithmSuites, + Client extends AwsEsdkKMSInterface +> extends Keyring { keyIds: ReadonlyArray generatorKeyId?: string clientProvider: KmsClientSupplier grantTokens?: string[] isDiscovery: boolean _onEncrypt(material: EncryptionMaterial): Promise> - _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> + _onDecrypt( + material: DecryptionMaterial, + encryptedDataKeys: EncryptedDataKey[] + ): Promise> } -export interface KmsKeyRingConstructible { - new(input: KmsKeyringInput): KeyRing +export interface KmsKeyRingConstructible< + S extends SupportedAlgorithmSuites, + Client extends AwsEsdkKMSInterface +> { + new (input: KmsKeyringInput): KeyRing } export interface KeyRingConstructible { - new(): Keyring + new (): Keyring } -export function KmsKeyringClass ( - BaseKeyring: KeyRingConstructible -): KmsKeyRingConstructible { +export function KmsKeyringClass< + S extends SupportedAlgorithmSuites, + Client extends AwsEsdkKMSInterface +>(BaseKeyring: KeyRingConstructible): KmsKeyRingConstructible { class KmsKeyring extends BaseKeyring implements KeyRing { public keyIds!: ReadonlyArray public generatorKeyId?: string @@ -58,17 +71,38 @@ export function KmsKeyringClass) { + constructor({ + clientProvider, + generatorKeyId, + keyIds = [], + grantTokens, + discovery, + }: KmsKeyringInput) { super() /* Precondition: This is an abstract class. (But TypeScript does not have a clean way to model this) */ needs(this.constructor !== KmsKeyring, 'new KmsKeyring is not allowed') /* Precondition: A noop KmsKeyring is not allowed. */ - needs(!(!discovery && !generatorKeyId && !keyIds.length), 'Noop keyring is not allowed: Set a keyId or discovery') + needs( + !(!discovery && !generatorKeyId && !keyIds.length), + 'Noop keyring is not allowed: Set a keyId or discovery' + ) /* Precondition: A keyring can be either a Discovery or have keyIds configured. */ - needs(!(discovery && (generatorKeyId || keyIds.length)), 'A keyring can be either a Discovery or have keyIds configured.') + needs( + !(discovery && (generatorKeyId || keyIds.length)), + 'A keyring can be either a Discovery or have keyIds configured.' + ) /* Precondition: All KMS key identifiers must be valid. */ - needs(!generatorKeyId || typeof regionFromKmsKeyArn(generatorKeyId) === 'string', 'Malformed arn.') - needs(keyIds.every(keyArn => typeof regionFromKmsKeyArn(keyArn) === 'string'), 'Malformed arn.') + needs( + !generatorKeyId || + typeof regionFromKmsKeyArn(generatorKeyId) === 'string', + 'Malformed arn.' + ) + needs( + keyIds.every( + (keyArn) => typeof regionFromKmsKeyArn(keyArn) === 'string' + ), + 'Malformed arn.' + ) /* Precondition: clientProvider needs to be a callable function. */ needs(typeof clientProvider === 'function', 'Missing clientProvider') @@ -80,7 +114,7 @@ export function KmsKeyringClass) { + async _onEncrypt(material: EncryptionMaterial) { /* Check for early return (Postcondition): Discovery Keyrings do not encrypt. */ if (this.isDiscovery) return material @@ -95,35 +129,46 @@ export function KmsKeyringClass, encryptedDataKeys: EncryptedDataKey[]) { + async _onDecrypt( + material: DecryptionMaterial, + encryptedDataKeys: EncryptedDataKey[] + ) { const keyIds = this.keyIds.slice() const { clientProvider, generatorKeyId, grantTokens } = this if (generatorKeyId) keyIds.unshift(generatorKeyId) /* If there are no key IDs in the list, keyring is in "discovery" mode and will attempt KMS calls with - * every ARN it comes across in the message. If there are key IDs in the list, it will cross check the - * ARN it reads with that list before attempting KMS calls. Note that if caller provided key IDs in - * anything other than a CMK ARN format, the Encryption SDK will not attempt to decrypt those data keys, because - * the EDK data format always specifies the CMK with the full (non-alias) ARN. - */ - const decryptableEDKs = encryptedDataKeys - .filter(({ providerId, providerInfo }) => { + * every ARN it comes across in the message. If there are key IDs in the list, it will cross check the + * ARN it reads with that list before attempting KMS calls. Note that if caller provided key IDs in + * anything other than a CMK ARN format, the Encryption SDK will not attempt to decrypt those data keys, because + * the EDK data format always specifies the CMK with the full (non-alias) ARN. + */ + const decryptableEDKs = encryptedDataKeys.filter( + ({ providerId, providerInfo }) => { if (providerId !== KMS_PROVIDER_ID) return false /* Discovery keyrings can not have keyIds configured, * and non-discovery keyrings must have keyIds configured. */ return this.isDiscovery || keyIds.includes(providerInfo) - }) + } + ) - let cmkErrors: Error[] = [] + const cmkErrors: Error[] = [] for (const edk of decryptableEDKs) { - let dataKey: RequiredDecryptResponse|false = false + let dataKey: RequiredDecryptResponse | false = false try { dataKey = await decrypt( clientProvider, @@ -183,14 +236,23 @@ export function KmsKeyringClass `${m} Error #${i + 1} \n ${e.stack} \n`, - 'Unable to decrypt data key and one or more KMS CMKs had an error. \n ')) + needs( + material.hasValidKey() || + (!material.hasValidKey() && !cmkErrors.length), + cmkErrors.reduce( + (m, e, i) => `${m} Error #${i + 1} \n ${e.stack} \n`, + 'Unable to decrypt data key and one or more KMS CMKs had an error. \n ' + ) + ) return material } diff --git a/modules/kms-keyring/src/kms_types.ts b/modules/kms-keyring/src/kms_types.ts index 3d9b1ec6d..9a4287779 100644 --- a/modules/kms-keyring/src/kms_types.ts +++ b/modules/kms-keyring/src/kms_types.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EncryptionContext } from '@aws-crypto/material-management' // eslint-disable-line no-unused-vars +import { EncryptionContext } from '@aws-crypto/material-management' export interface DecryptRequest { CiphertextBlob: Uint8Array @@ -16,7 +16,7 @@ export interface DecryptResponse { Plaintext?: Data } -export interface RequiredDecryptResponse extends Required{ +export interface RequiredDecryptResponse extends Required { Plaintext: Uint8Array } @@ -31,7 +31,7 @@ export interface EncryptResponse { KeyId?: string } -export interface RequiredEncryptResponse extends Required{ +export interface RequiredEncryptResponse extends Required { CiphertextBlob: Uint8Array } @@ -48,7 +48,8 @@ export interface GenerateDataKeyResponse { KeyId?: string } -export interface RequiredGenerateDataKeyResponse extends Required{ +export interface RequiredGenerateDataKeyResponse + extends Required { CiphertextBlob: Uint8Array Plaintext: Uint8Array } @@ -58,7 +59,15 @@ export interface AwsSdkV2Response { } export interface AwsEsdkKMSInterface { - decrypt(args: DecryptRequest): Promise|AwsSdkV2Response - encrypt(args: EncryptRequest): Promise|AwsSdkV2Response - generateDataKey(args: GenerateDataKeyRequest): Promise|AwsSdkV2Response + decrypt( + args: DecryptRequest + ): Promise | AwsSdkV2Response + encrypt( + args: EncryptRequest + ): Promise | AwsSdkV2Response + generateDataKey( + args: GenerateDataKeyRequest + ): + | Promise + | AwsSdkV2Response } diff --git a/modules/kms-keyring/src/region_from_kms_key_arn.ts b/modules/kms-keyring/src/region_from_kms_key_arn.ts index 9c0f945f0..ea271a5dd 100644 --- a/modules/kms-keyring/src/region_from_kms_key_arn.ts +++ b/modules/kms-keyring/src/region_from_kms_key_arn.ts @@ -12,7 +12,7 @@ import { needs } from '@aws-crypto/material-management' */ const aliasOrKeyResourceType = /^(alias|key)(\/.*)*$/ -export function regionFromKmsKeyArn (kmsKeyArn: string): string { +export function regionFromKmsKeyArn(kmsKeyArn: string): string { /* Precondition: A KMS key arn must be a string. */ needs(typeof kmsKeyArn === 'string', 'KMS key arn must be a string.') @@ -31,20 +31,16 @@ export function regionFromKmsKeyArn (kmsKeyArn: string): string { * In this case the arnLiteral should look like an alias. */ needs( - (arnLiteral === 'arn' && - partition && - service === 'kms' && - region) || - /* Partition may or may not have a value. - * If the resourceType delimiter is /, - * it will not have a value. - * However if the delimiter is : it will - * because of the split(':') - */ - (!service && - !region && - arnLiteral.match(aliasOrKeyResourceType)), - 'Malformed arn.') + (arnLiteral === 'arn' && partition && service === 'kms' && region) || + /* Partition may or may not have a value. + * If the resourceType delimiter is /, + * it will not have a value. + * However if the delimiter is : it will + * because of the split(':') + */ + (!service && !region && arnLiteral.match(aliasOrKeyResourceType)), + 'Malformed arn.' + ) return region } diff --git a/modules/material-management-browser/package.json b/modules/material-management-browser/package.json index e8de24f1b..5309e918f 100644 --- a/modules/material-management-browser/package.json +++ b/modules/material-management-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts b/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts index 5540a1f05..309fbc99d 100644 --- a/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts +++ b/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts @@ -2,37 +2,66 @@ // SPDX-License-Identifier: Apache-2.0 import { - WebCryptoMaterialsManager, EncryptionRequest, // eslint-disable-line no-unused-vars - DecryptionRequest, EncryptionContext, // eslint-disable-line no-unused-vars - WebCryptoAlgorithmSuite, WebCryptoEncryptionMaterial, - WebCryptoDecryptionMaterial, SignatureKey, needs, readOnlyProperty, - VerificationKey, AlgorithmSuiteIdentifier, immutableBaseClass, - KeyringWebCrypto, GetEncryptionMaterials, GetDecryptMaterials // eslint-disable-line no-unused-vars + WebCryptoMaterialsManager, + EncryptionRequest, + DecryptionRequest, + EncryptionContext, + WebCryptoAlgorithmSuite, + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, + SignatureKey, + needs, + readOnlyProperty, + VerificationKey, + AlgorithmSuiteIdentifier, + immutableBaseClass, + KeyringWebCrypto, + GetEncryptionMaterials, + GetDecryptMaterials, } from '@aws-crypto/material-management' import { ENCODED_SIGNER_KEY } from '@aws-crypto/serialize' -import { getWebCryptoBackend, getNonZeroByteBackend } from '@aws-crypto/web-crypto-backend' +import { + getWebCryptoBackend, + getNonZeroByteBackend, +} from '@aws-crypto/web-crypto-backend' import { fromBase64, toBase64 } from '@aws-sdk/util-base64-browser' -export type WebCryptoEncryptionRequest = EncryptionRequest -export type WebCryptoDecryptionRequest = DecryptionRequest -export type WebCryptoGetEncryptionMaterials = GetEncryptionMaterials -export type WebCryptoGetDecryptMaterials = GetDecryptMaterials +export type WebCryptoEncryptionRequest = EncryptionRequest< + WebCryptoAlgorithmSuite +> +export type WebCryptoDecryptionRequest = DecryptionRequest< + WebCryptoAlgorithmSuite +> +export type WebCryptoGetEncryptionMaterials = GetEncryptionMaterials< + WebCryptoAlgorithmSuite +> +export type WebCryptoGetDecryptMaterials = GetDecryptMaterials< + WebCryptoAlgorithmSuite +> /** * The DefaultCryptographicMaterialsManager is a specific implementation of the CryptographicMaterialsManager. * New cryptography materials managers SHOULD extend from WebCryptoMaterialsManager. * Users should never need to create an instance of a DefaultCryptographicMaterialsManager. */ -export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoMaterialsManager { +export class WebCryptoDefaultCryptographicMaterialsManager + implements WebCryptoMaterialsManager { readonly keyring!: KeyringWebCrypto - constructor (keyring: KeyringWebCrypto) { + constructor(keyring: KeyringWebCrypto) { /* Precondition: keyrings must be a KeyringWebCrypto. */ needs(keyring instanceof KeyringWebCrypto, 'Unsupported type.') readOnlyProperty(this, 'keyring', keyring) } - async getEncryptionMaterials ({ suite, encryptionContext }: WebCryptoEncryptionRequest): Promise { - suite = suite || new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) + async getEncryptionMaterials({ + suite, + encryptionContext, + }: WebCryptoEncryptionRequest): Promise { + suite = + suite || + new WebCryptoAlgorithmSuite( + AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 + ) /* Precondition: WebCryptoDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize. * A CryptographicMaterialsManager can change entries to the encryptionContext @@ -40,11 +69,17 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM * The DefaultCryptographicMaterialsManager uses the value in the encryption context to store public signing key. * If the caller is using this value in their encryption context the Default CMM is probably not the CMM they want to use. */ - needs(!encryptionContext.hasOwnProperty(ENCODED_SIGNER_KEY), `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.`) + needs( + !Object.prototype.hasOwnProperty.call( + encryptionContext, + ENCODED_SIGNER_KEY + ), + `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.` + ) - const material = await this - .keyring - .onEncrypt(await this._initializeEncryptionMaterial(suite, encryptionContext)) + const material = await this.keyring.onEncrypt( + await this._initializeEncryptionMaterial(suite, encryptionContext) + ) /* Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey. * This verifies that the data key matches the algorithm suite specification @@ -54,15 +89,23 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM needs(material.hasValidKey(), 'Unencrypted data key is invalid.') /* Postcondition: The WebCryptoEncryptionMaterial must contain at least 1 EncryptedDataKey. */ - needs(material.encryptedDataKeys.length, 'No EncryptedDataKeys: the ciphertext can never be decrypted.') + needs( + material.encryptedDataKeys.length, + 'No EncryptedDataKeys: the ciphertext can never be decrypted.' + ) return material } - async decryptMaterials ({ suite, encryptedDataKeys, encryptionContext }: WebCryptoDecryptionRequest): Promise { - const material = await this - .keyring - .onDecrypt(await this._initializeDecryptionMaterial(suite, encryptionContext), encryptedDataKeys.slice()) + async decryptMaterials({ + suite, + encryptedDataKeys, + encryptionContext, + }: WebCryptoDecryptionRequest): Promise { + const material = await this.keyring.onDecrypt( + await this._initializeDecryptionMaterial(suite, encryptionContext), + encryptedDataKeys.slice() + ) /* Postcondition: The WebCryptoDecryptionMaterial must contain a valid dataKey. * See: cryptographic_materials.ts, `getUnencryptedDataKey` also verifies @@ -75,11 +118,15 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM return material } - async _initializeEncryptionMaterial (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + async _initializeEncryptionMaterial( + suite: WebCryptoAlgorithmSuite, + encryptionContext: EncryptionContext + ) { const { signatureCurve: namedCurve } = suite /* Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to generate a signing key. */ - if (!namedCurve) return new WebCryptoEncryptionMaterial(suite, encryptionContext) + if (!namedCurve) + return new WebCryptoEncryptionMaterial(suite, encryptionContext) const backend = await getWebCryptoBackend() const subtle = getNonZeroByteBackend(backend) @@ -89,25 +136,38 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM const usages = ['sign'] const format = 'raw' - const { publicKey, privateKey } = await subtle.generateKey(webCryptoAlgorithm, extractable, usages) + const { publicKey, privateKey } = await subtle.generateKey( + webCryptoAlgorithm, + extractable, + usages + ) const publicKeyBytes = await subtle.exportKey(format, publicKey) - const compressPoint = SignatureKey.encodeCompressPoint(new Uint8Array(publicKeyBytes), suite) - const signatureKey = new SignatureKey(privateKey, compressPoint, suite) - return new WebCryptoEncryptionMaterial( - suite, - { ...encryptionContext, [ENCODED_SIGNER_KEY]: toBase64(compressPoint) } + const compressPoint = SignatureKey.encodeCompressPoint( + new Uint8Array(publicKeyBytes), + suite ) - .setSignatureKey(signatureKey) + const signatureKey = new SignatureKey(privateKey, compressPoint, suite) + return new WebCryptoEncryptionMaterial(suite, { + ...encryptionContext, + [ENCODED_SIGNER_KEY]: toBase64(compressPoint), + }).setSignatureKey(signatureKey) } - async _initializeDecryptionMaterial (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + async _initializeDecryptionMaterial( + suite: WebCryptoAlgorithmSuite, + encryptionContext: EncryptionContext + ) { const { signatureCurve: namedCurve } = suite /* Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to extract a verification key. */ - if (!namedCurve) return new WebCryptoDecryptionMaterial(suite, encryptionContext) + if (!namedCurve) + return new WebCryptoDecryptionMaterial(suite, encryptionContext) /* Precondition: WebCryptoDefaultCryptographicMaterialsManager If the algorithm suite specification requires a signatureCurve a context must exist. */ - if (!encryptionContext) throw new Error('Encryption context does not contain required public key.') + if (!encryptionContext) + throw new Error( + 'Encryption context does not contain required public key.' + ) const { [ENCODED_SIGNER_KEY]: compressPoint } = encryptionContext @@ -121,11 +181,22 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM const usages = ['verify'] const format = 'raw' - const publicKeyBytes = VerificationKey.decodeCompressPoint(fromBase64(compressPoint), suite) - const publicKey = await subtle.importKey(format, publicKeyBytes, webCryptoAlgorithm, extractable, usages) + const publicKeyBytes = VerificationKey.decodeCompressPoint( + fromBase64(compressPoint), + suite + ) + const publicKey = await subtle.importKey( + format, + publicKeyBytes, + webCryptoAlgorithm, + extractable, + usages + ) - return new WebCryptoDecryptionMaterial(suite, encryptionContext) - .setVerificationKey(new VerificationKey(publicKey, suite)) + return new WebCryptoDecryptionMaterial( + suite, + encryptionContext + ).setVerificationKey(new VerificationKey(publicKey, suite)) } } diff --git a/modules/material-management-browser/src/bytes2_jwk.ts b/modules/material-management-browser/src/bytes2_jwk.ts index adadf93ff..d29d94c88 100644 --- a/modules/material-management-browser/src/bytes2_jwk.ts +++ b/modules/material-management-browser/src/bytes2_jwk.ts @@ -3,12 +3,15 @@ import { toBase64 } from '@aws-sdk/util-base64-browser' -export function bytes2JWK (rawKeyBytes: Uint8Array): JsonWebKey { +export function bytes2JWK(rawKeyBytes: Uint8Array): JsonWebKey { // See https://tools.ietf.org/html/rfc7515#appendix-C Base64url Encoding const base64 = toBase64(rawKeyBytes) - const base64Url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') + const base64Url = base64 + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, '') return { kty: 'oct', - k: base64Url + k: base64Url, } } diff --git a/modules/material-management-browser/src/index.ts b/modules/material-management-browser/src/index.ts index ec7f6f759..664b50ec8 100644 --- a/modules/material-management-browser/src/index.ts +++ b/modules/material-management-browser/src/index.ts @@ -6,9 +6,26 @@ export * from './material_helpers' export * from './bytes2_jwk' export * from './keyring_helpers' export { - WebCryptoDecryptionMaterial, WebCryptoEncryptionMaterial, WebCryptoAlgorithmSuite, - AlgorithmSuiteIdentifier, EncryptionContext, EncryptedDataKey, KeyringWebCrypto, - KeyringTrace, KeyringTraceFlag, needs, MixedBackendCryptoKey, MultiKeyringWebCrypto, - immutableBaseClass, immutableClass, frozenClass, readOnlyProperty, keyUsageForMaterial, - isValidCryptoKey, isCryptoKey, WebCryptoMaterialsManager, unwrapDataKey, AwsEsdkJsCryptoKey + WebCryptoDecryptionMaterial, + WebCryptoEncryptionMaterial, + WebCryptoAlgorithmSuite, + AlgorithmSuiteIdentifier, + EncryptionContext, + EncryptedDataKey, + KeyringWebCrypto, + KeyringTrace, + KeyringTraceFlag, + needs, + MixedBackendCryptoKey, + MultiKeyringWebCrypto, + immutableBaseClass, + immutableClass, + frozenClass, + readOnlyProperty, + keyUsageForMaterial, + isValidCryptoKey, + isCryptoKey, + WebCryptoMaterialsManager, + unwrapDataKey, + AwsEsdkJsCryptoKey, } from '@aws-crypto/material-management' diff --git a/modules/material-management-browser/src/keyring_helpers.ts b/modules/material-management-browser/src/keyring_helpers.ts index 81b1aed58..ba1a96436 100644 --- a/modules/material-management-browser/src/keyring_helpers.ts +++ b/modules/material-management-browser/src/keyring_helpers.ts @@ -2,42 +2,44 @@ // SPDX-License-Identifier: Apache-2.0 import { - WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoMaterial // eslint-disable-line no-unused-vars + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, + WebCryptoMaterial, } from '@aws-crypto/material-management' -import { - importCryptoKey -} from './material_helpers' +import { importCryptoKey } from './material_helpers' -import { - getWebCryptoBackend -} from '@aws-crypto/web-crypto-backend' +import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend' -export async function importForWebCryptoEncryptionMaterial (material: WebCryptoEncryptionMaterial) { +export async function importForWebCryptoEncryptionMaterial( + material: WebCryptoEncryptionMaterial +) { /* Check for early return (Postcondition): If a cryptoKey has already been imported for encrypt, return. */ if (material.hasUnencryptedDataKey && material.hasCryptoKey) return material return importCryptoKeyToMaterial(material) } -export async function importForWebCryptoDecryptionMaterial (material: WebCryptoDecryptionMaterial) { +export async function importForWebCryptoDecryptionMaterial( + material: WebCryptoDecryptionMaterial +) { /* Check for early return (Postcondition): If a cryptoKey has already been imported for decrypt, return. */ if (material.hasValidKey()) return material /* Check for early return (Postcondition): If no key was able to be decrypted, return. */ if (!material.hasUnencryptedDataKey) return material - return (await importCryptoKeyToMaterial(material)) - /* Now that a cryptoKey has been imported, the unencrypted data key can be zeroed. - * this is safe, because one and only one EncryptedDataKey should be used to - * set the unencrypted data key on the material, - * and in the browser, all crypto operations are done with a CryptoKey - */ - .zeroUnencryptedDataKey() + return ( + (await importCryptoKeyToMaterial(material)) + /* Now that a cryptoKey has been imported, the unencrypted data key can be zeroed. + * this is safe, because one and only one EncryptedDataKey should be used to + * set the unencrypted data key on the material, + * and in the browser, all crypto operations are done with a CryptoKey + */ + .zeroUnencryptedDataKey() + ) } -export async function importCryptoKeyToMaterial> ( +export async function importCryptoKeyToMaterial>( material: T ) { const backend = await getWebCryptoBackend() diff --git a/modules/material-management-browser/src/material_helpers.ts b/modules/material-management-browser/src/material_helpers.ts index 80ef43f5e..93ec85e17 100644 --- a/modules/material-management-browser/src/material_helpers.ts +++ b/modules/material-management-browser/src/material_helpers.ts @@ -3,16 +3,16 @@ import { needs, - WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, + MixedBackendCryptoKey, isCryptoKey, isValidCryptoKey, keyUsageForMaterial, subtleFunctionForMaterial, unwrapDataKey, - AwsEsdkJsCryptoKey, // eslint-disable-line no-unused-vars - WebCryptoMaterial // eslint-disable-line no-unused-vars + AwsEsdkJsCryptoKey, + WebCryptoMaterial, } from '@aws-crypto/material-management' import { @@ -20,21 +20,23 @@ import { getNonZeroByteBackend, getZeroByteSubtle, isFullSupportWebCryptoBackend, - WebCryptoBackend // eslint-disable-line no-unused-vars + WebCryptoBackend, } from '@aws-crypto/web-crypto-backend' import { bytes2JWK } from './bytes2_jwk' export interface GetSubtleEncrypt { - (iv: Uint8Array, additionalData: Uint8Array) : (data: Uint8Array) => Promise + (iv: Uint8Array, additionalData: Uint8Array): ( + data: Uint8Array + ) => Promise } export interface KdfGetSubtleEncrypt { - (info: Uint8Array) : GetSubtleEncrypt + (info: Uint8Array): GetSubtleEncrypt } export interface SubtleSign { - (data: Uint8Array) : PromiseLike + (data: Uint8Array): PromiseLike } export interface WebCryptoEncryptionMaterialHelper { @@ -44,25 +46,34 @@ export interface WebCryptoEncryptionMaterialHelper { } export interface GetEncryptHelper { - (material: WebCryptoEncryptionMaterial) : Promise + (material: WebCryptoEncryptionMaterial): Promise< + WebCryptoEncryptionMaterialHelper + > } -export const getEncryptHelper: GetEncryptHelper = async (material: WebCryptoEncryptionMaterial) => { +export const getEncryptHelper: GetEncryptHelper = async ( + material: WebCryptoEncryptionMaterial +) => { const backend = await getWebCryptoBackend() /* Precondition: WebCryptoEncryptionMaterial must have a valid data key. */ needs(material.hasValidKey(), 'Material has no CryptoKey.') const { signatureHash } = material.suite - const kdfGetSubtleEncrypt = getSubtleFunction(material, backend, 'encrypt') + const kdfGetSubtleEncrypt = getSubtleFunction( + material, + backend, + 'encrypt' + ) as KdfGetSubtleEncrypt return Object.freeze({ kdfGetSubtleEncrypt, subtleSign: signatureHash ? getSubtleSign : undefined, - dispose + dispose, }) - function getSubtleSign (data: Uint8Array) { - if (!signatureHash) throw new Error('Algorithm suite does not support signing.') + function getSubtleSign(data: Uint8Array) { + if (!signatureHash) + throw new Error('Algorithm suite does not support signing.') const { signatureKey } = material if (!signatureKey) throw new Error('Malformed Material.') const { privateKey } = signatureKey @@ -71,7 +82,7 @@ export const getEncryptHelper: GetEncryptHelper = async (material: WebCryptoEncr return getNonZeroByteBackend(backend).sign(algorithm, privateKey, data) } - function dispose () { + function dispose() { material.zeroUnencryptedDataKey() } } @@ -79,11 +90,11 @@ export const getEncryptHelper: GetEncryptHelper = async (material: WebCryptoEncr export interface GetSubtleDecrypt extends GetSubtleEncrypt {} export interface KdfGetSubtleDecrypt { - (info: Uint8Array) : GetSubtleDecrypt + (info: Uint8Array): GetSubtleDecrypt } interface SubtleVerify { - (signature: Uint8Array, data: Uint8Array) : PromiseLike + (signature: Uint8Array, data: Uint8Array): PromiseLike } export interface WebCryptoDecryptionMaterialHelper { @@ -93,10 +104,14 @@ export interface WebCryptoDecryptionMaterialHelper { } export interface GetDecryptionHelper { - (material: WebCryptoDecryptionMaterial) : Promise + (material: WebCryptoDecryptionMaterial): Promise< + WebCryptoDecryptionMaterialHelper + > } -export const getDecryptionHelper: GetDecryptionHelper = async (material: WebCryptoDecryptionMaterial) => { +export const getDecryptionHelper: GetDecryptionHelper = async ( + material: WebCryptoDecryptionMaterial +) => { const backend = await getWebCryptoBackend() /* Precondition: WebCryptoDecryptionMaterial must have a valid data key. */ @@ -104,62 +119,104 @@ export const getDecryptionHelper: GetDecryptionHelper = async (material: WebCryp const { signatureHash } = material.suite - const kdfGetSubtleDecrypt = getSubtleFunction(material, backend, 'decrypt') + const kdfGetSubtleDecrypt = getSubtleFunction( + material, + backend, + 'decrypt' + ) as KdfGetSubtleDecrypt return Object.freeze({ kdfGetSubtleDecrypt, subtleVerify: signatureHash ? subtleVerify : undefined, - dispose + dispose, }) - function subtleVerify (signature: Uint8Array, data: Uint8Array) { - if (!signatureHash) throw new Error('Algorithm suite does not support signing.') + function subtleVerify(signature: Uint8Array, data: Uint8Array) { + if (!signatureHash) + throw new Error('Algorithm suite does not support signing.') const { verificationKey } = material if (!verificationKey) throw new Error('Malformed Material.') const { publicKey } = verificationKey if (!isCryptoKey(publicKey)) throw new Error('Malformed Material.') const algorithm = { name: 'ECDSA', hash: { name: signatureHash } } - return getNonZeroByteBackend(backend).verify(algorithm, publicKey, signature, data) + return getNonZeroByteBackend(backend).verify( + algorithm, + publicKey, + signature, + data + ) } - function dispose () { + function dispose() { material.zeroUnencryptedDataKey() } } -type SubtleFunction = 'encrypt'|'decrypt' +type SubtleFunction = 'encrypt' | 'decrypt' -export function getSubtleFunction> ( +export function getSubtleFunction>( material: T, backend: WebCryptoBackend, subtleFunction: SubtleFunction = subtleFunctionForMaterial(material) -): KdfGetSubtleEncrypt|KdfGetSubtleDecrypt { +): KdfGetSubtleEncrypt | KdfGetSubtleDecrypt { /* Precondition: The material must have a CryptoKey. */ needs(material.hasCryptoKey, 'Material must have a CryptoKey.') const cryptoKey = material.getCryptoKey() /* Precondition: The cryptoKey and backend must match in terms of Mixed vs Full support. */ - needs(isCryptoKey(cryptoKey) === isFullSupportWebCryptoBackend(backend), 'CryptoKey vs WebCrypto backend mismatch.') + needs( + isCryptoKey(cryptoKey) === isFullSupportWebCryptoBackend(backend), + 'CryptoKey vs WebCrypto backend mismatch.' + ) const { suite } = material const { encryption: cipherName, ivLength, tagLength } = suite return (info: Uint8Array) => { - const derivedKeyPromise: Promise = isCryptoKey(cryptoKey) - ? WebCryptoKdf(getNonZeroByteBackend(backend), material, cryptoKey, [subtleFunction], info) + const derivedKeyPromise: Promise< + AwsEsdkJsCryptoKey | MixedBackendCryptoKey + > = isCryptoKey(cryptoKey) + ? WebCryptoKdf( + getNonZeroByteBackend(backend), + material, + cryptoKey, + [subtleFunction], + info + ) : Promise.all([ - WebCryptoKdf(getNonZeroByteBackend(backend), material, cryptoKey.nonZeroByteCryptoKey, [subtleFunction], info), - WebCryptoKdf(getZeroByteSubtle(backend), material, cryptoKey.zeroByteCryptoKey, [subtleFunction], info) - ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ nonZeroByteCryptoKey, zeroByteCryptoKey })) + WebCryptoKdf( + getNonZeroByteBackend(backend), + material, + cryptoKey.nonZeroByteCryptoKey, + [subtleFunction], + info + ), + WebCryptoKdf( + getZeroByteSubtle(backend), + material, + cryptoKey.zeroByteCryptoKey, + [subtleFunction], + info + ), + ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ + nonZeroByteCryptoKey, + zeroByteCryptoKey, + })) return (iv: Uint8Array, additionalData: Uint8Array) => { /* Precondition: The length of the IV must match the WebCryptoAlgorithmSuite specification. */ - needs(iv.byteLength === ivLength, 'Iv length does not match algorithm suite specification') + needs( + iv.byteLength === ivLength, + 'Iv length does not match algorithm suite specification' + ) return async (data: Uint8Array) => { const deriveKey = await derivedKeyPromise if (isCryptoKey(deriveKey) && isFullSupportWebCryptoBackend(backend)) { const { subtle } = backend const algorithm = { name: cipherName, iv, additionalData, tagLength } return subtle[subtleFunction](algorithm, deriveKey, data) - } else if (!isCryptoKey(deriveKey) && !isFullSupportWebCryptoBackend(backend)) { + } else if ( + !isCryptoKey(deriveKey) && + !isFullSupportWebCryptoBackend(backend) + ) { const { nonZeroByteSubtle, zeroByteSubtle } = backend const { nonZeroByteCryptoKey, zeroByteCryptoKey } = deriveKey const algorithm = { name: cipherName, iv, additionalData, tagLength } @@ -167,12 +224,23 @@ export function getSubtleFunction> ( * This means that on decrypt any amount of data less than tagLength is invalid. * This also means that zero encrypted data will be equal to tagLength. */ - const dataByteLength = subtleFunction === 'decrypt' ? data.byteLength - tagLength / 8 : data.byteLength + const dataByteLength = + subtleFunction === 'decrypt' + ? data.byteLength - tagLength / 8 + : data.byteLength needs(dataByteLength >= 0, 'Invalid data length.') if (dataByteLength === 0) { - return zeroByteSubtle[subtleFunction](algorithm, zeroByteCryptoKey, data) + return zeroByteSubtle[subtleFunction]( + algorithm, + zeroByteCryptoKey, + data + ) } else { - return nonZeroByteSubtle[subtleFunction](algorithm, nonZeroByteCryptoKey, data) + return nonZeroByteSubtle[subtleFunction]( + algorithm, + nonZeroByteCryptoKey, + data + ) } } // This should be impossible @@ -182,7 +250,7 @@ export function getSubtleFunction> ( } } -export async function WebCryptoKdf> ( +export async function WebCryptoKdf>( subtle: SubtleCrypto, material: T, cryptoKey: AwsEsdkJsCryptoKey, @@ -196,31 +264,32 @@ export async function WebCryptoKdf> ( /* Precondition: Valid HKDF values must exist for browsers. */ needs( - kdf === 'HKDF' && - kdfHash && - info instanceof Uint8Array && - info.byteLength, + kdf === 'HKDF' && kdfHash && info instanceof Uint8Array && info.byteLength, 'Invalid HKDF values.' ) // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams - const kdfAlgorithm = { name: kdf, hash: { name: kdfHash }, info, salt: new Uint8Array() } + const kdfAlgorithm = { + name: kdf, + hash: { name: kdfHash }, + info, + salt: new Uint8Array(), + } const derivedKeyAlgorithm = { name: encryption, length: keyLength } const extractable = false - const deriveKey = await subtle - .deriveKey( - // @ts-ignore types need to be updated see: https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams - kdfAlgorithm, - cryptoKey, - derivedKeyAlgorithm, - extractable, - keyUsages - ) + const deriveKey = await subtle.deriveKey( + // @ts-ignore types need to be updated see: https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams + kdfAlgorithm, + cryptoKey, + derivedKeyAlgorithm, + extractable, + keyUsages + ) /* Postcondition: The derived key must conform to the algorith suite specification. */ needs(isValidCryptoKey(deriveKey, material), 'Invalid derived key') return deriveKey } -export async function importCryptoKey> ( +export async function importCryptoKey>( backend: WebCryptoBackend, material: T, keyUsages: KeyUsage[] = [keyUsageForMaterial(material)] @@ -230,12 +299,15 @@ export async function importCryptoKey> ( } else { return Promise.all([ _importCryptoKey(getNonZeroByteBackend(backend), material, keyUsages), - _importCryptoKey(getZeroByteSubtle(backend), material, keyUsages) - ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ nonZeroByteCryptoKey, zeroByteCryptoKey })) + _importCryptoKey(getZeroByteSubtle(backend), material, keyUsages), + ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ + nonZeroByteCryptoKey, + zeroByteCryptoKey, + })) } } -export async function _importCryptoKey> ( +export async function _importCryptoKey>( subtle: SubtleCrypto, material: T, keyUsages: KeyUsage[] = [keyUsageForMaterial(material)] diff --git a/modules/material-management-node/package.json b/modules/material-management-node/package.json index bfa25c5b8..6e83eac4e 100644 --- a/modules/material-management-node/package.json +++ b/modules/material-management-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management-node/src/index.ts b/modules/material-management-node/src/index.ts index 4bd001c25..469113614 100644 --- a/modules/material-management-node/src/index.ts +++ b/modules/material-management-node/src/index.ts @@ -10,12 +10,25 @@ export { GetCipher, GetDecipher, AwsEsdkJsCipherGCM, - AwsEsdkJsDecipherGCM + AwsEsdkJsDecipherGCM, } from './material_helpers' export { - NodeDecryptionMaterial, NodeEncryptionMaterial, NodeAlgorithmSuite, - AlgorithmSuiteIdentifier, EncryptionContext, EncryptedDataKey, - KeyringTrace, KeyringTraceFlag, needs, KeyringNode, MultiKeyringNode, - immutableBaseClass, immutableClass, frozenClass, readOnlyProperty, - NodeMaterialsManager, unwrapDataKey, AwsEsdkKeyObject + NodeDecryptionMaterial, + NodeEncryptionMaterial, + NodeAlgorithmSuite, + AlgorithmSuiteIdentifier, + EncryptionContext, + EncryptedDataKey, + KeyringTrace, + KeyringTraceFlag, + needs, + KeyringNode, + MultiKeyringNode, + immutableBaseClass, + immutableClass, + frozenClass, + readOnlyProperty, + NodeMaterialsManager, + unwrapDataKey, + AwsEsdkKeyObject, } from '@aws-crypto/material-management' diff --git a/modules/material-management-node/src/material_helpers.ts b/modules/material-management-node/src/material_helpers.ts index 4ceeea68a..44738c8cc 100644 --- a/modules/material-management-node/src/material_helpers.ts +++ b/modules/material-management-node/src/material_helpers.ts @@ -2,15 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { - needs, NodeEncryptionMaterial, NodeDecryptionMaterial, + needs, + NodeEncryptionMaterial, + NodeDecryptionMaterial, unwrapDataKey, wrapWithKeyObjectIfSupported, - AwsEsdkKeyObject, // eslint-disable-line no-unused-vars - NodeHash // eslint-disable-line no-unused-vars + AwsEsdkKeyObject, + NodeHash, } from '@aws-crypto/material-management' import { - Signer, Verify, // eslint-disable-line no-unused-vars - createCipheriv, createDecipheriv, createSign, createVerify + Signer, + Verify, + createCipheriv, + createDecipheriv, + createSign, + createVerify, } from 'crypto' import { HKDF } from '@aws-crypto/hkdf-node' @@ -28,10 +34,10 @@ export interface AwsEsdkJsDecipherGCM { setAAD(aad: Buffer): this } -type KDFIndex = Readonly<{[K in NodeHash]: ReturnType}> +type KDFIndex = Readonly<{ [K in NodeHash]: ReturnType }> const kdfIndex: KDFIndex = Object.freeze({ sha256: HKDF('sha256' as NodeHash), - sha384: HKDF('sha384' as NodeHash) + sha384: HKDF('sha384' as NodeHash), }) export interface GetCipher { @@ -43,7 +49,7 @@ export interface CurryGetCipher { } export interface GetSigner { - () : Signer & {awsCryptoSign: () => Buffer} + (): Signer & { awsCryptoSign: () => Buffer } } export interface NodeEncryptionMaterialHelper { @@ -53,10 +59,12 @@ export interface NodeEncryptionMaterialHelper { } export interface GetEncryptHelper { - (material: NodeEncryptionMaterial) : NodeEncryptionMaterialHelper + (material: NodeEncryptionMaterial): NodeEncryptionMaterialHelper } -export const getEncryptHelper: GetEncryptHelper = (material: NodeEncryptionMaterial) => { +export const getEncryptHelper: GetEncryptHelper = ( + material: NodeEncryptionMaterial +) => { /* Precondition: NodeEncryptionMaterial must have a valid data key. */ needs(material.hasValidKey(), 'Material has no unencrypted data key.') @@ -65,14 +73,14 @@ export const getEncryptHelper: GetEncryptHelper = (material: NodeEncryptionMater * Function overloads "works" but then I can not export * the function and have eslint be happy (Multiple exports of name) */ - const kdfGetCipher = getCryptoStream(material) + const kdfGetCipher = getCryptoStream(material) as CurryGetCipher return Object.freeze({ kdfGetCipher, getSigner: signatureHash ? getSigner : undefined, - dispose + dispose, }) - function getSigner () { + function getSigner() { /* Precondition: The NodeEncryptionMaterial must have not been zeroed. * hasUnencryptedDataKey will check that the unencrypted data key has been set * *and* that it has not been zeroed. At this point it must have been set @@ -80,23 +88,28 @@ export const getEncryptHelper: GetEncryptHelper = (material: NodeEncryptionMater * we are protecting that someone has zeroed out the material * because the Encrypt process has been complete. */ - needs(material.hasUnencryptedDataKey, 'Unencrypted data key has been zeroed.') + needs( + material.hasUnencryptedDataKey, + 'Unencrypted data key has been zeroed.' + ) if (!signatureHash) throw new Error('Material does not support signature.') const { signatureKey } = material if (!signatureKey) throw new Error('Material does not support signature.') const { privateKey } = signatureKey - if (typeof privateKey !== 'string') throw new Error('Material does not support signature.') + if (typeof privateKey !== 'string') + throw new Error('Material does not support signature.') const signer = Object.assign( createSign(signatureHash), // don't export the private key if we don't have to - { awsCryptoSign: () => signer.sign(privateKey) }) + { awsCryptoSign: () => signer.sign(privateKey) } + ) return signer } - function dispose () { + function dispose() { material.zeroUnencryptedDataKey() } } @@ -105,10 +118,10 @@ export interface GetDecipher { (iv: Uint8Array): AwsEsdkJsDecipherGCM } export interface CurryGetDecipher { - (info?: Uint8Array) : GetDecipher + (info?: Uint8Array): GetDecipher } export interface GetVerify { - () : Verify & {awsCryptoVerify: (signature: Buffer) => boolean} + (): Verify & { awsCryptoVerify: (signature: Buffer) => boolean } } export interface NodeDecryptionMaterialHelper { @@ -118,10 +131,12 @@ export interface NodeDecryptionMaterialHelper { } export interface GetDecryptionHelper { - (material: NodeDecryptionMaterial) : NodeDecryptionMaterialHelper + (material: NodeDecryptionMaterial): NodeDecryptionMaterialHelper } -export const getDecryptionHelper: GetDecryptionHelper = (material: NodeDecryptionMaterial) => { +export const getDecryptionHelper: GetDecryptionHelper = ( + material: NodeDecryptionMaterial +) => { /* Precondition: NodeDecryptionMaterial must have a valid data key. */ needs(material.hasValidKey(), 'Material has no unencrypted data key.') @@ -131,56 +146,71 @@ export const getDecryptionHelper: GetDecryptionHelper = (material: NodeDecryptio * Function overloads "works" but then I can not export * the function and have eslint be happy (Multiple exports of name) */ - const kdfGetDecipher = getCryptoStream(material) + const kdfGetDecipher = getCryptoStream(material) as CurryGetDecipher return Object.freeze({ kdfGetDecipher, getVerify: signatureHash ? getVerify : undefined, - dispose + dispose, }) - function getVerify () { + function getVerify() { if (!signatureHash) throw new Error('Material does not support signature.') const { verificationKey } = material - if (!verificationKey) throw new Error('Material does not support signature.') + if (!verificationKey) + throw new Error('Material does not support signature.') const verify = Object.assign( createVerify(signatureHash), // explicitly bind the public key for this material - { awsCryptoVerify: (signature: Buffer) => verify.verify(verificationKey.publicKey, signature) }) + { + awsCryptoVerify: (signature: Buffer) => + verify.verify(verificationKey.publicKey, signature), + } + ) return verify } - function dispose () { + function dispose() { material.zeroUnencryptedDataKey() } } -export function getCryptoStream (material: NodeEncryptionMaterial|NodeDecryptionMaterial) { +export function getCryptoStream( + material: NodeEncryptionMaterial | NodeDecryptionMaterial +) { const { encryption: cipherName, ivLength } = material.suite - const createCryptoStream = material instanceof NodeEncryptionMaterial - ? createCipheriv - : material instanceof NodeDecryptionMaterial + const createCryptoStream = + material instanceof NodeEncryptionMaterial + ? createCipheriv + : material instanceof NodeDecryptionMaterial ? createDecipheriv : false /* Precondition: material must be either NodeEncryptionMaterial or NodeDecryptionMaterial. */ - if (!createCryptoStream) throw new Error('Unsupported cryptographic material.') + if (!createCryptoStream) + throw new Error('Unsupported cryptographic material.') return (info?: Uint8Array) => { const derivedKey = nodeKdf(material, info) - return (iv: Uint8Array): AwsEsdkJsCipherGCM|AwsEsdkJsDecipherGCM => { + return (iv: Uint8Array): AwsEsdkJsCipherGCM | AwsEsdkJsDecipherGCM => { /* Precondition: The length of the IV must match the NodeAlgorithmSuite specification. */ - needs(iv.byteLength === ivLength, 'Iv length does not match algorithm suite specification') + needs( + iv.byteLength === ivLength, + 'Iv length does not match algorithm suite specification' + ) /* Precondition: The material must have not been zeroed. - * hasUnencryptedDataKey will check that the unencrypted data key has been set - * *and* that it has not been zeroed. At this point it must have been set - * because the KDF function operated on it. So at this point - * we are protecting that someone has zeroed out the material - * because the Encrypt process has been complete. - */ - needs(material.hasUnencryptedDataKey, 'Unencrypted data key has been zeroed.') + * hasUnencryptedDataKey will check that the unencrypted data key has been set + * *and* that it has not been zeroed. At this point it must have been set + * because the KDF function operated on it. So at this point + * we are protecting that someone has zeroed out the material + * because the Encrypt process has been complete. + */ + needs( + material.hasUnencryptedDataKey, + 'Unencrypted data key has been zeroed.' + ) // createDecipheriv is incorrectly typed in @types/node. It should take key: CipherKey, not key: BinaryLike return createCryptoStream(cipherName, derivedKey as any, iv) @@ -188,7 +218,10 @@ export function getCryptoStream (material: NodeEncryptionMaterial|NodeDecryption } } -export function nodeKdf (material: NodeEncryptionMaterial|NodeDecryptionMaterial, info?: Uint8Array): Uint8Array|AwsEsdkKeyObject { +export function nodeKdf( + material: NodeEncryptionMaterial | NodeDecryptionMaterial, + info?: Uint8Array +): Uint8Array | AwsEsdkKeyObject { const dataKey = material.getUnencryptedDataKey() const { kdf, kdfHash, keyLengthBytes } = material.suite @@ -199,22 +232,29 @@ export function nodeKdf (material: NodeEncryptionMaterial|NodeDecryptionMaterial /* Precondition: Valid HKDF values must exist for Node.js. */ needs( kdf === 'HKDF' && - kdfHash && - kdfIndex[kdfHash] && - info instanceof Uint8Array, + kdfHash && + kdfIndex[kdfHash] && + info instanceof Uint8Array, 'Invalid HKDF values.' ) /* The unwrap is done once we *know* that a KDF is required. * If we unwrapped before everything will work, * but we may be creating new copies of the unencrypted data key (export). */ - const { buffer: dkBuffer, byteOffset: dkByteOffset, byteLength: dkByteLength } = unwrapDataKey(dataKey) + const { + buffer: dkBuffer, + byteOffset: dkByteOffset, + byteLength: dkByteLength, + } = unwrapDataKey(dataKey) // info and kdfHash are now defined const toExtract = Buffer.from(dkBuffer, dkByteOffset, dkByteLength) - const { buffer, byteOffset, byteLength } = info + const { buffer, byteOffset, byteLength } = info as Uint8Array const infoBuff = Buffer.from(buffer, byteOffset, byteLength) - const derivedBytes = kdfIndex[kdfHash](toExtract)(keyLengthBytes, infoBuff) + const derivedBytes = kdfIndex[kdfHash as NodeHash](toExtract)( + keyLengthBytes, + infoBuff + ) return wrapWithKeyObjectIfSupported(derivedBytes) } diff --git a/modules/material-management-node/src/node_cryptographic_materials_manager.ts b/modules/material-management-node/src/node_cryptographic_materials_manager.ts index a3c2d87e5..c11fb1f8b 100644 --- a/modules/material-management-node/src/node_cryptographic_materials_manager.ts +++ b/modules/material-management-node/src/node_cryptographic_materials_manager.ts @@ -2,11 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 import { - NodeMaterialsManager, EncryptionRequest, DecryptionRequest, EncryptionContext, // eslint-disable-line no-unused-vars - NodeAlgorithmSuite, NodeEncryptionMaterial, NodeDecryptionMaterial, SignatureKey, - needs, VerificationKey, AlgorithmSuiteIdentifier, - immutableClass, readOnlyProperty, KeyringNode, - GetEncryptionMaterials, GetDecryptMaterials // eslint-disable-line no-unused-vars + NodeMaterialsManager, + EncryptionRequest, + DecryptionRequest, + EncryptionContext, + NodeAlgorithmSuite, + NodeEncryptionMaterial, + NodeDecryptionMaterial, + SignatureKey, + needs, + VerificationKey, + AlgorithmSuiteIdentifier, + immutableClass, + readOnlyProperty, + KeyringNode, + GetEncryptionMaterials, + GetDecryptMaterials, } from '@aws-crypto/material-management' import { ENCODED_SIGNER_KEY } from '@aws-crypto/serialize' @@ -15,7 +26,9 @@ import { createECDH } from 'crypto' export type NodeEncryptionRequest = EncryptionRequest export type NodeDecryptionRequest = DecryptionRequest -export type NodeGetEncryptionMaterials = GetEncryptionMaterials +export type NodeGetEncryptionMaterials = GetEncryptionMaterials< + NodeAlgorithmSuite +> export type NodeGetDecryptMaterials = GetDecryptMaterials /** @@ -23,16 +36,24 @@ export type NodeGetDecryptMaterials = GetDecryptMaterials * New cryptography materials managers SHOULD extend from NodeMaterialsManager. * Users should never need to create an instance of a DefaultCryptographicMaterialsManager. */ -export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsManager { +export class NodeDefaultCryptographicMaterialsManager + implements NodeMaterialsManager { readonly keyring!: KeyringNode - constructor (keyring: KeyringNode) { + constructor(keyring: KeyringNode) { /* Precondition: keyrings must be a KeyringNode. */ needs(keyring instanceof KeyringNode, 'Unsupported type.') readOnlyProperty(this, 'keyring', keyring) } - async getEncryptionMaterials ({ suite, encryptionContext }: NodeEncryptionRequest): Promise { - suite = suite || new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) + async getEncryptionMaterials({ + suite, + encryptionContext, + }: NodeEncryptionRequest): Promise { + suite = + suite || + new NodeAlgorithmSuite( + AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 + ) /* Precondition: NodeDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize. * A CryptographicMaterialsManager can change entries to the encryptionContext @@ -40,11 +61,17 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa * The DefaultCryptographicMaterialsManager uses the value in the encryption context to store public signing key. * If the caller is using this value in their encryption context the Default CMM is probably not the CMM they want to use. */ - needs(!encryptionContext.hasOwnProperty(ENCODED_SIGNER_KEY), `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.`) + needs( + !Object.prototype.hasOwnProperty.call( + encryptionContext, + ENCODED_SIGNER_KEY + ), + `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.` + ) - const material = await this - .keyring - .onEncrypt(this._initializeEncryptionMaterial(suite, encryptionContext)) + const material = await this.keyring.onEncrypt( + this._initializeEncryptionMaterial(suite, encryptionContext) + ) /* Postcondition: The NodeEncryptionMaterial must contain a valid dataKey. * This verifies that the data key matches the algorithm suite specification @@ -54,15 +81,23 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa needs(material.getUnencryptedDataKey(), 'Unencrypted data key is invalid.') /* Postcondition: The NodeEncryptionMaterial must contain at least 1 EncryptedDataKey. */ - needs(material.encryptedDataKeys.length, 'No EncryptedDataKeys: the ciphertext can never be decrypted.') + needs( + material.encryptedDataKeys.length, + 'No EncryptedDataKeys: the ciphertext can never be decrypted.' + ) return material } - async decryptMaterials ({ suite, encryptedDataKeys, encryptionContext }: NodeDecryptionRequest): Promise { - const material = await this - .keyring - .onDecrypt(this._initializeDecryptionMaterial(suite, encryptionContext), encryptedDataKeys.slice()) + async decryptMaterials({ + suite, + encryptedDataKeys, + encryptionContext, + }: NodeDecryptionRequest): Promise { + const material = await this.keyring.onDecrypt( + this._initializeDecryptionMaterial(suite, encryptionContext), + encryptedDataKeys.slice() + ) /* Postcondition: The NodeDecryptionMaterial must contain a valid dataKey. * See: cryptographic_materials.ts, `getUnencryptedDataKey` also verifies @@ -75,7 +110,10 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa return material } - _initializeEncryptionMaterial (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { + _initializeEncryptionMaterial( + suite: NodeAlgorithmSuite, + encryptionContext: EncryptionContext + ) { const { signatureCurve: namedCurve } = suite /* Check for early return (Postcondition): The algorithm suite specification must support a signatureCurve to generate a ECDH key. */ @@ -86,36 +124,47 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa // @ts-ignore I want a compressed buffer. const compressPoint = ecdh.getPublicKey(undefined, 'compressed') const privateKey = ecdh.getPrivateKey() - const signatureKey = new SignatureKey(privateKey, new Uint8Array(compressPoint), suite) - - return new NodeEncryptionMaterial( - suite, - { - ...encryptionContext, - [ENCODED_SIGNER_KEY]: compressPoint.toString('base64') - } + const signatureKey = new SignatureKey( + privateKey, + new Uint8Array(compressPoint), + suite ) - .setSignatureKey(signatureKey) + + return new NodeEncryptionMaterial(suite, { + ...encryptionContext, + [ENCODED_SIGNER_KEY]: compressPoint.toString('base64'), + }).setSignatureKey(signatureKey) } - _initializeDecryptionMaterial (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { + _initializeDecryptionMaterial( + suite: NodeAlgorithmSuite, + encryptionContext: EncryptionContext + ) { const { signatureCurve: namedCurve } = suite /* Check for early return (Postcondition): The algorithm suite specification must support a signatureCurve to load a signature key. */ if (!namedCurve) return new NodeDecryptionMaterial(suite, encryptionContext) /* Precondition: NodeDefaultCryptographicMaterialsManager If the algorithm suite specification requires a signatureCurve a context must exist. */ - if (!encryptionContext) throw new Error('Encryption context does not contain required public key.') + if (!encryptionContext) + throw new Error( + 'Encryption context does not contain required public key.' + ) const { [ENCODED_SIGNER_KEY]: compressPoint } = encryptionContext /* Precondition: NodeDefaultCryptographicMaterialsManager The context must contain the public key. */ needs(compressPoint, 'Context does not contain required public key.') - const publicKeyBytes = VerificationKey.decodeCompressPoint(Buffer.from(compressPoint, 'base64'), suite) + const publicKeyBytes = VerificationKey.decodeCompressPoint( + Buffer.from(compressPoint, 'base64'), + suite + ) - return new NodeDecryptionMaterial(suite, encryptionContext) - .setVerificationKey(new VerificationKey(publicKeyBytes, suite)) + return new NodeDecryptionMaterial( + suite, + encryptionContext + ).setVerificationKey(new VerificationKey(publicKeyBytes, suite)) } } immutableClass(NodeDefaultCryptographicMaterialsManager) diff --git a/modules/material-management/package.json b/modules/material-management/package.json index d0368e8c3..a76ea2abb 100644 --- a/modules/material-management/package.json +++ b/modules/material-management/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management/src/algorithm_suites.ts b/modules/material-management/src/algorithm_suites.ts index 756b49e16..cf9897538 100644 --- a/modules/material-management/src/algorithm_suites.ts +++ b/modules/material-management/src/algorithm_suites.ts @@ -30,45 +30,49 @@ export enum AlgorithmSuiteIdentifier { 'ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256' = 0x0178, 'ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256' = 0x0214, 'ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384' = 0x0346, - 'ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384' = 0x0378 + 'ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384' = 0x0378, } Object.freeze(AlgorithmSuiteIdentifier) export type AlgorithmSuiteName = keyof typeof AlgorithmSuiteIdentifier export type AlgorithmSuiteTypeNode = 'node' export type AlgorithmSuiteTypeWebCrypto = 'webCrypto' -export type NodeEncryption = 'aes-128-gcm'|'aes-192-gcm'|'aes-256-gcm' +export type NodeEncryption = 'aes-128-gcm' | 'aes-192-gcm' | 'aes-256-gcm' export type WebCryptoEncryption = 'AES-GCM' export type KDF = 'HKDF' -export type NodeHash = 'sha256'|'sha384' -export type WebCryptoHash = 'SHA-256'|'SHA-384' -export type NodeECDHCurve = 'prime256v1'|'secp384r1' -export type WebCryptoECDHCurve = 'P-256'|'P-384' -export type KeyLength = 128|192|256 +export type NodeHash = 'sha256' | 'sha384' +export type WebCryptoHash = 'SHA-256' | 'SHA-384' +export type NodeECDHCurve = 'prime256v1' | 'secp384r1' +export type WebCryptoECDHCurve = 'P-256' | 'P-384' +export type KeyLength = 128 | 192 | 256 export type IvLength = 12 export type TagLength = 128 - -export interface IAlgorithmSuite extends Readonly<{ - id: AlgorithmSuiteIdentifier - encryption: NodeEncryption|WebCryptoEncryption - keyLength: KeyLength - ivLength: IvLength - tagLength: TagLength - cacheSafe: boolean - kdf?: KDF - kdfHash?: NodeHash|WebCryptoHash - signatureCurve?: NodeECDHCurve|WebCryptoECDHCurve - signatureHash?: NodeHash|WebCryptoHash -}>{} - +/* eslint-disable @typescript-eslint/interface-name-prefix */ +export interface IAlgorithmSuite + extends Readonly<{ + /* eslint-enable @typescript-eslint/interface-name-prefix */ + id: AlgorithmSuiteIdentifier + encryption: NodeEncryption | WebCryptoEncryption + keyLength: KeyLength + ivLength: IvLength + tagLength: TagLength + cacheSafe: boolean + kdf?: KDF + kdfHash?: NodeHash | WebCryptoHash + signatureCurve?: NodeECDHCurve | WebCryptoECDHCurve + signatureHash?: NodeHash | WebCryptoHash + }> {} +/* eslint-disable @typescript-eslint/interface-name-prefix */ export interface INodeAlgorithmSuite extends IAlgorithmSuite { + /* eslint-enable @typescript-eslint/interface-name-prefix */ encryption: NodeEncryption kdfHash?: NodeHash signatureCurve?: NodeECDHCurve signatureHash?: NodeHash } - +/* eslint-disable @typescript-eslint/interface-name-prefix */ export interface IWebCryptoAlgorithmSuite extends IAlgorithmSuite { + /* eslint-enable @typescript-eslint/interface-name-prefix */ encryption: WebCryptoEncryption kdfHash?: WebCryptoHash signatureCurve?: WebCryptoECDHCurve @@ -78,31 +82,37 @@ export interface IWebCryptoAlgorithmSuite extends IAlgorithmSuite { export abstract class AlgorithmSuite implements IAlgorithmSuite { id!: AlgorithmSuiteIdentifier name!: AlgorithmSuiteName - encryption!: NodeEncryption|WebCryptoEncryption + encryption!: NodeEncryption | WebCryptoEncryption keyLength!: KeyLength keyLengthBytes!: number ivLength!: IvLength tagLength!: TagLength cacheSafe!: boolean kdf?: KDF - kdfHash?: NodeHash|WebCryptoHash - signatureCurve?: NodeECDHCurve|WebCryptoECDHCurve - signatureHash?: NodeHash|WebCryptoHash - type!: AlgorithmSuiteTypeNode|AlgorithmSuiteTypeWebCrypto - constructor (suite: INodeAlgorithmSuite|IWebCryptoAlgorithmSuite) { - needs(this.constructor !== AlgorithmSuite, 'new AlgorithmSuite is not allowed') + kdfHash?: NodeHash | WebCryptoHash + signatureCurve?: NodeECDHCurve | WebCryptoECDHCurve + signatureHash?: NodeHash | WebCryptoHash + type!: AlgorithmSuiteTypeNode | AlgorithmSuiteTypeWebCrypto + constructor(suite: INodeAlgorithmSuite | IWebCryptoAlgorithmSuite) { + needs( + this.constructor !== AlgorithmSuite, + 'new AlgorithmSuite is not allowed' + ) /* Precondition: A algorithm suite specification must be passed. */ needs(suite, 'Algorithm specification not set.') /* Precondition: The Algorithm Suite Identifier must exist. */ - needs(AlgorithmSuiteIdentifier[suite.id], 'No suite by that identifier exists.') + needs( + AlgorithmSuiteIdentifier[suite.id], + 'No suite by that identifier exists.' + ) Object.defineProperty(this, 'keyLengthBytes', { get: () => this.keyLength / 8, - enumerable: true + enumerable: true, }) Object.defineProperty(this, 'name', { get: () => AlgorithmSuiteIdentifier[this.id], - enumerable: true + enumerable: true, }) Object.assign(this, suite) } diff --git a/modules/material-management/src/clone_cryptographic_material.ts b/modules/material-management/src/clone_cryptographic_material.ts index 739678a36..f9e15c92a 100644 --- a/modules/material-management/src/clone_cryptographic_material.ts +++ b/modules/material-management/src/clone_cryptographic_material.ts @@ -7,28 +7,28 @@ import { WebCryptoEncryptionMaterial, WebCryptoDecryptionMaterial, isEncryptionMaterial, - isDecryptionMaterial + isDecryptionMaterial, } from './cryptographic_material' -import { - NodeAlgorithmSuite -} from './node_algorithms' -import { - AwsEsdkKeyObject // eslint-disable-line no-unused-vars -} from './types' +import { NodeAlgorithmSuite } from './node_algorithms' +import { AwsEsdkKeyObject } from './types' import { needs } from './needs' -type Material = NodeEncryptionMaterial|NodeDecryptionMaterial|WebCryptoEncryptionMaterial|WebCryptoDecryptionMaterial +type Material = + | NodeEncryptionMaterial + | NodeDecryptionMaterial + | WebCryptoEncryptionMaterial + | WebCryptoDecryptionMaterial -export function cloneMaterial (source: M): M { +export function cloneMaterial(source: M): M { const { suite, encryptionContext } = source - const clone = (suite instanceof NodeAlgorithmSuite + const clone = (suite instanceof NodeAlgorithmSuite ? source instanceof NodeEncryptionMaterial ? new NodeEncryptionMaterial(suite, encryptionContext) : new NodeDecryptionMaterial(suite, encryptionContext) : source instanceof WebCryptoEncryptionMaterial - ? new WebCryptoEncryptionMaterial(suite, encryptionContext) - : new WebCryptoDecryptionMaterial(suite, encryptionContext)) + ? new WebCryptoEncryptionMaterial(suite, encryptionContext) + : new WebCryptoDecryptionMaterial(suite, encryptionContext)) as M /* The setTrace _must_ be the first trace, * If the material is an EncryptionMaterial @@ -49,21 +49,26 @@ export function cloneMaterial (source: M): M { clone.setUnencryptedDataKey(udk, setTrace) } - if ((source).hasCryptoKey) { - const cryptoKey = (source).getCryptoKey() - ;(clone) - .setCryptoKey(cryptoKey, setTrace) + if ((source as WebCryptoDecryptionMaterial).hasCryptoKey) { + const cryptoKey = (source as WebCryptoDecryptionMaterial).getCryptoKey() + ;(clone as WebCryptoDecryptionMaterial).setCryptoKey(cryptoKey, setTrace) } if (isEncryptionMaterial(source) && isEncryptionMaterial(clone)) { const encryptedDataKeys = source.encryptedDataKeys /* Precondition: For each encrypted data key, there must be a trace. */ - needs(encryptedDataKeys.length === traces.length, 'KeyringTrace length does not match encrypted data keys.') + needs( + encryptedDataKeys.length === traces.length, + 'KeyringTrace length does not match encrypted data keys.' + ) encryptedDataKeys.forEach((edk, i) => { const { providerInfo, providerId } = edk const { keyNamespace, keyName, flags } = traces[i] /* Precondition: The traces must be in the same order as the encrypted data keys. */ - needs(keyName === providerInfo && keyNamespace === providerId, 'Keyring trace does not match encrypted data key.') + needs( + keyName === providerInfo && keyNamespace === providerId, + 'Keyring trace does not match encrypted data key.' + ) clone.addEncryptedDataKey(edk, flags) }) @@ -83,7 +88,7 @@ export function cloneMaterial (source: M): M { return clone } -function cloneUnencryptedDataKey (dataKey: AwsEsdkKeyObject| Uint8Array) { +function cloneUnencryptedDataKey(dataKey: AwsEsdkKeyObject | Uint8Array) { if (dataKey instanceof Uint8Array) { return new Uint8Array(dataKey) } diff --git a/modules/material-management/src/cryptographic_material.ts b/modules/material-management/src/cryptographic_material.ts index 83f618435..955b6f362 100644 --- a/modules/material-management/src/cryptographic_material.ts +++ b/modules/material-management/src/cryptographic_material.ts @@ -2,18 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - AwsEsdkJsCryptoKey, // eslint-disable-line no-unused-vars - AwsEsdkJsKeyUsage, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars - AwsEsdkKeyObject, // eslint-disable-line no-unused-vars - AwsEsdkCreateSecretKey // eslint-disable-line no-unused-vars + MixedBackendCryptoKey, + SupportedAlgorithmSuites, + AwsEsdkJsCryptoKey, + AwsEsdkJsKeyUsage, + EncryptionContext, + AwsEsdkKeyObject, + AwsEsdkCreateSecretKey, } from './types' import { EncryptedDataKey } from './encrypted_data_key' import { SignatureKey, VerificationKey } from './signature_key' import { frozenClass, readOnlyProperty } from './immutable_class' -import { KeyringTrace, KeyringTraceFlag } from './keyring_trace' // eslint-disable-line no-unused-vars +import { KeyringTrace, KeyringTraceFlag } from './keyring_trace' import { NodeAlgorithmSuite } from './node_algorithms' import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' import { needs } from './needs' @@ -27,7 +27,10 @@ import { needs } from './needs' interface AwsEsdkKeyObjectInstanceOf { new (): AwsEsdkKeyObject } -type AwsEsdkCrypto = {KeyObject: AwsEsdkKeyObjectInstanceOf, createSecretKey: AwsEsdkCreateSecretKey, } +type AwsEsdkCrypto = { + KeyObject: AwsEsdkKeyObjectInstanceOf + createSecretKey: AwsEsdkCreateSecretKey +} export const supportsKeyObject = (function () { try { const { KeyObject, createSecretKey } = require('crypto') as AwsEsdkCrypto @@ -56,19 +59,22 @@ export const supportsKeyObject = (function () { * it is no longer needed. */ -const timingSafeEqual: (a: Uint8Array, b: Uint8Array) => boolean = (function () { +const timingSafeEqual: ( + a: Uint8Array, + b: Uint8Array +) => boolean = (function () { try { /* It is possible for `require` to return an empty object, or an object * that does not implement `timingSafeEqual`. * in this case I need a fallback */ - const { timingSafeEqual: nodeTimingSafeEqual } = require('crypto') + const { timingSafeEqual: nodeTimingSafeEqual } = require('crypto') // eslint-disable-line @typescript-eslint/no-var-requires return nodeTimingSafeEqual || portableTimingSafeEqual } catch (e) { return portableTimingSafeEqual } /* https://codahale.com/a-lesson-in-timing-attacks/ */ - function portableTimingSafeEqual (a: Uint8Array, b: Uint8Array) { + function portableTimingSafeEqual(a: Uint8Array, b: Uint8Array) { /* It is *possible* that a runtime could optimize this constant time function. * Adding `eval` could prevent the optimization, but this is no guarantee. * The eval below is commented out @@ -90,7 +96,7 @@ const timingSafeEqual: (a: Uint8Array, b: Uint8Array) => boolean = (function () for (let i = 0; i < b.length; i++) { diff |= a[i] ^ b[i] } - return (diff === 0) + return diff === 0 } })() @@ -100,53 +106,75 @@ export interface FunctionalCryptographicMaterial { export interface CryptographicMaterial> { suite: SupportedAlgorithmSuites - setUnencryptedDataKey: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => T - getUnencryptedDataKey: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => T + getUnencryptedDataKey: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey: () => T hasUnencryptedDataKey: boolean keyringTrace: KeyringTrace[] encryptionContext: Readonly } -export interface EncryptionMaterial> extends CryptographicMaterial { +export interface EncryptionMaterial> + extends CryptographicMaterial { encryptedDataKeys: EncryptedDataKey[] addEncryptedDataKey: (edk: EncryptedDataKey, flags: KeyringTraceFlag) => T setSignatureKey: (key: SignatureKey) => T signatureKey?: SignatureKey } -export interface DecryptionMaterial> extends CryptographicMaterial { +export interface DecryptionMaterial> + extends CryptographicMaterial { setVerificationKey: (key: VerificationKey) => T verificationKey?: VerificationKey } -export interface WebCryptoMaterial> extends CryptographicMaterial { - setCryptoKey: (dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, trace: KeyringTrace) => T - getCryptoKey: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey +export interface WebCryptoMaterial> + extends CryptographicMaterial { + setCryptoKey: ( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, + trace: KeyringTrace + ) => T + getCryptoKey: () => AwsEsdkJsCryptoKey | MixedBackendCryptoKey hasCryptoKey: boolean validUsages: ReadonlyArray } -export class NodeEncryptionMaterial implements - Readonly>, - FunctionalCryptographicMaterial { +export class NodeEncryptionMaterial + implements + Readonly>, + FunctionalCryptographicMaterial { suite: NodeAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => NodeEncryptionMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => NodeEncryptionMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => NodeEncryptionMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] encryptedDataKeys!: EncryptedDataKey[] - addEncryptedDataKey!: (edk: EncryptedDataKey, flags: KeyringTraceFlag) => NodeEncryptionMaterial + addEncryptedDataKey!: ( + edk: EncryptedDataKey, + flags: KeyringTraceFlag + ) => NodeEncryptionMaterial setSignatureKey!: (key: SignatureKey) => NodeEncryptionMaterial signatureKey?: SignatureKey encryptionContext: Readonly - constructor (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { + constructor(suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: NodeEncryptionMaterial suite must be NodeAlgorithmSuite. */ - needs(suite instanceof NodeAlgorithmSuite, 'Suite must be a NodeAlgorithmSuite') + needs( + suite instanceof NodeAlgorithmSuite, + 'Suite must be a NodeAlgorithmSuite' + ) this.suite = suite /* Precondition: NodeEncryptionMaterial encryptionContext must be an object, even if it is empty. */ - needs(encryptionContext && typeof encryptionContext === 'object', 'Encryption context must be set') + needs( + encryptionContext && typeof encryptionContext === 'object', + 'Encryption context must be set' + ) this.encryptionContext = Object.freeze({ ...encryptionContext }) // EncryptionMaterial have generated a data key on setUnencryptedDataKey const setFlags = KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY @@ -155,30 +183,40 @@ export class NodeEncryptionMaterial implements Object.setPrototypeOf(this, NodeEncryptionMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasUnencryptedDataKey } } frozenClass(NodeEncryptionMaterial) -export class NodeDecryptionMaterial implements - Readonly>, - FunctionalCryptographicMaterial { +export class NodeDecryptionMaterial + implements + Readonly>, + FunctionalCryptographicMaterial { suite: NodeAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => NodeDecryptionMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => NodeDecryptionMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => NodeDecryptionMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] setVerificationKey!: (key: VerificationKey) => NodeDecryptionMaterial verificationKey?: VerificationKey encryptionContext: Readonly - constructor (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { + constructor(suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: NodeDecryptionMaterial suite must be NodeAlgorithmSuite. */ - needs(suite instanceof NodeAlgorithmSuite, 'Suite must be a NodeAlgorithmSuite') + needs( + suite instanceof NodeAlgorithmSuite, + 'Suite must be a NodeAlgorithmSuite' + ) this.suite = suite /* Precondition: NodeDecryptionMaterial encryptionContext must be an object, even if it is empty. */ - needs(encryptionContext && typeof encryptionContext === 'object', 'Encryption context must be set') + needs( + encryptionContext && typeof encryptionContext === 'object', + 'Encryption context must be set' + ) this.encryptionContext = Object.freeze({ ...encryptionContext }) // DecryptionMaterial have decrypted a data key on setUnencryptedDataKey const setFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY @@ -187,38 +225,60 @@ export class NodeDecryptionMaterial implements Object.setPrototypeOf(this, NodeDecryptionMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasUnencryptedDataKey } } frozenClass(NodeDecryptionMaterial) -export class WebCryptoEncryptionMaterial implements - Readonly>, - Readonly>, - FunctionalCryptographicMaterial { +export class WebCryptoEncryptionMaterial + implements + Readonly>, + Readonly>, + FunctionalCryptographicMaterial { suite: WebCryptoAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => WebCryptoEncryptionMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => WebCryptoEncryptionMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => WebCryptoEncryptionMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] encryptedDataKeys!: EncryptedDataKey[] - addEncryptedDataKey!: (edk: EncryptedDataKey, flags: KeyringTraceFlag) => WebCryptoEncryptionMaterial + addEncryptedDataKey!: ( + edk: EncryptedDataKey, + flags: KeyringTraceFlag + ) => WebCryptoEncryptionMaterial setSignatureKey!: (key: SignatureKey) => WebCryptoEncryptionMaterial signatureKey?: SignatureKey - setCryptoKey!: (dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, trace: KeyringTrace) => WebCryptoEncryptionMaterial - getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey + setCryptoKey!: ( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, + trace: KeyringTrace + ) => WebCryptoEncryptionMaterial + getCryptoKey!: () => AwsEsdkJsCryptoKey | MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray encryptionContext: Readonly - constructor (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + constructor( + suite: WebCryptoAlgorithmSuite, + encryptionContext: EncryptionContext + ) { /* Precondition: WebCryptoEncryptionMaterial suite must be WebCryptoAlgorithmSuite. */ - needs(suite instanceof WebCryptoAlgorithmSuite, 'Suite must be a WebCryptoAlgorithmSuite') + needs( + suite instanceof WebCryptoAlgorithmSuite, + 'Suite must be a WebCryptoAlgorithmSuite' + ) this.suite = suite - this.validUsages = Object.freeze(['deriveKey', 'encrypt']) + this.validUsages = Object.freeze([ + 'deriveKey', + 'encrypt', + ] as AwsEsdkJsKeyUsage[]) /* Precondition: WebCryptoEncryptionMaterial encryptionContext must be an object, even if it is empty. */ - needs(encryptionContext && typeof encryptionContext === 'object', 'Encryption context must be set') + needs( + encryptionContext && typeof encryptionContext === 'object', + 'Encryption context must be set' + ) this.encryptionContext = Object.freeze({ ...encryptionContext }) // EncryptionMaterial have generated a data key on setUnencryptedDataKey const setFlag = KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY @@ -228,36 +288,55 @@ export class WebCryptoEncryptionMaterial implements Object.setPrototypeOf(this, WebCryptoEncryptionMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasUnencryptedDataKey && this.hasCryptoKey } } frozenClass(WebCryptoEncryptionMaterial) -export class WebCryptoDecryptionMaterial implements - Readonly>, - Readonly>, - FunctionalCryptographicMaterial { +export class WebCryptoDecryptionMaterial + implements + Readonly>, + Readonly>, + FunctionalCryptographicMaterial { suite: WebCryptoAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => WebCryptoDecryptionMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => WebCryptoDecryptionMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => WebCryptoDecryptionMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] setVerificationKey!: (key: VerificationKey) => WebCryptoDecryptionMaterial verificationKey?: VerificationKey - setCryptoKey!: (dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, trace: KeyringTrace) => WebCryptoDecryptionMaterial - getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey + setCryptoKey!: ( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, + trace: KeyringTrace + ) => WebCryptoDecryptionMaterial + getCryptoKey!: () => AwsEsdkJsCryptoKey | MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray encryptionContext: Readonly - constructor (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + constructor( + suite: WebCryptoAlgorithmSuite, + encryptionContext: EncryptionContext + ) { /* Precondition: WebCryptoDecryptionMaterial suite must be WebCryptoAlgorithmSuite. */ - needs(suite instanceof WebCryptoAlgorithmSuite, 'Suite must be a WebCryptoAlgorithmSuite') + needs( + suite instanceof WebCryptoAlgorithmSuite, + 'Suite must be a WebCryptoAlgorithmSuite' + ) this.suite = suite - this.validUsages = Object.freeze(['deriveKey', 'decrypt']) + this.validUsages = Object.freeze([ + 'deriveKey', + 'decrypt', + ] as AwsEsdkJsKeyUsage[]) /* Precondition: WebCryptoDecryptionMaterial encryptionContext must be an object, even if it is empty. */ - needs(encryptionContext && typeof encryptionContext === 'object', 'Encryption context must be set') + needs( + encryptionContext && typeof encryptionContext === 'object', + 'Encryption context must be set' + ) this.encryptionContext = Object.freeze({ ...encryptionContext }) // DecryptionMaterial have decrypted a data key on setUnencryptedDataKey const setFlag = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY @@ -267,33 +346,46 @@ export class WebCryptoDecryptionMaterial implements Object.setPrototypeOf(this, WebCryptoDecryptionMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasCryptoKey } } frozenClass(WebCryptoDecryptionMaterial) -export function isEncryptionMaterial (obj: any): obj is WebCryptoEncryptionMaterial|NodeEncryptionMaterial { - return (obj instanceof WebCryptoEncryptionMaterial) || (obj instanceof NodeEncryptionMaterial) +export function isEncryptionMaterial( + obj: any +): obj is WebCryptoEncryptionMaterial | NodeEncryptionMaterial { + return ( + obj instanceof WebCryptoEncryptionMaterial || + obj instanceof NodeEncryptionMaterial + ) } -export function isDecryptionMaterial (obj: any): obj is WebCryptoDecryptionMaterial|NodeDecryptionMaterial { - return (obj instanceof WebCryptoDecryptionMaterial) || (obj instanceof NodeDecryptionMaterial) +export function isDecryptionMaterial( + obj: any +): obj is WebCryptoDecryptionMaterial | NodeDecryptionMaterial { + return ( + obj instanceof WebCryptoDecryptionMaterial || + obj instanceof NodeDecryptionMaterial + ) } -export function decorateCryptographicMaterial> (material: T, setFlag: KeyringTraceFlag) { +export function decorateCryptographicMaterial< + T extends CryptographicMaterial +>(material: T, setFlag: KeyringTraceFlag) { /* Precondition: setFlag must be in the set of KeyringTraceFlag.SET_FLAGS. */ needs(setFlag & KeyringTraceFlag.SET_FLAGS, 'Invalid setFlag') /* When a KeyringTraceFlag is passed to setUnencryptedDataKey, * it must be valid for the type of material. * It is invalid to claim that EncryptionMaterial were decrypted. */ - const deniedSetFlags = (KeyringTraceFlag.SET_FLAGS ^ setFlag) | ( - setFlag === KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY + const deniedSetFlags = + (KeyringTraceFlag.SET_FLAGS ^ setFlag) | + (setFlag === KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY ? KeyringTraceFlag.DECRYPT_FLAGS : setFlag === KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY - ? KeyringTraceFlag.ENCRYPT_FLAGS - : 0) + ? KeyringTraceFlag.ENCRYPT_FLAGS + : 0) let unencryptedDataKeyZeroed = false let unencryptedDataKey: AwsEsdkKeyObject | Uint8Array @@ -303,9 +395,13 @@ export function decorateCryptographicMaterial // to it would be propagated to any cached versions. let udkForVerification: Uint8Array - const setUnencryptedDataKey = (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => { + const setUnencryptedDataKey = ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => { /* Avoid making unnecessary copies of the dataKey. */ - const tempUdk = dataKey instanceof Uint8Array ? dataKey : unwrapDataKey(dataKey) + const tempUdk = + dataKey instanceof Uint8Array ? dataKey : unwrapDataKey(dataKey) /* All security conditions are tested here and failures will throw. */ verifyUnencryptedDataKeyForSet(tempUdk, trace) unencryptedDataKey = wrapWithKeyObjectIfSupported(dataKey) @@ -314,7 +410,7 @@ export function decorateCryptographicMaterial return material } - const getUnencryptedDataKey = (): Uint8Array|AwsEsdkKeyObject => { + const getUnencryptedDataKey = (): Uint8Array | AwsEsdkKeyObject => { /* Precondition: unencryptedDataKey must be set before we can return it. */ needs(unencryptedDataKey, 'unencryptedDataKey has not been set') /* Precondition: unencryptedDataKey must not be Zeroed out. @@ -326,13 +422,17 @@ export function decorateCryptographicMaterial * then the security around modification is handled in C. * Do not duplicate the secret just to check... */ - needs(!(unencryptedDataKey instanceof Uint8Array) || timingSafeEqual(udkForVerification, unwrapDataKey(unencryptedDataKey)), 'unencryptedDataKey has been corrupted.') + needs( + !(unencryptedDataKey instanceof Uint8Array) || + timingSafeEqual(udkForVerification, unwrapDataKey(unencryptedDataKey)), + 'unencryptedDataKey has been corrupted.' + ) return unencryptedDataKey } Object.defineProperty(material, 'hasUnencryptedDataKey', { // Check that we have both not zeroed AND that we have not set get: () => !!unencryptedDataKey && !unencryptedDataKeyZeroed, - enumerable: true + enumerable: true, }) const zeroUnencryptedDataKey = () => { /* These checks are separated on purpose. It should be impossible to have only one unset. @@ -367,7 +467,10 @@ export function decorateCryptographicMaterial * If it is ever the case that only one was unset, then something is wrong in a profound way. * It is not clear how this could ever happen, unless someone is manipulating the OS... */ - needs(unsetCount === 0 || unsetCount === 2, 'Either unencryptedDataKey or udkForVerification was not set.') + needs( + unsetCount === 0 || unsetCount === 2, + 'Either unencryptedDataKey or udkForVerification was not set.' + ) return material } @@ -377,7 +480,10 @@ export function decorateCryptographicMaterial return material - function verifyUnencryptedDataKeyForSet (dataKey: Uint8Array, trace: KeyringTrace) { + function verifyUnencryptedDataKeyForSet( + dataKey: Uint8Array, + trace: KeyringTrace + ) { /* Precondition: unencryptedDataKey must not be set. Modifying the unencryptedDataKey is denied */ needs(!unencryptedDataKey, 'unencryptedDataKey has already been set') /* Precondition: dataKey must be Binary Data */ @@ -387,15 +493,24 @@ export function decorateCryptographicMaterial * a large potentially shared ArrayBuffer. * If this was the case, it may be possible to find or manipulate. */ - needs(dataKey.byteOffset === 0, 'Unencrypted Master Key must be an isolated buffer.') + needs( + dataKey.byteOffset === 0, + 'Unencrypted Master Key must be an isolated buffer.' + ) /* Precondition: The data key length must agree with algorithm specification. * If this is not the case, it either means ciphertext was tampered * with or the keyring implementation is not setting the length properly. */ - needs(dataKey.byteLength === material.suite.keyLengthBytes, 'Key length does not agree with the algorithm specification.') + needs( + dataKey.byteLength === material.suite.keyLengthBytes, + 'Key length does not agree with the algorithm specification.' + ) /* Precondition: Trace must be set, and the flag must indicate that the data key was generated. */ - needs(trace && trace.keyName && trace.keyNamespace, 'Malformed KeyringTrace') + needs( + trace && trace.keyName && trace.keyNamespace, + 'Malformed KeyringTrace' + ) /* Precondition: On set the required KeyringTraceFlag must be set. */ needs(trace.flags & setFlag, 'Required KeyringTraceFlag not set') /* Precondition: Only valid flags are allowed. @@ -405,12 +520,18 @@ export function decorateCryptographicMaterial } } -export function decorateEncryptionMaterial> (material: T) { - const deniedEncryptFlags = KeyringTraceFlag.SET_FLAGS | KeyringTraceFlag.DECRYPT_FLAGS +export function decorateEncryptionMaterial>( + material: T +) { + const deniedEncryptFlags = + KeyringTraceFlag.SET_FLAGS | KeyringTraceFlag.DECRYPT_FLAGS const encryptedDataKeys: EncryptedDataKey[] = [] - let signatureKey: Readonly|undefined + let signatureKey: Readonly | undefined - const addEncryptedDataKey = (edk: EncryptedDataKey, flags: KeyringTraceFlag) => { + const addEncryptedDataKey = ( + edk: EncryptedDataKey, + flags: KeyringTraceFlag + ) => { /* Precondition: If a data key has not already been generated, there must be no EDKs. * Pushing EDKs on the list before the data key has been generated may cause the list of * EDKs to be inconsistent. (i.e., they would decrypt to different data keys.) @@ -420,10 +541,16 @@ export function decorateEncryptionMaterial> (mat * Putting things onto the list that are not EncryptedDataKey * may cause the list of EDKs to be inconsistent. (i.e. they may not serialize, or be mutable) */ - needs(edk instanceof EncryptedDataKey, 'Unsupported instance of encryptedDataKey') + needs( + edk instanceof EncryptedDataKey, + 'Unsupported instance of encryptedDataKey' + ) /* Precondition: flags must indicate that the key was encrypted. */ - needs(flags & KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY, 'Encrypted data key flag must be set.') + needs( + flags & KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY, + 'Encrypted data key flag must be set.' + ) /* Precondition: flags must not include a setFlag or a decrypt flag. * The setFlag is reserved for setting the unencrypted data key @@ -435,7 +562,11 @@ export function decorateEncryptionMaterial> (mat * KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX is reserved for the decrypt path */ needs(!(flags & deniedEncryptFlags), 'Invalid flag for EncryptedDataKey.') - material.keyringTrace.push({ keyName: edk.providerInfo, keyNamespace: edk.providerId, flags }) + material.keyringTrace.push({ + keyName: edk.providerInfo, + keyNamespace: edk.providerId, + flags, + }) encryptedDataKeys.push(edk) return material @@ -446,14 +577,17 @@ export function decorateEncryptionMaterial> (mat // I only want EDKs added through addEncryptedDataKey // so I return a new array get: () => [...encryptedDataKeys], - enumerable: true + enumerable: true, }) const setSignatureKey = (key: SignatureKey) => { /* Precondition: The SignatureKey stored must agree with the algorithm specification. * If this is not the case it means the MaterialManager or Keyring is not setting * the SignatureKey correctly */ - needs(material.suite.signatureCurve, 'Algorithm specification does not support signatures.') + needs( + material.suite.signatureCurve, + 'Algorithm specification does not support signatures.' + ) /* Precondition: signatureKey must not be set. Modifying the signatureKey is denied. */ needs(!signatureKey, 'Signature key has already been set.') /* Precondition: key must be a SignatureKey. */ @@ -468,24 +602,32 @@ export function decorateEncryptionMaterial> (mat * If this is not the case it means the MaterialManager or Keyring is not setting * the SignatureKey correctly */ - needs(!!material.suite.signatureCurve === !!signatureKey, 'Algorithm specification not satisfied.') + needs( + !!material.suite.signatureCurve === !!signatureKey, + 'Algorithm specification not satisfied.' + ) return signatureKey }, - enumerable: true + enumerable: true, }) return material } -export function decorateDecryptionMaterial> (material: T) { +export function decorateDecryptionMaterial>( + material: T +) { // Verification Key - let verificationKey: Readonly|undefined + let verificationKey: Readonly | undefined const setVerificationKey = (key: VerificationKey) => { /* Precondition: The VerificationKey stored must agree with the algorithm specification. * If this is not the case it means the MaterialManager or Keyring is not setting * the VerificationKey correctly */ - needs(material.suite.signatureCurve, 'Algorithm specification does not support signatures.') + needs( + material.suite.signatureCurve, + 'Algorithm specification does not support signatures.' + ) /* Precondition: verificationKey must not be set. Modifying the verificationKey is denied. */ needs(!verificationKey, 'Verification key has already been set.') /* Precondition: key must be a VerificationKey. */ @@ -500,32 +642,52 @@ export function decorateDecryptionMaterial> (mat * If this is not the case it means the MaterialManager or Keyring is not setting * the VerificationKey correctly */ - needs(!!material.suite.signatureCurve === !!verificationKey, 'Algorithm specification not satisfied.') + needs( + !!material.suite.signatureCurve === !!verificationKey, + 'Algorithm specification not satisfied.' + ) return verificationKey }, - enumerable: true + enumerable: true, }) return material } -export function decorateWebCryptoMaterial> (material: T, setFlags: KeyringTraceFlag) { - let cryptoKey: Readonly|undefined - - const setCryptoKey = (dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, trace: KeyringTrace) => { +export function decorateWebCryptoMaterial>( + material: T, + setFlags: KeyringTraceFlag +) { + let cryptoKey: + | Readonly + | undefined + + const setCryptoKey = ( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, + trace: KeyringTrace + ) => { /* Precondition: cryptoKey must not be set. Modifying the cryptoKey is denied */ needs(!cryptoKey, 'cryptoKey is already set.') /* Precondition: dataKey must be a supported type. */ - needs(isCryptoKey(dataKey) || isMixedBackendCryptoKey(dataKey), 'Unsupported dataKey type.') + needs( + isCryptoKey(dataKey) || isMixedBackendCryptoKey(dataKey), + 'Unsupported dataKey type.' + ) /* Precondition: The CryptoKey must match the algorithm suite specification. */ - needs(isValidCryptoKey(dataKey, material), 'CryptoKey settings not acceptable.') + needs( + isValidCryptoKey(dataKey, material), + 'CryptoKey settings not acceptable.' + ) /* If the material does not have an unencrypted data key, * then we are setting the crypto key here and need a keyring trace . */ if (!material.hasUnencryptedDataKey) { /* Precondition: If the CryptoKey is the only version, the trace information must be set here. */ - needs(trace && trace.keyName && trace.keyNamespace, 'Malformed KeyringTrace') + needs( + trace && trace.keyName && trace.keyNamespace, + 'Malformed KeyringTrace' + ) /* Precondition: On setting the CryptoKey the required KeyringTraceFlag must be set. */ needs(trace.flags & setFlags, 'Required KeyringTraceFlag not set') /* If I a setting a cryptoKey without an unencrypted data key, @@ -554,34 +716,38 @@ export function decorateWebCryptoMaterial> (mater needs(cryptoKey, 'Crypto key is not set.') // In the case of MixedBackendCryptoKey the object // has already been frozen above so it is safe to return - return >cryptoKey + return cryptoKey as Readonly } readOnlyProperty(material, 'getCryptoKey', getCryptoKey) Object.defineProperty(material, 'hasCryptoKey', { get: () => !!cryptoKey, - enumerable: true + enumerable: true, }) return material } -export function isCryptoKey (dataKey: any): dataKey is AwsEsdkJsCryptoKey { - return dataKey && +export function isCryptoKey(dataKey: any): dataKey is AwsEsdkJsCryptoKey { + return ( + dataKey && 'algorithm' in dataKey && 'type' in dataKey && 'usages' in dataKey && 'extractable' in dataKey + ) } -export function isValidCryptoKey> ( - dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, +export function isValidCryptoKey>( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, material: T -) : boolean { +): boolean { if (!isCryptoKey(dataKey)) { const { zeroByteCryptoKey, nonZeroByteCryptoKey } = dataKey - return isValidCryptoKey(zeroByteCryptoKey, material) && + return ( + isValidCryptoKey(zeroByteCryptoKey, material) && isValidCryptoKey(nonZeroByteCryptoKey, material) + ) } const { suite, validUsages } = material @@ -604,44 +770,57 @@ export function isValidCryptoKey> ( */ // Only symmetric algorithms - return type === 'secret' && + return ( + type === 'secret' && // Must match the suite ((kdf && name.toUpperCase() === kdf) || - (name.toUpperCase() === encryption && length === keyLength)) && + (name.toUpperCase() === encryption && length === keyLength)) && /* Only valid usage are: encrypt|decrypt|deriveKey * The complexity between deriveKey and suite.kdf should be handled in the Material class. */ - usages.some(u => validUsages.includes(u)) && + usages.some((u) => validUsages.includes(u)) && // Since CryptoKey can not be zeroized, not extractable is the next best thing !extractable + ) } -function isMixedBackendCryptoKey (dataKey: any): dataKey is MixedBackendCryptoKey { +function isMixedBackendCryptoKey( + dataKey: any +): dataKey is MixedBackendCryptoKey { const { zeroByteCryptoKey, nonZeroByteCryptoKey } = dataKey return isCryptoKey(zeroByteCryptoKey) && isCryptoKey(nonZeroByteCryptoKey) } -export function keyUsageForMaterial> (material: T): AwsEsdkJsKeyUsage { +export function keyUsageForMaterial>( + material: T +): AwsEsdkJsKeyUsage { const { suite } = material if (suite.kdf) return 'deriveKey' return subtleFunctionForMaterial(material) } -export function subtleFunctionForMaterial> (material: T) { +export function subtleFunctionForMaterial>( + material: T +) { if (material instanceof WebCryptoEncryptionMaterial) return 'encrypt' if (material instanceof WebCryptoDecryptionMaterial) return 'decrypt' throw new Error('Unsupported material') } -export function unwrapDataKey (dataKey: Uint8Array|AwsEsdkKeyObject): Uint8Array { +export function unwrapDataKey( + dataKey: Uint8Array | AwsEsdkKeyObject +): Uint8Array { if (dataKey instanceof Uint8Array) return dataKey - if (supportsKeyObject && dataKey instanceof supportsKeyObject.KeyObject) return dataKey.export() + if (supportsKeyObject && dataKey instanceof supportsKeyObject.KeyObject) + return dataKey.export() throw new Error('Unsupported dataKey type') } -export function wrapWithKeyObjectIfSupported (dataKey: Uint8Array|AwsEsdkKeyObject): Uint8Array|AwsEsdkKeyObject { +export function wrapWithKeyObjectIfSupported( + dataKey: Uint8Array | AwsEsdkKeyObject +): Uint8Array | AwsEsdkKeyObject { if (supportsKeyObject) { if (dataKey instanceof Uint8Array) { const ko = supportsKeyObject.createSecretKey(dataKey) diff --git a/modules/material-management/src/ecc_decode.ts b/modules/material-management/src/ecc_decode.ts index 2acd0b311..466d948b1 100644 --- a/modules/material-management/src/ecc_decode.ts +++ b/modules/material-management/src/ecc_decode.ts @@ -2,37 +2,56 @@ // SPDX-License-Identifier: Apache-2.0 import BN from 'bn.js' -import { NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' // eslint-disable-line no-unused-vars +import { NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' import { needs } from './needs' const prime256v1 = eccDecodeCompressedPoint( - new BN('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16), - new BN('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16), + new BN( + 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', + 16 + ), + new BN( + 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', + 16 + ), new BN('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16) // new BN('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16) ) const secp384r1 = eccDecodeCompressedPoint( - new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', 16), - new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', 16), - new BN('B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', 16) + new BN( + 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', + 16 + ), + new BN( + 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', + 16 + ), + new BN( + 'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', + 16 + ) // new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16) ) -type decodeNamedCurves = {[K in NodeECDHCurve|WebCryptoECDHCurve]: (compressedPoint: Uint8Array) => Uint8Array} +type decodeNamedCurves = { + [K in NodeECDHCurve | WebCryptoECDHCurve]: ( + compressedPoint: Uint8Array + ) => Uint8Array +} export const decodeNamedCurves: Readonly = Object.freeze({ // NodeJS/OpenSSL names prime256v1, secp384r1, // WebCrypto/Browser names 'P-256': prime256v1, - 'P-384': secp384r1 + 'P-384': secp384r1, }) /* * 1. This only works for prime curves * 2. This will not handle the point at infinity */ -function eccDecodeCompressedPoint (p: BN, a: BN, b: BN/*, order: BN */) { +function eccDecodeCompressedPoint(p: BN, a: BN, b: BN /*, order: BN */) { const zero = new BN(0) const one = new BN(1) const two = new BN(2) @@ -49,14 +68,17 @@ function eccDecodeCompressedPoint (p: BN, a: BN, b: BN/*, order: BN */) { const montP = BN.mont(p) const redPow = p.add(one).div(four) - const yOrderMap: {[index: number]: BN} = { + const yOrderMap: { [index: number]: BN } = { 2: zero, - 3: one + 3: one, } const compressedLength = 1 + p.bitLength() / 8 - return function decode (compressedPoint: Uint8Array) { + return function decode(compressedPoint: Uint8Array) { /* Precondition: compressedPoint must be the correct length. */ - needs(compressedPoint.byteLength === compressedLength, 'Compressed point length is not correct.') + needs( + compressedPoint.byteLength === compressedLength, + 'Compressed point length is not correct.' + ) const xBuff = compressedPoint.slice(1) const keyLength = xBuff.byteLength @@ -76,10 +98,10 @@ function eccDecodeCompressedPoint (p: BN, a: BN, b: BN/*, order: BN */) { } } -function returnBuffer (x:BN, y:BN, keyLength: number) { +function returnBuffer(x: BN, y: BN, keyLength: number) { return new Uint8Array([ 4, ...x.toArray('be', keyLength), - ...y.toArray('be', keyLength) + ...y.toArray('be', keyLength), ]) } diff --git a/modules/material-management/src/ecc_encode.ts b/modules/material-management/src/ecc_encode.ts index dbc7c92ea..6728736d5 100644 --- a/modules/material-management/src/ecc_encode.ts +++ b/modules/material-management/src/ecc_encode.ts @@ -2,13 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import BN from 'bn.js' -import { NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' // eslint-disable-line no-unused-vars +import { NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' import { needs } from './needs' const prime256v1 = eccEncodeCompressedPoint(32) const secp384r1 = eccEncodeCompressedPoint(48) -type encodeNamedCurves = {[K in NodeECDHCurve|WebCryptoECDHCurve]: (publicKey: Uint8Array) => Uint8Array} +type encodeNamedCurves = { + [K in NodeECDHCurve | WebCryptoECDHCurve]: ( + publicKey: Uint8Array + ) => Uint8Array +} export const encodeNamedCurves: Readonly = Object.freeze({ // NodeJS/OpenSSL names @@ -16,15 +20,15 @@ export const encodeNamedCurves: Readonly = Object.freeze({ secp384r1, // WebCrypto/Browser names 'P-256': prime256v1, - 'P-384': secp384r1 + 'P-384': secp384r1, }) /* * 1. This only works for prime curves * 2. This will not handle the point at infinity */ -function eccEncodeCompressedPoint (keyLength: number) { - return function encode (publicKey: Uint8Array) { +function eccEncodeCompressedPoint(keyLength: number) { + return function encode(publicKey: Uint8Array) { /* Precondition: publicKey must be the right length. * The format for the public key is [type, ...keyLength, ...keyLength] */ @@ -34,7 +38,7 @@ function eccEncodeCompressedPoint (keyLength: number) { const x = publicKey.slice(1, keyLength + 1) const y = publicKey.slice(keyLength + 1, keyLength * 2 + 1) - const yOrder = (new BN([...y])).mod(new BN(2)).toNumber() + 2 + const yOrder = new BN([...y]).mod(new BN(2)).toNumber() + 2 const compressPoint = new Uint8Array(1 + x.length) compressPoint.set([yOrder], 0) diff --git a/modules/material-management/src/encrypted_data_key.ts b/modules/material-management/src/encrypted_data_key.ts index 68251c0be..64cb2d439 100644 --- a/modules/material-management/src/encrypted_data_key.ts +++ b/modules/material-management/src/encrypted_data_key.ts @@ -1,7 +1,11 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { readOnlyBinaryProperty, readOnlyProperty, frozenClass } from './immutable_class' +import { + readOnlyBinaryProperty, + readOnlyProperty, + frozenClass, +} from './immutable_class' import { needs } from './needs' /* @@ -26,12 +30,13 @@ import { needs } from './needs' * *It is not required that the providerInfo string "equal" the binary rawInfo* * */ -export interface IEncryptedDataKey extends Readonly<{ - providerInfo: string - providerId: string - encryptedDataKey: Uint8Array - rawInfo?: Uint8Array -}>{} +export interface IEncryptedDataKey // eslint-disable-line @typescript-eslint/interface-name-prefix + extends Readonly<{ + providerInfo: string + providerId: string + encryptedDataKey: Uint8Array + rawInfo?: Uint8Array + }> {} export class EncryptedDataKey { readonly providerInfo!: string @@ -39,13 +44,17 @@ export class EncryptedDataKey { readonly encryptedDataKey!: Uint8Array readonly rawInfo?: Uint8Array - constructor (edkInput: IEncryptedDataKey) { + constructor(edkInput: IEncryptedDataKey) { const { providerInfo, providerId, encryptedDataKey, rawInfo } = edkInput needs( - typeof providerInfo === 'string' && providerInfo && - typeof providerId === 'string' && providerId && - encryptedDataKey instanceof Uint8Array && encryptedDataKey.byteLength, - 'Malformed encrypted data key') + typeof providerInfo === 'string' && + providerInfo && + typeof providerId === 'string' && + providerId && + encryptedDataKey instanceof Uint8Array && + encryptedDataKey.byteLength, + 'Malformed encrypted data key' + ) readOnlyProperty(this, 'providerInfo', providerInfo) readOnlyProperty(this, 'providerId', providerId) diff --git a/modules/material-management/src/immutable_class.ts b/modules/material-management/src/immutable_class.ts index 3b62c1be3..ed6b1858a 100644 --- a/modules/material-management/src/immutable_class.ts +++ b/modules/material-management/src/immutable_class.ts @@ -1,39 +1,49 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export function immutableClass (ObjectClass: any) { +export function immutableClass(ObjectClass: any) { Object.freeze(ObjectClass) const propertyNames = Object.getOwnPropertyNames(ObjectClass.prototype) propertyNames - .filter(name => name !== 'constructor') - .forEach(name => Object.defineProperty(ObjectClass.prototype, name, { writable: false })) + .filter((name) => name !== 'constructor') + .forEach((name) => + Object.defineProperty(ObjectClass.prototype, name, { writable: false }) + ) Object.seal(ObjectClass.prototype) return ObjectClass } -export function immutableBaseClass (ObjectClass: any) { +export function immutableBaseClass(ObjectClass: any) { Object.setPrototypeOf(ObjectClass.prototype, null) immutableClass(ObjectClass) return ObjectClass } -export function frozenClass (ObjectClass: any) { +export function frozenClass(ObjectClass: any) { Object.setPrototypeOf(ObjectClass.prototype, null) Object.freeze(ObjectClass.prototype) Object.freeze(ObjectClass) return ObjectClass } -export function readOnlyBinaryProperty (obj: any, name: string, value: Uint8Array) { +export function readOnlyBinaryProperty( + obj: any, + name: string, + value: Uint8Array +) { // should this also add a zero property? // and should it create a local value? maybe not. const safeValue = new Uint8Array(value) Object.defineProperty(obj, name, { get: () => new Uint8Array(safeValue), // inefficient, but immutable - enumerable: true + enumerable: true, }) } -export function readOnlyProperty (obj: T, name: K, value: T[K]) { +export function readOnlyProperty( + obj: T, + name: K, + value: T[K] +) { Object.defineProperty(obj, name, { value, enumerable: true }) } diff --git a/modules/material-management/src/index.ts b/modules/material-management/src/index.ts index 596c30fcd..d98611c60 100644 --- a/modules/material-management/src/index.ts +++ b/modules/material-management/src/index.ts @@ -1,10 +1,22 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { AlgorithmSuiteIdentifier, AlgorithmSuiteName, AlgorithmSuite } from './algorithm_suites' -export { AlgorithmSuiteTypeNode, AlgorithmSuiteTypeWebCrypto } from './algorithm_suites' +export { + AlgorithmSuiteIdentifier, + AlgorithmSuiteName, + AlgorithmSuite, +} from './algorithm_suites' +export { + AlgorithmSuiteTypeNode, + AlgorithmSuiteTypeWebCrypto, +} from './algorithm_suites' export { NodeEncryption, WebCryptoEncryption } from './algorithm_suites' -export { NodeHash, WebCryptoHash, NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' +export { + NodeHash, + WebCryptoHash, + NodeECDHCurve, + WebCryptoECDHCurve, +} from './algorithm_suites' export { KDF, KeyLength, IvLength, TagLength } from './algorithm_suites' export { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' @@ -15,16 +27,43 @@ export { KeyringTrace, KeyringTraceFlag } from './keyring_trace' export { MultiKeyringNode, MultiKeyringWebCrypto } from './multi_keyring' export * from './materials_manager' -export { NodeEncryptionMaterial, NodeDecryptionMaterial } from './cryptographic_material' -export { isValidCryptoKey, isCryptoKey, keyUsageForMaterial, subtleFunctionForMaterial } from './cryptographic_material' -export { WebCryptoEncryptionMaterial, WebCryptoDecryptionMaterial } from './cryptographic_material' -export { isEncryptionMaterial, isDecryptionMaterial } from './cryptographic_material' -export { unwrapDataKey, wrapWithKeyObjectIfSupported } from './cryptographic_material' -export { CryptographicMaterial, decorateCryptographicMaterial, decorateWebCryptoMaterial, WebCryptoMaterial } from './cryptographic_material' +export { + NodeEncryptionMaterial, + NodeDecryptionMaterial, +} from './cryptographic_material' +export { + isValidCryptoKey, + isCryptoKey, + keyUsageForMaterial, + subtleFunctionForMaterial, +} from './cryptographic_material' +export { + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, +} from './cryptographic_material' +export { + isEncryptionMaterial, + isDecryptionMaterial, +} from './cryptographic_material' +export { + unwrapDataKey, + wrapWithKeyObjectIfSupported, +} from './cryptographic_material' +export { + CryptographicMaterial, + decorateCryptographicMaterial, + decorateWebCryptoMaterial, + WebCryptoMaterial, +} from './cryptographic_material' export { SignatureKey, VerificationKey } from './signature_key' export { EncryptedDataKey, IEncryptedDataKey } from './encrypted_data_key' -export { immutableBaseClass, immutableClass, frozenClass, readOnlyProperty } from './immutable_class' +export { + immutableBaseClass, + immutableClass, + frozenClass, + readOnlyProperty, +} from './immutable_class' export { needs } from './needs' export { cloneMaterial } from './clone_cryptographic_material' diff --git a/modules/material-management/src/keyring.ts b/modules/material-management/src/keyring.ts index 1682acac5..f80f9aa1a 100644 --- a/modules/material-management/src/keyring.ts +++ b/modules/material-management/src/keyring.ts @@ -4,11 +4,18 @@ import { EncryptedDataKey } from './encrypted_data_key' import { immutableBaseClass, immutableClass } from './immutable_class' -import { isEncryptionMaterial, isDecryptionMaterial } from './cryptographic_material' -import { EncryptionMaterial, DecryptionMaterial, SupportedAlgorithmSuites } from './types' // eslint-disable-line no-unused-vars +import { + isEncryptionMaterial, + isDecryptionMaterial, +} from './cryptographic_material' +import { + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, +} from './types' import { needs } from './needs' -import { NodeAlgorithmSuite } from './node_algorithms' // eslint-disable-line no-unused-vars -import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disable-line no-unused-vars +import { NodeAlgorithmSuite } from './node_algorithms' +import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' /* * This public interface to the Keyring object is provided for @@ -18,7 +25,9 @@ import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disa */ export abstract class Keyring { - async onEncrypt (material: EncryptionMaterial): Promise> { + async onEncrypt( + material: EncryptionMaterial + ): Promise> { /* Precondition: material must be a type of isEncryptionMaterial. * There are several security properties that NodeEncryptionMaterial and WebCryptoEncryptionMaterial * posses. @@ -36,17 +45,22 @@ export abstract class Keyring { * the unencrypted data key and the ability to zero the data key. * This is insured by returning the same material. */ - needs(material === _material, 'New EncryptionMaterial instances can not be created.') + needs( + material === _material, + 'New EncryptionMaterial instances can not be created.' + ) /* Postcondition UNTESTED: If this keyring generated data key, it must be the right length. * See cryptographic_materials.ts This is handled in setUnencryptedDataKey * this condition is listed here to keep help keep track of important conditions - */ + */ return material } - abstract _onEncrypt(material: EncryptionMaterial): Promise> + abstract _onEncrypt( + material: EncryptionMaterial + ): Promise> /* NOTE: The order of EDK's passed to the onDecrypt function is a clear * intent on the part of the person who did the encryption. @@ -59,7 +73,10 @@ export abstract class Keyring { * who called encrypt can control the order of EDK and in the * configuration of the KMS Keyring. */ - async onDecrypt (material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> { + async onDecrypt( + material: DecryptionMaterial, + encryptedDataKeys: EncryptedDataKey[] + ): Promise> { /* Precondition: material must be DecryptionMaterial. */ needs(isDecryptionMaterial(material), 'Unsupported material type.') @@ -67,7 +84,10 @@ export abstract class Keyring { if (material.hasValidKey()) return material /* Precondition: encryptedDataKeys must all be EncryptedDataKey. */ - needs(encryptedDataKeys.every(edk => edk instanceof EncryptedDataKey), 'Unsupported EncryptedDataKey type') + needs( + encryptedDataKeys.every((edk) => edk instanceof EncryptedDataKey), + 'Unsupported EncryptedDataKey type' + ) const _material = await this._onDecrypt(material, encryptedDataKeys) @@ -77,7 +97,10 @@ export abstract class Keyring { * the unencrypted data key and the ability to zero the data key. * This is insured by returning the same material. */ - needs(material === _material, 'New DecryptionMaterial instances can not be created.') + needs( + material === _material, + 'New DecryptionMaterial instances can not be created.' + ) /* See cryptographic_materials.ts The length condition is handled there. * But the condition is important and so repeated here. @@ -89,12 +112,17 @@ export abstract class Keyring { return material } - abstract _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> + abstract _onDecrypt( + material: DecryptionMaterial, + encryptedDataKeys: EncryptedDataKey[] + ): Promise> } immutableBaseClass(Keyring) export abstract class KeyringNode extends Keyring {} immutableClass(KeyringNode) -export abstract class KeyringWebCrypto extends Keyring {} +export abstract class KeyringWebCrypto extends Keyring< + WebCryptoAlgorithmSuite +> {} immutableClass(KeyringWebCrypto) diff --git a/modules/material-management/src/keyring_trace.ts b/modules/material-management/src/keyring_trace.ts index 0744aade4..1e6617903 100644 --- a/modules/material-management/src/keyring_trace.ts +++ b/modules/material-management/src/keyring_trace.ts @@ -30,36 +30,36 @@ */ export interface KeyringTrace { - readonly keyNamespace: String - readonly keyName: String + readonly keyNamespace: string + readonly keyName: string flags: KeyringTraceFlag } export enum KeyringTraceFlag { -/** - * Bit flag indicating this wrapping key generated the data key. - */ - WRAPPING_KEY_GENERATED_DATA_KEY = 1, // eslint-disable-line no-unused-vars + /** + * Bit flag indicating this wrapping key generated the data key. + */ + WRAPPING_KEY_GENERATED_DATA_KEY = 1, -/** - * Bit flag indicating this wrapping key encrypted the data key. - */ - WRAPPING_KEY_ENCRYPTED_DATA_KEY = (1 << 1), // eslint-disable-line no-unused-vars + /** + * Bit flag indicating this wrapping key encrypted the data key. + */ + WRAPPING_KEY_ENCRYPTED_DATA_KEY = 1 << 1, -/** - * Bit flag indicating this wrapping key decrypted the data key. - */ - WRAPPING_KEY_DECRYPTED_DATA_KEY = (1 << 2), // eslint-disable-line no-unused-vars + /** + * Bit flag indicating this wrapping key decrypted the data key. + */ + WRAPPING_KEY_DECRYPTED_DATA_KEY = 1 << 2, -/** - * Bit flag indicating this wrapping key signed the encryption context. - */ - WRAPPING_KEY_SIGNED_ENC_CTX = (1 << 3), // eslint-disable-line no-unused-vars + /** + * Bit flag indicating this wrapping key signed the encryption context. + */ + WRAPPING_KEY_SIGNED_ENC_CTX = 1 << 3, -/** - * Bit flag indicating this wrapping key verified the signature of the encryption context. - */ - WRAPPING_KEY_VERIFIED_ENC_CTX = (1 << 4), // eslint-disable-line no-unused-vars + /** + * Bit flag indicating this wrapping key verified the signature of the encryption context. + */ + WRAPPING_KEY_VERIFIED_ENC_CTX = 1 << 4, /* KeyringTraceFlags are organized here. * The three groupings are set, encrypt, and decrypt. @@ -77,9 +77,10 @@ export enum KeyringTraceFlag { * for listing the WRAPPING_KEY_VERIFIED_ENC_CTX flag. */ - ENCRYPT_FLAGS = WRAPPING_KEY_ENCRYPTED_DATA_KEY | WRAPPING_KEY_SIGNED_ENC_CTX, // eslint-disable-line no-unused-vars + ENCRYPT_FLAGS = WRAPPING_KEY_ENCRYPTED_DATA_KEY | WRAPPING_KEY_SIGNED_ENC_CTX, - SET_FLAGS = WRAPPING_KEY_GENERATED_DATA_KEY | WRAPPING_KEY_DECRYPTED_DATA_KEY, // eslint-disable-line no-unused-vars + SET_FLAGS = WRAPPING_KEY_GENERATED_DATA_KEY | WRAPPING_KEY_DECRYPTED_DATA_KEY, - DECRYPT_FLAGS = WRAPPING_KEY_DECRYPTED_DATA_KEY | WRAPPING_KEY_VERIFIED_ENC_CTX, // eslint-disable-line no-unused-vars + DECRYPT_FLAGS = WRAPPING_KEY_DECRYPTED_DATA_KEY | + WRAPPING_KEY_VERIFIED_ENC_CTX, } diff --git a/modules/material-management/src/materials_manager.ts b/modules/material-management/src/materials_manager.ts index 8335425ac..3d7939d40 100644 --- a/modules/material-management/src/materials_manager.ts +++ b/modules/material-management/src/materials_manager.ts @@ -1,10 +1,14 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EncryptionRequest, DecryptionRequest } from '.' // eslint-disable-line no-unused-vars -import { EncryptionMaterial, DecryptionMaterial, SupportedAlgorithmSuites } from './types' // eslint-disable-line no-unused-vars -import { NodeAlgorithmSuite } from './node_algorithms' // eslint-disable-line no-unused-vars -import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disable-line no-unused-vars +import { EncryptionRequest, DecryptionRequest } from '.' +import { + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, +} from './types' +import { NodeAlgorithmSuite } from './node_algorithms' +import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' /* * This public interface to the MaterialsManager object is provided for @@ -26,5 +30,7 @@ export interface MaterialsManager { decryptMaterials: GetDecryptMaterials } -export interface NodeMaterialsManager extends MaterialsManager {} -export interface WebCryptoMaterialsManager extends MaterialsManager {} +export interface NodeMaterialsManager + extends MaterialsManager {} +export interface WebCryptoMaterialsManager + extends MaterialsManager {} diff --git a/modules/material-management/src/multi_keyring.ts b/modules/material-management/src/multi_keyring.ts index e95f4c8fe..8749624c9 100644 --- a/modules/material-management/src/multi_keyring.ts +++ b/modules/material-management/src/multi_keyring.ts @@ -2,21 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 import { immutableClass, readOnlyProperty } from './immutable_class' +import { Keyring, KeyringNode, KeyringWebCrypto } from './keyring' import { - Keyring, // eslint-disable-line no-unused-vars - KeyringNode, - KeyringWebCrypto -} from './keyring' -import { SupportedAlgorithmSuites, EncryptionMaterial, DecryptionMaterial } from './types' // eslint-disable-line no-unused-vars + SupportedAlgorithmSuites, + EncryptionMaterial, + DecryptionMaterial, +} from './types' import { needs } from './needs' -import { EncryptedDataKey } from './encrypted_data_key' // eslint-disable-line no-unused-vars -import { NodeAlgorithmSuite } from './node_algorithms' // eslint-disable-line no-unused-vars -import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disable-line no-unused-vars +import { EncryptedDataKey } from './encrypted_data_key' +import { NodeAlgorithmSuite } from './node_algorithms' +import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' -export class MultiKeyringNode extends KeyringNode implements IMultiKeyring { +export class MultiKeyringNode extends KeyringNode + implements MultiKeyring { public readonly generator?: KeyringNode public readonly children!: ReadonlyArray - constructor (input: MultiKeyringInput) { + constructor(input: MultiKeyringInput) { super() decorateProperties(this, KeyringNode, input) } @@ -25,11 +26,12 @@ export class MultiKeyringNode extends KeyringNode implements IMultiKeyring { +export class MultiKeyringWebCrypto extends KeyringWebCrypto + implements MultiKeyring { public readonly generator?: KeyringWebCrypto public readonly children!: ReadonlyArray - constructor (input: MultiKeyringInput) { + constructor(input: MultiKeyringInput) { super() decorateProperties(this, KeyringWebCrypto, input) } @@ -38,25 +40,31 @@ export class MultiKeyringWebCrypto extends KeyringWebCrypto implements IMultiKey } immutableClass(MultiKeyringWebCrypto) -function decorateProperties ( - obj: IMultiKeyring, +function decorateProperties( + obj: MultiKeyring, BaseKeyring: any, { generator, children = [] }: MultiKeyringInput ) { /* Precondition: MultiKeyring must have keyrings. */ needs(generator || children.length, 'Noop MultiKeyring is not supported.') /* Precondition: generator must be a Keyring. */ - needs(!!generator === generator instanceof BaseKeyring, 'Generator must be a Keyring') + needs( + !!generator === generator instanceof BaseKeyring, + 'Generator must be a Keyring' + ) /* Precondition: All children must be Keyrings. */ - needs(children.every(kr => kr instanceof BaseKeyring), 'Child must be a Keyring') + needs( + children.every((kr) => kr instanceof BaseKeyring), + 'Child must be a Keyring' + ) readOnlyProperty(obj, 'children', Object.freeze(children.slice())) readOnlyProperty(obj, 'generator', generator) } -function buildPrivateOnEncrypt () { - return async function _onEncrypt ( - this: IMultiKeyring, +function buildPrivateOnEncrypt() { + return async function _onEncrypt( + this: MultiKeyring, material: EncryptionMaterial ): Promise> { /* Precondition: Only Keyrings explicitly designated as generators can generate material. @@ -66,14 +74,20 @@ function buildPrivateOnEncrypt () { * then generated.hasUnencryptedDataKey === false will throw. * But this is a much more meaningful error. */ - needs(!material.hasUnencryptedDataKey ? this.generator : true, 'Only Keyrings explicitly designated as generators can generate material.') + needs( + !material.hasUnencryptedDataKey ? this.generator : true, + 'Only Keyrings explicitly designated as generators can generate material.' + ) const generated = this.generator ? await this.generator.onEncrypt(material) : material /* Precondition: A Generator Keyring *must* ensure generated material. */ - needs(generated.hasUnencryptedDataKey, 'Generator Keyring has not generated material.') + needs( + generated.hasUnencryptedDataKey, + 'Generator Keyring has not generated material.' + ) /* By default this is a serial operation. A keyring _may_ perform an expensive operation * or create resource constraints such that encrypting with multiple keyrings could @@ -92,28 +106,28 @@ function buildPrivateOnEncrypt () { } } -function buildPrivateOnDecrypt () { - return async function _onDecrypt ( - this: IMultiKeyring, +function buildPrivateOnDecrypt() { + return async function _onDecrypt( + this: MultiKeyring, material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[] ): Promise> { const children = this.children.slice() if (this.generator) children.unshift(this.generator) - let childKeyringErrors: Error[] = [] + const childKeyringErrors: Error[] = [] for (const keyring of children) { - /* Check for early return (Postcondition): Do not attempt to decrypt once I have a valid key. */ + /* Check for early return (Postcondition): Do not attempt to decrypt once I have a valid key. */ if (material.hasValidKey()) return material try { await keyring.onDecrypt(material, encryptedDataKeys) } catch (e) { - /* Failures onDecrypt should not short-circuit the process - * If the caller does not have access they may have access - * through another Keyring. - */ + /* Failures onDecrypt should not short-circuit the process + * If the caller does not have access they may have access + * through another Keyring. + */ childKeyringErrors.push(e) } } @@ -127,10 +141,14 @@ function buildPrivateOnDecrypt () { * the material does not have an unencrypted data key. * So I return a concatenated Error message */ - needs(material.hasValidKey() || (!material.hasValidKey() && !childKeyringErrors.length) - , childKeyringErrors - .reduce((m, e, i) => `${m} Error #${i + 1} \n ${e.stack} \n`, - 'Unable to decrypt data key and one or more child keyrings had an error. \n ')) + needs( + material.hasValidKey() || + (!material.hasValidKey() && !childKeyringErrors.length), + childKeyringErrors.reduce( + (m, e, i) => `${m} Error #${i + 1} \n ${e.stack} \n`, + 'Unable to decrypt data key and one or more child keyrings had an error. \n ' + ) + ) return material } @@ -141,7 +159,7 @@ interface MultiKeyringInput { children?: Keyring[] } -interface IMultiKeyring extends Keyring { +interface MultiKeyring extends Keyring { generator?: Keyring children: ReadonlyArray> } diff --git a/modules/material-management/src/needs.ts b/modules/material-management/src/needs.ts index 79ed90ae2..5c62a662b 100644 --- a/modules/material-management/src/needs.ts +++ b/modules/material-management/src/needs.ts @@ -2,17 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 /* Preconditions, postconditions, and loop invariants are very - * useful for safe programing. They also document the specifications. - * This function is to help simplify the semantic burden of parsing - * these constructions. - * - * Instead of constructions like - * if (!goodCondition) throw new Error('condition not true') - * - * needs(goodCondition, 'condition not true') - */ + * useful for safe programing. They also document the specifications. + * This function is to help simplify the semantic burden of parsing + * these constructions. + * + * Instead of constructions like + * if (!goodCondition) throw new Error('condition not true') + * + * needs(goodCondition, 'condition not true') + */ -export function needs (condition: any, errorMessage: string, Err: ErrorConstructor = Error) { +export function needs( + condition: any, + errorMessage: string, + Err: ErrorConstructor = Error +) { if (!condition) { throw new Err(errorMessage) } diff --git a/modules/material-management/src/node_algorithms.ts b/modules/material-management/src/node_algorithms.ts index 982e6bf2e..d3659687b 100644 --- a/modules/material-management/src/node_algorithms.ts +++ b/modules/material-management/src/node_algorithms.ts @@ -1,4 +1,3 @@ - // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 @@ -13,8 +12,13 @@ */ import { - AlgorithmSuite, AlgorithmSuiteIdentifier, - INodeAlgorithmSuite, NodeEncryption, NodeHash, NodeECDHCurve, AlgorithmSuiteTypeNode // eslint-disable-line no-unused-vars + AlgorithmSuite, + AlgorithmSuiteIdentifier, + INodeAlgorithmSuite, + NodeEncryption, + NodeHash, + NodeECDHCurve, + AlgorithmSuiteTypeNode, } from './algorithm_suites' /* References to https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/algorithms-reference.html @@ -28,7 +32,7 @@ const nodeAlgAes128GcmIv12Tag16: INodeAlgorithmSuite = { keyLength: 128, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } const nodeAlgAes192GcmIv12Tag16: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16, @@ -36,7 +40,7 @@ const nodeAlgAes192GcmIv12Tag16: INodeAlgorithmSuite = { keyLength: 192, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } const nodeAlgAes256GcmIv12Tag16: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16, @@ -44,7 +48,7 @@ const nodeAlgAes256GcmIv12Tag16: INodeAlgorithmSuite = { keyLength: 256, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } const nodeAlgAes128GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256, @@ -54,7 +58,7 @@ const nodeAlgAes128GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'sha256', - cacheSafe: true + cacheSafe: true, } const nodeAlgAes192GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256, @@ -64,7 +68,7 @@ const nodeAlgAes192GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'sha256', - cacheSafe: true + cacheSafe: true, } const nodeAlgAes256GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256, @@ -74,7 +78,7 @@ const nodeAlgAes256GcmIv12Tag16HkdfSha256: INodeAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'sha256', - cacheSafe: true + cacheSafe: true, } const nodeAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256, @@ -86,7 +90,7 @@ const nodeAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: INodeAlgorithmSuite = { kdfHash: 'sha256', cacheSafe: true, signatureCurve: 'prime256v1', - signatureHash: 'sha256' + signatureHash: 'sha256', } const nodeAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -98,7 +102,7 @@ const nodeAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: INodeAlgorithmSuite = { kdfHash: 'sha384', cacheSafe: true, signatureCurve: 'secp384r1', - signatureHash: 'sha384' + signatureHash: 'sha384', } const nodeAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: INodeAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -110,29 +114,50 @@ const nodeAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: INodeAlgorithmSuite = { kdfHash: 'sha384', cacheSafe: true, signatureCurve: 'secp384r1', - signatureHash: 'sha384' + signatureHash: 'sha384', } -type NodeAlgorithms = Readonly<{[id in AlgorithmSuiteIdentifier]: INodeAlgorithmSuite}> +type NodeAlgorithms = Readonly< + { [id in AlgorithmSuiteIdentifier]: INodeAlgorithmSuite } +> const nodeAlgorithms: NodeAlgorithms = Object.freeze({ - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze(nodeAlgAes128GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze(nodeAlgAes192GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze(nodeAlgAes256GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(nodeAlgAes128GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(nodeAlgAes192GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(nodeAlgAes256GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze(nodeAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(nodeAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(nodeAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384) + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze( + nodeAlgAes128GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze( + nodeAlgAes192GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze( + nodeAlgAes256GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + nodeAlgAes128GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + nodeAlgAes192GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + nodeAlgAes256GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze( + nodeAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze( + nodeAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze( + nodeAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 + ), }) -export class NodeAlgorithmSuite extends AlgorithmSuite implements INodeAlgorithmSuite { +export class NodeAlgorithmSuite extends AlgorithmSuite + implements INodeAlgorithmSuite { encryption!: NodeEncryption kdfHash?: NodeHash signatureCurve?: NodeECDHCurve signatureHash?: NodeHash type: AlgorithmSuiteTypeNode = 'node' - constructor (id: AlgorithmSuiteIdentifier) { + constructor(id: AlgorithmSuiteIdentifier) { super(nodeAlgorithms[id]) Object.setPrototypeOf(this, NodeAlgorithmSuite.prototype) Object.freeze(this) diff --git a/modules/material-management/src/pem_helpers.ts b/modules/material-management/src/pem_helpers.ts index a69ffa0b0..9cb8aa829 100644 --- a/modules/material-management/src/pem_helpers.ts +++ b/modules/material-management/src/pem_helpers.ts @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 /* Before Node.js v11 the crypto module did not support - * a method to PEM format a ECDH key. It has always supported - * producing such keys: `crypto.createECDH`. But formating - * these keys as a PEM for use in `crypto.Sign` and - * `crypto.Verify` has not been possible in native `crypto`. - * As Node.js v6, v8, and v10 reach end of life, this code - * can be deleted. - */ + * a method to PEM format a ECDH key. It has always supported + * producing such keys: `crypto.createECDH`. But formating + * these keys as a PEM for use in `crypto.Sign` and + * `crypto.Verify` has not been possible in native `crypto`. + * As Node.js v6, v8, and v10 reach end of life, this code + * can be deleted. + */ // @ts-ignore import asn from 'asn1.js' @@ -19,7 +19,7 @@ const Rfc5915Key = asn.define('Rfc5915Key', function (this: any) { this.key('privateKey').octstr(), this.key('parameters').optional().explicit(0).objid({ '1 2 840 10045 3 1 7': 'prime256v1', - '1 3 132 0 34': 'secp384r1' + '1 3 132 0 34': 'secp384r1', }), this.key('publicKey').optional().explicit(1).bitstr() ) @@ -27,53 +27,65 @@ const Rfc5915Key = asn.define('Rfc5915Key', function (this: any) { const SpkiKey = asn.define('SpkiKey', function (this: any) { this.seq().obj( - this.key('algorithmIdentifier').seq().obj( - this.key('publicKeyType').objid({ - '1 2 840 10045 2 1': 'EC' - }), - this.key('parameters').objid({ - '1 2 840 10045 3 1 7': 'prime256v1', - '1 3 132 0 34': 'secp384r1' - }) - ), + this.key('algorithmIdentifier') + .seq() + .obj( + this.key('publicKeyType').objid({ + '1 2 840 10045 2 1': 'EC', + }), + this.key('parameters').objid({ + '1 2 840 10045 3 1 7': 'prime256v1', + '1 3 132 0 34': 'secp384r1', + }) + ), this.key('publicKey').bitstr() ) }) -export function publicKeyPem (curve: string, publicKey: Buffer) { - const buff: Buffer = SpkiKey.encode({ - algorithmIdentifier: { - publicKeyType: 'EC', - parameters: curve +export function publicKeyPem(curve: string, publicKey: Buffer) { + const buff: Buffer = SpkiKey.encode( + { + algorithmIdentifier: { + publicKeyType: 'EC', + parameters: curve, + }, + publicKey: { data: publicKey }, }, - publicKey: { data: publicKey } - }, 'der') + 'der' + ) return [ '-----BEGIN PUBLIC KEY-----', ...chunk64(buff), '-----END PUBLIC KEY-----', - '' + '', ].join('\n') } -export function privateKeyPem (curve: string, privateKey: Buffer, publicKey: Buffer) { - const buff: Buffer = Rfc5915Key.encode({ - version: 1, - privateKey: privateKey, - parameters: curve, - publicKey: { data: publicKey } - }, 'der') +export function privateKeyPem( + curve: string, + privateKey: Buffer, + publicKey: Buffer +) { + const buff: Buffer = Rfc5915Key.encode( + { + version: 1, + privateKey: privateKey, + parameters: curve, + publicKey: { data: publicKey }, + }, + 'der' + ) return [ '-----BEGIN EC PRIVATE KEY-----', ...chunk64(buff), '-----END EC PRIVATE KEY-----', - '' + '', ].join('\n') } -export function chunk64 (buff: Buffer) { +export function chunk64(buff: Buffer) { const chunkSize = 64 const str = buff.toString('base64') const numChunks = Math.ceil(str.length / chunkSize) diff --git a/modules/material-management/src/signature_key.ts b/modules/material-management/src/signature_key.ts index 26864f339..479466879 100644 --- a/modules/material-management/src/signature_key.ts +++ b/modules/material-management/src/signature_key.ts @@ -1,12 +1,20 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AlgorithmSuite, NodeECDHCurve, WebCryptoECDHCurve } from './algorithm_suites' // eslint-disable-line no-unused-vars +import { + AlgorithmSuite, + NodeECDHCurve, + WebCryptoECDHCurve, +} from './algorithm_suites' import { encodeNamedCurves } from './ecc_encode' import { decodeNamedCurves } from './ecc_decode' -import { frozenClass, readOnlyBinaryProperty, readOnlyProperty } from './immutable_class' +import { + frozenClass, + readOnlyBinaryProperty, + readOnlyProperty, +} from './immutable_class' import { publicKeyPem, privateKeyPem } from './pem_helpers' -import { AwsEsdkJsCryptoKey } from './types' // eslint-disable-line no-unused-vars +import { AwsEsdkJsCryptoKey } from './types' /* * This public interface to the SignatureKey object is provided for @@ -16,10 +24,14 @@ import { AwsEsdkJsCryptoKey } from './types' // eslint-disable-line no-unused-va */ export class SignatureKey { - public readonly privateKey!: string|AwsEsdkJsCryptoKey + public readonly privateKey!: string | AwsEsdkJsCryptoKey public readonly compressPoint!: Uint8Array - public readonly signatureCurve!: NodeECDHCurve|WebCryptoECDHCurve - constructor (privateKey: Uint8Array|AwsEsdkJsCryptoKey, compressPoint: Uint8Array, suite: AlgorithmSuite) { + public readonly signatureCurve!: NodeECDHCurve | WebCryptoECDHCurve + constructor( + privateKey: Uint8Array | AwsEsdkJsCryptoKey, + compressPoint: Uint8Array, + suite: AlgorithmSuite + ) { const { signatureCurve: namedCurve } = suite /* Precondition: Do not create a SignatureKey for an algorithm suite that does not have an EC named curve. */ if (!namedCurve) throw new Error('Unsupported Algorithm') @@ -31,10 +43,18 @@ export class SignatureKey { * _only_ the raw points. */ if (privateKey instanceof Uint8Array) { - const pem = privateKeyPem(namedCurve, fromBuffer(privateKey), fromBuffer(compressPoint)) + const pem = privateKeyPem( + namedCurve, + fromBuffer(privateKey), + fromBuffer(compressPoint) + ) readOnlyProperty(this, 'privateKey', pem) } else { - readOnlyProperty(this, 'privateKey', privateKey) + readOnlyProperty( + this, + 'privateKey', + privateKey + ) } readOnlyBinaryProperty(this, 'compressPoint', compressPoint) readOnlyProperty(this, 'signatureCurve', namedCurve) @@ -42,7 +62,10 @@ export class SignatureKey { Object.freeze(this) } - static encodeCompressPoint (publicKeyBytes: Uint8Array, suite: AlgorithmSuite) { + static encodeCompressPoint( + publicKeyBytes: Uint8Array, + suite: AlgorithmSuite + ) { const { signatureCurve: namedCurve } = suite /* Precondition: Do not return a compress point for an algorithm suite that does not have an EC named curve. */ if (!namedCurve) throw new Error('Unsupported Algorithm') @@ -52,9 +75,12 @@ export class SignatureKey { frozenClass(SignatureKey) export class VerificationKey { - public readonly publicKey!: string|AwsEsdkJsCryptoKey - public readonly signatureCurve!: NodeECDHCurve|WebCryptoECDHCurve - constructor (publicKey: Uint8Array|AwsEsdkJsCryptoKey, suite: AlgorithmSuite) { + public readonly publicKey!: string | AwsEsdkJsCryptoKey + public readonly signatureCurve!: NodeECDHCurve | WebCryptoECDHCurve + constructor( + publicKey: Uint8Array | AwsEsdkJsCryptoKey, + suite: AlgorithmSuite + ) { const { signatureCurve: namedCurve } = suite /* Precondition: Do not create a VerificationKey for an algorithm suite that does not have an EC named curve. */ if (!namedCurve) throw new Error('Unsupported Algorithm') @@ -69,14 +95,18 @@ export class VerificationKey { const pem = publicKeyPem(namedCurve, fromBuffer(publicKey)) readOnlyProperty(this, 'publicKey', pem) } else { - readOnlyProperty(this, 'publicKey', publicKey) + readOnlyProperty( + this, + 'publicKey', + publicKey + ) } readOnlyProperty(this, 'signatureCurve', namedCurve) Object.setPrototypeOf(this, VerificationKey.prototype) Object.freeze(this) } - static decodeCompressPoint (compressPoint: Uint8Array, suite: AlgorithmSuite) { + static decodeCompressPoint(compressPoint: Uint8Array, suite: AlgorithmSuite) { const { signatureCurve: namedCurve } = suite /* Precondition: Do not decode a public key for an algorithm suite that does not have an EC named curve. */ if (!namedCurve) throw new Error('Unsupported Algorithm') @@ -86,7 +116,7 @@ export class VerificationKey { } frozenClass(VerificationKey) -function fromBuffer (uint: Uint8Array) { +function fromBuffer(uint: Uint8Array) { const { buffer, byteOffset, byteLength } = uint return Buffer.from(buffer, byteOffset, byteLength) } diff --git a/modules/material-management/src/types.ts b/modules/material-management/src/types.ts index 452e47f09..0f2bdc6c4 100644 --- a/modules/material-management/src/types.ts +++ b/modules/material-management/src/types.ts @@ -1,22 +1,32 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { NodeAlgorithmSuite } from './node_algorithms' // eslint-disable-line no-unused-vars -import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disable-line no-unused-vars -import { EncryptedDataKey } from './encrypted_data_key' // eslint-disable-line no-unused-vars +import { NodeAlgorithmSuite } from './node_algorithms' +import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' +import { EncryptedDataKey } from './encrypted_data_key' import { - NodeEncryptionMaterial, WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - NodeDecryptionMaterial, WebCryptoDecryptionMaterial // eslint-disable-line no-unused-vars + NodeEncryptionMaterial, + WebCryptoEncryptionMaterial, + NodeDecryptionMaterial, + WebCryptoDecryptionMaterial, } from './cryptographic_material' -export type EncryptionContext = {[index: string]: string} +export type EncryptionContext = { [index: string]: string } /* need to copy some things from DOM */ export interface AwsEsdkJsKeyAlgorithm { name: string } export type AwsEsdkJsKeyType = 'public' | 'private' | 'secret' -export type AwsEsdkJsKeyUsage = 'encrypt' | 'decrypt' | 'sign' | 'verify' | 'deriveKey' | 'deriveBits' | 'wrapKey' | 'unwrapKey' +export type AwsEsdkJsKeyUsage = + | 'encrypt' + | 'decrypt' + | 'sign' + | 'verify' + | 'deriveKey' + | 'deriveBits' + | 'wrapKey' + | 'unwrapKey' export interface AwsEsdkJsCryptoKey { readonly algorithm: AwsEsdkJsKeyAlgorithm @@ -30,29 +40,37 @@ export type MixedBackendCryptoKey = { zeroByteCryptoKey: AwsEsdkJsCryptoKey } -export interface EncryptionRequest { +export interface EncryptionRequest< + S extends NodeAlgorithmSuite | WebCryptoAlgorithmSuite +> { readonly suite?: S readonly encryptionContext: EncryptionContext readonly plaintextLength?: number } -export interface DecryptionRequest { +export interface DecryptionRequest< + S extends NodeAlgorithmSuite | WebCryptoAlgorithmSuite +> { readonly suite: S readonly encryptionContext: EncryptionContext readonly encryptedDataKeys: ReadonlyArray } -export type SupportedAlgorithmSuites = NodeAlgorithmSuite|WebCryptoAlgorithmSuite +export type SupportedAlgorithmSuites = + | NodeAlgorithmSuite + | WebCryptoAlgorithmSuite -export type EncryptionMaterial = - Suite extends NodeAlgorithmSuite ? NodeEncryptionMaterial : - Suite extends WebCryptoAlgorithmSuite ? WebCryptoEncryptionMaterial : - never +export type EncryptionMaterial = Suite extends NodeAlgorithmSuite + ? NodeEncryptionMaterial + : Suite extends WebCryptoAlgorithmSuite + ? WebCryptoEncryptionMaterial + : never -export type DecryptionMaterial = - Suite extends NodeAlgorithmSuite ? NodeDecryptionMaterial : - Suite extends WebCryptoAlgorithmSuite ? WebCryptoDecryptionMaterial : - never +export type DecryptionMaterial = Suite extends NodeAlgorithmSuite + ? NodeDecryptionMaterial + : Suite extends WebCryptoAlgorithmSuite + ? WebCryptoDecryptionMaterial + : never /* These are copies of the v12 Node.js types. * I copied them here to avoid exporting v12 types diff --git a/modules/material-management/src/web_crypto_algorithms.ts b/modules/material-management/src/web_crypto_algorithms.ts index d09bf27c1..1e9d0113b 100644 --- a/modules/material-management/src/web_crypto_algorithms.ts +++ b/modules/material-management/src/web_crypto_algorithms.ts @@ -12,9 +12,13 @@ */ import { - AlgorithmSuite, AlgorithmSuiteIdentifier, - IWebCryptoAlgorithmSuite, WebCryptoEncryption, WebCryptoHash, // eslint-disable-line no-unused-vars - WebCryptoECDHCurve, AlgorithmSuiteTypeWebCrypto // eslint-disable-line no-unused-vars + AlgorithmSuite, + AlgorithmSuiteIdentifier, + IWebCryptoAlgorithmSuite, + WebCryptoEncryption, + WebCryptoHash, + WebCryptoECDHCurve, + AlgorithmSuiteTypeWebCrypto, } from './algorithm_suites' import { needs } from './needs' @@ -28,7 +32,7 @@ const webCryptoAlgAes128GcmIv12Tag16: IWebCryptoAlgorithmSuite = { keyLength: 128, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } /* Web browsers do not support 192 bit key lengths at this time. */ const webCryptoAlgAes192GcmIv12Tag16: IWebCryptoAlgorithmSuite = { @@ -37,7 +41,7 @@ const webCryptoAlgAes192GcmIv12Tag16: IWebCryptoAlgorithmSuite = { keyLength: 192, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } const webCryptoAlgAes256GcmIv12Tag16: IWebCryptoAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16, @@ -45,7 +49,7 @@ const webCryptoAlgAes256GcmIv12Tag16: IWebCryptoAlgorithmSuite = { keyLength: 256, ivLength: 12, tagLength: 128, - cacheSafe: false + cacheSafe: false, } const webCryptoAlgAes128GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256, @@ -55,7 +59,7 @@ const webCryptoAlgAes128GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'SHA-256', - cacheSafe: true + cacheSafe: true, } /* Web browsers do not support 192 bit key lengths at this time. */ const webCryptoAlgAes192GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { @@ -66,7 +70,7 @@ const webCryptoAlgAes192GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'SHA-256', - cacheSafe: true + cacheSafe: true, } const webCryptoAlgAes256GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256, @@ -76,7 +80,7 @@ const webCryptoAlgAes256GcmIv12Tag16HkdfSha256: IWebCryptoAlgorithmSuite = { tagLength: 128, kdf: 'HKDF', kdfHash: 'SHA-256', - cacheSafe: true + cacheSafe: true, } const webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: IWebCryptoAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256, @@ -88,7 +92,7 @@ const webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: IWebCryptoAlgorithmSuit kdfHash: 'SHA-256', cacheSafe: true, signatureCurve: 'P-256', - signatureHash: 'SHA-256' + signatureHash: 'SHA-256', } /* Web browsers do not support 192 bit key lengths at this time. */ const webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: IWebCryptoAlgorithmSuite = { @@ -101,7 +105,7 @@ const webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: IWebCryptoAlgorithmSuit kdfHash: 'SHA-384', cacheSafe: true, signatureCurve: 'P-384', - signatureHash: 'SHA-384' + signatureHash: 'SHA-384', } const webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: IWebCryptoAlgorithmSuite = { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -113,20 +117,40 @@ const webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: IWebCryptoAlgorithmSuit kdfHash: 'SHA-384', cacheSafe: true, signatureCurve: 'P-384', - signatureHash: 'SHA-384' + signatureHash: 'SHA-384', } -type WebCryptoAlgorithms = Readonly<{[id in AlgorithmSuiteIdentifier]: IWebCryptoAlgorithmSuite}> +type WebCryptoAlgorithms = Readonly< + { [id in AlgorithmSuiteIdentifier]: IWebCryptoAlgorithmSuite } +> const webCryptoAlgorithms: WebCryptoAlgorithms = Object.freeze({ - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256), - [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384) + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze( + webCryptoAlgAes192GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + webCryptoAlgAes192GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256 + ), + [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze( + webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384 + ), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 + ), }) /* Web browsers do not support 192 bit key lengths at this time. @@ -134,36 +158,62 @@ const webCryptoAlgorithms: WebCryptoAlgorithms = Object.freeze({ * I need to have the same list of AlgorithmSuiteIdentifier. * This list is maintained here to make sure that the error message is helpful. */ -type WebCryptoAlgorithmSuiteIdentifier = Exclude, - AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256>, - AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384> -type SupportedWebCryptoAlgorithms = Readonly<{[id in WebCryptoAlgorithmSuiteIdentifier]: IWebCryptoAlgorithmSuite}> -const supportedWebCryptoAlgorithms: SupportedWebCryptoAlgorithms = Object.freeze({ - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16), - // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16HkdfSha256), - // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16HkdfSha256), - [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze(webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256), - // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384), - [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384) -}) +type WebCryptoAlgorithmSuiteIdentifier = Exclude< + Exclude< + Exclude< + AlgorithmSuiteIdentifier, + AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16 + >, + AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256 + >, + AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 +> +type SupportedWebCryptoAlgorithms = Readonly< + { [id in WebCryptoAlgorithmSuiteIdentifier]: IWebCryptoAlgorithmSuite } +> +const supportedWebCryptoAlgorithms: SupportedWebCryptoAlgorithms = Object.freeze( + { + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16 + ), + // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16HkdfSha256 + ), + // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha256), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16HkdfSha256 + ), + [AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256]: Object.freeze( + webCryptoAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256 + ), + // [AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze(webCryptoAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384), + [AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384]: Object.freeze( + webCryptoAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 + ), + } +) -export class WebCryptoAlgorithmSuite extends AlgorithmSuite implements IWebCryptoAlgorithmSuite { +export class WebCryptoAlgorithmSuite extends AlgorithmSuite + implements IWebCryptoAlgorithmSuite { encryption!: WebCryptoEncryption kdfHash?: WebCryptoHash signatureCurve?: WebCryptoECDHCurve signatureHash?: WebCryptoHash type: AlgorithmSuiteTypeWebCrypto = 'webCrypto' - constructor (id: AlgorithmSuiteIdentifier) { + constructor(id: AlgorithmSuiteIdentifier) { super(webCryptoAlgorithms[id]) /* Precondition: Browsers do not support 192 bit keys so the AlgorithmSuiteIdentifier is removed. * This is primarily an error in decrypt but this make it clear. * The error can manifest deep in the decrypt loop making it hard to debug. */ - needs(supportedWebCryptoAlgorithms.hasOwnProperty(id), '192-bit AES keys are not supported') + needs( + Object.prototype.hasOwnProperty.call(supportedWebCryptoAlgorithms, id), + '192-bit AES keys are not supported' + ) Object.setPrototypeOf(this, WebCryptoAlgorithmSuite.prototype) Object.freeze(this) } diff --git a/modules/raw-aes-keyring-browser/package.json b/modules/raw-aes-keyring-browser/package.json index 6112f7167..22f5c753d 100644 --- a/modules/raw-aes-keyring-browser/package.json +++ b/modules/raw-aes-keyring-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/raw-aes-keyring-browser/src/index.ts b/modules/raw-aes-keyring-browser/src/index.ts index 57c608edc..e0c04f1a2 100644 --- a/modules/raw-aes-keyring-browser/src/index.ts +++ b/modules/raw-aes-keyring-browser/src/index.ts @@ -1,5 +1,8 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { RawAesWrappingSuiteIdentifier, WrappingSuiteIdentifier } from '@aws-crypto/raw-keyring' +export { + RawAesWrappingSuiteIdentifier, + WrappingSuiteIdentifier, +} from '@aws-crypto/raw-keyring' export * from './raw_aes_keyring_browser' diff --git a/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts b/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts index c660ff15e..7532e4236 100644 --- a/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts +++ b/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts @@ -4,47 +4,54 @@ import { KeyringWebCrypto, needs, - WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, + EncryptedDataKey, KeyringTraceFlag, immutableClass, readOnlyProperty, - WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars + WebCryptoAlgorithmSuite, getSubtleFunction, _importCryptoKey, unwrapDataKey, importForWebCryptoEncryptionMaterial, importForWebCryptoDecryptionMaterial, - AwsEsdkJsCryptoKey // eslint-disable-line no-unused-vars + AwsEsdkJsCryptoKey, } from '@aws-crypto/material-management-browser' -import { - serializeFactory, - concatBuffers -} from '@aws-crypto/serialize' +import { serializeFactory, concatBuffers } from '@aws-crypto/serialize' import { _onEncrypt, _onDecrypt, WebCryptoRawAesMaterial, rawAesEncryptedDataKeyFactory, rawAesEncryptedPartsFactory, - WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars - WrapKey, // eslint-disable-line no-unused-vars - UnwrapKey // eslint-disable-line no-unused-vars + WrappingSuiteIdentifier, + WrapKey, + UnwrapKey, } from '@aws-crypto/raw-keyring' import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-browser' import { randomValuesOnly } from '@aws-crypto/random-source-browser' -import { getWebCryptoBackend, getNonZeroByteBackend } from '@aws-crypto/web-crypto-backend' +import { + getWebCryptoBackend, + getNonZeroByteBackend, +} from '@aws-crypto/web-crypto-backend' const { serializeEncryptionContext } = serializeFactory(fromUtf8) -const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory(toUtf8, fromUtf8) +const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory( + toUtf8, + fromUtf8 +) const { rawAesEncryptedParts } = rawAesEncryptedPartsFactory(fromUtf8) -const encryptFlags = KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_SIGNED_ENC_CTX -const decryptFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX +const encryptFlags = + KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY | + KeyringTraceFlag.WRAPPING_KEY_SIGNED_ENC_CTX +const decryptFlags = + KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | + KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX export type RawAesKeyringWebCryptoInput = { keyNamespace: string keyName: string - masterKey: AwsEsdkJsCryptoKey, + masterKey: AwsEsdkJsCryptoKey wrappingSuite: WrappingSuiteIdentifier } @@ -54,7 +61,7 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { _wrapKey!: WrapKey _unwrapKey!: UnwrapKey - constructor (input: RawAesKeyringWebCryptoInput) { + constructor(input: RawAesKeyringWebCryptoInput) { super() const { keyName, keyNamespace, masterKey, wrappingSuite } = input /* Precondition: AesKeyringWebCrypto needs identifying information for encrypt and decrypt. */ @@ -65,7 +72,11 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { * Note: the KeyringTrace and flag are _only_ set because I am reusing an existing implementation. * See: raw_aes_material.ts in @aws-crypto/raw-keyring for details */ - .setCryptoKey(masterKey, { keyNamespace, keyName, flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) + .setCryptoKey(masterKey, { + keyNamespace, + keyName, + flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY, + }) const _wrapKey = async (material: WebCryptoEncryptionMaterial) => { /* The AAD section is uInt16BE(length) + AAD @@ -73,22 +84,42 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { * However, the RAW Keyring wants _only_ the ADD. * So, I just slice off the length. */ - const aad = serializeEncryptionContext(material.encryptionContext).slice(2) + const aad = serializeEncryptionContext(material.encryptionContext).slice( + 2 + ) const { keyNamespace, keyName } = this - return aesGcmWrapKey(keyNamespace, keyName, material, aad, wrappingMaterial) + return aesGcmWrapKey( + keyNamespace, + keyName, + material, + aad, + wrappingMaterial + ) } - const _unwrapKey = async (material: WebCryptoDecryptionMaterial, edk: EncryptedDataKey) => { + const _unwrapKey = async ( + material: WebCryptoDecryptionMaterial, + edk: EncryptedDataKey + ) => { /* The AAD section is uInt16BE(length) + AAD * see: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-aad * However, the RAW Keyring wants _only_ the ADD. * So, I just slice off the length. */ - const aad = serializeEncryptionContext(material.encryptionContext).slice(2) + const aad = serializeEncryptionContext(material.encryptionContext).slice( + 2 + ) const { keyNamespace, keyName } = this - return aesGcmUnwrapKey(keyNamespace, keyName, material, wrappingMaterial, edk, aad) + return aesGcmUnwrapKey( + keyNamespace, + keyName, + material, + wrappingMaterial, + edk, + aad + ) } readOnlyProperty(this, 'keyName', keyName) @@ -97,12 +128,14 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { readOnlyProperty(this, '_unwrapKey', _unwrapKey) } - _filter ({ providerId, providerInfo }: EncryptedDataKey) { + _filter({ providerId, providerInfo }: EncryptedDataKey) { const { keyNamespace, keyName } = this return providerId === keyNamespace && providerInfo.startsWith(keyName) } - _rawOnEncrypt = _onEncrypt(randomValuesOnly) + _rawOnEncrypt = _onEncrypt( + randomValuesOnly + ) _onEncrypt = async (material: WebCryptoEncryptionMaterial) => { const _material = await this._rawOnEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) @@ -114,16 +147,24 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { */ _onDecrypt = _onDecrypt() - static async importCryptoKey (masterKey: Uint8Array, wrappingSuite: WrappingSuiteIdentifier): Promise { + static async importCryptoKey( + masterKey: Uint8Array, + wrappingSuite: WrappingSuiteIdentifier + ): Promise { needs(masterKey instanceof Uint8Array, 'Unsupported master key type.') const material = new WebCryptoRawAesMaterial(wrappingSuite) /* Precondition: masterKey must correspond to the algorithm suite specification. - * Note: the KeyringTrace and flag are _only_ set because I am reusing an existing implementation. - * See: raw_aes_material.ts in @aws-crypto/raw-keyring for details - */ - .setUnencryptedDataKey(masterKey, { keyNamespace: 'importOnly', keyName: 'importOnly', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) - return backendForRawAesMasterKey() - .then(backend => _importCryptoKey(backend.subtle, material, ['encrypt', 'decrypt'])) + * Note: the KeyringTrace and flag are _only_ set because I am reusing an existing implementation. + * See: raw_aes_material.ts in @aws-crypto/raw-keyring for details + */ + .setUnencryptedDataKey(masterKey, { + keyNamespace: 'importOnly', + keyName: 'importOnly', + flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY, + }) + return backendForRawAesMasterKey().then((backend) => + _importCryptoKey(backend.subtle, material, ['encrypt', 'decrypt']) + ) } } immutableClass(RawAesKeyringWebCrypto) @@ -138,7 +179,7 @@ immutableClass(RawAesKeyringWebCrypto) * @param wrappingMaterial [WebCryptoRawAesMaterial] The material used to decrypt the EncryptedDataKey * @returns [WebCryptoEncryptionMaterial] Mutates and returns the same WebCryptoEncryptionMaterial that was passed but with an EncryptedDataKey added */ -async function aesGcmWrapKey ( +async function aesGcmWrapKey( keyNamespace: string, keyName: string, material: WebCryptoEncryptionMaterial, @@ -148,14 +189,31 @@ async function aesGcmWrapKey ( const backend = await backendForRawAesMasterKey() const iv = await backend.randomValues(material.suite.ivLength) - const kdfGetSubtleEncrypt = getSubtleFunction(wrappingMaterial, backend, 'encrypt') + const kdfGetSubtleEncrypt = getSubtleFunction( + wrappingMaterial, + backend, + 'encrypt' + ) const info = new Uint8Array() const dataKey = unwrapDataKey(material.getUnencryptedDataKey()) const buffer = await kdfGetSubtleEncrypt(info)(iv, aad)(dataKey) - const ciphertext = new Uint8Array(buffer, 0, buffer.byteLength - material.suite.tagLength / 8) - const authTag = new Uint8Array(buffer, buffer.byteLength - material.suite.tagLength / 8) + const ciphertext = new Uint8Array( + buffer, + 0, + buffer.byteLength - material.suite.tagLength / 8 + ) + const authTag = new Uint8Array( + buffer, + buffer.byteLength - material.suite.tagLength / 8 + ) - const edk = rawAesEncryptedDataKey(keyNamespace, keyName, iv, ciphertext, authTag) + const edk = rawAesEncryptedDataKey( + keyNamespace, + keyName, + iv, + ciphertext, + authTag + ) return material.addEncryptedDataKey(edk, encryptFlags) } @@ -170,7 +228,7 @@ async function aesGcmWrapKey ( * @param aad [Uint8Array] The serialized aad (EncryptionContext) * @returns [WebCryptoDecryptionMaterial] Mutates and returns the same WebCryptoDecryptionMaterial that was passed but with the unencrypted data key set */ -async function aesGcmUnwrapKey ( +async function aesGcmUnwrapKey( keyNamespace: string, keyName: string, material: WebCryptoDecryptionMaterial, @@ -183,9 +241,15 @@ async function aesGcmUnwrapKey ( const backend = await backendForRawAesMasterKey() - const KdfGetSubtleDecrypt = getSubtleFunction(wrappingMaterial, backend, 'decrypt') + const KdfGetSubtleDecrypt = getSubtleFunction( + wrappingMaterial, + backend, + 'decrypt' + ) const info = new Uint8Array() - const buffer = await KdfGetSubtleDecrypt(info)(iv, aad)(concatBuffers(ciphertext, authTag)) + const buffer = await KdfGetSubtleDecrypt(info)(iv, aad)( + concatBuffers(ciphertext, authTag) + ) const trace = { keyNamespace, keyName, flags: decryptFlags } material.setUnencryptedDataKey(new Uint8Array(buffer), trace) return importForWebCryptoDecryptionMaterial(material) @@ -200,7 +264,7 @@ async function aesGcmUnwrapKey ( * from the native implementation than a JS implementation. * So I *force* the master key to be stored in the native implementation **only**. */ -async function backendForRawAesMasterKey () { +async function backendForRawAesMasterKey() { const backend = await getWebCryptoBackend() const { randomValues } = backend const subtle = getNonZeroByteBackend(backend) diff --git a/modules/raw-aes-keyring-node/package.json b/modules/raw-aes-keyring-node/package.json index ce476e068..32470af97 100644 --- a/modules/raw-aes-keyring-node/package.json +++ b/modules/raw-aes-keyring-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-aes-keyring-node/src/index.ts b/modules/raw-aes-keyring-node/src/index.ts index aa7bf6e4e..a74b9d626 100644 --- a/modules/raw-aes-keyring-node/src/index.ts +++ b/modules/raw-aes-keyring-node/src/index.ts @@ -1,5 +1,8 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { RawAesWrappingSuiteIdentifier, WrappingSuiteIdentifier } from '@aws-crypto/raw-keyring' +export { + RawAesWrappingSuiteIdentifier, + WrappingSuiteIdentifier, +} from '@aws-crypto/raw-keyring' export * from './raw_aes_keyring_node' diff --git a/modules/raw-aes-keyring-node/src/raw_aes_keyring_node.ts b/modules/raw-aes-keyring-node/src/raw_aes_keyring_node.ts index d5744dd09..c499b3281 100644 --- a/modules/raw-aes-keyring-node/src/raw_aes_keyring_node.ts +++ b/modules/raw-aes-keyring-node/src/raw_aes_keyring_node.ts @@ -4,42 +4,41 @@ import { KeyringNode, needs, - NodeEncryptionMaterial, // eslint-disable-line no-unused-vars - NodeDecryptionMaterial, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars + NodeEncryptionMaterial, + NodeDecryptionMaterial, + EncryptedDataKey, KeyringTraceFlag, immutableClass, readOnlyProperty, unwrapDataKey, - NodeAlgorithmSuite // eslint-disable-line no-unused-vars + NodeAlgorithmSuite, } from '@aws-crypto/material-management-node' import { randomBytes, createCipheriv, createDecipheriv } from 'crypto' -import { - serializeFactory, - concatBuffers -} from '@aws-crypto/serialize' +import { serializeFactory, concatBuffers } from '@aws-crypto/serialize' import { _onEncrypt, _onDecrypt, NodeRawAesMaterial, rawAesEncryptedDataKeyFactory, rawAesEncryptedPartsFactory, - WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars - WrapKey, // eslint-disable-line no-unused-vars - UnwrapKey // eslint-disable-line no-unused-vars + WrappingSuiteIdentifier, + WrapKey, + UnwrapKey, } from '@aws-crypto/raw-keyring' const fromUtf8 = (input: string) => Buffer.from(input, 'utf8') -const toUtf8 = (input: Uint8Array) => Buffer - .from(input.buffer, input.byteOffset, input.byteLength) - .toString('utf8') +const toUtf8 = (input: Uint8Array) => + Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString('utf8') const { serializeEncryptionContext } = serializeFactory(fromUtf8) -const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory(toUtf8, fromUtf8) +const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory( + toUtf8, + fromUtf8 +) const { rawAesEncryptedParts } = rawAesEncryptedPartsFactory(fromUtf8) export type RawAesKeyringNodeInput = { keyNamespace: string keyName: string - unencryptedMasterKey: Uint8Array, + unencryptedMasterKey: Uint8Array wrappingSuite: WrappingSuiteIdentifier } @@ -49,7 +48,7 @@ export class RawAesKeyringNode extends KeyringNode { _wrapKey!: WrapKey _unwrapKey!: UnwrapKey - constructor (input: RawAesKeyringNodeInput) { + constructor(input: RawAesKeyringNodeInput) { super() const { keyName, keyNamespace, unencryptedMasterKey, wrappingSuite } = input @@ -61,7 +60,11 @@ export class RawAesKeyringNode extends KeyringNode { * Note: the KeyringTrace and flag are _only_ set because I am reusing an existing implementation. * See: raw_aes_material.ts in @aws-crypto/raw-keyring for details */ - .setUnencryptedDataKey(unencryptedMasterKey, { keyNamespace, keyName, flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) + .setUnencryptedDataKey(unencryptedMasterKey, { + keyNamespace, + keyName, + flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY, + }) const _wrapKey = async (material: NodeEncryptionMaterial) => { /* The AAD section is uInt16BE(length) + AAD @@ -69,25 +72,45 @@ export class RawAesKeyringNode extends KeyringNode { * However, the RAW Keyring wants _only_ the ADD. * So, I just slice off the length. */ - const { buffer, byteOffset, byteLength } = serializeEncryptionContext(material.encryptionContext).slice(2) + const { buffer, byteOffset, byteLength } = serializeEncryptionContext( + material.encryptionContext + ).slice(2) const aad = Buffer.from(buffer, byteOffset, byteLength) const { keyNamespace, keyName } = this - return aesGcmWrapKey(keyNamespace, keyName, material, aad, wrappingMaterial) + return aesGcmWrapKey( + keyNamespace, + keyName, + material, + aad, + wrappingMaterial + ) } - const _unwrapKey = async (material: NodeDecryptionMaterial, edk: EncryptedDataKey) => { + const _unwrapKey = async ( + material: NodeDecryptionMaterial, + edk: EncryptedDataKey + ) => { const { keyNamespace, keyName } = this /* The AAD section is uInt16BE(length) + AAD * see: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-aad * However, the RAW Keyring wants _only_ the ADD. * So, I just slice off the length. */ - const { buffer, byteOffset, byteLength } = serializeEncryptionContext(material.encryptionContext).slice(2) + const { buffer, byteOffset, byteLength } = serializeEncryptionContext( + material.encryptionContext + ).slice(2) const aad = Buffer.from(buffer, byteOffset, byteLength) // const aad = Buffer.concat(encodeEncryptionContext(context || {})) - return aesGcmUnwrapKey(keyNamespace, keyName, material, wrappingMaterial, edk, aad) + return aesGcmUnwrapKey( + keyNamespace, + keyName, + material, + wrappingMaterial, + edk, + aad + ) } readOnlyProperty(this, 'keyName', keyName) @@ -96,18 +119,24 @@ export class RawAesKeyringNode extends KeyringNode { readOnlyProperty(this, '_unwrapKey', _unwrapKey) } - _filter ({ providerId, providerInfo }: EncryptedDataKey) { + _filter({ providerId, providerInfo }: EncryptedDataKey) { const { keyNamespace, keyName } = this return providerId === keyNamespace && providerInfo.startsWith(keyName) } - _onEncrypt = _onEncrypt(randomBytesAsync) + _onEncrypt = _onEncrypt( + randomBytesAsync + ) _onDecrypt = _onDecrypt() } immutableClass(RawAesKeyringNode) -const encryptFlags = KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_SIGNED_ENC_CTX -const decryptFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX +const encryptFlags = + KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY | + KeyringTraceFlag.WRAPPING_KEY_SIGNED_ENC_CTX +const decryptFlags = + KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | + KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX /** * Uses aes-gcm to encrypt the data key and return the passed NodeEncryptionMaterial with @@ -119,7 +148,7 @@ const decryptFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | KeyringT * @param wrappingMaterial [NodeRawAesMaterial] The material used to decrypt the EncryptedDataKey * @returns [NodeEncryptionMaterial] Mutates and returns the same NodeEncryptionMaterial that was passed but with an EncryptedDataKey added */ -function aesGcmWrapKey ( +function aesGcmWrapKey( keyNamespace: string, keyName: string, material: NodeEncryptionMaterial, @@ -132,8 +161,7 @@ function aesGcmWrapKey ( const wrappingDataKey = wrappingMaterial.getUnencryptedDataKey() const dataKey = unwrapDataKey(material.getUnencryptedDataKey()) - const cipher = createCipheriv(encryption, wrappingDataKey, iv) - .setAAD(aad) + const cipher = createCipheriv(encryption, wrappingDataKey, iv).setAAD(aad) // Buffer.concat will use the shared buffer space, and the resultant buffer will have a byteOffset... const ciphertext = concatBuffers(cipher.update(dataKey), cipher.final()) const authTag = cipher.getAuthTag() @@ -160,7 +188,7 @@ function aesGcmWrapKey ( * @param aad [Buffer] The serialized aad (EncryptionContext) * @returns [NodeDecryptionMaterial] Mutates and returns the same NodeDecryptionMaterial that was passed but with the unencrypted data key set */ -function aesGcmUnwrapKey ( +function aesGcmUnwrapKey( keyNamespace: string, keyName: string, material: NodeDecryptionMaterial, @@ -168,22 +196,33 @@ function aesGcmUnwrapKey ( edk: EncryptedDataKey, aad: Buffer ): NodeDecryptionMaterial { - const { authTag, ciphertext, iv } = rawAesEncryptedParts(material.suite, keyName, edk) + const { authTag, ciphertext, iv } = rawAesEncryptedParts( + material.suite, + keyName, + edk + ) const { encryption } = wrappingMaterial.suite // createDecipheriv is incorrectly typed in @types/node. It should take key: CipherKey, not key: BinaryLike - const decipher = createDecipheriv(encryption, wrappingMaterial.getUnencryptedDataKey() as any, iv) + const decipher = createDecipheriv( + encryption, + wrappingMaterial.getUnencryptedDataKey() as any, + iv + ) .setAAD(aad) .setAuthTag(authTag) // Buffer.concat will use the shared buffer space, and the resultant buffer will have a byteOffset... - const unencryptedDataKey = concatBuffers(decipher.update(ciphertext), decipher.final()) + const unencryptedDataKey = concatBuffers( + decipher.update(ciphertext), + decipher.final() + ) const trace = { keyNamespace, keyName, flags: decryptFlags } return material.setUnencryptedDataKey(unencryptedDataKey, trace) } -function randomBytesAsync (size: number): Promise { +async function randomBytesAsync(size: number): Promise { return new Promise((resolve, reject) => { - randomBytes(size, (err: Error|null, buffer: Buffer) => { + randomBytes(size, (err: Error | null, buffer: Buffer) => { if (err) return reject(err) resolve(buffer) }) diff --git a/modules/raw-keyring/package.json b/modules/raw-keyring/package.json index 750f15b8d..4ad5836e7 100644 --- a/modules/raw-keyring/package.json +++ b/modules/raw-keyring/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-keyring/src/raw_aes_algorithm_suite.ts b/modules/raw-keyring/src/raw_aes_algorithm_suite.ts index b3c3dcbd7..9e6152b5e 100644 --- a/modules/raw-keyring/src/raw_aes_algorithm_suite.ts +++ b/modules/raw-keyring/src/raw_aes_algorithm_suite.ts @@ -3,21 +3,30 @@ import { AlgorithmSuiteIdentifier } from '@aws-crypto/material-management' -export type WrappingSuiteIdentifier = AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16 | -AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16 | -AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16 +export type WrappingSuiteIdentifier = + | AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16 + | AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16 + | AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16 -type RawAesWrappingNames = 'AES128_GCM_IV12_TAG16_NO_PADDING'| 'AES192_GCM_IV12_TAG16_NO_PADDING'| 'AES256_GCM_IV12_TAG16_NO_PADDING' +type RawAesWrappingNames = + | 'AES128_GCM_IV12_TAG16_NO_PADDING' + | 'AES192_GCM_IV12_TAG16_NO_PADDING' + | 'AES256_GCM_IV12_TAG16_NO_PADDING' -const AES128_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16 -const AES192_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16 -const AES256_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16 -export const RawAesWrappingSuiteIdentifier: {[K in RawAesWrappingNames|WrappingSuiteIdentifier]: WrappingSuiteIdentifier} = Object.freeze({ +const AES128_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = + AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16 +const AES192_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = + AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16 +const AES256_GCM_IV12_TAG16_NO_PADDING: AlgorithmSuiteIdentifier = + AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16 +export const RawAesWrappingSuiteIdentifier: { + [K in RawAesWrappingNames | WrappingSuiteIdentifier]: WrappingSuiteIdentifier +} = Object.freeze({ AES128_GCM_IV12_TAG16_NO_PADDING, AES192_GCM_IV12_TAG16_NO_PADDING, AES256_GCM_IV12_TAG16_NO_PADDING, // Adding reverse lookup to support checking supported suites [AES128_GCM_IV12_TAG16_NO_PADDING]: AES128_GCM_IV12_TAG16_NO_PADDING, [AES192_GCM_IV12_TAG16_NO_PADDING]: AES192_GCM_IV12_TAG16_NO_PADDING, - [AES256_GCM_IV12_TAG16_NO_PADDING]: AES256_GCM_IV12_TAG16_NO_PADDING + [AES256_GCM_IV12_TAG16_NO_PADDING]: AES256_GCM_IV12_TAG16_NO_PADDING, }) diff --git a/modules/raw-keyring/src/raw_aes_encrypted_data_keys.ts b/modules/raw-keyring/src/raw_aes_encrypted_data_keys.ts index d43686a0f..a9ee7c1eb 100644 --- a/modules/raw-keyring/src/raw_aes_encrypted_data_keys.ts +++ b/modules/raw-keyring/src/raw_aes_encrypted_data_keys.ts @@ -2,32 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 /* For raw AES keyrings the required wrapping information is stored in an EncryptedDataKey. - * KeyNamespace (identifies the keyring "class"), - * KeyName (identifies this specific keyring, like a KMS CMK ARN) - * - * { - * providerId: KeyNamespace - * providerInfo: utf8Encode(KeyName + TagLengthBits uInt32BE + IVLength uInt32BE + iv) - * encryptedDataKey: wrapped key + authTag - * } - * - * The AAD (encryption context) is the same as the message. - */ + * KeyNamespace (identifies the keyring "class"), + * KeyName (identifies this specific keyring, like a KMS CMK ARN) + * + * { + * providerId: KeyNamespace + * providerInfo: utf8Encode(KeyName + TagLengthBits uInt32BE + IVLength uInt32BE + iv) + * encryptedDataKey: wrapped key + authTag + * } + * + * The AAD (encryption context) is the same as the message. + */ import { concatBuffers, uInt32BE } from '@aws-crypto/serialize' import { - AlgorithmSuite, // eslint-disable-line no-unused-vars - EncryptedDataKey, // eslint-disable-line no-unused-vars - needs + AlgorithmSuite, + EncryptedDataKey, + needs, } from '@aws-crypto/material-management' -export function rawAesEncryptedDataKeyFactory ( +export function rawAesEncryptedDataKeyFactory( toUtf8: (input: Uint8Array) => string, fromUtf8: (input: string) => Uint8Array ) { return { rawAesEncryptedDataKey } - function rawAesEncryptedDataKey ( + function rawAesEncryptedDataKey( keyNamespace: string, keyName: string, iv: Uint8Array, @@ -38,38 +38,51 @@ export function rawAesEncryptedDataKeyFactory ( const authTagBitLength = authTag.byteLength * 8 const encryptedDataKey = concatBuffers(ciphertext, authTag) const providerId = keyNamespace - const rawInfo = concatBuffers(fromUtf8(keyName), uInt32BE(authTagBitLength), uInt32BE(ivLength), iv) + const rawInfo = concatBuffers( + fromUtf8(keyName), + uInt32BE(authTagBitLength), + uInt32BE(ivLength), + iv + ) const providerInfo = toUtf8(rawInfo) - return new EncryptedDataKey({ encryptedDataKey, providerId, providerInfo, rawInfo }) + return new EncryptedDataKey({ + encryptedDataKey, + providerId, + providerInfo, + rawInfo, + }) } } -export function rawAesEncryptedPartsFactory (fromUtf8: (input: string) => Uint8Array) { +export function rawAesEncryptedPartsFactory( + fromUtf8: (input: string) => Uint8Array +) { return { rawAesEncryptedParts } - function rawAesEncryptedParts ( + function rawAesEncryptedParts( suite: AlgorithmSuite, keyName: string, { encryptedDataKey, rawInfo }: EncryptedDataKey ) { /* Precondition: rawInfo must be a Uint8Array. */ - if (!(rawInfo instanceof Uint8Array)) throw new Error('Malformed Encrypted Data Key.') + if (!(rawInfo instanceof Uint8Array)) + throw new Error('Malformed Encrypted Data Key.') // see above for format, slice off the "string part" rawInfo = rawInfo.slice(fromUtf8(keyName).byteLength) /* Uint8Array is a view on top of the underlying ArrayBuffer. - * This means that raw underlying memory stored in the ArrayBuffer - * may be larger than the Uint8Array. This is especially true of - * the Node.js Buffer object. The offset and length *must* be - * passed to the DataView otherwise I will get unexpected results. - */ + * This means that raw underlying memory stored in the ArrayBuffer + * may be larger than the Uint8Array. This is especially true of + * the Node.js Buffer object. The offset and length *must* be + * passed to the DataView otherwise I will get unexpected results. + */ const dataView = new DataView( rawInfo.buffer, rawInfo.byteOffset, rawInfo.byteLength ) /* See above: - * uInt32BE(authTagBitLength),uInt32BE(ivLength), iv - */ + * uInt32BE(authTagBitLength),uInt32BE(ivLength), iv + */ const tagLengthBits = dataView.getUint32(0, false) // big endian const ivLength = dataView.getUint32(4, false) // big endian /* Precondition: The ivLength must match the algorith suite specification. */ @@ -80,7 +93,10 @@ export function rawAesEncryptedPartsFactory (fromUtf8: (input: string) => Uint8A needs(rawInfo.byteLength === 4 + 4 + ivLength, 'Malformed providerInfo') const tagLength = tagLengthBits / 8 /* Precondition: The encryptedDataKey byteLength must match the algorith suite specification and encoded length. */ - needs(encryptedDataKey.byteLength === tagLength + suite.keyLengthBytes, 'Malformed providerInfo') + needs( + encryptedDataKey.byteLength === tagLength + suite.keyLengthBytes, + 'Malformed providerInfo' + ) const iv = rawInfo.slice(-ivLength) const authTag = encryptedDataKey.slice(-tagLength) const ciphertext = encryptedDataKey.slice(0, -tagLength) diff --git a/modules/raw-keyring/src/raw_aes_material.ts b/modules/raw-keyring/src/raw_aes_material.ts index 4745c9eed..458f4e75c 100644 --- a/modules/raw-keyring/src/raw_aes_material.ts +++ b/modules/raw-keyring/src/raw_aes_material.ts @@ -9,38 +9,41 @@ */ import { - CryptographicMaterial, // eslint-disable-line no-unused-vars - WebCryptoMaterial, // eslint-disable-line no-unused-vars - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars + CryptographicMaterial, + WebCryptoMaterial, + MixedBackendCryptoKey, decorateCryptographicMaterial, decorateWebCryptoMaterial, frozenClass, NodeAlgorithmSuite, WebCryptoAlgorithmSuite, - AwsEsdkJsCryptoKey, // eslint-disable-line no-unused-vars - AwsEsdkJsKeyUsage, // eslint-disable-line no-unused-vars - AwsEsdkKeyObject, // eslint-disable-line no-unused-vars - KeyringTrace, // eslint-disable-line no-unused-vars + AwsEsdkJsCryptoKey, + AwsEsdkJsKeyUsage, + AwsEsdkKeyObject, + KeyringTrace, KeyringTraceFlag, needs, - EncryptionContext // eslint-disable-line no-unused-vars + EncryptionContext, } from '@aws-crypto/material-management' import { - WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars - RawAesWrappingSuiteIdentifier + WrappingSuiteIdentifier, + RawAesWrappingSuiteIdentifier, } from './raw_aes_algorithm_suite' -export class NodeRawAesMaterial implements - Readonly> { +export class NodeRawAesMaterial + implements Readonly> { suite: NodeAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => NodeRawAesMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => NodeRawAesMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => NodeRawAesMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] encryptionContext: EncryptionContext = Object.freeze({}) - constructor (suiteId: WrappingSuiteIdentifier) { + constructor(suiteId: WrappingSuiteIdentifier) { /* Precondition: NodeRawAesMaterial suiteId must be RawAesWrappingSuiteIdentifier. */ needs(RawAesWrappingSuiteIdentifier[suiteId], 'suiteId not supported.') this.suite = new NodeAlgorithmSuite(suiteId) @@ -53,31 +56,41 @@ export class NodeRawAesMaterial implements Object.setPrototypeOf(this, NodeRawAesMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasUnencryptedDataKey } } frozenClass(NodeRawAesMaterial) -export class WebCryptoRawAesMaterial implements - Readonly>, - Readonly> { +export class WebCryptoRawAesMaterial + implements + Readonly>, + Readonly> { suite: WebCryptoAlgorithmSuite - setUnencryptedDataKey!: (dataKey: Uint8Array|AwsEsdkKeyObject, trace: KeyringTrace) => WebCryptoRawAesMaterial - getUnencryptedDataKey!: () => Uint8Array|AwsEsdkKeyObject + setUnencryptedDataKey!: ( + dataKey: Uint8Array | AwsEsdkKeyObject, + trace: KeyringTrace + ) => WebCryptoRawAesMaterial + getUnencryptedDataKey!: () => Uint8Array | AwsEsdkKeyObject zeroUnencryptedDataKey!: () => WebCryptoRawAesMaterial hasUnencryptedDataKey!: boolean keyringTrace: KeyringTrace[] = [] - setCryptoKey!: (dataKey: AwsEsdkJsCryptoKey|MixedBackendCryptoKey, trace: KeyringTrace) => WebCryptoRawAesMaterial - getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey + setCryptoKey!: ( + dataKey: AwsEsdkJsCryptoKey | MixedBackendCryptoKey, + trace: KeyringTrace + ) => WebCryptoRawAesMaterial + getCryptoKey!: () => AwsEsdkJsCryptoKey | MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray encryptionContext: EncryptionContext = Object.freeze({}) - constructor (suiteId: WrappingSuiteIdentifier) { + constructor(suiteId: WrappingSuiteIdentifier) { /* Precondition: WebCryptoAlgorithmSuite suiteId must be RawAesWrappingSuiteIdentifier. */ needs(RawAesWrappingSuiteIdentifier[suiteId], 'suiteId not supported.') this.suite = new WebCryptoAlgorithmSuite(suiteId) - this.validUsages = Object.freeze(['decrypt', 'encrypt']) + this.validUsages = Object.freeze([ + 'decrypt', + 'encrypt', + ] as AwsEsdkJsKeyUsage[]) /* WebCryptoRawAesMaterial need to set a flag, this is an abuse of TraceFlags * because the material is not generated. * but CryptographicMaterial force a flag to be set. @@ -88,7 +101,7 @@ export class WebCryptoRawAesMaterial implements Object.setPrototypeOf(this, WebCryptoRawAesMaterial.prototype) Object.freeze(this) } - hasValidKey () { + hasValidKey() { return this.hasUnencryptedDataKey && this.hasCryptoKey } } diff --git a/modules/raw-keyring/src/raw_keyring_decorators.ts b/modules/raw-keyring/src/raw_keyring_decorators.ts index 18366fba6..c3ecf50af 100644 --- a/modules/raw-keyring/src/raw_keyring_decorators.ts +++ b/modules/raw-keyring/src/raw_keyring_decorators.ts @@ -2,26 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 import { - EncryptionMaterial, // eslint-disable-line no-unused-vars - DecryptionMaterial, // eslint-disable-line no-unused-vars - SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - KeyringTrace, // eslint-disable-line no-unused-vars + EncryptionMaterial, + DecryptionMaterial, + SupportedAlgorithmSuites, + KeyringTrace, KeyringTraceFlag, - EncryptedDataKey // eslint-disable-line no-unused-vars + EncryptedDataKey, } from '@aws-crypto/material-management' export interface RawKeyRing { keyNamespace: string keyName: string - _wrapKey: WrapKey, - _unwrapKey: UnwrapKey, + _wrapKey: WrapKey + _unwrapKey: UnwrapKey _filter: FilterEncryptedDataKey } -export function _onEncrypt> ( - randomBytes: (bytes: number) => Promise -) { - return async function _onEncrypt ( +export function _onEncrypt< + S extends SupportedAlgorithmSuites, + K extends RawKeyRing +>(randomBytes: (bytes: number) => Promise) { + return async function _onEncrypt( this: K, material: EncryptionMaterial ): Promise> { @@ -29,7 +30,7 @@ export function _onEncrypt> () { - return async function _onDecrypt ( +export function _onDecrypt< + S extends SupportedAlgorithmSuites, + K extends RawKeyRing +>() { + return async function _onDecrypt( this: K, material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[] @@ -70,7 +74,9 @@ export interface WrapKey { } export interface UnwrapKey { - (material: DecryptionMaterial, edk: EncryptedDataKey): Promise> + (material: DecryptionMaterial, edk: EncryptedDataKey): Promise< + DecryptionMaterial + > } export interface FilterEncryptedDataKey { diff --git a/modules/raw-rsa-keyring-browser/package.json b/modules/raw-rsa-keyring-browser/package.json index 45febeb57..5b95dd8af 100644 --- a/modules/raw-rsa-keyring-browser/package.json +++ b/modules/raw-rsa-keyring-browser/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/raw-rsa-keyring-browser/src/get_import_options.ts b/modules/raw-rsa-keyring-browser/src/get_import_options.ts index 66f3548c1..a11e50f0b 100644 --- a/modules/raw-rsa-keyring-browser/src/get_import_options.ts +++ b/modules/raw-rsa-keyring-browser/src/get_import_options.ts @@ -5,71 +5,90 @@ import { RsaPadding, JsonWebKeyRsaAlg, RsaHash, - BinaryKey, // eslint-disable-line no-unused-vars - RsaJsonWebKey, // eslint-disable-line no-unused-vars - RsaImportableKey, // eslint-disable-line no-unused-vars - RsaWrappingKeyAlgorithm // eslint-disable-line no-unused-vars + BinaryKey, + RsaJsonWebKey, + RsaImportableKey, + RsaWrappingKeyAlgorithm, } from './types' import { - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars + MixedBackendCryptoKey, needs, isCryptoKey, - AwsEsdkJsCryptoKey // eslint-disable-line no-unused-vars + AwsEsdkJsCryptoKey, } from '@aws-crypto/material-management-browser' type WebCryptoRsaName = keyof typeof JsonWebKeyRsaAlg -const OAEP_SHA1_MFG1: RsaWrappingKeyAlgorithm = { name: 'RSA-OAEP', hash: { name: 'SHA-1' } } +const OAEP_SHA1_MFG1: RsaWrappingKeyAlgorithm = { + name: 'RSA-OAEP', + hash: { name: 'SHA-1' }, +} Object.freeze(OAEP_SHA1_MFG1) Object.freeze(OAEP_SHA1_MFG1.hash) -const OAEP_SHA256_MFG1: RsaWrappingKeyAlgorithm = { name: 'RSA-OAEP', hash: { name: 'SHA-256' } } +const OAEP_SHA256_MFG1: RsaWrappingKeyAlgorithm = { + name: 'RSA-OAEP', + hash: { name: 'SHA-256' }, +} Object.freeze(OAEP_SHA256_MFG1) Object.freeze(OAEP_SHA256_MFG1.hash) -const OAEP_SHA384_MFG1: RsaWrappingKeyAlgorithm = { name: 'RSA-OAEP', hash: { name: 'SHA-384' } } +const OAEP_SHA384_MFG1: RsaWrappingKeyAlgorithm = { + name: 'RSA-OAEP', + hash: { name: 'SHA-384' }, +} Object.freeze(OAEP_SHA384_MFG1) Object.freeze(OAEP_SHA384_MFG1.hash) -const OAEP_SHA512_MFG1: RsaWrappingKeyAlgorithm = { name: 'RSA-OAEP', hash: { name: 'SHA-512' } } +const OAEP_SHA512_MFG1: RsaWrappingKeyAlgorithm = { + name: 'RSA-OAEP', + hash: { name: 'SHA-512' }, +} Object.freeze(OAEP_SHA512_MFG1) Object.freeze(OAEP_SHA512_MFG1.hash) -const JsonWebKeyMap: {[key in JsonWebKeyRsaAlg]: RsaWrappingKeyAlgorithm} = Object.freeze({ +const JsonWebKeyMap: { + [key in JsonWebKeyRsaAlg]: RsaWrappingKeyAlgorithm +} = Object.freeze({ [JsonWebKeyRsaAlg['RSA-OAEP']]: OAEP_SHA1_MFG1, [JsonWebKeyRsaAlg['RSA-OAEP-256']]: OAEP_SHA256_MFG1, [JsonWebKeyRsaAlg['RSA-OAEP-384']]: OAEP_SHA384_MFG1, - [JsonWebKeyRsaAlg['RSA-OAEP-512']]: OAEP_SHA512_MFG1 + [JsonWebKeyRsaAlg['RSA-OAEP-512']]: OAEP_SHA512_MFG1, }) -const RsaPaddingMap: {[key in RsaPadding]: RsaWrappingKeyAlgorithm} = Object.freeze({ +const RsaPaddingMap: { + [key in RsaPadding]: RsaWrappingKeyAlgorithm +} = Object.freeze({ [RsaPadding.OAEP_SHA1_MFG1]: OAEP_SHA1_MFG1, [RsaPadding.OAEP_SHA256_MFG1]: OAEP_SHA256_MFG1, [RsaPadding.OAEP_SHA384_MFG1]: OAEP_SHA384_MFG1, - [RsaPadding.OAEP_SHA512_MFG1]: OAEP_SHA512_MFG1 + [RsaPadding.OAEP_SHA512_MFG1]: OAEP_SHA512_MFG1, }) -export function getImportOptions (keyInfo: RsaImportableKey) { - const { alg } = (keyInfo) - const { padding } = (keyInfo) +export function getImportOptions(keyInfo: RsaImportableKey) { + const { alg } = keyInfo as RsaJsonWebKey + const { padding } = keyInfo as BinaryKey if (JsonWebKeyMap[alg]) { return { format: 'jwk', - key: (keyInfo), - wrappingAlgorithm: JsonWebKeyMap[alg] + key: keyInfo as RsaJsonWebKey, + wrappingAlgorithm: JsonWebKeyMap[alg], } } else if (RsaPaddingMap[padding]) { - const { format, key } = (keyInfo) + const { format, key } = keyInfo as BinaryKey return { format, key, - wrappingAlgorithm: RsaPaddingMap[padding] + wrappingAlgorithm: RsaPaddingMap[padding], } } throw new Error('Unsupported RsaImportableKey') } -export function getWrappingAlgorithm (publicKey?: AwsEsdkJsCryptoKey, privateKey?: AwsEsdkJsCryptoKey|MixedBackendCryptoKey) { +export function getWrappingAlgorithm( + publicKey?: AwsEsdkJsCryptoKey, + privateKey?: AwsEsdkJsCryptoKey | MixedBackendCryptoKey +) { const privateKeys = flattenMixedCryptoKey(privateKey) if (publicKey && privateKeys.length) { return verify(...[publicKey, ...privateKeys].map(extract)) @@ -81,14 +100,14 @@ export function getWrappingAlgorithm (publicKey?: AwsEsdkJsCryptoKey, privateKey throw new Error('No Key provided.') } -export function extract (key: AwsEsdkJsCryptoKey): RsaWrappingKeyAlgorithm { +export function extract(key: AwsEsdkJsCryptoKey): RsaWrappingKeyAlgorithm { const { algorithm } = key // @ts-ignore const { name, hash } = algorithm - return { name: (name), hash } + return { name: name as WebCryptoRsaName, hash } } -export function verify (...args: RsaWrappingKeyAlgorithm[]) { +export function verify(...args: RsaWrappingKeyAlgorithm[]) { const [wrappingAlgorithm, ...rest] = args /* Precondition: Need at least 1 algorithm to verify. */ needs(wrappingAlgorithm, 'Can not verify an empty set of algorithms.') @@ -100,18 +119,25 @@ export function verify (...args: RsaWrappingKeyAlgorithm[]) { /* Check for early return (Postcondition): Only 1 wrappingAlgorithm is clearly valid. */ if (!rest.length) return wrappingAlgorithm /* Precondition: All keys must have the same wrappingAlgorithm. */ - needs(rest.every(equalWrappingAlgorithm), 'Not all RSA keys have the same wrappingAlgorithm.') + needs( + rest.every(equalWrappingAlgorithm), + 'Not all RSA keys have the same wrappingAlgorithm.' + ) return wrappingAlgorithm - function equalWrappingAlgorithm (algorithm: RsaWrappingKeyAlgorithm) { - return algorithm.name === name && + function equalWrappingAlgorithm(algorithm: RsaWrappingKeyAlgorithm) { + return ( + algorithm.name === name && algorithm.hash && algorithm.hash.name === hash.name + ) } } -export function flattenMixedCryptoKey (key?: AwsEsdkJsCryptoKey|MixedBackendCryptoKey): AwsEsdkJsCryptoKey[] { +export function flattenMixedCryptoKey( + key?: AwsEsdkJsCryptoKey | MixedBackendCryptoKey +): AwsEsdkJsCryptoKey[] { /* Check for early return (Postcondition): empty inputs should return an empty array. */ if (!key) return [] if (isCryptoKey(key)) return [key] diff --git a/modules/raw-rsa-keyring-browser/src/raw_rsa_keyring_web_crypto.ts b/modules/raw-rsa-keyring-browser/src/raw_rsa_keyring_web_crypto.ts index d927efad7..b9f6df46e 100644 --- a/modules/raw-rsa-keyring-browser/src/raw_rsa_keyring_web_crypto.ts +++ b/modules/raw-rsa-keyring-browser/src/raw_rsa_keyring_web_crypto.ts @@ -4,10 +4,10 @@ import { KeyringWebCrypto, needs, - WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars - WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars + WebCryptoEncryptionMaterial, + WebCryptoDecryptionMaterial, EncryptedDataKey, - KeyringTrace, // eslint-disable-line no-unused-vars + KeyringTrace, KeyringTraceFlag, immutableClass, readOnlyProperty, @@ -15,25 +15,29 @@ import { keyUsageForMaterial, importForWebCryptoEncryptionMaterial, unwrapDataKey, - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars - WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars - AwsEsdkJsCryptoKey // eslint-disable-line no-unused-vars + MixedBackendCryptoKey, + WebCryptoAlgorithmSuite, + AwsEsdkJsCryptoKey, } from '@aws-crypto/material-management-browser' import { getWebCryptoBackend, getNonZeroByteBackend, - isFullSupportWebCryptoBackend + isFullSupportWebCryptoBackend, } from '@aws-crypto/web-crypto-backend' import { _onEncrypt, _onDecrypt, - WrapKey, // eslint-disable-line no-unused-vars - UnwrapKey // eslint-disable-line no-unused-vars + WrapKey, + UnwrapKey, } from '@aws-crypto/raw-keyring' import { randomValuesOnly } from '@aws-crypto/random-source-browser' -import { RawRsaKeyringWebCryptoInput, RsaImportableKey } from './types' // eslint-disable-line no-unused-vars -import { getImportOptions, getWrappingAlgorithm, flattenMixedCryptoKey } from './get_import_options' +import { RawRsaKeyringWebCryptoInput, RsaImportableKey } from './types' +import { + getImportOptions, + getWrappingAlgorithm, + flattenMixedCryptoKey, +} from './get_import_options' export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { public keyNamespace!: string @@ -41,7 +45,7 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { _wrapKey!: WrapKey _unwrapKey!: UnwrapKey - constructor (input: RawRsaKeyringWebCryptoInput) { + constructor(input: RawRsaKeyringWebCryptoInput) { super() const { publicKey, privateKey, keyName, keyNamespace } = input @@ -54,7 +58,8 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { const _wrapKey = async (material: WebCryptoEncryptionMaterial) => { /* Precondition: I must have a publicKey to wrap. */ - if (!publicKey) throw new Error('No publicKey configured, encrypt not supported.') + if (!publicKey) + throw new Error('No publicKey configured, encrypt not supported.') // The nonZero backend is used because some browsers support Subtle Crypto // but do not support Zero Byte AES-GCM. I want to use the native @@ -69,19 +74,33 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { const importFormat = 'jwk' const keyUsages: KeyUsage[] = ['wrapKey'] // limit the use of this key (*not* decrypt, encrypt, deriveKey) const jwk = bytes2JWK(unwrapDataKey(material.getUnencryptedDataKey())) - const cryptoKey = await subtle.importKey(importFormat, jwk, encryption, extractable, keyUsages) + const cryptoKey = await subtle.importKey( + importFormat, + jwk, + encryption, + extractable, + keyUsages + ) const wrapFormat = 'raw' - const encryptedArrayBuffer = await subtle.wrapKey(wrapFormat, cryptoKey, publicKey, wrappingAlgorithm) + const encryptedArrayBuffer = await subtle.wrapKey( + wrapFormat, + cryptoKey, + publicKey, + wrappingAlgorithm + ) // Can the extractable setting of cryptoKey be changed to false here? If so, do it. const edk = new EncryptedDataKey({ providerId: keyNamespace, providerInfo: keyName, - encryptedDataKey: new Uint8Array(encryptedArrayBuffer) + encryptedDataKey: new Uint8Array(encryptedArrayBuffer), }) - return material.addEncryptedDataKey(edk, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) + return material.addEncryptedDataKey( + edk, + KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY + ) } /* returns either an array of 1 CryptoKey or an array of both from MixedBackendCryptoKey e.g. @@ -89,16 +108,20 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { */ const privateKeys = flattenMixedCryptoKey(privateKey) - const _unwrapKey = async (material: WebCryptoDecryptionMaterial, edk: EncryptedDataKey) => { + const _unwrapKey = async ( + material: WebCryptoDecryptionMaterial, + edk: EncryptedDataKey + ) => { /* Precondition: I must have a privateKey to unwrap. */ - if (!privateKey) throw new Error('No privateKey configured, decrypt not supported.') + if (!privateKey) + throw new Error('No privateKey configured, decrypt not supported.') const backend = await getWebCryptoBackend() const { suite } = material const trace: KeyringTrace = { keyName: this.keyName, keyNamespace: this.keyNamespace, - flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY + flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY, } const format = 'raw' @@ -106,14 +129,14 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { const algorithm = suite.kdf ? suite.kdf : suite.encryption const keyUsages = [keyUsageForMaterial(material)] - const importArgs:Parameters = [ + const importArgs: Parameters = [ format, edk.encryptedDataKey, privateKeys[0], wrappingAlgorithm, algorithm, extractable, - keyUsages + keyUsages, ] /* This is superior to importForWebCryptoDecryptionMaterial. @@ -124,12 +147,17 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { const cryptoKey = await backend.subtle.unwrapKey(...importArgs) return material.setCryptoKey(cryptoKey, trace) } else { - const importZeroBackend = >[...importArgs] + const importZeroBackend = [...importArgs] as Parameters< + SubtleCrypto['unwrapKey'] + > importZeroBackend[2] = privateKeys[1] const mixedDataKey: MixedBackendCryptoKey = await Promise.all([ backend.nonZeroByteSubtle.unwrapKey(...importArgs), - backend.zeroByteSubtle.unwrapKey(...importZeroBackend) - ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ nonZeroByteCryptoKey, zeroByteCryptoKey })) + backend.zeroByteSubtle.unwrapKey(...importZeroBackend), + ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ + nonZeroByteCryptoKey, + zeroByteCryptoKey, + })) return material.setCryptoKey(mixedDataKey, trace) } } @@ -140,12 +168,14 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { readOnlyProperty(this, '_unwrapKey', _unwrapKey) } - _filter ({ providerId, providerInfo }: EncryptedDataKey) { + _filter({ providerId, providerInfo }: EncryptedDataKey) { const { keyNamespace, keyName } = this return providerId === keyNamespace && providerInfo.startsWith(keyName) } - _rawOnEncrypt = _onEncrypt(randomValuesOnly) + _rawOnEncrypt = _onEncrypt( + randomValuesOnly + ) _onEncrypt = async (material: WebCryptoEncryptionMaterial) => { const _material = await this._rawOnEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) @@ -157,24 +187,45 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { */ _onDecrypt = _onDecrypt() - static async importPublicKey (publicKey: RsaImportableKey): Promise { + static async importPublicKey( + publicKey: RsaImportableKey + ): Promise { const { wrappingAlgorithm, format, key } = getImportOptions(publicKey) const backend = await getWebCryptoBackend() const subtle = getNonZeroByteBackend(backend) return subtle.importKey(format, key, wrappingAlgorithm, false, ['wrapKey']) } - static async importPrivateKey (privateKey: RsaImportableKey): Promise { + static async importPrivateKey( + privateKey: RsaImportableKey + ): Promise { const { wrappingAlgorithm, format, key } = getImportOptions(privateKey) const backend = await getWebCryptoBackend() if (isFullSupportWebCryptoBackend(backend)) { - return backend.subtle.importKey(format, key, wrappingAlgorithm, false, ['unwrapKey']) + return backend.subtle.importKey(format, key, wrappingAlgorithm, false, [ + 'unwrapKey', + ]) } else { return Promise.all([ - backend.nonZeroByteSubtle.importKey(format, key, wrappingAlgorithm, false, ['unwrapKey']), - backend.zeroByteSubtle.importKey(format, key, wrappingAlgorithm, false, ['unwrapKey']) - ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ nonZeroByteCryptoKey, zeroByteCryptoKey })) + backend.nonZeroByteSubtle.importKey( + format, + key, + wrappingAlgorithm, + false, + ['unwrapKey'] + ), + backend.zeroByteSubtle.importKey( + format, + key, + wrappingAlgorithm, + false, + ['unwrapKey'] + ), + ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({ + nonZeroByteCryptoKey, + zeroByteCryptoKey, + })) } } } diff --git a/modules/raw-rsa-keyring-browser/src/types.ts b/modules/raw-rsa-keyring-browser/src/types.ts index 748bf9674..7c031b131 100644 --- a/modules/raw-rsa-keyring-browser/src/types.ts +++ b/modules/raw-rsa-keyring-browser/src/types.ts @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { - MixedBackendCryptoKey, // eslint-disable-line no-unused-vars - AwsEsdkJsCryptoKey // eslint-disable-line no-unused-vars + MixedBackendCryptoKey, + AwsEsdkJsCryptoKey, } from '@aws-crypto/material-management-browser' export enum RsaPadding { - OAEP_SHA1_MFG1 = 'OAEP_SHA1_MFG1', // eslint-disable-line no-unused-vars - OAEP_SHA256_MFG1 = 'OAEP_SHA256_MFG1', // eslint-disable-line no-unused-vars - OAEP_SHA384_MFG1 = 'OAEP_SHA384_MFG1', // eslint-disable-line no-unused-vars - OAEP_SHA512_MFG1 = 'OAEP_SHA512_MFG1', // eslint-disable-line no-unused-vars + OAEP_SHA1_MFG1 = 'OAEP_SHA1_MFG1', + OAEP_SHA256_MFG1 = 'OAEP_SHA256_MFG1', + OAEP_SHA384_MFG1 = 'OAEP_SHA384_MFG1', + OAEP_SHA512_MFG1 = 'OAEP_SHA512_MFG1', } export enum Format { - raw = 'raw', // eslint-disable-line no-unused-vars - pkcs8 = 'pkcs8', // eslint-disable-line no-unused-vars - spki = 'spki', // eslint-disable-line no-unused-vars + raw = 'raw', + pkcs8 = 'pkcs8', + spki = 'spki', } // RSA_PKCS1, https://github.com/aws/aws-encryption-sdk-python/blob/master/src/aws_encryption_sdk/identifiers.py#L262 @@ -24,14 +24,14 @@ export enum JsonWebKeyRsaAlg { 'RSA-OAEP' = 'RSA-OAEP', 'RSA-OAEP-256' = 'RSA-OAEP-256', 'RSA-OAEP-384' = 'RSA-OAEP-384', - 'RSA-OAEP-512' = 'RSA-OAEP-512' + 'RSA-OAEP-512' = 'RSA-OAEP-512', } export enum RsaHash { 'SHA-1' = 'SHA-1', 'SHA-256' = 'SHA-256', 'SHA-384' = 'SHA-384', - 'SHA-512' = 'SHA-512' + 'SHA-512' = 'SHA-512', } export type RsaWrappingKeyHash = Readonly<{ @@ -58,6 +58,6 @@ export type RsaImportableKey = RsaJsonWebKey | BinaryKey export type RawRsaKeyringWebCryptoInput = { keyNamespace: string keyName: string - privateKey?: AwsEsdkJsCryptoKey|MixedBackendCryptoKey + privateKey?: AwsEsdkJsCryptoKey | MixedBackendCryptoKey publicKey?: AwsEsdkJsCryptoKey } diff --git a/modules/raw-rsa-keyring-node/package.json b/modules/raw-rsa-keyring-node/package.json index a752524e3..70acabdba 100644 --- a/modules/raw-rsa-keyring-node/package.json +++ b/modules/raw-rsa-keyring-node/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-rsa-keyring-node/src/oaep_hash_supported.ts b/modules/raw-rsa-keyring-node/src/oaep_hash_supported.ts index 9eb8d1494..a4bfd1514 100644 --- a/modules/raw-rsa-keyring-node/src/oaep_hash_supported.ts +++ b/modules/raw-rsa-keyring-node/src/oaep_hash_supported.ts @@ -23,29 +23,31 @@ * But sending an invalid hash to a version that does not support oaepHash will be ignored. */ -import { - needs -} from '@aws-crypto/material-management-node' +import { needs } from '@aws-crypto/material-management-node' -import { - constants, - publicEncrypt -} from 'crypto' +import { constants, publicEncrypt } from 'crypto' export const oaepHashSupported = (function () { - const key = '-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs7RoNYEPAIws89VV+kra\nrVv/4wbdmUAaAKWgWuxZi5na9GJSmnhCkqyLRm7wPbQY4LCoa5/IMUxkHLsYDPdu\nudY0Qm0GcoxOlvJKHYo4RjF7HyiS34D6dvyO4Gd3aq0mZHoxSGCxW/7hf03wEMzc\niVJXWHXhaI0lD6nrzIEgLrE4L+3V2LeAQjvZsTKd+bYMqeZOL2syiVVIAU8POwAG\nGVBroJoveFm/SUp6lCiN0M2kTeyQA2ax3QTtZSAa8nwrI7U52XOzVmdMicJsy2Pg\nuW98te3MuODdK24yNkHIkYameP/Umf/SJshUJQd5a/TUp3XE+HhOWAumx22tIDlC\nvZS11cuk2fp0WeHUnXaC19N5qWKfvHEKSugzty/z3lGP7ItFhrF2X1qJHeAAsL11\nkjo6Lc48KsE1vKvbnW4VLyB3wdNiVvmUNO29tPXwaR0Q5Gbr3jk3nUzdkEHouHWQ\n41lubOHCCBN3V13mh/MgtNhESHjfmmOnh54ErD9saA1d7CjTf8g2wqmjEqvGSW6N\nq7zhcWR2tp1olflS7oHzul4/I3hnkfL6Kb2xAWWaQKvg3mtsY2OPlzFEP0tR5UcH\nPfp5CeS1Xzg7hN6vRICW6m4l3u2HJFld2akDMm1vnSz8RCbPW7jp7YBxUkWJmypM\ntG7Yv2aGZXGbUtM8o1cZarECAwEAAQ==\n-----END PUBLIC KEY-----' + const key = + '-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs7RoNYEPAIws89VV+kra\nrVv/4wbdmUAaAKWgWuxZi5na9GJSmnhCkqyLRm7wPbQY4LCoa5/IMUxkHLsYDPdu\nudY0Qm0GcoxOlvJKHYo4RjF7HyiS34D6dvyO4Gd3aq0mZHoxSGCxW/7hf03wEMzc\niVJXWHXhaI0lD6nrzIEgLrE4L+3V2LeAQjvZsTKd+bYMqeZOL2syiVVIAU8POwAG\nGVBroJoveFm/SUp6lCiN0M2kTeyQA2ax3QTtZSAa8nwrI7U52XOzVmdMicJsy2Pg\nuW98te3MuODdK24yNkHIkYameP/Umf/SJshUJQd5a/TUp3XE+HhOWAumx22tIDlC\nvZS11cuk2fp0WeHUnXaC19N5qWKfvHEKSugzty/z3lGP7ItFhrF2X1qJHeAAsL11\nkjo6Lc48KsE1vKvbnW4VLyB3wdNiVvmUNO29tPXwaR0Q5Gbr3jk3nUzdkEHouHWQ\n41lubOHCCBN3V13mh/MgtNhESHjfmmOnh54ErD9saA1d7CjTf8g2wqmjEqvGSW6N\nq7zhcWR2tp1olflS7oHzul4/I3hnkfL6Kb2xAWWaQKvg3mtsY2OPlzFEP0tR5UcH\nPfp5CeS1Xzg7hN6vRICW6m4l3u2HJFld2akDMm1vnSz8RCbPW7jp7YBxUkWJmypM\ntG7Yv2aGZXGbUtM8o1cZarECAwEAAQ==\n-----END PUBLIC KEY-----' const oaepHash = 'i_am_not_valid' try { // @ts-ignore - publicEncrypt({ key, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash }, Buffer.from([1, 2, 3, 4])) + publicEncrypt( + { key, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash }, + Buffer.from([1, 2, 3, 4]) + ) /* See note above, * only versions that support oaepHash will respond. * So the only way I can get here is if the option was ignored. */ return false } catch (ex) { - needs(ex.code === 'ERR_OSSL_EVP_INVALID_DIGEST', 'Unexpected error testing oaepHash.') + needs( + ex.code === 'ERR_OSSL_EVP_INVALID_DIGEST', + 'Unexpected error testing oaepHash.' + ) return true } })() diff --git a/modules/raw-rsa-keyring-node/src/raw_rsa_keyring_node.ts b/modules/raw-rsa-keyring-node/src/raw_rsa_keyring_node.ts index a92586013..1353e7e78 100644 --- a/modules/raw-rsa-keyring-node/src/raw_rsa_keyring_node.ts +++ b/modules/raw-rsa-keyring-node/src/raw_rsa_keyring_node.ts @@ -4,16 +4,16 @@ import { KeyringNode, needs, - NodeEncryptionMaterial, // eslint-disable-line no-unused-vars - NodeDecryptionMaterial, // eslint-disable-line no-unused-vars + NodeEncryptionMaterial, + NodeDecryptionMaterial, EncryptedDataKey, - KeyringTrace, // eslint-disable-line no-unused-vars + KeyringTrace, KeyringTraceFlag, immutableClass, readOnlyProperty, unwrapDataKey, - AwsEsdkKeyObject, // eslint-disable-line no-unused-vars - NodeAlgorithmSuite // eslint-disable-line no-unused-vars + AwsEsdkKeyObject, + NodeAlgorithmSuite, } from '@aws-crypto/material-management-node' import { @@ -21,15 +21,15 @@ import { publicEncrypt, privateDecrypt, randomBytes, - RsaPublicKey, // eslint-disable-line no-unused-vars - RsaPrivateKey // eslint-disable-line no-unused-vars + RsaPublicKey, + RsaPrivateKey, } from 'crypto' import { _onEncrypt, _onDecrypt, - WrapKey, // eslint-disable-line no-unused-vars - UnwrapKey // eslint-disable-line no-unused-vars + WrapKey, + UnwrapKey, } from '@aws-crypto/raw-keyring' import { oaepHashSupported } from './oaep_hash_supported' @@ -49,8 +49,14 @@ interface RsaKey { privateKey?: string | Buffer | AwsEsdkKeyObject } -export type OaepHash = 'sha1'|'sha256'|'sha384'|'sha512'|undefined -const supportedOaepHash: OaepHash[] = ['sha1', 'sha256', 'sha384', 'sha512', undefined] +export type OaepHash = 'sha1' | 'sha256' | 'sha384' | 'sha512' | undefined +const supportedOaepHash: OaepHash[] = [ + 'sha1', + 'sha256', + 'sha384', + 'sha512', + undefined, +] export type RawRsaKeyringNodeInput = { keyNamespace: string @@ -66,51 +72,77 @@ export class RawRsaKeyringNode extends KeyringNode { _wrapKey!: WrapKey _unwrapKey!: UnwrapKey - constructor (input: RawRsaKeyringNodeInput) { + constructor(input: RawRsaKeyringNodeInput) { super() - const { rsaKey, keyName, keyNamespace, padding = constants.RSA_PKCS1_OAEP_PADDING, oaepHash } = input + const { + rsaKey, + keyName, + keyNamespace, + padding = constants.RSA_PKCS1_OAEP_PADDING, + oaepHash, + } = input const { publicKey, privateKey } = rsaKey /* Precondition: RsaKeyringNode needs either a public or a private key to operate. */ needs(publicKey || privateKey, 'No Key provided.') /* Precondition: RsaKeyringNode needs identifying information for encrypt and decrypt. */ needs(keyName && keyNamespace, 'Identifying information must be defined.') /* Precondition: The AWS ESDK only supports specific hash values for OAEP padding. */ - needs(padding === constants.RSA_PKCS1_OAEP_PADDING - ? oaepHashSupported - ? supportedOaepHash.includes(oaepHash) - : !oaepHash || oaepHash === 'sha1' - : !oaepHash, 'Unsupported oaepHash') + needs( + padding === constants.RSA_PKCS1_OAEP_PADDING + ? oaepHashSupported + ? supportedOaepHash.includes(oaepHash) + : !oaepHash || oaepHash === 'sha1' + : !oaepHash, + 'Unsupported oaepHash' + ) const _wrapKey = async (material: NodeEncryptionMaterial) => { /* Precondition: Public key must be defined to support encrypt. */ - if (!publicKey) throw new Error('No public key defined in constructor. Encrypt disabled.') - const { buffer, byteOffset, byteLength } = unwrapDataKey(material.getUnencryptedDataKey()) + if (!publicKey) + throw new Error( + 'No public key defined in constructor. Encrypt disabled.' + ) + const { buffer, byteOffset, byteLength } = unwrapDataKey( + material.getUnencryptedDataKey() + ) const encryptedDataKey = publicEncrypt( { key: publicKey, padding, oaepHash } as RsaPublicKey, - Buffer.from(buffer, byteOffset, byteLength)) + Buffer.from(buffer, byteOffset, byteLength) + ) const providerInfo = this.keyName const providerId = this.keyNamespace const flag = KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY - const edk = new EncryptedDataKey({ encryptedDataKey, providerInfo, providerId }) + const edk = new EncryptedDataKey({ + encryptedDataKey, + providerInfo, + providerId, + }) return material.addEncryptedDataKey(edk, flag) } - const _unwrapKey = async (material: NodeDecryptionMaterial, edk: EncryptedDataKey) => { + const _unwrapKey = async ( + material: NodeDecryptionMaterial, + edk: EncryptedDataKey + ) => { /* Precondition: Private key must be defined to support decrypt. */ - if (!privateKey) throw new Error('No private key defined in constructor. Decrypt disabled.') + if (!privateKey) + throw new Error( + 'No private key defined in constructor. Decrypt disabled.' + ) const trace: KeyringTrace = { keyName: this.keyName, keyNamespace: this.keyNamespace, - flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY + flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY, } const { buffer, byteOffset, byteLength } = edk.encryptedDataKey const encryptedDataKey = Buffer.from(buffer, byteOffset, byteLength) const unencryptedDataKey = privateDecrypt( { key: privateKey, padding, oaepHash } as RsaPrivateKey, - encryptedDataKey) + encryptedDataKey + ) return material.setUnencryptedDataKey(unencryptedDataKey, trace) } @@ -120,19 +152,21 @@ export class RawRsaKeyringNode extends KeyringNode { readOnlyProperty(this, '_unwrapKey', _unwrapKey) } - _filter ({ providerId, providerInfo }: EncryptedDataKey) { + _filter({ providerId, providerInfo }: EncryptedDataKey) { const { keyNamespace, keyName } = this return providerId === keyNamespace && providerInfo === keyName } - _onEncrypt = _onEncrypt(randomBytesAsync) + _onEncrypt = _onEncrypt( + randomBytesAsync + ) _onDecrypt = _onDecrypt() } immutableClass(RawRsaKeyringNode) -function randomBytesAsync (size: number): Promise { +async function randomBytesAsync(size: number): Promise { return new Promise((resolve, reject) => { - randomBytes(size, (err: Error|null, buffer: Buffer) => { + randomBytes(size, (err: Error | null, buffer: Buffer) => { if (err) return reject(err) resolve(buffer) }) diff --git a/modules/serialize/package.json b/modules/serialize/package.json index 71415b312..77995a784 100644 --- a/modules/serialize/package.json +++ b/modules/serialize/package.json @@ -3,7 +3,9 @@ "version": "1.0.3", "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/serialize/src/aad_factory.ts b/modules/serialize/src/aad_factory.ts index 19d703e35..8bc158052 100644 --- a/modules/serialize/src/aad_factory.ts +++ b/modules/serialize/src/aad_factory.ts @@ -12,18 +12,24 @@ */ import BN from 'bn.js' -import { ContentAADString, ContentType } from './identifiers' // eslint-disable-line no-unused-vars -import { BinaryData } from './types' // eslint-disable-line no-unused-vars +import { ContentAADString, ContentType } from './identifiers' +import { BinaryData } from './types' import { concatBuffers } from './concat_buffers' import { uInt32BE } from './uint_util' -export function aadFactory (fromUtf8: (input: string) => Uint8Array) { +export function aadFactory(fromUtf8: (input: string) => Uint8Array) { return { messageAADContentString, - messageAAD + messageAAD, } - function messageAADContentString ({ contentType, isFinalFrame }: {contentType: ContentType, isFinalFrame: boolean}) { + function messageAADContentString({ + contentType, + isFinalFrame, + }: { + contentType: ContentType + isFinalFrame: boolean + }) { switch (contentType) { case ContentType.NO_FRAMING: return ContentAADString.NON_FRAMED_STRING_ID @@ -36,7 +42,12 @@ export function aadFactory (fromUtf8: (input: string) => Uint8Array) { } } - function messageAAD (messageId: BinaryData, aadContentString: ContentAADString, seqNum: number, contentLength: number) { + function messageAAD( + messageId: BinaryData, + aadContentString: ContentAADString, + seqNum: number, + contentLength: number + ) { return concatBuffers( messageId, fromUtf8(aadContentString), diff --git a/modules/serialize/src/concat_buffers.ts b/modules/serialize/src/concat_buffers.ts index 61471a915..54f00c138 100644 --- a/modules/serialize/src/concat_buffers.ts +++ b/modules/serialize/src/concat_buffers.ts @@ -1,23 +1,27 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { BinaryData } from './types' // eslint-disable-line no-unused-vars +import { BinaryData } from './types' -export function concatBuffers (...inputBuffers: (BinaryData|ArrayBufferView)[]) { - const neededLength = inputBuffers.reduce((sum, buff) => sum + buff.byteLength, 0) +export function concatBuffers( + ...inputBuffers: (BinaryData | ArrayBufferView)[] +) { + const neededLength = inputBuffers.reduce( + (sum, buff) => sum + buff.byteLength, + 0 + ) const outputBuffer = new Uint8Array(neededLength) let offset = 0 - inputBuffers - .forEach(buff => { - if (ArrayBuffer.isView(buff)) { - const { buffer, byteOffset, byteLength } = buff - outputBuffer.set(new Uint8Array(buffer, byteOffset, byteLength), offset) - } else { - outputBuffer.set(new Uint8Array(buff), offset) - } - offset += buff.byteLength - }) + inputBuffers.forEach((buff) => { + if (ArrayBuffer.isView(buff)) { + const { buffer, byteOffset, byteLength } = buff + outputBuffer.set(new Uint8Array(buffer, byteOffset, byteLength), offset) + } else { + outputBuffer.set(new Uint8Array(buff), offset) + } + offset += buff.byteLength + }) return outputBuffer } diff --git a/modules/serialize/src/decode_body_header.ts b/modules/serialize/src/decode_body_header.ts index 229f68683..225ab7f21 100644 --- a/modules/serialize/src/decode_body_header.ts +++ b/modules/serialize/src/decode_body_header.ts @@ -3,7 +3,12 @@ import BN from 'bn.js' import { ContentType, SequenceIdentifier } from './identifiers' -import { HeaderInfo, BodyHeader, FrameBodyHeader, NonFrameBodyHeader } from './types' // eslint-disable-line no-unused-vars +import { + HeaderInfo, + BodyHeader, + FrameBodyHeader, + NonFrameBodyHeader, +} from './types' import { needs } from '@aws-crypto/material-management' /* @@ -30,9 +35,16 @@ import { needs } from '@aws-crypto/material-management' * @param readPos number * @returns BodyHeader|false */ -export function decodeBodyHeader (buffer: Uint8Array, headerInfo: HeaderInfo, readPos: number): BodyHeader|false { +export function decodeBodyHeader( + buffer: Uint8Array, + headerInfo: HeaderInfo, + readPos: number +): BodyHeader | false { /* Precondition: The contentType must be a supported format. */ - needs(ContentType[headerInfo.messageHeader.contentType], 'Unknown contentType') + needs( + ContentType[headerInfo.messageHeader.contentType], + 'Unknown contentType' + ) switch (headerInfo.messageHeader.contentType) { case ContentType.FRAMED_DATA: @@ -49,9 +61,16 @@ export function decodeBodyHeader (buffer: Uint8Array, headerInfo: HeaderInfo, re * @param headerInfo HeaderInfo * @param readPos number */ -export function decodeFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInfo, readPos: number): FrameBodyHeader|false { +export function decodeFrameBodyHeader( + buffer: Uint8Array, + headerInfo: HeaderInfo, + readPos: number +): FrameBodyHeader | false { /* Precondition: The contentType must be FRAMED_DATA. */ - needs(ContentType.FRAMED_DATA === headerInfo.messageHeader.contentType, 'Unknown contentType') + needs( + ContentType.FRAMED_DATA === headerInfo.messageHeader.contentType, + 'Unknown contentType' + ) const { frameLength } = headerInfo.messageHeader const { ivLength, tagLength } = headerInfo.algorithmSuite @@ -69,7 +88,10 @@ export function decodeFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInf ) /* Precondition: decodeFrameBodyHeader readPos must be within the byte length of the buffer given. */ - needs(dataView.byteLength >= readPos && readPos >= 0, 'readPos out of bounds.') + needs( + dataView.byteLength >= readPos && readPos >= 0, + 'readPos out of bounds.' + ) /* Check for early return (Postcondition): There must be enough data to decodeFrameBodyHeader. * The format expressed here is @@ -86,7 +108,7 @@ export function decodeFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInf return decodeFinalFrameBodyHeader(buffer, headerInfo, readPos) } - const iv = buffer.slice(readPos += 4, readPos += ivLength) + const iv = buffer.slice((readPos += 4), (readPos += ivLength)) return { sequenceNumber, iv, @@ -94,7 +116,7 @@ export function decodeFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInf readPos, tagLength, isFinalFrame: false, - contentType: ContentType.FRAMED_DATA + contentType: ContentType.FRAMED_DATA, } } @@ -104,9 +126,16 @@ export function decodeFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInf * @param headerInfo HeaderInfo * @param readPos number */ -export function decodeFinalFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInfo, readPos: number): FrameBodyHeader|false { +export function decodeFinalFrameBodyHeader( + buffer: Uint8Array, + headerInfo: HeaderInfo, + readPos: number +): FrameBodyHeader | false { /* Precondition: The contentType must be FRAMED_DATA to be a Final Frame. */ - needs(ContentType.FRAMED_DATA === headerInfo.messageHeader.contentType, 'Unknown contentType') + needs( + ContentType.FRAMED_DATA === headerInfo.messageHeader.contentType, + 'Unknown contentType' + ) const { ivLength, tagLength } = headerInfo.algorithmSuite @@ -123,7 +152,10 @@ export function decodeFinalFrameBodyHeader (buffer: Uint8Array, headerInfo: Head ) /* Precondition: decodeFinalFrameBodyHeader readPos must be within the byte length of the buffer given. */ - needs(dataView.byteLength >= readPos && readPos >= 0, 'readPos out of bounds.') + needs( + dataView.byteLength >= readPos && readPos >= 0, + 'readPos out of bounds.' + ) /* Check for early return (Postcondition): There must be enough data to decodeFinalFrameBodyHeader. * The format expressed here is * SEQUENCE_NUMBER_END: Uint32(FFFF) @@ -137,14 +169,20 @@ export function decodeFinalFrameBodyHeader (buffer: Uint8Array, headerInfo: Head /* The precondition SEQUENCE_NUMBER_END: Uint32(FFFF) is handled above. */ const sequenceEnd = dataView.getUint32(readPos, false) // big endian /* Postcondition: sequenceEnd must be SEQUENCE_NUMBER_END. */ - needs(sequenceEnd === SequenceIdentifier.SEQUENCE_NUMBER_END, 'Malformed final frame: Invalid sequence number end value') - const sequenceNumber = dataView.getUint32(readPos += 4, false) // big endian + needs( + sequenceEnd === SequenceIdentifier.SEQUENCE_NUMBER_END, + 'Malformed final frame: Invalid sequence number end value' + ) + const sequenceNumber = dataView.getUint32((readPos += 4), false) // big endian /* Postcondition: decodeFinalFrameBodyHeader sequenceNumber must be greater than 0. */ needs(sequenceNumber > 0, 'Malformed sequenceNumber.') - const iv = buffer.slice(readPos += 4, readPos += ivLength) + const iv = buffer.slice((readPos += 4), (readPos += ivLength)) const contentLength = dataView.getUint32(readPos) /* Postcondition: The final frame MUST NOT exceed the frameLength. */ - needs(headerInfo.messageHeader.frameLength >= contentLength, 'Final frame length exceeds frame length.') + needs( + headerInfo.messageHeader.frameLength >= contentLength, + 'Final frame length exceeds frame length.' + ) return { sequenceNumber, iv, @@ -152,7 +190,7 @@ export function decodeFinalFrameBodyHeader (buffer: Uint8Array, headerInfo: Head readPos: readPos + 4, tagLength, isFinalFrame: true, - contentType: ContentType.FRAMED_DATA + contentType: ContentType.FRAMED_DATA, } } @@ -162,9 +200,16 @@ export function decodeFinalFrameBodyHeader (buffer: Uint8Array, headerInfo: Head * @param headerInfo HeaderInfo * @param readPos number */ -export function decodeNonFrameBodyHeader (buffer: Uint8Array, headerInfo: HeaderInfo, readPos: number): NonFrameBodyHeader|false { +export function decodeNonFrameBodyHeader( + buffer: Uint8Array, + headerInfo: HeaderInfo, + readPos: number +): NonFrameBodyHeader | false { /* Precondition: The contentType must be NO_FRAMING. */ - needs(ContentType.NO_FRAMING === headerInfo.messageHeader.contentType, 'Unknown contentType') + needs( + ContentType.NO_FRAMING === headerInfo.messageHeader.contentType, + 'Unknown contentType' + ) const { ivLength, tagLength } = headerInfo.algorithmSuite @@ -181,17 +226,20 @@ export function decodeNonFrameBodyHeader (buffer: Uint8Array, headerInfo: Header ) /* Precondition: decodeNonFrameBodyHeader readPos must be within the byte length of the buffer given. */ - needs(dataView.byteLength >= readPos && readPos >= 0, 'readPos out of bounds.') + needs( + dataView.byteLength >= readPos && readPos >= 0, + 'readPos out of bounds.' + ) /* Check for early return (Postcondition): There must be enough data to decodeNonFrameBodyHeader. - * The format expressed here is - * IVLength: Uint8 - * ContentLength: Uint64 - */ + * The format expressed here is + * IVLength: Uint8 + * ContentLength: Uint64 + */ if (ivLength + 8 + readPos > dataView.byteLength) return false - const iv = buffer.slice(readPos, readPos += ivLength) - const contentLengthBuff = buffer.slice(readPos, readPos += 8) + const iv = buffer.slice(readPos, (readPos += ivLength)) + const contentLengthBuff = buffer.slice(readPos, (readPos += 8)) const contentLengthBN = new BN([...contentLengthBuff], 16, 'be') // This will throw if the number is larger than Number.MAX_SAFE_INTEGER. // i.e. a 53 bit number @@ -203,6 +251,6 @@ export function decodeNonFrameBodyHeader (buffer: Uint8Array, headerInfo: Header readPos, tagLength, isFinalFrame: true, - contentType: ContentType.NO_FRAMING + contentType: ContentType.NO_FRAMING, } } diff --git a/modules/serialize/src/deserialize_factory.ts b/modules/serialize/src/deserialize_factory.ts index 525496d67..24a94e7ab 100644 --- a/modules/serialize/src/deserialize_factory.ts +++ b/modules/serialize/src/deserialize_factory.ts @@ -11,25 +11,25 @@ */ import { - IvLength, // eslint-disable-line no-unused-vars + IvLength, AlgorithmSuiteIdentifier, - AlgorithmSuite, // eslint-disable-line no-unused-vars + AlgorithmSuite, EncryptedDataKey, - EncryptionContext, // eslint-disable-line no-unused-vars - needs + EncryptionContext, + needs, } from '@aws-crypto/material-management' -import { HeaderInfo, AlgorithmSuiteConstructor, MessageHeader } from './types' // eslint-disable-line no-unused-vars +import { HeaderInfo, AlgorithmSuiteConstructor, MessageHeader } from './types' import { readElements } from './read_element' // To deal with Browser and Node.js I inject a function to handle utf8 encoding. -export function deserializeFactory ( +export function deserializeFactory( toUtf8: (input: Uint8Array) => string, SdkSuite: AlgorithmSuiteConstructor ) { return { deserializeMessageHeader, deserializeEncryptedDataKeys, - decodeEncryptionContext + decodeEncryptionContext, } /** @@ -43,7 +43,9 @@ export function deserializeFactory ( * @param messageBuffer * @returns HeaderInfo|undefined */ - function deserializeMessageHeader (messageBuffer: Uint8Array): HeaderInfo|false { + function deserializeMessageHeader( + messageBuffer: Uint8Array + ): HeaderInfo | false { /* Uint8Array is a view on top of the underlying ArrayBuffer. * This means that raw underlying memory stored in the ArrayBuffer * may be larger than the Uint8Array. This is especially true of @@ -65,10 +67,14 @@ export function deserializeFactory ( const version = dataView.getUint8(0) const type = dataView.getUint8(1) /* Precondition: version and type must be the required values. */ - needs(version === 1 && type === 128, - version === 65 && type === 89 ? 'Malformed Header: This blob may be base64 encoded.' : 'Malformed Header.') + needs( + version === 1 && type === 128, + version === 65 && type === 89 + ? 'Malformed Header: This blob may be base64 encoded.' + : 'Malformed Header.' + ) - const suiteId = dataView.getUint16(2, false) // big endian + const suiteId = dataView.getUint16(2, false) as AlgorithmSuiteIdentifier // big endian /* Precondition: suiteId must match supported algorithm suite */ needs(AlgorithmSuiteIdentifier[suiteId], 'Unsupported algorithm suite.') const messageId = messageBuffer.slice(4, 20) @@ -79,8 +85,13 @@ export function deserializeFactory ( */ if (22 + contextLength > dataView.byteLength) return false // not enough data - const encryptionContext = decodeEncryptionContext(messageBuffer.slice(22, 22 + contextLength)) - const dataKeyInfo = deserializeEncryptedDataKeys(messageBuffer, 22 + contextLength) + const encryptionContext = decodeEncryptionContext( + messageBuffer.slice(22, 22 + contextLength) + ) + const dataKeyInfo = deserializeEncryptedDataKeys( + messageBuffer, + 22 + contextLength + ) /* Check for early return (Postcondition): Not Enough Data. deserializeEncryptedDataKeys will return false if it does not have enough data. * This is the second variable length section. @@ -98,7 +109,8 @@ export function deserializeFactory ( const headerLength = readPos + 1 + 4 + 1 + 4 /* Check for early return (Postcondition): Not Enough Data. Need to have the remaining fixed length data to parse. */ - if (headerLength + ivLength + tagLengthBytes > dataView.byteLength) return false // not enough data + if (headerLength + ivLength + tagLengthBytes > dataView.byteLength) + return false // not enough data const contentType = dataView.getUint8(readPos) const reservedBytes = dataView.getUint32(readPos + 1, false) // big endian @@ -106,7 +118,7 @@ export function deserializeFactory ( * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-reserved */ needs(reservedBytes === 0, 'Malformed Header') - const headerIvLength = dataView.getUint8(readPos + 1 + 4) + const headerIvLength = dataView.getUint8(readPos + 1 + 4) as IvLength /* Postcondition: The headerIvLength must match the algorithm suite specification. */ needs(headerIvLength === ivLength, 'Malformed Header') const frameLength = dataView.getUint32(readPos + 1 + 4 + 1, false) // big endian @@ -121,11 +133,14 @@ export function deserializeFactory ( encryptedDataKeys, contentType, headerIvLength, - frameLength + frameLength, } const headerIv = messageBuffer.slice(headerLength, headerLength + ivLength) - const headerAuthTag = messageBuffer.slice(headerLength + ivLength, headerLength + ivLength + tagLengthBytes) + const headerAuthTag = messageBuffer.slice( + headerLength + ivLength, + headerLength + ivLength + tagLengthBytes + ) return { messageHeader, @@ -133,7 +148,7 @@ export function deserializeFactory ( rawHeader, algorithmSuite, headerIv, - headerAuthTag + headerAuthTag, } } @@ -142,9 +157,17 @@ export function deserializeFactory ( * @param buffer Uint8Array * @param startPos number */ - function deserializeEncryptedDataKeys (buffer: Uint8Array, startPos: number): {encryptedDataKeys: ReadonlyArray, readPos: number}|false { + function deserializeEncryptedDataKeys( + buffer: Uint8Array, + startPos: number + ): + | { encryptedDataKeys: ReadonlyArray; readPos: number } + | false { /* Precondition: startPos must be within the byte length of the buffer given. */ - needs(buffer.byteLength >= startPos && startPos >= 0, 'startPos out of bounds.') + needs( + buffer.byteLength >= startPos && startPos >= 0, + 'startPos out of bounds.' + ) /* Check for early return (Postcondition): Need to have at least Uint16 (2) bytes of data. */ if (startPos + 2 > buffer.byteLength) return false @@ -165,7 +188,12 @@ export function deserializeFactory ( /* Precondition: There must be at least 1 EncryptedDataKey element. */ needs(encryptedDataKeysCount, 'No EncryptedDataKey found.') - const elementInfo = readElements(encryptedDataKeysCount, 3, buffer, startPos + 2) + const elementInfo = readElements( + encryptedDataKeysCount, + 3, + buffer, + startPos + 2 + ) /* Check for early return (Postcondition): readElement will return false if there is not enough data. * I can only continue if I have at least the entire EDK section. */ @@ -173,10 +201,15 @@ export function deserializeFactory ( const { elements, readPos } = elementInfo const encryptedDataKeys = elements.map( - ([rawId, rawInfo, encryptedDataKey], _) => { + ([rawId, rawInfo, encryptedDataKey]) => { const providerId = toUtf8(rawId) const providerInfo = toUtf8(rawInfo) - return new EncryptedDataKey({ providerInfo, providerId, encryptedDataKey, rawInfo }) + return new EncryptedDataKey({ + providerInfo, + providerId, + encryptedDataKey, + rawInfo, + }) } ) Object.freeze(encryptedDataKeys) @@ -187,7 +220,7 @@ export function deserializeFactory ( * Exported for testing. Used by deserializeMessageHeader to compose a complete solution. * @param encodedEncryptionContext Uint8Array */ - function decodeEncryptionContext (encodedEncryptionContext: Uint8Array) { + function decodeEncryptionContext(encodedEncryptionContext: Uint8Array) { const encryptionContext: EncryptionContext = Object.create(null) /* Check for early return (Postcondition): The case of 0 length is defined as an empty object. */ if (!encodedEncryptionContext.byteLength) { @@ -214,14 +247,20 @@ export function deserializeFactory ( const { elements, readPos } = elementInfo /* Postcondition: The byte length of the encodedEncryptionContext must match the readPos. */ - needs(encodedEncryptionContext.byteLength === readPos, 'Overflow, too much data.') + needs( + encodedEncryptionContext.byteLength === readPos, + 'Overflow, too much data.' + ) for (let count = 0; count < pairsCount; count++) { const [key, value] = elements[count].map(toUtf8) /* Postcondition: The number of keys in the encryptionContext must match the pairsCount. * If the same Key value is serialized... */ - needs(encryptionContext[key] === undefined, 'Duplicate encryption context key value.') + needs( + encryptionContext[key] === undefined, + 'Duplicate encryption context key value.' + ) encryptionContext[key] = value } Object.freeze(encryptionContext) diff --git a/modules/serialize/src/ecdsa_signature.ts b/modules/serialize/src/ecdsa_signature.ts index 1eb36f317..66b6bcbce 100644 --- a/modules/serialize/src/ecdsa_signature.ts +++ b/modules/serialize/src/ecdsa_signature.ts @@ -12,22 +12,19 @@ import asn from 'asn1.js' import { concatBuffers } from './concat_buffers' import { needs, - WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars - WebCryptoECDHCurve // eslint-disable-line no-unused-vars + WebCryptoAlgorithmSuite, + WebCryptoECDHCurve, } from '@aws-crypto/material-management' // https://tools.ietf.org/html/rfc3279#section-2.2.2 const ECDSASignature = asn.define('ECDSASignature', function (this: any) { - this.seq().obj( - this.key('r').int(), - this.key('s').int() - ) + this.seq().obj(this.key('r').int(), this.key('s').int()) }) // Map the ECDSA Curve to key lengths -const keyLengthBytes : {[key in WebCryptoECDHCurve]: number} = Object.freeze({ +const keyLengthBytes: { [key in WebCryptoECDHCurve]: number } = Object.freeze({ 'P-256': 32, - 'P-384': 48 + 'P-384': 48, }) /** @@ -38,14 +35,21 @@ const keyLengthBytes : {[key in WebCryptoECDHCurve]: number} = Object.freeze({ * @param suite [WebCryptoAlgorithmSuite] The Algorithm suite used to create the signature * @returns Uint8Array The raw formated signature (r,s) used to verify in WebCrypto */ -export function der2raw (derSignature: Uint8Array, { signatureCurve }: WebCryptoAlgorithmSuite): Uint8Array { +export function der2raw( + derSignature: Uint8Array, + { signatureCurve }: WebCryptoAlgorithmSuite +): Uint8Array { /* Precondition: Do not attempt to RAW format if the suite does not support signing. */ - if (!signatureCurve) throw new Error('AlgorithmSuite does not support signing') + if (!signatureCurve) + throw new Error('AlgorithmSuite does not support signing') const _keyLengthBytes = keyLengthBytes[signatureCurve] // A little more portable than Buffer.from, but not much - const { r, s } = ECDSASignature.decode(new asn.bignum.BN(derSignature).toArrayLike(Buffer), 'der') + const { r, s } = ECDSASignature.decode( + new asn.bignum.BN(derSignature).toArrayLike(Buffer), + 'der' + ) const rLength = r.byteLength() const sLength = r.byteLength() @@ -67,9 +71,13 @@ export function der2raw (derSignature: Uint8Array, { signatureCurve }: WebCrypto * @param suite [WebCryptoAlgorithmSuite] The Algorithm suite used to create the signature * @returns Uint8Array The DER formated signature */ -export function raw2der (rawSignature: Uint8Array, { signatureCurve }: WebCryptoAlgorithmSuite): Uint8Array { +export function raw2der( + rawSignature: Uint8Array, + { signatureCurve }: WebCryptoAlgorithmSuite +): Uint8Array { /* Precondition: Do not attempt to DER format if the suite does not support signing. */ - if (!signatureCurve) throw new Error('AlgorithmSuite does not support signing') + if (!signatureCurve) + throw new Error('AlgorithmSuite does not support signing') const { byteLength } = rawSignature diff --git a/modules/serialize/src/identifiers.ts b/modules/serialize/src/identifiers.ts index 4d8e6a7f9..b85ec4103 100644 --- a/modules/serialize/src/identifiers.ts +++ b/modules/serialize/src/identifiers.ts @@ -20,36 +20,36 @@ export const ENCODED_SIGNER_KEY = 'aws-crypto-public-key' export enum SerializationVersion { - V1 = 1 // eslint-disable-line no-unused-vars + V1 = 1, } Object.freeze(SerializationVersion) export enum ContentType { - NO_FRAMING = 1, // eslint-disable-line no-unused-vars - FRAMED_DATA = 2 // eslint-disable-line no-unused-vars + NO_FRAMING = 1, + FRAMED_DATA = 2, } Object.freeze(ContentType) export enum ContentAADString { - FRAME_STRING_ID = 'AWSKMSEncryptionClient Frame', // eslint-disable-line no-unused-vars - FINAL_FRAME_STRING_ID = 'AWSKMSEncryptionClient Final Frame', // eslint-disable-line no-unused-vars - NON_FRAMED_STRING_ID = 'AWSKMSEncryptionClient Single Block', // eslint-disable-line no-unused-vars + FRAME_STRING_ID = 'AWSKMSEncryptionClient Frame', + FINAL_FRAME_STRING_ID = 'AWSKMSEncryptionClient Final Frame', + NON_FRAMED_STRING_ID = 'AWSKMSEncryptionClient Single Block', } Object.freeze(ContentAADString) export enum ObjectType { - CUSTOMER_AE_DATA = 128 // eslint-disable-line no-unused-vars + CUSTOMER_AE_DATA = 128, } Object.freeze(ObjectType) export enum SequenceIdentifier { - SEQUENCE_NUMBER_END = 0xFFFFFFFF // eslint-disable-line no-unused-vars + SEQUENCE_NUMBER_END = 0xffffffff, } Object.freeze(SequenceIdentifier) export enum Maximum { // Maximum number of messages which are allowed to be encrypted under a single cached data key - MESSAGES_PER_CACHED_KEY_LIMIT = 2 ** 32, // eslint-disable-line no-unused-vars + MESSAGES_PER_CACHED_KEY_LIMIT = 2 ** 32, /* Maximum number of bytes that are allowed to be encrypted * under a single cached data key across messages. * The maximum value defined in the AWS Encryption SDK specification is 2 ** 63 - 1. @@ -67,22 +67,22 @@ export enum Maximum { * This is because in the future Number.MAX_SAFE_INTEGER could be raised to 2 ** 66 * or some value larger 2 ** 63. */ - BYTES_PER_CACHED_KEY_LIMIT = 2 ** 53 - 1, // eslint-disable-line no-unused-vars + BYTES_PER_CACHED_KEY_LIMIT = 2 ** 53 - 1, /* This value should be Maximum.FRAME_COUNT * Maximum.FRAME_SIZE. * However this would be ~ 2 ** 64, much larger than Number.MAX_SAFE_INTEGER. * For the same reasons outlined above in BYTES_PER_CACHED_KEY_LIMIT * this value is set to 2 ** 53 - 1. */ - BYTES_PER_MESSAGE = 2 ** 53 - 1, // eslint-disable-line no-unused-vars + BYTES_PER_MESSAGE = 2 ** 53 - 1, // Maximum number of frames allowed in one message as defined in specification - FRAME_COUNT = 2 ** 32 - 1, // eslint-disable-line no-unused-vars + FRAME_COUNT = 2 ** 32 - 1, // Maximum bytes allowed in a single frame as defined in specification - FRAME_SIZE = 2 ** 32 - 1, // eslint-disable-line no-unused-vars + FRAME_SIZE = 2 ** 32 - 1, // Maximum bytes allowed in a non-framed message ciphertext as defined in specification - GCM_CONTENT_SIZE = 2 ** 32 - 1, // eslint-disable-line no-unused-vars - NON_FRAMED_SIZE = 2 ** 32 - 1, // eslint-disable-line no-unused-vars + GCM_CONTENT_SIZE = 2 ** 32 - 1, + NON_FRAMED_SIZE = 2 ** 32 - 1, // Maximum number of AAD bytes allowed as defined in specification - AAD_BYTE_SIZE = 2 ** 16 - 1, // eslint-disable-line no-unused-vars + AAD_BYTE_SIZE = 2 ** 16 - 1, } Object.freeze(Maximum) diff --git a/modules/serialize/src/kdf_info.ts b/modules/serialize/src/kdf_info.ts index d5544c7cc..1a7a27cf1 100644 --- a/modules/serialize/src/kdf_info.ts +++ b/modules/serialize/src/kdf_info.ts @@ -11,14 +11,14 @@ * The Key Derivation Algorithm section */ -import { AlgorithmSuiteIdentifier } from '@aws-crypto/material-management' // eslint-disable-line no-unused-vars -import { BinaryData } from './types' // eslint-disable-line no-unused-vars +import { AlgorithmSuiteIdentifier } from '@aws-crypto/material-management' +import { BinaryData } from './types' import { concatBuffers } from './concat_buffers' import { uInt16BE } from './uint_util' -export function kdfInfo (suiteId: AlgorithmSuiteIdentifier, messageId: BinaryData) { - return concatBuffers( - uInt16BE(suiteId), - messageId - ) +export function kdfInfo( + suiteId: AlgorithmSuiteIdentifier, + messageId: BinaryData +) { + return concatBuffers(uInt16BE(suiteId), messageId) } diff --git a/modules/serialize/src/read_element.ts b/modules/serialize/src/read_element.ts index 6ef4e4aaa..4089cd234 100644 --- a/modules/serialize/src/read_element.ts +++ b/modules/serialize/src/read_element.ts @@ -29,11 +29,11 @@ import { needs } from '@aws-crypto/material-management' * @param buffer * @param readPos */ -export function readElements ( +export function readElements( elementCount: number, fieldsPerElement: number, buffer: Uint8Array, - readPos: number = 0 + readPos = 0 ) { /* Uint8Array is a view on top of the underlying ArrayBuffer. * This means that raw underlying memory stored in the ArrayBuffer @@ -49,13 +49,19 @@ export function readElements ( const elements = [] /* Precondition: readPos must be non-negative and within the byte length of the buffer given. */ - needs(readPos >= 0 && dataView.byteLength >= readPos, 'readPos out of bounds.') + needs( + readPos >= 0 && dataView.byteLength >= readPos, + 'readPos out of bounds.' + ) /* Precondition: elementCount and fieldsPerElement must be non-negative. */ - needs(elementCount >= 0 && fieldsPerElement >= 0, 'elementCount and fieldsPerElement must be positive.') + needs( + elementCount >= 0 && fieldsPerElement >= 0, + 'elementCount and fieldsPerElement must be positive.' + ) while (elementCount--) { - let element = [] + const element = [] let fieldCount = fieldsPerElement while (fieldCount--) { /* Check for early return (Postcondition): Enough data must exist to read the Uint16 length value. */ diff --git a/modules/serialize/src/serialize_factory.ts b/modules/serialize/src/serialize_factory.ts index abb250783..0589bac1c 100644 --- a/modules/serialize/src/serialize_factory.ts +++ b/modules/serialize/src/serialize_factory.ts @@ -11,12 +11,17 @@ */ import { concatBuffers } from './concat_buffers' -import { IvLength, EncryptionContext, needs, EncryptedDataKey } from '@aws-crypto/material-management' // eslint-disable-line no-unused-vars +import { + IvLength, + EncryptionContext, + needs, + EncryptedDataKey, +} from '@aws-crypto/material-management' import { SequenceIdentifier } from './identifiers' import { uInt16BE, uInt8, uInt32BE } from './uint_util' -import { MessageHeader } from './types' // eslint-disable-line no-unused-vars +import { MessageHeader } from './types' -export function serializeFactory (fromUtf8: (input: any) => Uint8Array) { +export function serializeFactory(fromUtf8: (input: any) => Uint8Array) { return { frameIv, nonFramedBodyIv, @@ -27,15 +32,19 @@ export function serializeFactory (fromUtf8: (input: any) => Uint8Array) { serializeEncryptionContext, serializeEncryptedDataKeys, serializeEncryptedDataKey, - serializeMessageHeader + serializeMessageHeader, } - function frameIv (ivLength: IvLength, sequenceNumber: number) { + function frameIv(ivLength: IvLength, sequenceNumber: number) { /* Precondition: sequenceNumber must conform to the specification. i.e. 1 - (2^32 - 1) * The sequence number starts at 1 * https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/data-format/message-body.md#sequence-number */ - needs(sequenceNumber > 0 && SequenceIdentifier.SEQUENCE_NUMBER_END >= sequenceNumber, 'sequenceNumber out of bounds') + needs( + sequenceNumber > 0 && + SequenceIdentifier.SEQUENCE_NUMBER_END >= sequenceNumber, + 'sequenceNumber out of bounds' + ) const buff = new Uint8Array(ivLength) const view = new DataView(buff.buffer, buff.byteOffset, buff.byteLength) @@ -43,36 +52,51 @@ export function serializeFactory (fromUtf8: (input: any) => Uint8Array) { return buff } - function nonFramedBodyIv (ivLength: IvLength) { + function nonFramedBodyIv(ivLength: IvLength) { return frameIv(ivLength, 1) } - function headerAuthIv (ivLength: IvLength) { + function headerAuthIv(ivLength: IvLength) { return new Uint8Array(ivLength) // new Uint8Array is 0 filled by default } - function frameHeader (sequenceNumber:number, iv: Uint8Array) { + function frameHeader(sequenceNumber: number, iv: Uint8Array) { return concatBuffers(uInt32BE(sequenceNumber), iv) } - function finalFrameHeader (sequenceNumber: number, iv: Uint8Array, contentLength: number) { + function finalFrameHeader( + sequenceNumber: number, + iv: Uint8Array, + contentLength: number + ) { return concatBuffers( uInt32BE(SequenceIdentifier.SEQUENCE_NUMBER_END), // Final Frame identifier uInt32BE(sequenceNumber), iv, - uInt32BE(contentLength)) + uInt32BE(contentLength) + ) } - function encodeEncryptionContext (encryptionContext: EncryptionContext): Uint8Array[] { - return Object - .entries(encryptionContext) - /* Precondition: The serialized encryption context entries must be sorted by UTF-8 key value. */ - .sort(([aKey], [bKey]) => aKey.localeCompare(bKey)) - .map(entries => entries.map(fromUtf8)) - .map(([key, value]) => concatBuffers(uInt16BE(key.byteLength), key, uInt16BE(value.byteLength), value)) + function encodeEncryptionContext( + encryptionContext: EncryptionContext + ): Uint8Array[] { + return ( + Object.entries(encryptionContext) + /* Precondition: The serialized encryption context entries must be sorted by UTF-8 key value. */ + .sort(([aKey], [bKey]) => aKey.localeCompare(bKey)) + .map((entries) => entries.map(fromUtf8)) + .map(([key, value]) => + concatBuffers( + uInt16BE(key.byteLength), + key, + uInt16BE(value.byteLength), + value + ) + ) + ) } - function serializeEncryptionContext (encryptionContext: EncryptionContext) { + function serializeEncryptionContext(encryptionContext: EncryptionContext) { const encryptionContextElements = encodeEncryptionContext(encryptionContext) /* Check for early return (Postcondition): If there is no context then the length of the _whole_ serialized portion is 0. @@ -81,14 +105,18 @@ export function serializeFactory (fromUtf8: (input: any) => Uint8Array) { */ if (!encryptionContextElements.length) return uInt16BE(0) - const aadData = concatBuffers(uInt16BE(encryptionContextElements.length), ...encryptionContextElements) + const aadData = concatBuffers( + uInt16BE(encryptionContextElements.length), + ...encryptionContextElements + ) const aadLength = uInt16BE(aadData.byteLength) return concatBuffers(aadLength, aadData) } - function serializeEncryptedDataKeys (encryptedDataKeys: ReadonlyArray) { - const encryptedKeyInfo = encryptedDataKeys - .map(serializeEncryptedDataKey) + function serializeEncryptedDataKeys( + encryptedDataKeys: ReadonlyArray + ) { + const encryptedKeyInfo = encryptedDataKeys.map(serializeEncryptedDataKey) return concatBuffers( uInt16BE(encryptedDataKeys.length), @@ -96,19 +124,22 @@ export function serializeFactory (fromUtf8: (input: any) => Uint8Array) { ) } - function serializeEncryptedDataKey (edk: EncryptedDataKey) { + function serializeEncryptedDataKey(edk: EncryptedDataKey) { const { providerId, providerInfo, encryptedDataKey, rawInfo } = edk const providerIdBytes = fromUtf8(providerId) // The providerInfo is technically a binary field, so I prefer rawInfo const providerInfoBytes = rawInfo || fromUtf8(providerInfo) return concatBuffers( - uInt16BE(providerIdBytes.byteLength), providerIdBytes, - uInt16BE(providerInfoBytes.byteLength), providerInfoBytes, - uInt16BE(encryptedDataKey.byteLength), encryptedDataKey + uInt16BE(providerIdBytes.byteLength), + providerIdBytes, + uInt16BE(providerInfoBytes.byteLength), + providerInfoBytes, + uInt16BE(encryptedDataKey.byteLength), + encryptedDataKey ) } - function serializeMessageHeader (messageHeader: MessageHeader) { + function serializeMessageHeader(messageHeader: MessageHeader) { return concatBuffers( uInt8(messageHeader.version), uInt8(messageHeader.type), diff --git a/modules/serialize/src/signature_info.ts b/modules/serialize/src/signature_info.ts index a7df5376a..a4aac2c1b 100644 --- a/modules/serialize/src/signature_info.ts +++ b/modules/serialize/src/signature_info.ts @@ -14,11 +14,15 @@ import { concatBuffers } from './concat_buffers' import { uInt16BE } from './uint_util' import { needs } from '@aws-crypto/material-management' -export function serializeSignatureInfo (signature: Uint8Array) { +export function serializeSignatureInfo(signature: Uint8Array) { return concatBuffers(uInt16BE(signature.byteLength), signature) } -export function deserializeSignature ({ buffer, byteOffset, byteLength }: Uint8Array) { +export function deserializeSignature({ + buffer, + byteOffset, + byteLength, +}: Uint8Array) { /* Precondition: There must be information for a signature. */ needs(byteLength, 'Invalid Signature') /* Uint8Array is a view on top of the underlying ArrayBuffer. diff --git a/modules/serialize/src/types.ts b/modules/serialize/src/types.ts index a2a3612de..564ed3644 100644 --- a/modules/serialize/src/types.ts +++ b/modules/serialize/src/types.ts @@ -1,22 +1,40 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ContentType, SerializationVersion, ObjectType } from './identifiers' // eslint-disable-line no-unused-vars -import { IvLength, AlgorithmSuiteIdentifier, AlgorithmSuite, EncryptedDataKey, EncryptionContext } from '@aws-crypto/material-management' // eslint-disable-line no-unused-vars +import { ContentType, SerializationVersion, ObjectType } from './identifiers' +import { + IvLength, + AlgorithmSuiteIdentifier, + AlgorithmSuite, + EncryptedDataKey, + EncryptionContext, +} from '@aws-crypto/material-management' -export type BinaryData = Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array | DataView | ArrayBuffer +export type BinaryData = + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint16Array + | Uint32Array + | Uint8ClampedArray + | Float32Array + | Float64Array + | DataView + | ArrayBuffer -export interface MessageHeader extends Readonly<{ - version: SerializationVersion - type: ObjectType - suiteId: AlgorithmSuiteIdentifier - messageId: BinaryData - encryptionContext: Readonly - encryptedDataKeys: ReadonlyArray - contentType: ContentType - headerIvLength: IvLength - frameLength: number -}> {} +export interface MessageHeader + extends Readonly<{ + version: SerializationVersion + type: ObjectType + suiteId: AlgorithmSuiteIdentifier + messageId: BinaryData + encryptionContext: Readonly + encryptedDataKeys: ReadonlyArray + contentType: ContentType + headerIvLength: IvLength + frameLength: number + }> {} export interface BodyHeader { sequenceNumber: number @@ -24,7 +42,7 @@ export interface BodyHeader { contentLength: number readPos: number tagLength: number - isFinalFrame: boolean, + isFinalFrame: boolean contentType: ContentType } @@ -34,7 +52,7 @@ export interface FrameBodyHeader extends BodyHeader { contentLength: number readPos: number tagLength: number - isFinalFrame: boolean, + isFinalFrame: boolean contentType: ContentType.FRAMED_DATA } export interface NonFrameBodyHeader extends BodyHeader { diff --git a/modules/serialize/src/uint_util.ts b/modules/serialize/src/uint_util.ts index b84aeb489..9b38398b9 100644 --- a/modules/serialize/src/uint_util.ts +++ b/modules/serialize/src/uint_util.ts @@ -4,7 +4,7 @@ import { needs } from '@aws-crypto/material-management' const UINT8_OVERFLOW = 2 ** 8 -export function uInt8 (number:number) { +export function uInt8(number: number) { /* Precondition: Number must be 0-(2^8 - 1). */ needs(number < UINT8_OVERFLOW && number >= 0, 'number out of bounds.') @@ -15,7 +15,7 @@ export function uInt8 (number:number) { } const UINT16__OVERFLOW = 2 ** 16 -export function uInt16BE (number: number) { +export function uInt16BE(number: number) { /* Precondition: Number must be 0-(2^16 - 1). */ needs(number < UINT16__OVERFLOW && number >= 0, 'number out of bounds.') @@ -26,7 +26,7 @@ export function uInt16BE (number: number) { } const UINT32__OVERFLOW = 2 ** 32 -export function uInt32BE (number: number) { +export function uInt32BE(number: number) { /* Precondition: Number must be 0-(2^32 - 1). */ needs(number < UINT32__OVERFLOW && number >= 0, 'number out of bounds.') diff --git a/modules/web-crypto-backend/package.json b/modules/web-crypto-backend/package.json index fc833189f..ebe4723e6 100644 --- a/modules/web-crypto-backend/package.json +++ b/modules/web-crypto-backend/package.json @@ -4,7 +4,9 @@ "scripts": { "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", - "lint": "standard src/*.ts test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", "karma": "karma start karma.conf.js", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", diff --git a/modules/web-crypto-backend/src/backend-factory.ts b/modules/web-crypto-backend/src/backend-factory.ts index 5958d0446..c6a00953d 100644 --- a/modules/web-crypto-backend/src/backend-factory.ts +++ b/modules/web-crypto-backend/src/backend-factory.ts @@ -2,12 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { isMsWindow } from '@aws-crypto/ie11-detection' -import { supportsWebCrypto, supportsSubtleCrypto, supportsZeroByteGCM } from '@aws-crypto/supports-web-crypto' +import { + supportsWebCrypto, + supportsSubtleCrypto, + supportsZeroByteGCM, +} from '@aws-crypto/supports-web-crypto' import { synchronousRandomValues as randomValues } from './synchronous_random_values' import promisifyMsSubtleCrypto from './promisify-ms-crypto' -type MaybeSubtleCrypto = SubtleCrypto|false -export type WebCryptoBackend = FullSupportWebCryptoBackend|MixedSupportWebCryptoBackend +type MaybeSubtleCrypto = SubtleCrypto | false +export type WebCryptoBackend = + | FullSupportWebCryptoBackend + | MixedSupportWebCryptoBackend export type FullSupportWebCryptoBackend = { subtle: SubtleCrypto randomValues: (byteLength: number) => Uint8Array @@ -18,13 +24,13 @@ export type MixedSupportWebCryptoBackend = { randomValues: (byteLength: number) => Uint8Array } -export function webCryptoBackendFactory (window: Window) { +export function webCryptoBackendFactory(window: Window) { const fallbackRequiredPromise = windowRequiresFallback(window) - let webCryptoFallbackPromise: Promise|false = false + let webCryptoFallbackPromise: Promise | false = false return { getWebCryptoBackend, configureFallback } - async function getWebCryptoBackend (): Promise { + async function getWebCryptoBackend(): Promise { /* Precondition: Access to a secure random source is required. */ try { randomValues(1) @@ -40,12 +46,16 @@ export function webCryptoBackendFactory (window: Window) { * In this case the subtle backend does not support zero byte GCM operations. */ if (subtle && fallbackRequired && !webCryptoFallback) { - throw new Error('A Fallback is required for zero byte AES-GCM operations.') + throw new Error( + 'A Fallback is required for zero byte AES-GCM operations.' + ) } /* Postcondition: If no SubtleCrypto exists, a fallback must configured. */ if (!subtle && !webCryptoFallback) { - throw new Error('A Fallback is required because no subtle backend exists.') + throw new Error( + 'A Fallback is required because no subtle backend exists.' + ) } if (!fallbackRequired && subtle) { @@ -53,7 +63,11 @@ export function webCryptoBackendFactory (window: Window) { } if (fallbackRequired && subtle && webCryptoFallback) { - return { nonZeroByteSubtle: subtle, randomValues, zeroByteSubtle: webCryptoFallback } + return { + nonZeroByteSubtle: subtle, + randomValues, + zeroByteSubtle: webCryptoFallback, + } } if (fallbackRequired && !subtle && webCryptoFallback) { @@ -63,7 +77,7 @@ export function webCryptoBackendFactory (window: Window) { throw new Error('unknown error') } - async function configureFallback (fallback: SubtleCrypto) { + async function configureFallback(fallback: SubtleCrypto) { const fallbackRequired = await fallbackRequiredPromise /* Precondition: If a fallback is not required, do not configure one. */ if (!fallbackRequired) { @@ -71,41 +85,49 @@ export function webCryptoBackendFactory (window: Window) { } /* Precondition: Can not reconfigure fallback. */ - if (webCryptoFallbackPromise) throw new Error('Fallback reconfiguration denied') + if (webCryptoFallbackPromise) + throw new Error('Fallback reconfiguration denied') /* Precondition: Fallback must look like it supports the required operations. */ - if (!supportsSubtleCrypto(fallback)) throw new Error('Fallback does not support WebCrypto') + if (!supportsSubtleCrypto(fallback)) + throw new Error('Fallback does not support WebCrypto') // This if to lock the fallback. // when using the fallback, it is simpler // for the customer to not await the success // of configuration so we handle it for them // I still return in case they want to await - webCryptoFallbackPromise = supportsZeroByteGCM(fallback) - .then(zeroByteGCMSupport => { + webCryptoFallbackPromise = supportsZeroByteGCM(fallback).then( + (zeroByteGCMSupport) => { /* Postcondition: The fallback must specifically support ZeroByteGCM. */ - if (!zeroByteGCMSupport) throw new Error('Fallback does not support zero byte AES-GCM') + if (!zeroByteGCMSupport) + throw new Error('Fallback does not support zero byte AES-GCM') return fallback - }) + } + ) return webCryptoFallbackPromise } } -export function getNonZeroByteBackend (backend: WebCryptoBackend|false) { +export function getNonZeroByteBackend(backend: WebCryptoBackend | false) { /* Precondition: A backend must be passed to get a non zero byte backend. */ if (!backend) throw new Error('No supported backend.') - return (backend).subtle || - (backend).nonZeroByteSubtle + return ( + (backend as FullSupportWebCryptoBackend).subtle || + (backend as MixedSupportWebCryptoBackend).nonZeroByteSubtle + ) } -export function getZeroByteSubtle (backend: WebCryptoBackend|false) { +export function getZeroByteSubtle(backend: WebCryptoBackend | false) { /* Precondition: A backend must be passed to get a zero byte backend. */ if (!backend) throw new Error('No supported backend.') - return (backend).subtle || - (backend).zeroByteSubtle + return ( + (backend as FullSupportWebCryptoBackend).subtle || + (backend as MixedSupportWebCryptoBackend).zeroByteSubtle + ) } -export async function windowRequiresFallback (window: Window) { +export async function windowRequiresFallback(window: Window) { const subtle = pluckSubtleCrypto(window) if (!subtle) return true @@ -113,7 +135,7 @@ export async function windowRequiresFallback (window: Window) { return !zeroByteSupport } -export function pluckSubtleCrypto (window: Window): MaybeSubtleCrypto { +export function pluckSubtleCrypto(window: Window): MaybeSubtleCrypto { // if needed webkitSubtle check should be added here // see: https://webkit.org/blog/7790/update-on-web-cryptography/ if (supportsWebCrypto(window)) return window.crypto.subtle @@ -121,6 +143,8 @@ export function pluckSubtleCrypto (window: Window): MaybeSubtleCrypto { return false } -export function isFullSupportWebCryptoBackend (backend: WebCryptoBackend): backend is FullSupportWebCryptoBackend { - return !!(backend).subtle +export function isFullSupportWebCryptoBackend( + backend: WebCryptoBackend +): backend is FullSupportWebCryptoBackend { + return !!(backend as FullSupportWebCryptoBackend).subtle } diff --git a/modules/web-crypto-backend/src/index.ts b/modules/web-crypto-backend/src/index.ts index 331ef2b85..13033a0ae 100644 --- a/modules/web-crypto-backend/src/index.ts +++ b/modules/web-crypto-backend/src/index.ts @@ -4,10 +4,9 @@ import { locateWindow } from '@aws-sdk/util-locate-window' import { webCryptoBackendFactory } from './backend-factory' -const { - getWebCryptoBackend, - configureFallback -} = webCryptoBackendFactory(locateWindow()) +const { getWebCryptoBackend, configureFallback } = webCryptoBackendFactory( + locateWindow() +) export { getWebCryptoBackend, configureFallback } export { @@ -16,7 +15,7 @@ export { isFullSupportWebCryptoBackend, WebCryptoBackend, FullSupportWebCryptoBackend, - MixedSupportWebCryptoBackend + MixedSupportWebCryptoBackend, } from './backend-factory' export { synchronousRandomValues } from './synchronous_random_values' diff --git a/modules/web-crypto-backend/src/promisify-ms-crypto.ts b/modules/web-crypto-backend/src/promisify-ms-crypto.ts index 4cff53ca8..4c17e629f 100644 --- a/modules/web-crypto-backend/src/promisify-ms-crypto.ts +++ b/modules/web-crypto-backend/src/promisify-ms-crypto.ts @@ -1,18 +1,32 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { MsSubtleCrypto } from '@aws-crypto/ie11-detection' // eslint-disable-line no-unused-vars +import { MsSubtleCrypto } from '@aws-crypto/ie11-detection' type MsSubtleFunctions = keyof MsSubtleCrypto -export default function promisifyMsSubtleCrypto (backend: MsSubtleCrypto) { - const usages : MsSubtleFunctions[] = ['decrypt', 'digest', 'encrypt', 'exportKey', 'generateKey', 'importKey', 'sign', 'verify'] - const decorateUsage = (fakeBackend: any, usage: MsSubtleFunctions) => decorate(backend, fakeBackend, usage) - return usages.reduce(decorateUsage, {}) +export default function promisifyMsSubtleCrypto(backend: MsSubtleCrypto) { + const usages: MsSubtleFunctions[] = [ + 'decrypt', + 'digest', + 'encrypt', + 'exportKey', + 'generateKey', + 'importKey', + 'sign', + 'verify', + ] + const decorateUsage = (fakeBackend: any, usage: MsSubtleFunctions) => + decorate(backend, fakeBackend, usage) + return usages.reduce(decorateUsage, {}) as SubtleCrypto } -function decorate (subtle: MsSubtleCrypto, fakeBackend: any, name: MsSubtleFunctions) { - fakeBackend[name] = (...args:any[]) => { +function decorate( + subtle: MsSubtleCrypto, + fakeBackend: any, + name: MsSubtleFunctions +) { + fakeBackend[name] = async (...args: any[]) => { return new Promise((resolve, reject) => { // @ts-ignore const operation = subtle[name](...args) diff --git a/modules/web-crypto-backend/src/synchronous_random_values.ts b/modules/web-crypto-backend/src/synchronous_random_values.ts index 4ecef6864..86ac7400b 100644 --- a/modules/web-crypto-backend/src/synchronous_random_values.ts +++ b/modules/web-crypto-backend/src/synchronous_random_values.ts @@ -9,7 +9,7 @@ import { locateWindow } from '@aws-sdk/util-locate-window' * For example constructors need to be synchronous. * The AWS JS SDK uses IRandomValues to have a consistent interface. */ -export function synchronousRandomValues (byteLength: number): Uint8Array { +export function synchronousRandomValues(byteLength: number): Uint8Array { // Find the global scope for this runtime const globalScope = locateWindow() diff --git a/package-lock.json b/package-lock.json index a253cd188..da7b87491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -262,11 +262,52 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "requires": { + "buffer-alloc": "^1.2.0" + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "karma": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", + "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.1.0", + "connect": "^3.6.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.14", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + } + }, "karma-chrome-launcher": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", @@ -275,6 +316,14 @@ "which": "^1.2.1" } }, + "karma-mocha": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz", + "integrity": "sha1-7qrH/8DiAetjxGdEDStpx883eL8=", + "requires": { + "minimist": "1.2.0" + } + }, "karma-webpack": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-4.0.2.tgz", @@ -295,6 +344,11 @@ } } }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, "mocha": { "version": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", @@ -335,6 +389,29 @@ } } }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -561,6 +638,47 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "karma": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", + "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.1.0", + "connect": "^3.6.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.14", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + } + }, "karma-jasmine": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.1.1.tgz", @@ -603,6 +721,19 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -2377,6 +2508,23 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==" }, + "@babel/runtime": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", + "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + } + } + }, "@babel/template": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", @@ -4224,6 +4372,15 @@ "@types/node": ">= 8" } }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, "@sindresorhus/is": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", @@ -4399,9 +4556,9 @@ "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" }, "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, "@types/keyv": { @@ -4434,6 +4591,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.8.tgz", "integrity": "sha512-1WgO8hsyHynlx7nhP1kr0OFzsgKz5XDQL+Lfc3b1Q3qIln/n8cKD4m09NJ0+P1Rq7Zgnc7N0+SsMnoD1rEb0kA==" }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -4475,55 +4638,101 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz", - "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.29.0.tgz", + "integrity": "sha512-X/YAY7azKirENm4QRpT7OVmzok02cSkqeIcLmdz6gXUQG4Hk0Fi9oBAynSAyNXeGdMRuZvjBa0c1Lu0dn/u6VA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "1.13.0", - "eslint-utils": "^1.3.1", + "@typescript-eslint/experimental-utils": "2.29.0", "functional-red-black-tree": "^1.0.1", - "regexpp": "^2.0.1", - "tsutils": "^3.7.0" + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" } }, "@typescript-eslint/experimental-utils": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", - "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz", + "integrity": "sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-scope": "^4.0.0" + "@typescript-eslint/typescript-estree": "2.29.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } } }, "@typescript-eslint/parser": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", - "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.29.0.tgz", + "integrity": "sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "1.13.0", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-visitor-keys": "^1.0.0" + "@typescript-eslint/experimental-utils": "2.29.0", + "@typescript-eslint/typescript-estree": "2.29.0", + "eslint-visitor-keys": "^1.1.0" } }, "@typescript-eslint/typescript-estree": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", - "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz", + "integrity": "sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==", "dev": true, "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" }, "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -4871,9 +5080,9 @@ } }, "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true }, "acorn-walk": { @@ -4974,6 +5183,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -5098,96 +5313,6 @@ "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - } - } - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -5285,6 +5410,12 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -5963,24 +6094,15 @@ } } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { @@ -6049,9 +6171,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "check-error": { @@ -6102,12 +6224,6 @@ "safe-buffer": "^5.0.1" } }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -6144,6 +6260,44 @@ "restore-cursor": "^2.0.0" } }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", @@ -6436,12 +6590,6 @@ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -6912,6 +7060,12 @@ } } }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, "date-format": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", @@ -6937,12 +7091,6 @@ "ms": "2.0.0" } }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, "debuglog": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", @@ -7108,28 +7256,6 @@ } } }, - "deglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", - "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", - "dev": true, - "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^3.0.9", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - }, - "dependencies": { - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - } - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7221,9 +7347,9 @@ } }, "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { "esutils": "^2.0.2" @@ -7333,6 +7459,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, "elliptic": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", @@ -7650,55 +7782,54 @@ } }, "eslint": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", - "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", "dev": true, "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", "esquery": "^1.0.1", "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", + "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.5", + "lodash": "^4.17.14", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", + "optionator": "^0.8.3", "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -7708,9 +7839,9 @@ } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { @@ -7734,25 +7865,57 @@ } }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" } }, - "fast-deep-equal": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } }, "json-schema-traverse": { "version": "0.4.1", @@ -7761,9 +7924,9 @@ "dev": true }, "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -7775,15 +7938,33 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7792,148 +7973,24 @@ "requires": { "has-flag": "^3.0.0" } - } - } - }, - "eslint-config-standard": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", - "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz", - "integrity": "sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-module-utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", - "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - } - } - }, - "eslint-plugin-es": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", - "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", - "dev": true, - "requires": { - "eslint-utils": "^1.4.2", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", - "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true } } }, - "eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - } - }, - "eslint-plugin-promise": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", - "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", + "eslint-config-prettier": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", + "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.2" + "get-stdin": "^6.0.0" } }, - "eslint-plugin-standard": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", - "dev": true - }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -7944,12 +8001,12 @@ } }, "eslint-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", - "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.0.0" + "eslint-visitor-keys": "^1.1.0" } }, "eslint-visitor-keys": { @@ -7959,14 +8016,22 @@ "dev": true }, "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", "dev": true, "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + } } }, "esprima": { @@ -7975,12 +8040,20 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } } }, "esrecurse": { @@ -8157,13 +8230,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -8300,13 +8373,12 @@ } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "^2.0.1" } }, "file-uri-to-path": { @@ -8356,12 +8428,6 @@ "pkg-dir": "^3.0.0" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -8371,6 +8437,15 @@ "locate-path": "^2.0.0" } }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, "findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -8400,15 +8475,14 @@ } }, "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" }, "dependencies": { "rimraf": { @@ -8423,9 +8497,9 @@ } }, "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" }, "flush-write-stream": { "version": "1.1.1", @@ -8458,9 +8532,9 @@ } }, "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", + "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", "requires": { "debug": "^3.0.0" }, @@ -8745,6 +8819,12 @@ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "get-pkg-repo": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", @@ -9573,6 +9653,12 @@ } } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -9582,90 +9668,265 @@ "ms": "^2.0.0" } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", - "dev": true - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "husky": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", + "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", "dev": true, "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^6.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" }, "dependencies": { - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "caller-callsite": "^2.0.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - } - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "immediate": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", + "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "dependencies": { + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", "dev": true }, "indexof": { @@ -9714,69 +9975,186 @@ } }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "strip-ansi": { + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" } }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true } } }, @@ -9959,6 +10337,15 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + } + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -9994,10 +10381,10 @@ "has": "^1.0.3" } }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, "is-ssh": { @@ -10014,12 +10401,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -10066,12 +10447,10 @@ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "requires": { - "buffer-alloc": "^1.2.0" - } + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", + "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", + "dev": true }, "isexe": { "version": "2.0.0", @@ -10722,16 +11101,6 @@ "verror": "1.10.0" } }, - "jsx-ast-utils": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", - "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "object.assign": "^4.1.0" - } - }, "just-extend": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", @@ -10760,11 +11129,11 @@ } }, "karma": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", - "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/karma/-/karma-5.0.2.tgz", + "integrity": "sha512-RpUuCuGJfN3WnjYPGIH+VBF8023Lfm3TQH6D1kcNL+FxtEPc2UUz/nVjbVAGXH4Pm+Q7FVOAQjdAeFUpXpQ3IA==", + "dev": true, "requires": { - "bluebird": "^3.3.0", "body-parser": "^1.16.1", "braces": "^3.0.2", "chokidar": "^3.0.0", @@ -10776,26 +11145,129 @@ "glob": "^7.1.1", "graceful-fs": "^4.1.2", "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", + "isbinaryfile": "^4.0.2", "lodash": "^4.17.14", "log4js": "^4.0.0", "mime": "^2.3.1", "minimatch": "^3.0.2", - "optimist": "^0.6.1", "qjobs": "^1.1.4", "range-parser": "^1.2.0", "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", "socket.io": "2.1.1", "source-map": "^0.6.1", "tmp": "0.0.33", - "useragent": "2.3.0" + "ua-parser-js": "0.7.21", + "yargs": "^15.3.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "rimraf": { "version": "2.7.1", - "resolved": "http://localhost:4873/rimraf/-/rimraf-2.7.1.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, "requires": { "glob": "^7.1.3" } @@ -10803,7 +11275,68 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -10844,18 +11377,12 @@ "integrity": "sha1-T3ii6800OH+OVaur/2NGVRZMTHY=" }, "karma-mocha": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz", - "integrity": "sha1-7qrH/8DiAetjxGdEDStpx883eL8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.0.tgz", + "integrity": "sha512-qiZkZDJnn2kb9t2m4LoM4cYJHJVPoxvAYYe0B+go5s+A/3vc/3psUT05zW4yFz4vT0xHf+XzTTery8zdr8GWgA==", + "dev": true, "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } + "minimist": "^1.2.3" } }, "karma-parallel": { @@ -11032,110 +11559,432 @@ } } }, - "level-js": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/level-js/-/level-js-4.0.2.tgz", - "integrity": "sha512-PeGjZsyMG4O89KHiez1zoMJxStnkM+oBIqgACjoo5PJqFiSUUm3GNod/KcbqN5ktyZa8jkG7I1T0P2u6HN9lIg==", - "dev": true, - "requires": { - "abstract-leveldown": "~6.0.1", - "immediate": "~3.2.3", - "inherits": "^2.0.3", - "ltgt": "^2.1.2", - "typedarray-to-buffer": "~3.1.5" - } - }, - "level-packager": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", - "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", + "level-js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-4.0.2.tgz", + "integrity": "sha512-PeGjZsyMG4O89KHiez1zoMJxStnkM+oBIqgACjoo5PJqFiSUUm3GNod/KcbqN5ktyZa8jkG7I1T0P2u6HN9lIg==", + "dev": true, + "requires": { + "abstract-leveldown": "~6.0.1", + "immediate": "~3.2.3", + "inherits": "^2.0.3", + "ltgt": "^2.1.2", + "typedarray-to-buffer": "~3.1.5" + } + }, + "level-packager": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", + "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", + "dev": true, + "requires": { + "encoding-down": "^6.3.0", + "levelup": "^4.3.2" + } + }, + "level-supports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", + "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "dev": true, + "requires": { + "xtend": "^4.0.2" + } + }, + "leveldown": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.5.1.tgz", + "integrity": "sha512-GoC455/ncfg4yLLItr192HuXpA+CcQ2q9GncXJhewvvlpsBBEegChn5tMPP+kGvJt7u2LuXAd8fY2moQxFD+sQ==", + "dev": true, + "requires": { + "abstract-leveldown": "~6.2.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "~4.1.0" + }, + "dependencies": { + "abstract-leveldown": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.2.tgz", + "integrity": "sha512-/a+Iwj0rn//CX0EJOasNyZJd2o8xur8Ce9C57Sznti/Ilt/cb6Qd8/k98A4ZOklXgTG+iAYYUs1OTG0s1eH+zQ==", + "dev": true, + "requires": { + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + } + } + } + }, + "levelup": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.3.2.tgz", + "integrity": "sha512-cRTjU4ktWo59wf13PHEiOayHC3n0dOh4i5+FHr4tv4MX9+l7mqETicNq3Aj07HKlLdk0z5muVoDL2RD+ovgiyA==", + "dev": true, + "requires": { + "deferred-leveldown": "~5.3.0", + "level-errors": "~2.0.0", + "level-iterator-stream": "~4.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.1.7.tgz", + "integrity": "sha512-ZkK8t9Ep/AHuJQKV95izSa+DqotftGnSsNeEmCSqbQ6j4C4H0jDYhEZqVOGD1Q2Oe227igbqjMWycWyYbQtpoA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "commander": "^5.0.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "execa": "^4.0.0", + "listr": "^0.14.3", + "log-symbols": "^3.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", + "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==", + "dev": true + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "execa": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", + "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", "dev": true, "requires": { - "encoding-down": "^6.3.0", - "levelup": "^4.3.2" + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" } }, - "level-supports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", - "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", - "dev": true, - "requires": { - "xtend": "^4.0.2" - } + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true }, - "leveldown": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.5.1.tgz", - "integrity": "sha512-GoC455/ncfg4yLLItr192HuXpA+CcQ2q9GncXJhewvvlpsBBEegChn5tMPP+kGvJt7u2LuXAd8fY2moQxFD+sQ==", + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", "dev": true, "requires": { - "abstract-leveldown": "~6.2.1", - "napi-macros": "~2.0.0", - "node-gyp-build": "~4.1.0" + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" }, "dependencies": { - "abstract-leveldown": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.2.tgz", - "integrity": "sha512-/a+Iwj0rn//CX0EJOasNyZJd2o8xur8Ce9C57Sznti/Ilt/cb6Qd8/k98A4ZOklXgTG+iAYYUs1OTG0s1eH+zQ==", + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" } } } }, - "levelup": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.3.2.tgz", - "integrity": "sha512-cRTjU4ktWo59wf13PHEiOayHC3n0dOh4i5+FHr4tv4MX9+l7mqETicNq3Aj07HKlLdk0z5muVoDL2RD+ovgiyA==", - "dev": true, - "requires": { - "deferred-leveldown": "~5.3.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" }, "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "error-ex": "^1.2.0" + "color-convert": "^1.9.0" } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -11327,27 +12176,99 @@ "lodash.templatesettings": "^4.0.0" } }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0" + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + } + } } }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, "log4js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", @@ -11625,6 +12546,12 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", @@ -13610,6 +14537,15 @@ } } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-asn1": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", @@ -13730,12 +14666,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -13840,47 +14770,6 @@ "pinkie": "^2.0.0" } }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -13935,11 +14824,14 @@ "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", "dev": true }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } }, "posix-character-classes": { "version": "0.1.1", @@ -13951,6 +14843,12 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -14000,17 +14898,6 @@ "read": "1" } }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -14201,12 +15088,6 @@ "unpipe": "1.0.0" } }, - "react-is": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", - "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", - "dev": true - }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -14280,44 +15161,6 @@ } } }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - } - } - }, "readable-stream": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", @@ -14374,9 +15217,9 @@ } }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, "release-zalgo": { @@ -14478,24 +15321,6 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - } - } - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -14618,12 +15443,6 @@ "is-promise": "^2.1.0" } }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -14633,12 +15452,12 @@ } }, "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -14694,6 +15513,18 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -14868,12 +15699,25 @@ "dev": true }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } } }, "slide": { @@ -15009,9 +15853,9 @@ } }, "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" }, "socket.io-client": { "version": "2.1.1", @@ -15284,35 +16128,6 @@ "figgy-pudding": "^3.5.1" } }, - "standard": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/standard/-/standard-12.0.1.tgz", - "integrity": "sha512-UqdHjh87OG2gUrNCSM4QRLF5n9h3TFPwrCNyVlkqu31Hej0L/rc8hzKqVvkb2W3x0WMq7PzZdkLfEcBhVOR6lg==", - "dev": true, - "requires": { - "eslint": "~5.4.0", - "eslint-config-standard": "12.0.0", - "eslint-config-standard-jsx": "6.0.2", - "eslint-plugin-import": "~2.14.0", - "eslint-plugin-node": "~7.0.1", - "eslint-plugin-promise": "~4.0.0", - "eslint-plugin-react": "~7.11.1", - "eslint-plugin-standard": "~4.0.0", - "standard-engine": "~9.0.0" - } - }, - "standard-engine": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-9.0.0.tgz", - "integrity": "sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w==", - "dev": true, - "requires": { - "deglob": "^2.1.0", - "get-stdin": "^6.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^2.0.0" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -15483,6 +16298,12 @@ } } }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -15548,6 +16369,17 @@ "safe-buffer": "~5.1.0" } }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -15567,6 +16399,12 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-indent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", @@ -15597,9 +16435,9 @@ "dev": true }, "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", "dev": true }, "symbol-tree": { @@ -15609,23 +16447,21 @@ "dev": true }, "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" }, "dependencies": { "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -15634,25 +16470,11 @@ "uri-js": "^4.2.2" } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "fast-deep-equal": { "version": "3.1.1", @@ -15666,13 +16488,24 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" } } } @@ -16191,6 +17024,12 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", + "dev": true + }, "uglify-js": { "version": "3.7.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.4.tgz", @@ -16237,12 +17076,6 @@ "set-value": "^2.0.1" } }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -16804,25 +17637,25 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { @@ -16832,13 +17665,13 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "optional": true, "requires": { @@ -16848,37 +17681,37 @@ }, "chownr": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "3.2.6", - "resolved": "", + "resolved": false, "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "optional": true, "requires": { @@ -16887,25 +17720,25 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.7", - "resolved": "", + "resolved": false, "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "optional": true, "requires": { @@ -16914,13 +17747,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { @@ -16936,7 +17769,7 @@ }, "glob": { "version": "7.1.6", - "resolved": "", + "resolved": false, "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "optional": true, "requires": { @@ -16950,13 +17783,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "optional": true, "requires": { @@ -16965,7 +17798,7 @@ }, "ignore-walk": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "optional": true, "requires": { @@ -16974,7 +17807,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { @@ -16984,19 +17817,19 @@ }, "inherits": { "version": "2.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "optional": true, "requires": { @@ -17005,13 +17838,13 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "optional": true, "requires": { @@ -17020,13 +17853,13 @@ }, "minimist": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "optional": true }, "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "optional": true, "requires": { @@ -17036,7 +17869,7 @@ }, "minizlib": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "optional": true, "requires": { @@ -17045,7 +17878,7 @@ }, "mkdirp": { "version": "0.5.3", - "resolved": "", + "resolved": false, "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "optional": true, "requires": { @@ -17054,13 +17887,13 @@ }, "ms": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "optional": true }, "needle": { "version": "2.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", "optional": true, "requires": { @@ -17071,7 +17904,7 @@ }, "node-pre-gyp": { "version": "0.14.0", - "resolved": "", + "resolved": false, "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "optional": true, "requires": { @@ -17089,7 +17922,7 @@ }, "nopt": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "optional": true, "requires": { @@ -17099,7 +17932,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "optional": true, "requires": { @@ -17108,13 +17941,13 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "optional": true }, "npm-packlist": { "version": "1.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "optional": true, "requires": { @@ -17125,7 +17958,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { @@ -17137,19 +17970,19 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "optional": true, "requires": { @@ -17158,19 +17991,19 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { @@ -17180,19 +18013,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { @@ -17204,7 +18037,7 @@ }, "readable-stream": { "version": "2.3.7", - "resolved": "", + "resolved": false, "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "optional": true, "requires": { @@ -17219,7 +18052,7 @@ }, "rimraf": { "version": "2.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "optional": true, "requires": { @@ -17228,43 +18061,43 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "optional": true, "requires": { @@ -17275,7 +18108,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { @@ -17284,7 +18117,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "optional": true, "requires": { @@ -17293,13 +18126,13 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.13", - "resolved": "", + "resolved": false, "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "optional": true, "requires": { @@ -17314,13 +18147,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { @@ -17329,13 +18162,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true }, "yallist": { "version": "3.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "optional": true } @@ -17750,6 +18583,12 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -17840,18 +18679,18 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { "mkdirp": "^0.5.1" }, "dependencies": { "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -17995,6 +18834,15 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yaml": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.9.2.tgz", + "integrity": "sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "yargs": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", diff --git a/package.json b/package.json index 52f4170e6..e08c22778 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "postinstall": "./util/bootstrap_tsconfig", "clean": "npm run clear-build-cache && lerna clean", "clear-build-cache": "rimraf ./modules/*/build/*", - "lint": "standard modules/**/src/*.ts modules/**/test/**/*.ts", + "lint": "run-s lint-*", + "lint-eslint": "npx eslint modules/**/src/*.ts", + "lint-prettier": "prettier -c modules/**/src/*.ts", "build-node": "tsc -b tsconfig.json", "build-browser": "tsc -b tsconfig.module.json", "build": "run-s build-*", @@ -88,12 +90,6 @@ "workspaces": [ "modules/*" ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - }, "devDependencies": { "@aws-sdk/credential-provider-node": "^1.0.0-alpha.7", "@aws-sdk/karma-credential-loader": "^1.0.0-alpha.6", @@ -105,28 +101,32 @@ "@types/mocha": "^7.0.2", "@types/node": "^13.9.8", "@types/sinon": "^7.5.2", - "@typescript-eslint/eslint-plugin": "^1.9.0", - "@typescript-eslint/parser": "^1.9.0", + "@typescript-eslint/eslint-plugin": "^2.28.0", + "@typescript-eslint/parser": "^2.28.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.1", "from2": "^2.3.0", + "husky": "^4.2.5", "istanbul-instrumenter-loader": "^3.0.1", - "karma": "^4.4.1", + "karma": "^5.0.2", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^3.1.0", "karma-coverage-istanbul-reporter": "^2.1.1", "karma-jasmine": "^3.1.1", - "karma-mocha": "^1.3.0", + "karma-mocha": "^2.0.0", "karma-parallel": "^0.3.1", "karma-webpack": "^4.0.2", "lerna": "^3.20.2", + "lint-staged": "^10.1.4", "mocha": "^7.1.1", "npm-run-all": "^4.1.5", "nyc": "^15.0.0", + "prettier": "^2.0.4", "rimraf": "^3.0.2", "sinon": "^9.0.1", "source-map-support": "^0.5.12", - "standard": "^12.0.1", "ts-loader": "^6.2.2", "ts-node": "^8.8.1", "tslib": "^1.11.1", From a6f308fcd0da6909d4bdf6f723ea600a377c2c6a Mon Sep 17 00:00:00 2001 From: seebees Date: Tue, 21 Apr 2020 19:59:55 -0700 Subject: [PATCH 2/4] Add additional rules '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/promise-function-async': 'error', '@typescript-eslint/no-misused-promises': 'error', Are really worth the price of addmission. They were taking 5 min to run, but with some sluthing, got that down to ~10s --- .eslintrc.js | 72 ++++++++++++------- modules/example-browser/src/fallback.ts | 4 +- .../src/raw_aes_keyring_browser.ts | 2 +- tsconfig.lint.json | 5 ++ 4 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 tsconfig.lint.json diff --git a/.eslintrc.js b/.eslintrc.js index bb7d534c1..664ceb477 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,12 +1,24 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +/* + If you are here to disable rules + to try and fix specific rule sets + use + npm install --no-save eslint-nibble +*/ + module.exports = { root: true, parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - ], + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: './tsconfig.lint.json', + tsconfigRootDir: process.cwd(), + }, + plugins: ['@typescript-eslint'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', @@ -15,7 +27,14 @@ module.exports = { ], ignorePatterns: ['node_modules/'], rules: { - // I disagree with this rule. + // These are the most useful linting rules. + // They rely on types so they are the slowest rules, + // and they are NOT enabled by default on any + // shared plugins that I know of. + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/promise-function-async': 'error', + '@typescript-eslint/no-misused-promises': 'error', + // I disagree with these rules. // Humans read from less specific to more specific. // No on puts the outline at the end of the book. // Since the exported functions should be composed @@ -23,24 +42,27 @@ module.exports = { // it is good for understanding // for the source files to get more detailed // as you read down from the top. - "no-use-before-define": ["error", { "functions": false }], - "@typescript-eslint/no-use-before-define": ["error", { "functions": false }], + 'no-use-before-define': ['error', { functions: false }], + '@typescript-eslint/no-use-before-define': ['error', { functions: false }], // This is used in a few specific ways. // It may be that adding this to overrides for the tests // and then manual line overrides would be // the best way to handle this later. '@typescript-eslint/no-explicit-any': 'off', // Minimize churn. - '@typescript-eslint/member-delimiter-style': ['error', { - 'multiline': { - 'delimiter': 'none', - 'requireLast': false + '@typescript-eslint/member-delimiter-style': [ + 'error', + { + multiline: { + delimiter: 'none', + requireLast: false, + }, + singleline: { + delimiter: 'semi', + requireLast: false, + }, }, - 'singleline': { - 'delimiter': 'semi', - 'requireLast': false - } - }], + ], // The ESDK exports some interfaces // that conflict with this rule. // At a later date, this might be @@ -49,20 +71,20 @@ module.exports = { // this rule is disabled. // To be clear this would only impact // Typescript use of some types/interfaces. - "@typescript-eslint/no-empty-interface": 'off', + '@typescript-eslint/no-empty-interface': 'off', // To minimize the source change, // this is turned of. - "@typescript-eslint/ban-ts-ignore": 'off', + '@typescript-eslint/ban-ts-ignore': 'off', }, // This is a good rule, // but in many tests, // we are just looking to mock specific functions. - "overrides": [ + overrides: [ { - "files": ["modules/**/test/**/*.ts"], - "rules": { - "@typescript-eslint/no-empty-function": "off" - } - } - ] -}; + files: ['modules/**/test/**/*.ts'], + rules: { + '@typescript-eslint/no-empty-function': 'off', + }, + }, + ], +} diff --git a/modules/example-browser/src/fallback.ts b/modules/example-browser/src/fallback.ts index b06b4afde..f5ae5fe77 100644 --- a/modules/example-browser/src/fallback.ts +++ b/modules/example-browser/src/fallback.ts @@ -27,7 +27,9 @@ import { toBase64 } from '@aws-sdk/util-base64-browser' */ // @ts-ignore import { subtle } from './msrcrypto' -configureFallback(subtle) +configureFallback(subtle).catch((e) => { + throw e +}) /* This is done to facilitate testing. */ export async function testFallback() { diff --git a/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts b/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts index 7532e4236..39201f296 100644 --- a/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts +++ b/modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts @@ -162,7 +162,7 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { keyName: 'importOnly', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY, }) - return backendForRawAesMasterKey().then((backend) => + return backendForRawAesMasterKey().then(async (backend) => _importCryptoKey(backend.subtle, material, ['encrypt', 'decrypt']) ) } diff --git a/tsconfig.lint.json b/tsconfig.lint.json new file mode 100644 index 000000000..2d28b6c6d --- /dev/null +++ b/tsconfig.lint.json @@ -0,0 +1,5 @@ +{ + "extends": "./modules/tsconfig.settings.json", + "include": ["modules/**/src/**/*.ts", "modules/**/test/**/*.ts"], + "exclude": ["node_modules/**"] +} \ No newline at end of file From 0e39d1ae4221ce24360771efcb417c6c2b52065a Mon Sep 17 00:00:00 2001 From: seebees Date: Wed, 22 Apr 2020 09:08:08 -0700 Subject: [PATCH 3/4] remove standard from package.json --- modules/cache-material/package.json | 8 +------- modules/caching-materials-manager-browser/package.json | 8 +------- modules/caching-materials-manager-node/package.json | 8 +------- modules/client-browser/package.json | 8 +------- modules/client-node/package.json | 8 +------- modules/decrypt-browser/package.json | 8 +------- modules/decrypt-node/package.json | 8 +------- modules/encrypt-browser/package.json | 8 +------- modules/encrypt-node/package.json | 8 +------- modules/example-node/package.json | 8 +------- modules/integration-browser/package.json | 8 +------- modules/integration-node/package.json | 8 +------- modules/kms-keyring-browser/package.json | 8 +------- modules/kms-keyring-node/package.json | 8 +------- modules/kms-keyring/package.json | 8 +------- modules/material-management-browser/package.json | 8 +------- modules/material-management-node/package.json | 8 +------- modules/material-management/package.json | 8 +------- modules/raw-aes-keyring-browser/package.json | 8 +------- modules/raw-aes-keyring-node/package.json | 8 +------- modules/raw-keyring/package.json | 8 +------- modules/raw-rsa-keyring-browser/package.json | 8 +------- modules/raw-rsa-keyring-node/package.json | 8 +------- modules/serialize/package.json | 8 +------- 24 files changed, 24 insertions(+), 168 deletions(-) diff --git a/modules/cache-material/package.json b/modules/cache-material/package.json index 2da52cc92..797f6f042 100644 --- a/modules/cache-material/package.json +++ b/modules/cache-material/package.json @@ -30,11 +30,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/caching-materials-manager-browser/package.json b/modules/caching-materials-manager-browser/package.json index 1cb39b2e7..bcf598722 100644 --- a/modules/caching-materials-manager-browser/package.json +++ b/modules/caching-materials-manager-browser/package.json @@ -32,11 +32,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/caching-materials-manager-node/package.json b/modules/caching-materials-manager-node/package.json index 1851461ff..463fd611f 100644 --- a/modules/caching-materials-manager-node/package.json +++ b/modules/caching-materials-manager-node/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/client-browser/package.json b/modules/client-browser/package.json index ab70fcc30..2917a1205 100644 --- a/modules/client-browser/package.json +++ b/modules/client-browser/package.json @@ -39,11 +39,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/client-node/package.json b/modules/client-node/package.json index 3dcc49b52..e472c9e77 100644 --- a/modules/client-node/package.json +++ b/modules/client-node/package.json @@ -37,11 +37,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/decrypt-browser/package.json b/modules/decrypt-browser/package.json index 8d1aeb3b2..fc1e303a4 100644 --- a/modules/decrypt-browser/package.json +++ b/modules/decrypt-browser/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/decrypt-node/package.json b/modules/decrypt-node/package.json index 066961eaf..58ac21b8a 100644 --- a/modules/decrypt-node/package.json +++ b/modules/decrypt-node/package.json @@ -31,11 +31,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/encrypt-browser/package.json b/modules/encrypt-browser/package.json index 47cf2e554..55f687d77 100644 --- a/modules/encrypt-browser/package.json +++ b/modules/encrypt-browser/package.json @@ -29,11 +29,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/encrypt-node/package.json b/modules/encrypt-node/package.json index 8fe7501b4..7c81bac9d 100644 --- a/modules/encrypt-node/package.json +++ b/modules/encrypt-node/package.json @@ -30,11 +30,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/example-node/package.json b/modules/example-node/package.json index a4a90540b..486212270 100644 --- a/modules/example-node/package.json +++ b/modules/example-node/package.json @@ -27,11 +27,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/integration-browser/package.json b/modules/integration-browser/package.json index c9283330c..36728027b 100644 --- a/modules/integration-browser/package.json +++ b/modules/integration-browser/package.json @@ -48,11 +48,5 @@ "files": [ "build/**/*", "karma.conf.js" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/integration-node/package.json b/modules/integration-node/package.json index 36ba6a315..cd0a629fb 100644 --- a/modules/integration-node/package.json +++ b/modules/integration-node/package.json @@ -33,11 +33,5 @@ "bin": "./build/main/cli.js", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/kms-keyring-browser/package.json b/modules/kms-keyring-browser/package.json index 98cbd268b..2065a0e62 100644 --- a/modules/kms-keyring-browser/package.json +++ b/modules/kms-keyring-browser/package.json @@ -30,11 +30,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/kms-keyring-node/package.json b/modules/kms-keyring-node/package.json index 7d2376072..a47c5f30d 100644 --- a/modules/kms-keyring-node/package.json +++ b/modules/kms-keyring-node/package.json @@ -29,11 +29,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/kms-keyring/package.json b/modules/kms-keyring/package.json index 73419414f..8c152bb92 100644 --- a/modules/kms-keyring/package.json +++ b/modules/kms-keyring/package.json @@ -27,11 +27,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/material-management-browser/package.json b/modules/material-management-browser/package.json index 5309e918f..ac488e3e8 100644 --- a/modules/material-management-browser/package.json +++ b/modules/material-management-browser/package.json @@ -30,11 +30,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/material-management-node/package.json b/modules/material-management-node/package.json index 6e83eac4e..362f2eef9 100644 --- a/modules/material-management-node/package.json +++ b/modules/material-management-node/package.json @@ -29,11 +29,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/material-management/package.json b/modules/material-management/package.json index a76ea2abb..1f6a92be4 100644 --- a/modules/material-management/package.json +++ b/modules/material-management/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/raw-aes-keyring-browser/package.json b/modules/raw-aes-keyring-browser/package.json index 22f5c753d..a910695ad 100644 --- a/modules/raw-aes-keyring-browser/package.json +++ b/modules/raw-aes-keyring-browser/package.json @@ -32,11 +32,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/raw-aes-keyring-node/package.json b/modules/raw-aes-keyring-node/package.json index 32470af97..b6b33e39b 100644 --- a/modules/raw-aes-keyring-node/package.json +++ b/modules/raw-aes-keyring-node/package.json @@ -29,11 +29,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/raw-keyring/package.json b/modules/raw-keyring/package.json index 4ad5836e7..928642ad3 100644 --- a/modules/raw-keyring/package.json +++ b/modules/raw-keyring/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/raw-rsa-keyring-browser/package.json b/modules/raw-rsa-keyring-browser/package.json index 5b95dd8af..aec0bdc9d 100644 --- a/modules/raw-rsa-keyring-browser/package.json +++ b/modules/raw-rsa-keyring-browser/package.json @@ -31,11 +31,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/raw-rsa-keyring-node/package.json b/modules/raw-rsa-keyring-node/package.json index 70acabdba..a6378af27 100644 --- a/modules/raw-rsa-keyring-node/package.json +++ b/modules/raw-rsa-keyring-node/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } diff --git a/modules/serialize/package.json b/modules/serialize/package.json index 77995a784..58b98fe54 100644 --- a/modules/serialize/package.json +++ b/modules/serialize/package.json @@ -28,11 +28,5 @@ "types": "./build/main/index.d.ts", "files": [ "build/**/*" - ], - "standard": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ] - } + ] } From b1eb89938646d2c8044b747d050e0fbe6a303261 Mon Sep 17 00:00:00 2001 From: seebees Date: Wed, 22 Apr 2020 09:53:52 -0700 Subject: [PATCH 4/4] Update all modules to run lint individualy --- modules/cache-material/.eslintrc.js | 12 ++++++++++++ modules/cache-material/package.json | 4 ++-- .../caching-materials-manager-browser/.eslintrc.js | 12 ++++++++++++ .../caching-materials-manager-browser/package.json | 4 ++-- modules/caching-materials-manager-node/.eslintrc.js | 12 ++++++++++++ modules/caching-materials-manager-node/package.json | 4 ++-- modules/client-browser/.eslintrc.js | 12 ++++++++++++ modules/client-browser/package.json | 4 ++-- modules/client-node/.eslintrc.js | 12 ++++++++++++ modules/client-node/package.json | 4 ++-- modules/decrypt-browser/.eslintrc.js | 12 ++++++++++++ modules/decrypt-browser/package.json | 4 ++-- modules/decrypt-node/.eslintrc.js | 12 ++++++++++++ modules/decrypt-node/package.json | 4 ++-- modules/encrypt-browser/.eslintrc.js | 12 ++++++++++++ modules/encrypt-browser/package.json | 4 ++-- modules/encrypt-node/.eslintrc.js | 12 ++++++++++++ modules/encrypt-node/package.json | 4 ++-- modules/example-browser/.eslintrc.js | 12 ++++++++++++ modules/example-browser/package.json | 4 ++-- modules/example-node/.eslintrc.js | 12 ++++++++++++ modules/example-node/package.json | 4 ++-- modules/hkdf-node/.eslintrc.js | 12 ++++++++++++ modules/hkdf-node/package.json | 4 ++-- modules/integration-browser/.eslintrc.js | 12 ++++++++++++ modules/integration-browser/package.json | 4 ++-- modules/integration-node/.eslintrc.js | 12 ++++++++++++ modules/integration-node/package.json | 4 ++-- modules/kms-keyring-browser/.eslintrc.js | 12 ++++++++++++ modules/kms-keyring-browser/package.json | 4 ++-- modules/kms-keyring-node/.eslintrc.js | 12 ++++++++++++ modules/kms-keyring-node/package.json | 4 ++-- modules/kms-keyring/.eslintrc.js | 12 ++++++++++++ modules/kms-keyring/package.json | 4 ++-- modules/material-management-browser/.eslintrc.js | 12 ++++++++++++ modules/material-management-browser/package.json | 4 ++-- modules/material-management-node/.eslintrc.js | 12 ++++++++++++ modules/material-management-node/package.json | 4 ++-- modules/material-management/.eslintrc.js | 12 ++++++++++++ modules/material-management/package.json | 4 ++-- modules/raw-aes-keyring-browser/.eslintrc.js | 12 ++++++++++++ modules/raw-aes-keyring-browser/package.json | 4 ++-- modules/raw-aes-keyring-node/.eslintrc.js | 12 ++++++++++++ modules/raw-aes-keyring-node/package.json | 4 ++-- modules/raw-keyring/.eslintrc.js | 12 ++++++++++++ modules/raw-keyring/package.json | 4 ++-- modules/raw-rsa-keyring-browser/.eslintrc.js | 12 ++++++++++++ modules/raw-rsa-keyring-browser/package.json | 4 ++-- modules/raw-rsa-keyring-node/.eslintrc.js | 12 ++++++++++++ modules/raw-rsa-keyring-node/package.json | 4 ++-- modules/serialize/.eslintrc.js | 12 ++++++++++++ modules/serialize/package.json | 4 ++-- modules/web-crypto-backend/.eslintrc.js | 12 ++++++++++++ modules/web-crypto-backend/package.json | 4 ++-- 54 files changed, 378 insertions(+), 54 deletions(-) create mode 100644 modules/cache-material/.eslintrc.js create mode 100644 modules/caching-materials-manager-browser/.eslintrc.js create mode 100644 modules/caching-materials-manager-node/.eslintrc.js create mode 100644 modules/client-browser/.eslintrc.js create mode 100644 modules/client-node/.eslintrc.js create mode 100644 modules/decrypt-browser/.eslintrc.js create mode 100644 modules/decrypt-node/.eslintrc.js create mode 100644 modules/encrypt-browser/.eslintrc.js create mode 100644 modules/encrypt-node/.eslintrc.js create mode 100644 modules/example-browser/.eslintrc.js create mode 100644 modules/example-node/.eslintrc.js create mode 100644 modules/hkdf-node/.eslintrc.js create mode 100644 modules/integration-browser/.eslintrc.js create mode 100644 modules/integration-node/.eslintrc.js create mode 100644 modules/kms-keyring-browser/.eslintrc.js create mode 100644 modules/kms-keyring-node/.eslintrc.js create mode 100644 modules/kms-keyring/.eslintrc.js create mode 100644 modules/material-management-browser/.eslintrc.js create mode 100644 modules/material-management-node/.eslintrc.js create mode 100644 modules/material-management/.eslintrc.js create mode 100644 modules/raw-aes-keyring-browser/.eslintrc.js create mode 100644 modules/raw-aes-keyring-node/.eslintrc.js create mode 100644 modules/raw-keyring/.eslintrc.js create mode 100644 modules/raw-rsa-keyring-browser/.eslintrc.js create mode 100644 modules/raw-rsa-keyring-node/.eslintrc.js create mode 100644 modules/serialize/.eslintrc.js create mode 100644 modules/web-crypto-backend/.eslintrc.js diff --git a/modules/cache-material/.eslintrc.js b/modules/cache-material/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/cache-material/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/cache-material/package.json b/modules/cache-material/package.json index 797f6f042..41768cde3 100644 --- a/modules/cache-material/package.json +++ b/modules/cache-material/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/caching-materials-manager-browser/.eslintrc.js b/modules/caching-materials-manager-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/caching-materials-manager-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/caching-materials-manager-browser/package.json b/modules/caching-materials-manager-browser/package.json index bcf598722..9661c8a1b 100644 --- a/modules/caching-materials-manager-browser/package.json +++ b/modules/caching-materials-manager-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/caching-materials-manager-node/.eslintrc.js b/modules/caching-materials-manager-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/caching-materials-manager-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/caching-materials-manager-node/package.json b/modules/caching-materials-manager-node/package.json index 463fd611f..5c13b9015 100644 --- a/modules/caching-materials-manager-node/package.json +++ b/modules/caching-materials-manager-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/client-browser/.eslintrc.js b/modules/client-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/client-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/client-browser/package.json b/modules/client-browser/package.json index 2917a1205..b0a987c2b 100644 --- a/modules/client-browser/package.json +++ b/modules/client-browser/package.json @@ -13,8 +13,8 @@ "scripts": { "build": "tsc -b tsconfig.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts" + "lint-eslint": "eslint src/*.ts", + "lint-prettier": "prettier -c src/*.ts" }, "author": { "name": "AWS Crypto Tools Team", diff --git a/modules/client-node/.eslintrc.js b/modules/client-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/client-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/client-node/package.json b/modules/client-node/package.json index e472c9e77..e746bb919 100644 --- a/modules/client-node/package.json +++ b/modules/client-node/package.json @@ -13,8 +13,8 @@ "scripts": { "build": "tsc -b tsconfig.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts" + "lint-eslint": "eslint src/*.ts", + "lint-prettier": "prettier -c src/*.ts" }, "author": { "name": "AWS Crypto Tools Team", diff --git a/modules/decrypt-browser/.eslintrc.js b/modules/decrypt-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/decrypt-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/decrypt-browser/package.json b/modules/decrypt-browser/package.json index fc1e303a4..4317e3f76 100644 --- a/modules/decrypt-browser/package.json +++ b/modules/decrypt-browser/package.json @@ -4,8 +4,8 @@ "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/decrypt-node/.eslintrc.js b/modules/decrypt-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/decrypt-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/decrypt-node/package.json b/modules/decrypt-node/package.json index 58ac21b8a..85bb9abcd 100644 --- a/modules/decrypt-node/package.json +++ b/modules/decrypt-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/encrypt-browser/.eslintrc.js b/modules/encrypt-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/encrypt-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/encrypt-browser/package.json b/modules/encrypt-browser/package.json index 55f687d77..298361b57 100644 --- a/modules/encrypt-browser/package.json +++ b/modules/encrypt-browser/package.json @@ -4,8 +4,8 @@ "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/encrypt-node/.eslintrc.js b/modules/encrypt-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/encrypt-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/encrypt-node/package.json b/modules/encrypt-node/package.json index 7c81bac9d..74af6deb7 100644 --- a/modules/encrypt-node/package.json +++ b/modules/encrypt-node/package.json @@ -4,8 +4,8 @@ "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/example-browser/.eslintrc.js b/modules/example-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/example-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/example-browser/package.json b/modules/example-browser/package.json index 19b531068..439484082 100644 --- a/modules/example-browser/package.json +++ b/modules/example-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage", diff --git a/modules/example-node/.eslintrc.js b/modules/example-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/example-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/example-node/package.json b/modules/example-node/package.json index 486212270..cbe20c8f1 100644 --- a/modules/example-node/package.json +++ b/modules/example-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/hkdf-node/.eslintrc.js b/modules/hkdf-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/hkdf-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/hkdf-node/package.json b/modules/hkdf-node/package.json index c5177a81d..67804850d 100644 --- a/modules/hkdf-node/package.json +++ b/modules/hkdf-node/package.json @@ -6,8 +6,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/integration-browser/.eslintrc.js b/modules/integration-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/integration-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/integration-browser/package.json b/modules/integration-browser/package.json index 36728027b..1da522f79 100644 --- a/modules/integration-browser/package.json +++ b/modules/integration-browser/package.json @@ -4,8 +4,8 @@ "scripts": { "build": "tsc -b tsconfig.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts", + "lint-prettier": "prettier -c src/*.ts", "karma": "karma start karma.conf.js", "build_fixtures": "npx .", "test": "npm run lint && npm run karma" diff --git a/modules/integration-node/.eslintrc.js b/modules/integration-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/integration-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/integration-node/package.json b/modules/integration-node/package.json index cd0a629fb..743bf9c90 100644 --- a/modules/integration-node/package.json +++ b/modules/integration-node/package.json @@ -4,8 +4,8 @@ "scripts": { "build": "tsc -b tsconfig.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "test": "npm run lint && npm run build && node ./build/main/index.js", "integration_node": "npx ." }, diff --git a/modules/kms-keyring-browser/.eslintrc.js b/modules/kms-keyring-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/kms-keyring-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/kms-keyring-browser/package.json b/modules/kms-keyring-browser/package.json index 2065a0e62..09bfc23fa 100644 --- a/modules/kms-keyring-browser/package.json +++ b/modules/kms-keyring-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/kms-keyring-node/.eslintrc.js b/modules/kms-keyring-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/kms-keyring-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/kms-keyring-node/package.json b/modules/kms-keyring-node/package.json index a47c5f30d..e37857c17 100644 --- a/modules/kms-keyring-node/package.json +++ b/modules/kms-keyring-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/kms-keyring/.eslintrc.js b/modules/kms-keyring/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/kms-keyring/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/kms-keyring/package.json b/modules/kms-keyring/package.json index 8c152bb92..bea84fb72 100644 --- a/modules/kms-keyring/package.json +++ b/modules/kms-keyring/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management-browser/.eslintrc.js b/modules/material-management-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/material-management-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/material-management-browser/package.json b/modules/material-management-browser/package.json index ac488e3e8..7e600d5e8 100644 --- a/modules/material-management-browser/package.json +++ b/modules/material-management-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management-node/.eslintrc.js b/modules/material-management-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/material-management-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/material-management-node/package.json b/modules/material-management-node/package.json index 362f2eef9..aa7c440dc 100644 --- a/modules/material-management-node/package.json +++ b/modules/material-management-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/material-management/.eslintrc.js b/modules/material-management/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/material-management/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/material-management/package.json b/modules/material-management/package.json index 1f6a92be4..9fa42546f 100644 --- a/modules/material-management/package.json +++ b/modules/material-management/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-aes-keyring-browser/.eslintrc.js b/modules/raw-aes-keyring-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/raw-aes-keyring-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/raw-aes-keyring-browser/package.json b/modules/raw-aes-keyring-browser/package.json index a910695ad..54822fbe2 100644 --- a/modules/raw-aes-keyring-browser/package.json +++ b/modules/raw-aes-keyring-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/raw-aes-keyring-node/.eslintrc.js b/modules/raw-aes-keyring-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/raw-aes-keyring-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/raw-aes-keyring-node/package.json b/modules/raw-aes-keyring-node/package.json index b6b33e39b..1cdc39060 100644 --- a/modules/raw-aes-keyring-node/package.json +++ b/modules/raw-aes-keyring-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-keyring/.eslintrc.js b/modules/raw-keyring/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/raw-keyring/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/raw-keyring/package.json b/modules/raw-keyring/package.json index 928642ad3..716231603 100644 --- a/modules/raw-keyring/package.json +++ b/modules/raw-keyring/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/raw-rsa-keyring-browser/.eslintrc.js b/modules/raw-rsa-keyring-browser/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/raw-rsa-keyring-browser/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/raw-rsa-keyring-browser/package.json b/modules/raw-rsa-keyring-browser/package.json index aec0bdc9d..8bf93d846 100644 --- a/modules/raw-rsa-keyring-browser/package.json +++ b/modules/raw-rsa-keyring-browser/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "test": "npm run lint && npm run coverage", "coverage": "npm run karma && nyc report --exclude-after-remap false -t .karma_output --check-coverage" diff --git a/modules/raw-rsa-keyring-node/.eslintrc.js b/modules/raw-rsa-keyring-node/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/raw-rsa-keyring-node/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/raw-rsa-keyring-node/package.json b/modules/raw-rsa-keyring-node/package.json index a6378af27..4d28e013d 100644 --- a/modules/raw-rsa-keyring-node/package.json +++ b/modules/raw-rsa-keyring-node/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/serialize/.eslintrc.js b/modules/serialize/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/serialize/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/serialize/package.json b/modules/serialize/package.json index 58b98fe54..4242f0e98 100644 --- a/modules/serialize/package.json +++ b/modules/serialize/package.json @@ -4,8 +4,8 @@ "scripts": { "prepublishOnly": "tsc -p tsconfig.json && tsc -p tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage", "coverage": "nyc -e .ts npm run mocha" diff --git a/modules/web-crypto-backend/.eslintrc.js b/modules/web-crypto-backend/.eslintrc.js new file mode 100644 index 000000000..1c61b83da --- /dev/null +++ b/modules/web-crypto-backend/.eslintrc.js @@ -0,0 +1,12 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = { + parserOptions: { + // There is an issue with @typescript-eslint/parser performance. + // It scales with the number of projects + // see https://github.com/typescript-eslint/typescript-eslint/issues/1192#issuecomment-596741806 + project: '../../tsconfig.lint.json', + tsconfigRootDir: __dirname, + } +} diff --git a/modules/web-crypto-backend/package.json b/modules/web-crypto-backend/package.json index ebe4723e6..851c529ea 100644 --- a/modules/web-crypto-backend/package.json +++ b/modules/web-crypto-backend/package.json @@ -5,8 +5,8 @@ "prepublishOnly": "npm run build", "build": "tsc -b tsconfig.json && tsc -b tsconfig.module.json", "lint": "run-s lint-*", - "lint-eslint": "npx eslint modules/**/src/*.ts modules/**/test/**/*.ts", - "lint-prettier": "prettier -c modules/**/src/*.ts modules/**/test/**/*.ts", + "lint-eslint": "eslint src/*.ts test/**/*.ts", + "lint-prettier": "prettier -c src/*.ts test/**/*.ts", "karma": "karma start karma.conf.js", "mocha": "mocha --require ts-node/register test/**/*test.ts", "test": "npm run lint && npm run coverage",