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 72f7cdfd5..6f56c2165 100644 --- a/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts +++ b/modules/material-management-browser/src/browser_cryptographic_materials_manager.ts @@ -49,11 +49,23 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM async getEncryptionMaterials ({ suite, encryptionContext }: WebCryptoEncryptionRequest): Promise { suite = suite || new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) + /* Precondition: WebCryptoDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize. + * A CryptographicMaterialsManager can change entries to the encryptionContext + * but changing these values has consequences. + * The DefaultCryptographicMaterialsManager uses the value in the encryption context to store public signing key. + * If the caller is using this value in their encryption context the Default CMM is probably not the CMM they want to use. + */ + needs(!encryptionContext.hasOwnProperty(ENCODED_SIGNER_KEY), `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.`) + const material = await this .keyring .onEncrypt(await this._initializeEncryptionMaterial(suite, encryptionContext)) - /* Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey. */ + /* Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey. + * This verifies that the data key matches the algorithm suite specification + * and that the unencrypted data key is non-NULL. + * See: cryptographic_materials.ts, `getUnencryptedDataKey` + */ needs(material.hasValidKey(), 'Unencrypted data key is invalid.') /* Postcondition: The WebCryptoEncryptionMaterial must contain at least 1 EncryptedDataKey. */ @@ -67,7 +79,12 @@ export class WebCryptoDefaultCryptographicMaterialsManager implements WebCryptoM .keyring .onDecrypt(await this._initializeDecryptionMaterial(suite, encryptionContext), encryptedDataKeys.slice()) - /* Postcondition: The WebCryptoDecryptionMaterial must contain a valid dataKey. */ + /* Postcondition: The WebCryptoDecryptionMaterial must contain a valid dataKey. + * See: cryptographic_materials.ts, `getUnencryptedDataKey` also verifies + * that the unencrypted data key has not been manipulated, + * that the data key matches the algorithm suite specification + * and that the unencrypted data key is non-NULL. + */ needs(material.hasValidKey(), 'Unencrypted data key is invalid.') return material 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 d450f557b..5757d20f7 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 @@ -240,6 +240,25 @@ describe('WebCryptoDefaultCryptographicMaterialsManager', () => { expect(material.encryptionContext).to.have.haveOwnProperty('some').and.to.equal('context') }) + it('Precondition: WebCryptoDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize.', async () => { + class TestKeyring extends KeyringWebCrypto { + async _onEncrypt (): Promise { + throw new Error('I should never see this error') + } + async _onDecrypt (): Promise { + throw new Error('I should never see this error') + } + } + + const keyring = new TestKeyring() + const cmm = new WebCryptoDefaultCryptographicMaterialsManager(keyring) + const encryptionContext = { + [ENCODED_SIGNER_KEY]: 'context' + } + + expect(cmm.getEncryptionMaterials({ encryptionContext })).to.rejectedWith(Error, 'Reserved encryptionContext value') + }) + it('Postcondition: The WebCryptoEncryptionMaterial must contain a valid dataKey.', async () => { class TestKeyring extends KeyringWebCrypto { async _onEncrypt (material: WebCryptoEncryptionMaterial): Promise { 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 bc9bbe384..9d79fc7b5 100644 --- a/modules/material-management-node/src/node_cryptographic_materials_manager.ts +++ b/modules/material-management-node/src/node_cryptographic_materials_manager.ts @@ -49,11 +49,23 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa async getEncryptionMaterials ({ suite, encryptionContext }: NodeEncryptionRequest): Promise { suite = suite || new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384) + /* Precondition: NodeDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize. + * A CryptographicMaterialsManager can change entries to the encryptionContext + * but changing these values has consequences. + * The DefaultCryptographicMaterialsManager uses the value in the encryption context to store public signing key. + * If the caller is using this value in their encryption context the Default CMM is probably not the CMM they want to use. + */ + needs(!encryptionContext.hasOwnProperty(ENCODED_SIGNER_KEY), `Reserved encryptionContext value ${ENCODED_SIGNER_KEY} not allowed.`) + const material = await this .keyring .onEncrypt(this._initializeEncryptionMaterial(suite, encryptionContext)) - /* Postcondition: The NodeEncryptionMaterial must contain a valid dataKey. */ + /* Postcondition: The NodeEncryptionMaterial must contain a valid dataKey. + * This verifies that the data key matches the algorithm suite specification + * and that the unencrypted data key is non-NULL. + * See: cryptographic_materials.ts, `getUnencryptedDataKey` + */ needs(material.getUnencryptedDataKey(), 'Unencrypted data key is invalid.') /* Postcondition: The NodeEncryptionMaterial must contain at least 1 EncryptedDataKey. */ @@ -69,7 +81,9 @@ export class NodeDefaultCryptographicMaterialsManager implements NodeMaterialsMa /* Postcondition: The NodeDecryptionMaterial must contain a valid dataKey. * See: cryptographic_materials.ts, `getUnencryptedDataKey` also verifies - * that the unencrypted data key has not been manipulated. + * that the unencrypted data key has not been manipulated, + * that the data key matches the algorithm suite specification + * and that the unencrypted data key is non-NULL. */ needs(material.getUnencryptedDataKey(), 'Unencrypted data key is invalid.') 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 172f2f79d..8e1204459 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 @@ -183,6 +183,24 @@ describe('NodeDefaultCryptographicMaterialsManager', () => { )).to.throw() }) + it('Precondition: NodeDefaultCryptographicMaterialsManager must reserve the ENCODED_SIGNER_KEY constant from @aws-crypto/serialize.', async () => { + class TestKeyring extends KeyringNode { + async _onEncrypt (): Promise { + throw new Error('this should never happen') + } + async _onDecrypt (): Promise { + throw new Error('this should never happen') + } + } + const keyring = new TestKeyring() + const cmm = new NodeDefaultCryptographicMaterialsManager(keyring) + const encryptionContext = { + [ENCODED_SIGNER_KEY]: 'something' + } + + await expect(cmm.getEncryptionMaterials({ encryptionContext })).to.rejectedWith(Error, 'Reserved encryptionContext value') + }) + it('Postcondition: The NodeEncryptionMaterial must contain a valid dataKey.', async () => { const suite = new NodeAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256)