async _onDecrypt()

in modules/kms-keyring/src/kms_mrk_discovery_keyring.ts [178:307]


    async _onDecrypt(
      material: DecryptionMaterial<S>,
      encryptedDataKeys: EncryptedDataKey[]
    ): Promise<DecryptionMaterial<S>> {
      //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
      //# If the decryption materials (structures.md#decryption-materials)
      //# already contained a valid plaintext data key OnDecrypt MUST
      //# immediately return the unmodified decryption materials
      //# (structures.md#decryption-materials).
      if (material.hasValidKey()) return material

      // See the constructor, this is to support both AWS SDK v2 and v3.
      needs(
        typeof this.clientRegion === 'string' ||
          /* Precondition: AWS SDK V3 region promise MUST have resolved to a string.
           * In the constructor the region promise resolves
           * to the same value that is then set.
           */
          // @ts-ignore
          typeof (await this.clientRegion) == 'string',
        'clientRegion MUST be a string.'
      )

      const { client, grantTokens, clientRegion } = this

      //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
      //# The set of encrypted data keys MUST first be filtered to match this
      //# keyring's configuration.
      const decryptableEDKs = encryptedDataKeys.filter(filterEDKs(this))
      const cmkErrors: Catchable[] = []

      //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
      //# For each encrypted data key in the filtered set, one at a time, the
      //# OnDecrypt MUST attempt to decrypt the data key.
      for (const edk of decryptableEDKs) {
        //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
        //# Otherwise it MUST
        //# be the provider info.
        let keyId = edk.providerInfo
        //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
        //# *  "KeyId": If the provider info's resource type is "key" and its
        //# resource is a multi-Region key then a new ARN MUST be created
        //# where the region part MUST equal the AWS KMS client region and
        //# every other part MUST equal the provider info.
        const keyArn = parseAwsKmsKeyArn(edk.providerInfo)
        needs(keyArn, 'Unexpected EDK ProviderInfo for AWS KMS EDK')
        if (isMultiRegionAwsKmsArn(keyArn)) {
          keyId = constructArnInOtherRegion(keyArn, clientRegion)
        }

        let dataKey: RequiredDecryptResponse | false = false
        try {
          //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
          //# When calling AWS KMS Decrypt
          //# (https://docs.aws.amazon.com/kms/latest/APIReference/
          //# API_Decrypt.html), the keyring MUST call with a request constructed
          //# as follows:
          dataKey = await decrypt(
            //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
            //# To attempt to decrypt a particular encrypted data key
            //# (structures.md#encrypted-data-key), OnDecrypt MUST call AWS KMS
            //# Decrypt (https://docs.aws.amazon.com/kms/latest/APIReference/
            //# API_Decrypt.html) with the configured AWS KMS client.
            client,
            {
              providerId: edk.providerId,
              providerInfo: keyId,
              encryptedDataKey: edk.encryptedDataKey,
            },
            material.encryptionContext,
            grantTokens
          )
          /* This should be impossible given that decrypt only returns false if the client supplier does
           * or if the providerId is not "aws-kms", which we have already filtered out
           */
          if (!dataKey) continue

          //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
          //# *  The "KeyId" field in the response MUST equal the requested "KeyId"
          needs(
            dataKey.KeyId === keyId,
            '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,
          }

          //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
          //# *  The length of the response's "Plaintext" MUST equal the key
          //# derivation input length (algorithm-suites.md#key-derivation-input-
          //# length) specified by the algorithm suite (algorithm-suites.md)
          //# included in the input decryption materials
          //# (structures.md#decryption-materials).
          //
          //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
          //# Since the response does satisfies these requirements then OnDecrypt
          //# MUST do the following with the response:
          //
          // setUnencryptedDataKey will throw if the plaintext does not match the algorithm suite requirements.
          material.setUnencryptedDataKey(dataKey.Plaintext, trace)
          return material
        } catch (e) {
          //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
          //# If the response does not satisfies these requirements then an error
          //# is collected and the next encrypted data key in the filtered set MUST
          //# be attempted.
          cmkErrors.push({ errPlus: e })
        }
      }
      //= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
      //# If OnDecrypt fails to successfully decrypt any encrypted data key
      //# (structures.md#encrypted-data-key), then it MUST yield an error that
      //# includes all collected errors.
      needs(
        material.hasValidKey(),
        [
          `Unable to decrypt data key${
            !decryptableEDKs.length ? ': No EDKs supplied' : ''
          }.`,
          ...cmkErrors.map((e, i) => `Error #${i + 1}  \n${e.errPlus.stack}`),
        ].join('\n')
      )
      return material
    }