docs: async def put_object(self, Body: Union[bytes, IO], Bucket: str, Key: str, Metadata: Dict = None, **kwargs):
          """
          PutObject. Takes same args as Boto3 documentation
  
          Encrypts files
  
          :param: Body: File data
          :param Bucket: S3 Bucket
          :param Key: S3 Key (filepath)
          """
          if self._s3_client is None:
              await self.setup()
  
          if hasattr(Body, 'read'):
              if inspect.iscoroutinefunction(Body.read):
                  Body = await Body.read()
              else:
                  Body = Body.read()
  
          # We do some different V2 stuff if using kms
          is_kms = isinstance(self._crypto_context, KMSCryptoContext)
          # noinspection PyUnresolvedReferences
          authenticated_crypto = is_kms and self._crypto_context.authenticated_encryption
  
          Metadata = Metadata if Metadata is not None else {}
  
          aes_key, matdesc_metadata, key_metadata = await self._crypto_context.get_encryption_aes_key()
  
          if is_kms and authenticated_crypto:
              Metadata['x-amz-cek-alg'] = 'AES/GCM/NoPadding'
              Metadata['x-amz-tag-len'] = str(AES_BLOCK_SIZE)
              iv = os.urandom(12)
  
              # 16byte 128bit authentication tag forced
              aesgcm = AESGCM(aes_key)
  
              result = await self._loop.run_in_executor(None, lambda: aesgcm.encrypt(iv, Body, None))
  
          else:
              if is_kms:  # V1 is always AES/CBC/PKCS5Padding
                  Metadata['x-amz-cek-alg'] = 'AES/CBC/PKCS5Padding'
  
              iv = os.urandom(16)
  
              padder = PKCS7(AES.block_size).padder()
              padded_result = await self._loop.run_in_executor(None, lambda: (padder.update(Body) + padder.finalize()))
  
              aescbc = Cipher(AES(aes_key), CBC(iv), backend=self._backend).encryptor()
              result = await self._loop.run_in_executor(None, lambda: (aescbc.update(padded_result) + aescbc.finalize()))
  
          # For all V1 and V2
          Metadata['x-amz-unencrypted-content-length'] = str(len(Body))
          Metadata['x-amz-iv'] = base64.b64encode(iv).decode()
          Metadata['x-amz-matdesc'] = json.dumps(matdesc_metadata)
  
          if is_kms:
              Metadata['x-amz-wrap-alg'] = 'kms'
              Metadata['x-amz-key-v2'] = key_metadata
          else:
              Metadata['x-amz-key'] = key_metadata
  
          await self._s3_client.put_object(
              Bucket=Bucket,
              Key=Key,
              Body=result,
              Metadata=Metadata,
              **kwargs
          )