in modules/kms-keyring/src/kms_keyring.ts [231:310]
async _onDecrypt(
material: DecryptionMaterial<S>,
encryptedDataKeys: EncryptedDataKey[]
) {
const keyIds = this.keyIds.slice()
const { clientProvider, generatorKeyId, grantTokens } = this
if (generatorKeyId) keyIds.unshift(generatorKeyId)
/* If there are no key IDs in the list, keyring is in "discovery" mode and will attempt KMS calls with
* every ARN it comes across in the message. If there are key IDs in the list, it will cross check the
* ARN it reads with that list before attempting KMS calls. Note that if caller provided key IDs in
* anything other than a CMK ARN format, the Encryption SDK will not attempt to decrypt those data keys, because
* the EDK data format always specifies the CMK with the full (non-alias) ARN.
*/
const decryptableEDKs = encryptedDataKeys.filter(filterEDKs(keyIds, this))
const cmkErrors: Catchable[] = []
for (const edk of decryptableEDKs) {
let dataKey: RequiredDecryptResponse | false = false
try {
dataKey = await decrypt(
clientProvider,
edk,
material.encryptionContext,
grantTokens
)
} catch (e) {
/* Failures onDecrypt should not short-circuit the process
* If the caller does not have access they may have access
* through another Keyring.
*/
cmkErrors.push({ errPlus: e })
}
/* Check for early return (Postcondition): clientProvider may not return a client. */
if (!dataKey) continue
/* Postcondition: The KeyId from KMS must match the encoded KeyID. */
needs(
dataKey.KeyId === edk.providerInfo,
'KMS Decryption key does not match the requested key id.'
)
const flags =
KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY |
KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX
const trace: KeyringTrace = {
keyNamespace: KMS_PROVIDER_ID,
keyName: dataKey.KeyId,
flags,
}
/* Postcondition: The decrypted unencryptedDataKey length must match the algorithm specification.
* See cryptographic_materials as setUnencryptedDataKey will throw in this case.
*/
material.setUnencryptedDataKey(dataKey.Plaintext, trace)
return material
}
/* Postcondition: A CMK must provide a valid data key or KMS must not have raised any errors.
* If I have a data key,
* decrypt errors can be ignored.
* However, if I was unable to decrypt a data key AND I have errors,
* these errors should bubble up.
* Otherwise, the only error customers will see is that
* the material does not have an unencrypted data key.
* So I return a concatenated Error message
*/
needs(
material.hasValidKey() ||
(!material.hasValidKey() && !cmkErrors.length),
cmkErrors.reduce(
(m, e, i) => `${m} Error #${i + 1} \n ${e.errPlus.stack} \n`,
'Unable to decrypt data key and one or more KMS CMKs had an error. \n '
)
)
return material
}