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 e556d85d2..e2b1d37e1 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 @@ -37,13 +37,13 @@ export function buildCryptographicMaterialsCacheKeyHelpers ) { @@ -59,7 +59,7 @@ export function buildCryptographicMaterialsCacheKeyHelpers ) { @@ -84,26 +84,26 @@ export function buildCryptographicMaterialsCacheKeyHelpers { - buildEncryptionResponseCacheKey( + buildEncryptionMaterialCacheKey( partition: string, { suite, encryptionContext }: EncryptionRequest ): Promise - buildDecryptionResponseCacheKey( + buildDecryptionMaterialCacheKey( partition: string, { suite, encryptedDataKeys, encryptionContext }: DecryptionRequest ): Promise encryptedDataKeysHash(encryptedDataKeys: ReadonlyArray): Promise - encryptionContextHash(context?: EncryptionContext): 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 e4a7f48a7..501cb6a7a 100644 --- a/modules/cache-material/src/caching_cryptographic_materials_decorators.ts +++ b/modules/cache-material/src/caching_cryptographic_materials_decorators.ts @@ -16,10 +16,10 @@ import { GetEncryptionMaterials, // eslint-disable-line no-unused-vars GetDecryptMaterials, // eslint-disable-line no-unused-vars - DecryptionResponse, // 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 - EncryptionResponse, // 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 needs, @@ -64,38 +64,38 @@ export function decorateProperties ( } export function getEncryptionMaterials ( - { buildEncryptionResponseCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface + { buildEncryptionMaterialCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface ): GetEncryptionMaterials { return async function getEncryptionMaterials ( this: CachingMaterialsManager, request: EncryptionRequest - ): Promise> { + ): Promise> { const { suite, encryptionContext, frameLength, plaintextLength } = request - /* Check for early return (Postcondition): If I can not cache the EncryptionResponse, do not even look. */ + /* 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) } - const cacheKey = await buildEncryptionResponseCacheKey(this._partition, { suite, encryptionContext }) - const entry = this._cache.getEncryptionResponse(cacheKey, plaintextLength) - /* Check for early return (Postcondition): If I have a valid EncryptionResponse, return it. */ + 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)) { return cloneResponse(entry.response) } else { this._cache.del(cacheKey) } - const response = await this + 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. */ .getEncryptionMaterials({ suite, encryptionContext, frameLength }) - /* Check for early return (Postcondition): If I can not cache the EncryptionResponse, just return it. */ - if (!response.material.suite.cacheSafe) return response + /* Check for early return (Postcondition): If I can not cache the EncryptionMaterial, just return it. */ + if (!material.suite.cacheSafe) return material /* It is possible for an entry to exceed limits immediately. * The simplest case is to need to encrypt more than then maxBytesEncrypted. @@ -103,49 +103,49 @@ export function getEncryptionMaterials ( * but do not put a know invalid item into the cache. */ const testEntry = { - response, + response: material, now: Date.now(), messagesEncrypted: 1, bytesEncrypted: plaintextLength } if (!this._cacheEntryHasExceededLimits(testEntry)) { - this._cache.putEncryptionResponse(cacheKey, response, plaintextLength, this._maxAge) + this._cache.putEncryptionMaterial(cacheKey, material, plaintextLength, this._maxAge) } - return cloneResponse(response) + return cloneResponse(material) } } export function decryptMaterials ( - { buildDecryptionResponseCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface + { buildDecryptionMaterialCacheKey }: CryptographicMaterialsCacheKeyHelpersInterface ): GetDecryptMaterials { return async function decryptMaterials ( this: CachingMaterialsManager, request: DecryptionRequest - ): Promise> { + ): Promise> { const { suite } = request - /* Check for early return (Postcondition): If I can not cache the DecryptionResponse, do not even look. */ + /* Check for early return (Postcondition): If I can not cache the DecryptionMaterial, do not even look. */ if (!suite.cacheSafe) { return this ._backingMaterialsManager .decryptMaterials(request) } - const cacheKey = await buildDecryptionResponseCacheKey(this._partition, request) - const entry = this._cache.getDecryptionResponse(cacheKey) - /* Check for early return (Postcondition): If I have a valid DecryptionResponse, return it. */ + 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)) { return cloneResponse(entry.response) } else { this._cache.del(cacheKey) } - const response = await this + const material = await this ._backingMaterialsManager .decryptMaterials(request) - this._cache.putDecryptionResponse(cacheKey, response, this._maxAge) - return cloneResponse(response) + this._cache.putDecryptionMaterial(cacheKey, material, this._maxAge) + return cloneResponse(material) } } @@ -166,14 +166,13 @@ export function cacheEntryHasExceededLimits * Because when the Encryption SDK is done with material, it will zero it out. * Plucking off the material and cloning just that and then returning the rest of the response * can just be handled in one place. - * @param response EncryptionResponse|DecryptionResponse - * @return EncryptionResponse|DecryptionResponse + * @param material EncryptionMaterial|DecryptionMaterial + * @return EncryptionMaterial|DecryptionMaterial */ -function cloneResponse|DecryptionResponse> ( - response: R -): R { - const { material } = response - return { ...response, material: cloneMaterial(material) } +function cloneResponse|DecryptionMaterial> ( + material: M +): M { + return cloneMaterial(material) } export interface CachingMaterialsManagerInput extends Readonly<{ diff --git a/modules/cache-material/src/clone_cryptographic_material.ts b/modules/cache-material/src/clone_cryptographic_material.ts index 4d0f9b4a9..a62d84636 100644 --- a/modules/cache-material/src/clone_cryptographic_material.ts +++ b/modules/cache-material/src/clone_cryptographic_material.ts @@ -19,24 +19,23 @@ import { WebCryptoEncryptionMaterial, WebCryptoDecryptionMaterial, isEncryptionMaterial, - isDecryptionMaterial + isDecryptionMaterial, + NodeAlgorithmSuite } from '@aws-crypto/material-management' type Material = NodeEncryptionMaterial|NodeDecryptionMaterial|WebCryptoEncryptionMaterial|WebCryptoDecryptionMaterial export function cloneMaterial (source: M): M { - const clone = source instanceof NodeEncryptionMaterial - ? new NodeEncryptionMaterial(source.suite) - : source instanceof NodeDecryptionMaterial - ? new NodeDecryptionMaterial(source.suite) - : source instanceof WebCryptoEncryptionMaterial - ? new WebCryptoEncryptionMaterial(source.suite) - : source instanceof WebCryptoDecryptionMaterial - ? new WebCryptoDecryptionMaterial(source.suite) - : false + const { suite, encryptionContext } = source - if (!clone) throw new Error('Unsupported material type') + 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) const udk = new Uint8Array(source.getUnencryptedDataKey()) clone.setUnencryptedDataKey(udk, source.keyringTrace[0]) @@ -61,6 +60,8 @@ export function cloneMaterial (source: M): M { if (source.suite.signatureCurve && source.verificationKey) { clone.setVerificationKey(source.verificationKey) } + } else { + throw new Error('Material mismatch') } return clone diff --git a/modules/cache-material/src/cryptographic_materials_cache.ts b/modules/cache-material/src/cryptographic_materials_cache.ts index 37d8b66d0..58eb29c54 100644 --- a/modules/cache-material/src/cryptographic_materials_cache.ts +++ b/modules/cache-material/src/cryptographic_materials_cache.ts @@ -14,39 +14,39 @@ */ import { - EncryptionResponse, // eslint-disable-line no-unused-vars - DecryptionResponse, // 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 } from '@aws-crypto/material-management' export interface CryptographicMaterialsCache { - putEncryptionResponse( + putEncryptionMaterial( key: string, - response: EncryptionResponse, + response: EncryptionMaterial, plaintextLength: number, maxAge?: number ): void - putDecryptionResponse( + putDecryptionMaterial( key: string, - response: DecryptionResponse, + response: DecryptionMaterial, maxAge?: number ): void - getEncryptionResponse(key: string, plaintextLength: number): EncryptionResponseEntry|false - getDecryptionResponse(key: string): DecryptionResponseEntry|false + getEncryptionMaterial(key: string, plaintextLength: number): EncryptionMaterialEntry|false + getDecryptionMaterial(key: string): DecryptionMaterialEntry|false del(key: string): void } export interface Entry { - readonly response: EncryptionResponse|DecryptionResponse + response: EncryptionMaterial|DecryptionMaterial bytesEncrypted: number messagesEncrypted: number readonly now: number } -export interface EncryptionResponseEntry extends Entry { - readonly response: EncryptionResponse +export interface EncryptionMaterialEntry extends Entry { + readonly response: EncryptionMaterial } -export interface DecryptionResponseEntry extends Entry { - readonly response: DecryptionResponse +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 ff818816d..d2dff1bb0 100644 --- a/modules/cache-material/src/get_local_cryptographic_materials_cache.ts +++ b/modules/cache-material/src/get_local_cryptographic_materials_cache.ts @@ -15,8 +15,8 @@ import LRU from 'lru-cache' import { - EncryptionResponse, // eslint-disable-line no-unused-vars - DecryptionResponse, // 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 needs, isEncryptionMaterial, @@ -26,8 +26,8 @@ import { import { CryptographicMaterialsCache, // eslint-disable-line no-unused-vars Entry, // eslint-disable-line no-unused-vars - EncryptionResponseEntry, // eslint-disable-line no-unused-vars - DecryptionResponseEntry // eslint-disable-line no-unused-vars + EncryptionMaterialEntry, // eslint-disable-line no-unused-vars + DecryptionMaterialEntry // eslint-disable-line no-unused-vars } from './cryptographic_materials_cache' export function getLocalCryptographicMaterialsCache ( @@ -38,7 +38,7 @@ export function getLocalCryptographicMaterialsCache, + material: EncryptionMaterial, plaintextLength: number, maxAge?: number ) { - /* Precondition: putEncryptionResponse plaintextLength can not be negative. */ + /* Precondition: putEncryptionMaterial plaintextLength can not be negative. */ needs(plaintextLength >= 0, 'Malformed plaintextLength') /* Precondition: Only cache EncryptionMaterial. */ - needs(isEncryptionMaterial(response.material), 'Malformed response.') + needs(isEncryptionMaterial(material), 'Malformed response.') /* Precondition: Only cache EncryptionMaterial that is cacheSafe. */ - needs(response.material.suite.cacheSafe, 'Can not cache non-cache safe material') + needs(material.suite.cacheSafe, 'Can not cache non-cache safe material') const entry = Object.seal({ - response: Object.freeze(response), + response: material, bytesEncrypted: plaintextLength, messagesEncrypted: 1, now: Date.now() @@ -92,17 +92,17 @@ export function getLocalCryptographicMaterialsCache, + material: DecryptionMaterial, maxAge?: number ) { /* Precondition: Only cache DecryptionMaterial. */ - needs(isDecryptionMaterial(response.material), 'Malformed response.') + needs(isDecryptionMaterial(material), 'Malformed response.') /* Precondition: Only cache DecryptionMaterial that is cacheSafe. */ - needs(response.material.suite.cacheSafe, 'Can not cache non-cache safe material') + needs(material.suite.cacheSafe, 'Can not cache non-cache safe material') const entry = Object.seal({ - response: Object.freeze(response), + response: material, bytesEncrypted: 0, messagesEncrypted: 0, now: Date.now() @@ -110,28 +110,28 @@ export function getLocalCryptographicMaterialsCache= 0, 'Malformed plaintextLength') const entry = cache.get(key) /* Check for early return (Postcondition): If this key does not have an EncryptionMaterial, return false. */ if (!entry) return false /* Postcondition: Only return EncryptionMaterial. */ - needs(isEncryptionMaterial(entry.response.material), 'Malformed response.') + needs(isEncryptionMaterial(entry.response), 'Malformed response.') entry.bytesEncrypted += plaintextLength entry.messagesEncrypted += 1 - return >entry + return >entry }, - getDecryptionResponse (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.material), 'Malformed response.') + needs(isDecryptionMaterial(entry.response), 'Malformed response.') - return >entry + return >entry }, del (key: string) { cache.del(key) diff --git a/modules/cache-material/test/build_cryptographic_materials_cache_key_helpers.test.ts b/modules/cache-material/test/build_cryptographic_materials_cache_key_helpers.test.ts index dc888f4a8..5037087a0 100644 --- a/modules/cache-material/test/build_cryptographic_materials_cache_key_helpers.test.ts +++ b/modules/cache-material/test/build_cryptographic_materials_cache_key_helpers.test.ts @@ -31,14 +31,14 @@ const sha512 = async (...data: (Uint8Array|string)[]) => data const { encryptionContextHash, encryptedDataKeysHash, - buildEncryptionResponseCacheKey, - buildDecryptionResponseCacheKey + buildEncryptionMaterialCacheKey, + buildDecryptionMaterialCacheKey } = buildCryptographicMaterialsCacheKeyHelpers(fromUtf8, toUtf8, sha512) describe('buildCryptographicMaterialsCacheKeyHelpers::encryptionContextHash', () => { for (const vector of encryptionContextVectors) { it(`${vector.name}`, async () => { - const test = await encryptionContextHash(vector.context) + const test = await encryptionContextHash(vector.encryptionContext) expect(test).to.deep.equal(vector.hash) }) } @@ -54,19 +54,19 @@ describe('buildCryptographicMaterialsCacheKeyHelpers::encryptedDataKeysHash', () } }) -describe('buildCryptographicMaterialsCacheKeyHelpers::buildEncryptionResponseCacheKey', () => { +describe('buildCryptographicMaterialsCacheKeyHelpers::buildEncryptionMaterialCacheKey', () => { for (const vector of encryptCacheKeyVectors) { it(`${vector.id}`, async () => { - const test = await buildEncryptionResponseCacheKey(...vector.arguments) + const test = await buildEncryptionMaterialCacheKey(...vector.arguments) expect(test).to.equal(Buffer.from(vector.id, 'base64').toString()) }) } }) -describe('buildCryptographicMaterialsCacheKeyHelpers::buildEncryptionResponseCacheKey', () => { +describe('buildCryptographicMaterialsCacheKeyHelpers::buildEncryptionMaterialCacheKey', () => { for (const vector of decryptCacheKeyVectors) { it(`${vector.id}`, async () => { - const test = await buildDecryptionResponseCacheKey(...vector.arguments) + const test = await buildDecryptionMaterialCacheKey(...vector.arguments) expect(test).to.equal(Buffer.from(vector.id, 'base64').toString()) }) } diff --git a/modules/cache-material/test/caching_cryptographic_materials_decorators.test.ts b/modules/cache-material/test/caching_cryptographic_materials_decorators.test.ts index 66ae7e293..abcfbc379 100644 --- a/modules/cache-material/test/caching_cryptographic_materials_decorators.test.ts +++ b/modules/cache-material/test/caching_cryptographic_materials_decorators.test.ts @@ -207,23 +207,15 @@ describe('Cryptographic Material Functions', () => { const edk1 = new EncryptedDataKey({ providerId: 'keyNamespace', providerInfo: 'keyName', encryptedDataKey: new Uint8Array([1]) }) const edk2 = new EncryptedDataKey({ providerId: 'p2', providerInfo: 'pi2', encryptedDataKey: new Uint8Array([2]) }) - const encryptionMaterial = new NodeEncryptionMaterial(nodeSuite) + const encryptionMaterial = new NodeEncryptionMaterial(nodeSuite, {}) .setUnencryptedDataKey(udk128, trace) .addEncryptedDataKey(edk1, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) .addEncryptedDataKey(edk2, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) - const decryptionMaterial = new NodeDecryptionMaterial(nodeSuite) + const decryptionMaterial = new NodeDecryptionMaterial(nodeSuite, {}) .setUnencryptedDataKey(udk128, trace) const context = {} - const encryptionResponse = { - material: encryptionMaterial, - context - } - const decryptionResponse = { - material: decryptionMaterial, - context - } const _maxAge = 10 const _maxBytesEncrypted = 10 @@ -231,10 +223,10 @@ describe('Cryptographic Material Functions', () => { const _cache = getLocalCryptographicMaterialsCache(100) const _backingMaterialsManager = { getEncryptionMaterials () { - return encryptionResponse + return encryptionMaterial }, decryptMaterials () { - return decryptionResponse + return decryptionMaterial } } as any const _partition = 'partition' @@ -268,11 +260,9 @@ describe('Cryptographic Material Functions', () => { plaintextLength: 10 }) // The response must be cloned... i.e. not the same. - expect(test === encryptionResponse).to.equal(false) - // the material must be cloned... because after use it will be zeroed - expect(test.material === encryptionResponse.material).to.equal(false) - expect(test.context === encryptionResponse.context).to.equal(true) - expect(test.material.getUnencryptedDataKey()).to.deep.equal(encryptionMaterial.getUnencryptedDataKey()) + expect(test === encryptionMaterial).to.equal(false) + expect(test.encryptionContext).to.deep.equal(encryptionMaterial.encryptionContext) + expect(test.getUnencryptedDataKey()).to.deep.equal(encryptionMaterial.getUnencryptedDataKey()) }) }) @@ -284,11 +274,9 @@ describe('Cryptographic Material Functions', () => { encryptedDataKeys: [edk1] }) // The response must be cloned... i.e. not the same. - expect(test === decryptionResponse).to.equal(false) - // the material must be cloned... because after use it will be zeroed - expect(test.material === decryptionResponse.material).to.equal(false) - expect(test.context === decryptionResponse.context).to.equal(true) - expect(test.material.getUnencryptedDataKey()).to.deep.equal(decryptionMaterial.getUnencryptedDataKey()) + expect(test === decryptionMaterial).to.equal(false) + expect(test.encryptionContext).to.deep.equal(decryptionMaterial.encryptionContext) + expect(test.getUnencryptedDataKey()).to.deep.equal(decryptionMaterial.getUnencryptedDataKey()) }) }) }) diff --git a/modules/cache-material/test/clone_cryptographic_material.test.ts b/modules/cache-material/test/clone_cryptographic_material.test.ts index c87e3bece..91f94862c 100644 --- a/modules/cache-material/test/clone_cryptographic_material.test.ts +++ b/modules/cache-material/test/clone_cryptographic_material.test.ts @@ -46,7 +46,7 @@ const cryptoKey: any = { type: 'secret', algorithm: { name: webCryptoSuite.encry describe('cloneMaterial', () => { it('clone NodeEncryptionMaterial', () => { - const material = new NodeEncryptionMaterial(nodeSuite) + const material = new NodeEncryptionMaterial(nodeSuite, { some: 'context' }) .setUnencryptedDataKey(udk128, trace) .addEncryptedDataKey(edk1, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) .addEncryptedDataKey(edk2, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) @@ -56,20 +56,22 @@ describe('cloneMaterial', () => { expect(test.getUnencryptedDataKey()).to.deep.equal(udk128) expect(test.keyringTrace).to.deep.equal(material.keyringTrace) expect(test.encryptedDataKeys).to.deep.equal(material.encryptedDataKeys) + expect(test.encryptionContext).to.deep.equal(material.encryptionContext) }) it('clone NodeDecryptionMaterial', () => { - const material = new NodeDecryptionMaterial(nodeSuite) + const material = new NodeDecryptionMaterial(nodeSuite, { some: 'context' }) .setUnencryptedDataKey(udk128, trace) const test = cloneMaterial(material) expect(test).to.be.instanceOf(NodeDecryptionMaterial) expect(test.getUnencryptedDataKey()).to.deep.equal(udk128) expect(test.keyringTrace).to.deep.equal(material.keyringTrace) + expect(test.encryptionContext).to.deep.equal(material.encryptionContext) }) it('clone WebCryptoEncryptionMaterial', () => { - const material = new WebCryptoEncryptionMaterial(webCryptoSuite) + const material = new WebCryptoEncryptionMaterial(webCryptoSuite, { some: 'context' }) .setUnencryptedDataKey(udk128, trace) .setCryptoKey(cryptoKey, trace) .addEncryptedDataKey(edk1, KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY) @@ -81,10 +83,11 @@ describe('cloneMaterial', () => { expect(test.getCryptoKey()).to.deep.equal(cryptoKey) expect(test.keyringTrace).to.deep.equal(material.keyringTrace) expect(test.encryptedDataKeys).to.deep.equal(material.encryptedDataKeys) + expect(test.encryptionContext).to.deep.equal(material.encryptionContext) }) it('clone WebCryptoDecryptionMaterial', () => { - const material = new WebCryptoDecryptionMaterial(webCryptoSuite) + const material = new WebCryptoDecryptionMaterial(webCryptoSuite, { some: 'context' }) .setUnencryptedDataKey(udk128, trace) .setCryptoKey(cryptoKey, trace) @@ -93,5 +96,6 @@ describe('cloneMaterial', () => { expect(test.getUnencryptedDataKey()).to.deep.equal(udk128) expect(test.getCryptoKey()).to.deep.equal(cryptoKey) expect(test.keyringTrace).to.deep.equal(material.keyringTrace) + expect(test.encryptionContext).to.deep.equal(material.encryptionContext) }) }) diff --git a/modules/cache-material/test/fixtures.ts b/modules/cache-material/test/fixtures.ts index ba71aeed8..d7c8cd808 100644 --- a/modules/cache-material/test/fixtures.ts +++ b/modules/cache-material/test/fixtures.ts @@ -20,13 +20,13 @@ import { EncryptedDataKey, AlgorithmSuiteIdentifier } from '@aws-crypto/material const partitionName = 'c15b9079-6d0e-42b6-8784-5e804b025692' const encryptionContextEmpty = { name: 'encryptionContextEmpty', - context: {}, + encryptionContext: {}, hash: new Uint8Array([ 207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, 122, 129, 165, 56, 50, 122, 249, 39, 218, 62 ]) } const encryptionContextFull = { name: 'encryptionContextFull', - context: { 'this': 'is', 'a': 'non-empty', 'encryption': 'context' }, + encryptionContext: { 'this': 'is', 'a': 'non-empty', 'encryption': 'context' }, hash: new Uint8Array([ 4, 250, 62, 217, 137, 103, 44, 245, 231, 15, 24, 164, 62, 35, 99, 8, 4, 29, 75, 147, 51, 243, 111, 68, 2, 126, 189, 113, 20, 150, 243, 92, 188, 56, 128, 79, 167, 9, 114, 93, 83, 189, 146, 168, 7, 189, 229, 174, 231, 68, 184, 217, 66, 18, 60, 223, 54, 127, 13, 7, 230, 79, 129, 73 ]) } @@ -60,7 +60,7 @@ export const encryptCacheKeyVectors: VectorHack[] = [ partitionName, { suite: undefined, - encryptionContext: encryptionContextEmpty.context + encryptionContext: encryptionContextEmpty.encryptionContext } ], id: 'rkrFAso1YyPbOJbmwVMjrPw+wwLJT7xusn8tA8zMe9e3+OqbtfDueB7bvoKLU3fsmdUvZ6eMt7mBp1ThMMB25Q==' @@ -70,7 +70,7 @@ export const encryptCacheKeyVectors: VectorHack[] = [ partitionName, { suite: { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 }, - encryptionContext: encryptionContextEmpty.context + encryptionContext: encryptionContextEmpty.encryptionContext } ], id: '3icBIkLK4V3fVwbm3zSxUdUQV6ZvZYUOLl8buN36g6gDMqAkghcGryxX7QiVABkW1JhB6GRp5z+bzbiuciBcKQ==' @@ -80,7 +80,7 @@ export const encryptCacheKeyVectors: VectorHack[] = [ partitionName, { suite: undefined, - encryptionContext: encryptionContextFull.context + encryptionContext: encryptionContextFull.encryptionContext } ], id: 'IHiUHYOUVUEFTc3BcZPJDlsWct2Qy1A7JdfQl9sQoV/ILIbRpoz9q7RtGd/MlibaGl5ihE66cN8ygM8A5rtYbg==' @@ -90,7 +90,7 @@ export const encryptCacheKeyVectors: VectorHack[] = [ partitionName, { suite: { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 }, - encryptionContext: encryptionContextFull.context + encryptionContext: encryptionContextFull.encryptionContext } ], id: 'mRNK7qhTb/kJiiyGPgAevp0gwFRcET4KeeNYwZHhoEDvSUzQiDgl8Of+YRDaVzKxAqpNBgcAuFXde9JlaRRsmw==' @@ -104,7 +104,7 @@ export const decryptCacheKeyVectors: VectorHack[] = [ { suite: { id: AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256 }, encryptedDataKeys: [encryptedDataKey1.edk], - encryptionContext: encryptionContextEmpty.context + encryptionContext: encryptionContextEmpty.encryptionContext } ], id: 'n0zVzk9QIVxhz6ET+aJIKKOJNxtpGtSe1yAbu7WU5l272Iw/jmhlER4psDHJs9Mr8KYiIvLGSXzggNDCc23+9w==' @@ -115,7 +115,7 @@ export const decryptCacheKeyVectors: VectorHack[] = [ { suite: { id: AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 }, encryptedDataKeys: [encryptedDataKey1.edk, encryptedDataKey2.edk], - encryptionContext: encryptionContextFull.context + encryptionContext: encryptionContextFull.encryptionContext } ], id: '+rtwUe38CGnczGmYu12iqGWHIyDyZ44EvYQ4S6ACmsgS8VaEpiw0RTGpDk6Z/7YYN/jVHOAcNKDyCNP8EmstFg==' diff --git a/modules/cache-material/test/get_local_cryptographic_materials_cache.test.ts b/modules/cache-material/test/get_local_cryptographic_materials_cache.test.ts index 7c69c75c2..fec9c60bd 100644 --- a/modules/cache-material/test/get_local_cryptographic_materials_cache.test.ts +++ b/modules/cache-material/test/get_local_cryptographic_materials_cache.test.ts @@ -26,27 +26,24 @@ import { } from '@aws-crypto/material-management' const nodeSuite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) -const encryptionMaterial = new NodeEncryptionMaterial(nodeSuite) -const decryptionMaterial = new NodeDecryptionMaterial(nodeSuite) +const encryptionMaterial = new NodeEncryptionMaterial(nodeSuite, {}) +const decryptionMaterial = new NodeDecryptionMaterial(nodeSuite, {}) describe('getLocalCryptographicMaterialsCache', () => { const { - getEncryptionResponse, - getDecryptionResponse, + getEncryptionMaterial, + getDecryptionMaterial, del, - putEncryptionResponse, - putDecryptionResponse + putEncryptionMaterial, + putDecryptionMaterial } = getLocalCryptographicMaterialsCache(100) - it('putEncryptionResponse', () => { + it('putEncryptionMaterial', () => { const key = 'some encryption key' - const response: any = { - material: encryptionMaterial, - context: {} - } + const response: any = encryptionMaterial - putEncryptionResponse(key, response, 1) - const test = getEncryptionResponse(key, 1) + putEncryptionMaterial(key, response, 1) + const test = getEncryptionMaterial(key, 1) if (!test) throw new Error('never') expect(test.bytesEncrypted).to.equal(2) expect(test.messagesEncrypted).to.equal(2) @@ -54,50 +51,38 @@ describe('getLocalCryptographicMaterialsCache', () => { expect(Object.isFrozen(test.response)).to.equal(true) }) - it('Precondition: putEncryptionResponse plaintextLength can not be negative.', () => { - const response: any = { - material: encryptionMaterial, - context: {} - } + it('Precondition: putEncryptionMaterial plaintextLength can not be negative.', () => { + const response: any = encryptionMaterial const u: any = undefined const s: any = 'not-number' const n = -1 - expect(() => putEncryptionResponse('key', response, u)).to.throw() - expect(() => putEncryptionResponse('key', response, s)).to.throw() - expect(() => putEncryptionResponse('key', response, n)).to.throw() + expect(() => putEncryptionMaterial('key', response, u)).to.throw() + expect(() => putEncryptionMaterial('key', response, s)).to.throw() + expect(() => putEncryptionMaterial('key', response, n)).to.throw() }) it('Postcondition: Only return EncryptionMaterial.', () => { const key = 'some decryption key' - const response: any = { - material: decryptionMaterial, - context: {} - } + const response: any = decryptionMaterial - putDecryptionResponse(key, response) - expect(() => getEncryptionResponse(key, 1)).to.throw() + putDecryptionMaterial(key, response) + expect(() => getEncryptionMaterial(key, 1)).to.throw() }) it('Precondition: Only cache EncryptionMaterial that is cacheSafe.', () => { const key = 'some encryption key' const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const response: any = { - material: new NodeEncryptionMaterial(suite), - context: {} - } + const response: any = new NodeEncryptionMaterial(suite, {}) - expect(() => putEncryptionResponse(key, response, 1)).to.throw() + expect(() => putEncryptionMaterial(key, response, 1)).to.throw() }) - it('putDecryptionResponse', () => { + it('putDecryptionMaterial', () => { const key = 'some decryption key' - const response: any = { - material: decryptionMaterial, - context: {} - } + const response: any = decryptionMaterial - putDecryptionResponse(key, response) - const test = getDecryptionResponse(key) + putDecryptionMaterial(key, response) + const test = getDecryptionMaterial(key) if (!test) throw new Error('never') expect(test.bytesEncrypted).to.equal(0) expect(test.messagesEncrypted).to.equal(0) @@ -107,63 +92,51 @@ describe('getLocalCryptographicMaterialsCache', () => { it('Precondition: Only cache DecryptionMaterial.', () => { const key = 'some decryption key' - const response: any = { - material: 'not material', - context: {} - } + const response: any = 'not material' - expect(() => putDecryptionResponse(key, response)).to.throw() + expect(() => putDecryptionMaterial(key, response)).to.throw() }) it('Precondition: Only cache DecryptionMaterial that is cacheSafe.', () => { const key = 'some decryption key' const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const response: any = { - material: new NodeEncryptionMaterial(suite), - context: {} - } + const response: any = new NodeEncryptionMaterial(suite, {}) - expect(() => putDecryptionResponse(key, response)).to.throw() + expect(() => putDecryptionMaterial(key, response)).to.throw() }) it('Precondition: plaintextLength can not be negative.', () => { const u: any = undefined const s: any = 'not-number' const n = -1 - expect(() => getEncryptionResponse('key', u)).to.throw() - expect(() => getEncryptionResponse('key', s)).to.throw() - expect(() => getEncryptionResponse('key', n)).to.throw() + expect(() => getEncryptionMaterial('key', u)).to.throw() + expect(() => getEncryptionMaterial('key', s)).to.throw() + expect(() => getEncryptionMaterial('key', n)).to.throw() }) it('Check for early return (Postcondition): If this key does not have an EncryptionMaterial, return false.', () => { - const test = getEncryptionResponse('does-not-exist', 1) + const test = getEncryptionMaterial('does-not-exist', 1) expect(test).to.equal(false) }) it('Precondition: Only cache EncryptionMaterial.', () => { const key = 'some encryption key' - const response: any = { - material: 'not material', - context: {} - } + const response: any = 'not material' - expect(() => putEncryptionResponse(key, response, 1)).to.throw() + expect(() => putEncryptionMaterial(key, response, 1)).to.throw() }) it('Check for early return (Postcondition): If this key does not have a DecryptionMaterial, return false.', () => { - const test = getDecryptionResponse('does-not-exist') + const test = getDecryptionMaterial('does-not-exist') expect(test).to.equal(false) }) it('Postcondition: Only return DecryptionMaterial.', () => { const key = 'some encryption key' - const response: any = { - material: encryptionMaterial, - context: {} - } + const response: any = encryptionMaterial - putEncryptionResponse(key, response, 1) - expect(() => getDecryptionResponse(key)) + putEncryptionMaterial(key, response, 1) + expect(() => getDecryptionMaterial(key)) }) it('delete non-existent key', () => { @@ -172,13 +145,10 @@ describe('getLocalCryptographicMaterialsCache', () => { it('zero is an acceptable plaintextLength', () => { const key = 'some encryption key' - const response: any = { - material: encryptionMaterial, - context: {} - } + const response: any = encryptionMaterial - putEncryptionResponse(key, response, 0) - const test = getEncryptionResponse(key, 0) + putEncryptionMaterial(key, response, 0) + const test = getEncryptionMaterial(key, 0) if (!test) throw new Error('never') expect(test.bytesEncrypted).to.equal(0) expect(test.messagesEncrypted).to.equal(2) @@ -188,119 +158,101 @@ describe('getLocalCryptographicMaterialsCache', () => { }) describe('cache eviction', () => { - it('putDecryptionResponse can exceed maxSize', () => { + it('putDecryptionMaterial can exceed maxSize', () => { const { - getDecryptionResponse, - putDecryptionResponse + getDecryptionMaterial, + putDecryptionMaterial } = getLocalCryptographicMaterialsCache(1) const key1 = 'key lost' const key2 = 'key replace' - const response: any = { - material: decryptionMaterial, - context: {} - } - - putDecryptionResponse(key1, response) - putDecryptionResponse(key2, response) - const lost = getDecryptionResponse(key1) - const found = getDecryptionResponse(key2) + const response: any = decryptionMaterial + + putDecryptionMaterial(key1, response) + putDecryptionMaterial(key2, response) + const lost = getDecryptionMaterial(key1) + const found = getDecryptionMaterial(key2) expect(lost).to.equal(false) expect(found).to.not.equal(false) }) - it('putDecryptionResponse can be deleted', () => { + it('putDecryptionMaterial can be deleted', () => { const { - getDecryptionResponse, - putDecryptionResponse, + getDecryptionMaterial, + putDecryptionMaterial, del } = getLocalCryptographicMaterialsCache(1) const key = 'key deleted' - const response: any = { - material: decryptionMaterial, - context: {} - } + const response: any = decryptionMaterial - putDecryptionResponse(key, response) + putDecryptionMaterial(key, response) del(key) - const lost = getDecryptionResponse(key) + const lost = getDecryptionMaterial(key) expect(lost).to.equal(false) }) - it('putDecryptionResponse can be garbage collected', async () => { + it('putDecryptionMaterial can be garbage collected', async () => { const { - getDecryptionResponse, - putDecryptionResponse + getDecryptionMaterial, + putDecryptionMaterial } = getLocalCryptographicMaterialsCache(1, 10) const key = 'key lost' - const response: any = { - material: decryptionMaterial, - context: {} - } + const response: any = decryptionMaterial - putDecryptionResponse(key, response, 1) + putDecryptionMaterial(key, response, 1) await new Promise(resolve => setTimeout(resolve, 20)) - const lost = getDecryptionResponse(key) + const lost = getDecryptionMaterial(key) expect(lost).to.equal(false) }) - it('putEncryptionResponse can exceed maxSize', () => { + it('putEncryptionMaterial can exceed maxSize', () => { const { - getEncryptionResponse, - putEncryptionResponse + getEncryptionMaterial, + putEncryptionMaterial } = getLocalCryptographicMaterialsCache(1) const key1 = 'key lost' const key2 = 'key replace' - const response: any = { - material: encryptionMaterial, - context: {} - } - - putEncryptionResponse(key1, response, 0) - putEncryptionResponse(key2, response, 0) - const lost = getEncryptionResponse(key1, 0) - const found = getEncryptionResponse(key2, 0) + const response: any = encryptionMaterial + + putEncryptionMaterial(key1, response, 0) + putEncryptionMaterial(key2, response, 0) + const lost = getEncryptionMaterial(key1, 0) + const found = getEncryptionMaterial(key2, 0) expect(lost).to.equal(false) expect(found).to.not.equal(false) }) - it('putEncryptionResponse can be deleted', async () => { + it('putEncryptionMaterial can be deleted', async () => { const { - getEncryptionResponse, - putEncryptionResponse, + getEncryptionMaterial, + putEncryptionMaterial, del } = getLocalCryptographicMaterialsCache(1, 10) const key = 'key lost' - const response: any = { - material: encryptionMaterial, - context: {} - } + const response: any = encryptionMaterial - putEncryptionResponse(key, response, 1, 1) + putEncryptionMaterial(key, response, 1, 1) del(key) - const lost = getEncryptionResponse(key, 1) + const lost = getEncryptionMaterial(key, 1) expect(lost).to.equal(false) }) - it('putEncryptionResponse can be garbage collected', async () => { + it('putEncryptionMaterial can be garbage collected', async () => { const { - getEncryptionResponse, - putEncryptionResponse + getEncryptionMaterial, + putEncryptionMaterial } = getLocalCryptographicMaterialsCache(1, 10) const key = 'key lost' - const response: any = { - material: encryptionMaterial, - context: {} - } + const response: any = encryptionMaterial - putEncryptionResponse(key, response, 1, 1) + putEncryptionMaterial(key, response, 1, 1) await new Promise(resolve => setTimeout(resolve, 20)) - const lost = getEncryptionResponse(key, 1) + const lost = getEncryptionMaterial(key, 1) expect(lost).to.equal(false) }) }) diff --git a/modules/decrypt-browser/src/decrypt.ts b/modules/decrypt-browser/src/decrypt.ts index b322f629c..57d9b7c5f 100644 --- a/modules/decrypt-browser/src/decrypt.ts +++ b/modules/decrypt-browser/src/decrypt.ts @@ -59,7 +59,7 @@ export async function decrypt ( const { encryptionContext, encryptedDataKeys, suiteId, messageId } = messageHeader const suite = new WebCryptoAlgorithmSuite(suiteId) - const { material } = await cmm.decryptMaterials({ suite, encryptionContext, encryptedDataKeys }) + const material = await cmm.decryptMaterials({ suite, encryptionContext, encryptedDataKeys }) const { kdfGetSubtleDecrypt, subtleVerify, dispose } = await getDecryptionHelper(material) const info = kdfInfo(suiteId, messageId) const getSubtleDecrypt = kdfGetSubtleDecrypt(info) diff --git a/modules/decrypt-node/src/parse_header_stream.ts b/modules/decrypt-node/src/parse_header_stream.ts index e0e1f54e9..89197ba45 100644 --- a/modules/decrypt-node/src/parse_header_stream.ts +++ b/modules/decrypt-node/src/parse_header_stream.ts @@ -86,7 +86,7 @@ export class ParseHeaderStream extends PortableTransformWithType { this.materialsManager .decryptMaterials({ suite, encryptionContext, encryptedDataKeys }) - .then(({ material }) => { + .then((material) => { this._headerState.buffer = Buffer.alloc(0) // clear the Buffer... const { kdfGetDecipher, getVerify, dispose } = getDecryptionHelper(material) diff --git a/modules/encrypt-browser/src/encrypt.ts b/modules/encrypt-browser/src/encrypt.ts index 4d7d54f2c..facb37f44 100644 --- a/modules/encrypt-browser/src/encrypt.ts +++ b/modules/encrypt-browser/src/encrypt.ts @@ -60,7 +60,7 @@ export interface EncryptResult { 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}`) @@ -84,7 +84,7 @@ export async function encrypt ( plaintextLength } - const { material, context } = await cmm.getEncryptionMaterials(encryptionRequest) + const material = await cmm.getEncryptionMaterials(encryptionRequest) const { kdfGetSubtleEncrypt, subtleSign, dispose } = await getEncryptHelper(material) const messageId = await backend.randomValues(MESSAGE_ID_LENGTH) @@ -96,7 +96,7 @@ export async function encrypt ( type: ObjectType.CUSTOMER_AE_DATA, suiteId: id, messageId, - encryptionContext: context, + encryptionContext: material.encryptionContext, encryptedDataKeys: material.encryptedDataKeys, contentType: ContentType.FRAMED_DATA, headerIvLength: ivLength, diff --git a/modules/encrypt-node/src/encrypt_stream.ts b/modules/encrypt-node/src/encrypt_stream.ts index 1a48f293d..aadb83523 100644 --- a/modules/encrypt-node/src/encrypt_stream.ts +++ b/modules/encrypt-node/src/encrypt_stream.ts @@ -40,7 +40,7 @@ const { serializeMessageHeader, headerAuthIv } = serializeFactory(fromUtf8) export interface EncryptStreamInput { suiteId?: AlgorithmSuiteIdentifier - context?: EncryptionContext + encryptionContext?: EncryptionContext frameLength?: number plaintextLength?: number } @@ -56,7 +56,7 @@ export function encryptStream ( cmm: KeyringNode|NodeMaterialsManager, op: EncryptStreamInput = {} ): Duplex { - const { suiteId, context, frameLength = FRAME_LENGTH } = op + const { suiteId, encryptionContext = {}, frameLength = FRAME_LENGTH } = 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}`) @@ -70,11 +70,11 @@ export function encryptStream ( const wrappingStream = new Duplexify() - cmm.getEncryptionMaterials({ suite, encryptionContext: context, frameLength }) - .then(async ({ material, context }) => { + cmm.getEncryptionMaterials({ suite, encryptionContext, frameLength }) + .then(async (material) => { const { dispose, getSigner } = getEncryptHelper(material) - const { getCipher, messageHeader, rawHeader } = getEncryptionInfo(material, frameLength, context) + const { getCipher, messageHeader, rawHeader } = getEncryptionInfo(material, frameLength) wrappingStream.emit('MessageHeader', messageHeader) @@ -95,8 +95,9 @@ export function encryptStream ( return wrappingStream } -export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLength: number, context: EncryptionContext) { +export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLength: number) { const { kdfGetCipher } = getEncryptHelper(material) + const { encryptionContext } = material const messageId = randomBytes(MESSAGE_ID_LENGTH) const { id, ivLength } = material.suite @@ -105,7 +106,7 @@ export function getEncryptionInfo (material : NodeEncryptionMaterial, frameLengt type: ObjectType.CUSTOMER_AE_DATA, suiteId: id, messageId, - encryptionContext: context, + encryptionContext, encryptedDataKeys: Object.freeze(material.encryptedDataKeys), // freeze me please contentType: ContentType.FRAMED_DATA, headerIvLength: ivLength, diff --git a/modules/encrypt-node/test/encrypt.test.ts b/modules/encrypt-node/test/encrypt.test.ts index 82029b01e..5a2a820e5 100644 --- a/modules/encrypt-node/test/encrypt.test.ts +++ b/modules/encrypt-node/test/encrypt.test.ts @@ -92,10 +92,10 @@ describe('encrypt structural testing', () => { }) it('encrypt a buffer', async () => { - const context = { simple: 'context' } + const encryptionContext = { simple: 'context' } const plaintext = Buffer.from('asdf') - const { ciphertext, messageHeader } = await encrypt(keyRing, plaintext, { context }) + const { ciphertext, messageHeader } = await encrypt(keyRing, plaintext, { encryptionContext }) /* The default algorithm suite will add a signature key to the context. * So I only check that the passed context elements exist. @@ -111,7 +111,7 @@ describe('encrypt structural testing', () => { }) it('encrypt a stream', async () => { - const context = { simple: 'context' } + const encryptionContext = { simple: 'context' } let pushed = false const plaintext = from((_: number, next: Function) => { @@ -120,7 +120,7 @@ describe('encrypt structural testing', () => { next(null, 'asdf') }) - const { ciphertext, messageHeader } = await encrypt(keyRing, plaintext, { context }) + const { ciphertext, messageHeader } = await encrypt(keyRing, plaintext, { encryptionContext }) /* The default algorithm suite will add a signature key to the context. * So I only check that the passed context elements exist. @@ -141,7 +141,7 @@ describe('encrypt structural testing', () => { }) it('encryptStream', async () => { - const context = { simple: 'context' } + const encryptionContext = { simple: 'context' } const data = randomBytes(300) const i = data.values() @@ -157,7 +157,7 @@ describe('encrypt structural testing', () => { let messageHeader: any const buffer: Buffer[] = [] const stream = plaintext - .pipe(encryptStream(keyRing, { context, frameLength: 5 })) + .pipe(encryptStream(keyRing, { encryptionContext, frameLength: 5 })) .on('MessageHeader', (header: MessageHeader) => { // MessageHeader should only be called once if (messageHeader) throw new Error('I should never see this error') diff --git a/modules/example-node/src/aes_simple.ts b/modules/example-node/src/aes_simple.ts index 4f3aecb14..5d4efc5d4 100644 --- a/modules/example-node/src/aes_simple.ts +++ b/modules/example-node/src/aes_simple.ts @@ -57,7 +57,7 @@ export async function aesTest () { const cleartext = 'asdf' /* Encrypt the data. */ - const { ciphertext } = await encrypt(keyring, cleartext, { context }) + const { ciphertext } = await encrypt(keyring, cleartext, { encryptionContext: context }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, ciphertext) diff --git a/modules/example-node/src/kms_simple.ts b/modules/example-node/src/kms_simple.ts index 2c2e79ca8..f2e249a34 100644 --- a/modules/example-node/src/kms_simple.ts +++ b/modules/example-node/src/kms_simple.ts @@ -52,7 +52,7 @@ export async function kmsSimpleTest () { const cleartext = 'asdf' /* Encrypt the data. */ - const { ciphertext } = await encrypt(keyring, cleartext, { context }) + const { ciphertext } = await encrypt(keyring, cleartext, { encryptionContext: context }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, ciphertext) diff --git a/modules/example-node/src/kms_stream.ts b/modules/example-node/src/kms_stream.ts index 455c11586..a8f4d596f 100644 --- a/modules/example-node/src/kms_stream.ts +++ b/modules/example-node/src/kms_stream.ts @@ -66,7 +66,7 @@ export async function kmsStreamTest (filename: string) { /* Create a simple pipeline to encrypt the package.json for this project. */ const stream = createReadStream(filename) - .pipe(encryptStream(keyring, { context })) + .pipe(encryptStream(keyring, { encryptionContext: context })) .pipe(decryptStream(new KmsKeyringNode({ discovery: true }))) .on('MessageHeader', ({ encryptionContext }: MessageHeader) => { /* Verify the encryption context. diff --git a/modules/example-node/src/multi_keyring.ts b/modules/example-node/src/multi_keyring.ts index 5c86854b0..8fa98f71f 100644 --- a/modules/example-node/src/multi_keyring.ts +++ b/modules/example-node/src/multi_keyring.ts @@ -76,7 +76,7 @@ export async function multiKeyringTest () { const cleartext = 'asdf' /* Encrypt the data. */ - const { ciphertext } = await encrypt(keyring, cleartext, { context }) + const { ciphertext } = await encrypt(keyring, cleartext, { encryptionContext: context }) /* Decrypt the data. * This decrypt call could be done with **any** of the 3 keyrings. diff --git a/modules/example-node/src/rsa_simple.ts b/modules/example-node/src/rsa_simple.ts index 4f8904272..cfdc247ae 100644 --- a/modules/example-node/src/rsa_simple.ts +++ b/modules/example-node/src/rsa_simple.ts @@ -59,7 +59,7 @@ export async function rsaTest () { const cleartext = 'asdf' /* Encrypt the data. */ - const { ciphertext } = await encrypt(keyring, cleartext, { context }) + const { ciphertext } = await encrypt(keyring, cleartext, { encryptionContext: context }) /* Decrypt the data. */ const { plaintext, messageHeader } = await decrypt(keyring, ciphertext) diff --git a/modules/kms-keyring-browser/src/kms_keyring_browser.ts b/modules/kms-keyring-browser/src/kms_keyring_browser.ts index 24cd9554b..a80f6d3e9 100644 --- a/modules/kms-keyring-browser/src/kms_keyring_browser.ts +++ b/modules/kms-keyring-browser/src/kms_keyring_browser.ts @@ -28,7 +28,6 @@ import { WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars WebCryptoEncryptionMaterial, // eslint-disable-line no-unused-vars WebCryptoDecryptionMaterial, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars EncryptedDataKey, // eslint-disable-line no-unused-vars immutableClass, importForWebCryptoEncryptionMaterial, @@ -55,14 +54,14 @@ export class KmsKeyringBrowser extends KmsKeyringClass(KeyringWebCrypto as KeyRi super({ clientProvider, keyIds, generatorKeyId, grantTokens, discovery }) } - async _onEncrypt (material: WebCryptoEncryptionMaterial, context?: EncryptionContext) { - const _material = await super._onEncrypt(material, context) + async _onEncrypt (material: WebCryptoEncryptionMaterial) { + const _material = await super._onEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) } - async _onDecrypt (material: WebCryptoDecryptionMaterial, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext) { - const _material = await super._onDecrypt(material, encryptedDataKeys, context) + async _onDecrypt (material: WebCryptoDecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]) { + const _material = await super._onDecrypt(material, encryptedDataKeys) return importForWebCryptoDecryptionMaterial(_material) } diff --git a/modules/kms-keyring-browser/test/kms_keyring_browser.test.ts b/modules/kms-keyring-browser/test/kms_keyring_browser.test.ts index e49344a5a..7aca80e04 100644 --- a/modules/kms-keyring-browser/test/kms_keyring_browser.test.ts +++ b/modules/kms-keyring-browser/test/kms_keyring_browser.test.ts @@ -70,7 +70,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) const udk = test.getUnencryptedDataKey() @@ -82,7 +82,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) // The UnencryptedDataKey should be zeroed, because the cryptoKey has been set diff --git a/modules/kms-keyring-node/test/kms_keyring_node.test.ts b/modules/kms-keyring-node/test/kms_keyring_node.test.ts index 310024c61..7fd943b4f 100644 --- a/modules/kms-keyring-node/test/kms_keyring_node.test.ts +++ b/modules/kms-keyring-node/test/kms_keyring_node.test.ts @@ -58,7 +58,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) udk = test.getUnencryptedDataKey() @@ -70,7 +70,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) expect(test.getUnencryptedDataKey()).to.deep.equal(udk) diff --git a/modules/kms-keyring/src/helpers.ts b/modules/kms-keyring/src/helpers.ts index 0336ec3c5..ec3fa5437 100644 --- a/modules/kms-keyring/src/helpers.ts +++ b/modules/kms-keyring/src/helpers.ts @@ -36,7 +36,7 @@ export async function generateDataKey ( clientProvider: KmsClientSupplier, NumberOfBytes: number, KeyId: string, - EncryptionContext?: EncryptionContext, + EncryptionContext: EncryptionContext, GrantTokens?: string[] ): Promise { const region = regionFromKmsKeyArn(KeyId) @@ -57,7 +57,7 @@ export async function encrypt ( clientProvider: KmsClientSupplier, Plaintext: Uint8Array, KeyId: string, - EncryptionContext?: EncryptionContext, + EncryptionContext: EncryptionContext, GrantTokens?: string[] ): Promise { const region = regionFromKmsKeyArn(KeyId) @@ -78,7 +78,7 @@ export async function encrypt ( export async function decrypt ( clientProvider: KmsClientSupplier, { providerId, providerInfo, encryptedDataKey }: EncryptedDataKey, - EncryptionContext?: EncryptionContext, + EncryptionContext: EncryptionContext, GrantTokens?: string[] ): Promise { /* Precondition: The EDK must be a KMS edk. */ diff --git a/modules/kms-keyring/src/kms_keyring.ts b/modules/kms-keyring/src/kms_keyring.ts index 8b07edc2d..ae0223bb9 100644 --- a/modules/kms-keyring/src/kms_keyring.ts +++ b/modules/kms-keyring/src/kms_keyring.ts @@ -24,7 +24,6 @@ import { EncryptionMaterial, // eslint-disable-line no-unused-vars DecryptionMaterial, // eslint-disable-line no-unused-vars SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars KeyringTrace, // eslint-disable-line no-unused-vars KeyringTraceFlag, EncryptedDataKey, // eslint-disable-line no-unused-vars @@ -48,8 +47,8 @@ export interface KeyRing grantTokens?: string[] isDiscovery: boolean - _onEncrypt(material: EncryptionMaterial, context?: EncryptionContext): Promise> - _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext): Promise> + _onEncrypt(material: EncryptionMaterial): Promise> + _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> } export interface KmsKeyRingConstructible { @@ -92,14 +91,20 @@ export function KmsKeyringClass, context?: EncryptionContext) { + async _onEncrypt (material: EncryptionMaterial) { /* Check for early return (Postcondition): Discovery Keyrings do not encrypt. */ if (this.isDiscovery) return material const keyIds = this.keyIds.slice() const { clientProvider, generatorKeyId, grantTokens } = this if (generatorKeyId && !material.hasUnencryptedDataKey) { - const dataKey = await generateDataKey(clientProvider, material.suite.keyLengthBytes, generatorKeyId, context, grantTokens) + const dataKey = await generateDataKey( + clientProvider, + material.suite.keyLengthBytes, + generatorKeyId, + material.encryptionContext, + grantTokens + ) /* Precondition: A generatorKeyId must generate if we do not have an unencrypted data key. * Client supplier is allowed to return undefined if, for example, user wants to exclude particular * regions. But if we are here it means that user configured keyring with a KMS key that was @@ -130,7 +135,13 @@ export function KmsKeyringClass, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext) { + async _onDecrypt (material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]) { const keyIds = this.keyIds.slice() const { clientProvider, generatorKeyId, grantTokens } = this if (generatorKeyId) keyIds.unshift(generatorKeyId) @@ -162,7 +173,12 @@ export function KmsKeyringClass { return { decrypt } function decrypt ({ CiphertextBlob, EncryptionContext, GrantTokens }: any) { - expect(EncryptionContext === context).to.equal(true) + expect(EncryptionContext).to.deep.equal(context) expect(GrantTokens).to.equal(grantTokens) return { Plaintext: new Uint8Array(suite.keyLengthBytes), @@ -70,9 +70,8 @@ describe('KmsKeyring: _onDecrypt', }) const material = await testKeyring.onDecrypt( - new NodeDecryptionMaterial(suite), - [edk], - context + new NodeDecryptionMaterial(suite, context), + [edk] ) expect(material.hasUnencryptedDataKey).to.equal(true) @@ -95,7 +94,7 @@ describe('KmsKeyring: _onDecrypt', const clientProvider: any = () => { return { decrypt } function decrypt ({ CiphertextBlob, EncryptionContext, GrantTokens }: any) { - expect(EncryptionContext === context).to.equal(true) + expect(EncryptionContext).to.deep.equal(context) expect(GrantTokens).to.equal(grantTokens) return { Plaintext: new Uint8Array(suite.keyLengthBytes), @@ -118,9 +117,8 @@ describe('KmsKeyring: _onDecrypt', }) const material = await testKeyring.onDecrypt( - new NodeDecryptionMaterial(suite), - [edk], - context + new NodeDecryptionMaterial(suite, context), + [edk] ) expect(material.hasUnencryptedDataKey).to.equal(true) @@ -161,9 +159,8 @@ describe('KmsKeyring: _onDecrypt', }) const material = await testKeyring.onDecrypt( - new NodeDecryptionMaterial(suite), - [edk], - context + new NodeDecryptionMaterial(suite, context), + [edk] ) expect(material.hasUnencryptedDataKey).to.equal(false) diff --git a/modules/kms-keyring/test/kms_keyring.onencrypt.test.ts b/modules/kms-keyring/test/kms_keyring.onencrypt.test.ts index 752d1c722..202fbf460 100644 --- a/modules/kms-keyring/test/kms_keyring.onencrypt.test.ts +++ b/modules/kms-keyring/test/kms_keyring.onencrypt.test.ts @@ -44,7 +44,7 @@ describe('KmsKeyring: _onEncrypt', () => { const clientProvider: any = () => { return { generateDataKey, encrypt } function generateDataKey ({ KeyId, EncryptionContext, GrantTokens }: any) { - expect(EncryptionContext === context).to.equal(true) + expect(EncryptionContext).to.deep.equal(context) expect(GrantTokens).to.equal(grantTokens) return { Plaintext: new Uint8Array(suite.keyLengthBytes), @@ -53,7 +53,7 @@ describe('KmsKeyring: _onEncrypt', () => { } } function encrypt ({ KeyId, EncryptionContext, GrantTokens }: any) { - expect(EncryptionContext === context).to.equal(true) + expect(EncryptionContext).to.deep.equal(context) expect(GrantTokens).to.equal(grantTokens) return { KeyId, @@ -70,7 +70,7 @@ describe('KmsKeyring: _onEncrypt', () => { grantTokens }) - const material = await testKeyring.onEncrypt(new NodeEncryptionMaterial(suite), context) + const material = await testKeyring.onEncrypt(new NodeEncryptionMaterial(suite, context)) expect(material.hasUnencryptedDataKey).to.equal(true) @@ -114,7 +114,7 @@ describe('KmsKeyring: _onEncrypt', () => { grantTokens }) - await expect(testKeyring.onEncrypt(new NodeEncryptionMaterial(suite), context)) + await expect(testKeyring.onEncrypt(new NodeEncryptionMaterial(suite, context))) .to.rejectedWith(Error) }) @@ -136,7 +136,7 @@ describe('KmsKeyring: _onEncrypt', () => { grantTokens }) - await expect(testKeyring.onEncrypt(new NodeEncryptionMaterial(suite), context)) + await expect(testKeyring.onEncrypt(new NodeEncryptionMaterial(suite, context))) .to.rejectedWith(Error) }) @@ -160,7 +160,7 @@ describe('KmsKeyring: _onEncrypt', () => { generatorKeyId }) - const seedMaterial = new NodeEncryptionMaterial(suite) + const seedMaterial = new NodeEncryptionMaterial(suite, {}) .setUnencryptedDataKey(new Uint8Array(suite.keyLengthBytes), { keyName: 'keyName', keyNamespace: 'keyNamespace', @@ -197,7 +197,7 @@ describe('KmsKeyring: _onEncrypt', () => { generatorKeyId }) - const seedMaterial = new NodeEncryptionMaterial(suite) + const seedMaterial = new NodeEncryptionMaterial(suite, {}) .setUnencryptedDataKey(new Uint8Array(suite.keyLengthBytes), { keyName: 'keyName', keyNamespace: 'keyNamespace', 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 9e442e3a8..72f7cdfd5 100644 --- a/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts +++ b/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts @@ -16,7 +16,7 @@ import { WebCryptoMaterialsManager, EncryptionRequest, // eslint-disable-line no-unused-vars DecryptionRequest, EncryptionContext, // eslint-disable-line no-unused-vars - EncryptionResponse, DecryptionResponse, // eslint-disable-line no-unused-vars + EncryptionMaterial, DecryptionMaterial, // eslint-disable-line no-unused-vars WebCryptoAlgorithmSuite, WebCryptoEncryptionMaterial, WebCryptoDecryptionMaterial, SignatureKey, needs, readOnlyProperty, VerificationKey, AlgorithmSuiteIdentifier, immutableBaseClass, @@ -29,8 +29,8 @@ import { fromBase64, toBase64 } from '@aws-sdk/util-base64-browser' export type WebCryptoEncryptionRequest = EncryptionRequest export type WebCryptoDecryptionRequest = DecryptionRequest -export type WebCryptoEncryptionResponse = EncryptionResponse -export type WebCryptoDecryptionResponse = DecryptionResponse +export type WebCryptoEncryptionMaterial = EncryptionMaterial +export type WebCryptoDecryptionMaterial = DecryptionMaterial export type WebCryptoGetEncryptionMaterials = GetEncryptionMaterials export type WebCryptoGetDecryptMaterials = GetDecryptMaterials @@ -46,13 +46,12 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM needs(keyring instanceof KeyringWebCrypto, 'Unsupported type.') readOnlyProperty(this, 'keyring', keyring) } - async getEncryptionMaterials ({ suite, encryptionContext }: WebCryptoEncryptionRequest): Promise { + async getEncryptionMaterials ({ suite, encryptionContext }: WebCryptoEncryptionRequest): Promise { suite = suite || new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) - const material = new WebCryptoEncryptionMaterial(suite) - const context = await this._generateSigningKeyAndUpdateEncryptionContext(material, encryptionContext) - - await this.keyring.onEncrypt(material, context) + const material = await this + .keyring + .onEncrypt(await this._initializeEncryptionMaterial(suite, encryptionContext)) /* Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey. */ needs(material.hasValidKey(), 'Unencrypted data key is invalid.') @@ -60,25 +59,25 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM /* Postcondition: The WebCryptoEncryptionMaterial must contain at least 1 EncryptedDataKey. */ needs(material.encryptedDataKeys.length, 'No EncryptedDataKeys: the ciphertext can never be decrypted.') - return { material, context } + return material } - async decryptMaterials ({ suite, encryptedDataKeys, encryptionContext }: WebCryptoDecryptionRequest): Promise { - const material = await this._loadVerificationKeyFromEncryptionContext(new WebCryptoDecryptionMaterial(suite), encryptionContext) - - await this.keyring.onDecrypt(material, encryptedDataKeys.slice(), encryptionContext) + 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. */ needs(material.hasValidKey(), 'Unencrypted data key is invalid.') - return { material, context: encryptionContext || {} } + return material } - async _generateSigningKeyAndUpdateEncryptionContext (material: WebCryptoEncryptionMaterial, context?: EncryptionContext) { - const { signatureCurve: namedCurve } = material.suite + async _initializeEncryptionMaterial (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + const { signatureCurve: namedCurve } = suite - /* Precondition: The algorithm suite specification must support a signatureCurve to generate a signing key. */ - if (!namedCurve) return { ...context } + /* Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to generate a signing key. */ + if (!namedCurve) return new WebCryptoEncryptionMaterial(suite, encryptionContext) const backend = await getWebCryptoBackend() const subtle = getNonZeroByteBackend(backend) @@ -90,22 +89,25 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM const { publicKey, privateKey } = await subtle.generateKey(webCryptoAlgorithm, extractable, usages) const publicKeyBytes = await subtle.exportKey(format, publicKey) - const compressPoint = SignatureKey.encodeCompressPoint(new Uint8Array(publicKeyBytes), material.suite) - const signatureKey = new SignatureKey(privateKey, compressPoint, material.suite) - material.setSignatureKey(signatureKey) - return { ...context, [ENCODED_SIGNER_KEY]: toBase64(compressPoint) } + 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) } + ) + .setSignatureKey(signatureKey) } - async _loadVerificationKeyFromEncryptionContext (material: WebCryptoDecryptionMaterial, context?: EncryptionContext) { - const { signatureCurve: namedCurve } = material.suite + async _initializeDecryptionMaterial (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { + const { signatureCurve: namedCurve } = suite - /* Precondition: The algorithm suite specification must support a signatureCurve to extract a verification key. */ - if (!namedCurve) return material + /* Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to extract a verification key. */ + if (!namedCurve) return new WebCryptoDecryptionMaterial(suite, encryptionContext) /* Precondition: WebCryptoDefaultCryptographicMaterialsManager If the algorithm suite specification requires a signatureCurve a context must exist. */ - if (!context) throw new Error('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 } = context + const { [ENCODED_SIGNER_KEY]: compressPoint } = encryptionContext /* Precondition: WebCryptoDefaultCryptographicMaterialsManager The context must contain the public key. */ needs(compressPoint, 'Context does not contain required public key.') @@ -117,10 +119,11 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM const usages = ['verify'] const format = 'raw' - const publicKeyBytes = VerificationKey.decodeCompressPoint(fromBase64(compressPoint), material.suite) + const publicKeyBytes = VerificationKey.decodeCompressPoint(fromBase64(compressPoint), suite) const publicKey = await subtle.importKey(format, publicKeyBytes, webCryptoAlgorithm, extractable, usages) - return material.setVerificationKey(new VerificationKey(publicKey, material.suite)) + return new WebCryptoDecryptionMaterial(suite, encryptionContext) + .setVerificationKey(new VerificationKey(publicKey, suite)) } } diff --git a/modules/material-management-browser/test/browser_cryptographic_materials_manager.test.ts b/modules/material-management-browser/test/browser_cryptographic_materials_manager.test.ts index 8a1b2fe5f..d450f557b 100644 --- a/modules/material-management-browser/test/browser_cryptographic_materials_manager.test.ts +++ b/modules/material-management-browser/test/browser_cryptographic_materials_manager.test.ts @@ -70,21 +70,21 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { } const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoEncryptionMaterial(suite) const keyring = new TestKeyring() const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) - const test = await cmm._generateSigningKeyAndUpdateEncryptionContext(material, { some: 'context' }) - expect(Object.keys(test)).lengthOf(2) + const test = await cmm._initializeEncryptionMaterial(suite, { some: 'context' }) - const { signatureKey } = material + expect(test).to.be.instanceOf(WebCryptoEncryptionMaterial) + const { signatureKey, encryptionContext } = test if (!signatureKey) throw new Error('I should never see this error') - expect(test).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(signatureKey.compressPoint)) - expect(test).to.have.haveOwnProperty('some').and.to.equal('context') + expect(Object.keys(encryptionContext)).lengthOf(2) + expect(encryptionContext).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(signatureKey.compressPoint)) + expect(encryptionContext).to.have.haveOwnProperty('some').and.to.equal('context') }) - it('Precondition: The algorithm suite specification must support a signatureCurve to generate a signing key.', async () => { + it('Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to generate a signing key.', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (): Promise { throw new Error('I should never see this error') @@ -95,16 +95,15 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { } const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) const keyring = new TestKeyring() const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) - const test = await cmm._generateSigningKeyAndUpdateEncryptionContext(material, { some: 'context' }) - expect(Object.keys(test)).lengthOf(1) - expect(test).to.have.haveOwnProperty('some').and.to.equal('context') + const { encryptionContext } = await cmm._initializeEncryptionMaterial(suite, { some: 'context' }) + expect(Object.keys(encryptionContext)).lengthOf(1) + expect(encryptionContext).to.have.haveOwnProperty('some').and.to.equal('context') }) - it('set a verificationKey from context', async () => { + it('set a verificationKey from encryption context', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (): Promise { throw new Error('I should never see this error') @@ -119,13 +118,14 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) const context = { some: 'context', [ENCODED_SIGNER_KEY]: 'A29gmBT/NscB90u6npOulZQwAAiKVtoShudOm2J2sCgC' } - const test = await cmm._loadVerificationKeyFromEncryptionContext(new WebCryptoDecryptionMaterial(suite), context) + const test = await cmm._initializeDecryptionMaterial(suite, context) + expect(test).to.be.instanceOf(WebCryptoDecryptionMaterial) const { verificationKey } = test if (!verificationKey) throw new Error('I should never see this error') expect(verificationKey.signatureCurve).to.equal(suite.signatureCurve) }) - it('Precondition: The algorithm suite specification must support a signatureCurve to extract a verification key.', async () => { + it('Check for early return (Postcondition): The WebCryptoAlgorithmSuite specification must support a signatureCurve to extract a verification key.', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (): Promise { throw new Error('I should never see this error') @@ -140,7 +140,7 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) const context = { some: 'context' } - const test = await cmm._loadVerificationKeyFromEncryptionContext(new WebCryptoDecryptionMaterial(suite), context) + const test = await cmm._initializeDecryptionMaterial(suite, context) expect(test.verificationKey).to.equal(undefined) }) @@ -158,7 +158,7 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { const keyring = new TestKeyring() const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) - expect(cmm._loadVerificationKeyFromEncryptionContext(new WebCryptoDecryptionMaterial(suite), {})).to.rejectedWith(Error) + expect(cmm._initializeDecryptionMaterial(suite, {})).to.rejectedWith(Error) }) it('Precondition: WebCryptoDefaultCryptographicMaterialsManager The context must contain the public key.', async () => { @@ -176,10 +176,10 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) const context = { missing: 'signer key' } - expect(cmm._loadVerificationKeyFromEncryptionContext(new WebCryptoDecryptionMaterial(suite), context)).to.rejectedWith(Error) + expect(cmm._initializeDecryptionMaterial(suite, context)).to.rejectedWith(Error) }) - it('can return an encryption response', async () => { + it('can return a encryption material', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (material: WebCryptoEncryptionMaterial): Promise { const udk = synchronousRandomValues(suite.keyLengthBytes) @@ -203,11 +203,11 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { some: 'context' } - const test = await cmm.getEncryptionMaterials({ suite, encryptionContext }) - expect(Object.keys(test.context)).lengthOf(2) - if (!test.material.signatureKey) throw new Error('I should never see this error') - expect(test.context).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(test.material.signatureKey.compressPoint)) - expect(test.context).to.have.haveOwnProperty('some').and.to.equal('context') + const material = await cmm.getEncryptionMaterials({ suite, encryptionContext }) + expect(Object.keys(material.encryptionContext)).lengthOf(2) + if (!material.signatureKey) throw new Error('I should never see this error') + expect(material.encryptionContext).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(material.signatureKey.compressPoint)) + expect(material.encryptionContext).to.have.haveOwnProperty('some').and.to.equal('context') }) it('will pick a default Algorithm Suite', async () => { @@ -233,11 +233,11 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { some: 'context' } - const test = await cmm.getEncryptionMaterials({ encryptionContext }) - expect(Object.keys(test.context)).lengthOf(2) - if (!test.material.signatureKey) throw new Error('I should never see this error') - expect(test.context).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(test.material.signatureKey.compressPoint)) - expect(test.context).to.have.haveOwnProperty('some').and.to.equal('context') + const material = await cmm.getEncryptionMaterials({ encryptionContext }) + expect(Object.keys(material.encryptionContext)).lengthOf(2) + if (!material.signatureKey) throw new Error('I should never see this error') + expect(material.encryptionContext).to.have.haveOwnProperty(ENCODED_SIGNER_KEY).and.to.equal(toBase64(material.signatureKey.compressPoint)) + expect(material.encryptionContext).to.have.haveOwnProperty('some').and.to.equal('context') }) it('Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey.', async () => { @@ -288,7 +288,7 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { expect(cmm.getEncryptionMaterials({ encryptionContext })).to.rejectedWith(Error) }) - it('can return a decryption response', async () => { + it('can return decryption material', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (): Promise { throw new Error('I should never see this error') @@ -309,10 +309,10 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { const encryptionContext = { some: 'context', [ENCODED_SIGNER_KEY]: 'A29gmBT/NscB90u6npOulZQwAAiKVtoShudOm2J2sCgC' } const edk = new EncryptedDataKey({ providerId: ' keyNamespace', providerInfo: 'keyName', encryptedDataKey: new Uint8Array(5) }) - const test = await cmm.decryptMaterials({ suite, encryptionContext, encryptedDataKeys: [edk] }) - if (!test.material.verificationKey) throw new Error('I should never see this error') - expect(test.context).to.deep.equal(encryptionContext) - expect(test.material.verificationKey.signatureCurve).to.equal(suite.signatureCurve) + const material = await cmm.decryptMaterials({ suite, encryptionContext, encryptedDataKeys: [edk] }) + if (!material.verificationKey) throw new Error('I should never see this error') + expect(material.encryptionContext).to.deep.equal(encryptionContext) + expect(material.verificationKey.signatureCurve).to.equal(suite.signatureCurve) }) it('Postcondition: The WebCryptoDecryptionMaterial must contain a valid dataKey.', async () => { diff --git a/modules/material-management-browser/test/keyring_helpers.test.ts b/modules/material-management-browser/test/keyring_helpers.test.ts index 8a882432b..2d9689861 100644 --- a/modules/material-management-browser/test/keyring_helpers.test.ts +++ b/modules/material-management-browser/test/keyring_helpers.test.ts @@ -90,7 +90,7 @@ describe('importForWebCryptoDecryptionMaterial', () => { it('Check for early return (Postcondition): If no key was able to be decrypted, return.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) await importForWebCryptoDecryptionMaterial(material) expect(material.hasCryptoKey).to.equal(false) @@ -99,7 +99,7 @@ describe('importForWebCryptoDecryptionMaterial', () => { function getWebCryptoDecryptionMaterial () { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } return material.setUnencryptedDataKey(udk, trace) @@ -107,7 +107,7 @@ function getWebCryptoDecryptionMaterial () { function getWebCryptoEncryptionMaterial () { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } return material.setUnencryptedDataKey(udk, trace) diff --git a/modules/material-management-browser/test/material_helpers.test.ts b/modules/material-management-browser/test/material_helpers.test.ts index 741ed3650..69c5c571c 100644 --- a/modules/material-management-browser/test/material_helpers.test.ts +++ b/modules/material-management-browser/test/material_helpers.test.ts @@ -46,7 +46,7 @@ declare const CryptoKey: CryptoKey describe('_importCryptoKey', () => { it('can import WebCryptoEncryptionMaterial with a algorithm suite without a KDF', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -61,7 +61,7 @@ describe('_importCryptoKey', () => { it('can import WebCryptoEncryptionMaterial with a algorithm suite with a KDF', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -75,7 +75,7 @@ describe('_importCryptoKey', () => { it('can import WebCryptoDecryptionMaterial with a algorithm suite without a KDF', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -89,7 +89,7 @@ describe('_importCryptoKey', () => { it('can import WebCryptoDecryptionMaterial with a algorithm suite with a KDF', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -105,7 +105,7 @@ describe('_importCryptoKey', () => { describe('importCryptoKey', () => { it('can import when backend is isFullSupportWebCryptoBackend', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -118,7 +118,7 @@ describe('importCryptoKey', () => { it('can import when backend is mixed support', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -145,7 +145,7 @@ describe('importCryptoKey', () => { describe('WebCryptoKdf', () => { it('returns a valid kdf key', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -162,7 +162,7 @@ describe('WebCryptoKdf', () => { it('Check for early return (Postcondition): No WebCrypto KDF, just return the unencrypted data key.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -179,7 +179,7 @@ describe('WebCryptoKdf', () => { it('Precondition: Valid HKDF values must exist for browsers.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -192,7 +192,7 @@ describe('WebCryptoKdf', () => { it('Postcondition: The derived key must conform to the algorith suite specification.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -213,7 +213,7 @@ describe('WebCryptoKdf', () => { describe('getSubtleFunction', () => { it('can get encrypt', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -236,7 +236,7 @@ describe('getSubtleFunction', () => { it('Precondition: The material must have a CryptoKey.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -247,7 +247,7 @@ describe('getSubtleFunction', () => { it('Precondition: The cryptoKey and backend must match in terms of Mixed vs Full support.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -270,7 +270,7 @@ describe('getSubtleFunction', () => { it('Precondition: The length of the IV must match the algorithm suite specification.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const trace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(udk, trace) @@ -290,8 +290,8 @@ describe('getSubtleFunction', () => { it('no kdf, simple backend, can encrypt/decrypt', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } const decryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } @@ -317,8 +317,8 @@ describe('getSubtleFunction', () => { it('KDF, simple backend, can encrypt/decrypt', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } const decryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } @@ -344,8 +344,8 @@ describe('getSubtleFunction', () => { it('no kdf, mixed backend, can encrypt/decrypt', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } const decryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } @@ -385,8 +385,8 @@ describe('getSubtleFunction', () => { it('kdf, mixed backend, can encrypt/decrypt', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } const decryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } @@ -431,7 +431,7 @@ describe('getSubtleFunction', () => { describe('getEncryptHelper/getDecryptionHelper', () => { it('encryption helpers without a signature', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } encryptionMaterial.setUnencryptedDataKey(udk, encryptTrace) @@ -449,7 +449,7 @@ describe('getEncryptHelper/getDecryptionHelper', () => { it('decryption helpers without a signature ', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } decryptionMaterial.setUnencryptedDataKey(udk, encryptTrace) @@ -467,7 +467,7 @@ describe('getEncryptHelper/getDecryptionHelper', () => { it('encryption helpers with a signature', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const { signatureKey } = await sigKeys(suite) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } @@ -488,7 +488,7 @@ describe('getEncryptHelper/getDecryptionHelper', () => { it('decryption helpers with a signature ', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const { verificationKey } = await sigKeys(suite) const decryptionTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } @@ -509,22 +509,22 @@ describe('getEncryptHelper/getDecryptionHelper', () => { it('Precondition: WebCryptoEncryptionMaterial must have a valid data key.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) expect(getEncryptHelper(encryptionMaterial)).to.rejectedWith(Error) }) it('Precondition: WebCryptoDecryptionMaterial must have a valid data key.', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) expect(getDecryptionHelper(decryptionMaterial)).to.rejectedWith(Error) }) it('can verify what was signed', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const decryptionMaterial = new WebCryptoDecryptionMaterial(suite) - const encryptionMaterial = new WebCryptoEncryptionMaterial(suite) + const decryptionMaterial = new WebCryptoDecryptionMaterial(suite, {}) + const encryptionMaterial = new WebCryptoEncryptionMaterial(suite, {}) const udk = synchronousRandomValues(suite.keyLengthBytes) const { signatureKey, verificationKey } = await sigKeys(suite) const encryptTrace = { keyName: 'keyName', keyNamespace: 'keyNamespace', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } 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 ede548b2f..bc9bbe384 100644 --- a/modules/material-management-node/src/node_cryptographic_materials_manager.ts +++ b/modules/material-management-node/src/node_cryptographic_materials_manager.ts @@ -15,7 +15,7 @@ import { NodeMaterialsManager, EncryptionRequest, DecryptionRequest, EncryptionContext, // eslint-disable-line no-unused-vars - EncryptionResponse, DecryptionResponse, // eslint-disable-line no-unused-vars + EncryptionMaterial, DecryptionMaterial, // eslint-disable-line no-unused-vars NodeAlgorithmSuite, NodeEncryptionMaterial, NodeDecryptionMaterial, SignatureKey, needs, VerificationKey, AlgorithmSuiteIdentifier, immutableClass, readOnlyProperty, KeyringNode, @@ -28,8 +28,8 @@ import { createECDH } from 'crypto' export type NodeEncryptionRequest = EncryptionRequest export type NodeDecryptionRequest = DecryptionRequest -export type NodeEncryptionResponse = EncryptionResponse -export type NodeDecryptionResponse = DecryptionResponse +export type NodeEncryptionMaterial = EncryptionMaterial +export type NodeDecryptionMaterial = DecryptionMaterial export type NodeGetEncryptionMaterials = GetEncryptionMaterials export type NodeGetDecryptMaterials = GetDecryptMaterials @@ -46,13 +46,12 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa readOnlyProperty(this, 'keyring', keyring) } - async getEncryptionMaterials ({ suite, encryptionContext }: NodeEncryptionRequest): Promise { + async getEncryptionMaterials ({ suite, encryptionContext }: NodeEncryptionRequest): Promise { suite = suite || new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) - const material = new NodeEncryptionMaterial(suite) - const context = await this._generateSigningKeyAndUpdateEncryptionContext(material, encryptionContext) - - await this.keyring.onEncrypt(material, context) + const material = await this + .keyring + .onEncrypt(this._initializeEncryptionMaterial(suite, encryptionContext)) /* Postcondition: The NodeEncryptionMaterial must contain a valid dataKey. */ needs(material.getUnencryptedDataKey(), 'Unencrypted data key is invalid.') @@ -60,13 +59,13 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa /* Postcondition: The NodeEncryptionMaterial must contain at least 1 EncryptedDataKey. */ needs(material.encryptedDataKeys.length, 'No EncryptedDataKeys: the ciphertext can never be decrypted.') - return { material, context } + return material } - async decryptMaterials ({ suite, encryptedDataKeys, encryptionContext }: NodeDecryptionRequest): Promise { - const material = await this._loadVerificationKeyFromEncryptionContext(new NodeDecryptionMaterial(suite), encryptionContext) - - await this.keyring.onDecrypt(material, encryptedDataKeys.slice(), encryptionContext) + 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 @@ -74,47 +73,50 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa */ needs(material.getUnencryptedDataKey(), 'Unencrypted data key is invalid.') - return { material, context: encryptionContext || {} } + return material } - async _generateSigningKeyAndUpdateEncryptionContext (material: NodeEncryptionMaterial, context?: EncryptionContext) { - const { signatureCurve: namedCurve } = material.suite + _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. */ - if (!namedCurve) return Object.freeze({ ...context }) + if (!namedCurve) return new NodeEncryptionMaterial(suite, encryptionContext) const ecdh = createECDH(namedCurve) ecdh.generateKeys() // @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), material.suite) - - material.setSignatureKey(signatureKey) - - return Object.freeze({ - ...context, - [ENCODED_SIGNER_KEY]: compressPoint.toString('base64') - }) + const signatureKey = new SignatureKey(privateKey, new Uint8Array(compressPoint), suite) + + return new NodeEncryptionMaterial( + suite, + { + ...encryptionContext, + [ENCODED_SIGNER_KEY]: compressPoint.toString('base64') + } + ) + .setSignatureKey(signatureKey) } - async _loadVerificationKeyFromEncryptionContext (material: NodeDecryptionMaterial, context?: EncryptionContext) { - const { signatureCurve: namedCurve } = material.suite + _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 material + if (!namedCurve) return new NodeDecryptionMaterial(suite, encryptionContext) /* Precondition: NodeDefaultCryptographicMaterialsManager If the algorithm suite specification requires a signatureCurve a context must exist. */ - if (!context) throw new Error('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 } = context + 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'), material.suite) + const publicKeyBytes = VerificationKey.decodeCompressPoint(Buffer.from(compressPoint, 'base64'), suite) - return material.setVerificationKey(new VerificationKey(publicKeyBytes, material.suite)) + return new NodeDecryptionMaterial(suite, encryptionContext) + .setVerificationKey(new VerificationKey(publicKeyBytes, suite)) } } immutableClass(NodeDefaultCryptographicMaterialsManager) diff --git a/modules/material-management-node/test/material_helpers.test.ts b/modules/material-management-node/test/material_helpers.test.ts index 4d944d699..d165ce1ce 100644 --- a/modules/material-management-node/test/material_helpers.test.ts +++ b/modules/material-management-node/test/material_helpers.test.ts @@ -25,7 +25,7 @@ import { Decipheriv, Cipheriv, createECDH } from 'crypto' describe('nodeKdf', () => { it('Check for early return (Postcondition): No Node.js KDF, just return the unencrypted data key.', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -36,7 +36,7 @@ describe('nodeKdf', () => { it('HKDF SHA256', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -48,7 +48,7 @@ describe('nodeKdf', () => { it('HKDF SHA384', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -88,7 +88,7 @@ describe('nodeKdf', () => { describe('getCryptoStream', () => { it('return a Cipheriv', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -101,7 +101,7 @@ describe('getCryptoStream', () => { it('return a Decipheriv', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -114,7 +114,7 @@ describe('getCryptoStream', () => { it('Precondition: The length of the IV must match the algorithm suite specification.', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -126,7 +126,7 @@ describe('getCryptoStream', () => { it('Precondition: The material must have not been zeroed.', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -141,7 +141,7 @@ describe('getCryptoStream', () => { describe('getEncryptHelper', () => { it('basic shape', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -161,7 +161,7 @@ describe('getEncryptHelper', () => { it('signer', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY } @@ -192,7 +192,7 @@ describe('getEncryptHelper', () => { describe('getDecryptionHelper', () => { it('first test', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } material.setUnencryptedDataKey(dataKey, trace) @@ -212,7 +212,7 @@ describe('getDecryptionHelper', () => { it('verify', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const dataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } diff --git a/modules/material-management-node/test/node_cryptographic_materials_manager.test.ts b/modules/material-management-node/test/node_cryptographic_materials_manager.test.ts index 711eee7a2..172f2f79d 100644 --- a/modules/material-management-node/test/node_cryptographic_materials_manager.test.ts +++ b/modules/material-management-node/test/node_cryptographic_materials_manager.test.ts @@ -57,7 +57,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { expect(() => new NodeDefaultCryptographicMaterialsManager({} as any)).to.throw() }) - it('should create a signature key and append it to context', async () => { + it('should create signature key and append the verification key to context and return NodeEncryptionMaterial', async () => { class TestKeyring extends KeyringNode { async _onEncrypt (): Promise { throw new Error('never') @@ -70,14 +70,15 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new NodeEncryptionMaterial(suite) const context = { some: 'context' } - const test = await cmm._generateSigningKeyAndUpdateEncryptionContext(material, context) - expect(Object.keys(test)).lengthOf(2) - expect(Object.isFrozen(test)).to.equal(true) + const test = cmm._initializeEncryptionMaterial(suite, context) + expect(test).to.be.instanceOf(NodeEncryptionMaterial) + expect(test.suite).to.equal(suite) + expect(Object.keys(test.encryptionContext)).lengthOf(2) + expect(Object.isFrozen(test.encryptionContext)).to.equal(true) expect(Object.isFrozen(context)).to.equal(false) - expect(test).to.have.ownProperty('some').and.to.equal('context') - expect(test).to.have.ownProperty(ENCODED_SIGNER_KEY) + expect(test.encryptionContext).to.have.ownProperty('some').and.to.equal('context') + expect(test.encryptionContext).to.have.ownProperty(ENCODED_SIGNER_KEY) }) it('Check for early return (Postcondition): The algorithm suite specification must support a signatureCurve to generate a ECDH key.', async () => { @@ -93,13 +94,14 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) const context = { some: 'context' } - const test = await cmm._generateSigningKeyAndUpdateEncryptionContext(material, context) - expect(Object.keys(test)).lengthOf(1) - expect(Object.isFrozen(test)).to.equal(true) + const test = cmm._initializeEncryptionMaterial(suite, context) + expect(test).to.be.instanceOf(NodeEncryptionMaterial) + expect(test.suite).to.equal(suite) + expect(Object.keys(test.encryptionContext)).lengthOf(1) + expect(Object.isFrozen(test.encryptionContext)).to.equal(true) expect(Object.isFrozen(context)).to.equal(false) - expect(test).to.have.ownProperty('some').and.to.equal('context') + expect(test.encryptionContext).to.have.ownProperty('some').and.to.equal('context') }) it('Set the verification key.', async () => { @@ -116,14 +118,14 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const context = await cmm._generateSigningKeyAndUpdateEncryptionContext( - new NodeEncryptionMaterial(suite), + const { encryptionContext } = cmm._initializeEncryptionMaterial( + suite, { some: 'context' } ) - const material = await cmm._loadVerificationKeyFromEncryptionContext( - new NodeDecryptionMaterial(suite), - context + const material = cmm._initializeDecryptionMaterial( + suite, + encryptionContext ) expect(material.verificationKey).to.have.ownProperty('publicKey') }) @@ -141,10 +143,9 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) const context = { some: 'context' } - const test = await cmm._loadVerificationKeyFromEncryptionContext(material, context) - expect(test === material).to.equal(true) + const { encryptionContext } = cmm._initializeDecryptionMaterial(suite, context) + expect(encryptionContext).to.deep.equal(context) }) it('Precondition: NodeDefaultCryptographicMaterialsManager If the algorithm suite specification requires a signatureCurve a context must exist.', async () => { @@ -160,9 +161,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - await expect(cmm._loadVerificationKeyFromEncryptionContext( - new NodeDecryptionMaterial(suite) - )).to.rejectedWith(Error) + expect(() => cmm._initializeDecryptionMaterial(suite, undefined as any)).to.throw() }) it('Precondition: NodeDefaultCryptographicMaterialsManager The context must contain the public key.', async () => { @@ -178,10 +177,10 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - await expect(cmm._loadVerificationKeyFromEncryptionContext( - new NodeDecryptionMaterial(suite), + expect(() => cmm._initializeDecryptionMaterial( + suite, { no: 'signature' } - )).to.rejectedWith(Error) + )).to.throw() }) it('Postcondition: The NodeEncryptionMaterial must contain a valid dataKey.', async () => { @@ -198,7 +197,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const keyring = new TestKeyring() const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) - await expect(cmm.getEncryptionMaterials({ suite })).to.rejectedWith(Error) + await expect(cmm.getEncryptionMaterials({ suite, encryptionContext: {} })).to.rejectedWith(Error) }) it('Postcondition: The NodeEncryptionMaterial must contain at least 1 EncryptedDataKey.', async () => { @@ -217,7 +216,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { const keyring = new TestKeyring() const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) - await expect(cmm.getEncryptionMaterials({ suite })).to.rejectedWith(Error) + await expect(cmm.getEncryptionMaterials({ suite, encryptionContext: {} })).to.rejectedWith(Error) }) it('Postcondition: The NodeDecryptionMaterial must contain a valid dataKey.', async () => { @@ -237,7 +236,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { providerId: 'p', providerInfo: 'p', encryptedDataKey: new Uint8Array(5) })] - await expect(cmm.decryptMaterials({ suite, encryptedDataKeys })).to.rejectedWith(Error) + await expect(cmm.decryptMaterials({ suite, encryptedDataKeys, encryptionContext: {} })).to.rejectedWith(Error) }) it('Return decryption material', async () => { @@ -259,7 +258,7 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { providerId: 'p', providerInfo: 'p', encryptedDataKey: new Uint8Array(5) })] - const { material } = await cmm.decryptMaterials({ suite, encryptedDataKeys }) + const material = await cmm.decryptMaterials({ suite, encryptedDataKeys, encryptionContext: {} }) expect(material.hasUnencryptedDataKey).to.equal(true) }) }) diff --git a/modules/material-management/src/cryptographic_material.ts b/modules/material-management/src/cryptographic_material.ts index 455e8d1a5..3ed6faaa5 100644 --- a/modules/material-management/src/cryptographic_material.ts +++ b/modules/material-management/src/cryptographic_material.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { MixedBackendCryptoKey, SupportedAlgorithmSuites, AwsEsdkJsCryptoKey, AwsEsdkJsKeyUsage } from './types' // eslint-disable-line no-unused-vars +import { MixedBackendCryptoKey, SupportedAlgorithmSuites, AwsEsdkJsCryptoKey, AwsEsdkJsKeyUsage, EncryptionContext } from './types' // eslint-disable-line no-unused-vars import { EncryptedDataKey } from './encrypted_data_key' import { SignatureKey, VerificationKey } from './signature_key' import { frozenClass, readOnlyProperty } from './immutable_class' @@ -22,6 +22,23 @@ import { NodeAlgorithmSuite } from './node_algorithms' import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' import { needs } from './needs' +/* + * This public interface to the CryptographicMaterial object is provided for + * developers of CMMs and keyrings only. If you are a user of the AWS Encryption + * SDK and you are not developing your own CMMs and/or keyrings, you do not + * need to use it and you should not do so. + * + * The CryptographicMaterial's purpose is to bind together all the required elements for + * encrypting or decrypting a payload. + * The functional data key (unencrypted or CryptoKey) is the most sensitive data and needs to + * be protected. The longer this data persists in memory the + * greater the opportunity to be invalidated. Because + * a Caching CMM exists it is important to ensure that the + * unencrypted data key and its meta data can not be manipulated, + * and that the unencrypted data key can be zeroed when + * it is no longer needed. + */ + let timingSafeEqual: (a: Uint8Array, b: Uint8Array) => boolean try { /* It is possible for `require` to return an empty object, or an object @@ -53,23 +70,6 @@ function portableTimingSafeEqual (a: Uint8Array, b: Uint8Array) { return (diff === 0) } -/* - * This public interface to the CryptographicMaterial object is provided for - * developers of CMMs and keyrings only. If you are a user of the AWS Encryption - * SDK and you are not developing your own CMMs and/or keyrings, you do not - * need to use it and you should not do so. - * - * The CryptographicMaterial's purpose is to bind together all the required elements for - * encrypting or decrypting a payload. - * The functional data key (unencrypted or CryptoKey) is the most sensitive data and needs to - * be protected. The longer this data persists in memory the - * greater the opportunity to be invalidated. Because - * a Caching CMM exists is it important to insure that the - * unencrypted data key and it's meta data can not be manipulated, - * and that the unencrypted data key can be zeroed when - * it is no longer needed. - */ - export interface FunctionalCryptographicMaterial { hasValidKey: () => boolean } @@ -82,6 +82,7 @@ export interface CryptographicMaterial> { hasUnencryptedDataKey: boolean unencryptedDataKeyLength: number keyringTrace: KeyringTrace[] + encryptionContext: Readonly } export interface EncryptionMaterial> extends CryptographicMaterial { @@ -117,10 +118,14 @@ export class NodeEncryptionMaterial implements addEncryptedDataKey!: (edk: EncryptedDataKey, flags: KeyringTraceFlag) => NodeEncryptionMaterial setSignatureKey!: (key: SignatureKey) => NodeEncryptionMaterial signatureKey?: SignatureKey - constructor (suite: NodeAlgorithmSuite) { + encryptionContext: Readonly + constructor (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: NodeEncryptionMaterial suite must be 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') + this.encryptionContext = Object.freeze({ ...encryptionContext }) // EncryptionMaterial have generated a data key on setUnencryptedDataKey const setFlags = KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY decorateCryptographicMaterial(this, setFlags) @@ -146,10 +151,14 @@ export class NodeDecryptionMaterial implements keyringTrace: KeyringTrace[] = [] setVerificationKey!: (key: VerificationKey) => NodeDecryptionMaterial verificationKey?: VerificationKey - constructor (suite: NodeAlgorithmSuite) { + encryptionContext: Readonly + constructor (suite: NodeAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: NodeDecryptionMaterial suite must be 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') + this.encryptionContext = Object.freeze({ ...encryptionContext }) // DecryptionMaterial have decrypted a data key on setUnencryptedDataKey const setFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY decorateCryptographicMaterial(this, setFlags) @@ -182,11 +191,15 @@ export class WebCryptoEncryptionMaterial implements getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray - constructor (suite: WebCryptoAlgorithmSuite) { + encryptionContext: Readonly + constructor (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: WebCryptoEncryptionMaterial suite must be WebCryptoAlgorithmSuite. */ needs(suite instanceof WebCryptoAlgorithmSuite, 'Suite must be a WebCryptoAlgorithmSuite') this.suite = suite this.validUsages = Object.freeze(['deriveKey', 'encrypt']) + /* Precondition: WebCryptoEncryptionMaterial encryptionContext must be an object, even if it is empty. */ + 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 decorateCryptographicMaterial(this, setFlag) @@ -218,11 +231,15 @@ export class WebCryptoDecryptionMaterial implements getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray - constructor (suite: WebCryptoAlgorithmSuite) { + encryptionContext: Readonly + constructor (suite: WebCryptoAlgorithmSuite, encryptionContext: EncryptionContext) { /* Precondition: WebCryptoDecryptionMaterial suite must be WebCryptoAlgorithmSuite. */ needs(suite instanceof WebCryptoAlgorithmSuite, 'Suite must be a WebCryptoAlgorithmSuite') this.suite = suite this.validUsages = Object.freeze(['deriveKey', 'decrypt']) + /* Precondition: WebCryptoDecryptionMaterial encryptionContext must be an object, even if it is empty. */ + 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 decorateCryptographicMaterial(this, setFlag) diff --git a/modules/material-management/src/keyring.ts b/modules/material-management/src/keyring.ts index ed54990a2..5e0c13fc0 100644 --- a/modules/material-management/src/keyring.ts +++ b/modules/material-management/src/keyring.ts @@ -17,7 +17,7 @@ import { EncryptedDataKey } from './encrypted_data_key' import { immutableBaseClass, immutableClass } from './immutable_class' import { isEncryptionMaterial, isDecryptionMaterial } from './cryptographic_material' -import { EncryptionContext, EncryptionMaterial, DecryptionMaterial, SupportedAlgorithmSuites } from './types' // eslint-disable-line no-unused-vars +import { EncryptionMaterial, DecryptionMaterial, SupportedAlgorithmSuites } from './types' // eslint-disable-line no-unused-vars 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 @@ -30,9 +30,9 @@ import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disa */ export abstract class Keyring { - async onEncrypt (material: EncryptionMaterial, context?: EncryptionContext): Promise> { + async onEncrypt (material: EncryptionMaterial): Promise> { /* Precondition: material must be a type of isEncryptionMaterial. - * There are several security properties that NodeEncryptionMaterial and WebCryptoEncrypionMaterial + * There are several security properties that NodeEncryptionMaterial and WebCryptoEncryptionMaterial * posses. * The unencryptedDataKey can only be written once. * If a data key has not already been generated, there must be no EDKs. @@ -40,7 +40,7 @@ export abstract class Keyring { */ needs(isEncryptionMaterial(material), 'Unsupported type of material.') - const _material = await this._onEncrypt(material, context) + const _material = await this._onEncrypt(material) /* Postcondition: The EncryptionMaterial objects must be the same. * See cryptographic_materials.ts. The CryptographicMaterial objects @@ -58,7 +58,7 @@ export abstract class Keyring { return material } - abstract _onEncrypt(material: EncryptionMaterial, context?: EncryptionContext): 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. @@ -71,7 +71,7 @@ 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[], context?: EncryptionContext): Promise> { + async onDecrypt (material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> { /* Precondition: material must be DecryptionMaterial. */ needs(isDecryptionMaterial(material), 'Unsupported material type.') @@ -81,7 +81,7 @@ export abstract class Keyring { /* Precondition: encryptedDataKeys must all be EncryptedDataKey. */ needs(encryptedDataKeys.every(edk => edk instanceof EncryptedDataKey), 'Unsupported EncryptedDataKey type') - const _material = await this._onDecrypt(material, encryptedDataKeys, context) + const _material = await this._onDecrypt(material, encryptedDataKeys) /* Postcondition: The DecryptionMaterial objects must be the same. * See cryptographic_materials.ts. The CryptographicMaterial objects @@ -101,7 +101,7 @@ export abstract class Keyring { return material } - abstract _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext): Promise> + abstract _onDecrypt(material: DecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]): Promise> } immutableBaseClass(Keyring) diff --git a/modules/material-management/src/materials_manager.ts b/modules/material-management/src/materials_manager.ts index fc022873f..284715f31 100644 --- a/modules/material-management/src/materials_manager.ts +++ b/modules/material-management/src/materials_manager.ts @@ -14,7 +14,7 @@ */ import { EncryptionRequest, DecryptionRequest } from '.' // eslint-disable-line no-unused-vars -import { EncryptionResponse, DecryptionResponse, SupportedAlgorithmSuites } from './types' // 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 @@ -26,11 +26,11 @@ import { WebCryptoAlgorithmSuite } from './web_crypto_algorithms' // eslint-disa */ export interface GetEncryptionMaterials { - (request: EncryptionRequest): Promise> + (request: EncryptionRequest): Promise> } export interface GetDecryptMaterials { - (request: DecryptionRequest): Promise> + (request: DecryptionRequest): Promise> } export interface MaterialsManager { diff --git a/modules/material-management/src/multi_keyring.ts b/modules/material-management/src/multi_keyring.ts index 86a990533..8baccc484 100644 --- a/modules/material-management/src/multi_keyring.ts +++ b/modules/material-management/src/multi_keyring.ts @@ -19,7 +19,7 @@ import { KeyringNode, KeyringWebCrypto } from './keyring' -import { EncryptionContext, SupportedAlgorithmSuites, EncryptionMaterial, DecryptionMaterial } from './types' // eslint-disable-line no-unused-vars +import { SupportedAlgorithmSuites, EncryptionMaterial, DecryptionMaterial } from './types' // eslint-disable-line no-unused-vars 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 @@ -69,10 +69,10 @@ function decorateProperties ( function buildPrivateOnEncrypt () { return async function _onEncrypt ( this: IMultiKeyring, - material: EncryptionMaterial, context?: EncryptionContext + material: EncryptionMaterial ): Promise> { const generated = this.generator - ? await this.generator.onEncrypt(material, context) + ? await this.generator.onEncrypt(material) : material /* Precondition: A Generator Keyring *must* ensure generated material. */ @@ -87,7 +87,7 @@ function buildPrivateOnEncrypt () { * append based on already appended EDK's. */ for (const keyring of this.children) { - await keyring.onEncrypt(generated, context) + await keyring.onEncrypt(generated) } // Keyrings are required to not create new EncryptionMaterial instances, but @@ -101,8 +101,7 @@ function buildPrivateOnDecrypt () { return async function _onDecrypt ( this: IMultiKeyring, material: DecryptionMaterial, - encryptedDataKeys: EncryptedDataKey[], - context?: EncryptionContext + encryptedDataKeys: EncryptedDataKey[] ): Promise> { const children = this.children.slice() if (this.generator) children.unshift(this.generator) @@ -112,7 +111,7 @@ function buildPrivateOnDecrypt () { if (material.hasValidKey()) return material try { - await keyring.onDecrypt(material, encryptedDataKeys, context) + await keyring.onDecrypt(material, encryptedDataKeys) } catch (e) { // there should be some debug here? or wrap? // Failures onDecrypt should not short-circuit the process diff --git a/modules/material-management/src/types.ts b/modules/material-management/src/types.ts index b44d7cfad..db58db1e8 100644 --- a/modules/material-management/src/types.ts +++ b/modules/material-management/src/types.ts @@ -44,27 +44,17 @@ export type MixedBackendCryptoKey = { export interface EncryptionRequest { readonly suite?: S - readonly encryptionContext?: EncryptionContext + readonly encryptionContext: EncryptionContext readonly frameLength?: number readonly plaintextLength?: number } -export interface EncryptionResponse { - material: EncryptionMaterial, - context: EncryptionContext -} - export interface DecryptionRequest { readonly suite: S - readonly encryptionContext?: EncryptionContext + readonly encryptionContext: EncryptionContext readonly encryptedDataKeys: ReadonlyArray } -export interface DecryptionResponse { - material: DecryptionMaterial, - context: EncryptionContext -} - export type SupportedAlgorithmSuites = NodeAlgorithmSuite|WebCryptoAlgorithmSuite export type EncryptionMaterial = diff --git a/modules/material-management/test/cryptographic_material.test.ts b/modules/material-management/test/cryptographic_material.test.ts index ae94e915b..b2c7ee205 100644 --- a/modules/material-management/test/cryptographic_material.test.ts +++ b/modules/material-management/test/cryptographic_material.test.ts @@ -321,13 +321,13 @@ describe('decorateWebCryptoMaterial:Helpers', () => { describe('subtleFunctionForMaterial', () => { it('WebCryptoDecryptionMaterial is decrypt', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) expect(subtleFunctionForMaterial(material)).to.equal('decrypt') }) it('WebCryptoEncryptionMaterial is encrypt', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) expect(subtleFunctionForMaterial(material)).to.equal('encrypt') }) it('unsupported', () => { @@ -339,25 +339,25 @@ describe('decorateWebCryptoMaterial:Helpers', () => { describe('keyUsageForMaterial', () => { it('ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256 is deriveKey', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) expect(keyUsageForMaterial(material)).to.equal('deriveKey') }) it('ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256 is decrypt', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) expect(keyUsageForMaterial(material)).to.equal('deriveKey') }) it('WebCryptoDecryptionMaterial is decrypt', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) expect(keyUsageForMaterial(material)).to.equal('decrypt') }) it('WebCryptoEncryptionMaterial is encrypt', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) expect(keyUsageForMaterial(material)).to.equal('encrypt') }) @@ -375,7 +375,7 @@ describe('decorateWebCryptoMaterial:Helpers', () => { describe('isValidCryptoKey', () => { it('Suite with KDF is valid for both the derivable key and the derived key', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const keyKdf: any = { type: 'secret', algorithm: { name: suite.kdf }, usages: ['deriveKey'], extractable: false } const deriveKey: any = { type: 'secret', algorithm: { name: suite.encryption, length: suite.keyLength }, usages: ['encrypt'], extractable: false } expect(isValidCryptoKey(keyKdf, material)).to.equal(true) @@ -384,7 +384,7 @@ describe('decorateWebCryptoMaterial:Helpers', () => { it('Suite without the KDF is only derivable with the key', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const keyKdf: any = { type: 'secret', algorithm: { name: suite.kdf }, usages: ['deriveKey'], extractable: false } const key: any = { type: 'secret', algorithm: { name: suite.encryption, length: suite.keyLength }, usages: ['encrypt'], extractable: false } expect(isValidCryptoKey(keyKdf, material)).to.equal(false) @@ -392,28 +392,28 @@ describe('decorateWebCryptoMaterial:Helpers', () => { }) it('only type === secret is valid', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const key: any = { type: 'private', algorithm: { name: suite.encryption, length: suite.keyLength }, usages: ['encrypt'], extractable: false } expect(isValidCryptoKey(key, material)).to.equal(false) }) it('length must match', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const key: any = { type: 'secret', algorithm: { name: suite.encryption, length: suite.keyLength - 1 }, usages: ['encrypt'], extractable: false } expect(isValidCryptoKey(key, material)).to.equal(false) }) it('can not be extractable', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const key: any = { type: 'secret', algorithm: { name: suite.encryption, length: suite.keyLength }, usages: ['encrypt'], extractable: true } expect(isValidCryptoKey(key, material)).to.equal(false) }) it('usage must match', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const key: any = { type: 'secret', algorithm: { name: suite.encryption, length: suite.keyLength }, usages: ['decrypt'], extractable: false } expect(isValidCryptoKey(key, material)).to.equal(false) }) @@ -422,52 +422,68 @@ describe('decorateWebCryptoMaterial:Helpers', () => { describe('NodeEncryptionMaterial', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const test: any = new NodeEncryptionMaterial(suite) + const test: any = new NodeEncryptionMaterial(suite, {}) it('instance is frozen', () => expect(Object.isFrozen(test)).to.equal(true)) it('has a suite', () => expect(test.suite === suite).to.equal(true)) it('class is frozen', () => expect(Object.isFrozen(NodeAlgorithmSuite)).to.equal(true)) it('class prototype is frozen', () => expect(Object.isFrozen(NodeAlgorithmSuite.prototype)).to.equal(true)) it('Precondition: NodeEncryptionMaterial suite must be NodeAlgorithmSuite.', () => { const suite: any = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - expect(() => new NodeEncryptionMaterial(suite)).to.throw() + expect(() => new NodeEncryptionMaterial(suite, {})).to.throw() + }) + it('Precondition: NodeEncryptionMaterial encryptionContext must be an object, even if it is empty.', () => { + expect(() => new NodeEncryptionMaterial(suite, undefined as any)).to.throw() + expect(() => new NodeEncryptionMaterial(suite, true as any)).to.throw() }) }) describe('NodeDecryptionMaterial', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const test: any = new NodeDecryptionMaterial(suite) + const test: any = new NodeDecryptionMaterial(suite, {}) it('instance is frozen', () => expect(Object.isFrozen(test)).to.equal(true)) it('has a suite', () => expect(test.suite === suite).to.equal(true)) it('class is frozen', () => expect(Object.isFrozen(NodeAlgorithmSuite)).to.equal(true)) it('class prototype is frozen', () => expect(Object.isFrozen(NodeAlgorithmSuite.prototype)).to.equal(true)) it('Precondition: NodeDecryptionMaterial suite must be NodeAlgorithmSuite.', () => { const suite: any = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - expect(() => new NodeDecryptionMaterial(suite)).to.throw() + expect(() => new NodeDecryptionMaterial(suite, {})).to.throw() + }) + it('Precondition: NodeDecryptionMaterial encryptionContext must be an object, even if it is empty.', () => { + expect(() => new NodeDecryptionMaterial(suite, undefined as any)).to.throw() + expect(() => new NodeDecryptionMaterial(suite, true as any)).to.throw() }) }) describe('WebCryptoEncryptionMaterial', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const test: any = new WebCryptoEncryptionMaterial(suite) + const test: any = new WebCryptoEncryptionMaterial(suite, {}) it('instance is frozen', () => expect(Object.isFrozen(test)).to.equal(true)) it('has a suite', () => expect(test.suite === suite).to.equal(true)) it('class is frozen', () => expect(Object.isFrozen(WebCryptoAlgorithmSuite)).to.equal(true)) it('class prototype is frozen', () => expect(Object.isFrozen(WebCryptoAlgorithmSuite.prototype)).to.equal(true)) it('Precondition: WebCryptoEncryptionMaterial suite must be WebCryptoAlgorithmSuite.', () => { const suite: any = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - expect(() => new WebCryptoEncryptionMaterial(suite)).to.throw() + expect(() => new WebCryptoEncryptionMaterial(suite, {})).to.throw() + }) + it('Precondition: WebCryptoEncryptionMaterial encryptionContext must be an object, even if it is empty.', () => { + expect(() => new WebCryptoEncryptionMaterial(suite, undefined as any)).to.throw() + expect(() => new WebCryptoEncryptionMaterial(suite, true as any)).to.throw() }) }) describe('WebCryptoDecryptionMaterial', () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const test: any = new WebCryptoDecryptionMaterial(suite) + const test: any = new WebCryptoDecryptionMaterial(suite, {}) it('instance is frozen', () => expect(Object.isFrozen(test)).to.equal(true)) it('has a suite', () => expect(test.suite === suite).to.equal(true)) it('class is frozen', () => expect(Object.isFrozen(WebCryptoAlgorithmSuite)).to.equal(true)) it('class prototype is frozen', () => expect(Object.isFrozen(WebCryptoAlgorithmSuite.prototype)).to.equal(true)) it('Precondition: WebCryptoDecryptionMaterial suite must be WebCryptoAlgorithmSuite.', () => { const suite: any = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - expect(() => new WebCryptoDecryptionMaterial(suite)).to.throw() + expect(() => new WebCryptoDecryptionMaterial(suite, {})).to.throw() + }) + it('Precondition: WebCryptoDecryptionMaterial encryptionContext must be an object, even if it is empty.', () => { + expect(() => new WebCryptoDecryptionMaterial(suite, undefined as any)).to.throw() + expect(() => new WebCryptoDecryptionMaterial(suite, true as any)).to.throw() }) }) diff --git a/modules/material-management/test/keyring.test.ts b/modules/material-management/test/keyring.test.ts index 0ed2c93c6..0d1720f2b 100644 --- a/modules/material-management/test/keyring.test.ts +++ b/modules/material-management/test/keyring.test.ts @@ -23,7 +23,6 @@ import { AlgorithmSuiteIdentifier } from '../src/algorithm_suites' import { NodeAlgorithmSuite } from '../src/node_algorithms' import { EncryptedDataKey } from '../src/encrypted_data_key' import { Keyring } from '../src/keyring' -import { EncryptionContext } from '../src/types' // eslint-disable-line no-unused-vars import { KeyringTraceFlag } from '../src' chai.use(chaiAsPromised) const { expect } = chai @@ -46,7 +45,7 @@ describe('Keyring', () => { it('onEncrypt calls _onEncrypt', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const { keyLengthBytes } = suite - const m = new NodeEncryptionMaterial(suite) + const m = new NodeEncryptionMaterial(suite, {}) const unencryptedDataKey = new Uint8Array(keyLengthBytes).fill(1) let assertCount = 0 class TestKeyring extends Keyring { @@ -71,14 +70,15 @@ describe('Keyring', () => { it('onDecrypt calls _onDecrypt', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const edk = new EncryptedDataKey({ providerId: 'p', providerInfo: 'i', encryptedDataKey: new Uint8Array(3) }) - const material = new NodeDecryptionMaterial(suite) const encryptionContext = { some: 'context' } + const material = new NodeDecryptionMaterial(suite, encryptionContext) let assertCount = 0 + class TestKeyring extends Keyring { - async _onDecrypt (_material: NodeDecryptionMaterial, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext) { + async _onDecrypt (_material: NodeDecryptionMaterial, encryptedDataKeys: EncryptedDataKey[]) { expect(_material === material).to.equal(true) expect(encryptedDataKeys[0] === edk).to.equal(true) - expect(context === encryptionContext).to.equal(true) + expect(_material.encryptionContext).to.deep.equal(encryptionContext) assertCount += 1 return _material } @@ -87,7 +87,7 @@ describe('Keyring', () => { return material } } - const _material = await (new TestKeyring()).onDecrypt(material, [edk], encryptionContext) + const _material = await (new TestKeyring()).onDecrypt(material, [edk]) expect(material === _material).to.equal(true) expect(assertCount).to.equal(1) }) @@ -116,7 +116,7 @@ describe('Keyring: onEncrypt', () => { class TestKeyring extends Keyring { async _onEncrypt (material: NodeEncryptionMaterial) { assertCount += 1 - return new NodeEncryptionMaterial(material.suite) + return new NodeEncryptionMaterial(material.suite, {}) } async _onDecrypt (material: NodeDecryptionMaterial) { never() @@ -124,7 +124,7 @@ describe('Keyring: onEncrypt', () => { } } const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) await expect((new TestKeyring()).onEncrypt(material)).to.rejectedWith(Error) expect(assertCount).to.equal(1) }) @@ -136,7 +136,7 @@ describe('Keyring: onDecrypt', () => { const edk = new EncryptedDataKey({ providerId: 'p', providerInfo: 'i', encryptedDataKey: new Uint8Array(3) }) let assertCount = 0 class TestKeyring extends Keyring { - async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { assertCount += 1 return material } @@ -151,14 +151,14 @@ describe('Keyring: onDecrypt', () => { it('Precondition: Attempt to decrypt iif material does not have an unencrypted data key.', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const unencryptedDataKey = new Uint8Array(suite.keyLengthBytes).fill(1) const trace = { keyNamespace: 'k', keyName: 'k', flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY } material.setUnencryptedDataKey(unencryptedDataKey, trace) const edk = new EncryptedDataKey({ providerId: 'p', providerInfo: 'i', encryptedDataKey: new Uint8Array(3) }) let assertCount = 0 class TestKeyring extends Keyring { - async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { assertCount += 1 return material } @@ -174,11 +174,11 @@ describe('Keyring: onDecrypt', () => { it('Precondition: encryptedDataKeys must all be EncryptedDataKey.', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const edk: any = {} let assertCount = 0 class TestKeyring extends Keyring { - async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async _onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { assertCount += 1 return material } @@ -193,11 +193,11 @@ describe('Keyring: onDecrypt', () => { it('Postcondition: The DecryptionMaterial objects must be the same.', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const edk = new EncryptedDataKey({ providerId: 'p', providerInfo: 'i', encryptedDataKey: new Uint8Array(3) }) let assertCount = 0 class TestKeyring extends Keyring { - async _onDecrypt (/* material: NodeDecryptionMaterial , encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async _onDecrypt (/* material: NodeDecryptionMaterial , encryptedDataKeys: EncryptedDataKey[] */) { assertCount += 1 const _material: any = {} return _material diff --git a/modules/material-management/test/multi_keyring.test.ts b/modules/material-management/test/multi_keyring.test.ts index 90f95e040..15cb0b4c2 100644 --- a/modules/material-management/test/multi_keyring.test.ts +++ b/modules/material-management/test/multi_keyring.test.ts @@ -124,7 +124,7 @@ describe('MultiKeyring: onEncrypt', () => { }) const mkeyring = new MultiKeyringNode({ generator }) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const test: any = await mkeyring.onEncrypt(material) expect(test === material).to.equal(true) @@ -161,7 +161,7 @@ describe('MultiKeyring: onEncrypt', () => { ] const mkeyring = new MultiKeyringNode({ generator, children }) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const test: any = await mkeyring.onEncrypt(material) expect(test === material).to.equal(true) @@ -187,7 +187,7 @@ describe('MultiKeyring: onEncrypt', () => { }) const mkeyring = new MultiKeyringNode({ generator }) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) await expect(mkeyring.onEncrypt(material)).to.rejectedWith(Error) }) @@ -204,7 +204,7 @@ describe('MultiKeyring: onEncrypt', () => { }) const mkeyring = new MultiKeyringNode({ generator }) - const material = new NodeEncryptionMaterial(suite).setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) + const material = new NodeEncryptionMaterial(suite, {}).setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) await mkeyring.onEncrypt(material) }) @@ -215,10 +215,10 @@ describe('MultiKeyring: onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const unencryptedDataKey = new Uint8Array(suite.keyLengthBytes) const [edk0, keyringTrace0] = makeEDKandTrace(0) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const generator = keyRingFactory({ - async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { return material.setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) }, onEncrypt: never @@ -239,10 +239,10 @@ describe('MultiKeyring: onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const unencryptedDataKey = new Uint8Array(suite.keyLengthBytes) const [edk0, keyringTrace0] = makeEDKandTrace(0) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const child = keyRingFactory({ - async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { return material.setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) }, onEncrypt: never @@ -263,10 +263,10 @@ describe('MultiKeyring: onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const unencryptedDataKey = new Uint8Array(suite.keyLengthBytes) const [edk0, keyringTrace0] = makeEDKandTrace(0) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const child = keyRingFactory({ - async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { return material.setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) }, onEncrypt: never @@ -297,10 +297,10 @@ describe('MultiKeyring: onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const unencryptedDataKey = new Uint8Array(suite.keyLengthBytes) const [edk0, keyringTrace0] = makeEDKandTrace(0) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const child = keyRingFactory({ - async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[], context?: EncryptionContext */) { + async onDecrypt (material: NodeDecryptionMaterial /*, encryptedDataKeys: EncryptedDataKey[] */) { return material.setUnencryptedDataKey(unencryptedDataKey, keyringTrace0) }, onEncrypt: never 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 cbaeaf3bc..70a37f6ae 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 @@ -23,7 +23,6 @@ import { immutableClass, readOnlyProperty, WebCryptoAlgorithmSuite, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars getSubtleFunction, _importCryptoKey, importForWebCryptoEncryptionMaterial, @@ -78,25 +77,25 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { */ .setCryptoKey(masterKey, { keyNamespace, keyName, flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) - const _wrapKey = async (material: WebCryptoEncryptionMaterial, context?: EncryptionContext) => { + const _wrapKey = async (material: WebCryptoEncryptionMaterial) => { /* 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(context || {}).slice(2) + const aad = serializeEncryptionContext(material.encryptionContext).slice(2) const { keyNamespace, keyName } = this return aesGcmWrapKey(keyNamespace, keyName, material, aad, wrappingMaterial) } - const _unwrapKey = async (material: WebCryptoDecryptionMaterial, edk: EncryptedDataKey, context?: EncryptionContext) => { + 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(context || {}).slice(2) + const aad = serializeEncryptionContext(material.encryptionContext).slice(2) const { keyNamespace, keyName } = this return aesGcmUnwrapKey(keyNamespace, keyName, material, wrappingMaterial, edk, aad) @@ -114,8 +113,8 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto { } _rawOnEncrypt = _onEncrypt(randomValuesOnly) - _onEncrypt = async (material: WebCryptoEncryptionMaterial, context?: EncryptionContext) => { - const _material = await this._rawOnEncrypt(material, context) + _onEncrypt = async (material: WebCryptoEncryptionMaterial) => { + const _material = await this._rawOnEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) } diff --git a/modules/raw-aes-keyring-browser/test/raw_aes_keyring_browser.test.ts b/modules/raw-aes-keyring-browser/test/raw_aes_keyring_browser.test.ts index 85c93b5d6..41094baec 100644 --- a/modules/raw-aes-keyring-browser/test/raw_aes_keyring_browser.test.ts +++ b/modules/raw-aes-keyring-browser/test/raw_aes_keyring_browser.test.ts @@ -146,7 +146,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) const udk = test.getUnencryptedDataKey() @@ -159,7 +159,7 @@ describe('RawAesKeyringWebCrypto encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) // The UnencryptedDataKey should be zeroed, because the cryptoKey has been set 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 dd1d41950..50fcf4101 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 @@ -22,8 +22,7 @@ import { KeyringTraceFlag, immutableClass, readOnlyProperty, - NodeAlgorithmSuite, // eslint-disable-line no-unused-vars - EncryptionContext // eslint-disable-line no-unused-vars + NodeAlgorithmSuite // eslint-disable-line no-unused-vars } from '@aws-crypto/material-management-node' import { randomBytes, createCipheriv, createDecipheriv } from 'crypto' import { @@ -75,27 +74,27 @@ export class RawAesKeyringNode extends KeyringNode { */ .setUnencryptedDataKey(unencryptedMasterKey, { keyNamespace, keyName, flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) - const _wrapKey = async (material: NodeEncryptionMaterial, context?: EncryptionContext) => { + const _wrapKey = async (material: NodeEncryptionMaterial) => { /* 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(context || {}).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) } - const _unwrapKey = async (material: NodeDecryptionMaterial, edk: EncryptedDataKey, context?: EncryptionContext) => { + 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(context || {}).slice(2) + const { buffer, byteOffset, byteLength } = serializeEncryptionContext(material.encryptionContext).slice(2) const aad = Buffer.from(buffer, byteOffset, byteLength) // const aad = Buffer.concat(encodeEncryptionContext(context || {})) diff --git a/modules/raw-aes-keyring-node/test/raw_aes_keyring_node.test.ts b/modules/raw-aes-keyring-node/test/raw_aes_keyring_node.test.ts index d00ed1beb..2c510e218 100644 --- a/modules/raw-aes-keyring-node/test/raw_aes_keyring_node.test.ts +++ b/modules/raw-aes-keyring-node/test/raw_aes_keyring_node.test.ts @@ -116,7 +116,7 @@ describe('RawAesKeyringNode encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) const udk = test.getUnencryptedDataKey() @@ -129,7 +129,7 @@ describe('RawAesKeyringNode encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) }) diff --git a/modules/raw-keyring/src/raw_aes_material.ts b/modules/raw-keyring/src/raw_aes_material.ts index bb5d4bf31..08e70435b 100644 --- a/modules/raw-keyring/src/raw_aes_material.ts +++ b/modules/raw-keyring/src/raw_aes_material.ts @@ -33,7 +33,8 @@ import { AwsEsdkJsKeyUsage, // eslint-disable-line no-unused-vars KeyringTrace, // eslint-disable-line no-unused-vars KeyringTraceFlag, - needs + needs, + EncryptionContext // eslint-disable-line no-unused-vars } from '@aws-crypto/material-management' import { @@ -50,6 +51,7 @@ export class NodeRawAesMaterial implements hasUnencryptedDataKey!: boolean unencryptedDataKeyLength!: number keyringTrace: KeyringTrace[] = [] + encryptionContext: EncryptionContext = Object.freeze({}) constructor (suiteId: WrappingSuiteIdentifier) { /* Precondition: NodeRawAesMaterial suiteId must be RawAesWrappingSuiteIdentifier. */ needs(RawAesWrappingSuiteIdentifier[suiteId], 'suiteId not supported.') @@ -83,6 +85,7 @@ export class WebCryptoRawAesMaterial implements getCryptoKey!: () => AwsEsdkJsCryptoKey|MixedBackendCryptoKey hasCryptoKey!: boolean validUsages: ReadonlyArray + encryptionContext: EncryptionContext = Object.freeze({}) constructor (suiteId: WrappingSuiteIdentifier) { /* Precondition: WebCryptoAlgorithmSuite suiteId must be RawAesWrappingSuiteIdentifier. */ needs(RawAesWrappingSuiteIdentifier[suiteId], 'suiteId not supported.') diff --git a/modules/raw-keyring/src/raw_keyring_decorators.ts b/modules/raw-keyring/src/raw_keyring_decorators.ts index 06508fb5e..ad8a2c051 100644 --- a/modules/raw-keyring/src/raw_keyring_decorators.ts +++ b/modules/raw-keyring/src/raw_keyring_decorators.ts @@ -17,7 +17,6 @@ import { EncryptionMaterial, // eslint-disable-line no-unused-vars DecryptionMaterial, // eslint-disable-line no-unused-vars SupportedAlgorithmSuites, // eslint-disable-line no-unused-vars - EncryptionContext, // eslint-disable-line no-unused-vars KeyringTrace, // eslint-disable-line no-unused-vars KeyringTraceFlag, EncryptedDataKey // eslint-disable-line no-unused-vars @@ -36,8 +35,7 @@ export function _onEncrypt, - context?: EncryptionContext + material: EncryptionMaterial ): Promise> { if (!material.hasUnencryptedDataKey) { const trace: KeyringTrace = { @@ -48,7 +46,7 @@ export function _onEncrypt, - encryptedDataKeys: EncryptedDataKey[], - context?: EncryptionContext + encryptedDataKeys: EncryptedDataKey[] ): Promise> { /* Check for early return (Postcondition): If the material is already valid, attempting to decrypt is a bad idea. */ if (material.hasValidKey()) return material @@ -67,7 +64,7 @@ export function _onDecrypt { - (material: EncryptionMaterial, context?: EncryptionContext): Promise> + (material: EncryptionMaterial): Promise> } export interface UnwrapKey { - (material: DecryptionMaterial, edk: EncryptedDataKey, context?: EncryptionContext): Promise> + (material: DecryptionMaterial, edk: EncryptedDataKey): Promise> } export interface FilterEncryptedDataKey { diff --git a/modules/raw-keyring/test/raw_keyring_decorators.test.ts b/modules/raw-keyring/test/raw_keyring_decorators.test.ts index 3ce7e43bf..6a18ac048 100644 --- a/modules/raw-keyring/test/raw_keyring_decorators.test.ts +++ b/modules/raw-keyring/test/raw_keyring_decorators.test.ts @@ -23,7 +23,7 @@ import { AlgorithmSuiteIdentifier, NodeEncryptionMaterial, NodeAlgorithmSuite, K describe('_onEncrypt', () => { it('will create UnencryptedDataKey and call _wrapKey', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) let wrapCalled = 0 const notRandomBytes = async (bytes: number) => new Uint8Array(Array(bytes).fill(1)) const _wrapKey = (material: any) => { @@ -54,7 +54,7 @@ describe('_onEncrypt', () => { const udk = new Uint8Array(Array(suite.keyLengthBytes).fill(2)) const keyName = 'keyName' const keyNamespace = 'keyNamespace' - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) .setUnencryptedDataKey(udk, { keyName, keyNamespace, flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY }) let wrapCalled = 0 const notRandomBytes = async () => { throw new Error('never') } @@ -80,7 +80,7 @@ describe('_onDecrypt', () => { it('basic usage', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const udk = new Uint8Array(Array(suite.keyLengthBytes).fill(2)) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const keyName = 'keyName' const keyNamespace = 'keyNamespace' @@ -125,7 +125,7 @@ describe('_onDecrypt', () => { const udk = new Uint8Array(Array(suite.keyLengthBytes).fill(2)) const keyName = 'keyName' const keyNamespace = 'keyNamespace' - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) .setUnencryptedDataKey(udk, { keyName, keyNamespace, flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY }) const edk = new EncryptedDataKey({ @@ -163,7 +163,7 @@ describe('_onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const keyName = 'keyName' const keyNamespace = 'keyNamespace' - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const edk = new EncryptedDataKey({ providerId: keyName, @@ -200,7 +200,7 @@ describe('_onDecrypt', () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16) const keyName = 'keyName' const keyNamespace = 'keyNamespace' - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const edk = new EncryptedDataKey({ providerId: keyName, 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 6ff402c13..943e97789 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 @@ -25,7 +25,6 @@ import { readOnlyProperty, bytes2JWK, keyUsageForMaterial, - EncryptionContext, // eslint-disable-line no-unused-vars importForWebCryptoEncryptionMaterial, MixedBackendCryptoKey, // eslint-disable-line no-unused-vars WebCryptoAlgorithmSuite // eslint-disable-line no-unused-vars @@ -157,8 +156,8 @@ export class RawRsaKeyringWebCrypto extends KeyringWebCrypto { } _rawOnEncrypt = _onEncrypt(randomValuesOnly) - _onEncrypt = async (material: WebCryptoEncryptionMaterial, context?: EncryptionContext) => { - const _material = await this._rawOnEncrypt(material, context) + _onEncrypt = async (material: WebCryptoEncryptionMaterial) => { + const _material = await this._rawOnEncrypt(material) return importForWebCryptoEncryptionMaterial(_material) } diff --git a/modules/raw-rsa-keyring-browser/test/raw_rsa_keyring_web_crypto.test.ts b/modules/raw-rsa-keyring-browser/test/raw_rsa_keyring_web_crypto.test.ts index bc3ccb801..57f4650b3 100644 --- a/modules/raw-rsa-keyring-browser/test/raw_rsa_keyring_web_crypto.test.ts +++ b/modules/raw-rsa-keyring-browser/test/raw_rsa_keyring_web_crypto.test.ts @@ -141,7 +141,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) const udk = test.getUnencryptedDataKey() @@ -154,7 +154,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) // The UnencryptedDataKey should be zeroed, because the cryptoKey has been set @@ -166,7 +166,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { const keyring = new RawRsaKeyringWebCrypto({ privateKey, keyName, keyNamespace }) const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoEncryptionMaterial(suite) + const material = new WebCryptoEncryptionMaterial(suite, {}) expect(keyring.onEncrypt(material)).to.rejectedWith(Error) }) @@ -175,7 +175,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { const keyring = new RawRsaKeyringWebCrypto({ publicKey, keyName, keyNamespace }) const suite = new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new WebCryptoDecryptionMaterial(suite) + const material = new WebCryptoDecryptionMaterial(suite, {}) expect(keyring.onDecrypt(material, [encryptedDataKey])).to.rejectedWith(Error) }) }) diff --git a/modules/raw-rsa-keyring-node/test/raw_rsa_keyring_node.test.ts b/modules/raw-rsa-keyring-node/test/raw_rsa_keyring_node.test.ts index f87a5ed45..1e4bce61c 100644 --- a/modules/raw-rsa-keyring-node/test/raw_rsa_keyring_node.test.ts +++ b/modules/raw-rsa-keyring-node/test/raw_rsa_keyring_node.test.ts @@ -137,7 +137,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { it('can encrypt and create unencrypted data key', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) const test = await keyring.onEncrypt(material) expect(test.hasValidKey()).to.equal(true) const udk = test.getUnencryptedDataKey() @@ -150,7 +150,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { it('can decrypt an EncryptedDataKey', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) const test = await keyring.onDecrypt(material, [encryptedDataKey]) expect(test.hasValidKey()).to.equal(true) }) @@ -163,7 +163,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { }) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeEncryptionMaterial(suite) + const material = new NodeEncryptionMaterial(suite, {}) expect(keyring.onEncrypt(material)).to.rejectedWith(Error) }) @@ -175,7 +175,7 @@ describe('RawRsaKeyringWebCrypto encrypt/decrypt', () => { }) const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA256) - const material = new NodeDecryptionMaterial(suite) + const material = new NodeDecryptionMaterial(suite, {}) await keyring.onDecrypt(material, [encryptedDataKey]) expect(keyring.onDecrypt(material, [encryptedDataKey])).to.rejectedWith(Error) })