text: #!/usr/bin/python
  
  """
  Python wrapper of the botan crypto library
  https://botan.randombit.net
  
  (C) 2015,2017,2018,2019 Jack Lloyd
  (C) 2015 Uri  Blumenthal (extensions and patches)
  
  Botan is released under the Simplified BSD License (see license.txt)
  
  This module uses the ctypes module and is usable by programs running
  under at least CPython 2.7, CPython 3.x, and PyPy
  
  It uses botan's ffi module, which exposes a C API. This version of the
  module requires FFI API version 20180713, which was introduced in
  Botan 2.8
  
  """
  
  from ctypes import CDLL, POINTER, byref, create_string_buffer, \
      c_void_p, c_size_t, c_uint8, c_uint32, c_uint64, c_int, c_uint, c_char_p
  
  from sys import version_info, platform
  from time import strptime, mktime, time as system_time
  from binascii import hexlify
  from datetime import datetime
  
  BOTAN_FFI_VERSION = 20191214
  
  #
  # Base exception for all exceptions raised from this module
  #
  class BotanException(Exception):
  
      def __init__(self, message, rc=0):
  
          self.__rc = rc
  
          if rc == 0:
              super(BotanException, self).__init__(message)
          else:
              exn_msg = _DLL.botan_error_last_exception_message().decode('ascii')
              err_descr = _DLL.botan_error_description(rc).decode('ascii')
  
              formatted_msg = "%s: %d (%s)" % (message, rc, err_descr)
              if exn_msg != "":
                  formatted_msg += ': ' + exn_msg
  
              super(BotanException, self).__init__(formatted_msg)
  
      def error_code(self):
          return self.__rc
  
  #
  # Module initialization
  #
  
  def _load_botan_dll(expected_version):
  
      possible_dll_names = []
  
      if platform in ['win32', 'cygwin', 'msys']:
          possible_dll_names.append('botan-3.dll')
          possible_dll_names.append('botan.dll')
          possible_dll_names.append('libbotan-3.dll')
          possible_dll_names.append('libbotan-2.dll')
      elif platform in ['darwin', 'macos']:
          possible_dll_names.append('libbotan-3.dylib')
          possible_dll_names.append('libbotan-2.dylib')
      else:
          # assumed to be some Unix/Linux system
          possible_dll_names.append('libbotan-3.so')
          possible_dll_names += ['libbotan-3.so.%d' % (v) for v in reversed(range(0, 10))]
          possible_dll_names.append('libbotan-2.so')
          possible_dll_names += ['libbotan-2.so.%d' % (v) for v in reversed(range(13, 20))]
  
      for dll_name in possible_dll_names:
          try:
              dll = CDLL(dll_name)
              if hasattr(dll, 'botan_ffi_supports_api'):
                  dll.botan_ffi_supports_api.argtypes = [c_uint32]
                  dll.botan_ffi_supports_api.restype = c_int
                  if dll.botan_ffi_supports_api(expected_version) == 0:
                      return dll
          except OSError:
              pass
  
      raise BotanException("Could not find a usable Botan shared object library")
  
  def _errcheck(rc, fn, _args):
      # This errcheck should only be used for int-returning functions
      assert isinstance(rc, int)
  
      if rc >= 0 or rc in fn.allowed_errors:
          return rc
      raise BotanException('%s failed' % (fn.__name__), rc)
  
  def _set_prototypes(dll):
      # pylint: disable=too-many-statements,line-too-long
      def ffi_api(fn, args, allowed_errors=None):
          if allowed_errors is None:
              allowed_errors = [-10]
          fn.argtypes = args
          fn.restype = c_int
          fn.errcheck = _errcheck
          fn.allowed_errors = allowed_errors
  
      dll.botan_version_string.argtypes = []
      dll.botan_version_string.restype = c_char_p
  
      dll.botan_version_string.argtypes = []
      dll.botan_version_string.restype = c_char_p
  
      dll.botan_version_major.argtypes = []
      dll.botan_version_major.restype = c_uint32
  
      dll.botan_version_minor.argtypes = []
      dll.botan_version_minor.restype = c_uint32
  
      dll.botan_version_patch.argtypes = []
      dll.botan_version_patch.restype = c_uint32
  
      dll.botan_ffi_api_version.argtypes = []
      dll.botan_ffi_api_version.restype = c_uint32
  
      dll.botan_error_description.argtypes = [c_int]
      dll.botan_error_description.restype = c_char_p
  
      dll.botan_error_last_exception_message.argtypes = []
      dll.botan_error_last_exception_message.restype = c_char_p
  
      # These are generated using src/scripts/ffi_decls.py:
      ffi_api(dll.botan_constant_time_compare, [c_void_p, c_void_p, c_size_t], [-1])
      ffi_api(dll.botan_scrub_mem, [c_void_p, c_size_t])
  
      ffi_api(dll.botan_hex_encode, [c_char_p, c_size_t, c_char_p, c_uint32])
      ffi_api(dll.botan_hex_decode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
  
      ffi_api(dll.botan_base64_encode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_base64_decode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
  
      #  RNG
      ffi_api(dll.botan_rng_init, [c_void_p, c_char_p])
      ffi_api(dll.botan_rng_get, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_rng_reseed, [c_void_p, c_size_t])
      ffi_api(dll.botan_rng_reseed_from_rng, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_rng_add_entropy, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_rng_destroy, [c_void_p])
  
      #  HASH
      ffi_api(dll.botan_hash_init, [c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_hash_copy_state, [c_void_p, c_void_p])
      ffi_api(dll.botan_hash_output_length, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_hash_block_size, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_hash_update, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_hash_final, [c_void_p, c_char_p])
      ffi_api(dll.botan_hash_clear, [c_void_p])
      ffi_api(dll.botan_hash_destroy, [c_void_p])
      ffi_api(dll.botan_hash_name, [c_void_p, c_char_p, POINTER(c_size_t)])
  
      #  MAC
      ffi_api(dll.botan_mac_init, [c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_mac_output_length, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mac_set_key, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_mac_update, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_mac_final, [c_void_p, c_char_p])
      ffi_api(dll.botan_mac_clear, [c_void_p])
      ffi_api(dll.botan_mac_name, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mac_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
      ffi_api(dll.botan_mac_destroy, [c_void_p])
  
      #  CIPHER
      ffi_api(dll.botan_cipher_init, [c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_cipher_name, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_valid_nonce_length, [c_void_p, c_size_t])
      ffi_api(dll.botan_cipher_get_tag_length, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_get_default_nonce_length, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_get_update_granularity, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_query_keylen, [c_void_p, POINTER(c_size_t), POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_set_key, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_cipher_reset, [c_void_p])
      ffi_api(dll.botan_cipher_set_associated_data, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_cipher_start, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_cipher_update,
              [c_void_p, c_uint32, c_char_p, c_size_t, POINTER(c_size_t), c_char_p, c_size_t, POINTER(c_size_t)])
      ffi_api(dll.botan_cipher_clear, [c_void_p])
      ffi_api(dll.botan_cipher_destroy, [c_void_p])
  
      ffi_api(dll.botan_pbkdf,
              [c_char_p, c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t])
      ffi_api(dll.botan_pbkdf_timed,
              [c_char_p, c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t, POINTER(c_size_t)])
  
      ffi_api(dll.botan_pwdhash,
              [c_char_p, c_size_t, c_size_t, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
      ffi_api(dll.botan_pwdhash_timed,
              [c_char_p, c_uint32, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t), c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
  
      ffi_api(dll.botan_scrypt,
              [c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t, c_size_t, c_size_t])
  
      ffi_api(dll.botan_kdf,
              [c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
  
      #  BLOCK
      ffi_api(dll.botan_block_cipher_init, [c_void_p, c_char_p])
      ffi_api(dll.botan_block_cipher_destroy, [c_void_p])
      ffi_api(dll.botan_block_cipher_clear, [c_void_p])
      ffi_api(dll.botan_block_cipher_set_key, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_block_cipher_block_size, [c_void_p])
      ffi_api(dll.botan_block_cipher_encrypt_blocks, [c_void_p, c_char_p, c_char_p, c_size_t])
      ffi_api(dll.botan_block_cipher_decrypt_blocks, [c_void_p, c_char_p, c_char_p, c_size_t])
      ffi_api(dll.botan_block_cipher_name, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_block_cipher_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
  
      #  MP
      ffi_api(dll.botan_mp_init, [c_void_p])
      ffi_api(dll.botan_mp_destroy, [c_void_p])
      ffi_api(dll.botan_mp_to_hex, [c_void_p, c_char_p])
      ffi_api(dll.botan_mp_to_str, [c_void_p, c_uint8, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mp_clear, [c_void_p])
      ffi_api(dll.botan_mp_set_from_int, [c_void_p, c_int])
      ffi_api(dll.botan_mp_set_from_mp, [c_void_p, c_void_p])
      ffi_api(dll.botan_mp_set_from_str, [c_void_p, c_char_p])
      ffi_api(dll.botan_mp_set_from_radix_str, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_mp_num_bits, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mp_num_bytes, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mp_to_bin, [c_void_p, c_char_p])
      ffi_api(dll.botan_mp_from_bin, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_mp_to_uint32, [c_void_p, POINTER(c_uint32)])
      ffi_api(dll.botan_mp_is_positive, [c_void_p])
      ffi_api(dll.botan_mp_is_negative, [c_void_p])
      ffi_api(dll.botan_mp_flip_sign, [c_void_p])
      ffi_api(dll.botan_mp_is_zero, [c_void_p])
      ffi_api(dll.botan_mp_is_odd, [c_void_p])
      ffi_api(dll.botan_mp_is_even, [c_void_p])
      ffi_api(dll.botan_mp_add_u32, [c_void_p, c_void_p, c_uint32])
      ffi_api(dll.botan_mp_sub_u32, [c_void_p, c_void_p, c_uint32])
      ffi_api(dll.botan_mp_add, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_sub, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_mul, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_div, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_mod_mul, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_equal, [c_void_p, c_void_p])
      ffi_api(dll.botan_mp_cmp, [POINTER(c_int), c_void_p, c_void_p])
      ffi_api(dll.botan_mp_swap, [c_void_p, c_void_p])
      ffi_api(dll.botan_mp_powmod, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_lshift, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_mp_rshift, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_mp_mod_inverse, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_rand_bits, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_mp_rand_range, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_gcd, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_mp_is_prime, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_mp_get_bit, [c_void_p, c_size_t])
      ffi_api(dll.botan_mp_set_bit, [c_void_p, c_size_t])
      ffi_api(dll.botan_mp_clear_bit, [c_void_p, c_size_t])
  
      ffi_api(dll.botan_bcrypt_generate,
              [c_char_p, POINTER(c_size_t), c_char_p, c_void_p, c_size_t, c_uint32])
      ffi_api(dll.botan_bcrypt_is_valid, [c_char_p, c_char_p])
  
      #  PUBKEY
      ffi_api(dll.botan_privkey_create, [c_void_p, c_char_p, c_char_p, c_void_p])
      ffi_api(dll.botan_privkey_check_key, [c_void_p, c_void_p, c_uint32], [-1])
      ffi_api(dll.botan_privkey_create_rsa, [c_void_p, c_void_p, c_size_t])
      ffi_api(dll.botan_privkey_create_ecdsa, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_create_ecdh, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_create_mceliece, [c_void_p, c_void_p, c_size_t, c_size_t])
      ffi_api(dll.botan_privkey_create_dh, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_create_dsa, [c_void_p, c_void_p, c_size_t, c_size_t])
      ffi_api(dll.botan_privkey_create_elgamal, [c_void_p, c_void_p, c_size_t, c_size_t])
      ffi_api(dll.botan_privkey_load,
              [c_void_p, c_void_p, c_char_p, c_size_t, c_char_p])
      ffi_api(dll.botan_privkey_destroy, [c_void_p])
      ffi_api(dll.botan_privkey_export, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32])
      ffi_api(dll.botan_privkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_privkey_export_encrypted,
              [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_char_p, c_uint32])
      ffi_api(dll.botan_privkey_export_encrypted_pbkdf_msec,
              [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_uint32, POINTER(c_size_t), c_char_p, c_char_p, c_uint32])
      ffi_api(dll.botan_privkey_export_encrypted_pbkdf_iter,
              [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_size_t, c_char_p, c_char_p, c_uint32])
      ffi_api(dll.botan_privkey_export_pubkey, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_load, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_pubkey_export, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32])
      ffi_api(dll.botan_pubkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pubkey_check_key, [c_void_p, c_void_p, c_uint32], [-1])
      ffi_api(dll.botan_pubkey_estimated_strength, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pubkey_fingerprint, [c_void_p, c_char_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pubkey_destroy, [c_void_p])
      ffi_api(dll.botan_pubkey_get_field, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_get_field, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_rsa, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_load_rsa_pkcs1, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_privkey_rsa_get_p, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_rsa_get_q, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_rsa_get_d, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_rsa_get_n, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_rsa_get_e, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_rsa_get_privkey, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32])
      ffi_api(dll.botan_pubkey_load_rsa, [c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_rsa_get_e, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_rsa_get_n, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_load_dsa,
              [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_load_dsa,
              [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_dsa_get_x, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_dsa_get_p, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_dsa_get_q, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_dsa_get_g, [c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_dsa_get_y, [c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_load_dh, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_load_dh, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_pubkey_load_elgamal, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_load_elgamal, [c_void_p, c_void_p, c_void_p, c_void_p])
      ffi_api(dll.botan_privkey_load_ed25519, [c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_ed25519, [c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_ed25519_get_privkey, [c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_ed25519_get_pubkey, [c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_x25519, [c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_x25519, [c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_x25519_get_privkey, [c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_x25519_get_pubkey, [c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_ecdsa, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_ecdsa, [c_void_p, c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_ecdh, [c_void_p, c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_ecdh, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_sm2, [c_void_p, c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_sm2, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_load_sm2_enc, [c_void_p, c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_privkey_load_sm2_enc, [c_void_p, c_void_p, c_char_p])
      ffi_api(dll.botan_pubkey_sm2_compute_za,
              [c_char_p, POINTER(c_size_t), c_char_p, c_char_p, c_void_p])
  
      #  PK
      ffi_api(dll.botan_pk_op_encrypt_create, [c_void_p, c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_pk_op_encrypt_destroy, [c_void_p])
      ffi_api(dll.botan_pk_op_encrypt_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_encrypt,
              [c_void_p, c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t])
      ffi_api(dll.botan_pk_op_decrypt_create, [c_void_p, c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_pk_op_decrypt_destroy, [c_void_p])
      ffi_api(dll.botan_pk_op_decrypt_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_decrypt,
              [c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t])
      ffi_api(dll.botan_pk_op_sign_create, [c_void_p, c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_pk_op_sign_destroy, [c_void_p])
      ffi_api(dll.botan_pk_op_sign_output_length, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_sign_update, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_pk_op_sign_finish, [c_void_p, c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_verify_create, [c_void_p, c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_pk_op_verify_destroy, [c_void_p])
      ffi_api(dll.botan_pk_op_verify_update, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_pk_op_verify_finish, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_pk_op_key_agreement_create, [c_void_p, c_void_p, c_char_p, c_uint32])
      ffi_api(dll.botan_pk_op_key_agreement_destroy, [c_void_p])
      ffi_api(dll.botan_pk_op_key_agreement_export_public, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_key_agreement_size, [c_void_p, POINTER(c_size_t)])
      ffi_api(dll.botan_pk_op_key_agreement,
              [c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t, c_char_p, c_size_t])
  
      ffi_api(dll.botan_pkcs_hash_id, [c_char_p, c_char_p, POINTER(c_size_t)])
  
      ffi_api(dll.botan_mceies_encrypt,
              [c_void_p, c_void_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_mceies_decrypt,
              [c_void_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
  
      #  X509
      ffi_api(dll.botan_x509_cert_load, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_x509_cert_load_file, [c_void_p, c_char_p])
      ffi_api(dll.botan_x509_cert_destroy, [c_void_p])
      ffi_api(dll.botan_x509_cert_dup, [c_void_p, c_void_p])
      ffi_api(dll.botan_x509_cert_get_time_starts, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_time_expires, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_not_before, [c_void_p, POINTER(c_uint64)])
      ffi_api(dll.botan_x509_cert_not_after, [c_void_p, POINTER(c_uint64)])
      ffi_api(dll.botan_x509_cert_get_fingerprint, [c_void_p, c_char_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_serial_number, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_authority_key_id, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_subject_key_id, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_public_key_bits, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_public_key, [c_void_p, c_void_p])
      ffi_api(dll.botan_x509_cert_get_issuer_dn,
              [c_void_p, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_get_subject_dn,
              [c_void_p, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_to_string, [c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_x509_cert_allowed_usage, [c_void_p, c_uint])
      ffi_api(dll.botan_x509_cert_hostname_match, [c_void_p, c_char_p], [-1])
      ffi_api(dll.botan_x509_cert_verify,
              [POINTER(c_int), c_void_p, c_void_p, c_size_t, c_void_p, c_size_t, c_char_p, c_size_t, c_char_p, c_uint64])
  
      dll.botan_x509_cert_validation_status.argtypes = [c_int]
      dll.botan_x509_cert_validation_status.restype = c_char_p
  
      # X509 CRL
      ffi_api(dll.botan_x509_crl_load, [c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_x509_crl_load_file, [c_void_p, c_char_p])
      ffi_api(dll.botan_x509_crl_destroy, [c_void_p])
      ffi_api(dll.botan_x509_is_revoked, [c_void_p, c_void_p], [-1])
      ffi_api(dll.botan_x509_cert_verify_with_crl,
              [POINTER(c_int), c_void_p, c_void_p, c_size_t, c_void_p, c_size_t, c_void_p, c_size_t, c_char_p, c_size_t, c_char_p, c_uint64])
  
      ffi_api(dll.botan_key_wrap3394,
              [c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_key_unwrap3394,
              [c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
  
      #  HOTP
      ffi_api(dll.botan_hotp_init,
              [c_void_p, c_char_p, c_size_t, c_char_p, c_size_t])
      ffi_api(dll.botan_hotp_destroy, [c_void_p])
      ffi_api(dll.botan_hotp_generate, [c_void_p, POINTER(c_uint32), c_uint64])
      ffi_api(dll.botan_hotp_check,
              [c_void_p, POINTER(c_uint64), c_uint32, c_uint64, c_size_t])
  
      #  TOTP
      ffi_api(dll.botan_totp_init,
              [c_void_p, c_char_p, c_size_t, c_char_p, c_size_t, c_size_t])
      ffi_api(dll.botan_totp_destroy, [c_void_p])
      ffi_api(dll.botan_totp_generate, [c_void_p, POINTER(c_uint32), c_uint64])
      ffi_api(dll.botan_totp_check, [c_void_p, c_uint32, c_uint64, c_size_t])
  
      #  FPE
      ffi_api(dll.botan_fpe_fe1_init,
              [c_void_p, c_void_p, c_char_p, c_size_t, c_size_t, c_uint32])
      ffi_api(dll.botan_fpe_destroy, [c_void_p])
      ffi_api(dll.botan_fpe_encrypt, [c_void_p, c_void_p, c_char_p, c_size_t])
      ffi_api(dll.botan_fpe_decrypt, [c_void_p, c_void_p, c_char_p, c_size_t])
  
      # SRP6-a
      ffi_api(dll.botan_srp6_server_session_init, [c_void_p])
      ffi_api(dll.botan_srp6_server_session_destroy, [c_void_p])
      ffi_api(dll.botan_srp6_server_session_step1,
              [c_void_p, c_char_p, c_size_t, c_char_p, c_char_p, c_void_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_srp6_server_session_step2,
              [c_void_p, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_generate_srp6_verifier,
              [c_char_p, c_char_p, c_char_p, c_size_t, c_char_p, c_char_p, c_char_p, POINTER(c_size_t)])
      ffi_api(dll.botan_srp6_client_agree,
              [c_char_p, c_char_p, c_char_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_void_p,
               c_char_p, POINTER(c_size_t), c_char_p, POINTER(c_size_t)])
  
      return dll
  
  #
  # Load the DLL and set prototypes on it
  #
  _DLL = _set_prototypes(_load_botan_dll(BOTAN_FFI_VERSION))
  
  #
  # Internal utilities
  #
  def _call_fn_returning_sz(fn):
      sz = c_size_t(0)
      fn(byref(sz))
      return int(sz.value)
  
  def _call_fn_returning_vec(guess, fn):
  
      buf = create_string_buffer(guess)
      buf_len = c_size_t(len(buf))
  
      rc = fn(buf, byref(buf_len))
      if rc == -10 and buf_len.value > len(buf):
          return _call_fn_returning_vec(buf_len.value, fn)
  
      assert buf_len.value <= len(buf)
      return buf.raw[0:int(buf_len.value)]
  
  def _call_fn_returning_vec_pair(guess1, guess2, fn):
  
      buf1 = create_string_buffer(guess1)
      buf1_len = c_size_t(len(buf1))
  
      buf2 = create_string_buffer(guess2)
      buf2_len = c_size_t(len(buf2))
  
      rc = fn(buf1, byref(buf1_len), buf2, byref(buf2_len))
      if rc == -10:
          if buf1_len.value > len(buf1):
              guess1 = buf1_len.value
          if buf2_len.value > len(buf2):
              guess2 = buf2_len.value
          return _call_fn_returning_vec_pair(guess1, guess2, fn)
  
      assert buf1_len.value <= len(buf1)
      assert buf2_len.value <= len(buf2)
      return (buf1.raw[0:int(buf1_len.value)], buf2.raw[0:int(buf2_len.value)])
  
  def _call_fn_returning_str(guess, fn):
      # Assumes that anything called with this is returning plain ASCII strings
      # (base64 data, algorithm names, etc)
      v = _call_fn_returning_vec(guess, fn)
      return v.decode('ascii')[:-1]
  
  def _ctype_str(s):
      if s is None:
          return None
      assert isinstance(s, str)
      if version_info[0] < 3:
          return s
      else:
          return s.encode('utf-8')
  
  def _ctype_to_str(s):
      if version_info[0] < 3:
          return s.encode('utf-8')
      else:
          return s.decode('utf-8')
  
  def _ctype_bits(s):
      if version_info[0] < 3:
          if isinstance(s, str):
              return s
          elif isinstance(s, unicode): # pylint: disable=undefined-variable
              return s.decode('utf-8')
          else:
              raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
      else:
          if isinstance(s, bytes):
              return s
          elif isinstance(s, str):
              return s.encode('utf-8')
          else:
              raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
  
  def _ctype_bufout(buf):
      if version_info[0] < 3:
          return str(buf.raw)
      else:
          return buf.raw
  
  def _hex_encode(buf):
      return hexlify(buf).decode('ascii')
  
  #
  # Versioning
  #
  def version_major():
      return int(_DLL.botan_version_major())
  
  def version_minor():
      return int(_DLL.botan_version_minor())
  
  def version_patch():
      return int(_DLL.botan_version_patch())
  
  def ffi_api_version():
      return int(_DLL.botan_ffi_api_version())
  
  def version_string():
      return _DLL.botan_version_string().decode('ascii')
  
  #
  # Utilities
  #
  def const_time_compare(x, y):
      len_x = len(x)
      len_y = len(y)
      if len_x != len_y:
          return False
      rc = _DLL.botan_constant_time_compare(_ctype_bits(x), _ctype_bits(y), c_size_t(len_x))
      return rc == 0
  
  #
  # RNG
  #
  class RandomNumberGenerator(object):
      # Can also use type "system"
      def __init__(self, rng_type='system'):
          self.__obj = c_void_p(0)
          _DLL.botan_rng_init(byref(self.__obj), _ctype_str(rng_type))
  
      def __del__(self):
          _DLL.botan_rng_destroy(self.__obj)
  
      def handle_(self):
          return self.__obj
  
      def reseed(self, bits=256):
          _DLL.botan_rng_reseed(self.__obj, bits)
  
      def reseed_from_rng(self, source_rng, bits=256):
          _DLL.botan_rng_reseed_from_rng(self.__obj, source_rng.handle_(), bits)
  
      def add_entropy(self, seed):
          _DLL.botan_rng_add_entropy(self.__obj, _ctype_bits(seed), len(seed))
  
      def get(self, length):
          out = create_string_buffer(length)
          l = c_size_t(length)
          _DLL.botan_rng_get(self.__obj, out, l)
          return _ctype_bufout(out)
  
  #
  # Block cipher
  #
  class BlockCipher(object):
      def __init__(self, algo):
  
          if isinstance(algo, c_void_p):
              self.__obj = algo
          else:
              flags = c_uint32(0) # always zero in this API version
              self.__obj = c_void_p(0)
              _DLL.botan_block_cipher_init(byref(self.__obj), _ctype_str(algo), flags)
  
          min_keylen = c_size_t(0)
          max_keylen = c_size_t(0)
          mod_keylen = c_size_t(0)
          _DLL.botan_block_cipher_get_keyspec(self.__obj, byref(min_keylen), byref(max_keylen), byref(mod_keylen))
  
          self.__min_keylen = min_keylen.value
          self.__max_keylen = max_keylen.value
          self.__mod_keylen = mod_keylen.value
  
          self.__block_size = _DLL.botan_block_cipher_block_size(self.__obj)
  
      def __del__(self):
          _DLL.botan_block_cipher_destroy(self.__obj)
  
      def set_key(self, key):
          _DLL.botan_block_cipher_set_key(self.__obj, key, len(key))
  
      def encrypt(self, pt):
          if len(pt) % self.block_size() != 0:
              raise Exception("Invalid input must be multiple of block size")
  
          blocks = c_size_t(len(pt) // self.block_size())
          output = create_string_buffer(len(pt))
          _DLL.botan_block_cipher_encrypt_blocks(self.__obj, pt, output, blocks)
          return output
  
      def decrypt(self, ct):
          if len(ct) % self.block_size() != 0:
              raise Exception("Invalid input must be multiple of block size")
  
          blocks = c_size_t(len(ct) // self.block_size())
          output = create_string_buffer(len(ct))
          _DLL.botan_block_cipher_decrypt_blocks(self.__obj, ct, output, blocks)
          return output
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_block_cipher_name(self.__obj, b, bl))
  
      def clear(self):
          _DLL.botan_block_cipher_clear(self.__obj)
  
      def block_size(self):
          return self.__block_size
  
      def minimum_keylength(self):
          return self.__min_keylen
  
      def maximum_keylength(self):
          return self.__max_keylen
  
  
  #
  # Hash function
  #
  class HashFunction(object):
      def __init__(self, algo):
  
          if isinstance(algo, c_void_p):
              self.__obj = algo
          else:
              flags = c_uint32(0) # always zero in this API version
              self.__obj = c_void_p(0)
              _DLL.botan_hash_init(byref(self.__obj), _ctype_str(algo), flags)
  
          self.__output_length = _call_fn_returning_sz(lambda l: _DLL.botan_hash_output_length(self.__obj, l))
          self.__block_size = _call_fn_returning_sz(lambda l: _DLL.botan_hash_block_size(self.__obj, l))
  
      def __del__(self):
          _DLL.botan_hash_destroy(self.__obj)
  
      def copy_state(self):
          copy = c_void_p(0)
          _DLL.botan_hash_copy_state(byref(copy), self.__obj)
          return HashFunction(copy)
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_hash_name(self.__obj, b, bl))
  
      def clear(self):
          _DLL.botan_hash_clear(self.__obj)
  
      def output_length(self):
          return self.__output_length
  
      def block_size(self):
          return self.__block_size
  
      def update(self, x):
          _DLL.botan_hash_update(self.__obj, _ctype_bits(x), len(x))
  
      def final(self):
          out = create_string_buffer(self.output_length())
          _DLL.botan_hash_final(self.__obj, out)
          return _ctype_bufout(out)
  
  #
  # Message authentication codes
  #
  class MsgAuthCode(object):
      def __init__(self, algo):
          flags = c_uint32(0) # always zero in this API version
          self.__obj = c_void_p(0)
          _DLL.botan_mac_init(byref(self.__obj), _ctype_str(algo), flags)
  
          min_keylen = c_size_t(0)
          max_keylen = c_size_t(0)
          mod_keylen = c_size_t(0)
          _DLL.botan_mac_get_keyspec(self.__obj, byref(min_keylen), byref(max_keylen), byref(mod_keylen))
  
          self.__min_keylen = min_keylen.value
          self.__max_keylen = max_keylen.value
          self.__mod_keylen = mod_keylen.value
  
          output_length = c_size_t(0)
          _DLL.botan_mac_output_length(self.__obj, byref(output_length))
          self.__output_length = output_length.value
  
      def __del__(self):
          _DLL.botan_mac_destroy(self.__obj)
  
      def clear(self):
          _DLL.botan_mac_clear(self.__obj)
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_mac_name(self.__obj, b, bl))
  
      def output_length(self):
          return self.__output_length
  
      def minimum_keylength(self):
          return self.__min_keylen
  
      def maximum_keylength(self):
          return self.__max_keylen
  
      def set_key(self, key):
          _DLL.botan_mac_set_key(self.__obj, key, len(key))
  
      def update(self, x):
          _DLL.botan_mac_update(self.__obj, x, len(x))
  
      def final(self):
          out = create_string_buffer(self.output_length())
          _DLL.botan_mac_final(self.__obj, out)
          return _ctype_bufout(out)
  
  class SymmetricCipher(object):
      def __init__(self, algo, encrypt=True):
          flags = 0 if encrypt else 1
          self.__obj = c_void_p(0)
          _DLL.botan_cipher_init(byref(self.__obj), _ctype_str(algo), flags)
          self._is_cbc = algo.find('/CBC') > 0
          self._is_encrypt = encrypt
  
      def __del__(self):
          _DLL.botan_cipher_destroy(self.__obj)
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_cipher_name(self.__obj, b, bl))
  
      def default_nonce_length(self):
          l = c_size_t(0)
          _DLL.botan_cipher_get_default_nonce_length(self.__obj, byref(l))
          return l.value
  
      def update_granularity(self):
          l = c_size_t(0)
          _DLL.botan_cipher_get_update_granularity(self.__obj, byref(l))
          return l.value
  
      def key_length(self):
          kmin = c_size_t(0)
          kmax = c_size_t(0)
          _DLL.botan_cipher_query_keylen(self.__obj, byref(kmin), byref(kmax))
          return kmin.value, kmax.value
  
      def minimum_keylength(self):
          l = c_size_t(0)
          _DLL.botan_cipher_get_keyspec(self.__obj, byref(l), None, None)
          return l.value
  
      def maximum_keylength(self):
          l = c_size_t(0)
          _DLL.botan_cipher_get_keyspec(self.__obj, None, byref(l), None)
          return l.value
  
      def tag_length(self):
          l = c_size_t(0)
          _DLL.botan_cipher_get_tag_length(self.__obj, byref(l))
          return l.value
  
      def is_authenticated(self):
          return self.tag_length() > 0
  
      def valid_nonce_length(self, nonce_len):
          rc = _DLL.botan_cipher_valid_nonce_length(self.__obj, nonce_len)
          return rc == 1
  
      def reset(self):
          _DLL.botan_cipher_reset(self.__obj)
  
      def clear(self):
          _DLL.botan_cipher_clear(self.__obj)
  
      def set_key(self, key):
          _DLL.botan_cipher_set_key(self.__obj, key, len(key))
  
      def set_assoc_data(self, ad):
          _DLL.botan_cipher_set_associated_data(self.__obj, ad, len(ad))
  
      def start(self, nonce):
          _DLL.botan_cipher_start(self.__obj, nonce, len(nonce))
  
      def _update(self, txt, final):
  
          inp = txt if txt else ''
          inp_sz = c_size_t(len(inp))
          inp_consumed = c_size_t(0)
          extra_bytes = 0
          if final and self._is_encrypt:
              tag_len = self.tag_length()
              if tag_len > 0:
                  # AEADs don't expand beyond the tag
                  extra_bytes = tag_len
              elif self._is_cbc:
                  # Hack: the largest block size currently supported
                  extra_bytes = 64
          out = create_string_buffer(inp_sz.value + extra_bytes)
          out_sz = c_size_t(len(out))
          out_written = c_size_t(0)
          flags = c_uint32(1 if final else 0)
  
          _DLL.botan_cipher_update(self.__obj, flags,
                                   out, out_sz, byref(out_written),
                                   _ctype_bits(inp), inp_sz, byref(inp_consumed))
  
          # buffering not supported yet
          assert inp_consumed.value == inp_sz.value
          return out.raw[0:int(out_written.value)]
  
      def update(self, txt):
          return self._update(txt, False)
  
      def finish(self, txt=None):
          return self._update(txt, True)
  
  def bcrypt(passwd, rng_obj, work_factor=10):
      """
      Bcrypt password hashing
      """
      out_len = c_size_t(64)
      out = create_string_buffer(out_len.value)
      flags = c_uint32(0)
      _DLL.botan_bcrypt_generate(out, byref(out_len), _ctype_str(passwd),
                                 rng_obj.handle_(), c_size_t(work_factor), flags)
      b = out.raw[0:int(out_len.value)-1]
      if b[-1] == '\x00':
          b = b[:-1]
      return _ctype_to_str(b)
  
  def check_bcrypt(passwd, passwd_hash):
      rc = _DLL.botan_bcrypt_is_valid(_ctype_str(passwd), _ctype_str(passwd_hash))
      return rc == 0
  
  #
  # PBKDF
  #
  def pbkdf(algo, password, out_len, iterations=10000, salt=None):
      if salt is None:
          salt = RandomNumberGenerator().get(12)
  
      out_buf = create_string_buffer(out_len)
  
      _DLL.botan_pwdhash(_ctype_str(algo), iterations, 0, 0,
                         out_buf, out_len,
                         _ctype_str(password), len(password),
                         salt, len(salt))
      return (salt, iterations, out_buf.raw)
  
  def pbkdf_timed(algo, password, out_len, ms_to_run=300, salt=None):
      if salt is None:
          salt = RandomNumberGenerator().get(12)
  
      out_buf = create_string_buffer(out_len)
      iterations = c_size_t(0)
  
      _DLL.botan_pwdhash_timed(_ctype_str(algo), c_uint32(ms_to_run),
                               byref(iterations), None, None,
                               out_buf, out_len,
                               _ctype_str(password), len(password),
                               salt, len(salt))
      return (salt, iterations.value, out_buf.raw)
  
  #
  # Scrypt
  #
  def scrypt(out_len, password, salt, n=1024, r=8, p=8):
      out_buf = create_string_buffer(out_len)
      _DLL.botan_pwdhash(_ctype_str("Scrypt"), n, r, p,
                         out_buf, out_len,
                         _ctype_str(password), len(password),
                         _ctype_bits(salt), len(salt))
  
      return out_buf.raw
  
  #
  # KDF
  #
  def kdf(algo, secret, out_len, salt, label):
      out_buf = create_string_buffer(out_len)
      out_sz = c_size_t(out_len)
      _DLL.botan_kdf(_ctype_str(algo), out_buf, out_sz,
                     secret, len(secret),
                     salt, len(salt),
                     label, len(label))
      return out_buf.raw[0:int(out_sz.value)]
  
  #
  # Public key
  #
  class PublicKey(object): # pylint: disable=invalid-name
  
      def __init__(self, obj=c_void_p(0)):
          self.__obj = obj
  
      @classmethod
      def load(cls, val):
          obj = c_void_p(0)
          _DLL.botan_pubkey_load(byref(obj), _ctype_bits(val), len(val))
          return PublicKey(obj)
  
      @classmethod
      def load_rsa(cls, n, e):
          obj = c_void_p(0)
          n = MPI(n)
          e = MPI(e)
          _DLL.botan_pubkey_load_rsa(byref(obj), n.handle_(), e.handle_())
          return PublicKey(obj)
  
      @classmethod
      def load_dsa(cls, p, q, g, y):
          obj = c_void_p(0)
          p = MPI(p)
          q = MPI(q)
          g = MPI(g)
          y = MPI(y)
          _DLL.botan_pubkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_())
          return PublicKey(obj)
  
      @classmethod
      def load_dh(cls, p, g, y):
          obj = c_void_p(0)
          p = MPI(p)
          g = MPI(g)
          y = MPI(y)
          _DLL.botan_pubkey_load_dh(byref(obj), p.handle_(), g.handle_(), y.handle_())
          return PublicKey(obj)
  
      @classmethod
      def load_elgamal(cls, p, q, g, y):
          obj = c_void_p(0)
          p = MPI(p)
          q = MPI(q)
          g = MPI(g)
          y = MPI(y)
          _DLL.botan_pubkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_())
          return PublicKey(obj)
  
      @classmethod
      def load_ecdsa(cls, curve, pub_x, pub_y):
          obj = c_void_p(0)
          pub_x = MPI(pub_x)
          pub_y = MPI(pub_y)
          _DLL.botan_pubkey_load_ecdsa(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve))
          return PublicKey(obj)
  
      @classmethod
      def load_ecdh(cls, curve, pub_x, pub_y):
          obj = c_void_p(0)
          pub_x = MPI(pub_x)
          pub_y = MPI(pub_y)
          _DLL.botan_pubkey_load_ecdh(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve))
          return PublicKey(obj)
  
      @classmethod
      def load_sm2(cls, curve, pub_x, pub_y):
          obj = c_void_p(0)
          pub_x = MPI(pub_x)
          pub_y = MPI(pub_y)
          _DLL.botan_pubkey_load_sm2(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve))
          return PublicKey(obj)
  
      def __del__(self):
          _DLL.botan_pubkey_destroy(self.__obj)
  
      def handle_(self):
          return self.__obj
  
      def check_key(self, rng_obj, strong=True):
          flags = 1 if strong else 0
          rc = _DLL.botan_pubkey_check_key(self.__obj, rng_obj.handle_(), flags)
          return rc == 0
  
      def estimated_strength(self):
          r = c_size_t(0)
          _DLL.botan_pubkey_estimated_strength(self.__obj, byref(r))
          return r.value
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_pubkey_algo_name(self.__obj, b, bl))
  
      def export(self, pem=False):
          if pem:
              return _call_fn_returning_str(4096, lambda b, bl: _DLL.botan_pubkey_export(self.__obj, b, bl, 1))
          else:
              return _call_fn_returning_vec(4096, lambda b, bl: _DLL.botan_pubkey_export(self.__obj, b, bl, 0))
  
      def encoding(self, pem=False):
          return self.export(pem)
  
      def to_der(self):
          return self.export(False)
  
      def to_pem(self):
          return self.export(True)
  
      def fingerprint(self, hash_algorithm='SHA-256'):
          n = HashFunction(hash_algorithm).output_length()
          buf = create_string_buffer(n)
          buf_len = c_size_t(n)
  
          _DLL.botan_pubkey_fingerprint(self.__obj, _ctype_str(hash_algorithm), buf, byref(buf_len))
          return _hex_encode(buf[0:int(buf_len.value)])
  
      def get_field(self, field_name):
          v = MPI()
          _DLL.botan_pubkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name))
          return int(v)
  
  #
  # Private Key
  #
  class PrivateKey(object):
  
      def __init__(self, obj=c_void_p(0)):
          self.__obj = obj
  
      @classmethod
      def load(cls, val, passphrase=""):
          obj = c_void_p(0)
          rng_obj = c_void_p(0) # unused in recent versions
          _DLL.botan_privkey_load(byref(obj), rng_obj, _ctype_bits(val), len(val), _ctype_str(passphrase))
          return PrivateKey(obj)
  
      @classmethod
      def create(cls, algo, params, rng_obj):
          if algo == 'rsa':
              algo = 'RSA'
              params = "%d" % (params)
          elif algo == 'ecdsa':
              algo = 'ECDSA'
          elif algo in ['ecdh', 'ECDH']:
              if params == 'curve25519':
                  algo = 'Curve25519'
                  params = ''
              else:
                  algo = 'ECDH'
          elif algo in ['mce', 'mceliece']:
              algo = 'McEliece'
              params = "%d,%d" % (params[0], params[1])
  
          obj = c_void_p(0)
          _DLL.botan_privkey_create(byref(obj), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
          return PrivateKey(obj)
  
      @classmethod
      def load_rsa(cls, p, q, e):
          obj = c_void_p(0)
          p = MPI(p)
          q = MPI(q)
          e = MPI(e)
          _DLL.botan_privkey_load_rsa(byref(obj), p.handle_(), q.handle_(), e.handle_())
          return PrivateKey(obj)
  
      @classmethod
      def load_dsa(cls, p, q, g, x):
          obj = c_void_p(0)
          p = MPI(p)
          q = MPI(q)
          g = MPI(g)
          x = MPI(x)
          _DLL.botan_privkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_())
          return PrivateKey(obj)
  
      @classmethod
      def load_dh(cls, p, g, x):
          obj = c_void_p(0)
          p = MPI(p)
          g = MPI(g)
          x = MPI(x)
          _DLL.botan_privkey_load_dh(byref(obj), p.handle_(), g.handle_(), x.handle_())
          return PrivateKey(obj)
  
      @classmethod
      def load_elgamal(cls, p, q, g, x):
          obj = c_void_p(0)
          p = MPI(p)
          q = MPI(q)
          g = MPI(g)
          x = MPI(x)
          _DLL.botan_privkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_())
          return PrivateKey(obj)
  
      @classmethod
      def load_ecdsa(cls, curve, x):
          obj = c_void_p(0)
          x = MPI(x)
          _DLL.botan_privkey_load_ecdsa(byref(obj), x.handle_(), _ctype_str(curve))
          return PrivateKey(obj)
  
      @classmethod
      def load_ecdh(cls, curve, x):
          obj = c_void_p(0)
          x = MPI(x)
          _DLL.botan_privkey_load_ecdh(byref(obj), x.handle_(), _ctype_str(curve))
          return PrivateKey(obj)
  
      @classmethod
      def load_sm2(cls, curve, x):
          obj = c_void_p(0)
          x = MPI(x)
          _DLL.botan_privkey_load_sm2(byref(obj), x.handle_(), _ctype_str(curve))
          return PrivateKey(obj)
  
      def __del__(self):
          _DLL.botan_privkey_destroy(self.__obj)
  
      def handle_(self):
          return self.__obj
  
      def check_key(self, rng_obj, strong=True):
          flags = 1 if strong else 0
          rc = _DLL.botan_privkey_check_key(self.__obj, rng_obj.handle_(), flags)
          return rc == 0
  
      def algo_name(self):
          return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_privkey_algo_name(self.__obj, b, bl))
  
      def get_public_key(self):
          pub = c_void_p(0)
          _DLL.botan_privkey_export_pubkey(byref(pub), self.__obj)
          return PublicKey(pub)
  
      def to_der(self):
          return self.export(False)
  
      def to_pem(self):
          return self.export(True)
  
      def export(self, pem=False):
          if pem:
              return _call_fn_returning_str(4096, lambda b, bl: _DLL.botan_privkey_export(self.__obj, b, bl, 1))
          else:
              return _call_fn_returning_vec(4096, lambda b, bl: _DLL.botan_privkey_export(self.__obj, b, bl, 0))
  
      def export_encrypted(self, passphrase, rng_obj, pem=False, msec=300, cipher=None, pbkdf=None): # pylint: disable=redefined-outer-name
          flags = 1 if pem else 0
          msec = c_uint32(msec)
          _iters = c_size_t(0)
  
          cb = lambda b, bl: _DLL.botan_privkey_export_encrypted_pbkdf_msec(
              self.__obj, b, bl, rng_obj.handle_(), _ctype_str(passphrase),
              msec, byref(_iters), _ctype_str(cipher), _ctype_str(pbkdf), flags)
  
          if pem:
              return _call_fn_returning_str(8192, cb)
          else:
              return _call_fn_returning_vec(4096, cb)
  
      def get_field(self, field_name):
          v = MPI()
          _DLL.botan_privkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name))
          return int(v)
  
  class PKEncrypt(object):
      def __init__(self, key, padding):
          self.__obj = c_void_p(0)
          flags = c_uint32(0) # always zero in this ABI
          _DLL.botan_pk_op_encrypt_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
  
      def __del__(self):
          _DLL.botan_pk_op_encrypt_destroy(self.__obj)
  
      def encrypt(self, msg, rng_obj):
          outbuf_sz = c_size_t(0)
          _DLL.botan_pk_op_encrypt_output_length(self.__obj, len(msg), byref(outbuf_sz))
          outbuf = create_string_buffer(outbuf_sz.value)
          _DLL.botan_pk_op_encrypt(self.__obj, rng_obj.handle_(), outbuf, byref(outbuf_sz), msg, len(msg))
          return outbuf.raw[0:int(outbuf_sz.value)]
  
  
  class PKDecrypt(object):
      def __init__(self, key, padding):
          self.__obj = c_void_p(0)
          flags = c_uint32(0) # always zero in this ABI
          _DLL.botan_pk_op_decrypt_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
  
      def __del__(self):
          _DLL.botan_pk_op_decrypt_destroy(self.__obj)
  
      def decrypt(self, msg):
          outbuf_sz = c_size_t(0)
          _DLL.botan_pk_op_decrypt_output_length(self.__obj, len(msg), byref(outbuf_sz))
          outbuf = create_string_buffer(outbuf_sz.value)
          _DLL.botan_pk_op_decrypt(self.__obj, outbuf, byref(outbuf_sz), _ctype_bits(msg), len(msg))
          return outbuf.raw[0:int(outbuf_sz.value)]
  
  class PKSign(object): # pylint: disable=invalid-name
      def __init__(self, key, padding, der=False):
          self.__obj = c_void_p(0)
          flags = c_uint32(1) if der else c_uint32(0)
          _DLL.botan_pk_op_sign_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
  
      def __del__(self):
          _DLL.botan_pk_op_sign_destroy(self.__obj)
  
      def update(self, msg):
          _DLL.botan_pk_op_sign_update(self.__obj, _ctype_str(msg), len(msg))
  
      def finish(self, rng_obj):
          outbuf_sz = c_size_t(0)
          _DLL.botan_pk_op_sign_output_length(self.__obj, byref(outbuf_sz))
          outbuf = create_string_buffer(outbuf_sz.value)
          _DLL.botan_pk_op_sign_finish(self.__obj, rng_obj.handle_(), outbuf, byref(outbuf_sz))
          return outbuf.raw[0:int(outbuf_sz.value)]
  
  class PKVerify(object):
      def __init__(self, key, padding, der=False):
          self.__obj = c_void_p(0)
          flags = c_uint32(1) if der else c_uint32(0)
          _DLL.botan_pk_op_verify_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
  
      def __del__(self):
          _DLL.botan_pk_op_verify_destroy(self.__obj)
  
      def update(self, msg):
          _DLL.botan_pk_op_verify_update(self.__obj, _ctype_bits(msg), len(msg))
  
      def check_signature(self, signature):
          rc = _DLL.botan_pk_op_verify_finish(self.__obj, _ctype_bits(signature), len(signature))
          if rc == 0:
              return True
          return False
  
  class PKKeyAgreement(object):
      def __init__(self, key, kdf_name):
          self.__obj = c_void_p(0)
          flags = c_uint32(0) # always zero in this ABI
          _DLL.botan_pk_op_key_agreement_create(byref(self.__obj), key.handle_(), _ctype_str(kdf_name), flags)
  
          self.m_public_value = _call_fn_returning_vec(
              0, lambda b, bl: _DLL.botan_pk_op_key_agreement_export_public(key.handle_(), b, bl))
  
      def __del__(self):
          _DLL.botan_pk_op_key_agreement_destroy(self.__obj)
  
      def public_value(self):
          return self.m_public_value
  
      def underlying_output_length(self):
          out_len = c_size_t(0)
          _DLL.botan_pk_op_key_agreement_size(self.__obj, byref(out_len))
          return out_len.value
  
      def agree(self, other, key_len, salt):
          if key_len == 0:
              key_len = self.underlying_output_length()
          return _call_fn_returning_vec(key_len, lambda b, bl:
                                        _DLL.botan_pk_op_key_agreement(self.__obj, b, bl,
                                                                       other, len(other),
                                                                       salt, len(salt)))
  
  
  def _load_buf_or_file(filename, buf, file_fn, buf_fn):
      if filename is None and buf is None:
          raise BotanException("No filename or buf given")
      if filename is not None and buf is not None:
          raise BotanException("Both filename and buf given")
  
      obj = c_void_p(0)
  
      if filename is not None:
          file_fn(byref(obj), _ctype_str(filename))
      elif buf is not None:
          buf_fn(byref(obj), _ctype_bits(buf), len(buf))
  
      return obj
  
  
  #
  # X.509 certificates
  #
  class X509Cert(object): # pylint: disable=invalid-name
      def __init__(self, filename=None, buf=None):
          self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_cert_load_file, _DLL.botan_x509_cert_load)
  
      def __del__(self):
          _DLL.botan_x509_cert_destroy(self.__obj)
  
      def time_starts(self):
          starts = _call_fn_returning_str(
              16, lambda b, bl: _DLL.botan_x509_cert_get_time_starts(self.__obj, b, bl))
          if len(starts) == 13:
              # UTC time
              struct_time = strptime(starts, "%y%m%d%H%M%SZ")
          elif len(starts) == 15:
              # Generalized time
              struct_time = strptime(starts, "%Y%m%d%H%M%SZ")
          else:
              raise BotanException("Unexpected date/time format for x509 start time")
  
          return datetime.fromtimestamp(mktime(struct_time))
  
      def time_expires(self):
          expires = _call_fn_returning_str(
              16, lambda b, bl: _DLL.botan_x509_cert_get_time_expires(self.__obj, b, bl))
          if len(expires) == 13:
              # UTC time
              struct_time = strptime(expires, "%y%m%d%H%M%SZ")
          elif len(expires) == 15:
              # Generalized time
              struct_time = strptime(expires, "%Y%m%d%H%M%SZ")
          else:
              raise BotanException("Unexpected date/time format for x509 expire time")
  
          return datetime.fromtimestamp(mktime(struct_time))
  
      def to_string(self):
          return _call_fn_returning_str(
              4096, lambda b, bl: _DLL.botan_x509_cert_to_string(self.__obj, b, bl))
  
      def fingerprint(self, hash_algo='SHA-256'):
          n = HashFunction(hash_algo).output_length() * 3
          return _call_fn_returning_str(
              n, lambda b, bl: _DLL.botan_x509_cert_get_fingerprint(self.__obj, _ctype_str(hash_algo), b, bl))
  
      def serial_number(self):
          return _call_fn_returning_vec(
              32, lambda b, bl: _DLL.botan_x509_cert_get_serial_number(self.__obj, b, bl))
  
      def authority_key_id(self):
          return _call_fn_returning_vec(
              32, lambda b, bl: _DLL.botan_x509_cert_get_authority_key_id(self.__obj, b, bl))
  
      def subject_key_id(self):
          return _call_fn_returning_vec(
              32, lambda b, bl: _DLL.botan_x509_cert_get_subject_key_id(self.__obj, b, bl))
  
      def subject_public_key_bits(self):
          return _call_fn_returning_vec(
              512, lambda b, bl: _DLL.botan_x509_cert_get_public_key_bits(self.__obj, b, bl))
  
      def subject_public_key(self):
          pub = c_void_p(0)
          _DLL.botan_x509_cert_get_public_key(self.__obj, byref(pub))
          return PublicKey(pub)
  
      def subject_dn(self, key, index):
          return _call_fn_returning_str(
              0, lambda b, bl: _DLL.botan_x509_cert_get_subject_dn(self.__obj, _ctype_str(key), index, b, bl))
  
      def issuer_dn(self, key, index):
          return _call_fn_returning_str(
              0, lambda b, bl: _DLL.botan_x509_cert_get_issuer_dn(self.__obj, _ctype_str(key), index, b, bl))
  
      def hostname_match(self, hostname):
          rc = _DLL.botan_x509_cert_hostname_match(self.__obj, _ctype_str(hostname))
          return rc == 0
  
      def not_before(self):
          time = c_uint64(0)
          _DLL.botan_x509_cert_not_before(self.__obj, byref(time))
          return time.value
  
      def not_after(self):
          time = c_uint64(0)
          _DLL.botan_x509_cert_not_after(self.__obj, byref(time))
          return time.value
  
      def allowed_usage(self, usage_list):
          usage_values = {"NO_CONSTRAINTS": 0,
                          "DIGITAL_SIGNATURE": 32768,
                          "NON_REPUDIATION": 16384,
                          "KEY_ENCIPHERMENT": 8192,
                          "DATA_ENCIPHERMENT": 4096,
                          "KEY_AGREEMENT": 2048,
                          "KEY_CERT_SIGN": 1024,
                          "CRL_SIGN": 512,
                          "ENCIPHER_ONLY": 256,
                          "DECIPHER_ONLY": 128}
          usage = 0
          for u in usage_list:
              if u not in usage_values:
                  return False
              usage += usage_values[u]
  
          rc = _DLL.botan_x509_cert_allowed_usage(self.__obj, c_uint(usage))
          return rc == 0
  
      def handle_(self):
          return self.__obj
  
      def verify(self,
                 intermediates=None,
                 trusted=None,
                 trusted_path=None,
                 required_strength=0,
                 hostname=None,
                 reference_time=0,
                 crls=None):
          #pylint: disable=too-many-locals
  
          if intermediates is not None:
              c_intermediates = len(intermediates) * c_void_p
              arr_intermediates = c_intermediates()
              for i, ca in enumerate(intermediates):
                  arr_intermediates[i] = ca.handle_()
              len_intermediates = c_size_t(len(intermediates))
          else:
              arr_intermediates = c_void_p(0)
              len_intermediates = c_size_t(0)
  
          if trusted is not None:
              c_trusted = len(trusted) * c_void_p
              arr_trusted = c_trusted()
              for i, ca in enumerate(trusted):
                  arr_trusted[i] = ca.handle_()
              len_trusted = c_size_t(len(trusted))
          else:
              arr_trusted = c_void_p(0)
              len_trusted = c_size_t(0)
  
          if crls is not None:
              c_crls = len(crls) * c_void_p
              arr_crls = c_crls()
              for i, crl in enumerate(crls):
                  arr_crls[i] = crl.handle_()
              len_crls = c_size_t(len(crls))
          else:
              arr_crls = c_void_p(0)
              len_crls = c_size_t(0)
  
          error_code = c_int(0)
  
          _DLL.botan_x509_cert_verify_with_crl(byref(error_code),
                                               self.__obj,
                                               byref(arr_intermediates),
                                               len_intermediates,
                                               byref(arr_trusted),
                                               len_trusted,
                                               byref(arr_crls),
                                               len_crls,
                                               _ctype_str(trusted_path),
                                               c_size_t(required_strength),
                                               _ctype_str(hostname),
                                               c_uint64(reference_time))
  
          return error_code.value
  
      @classmethod
      def validation_status(cls, error_code):
          return _ctype_to_str(_DLL.botan_x509_cert_validation_status(c_int(error_code)))
  
      def is_revoked(self, crl):
          rc = _DLL.botan_x509_is_revoked(crl.handle_(), self.__obj)
          return rc == 0
  
  
  #
  # X.509 Certificate revocation lists
  #
  class X509CRL(object):
      def __init__(self, filename=None, buf=None):
          self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load)
  
      def __del__(self):
          _DLL.botan_x509_crl_destroy(self.__obj)
  
      def handle_(self):
          return self.__obj
  
  
  class MPI(object): # pylint: disable=too-many-public-methods
  
      def __init__(self, initial_value=None, radix=None):
  
          self.__obj = c_void_p(0)
          _DLL.botan_mp_init(byref(self.__obj))
  
          if initial_value is None:
              pass # left as zero
          elif isinstance(initial_value, MPI):
              _DLL.botan_mp_set_from_mp(self.__obj, initial_value.handle_())
          elif radix is not None:
              _DLL.botan_mp_set_from_radix_str(self.__obj, _ctype_str(initial_value), c_size_t(radix))
          elif isinstance(initial_value, str):
              _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(initial_value))
          else:
              # For int or long (or whatever else), try converting to string:
              _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(str(initial_value)))
  
      @classmethod
      def random(cls, rng_obj, bits):
          bn = MPI()
          _DLL.botan_mp_rand_bits(bn.handle_(), rng_obj.handle_(), c_size_t(bits))
          return bn
  
      @classmethod
      def random_range(cls, rng_obj, lower, upper):
          bn = MPI()
          _DLL.botan_mp_rand_range(bn.handle_(), rng_obj.handle_(), lower.handle_(), upper.handle_())
          return bn
  
      def __del__(self):
          _DLL.botan_mp_destroy(self.__obj)
  
      def handle_(self):
          return self.__obj
  
      def __int__(self):
          out = create_string_buffer(2*self.byte_count() + 3)
          _DLL.botan_mp_to_hex(self.__obj, out)
          return int(out.value, 16)
  
      def __repr__(self):
          # Should have a better size estimate than this ...
          out_len = c_size_t(self.bit_count() // 2)
          out = create_string_buffer(out_len.value)
  
          _DLL.botan_mp_to_str(self.__obj, c_uint8(10), out, byref(out_len))
  
          out = out.raw[0:int(out_len.value)]
          if out[-1] == '\x00':
              out = out[:-1]
              s = _ctype_to_str(out)
          if s[0] == '0':
              return s[1:]
          else:
              return s
  
      def to_bytes(self):
          byte_count = self.byte_count()
          out_len = c_size_t(byte_count)
          out = create_string_buffer(out_len.value)
          _DLL.botan_mp_to_bin(self.__obj, out, byref(out_len))
          assert out_len.value == byte_count
          return out
  
      def is_negative(self):
          rc = _DLL.botan_mp_is_negative(self.__obj)
          return rc == 1
  
      def is_positive(self):
          rc = _DLL.botan_mp_is_positive(self.__obj)
          return rc == 1
  
      def is_zero(self):
          rc = _DLL.botan_mp_is_zero(self.__obj)
          return rc == 1
  
      def is_odd(self):
          return self.get_bit(0) == 1
  
      def is_even(self):
          return self.get_bit(0) == 0
  
      def flip_sign(self):
          _DLL.botan_mp_flip_sign(self.__obj)
  
      def cmp(self, other):
          r = c_int(0)
          _DLL.botan_mp_cmp(byref(r), self.__obj, other.handle_())
          return r.value
  
      def __hash__(self):
          return hash(self.to_bytes())
  
      def __eq__(self, other):
          return self.cmp(other) == 0
  
      def __ne__(self, other):
          return self.cmp(other) != 0
  
      def __lt__(self, other):
          return self.cmp(other) < 0
  
      def __le__(self, other):
          return self.cmp(other) <= 0
  
      def __gt__(self, other):
          return self.cmp(other) > 0
  
      def __ge__(self, other):
          return self.cmp(other) >= 0
  
      def __add__(self, other):
          r = MPI()
          _DLL.botan_mp_add(r.handle_(), self.__obj, other.handle_())
          return r
  
      def __iadd__(self, other):
          _DLL.botan_mp_add(self.__obj, self.__obj, other.handle_())
          return self
  
      def __sub__(self, other):
          r = MPI()
          _DLL.botan_mp_sub(r.handle_(), self.__obj, other.handle_())
          return r
  
      def __isub__(self, other):
          _DLL.botan_mp_sub(self.__obj, self.__obj, other.handle_())
          return self
  
      def __mul__(self, other):
          r = MPI()
          _DLL.botan_mp_mul(r.handle_(), self.__obj, other.handle_())
          return r
  
      def __imul__(self, other):
          _DLL.botan_mp_mul(self.__obj, self.__obj, other.handle_())
          return self
  
      def __divmod__(self, other):
          d = MPI()
          q = MPI()
          _DLL.botan_mp_div(d.handle_(), q.handle_(), self.__obj, other.handle_())
          return (d, q)
  
      def __mod__(self, other):
          d = MPI()
          q = MPI()
          _DLL.botan_mp_div(d.handle_(), q.handle_(), self.__obj, other.handle_())
          return q
  
      def __lshift__(self, shift):
          shift = c_size_t(shift)
          r = MPI()
          _DLL.botan_mp_lshift(r.handle_(), self.__obj, shift)
          return r
  
      def __ilshift__(self, shift):
          shift = c_size_t(shift)
          _DLL.botan_mp_lshift(self.__obj, self.__obj, shift)
          return self
  
      def __rshift__(self, shift):
          shift = c_size_t(shift)
          r = MPI()
          _DLL.botan_mp_rshift(r.handle_(), self.__obj, shift)
          return r
  
      def __irshift__(self, shift):
          shift = c_size_t(shift)
          _DLL.botan_mp_rshift(self.__obj, self.__obj, shift)
          return self
  
      def mod_mul(self, other, modulus):
          r = MPI()
          _DLL.botan_mp_mod_mul(r.handle_(), self.__obj, other.handle_(), modulus.handle_())
          return r
  
      def gcd(self, other):
          r = MPI()
          _DLL.botan_mp_gcd(r.handle_(), self.__obj, other.handle_())
          return r
  
      def pow_mod(self, exponent, modulus):
          r = MPI()
          _DLL.botan_mp_powmod(r.handle_(), self.__obj, exponent.handle_(), modulus.handle_())
          return r
  
      def is_prime(self, rng_obj, prob=128):
          return _DLL.botan_mp_is_prime(self.__obj, rng_obj.handle_(), c_size_t(prob)) == 1
  
      def inverse_mod(self, modulus):
          r = MPI()
          _DLL.botan_mp_mod_inverse(r.handle_(), self.__obj, modulus.handle_())
          return r
  
      def bit_count(self):
          b = c_size_t(0)
          _DLL.botan_mp_num_bits(self.__obj, byref(b))
          return b.value
  
      def byte_count(self):
          b = c_size_t(0)
          _DLL.botan_mp_num_bytes(self.__obj, byref(b))
          return b.value
  
      def get_bit(self, bit):
          return _DLL.botan_mp_get_bit(self.__obj, c_size_t(bit)) == 1
  
      def clear_bit(self, bit):
          _DLL.botan_mp_clear_bit(self.__obj, c_size_t(bit))
  
      def set_bit(self, bit):
          _DLL.botan_mp_set_bit(self.__obj, c_size_t(bit))
  
  class FormatPreservingEncryptionFE1(object):
  
      def __init__(self, modulus, key, rounds=5, compat_mode=False):
          flags = c_uint32(1 if compat_mode else 0)
          self.__obj = c_void_p(0)
          _DLL.botan_fpe_fe1_init(byref(self.__obj), modulus.handle_(), key, len(key), c_size_t(rounds), flags)
  
      def __del__(self):
          _DLL.botan_fpe_destroy(self.__obj)
  
      def encrypt(self, msg, tweak):
          r = MPI(msg)
          _DLL.botan_fpe_encrypt(self.__obj, r.handle_(), _ctype_bits(tweak), len(tweak))
          return r
  
      def decrypt(self, msg, tweak):
          r = MPI(msg)
          _DLL.botan_fpe_decrypt(self.__obj, r.handle_(), _ctype_bits(tweak), len(tweak))
          return r
  
  class HOTP(object):
      def __init__(self, key, digest="SHA-1", digits=6):
          self.__obj = c_void_p(0)
          _DLL.botan_hotp_init(byref(self.__obj), key, len(key), _ctype_str(digest), digits)
  
      def __del__(self):
          _DLL.botan_hotp_destroy(self.__obj)
  
      def generate(self, counter):
          code = c_uint32(0)
          _DLL.botan_hotp_generate(self.__obj, byref(code), counter)
          return code.value
  
      def check(self, code, counter, resync_range=0):
          next_ctr = c_uint64(0)
          rc = _DLL.botan_hotp_check(self.__obj, byref(next_ctr), code, counter, resync_range)
          if rc == 0:
              return (True, next_ctr.value)
          else:
              return (False, counter)
  
  class TOTP(object):
      def __init__(self, key, digest="SHA-1", digits=6, timestep=30):
          self.__obj = c_void_p(0)
          _DLL.botan_totp_init(byref(self.__obj), key, len(key), _ctype_str(digest), digits, timestep)
  
      def __del__(self):
          _DLL.botan_totp_destroy(self.__obj)
  
      def generate(self, timestamp=None):
          if timestamp is None:
              timestamp = int(system_time())
          code = c_uint32(0)
          _DLL.botan_totp_generate(self.__obj, byref(code), timestamp)
          return code.value
  
      def check(self, code, timestamp=None, acceptable_drift=0):
          if timestamp is None:
              timestamp = int(system_time())
          rc = _DLL.botan_totp_check(self.__obj, code, timestamp, acceptable_drift)
          if rc == 0:
              return True
          return False
  
  def nist_key_wrap(kek, key):
      output = create_string_buffer(len(key) + 8)
      out_len = c_size_t(len(output))
      _DLL.botan_key_wrap3394(key, len(key), kek, len(kek), output, byref(out_len))
      return output[0:int(out_len.value)]
  
  def nist_key_unwrap(kek, wrapped):
      output = create_string_buffer(len(wrapped))
      out_len = c_size_t(len(output))
      _DLL.botan_key_unwrap3394(wrapped, len(wrapped), kek, len(kek), output, byref(out_len))
      return output[0:int(out_len.value)]
  
  class Srp6ServerSession(object):
      __obj = c_void_p(0)
  
      def __init__(self):
          _DLL.botan_srp6_server_session_init(byref(self.__obj))
  
      def __del__(self):
          _DLL.botan_srp6_server_session_destroy(self.__obj)
  
      def step1(self, verifier, group, hsh, rng):
          return _call_fn_returning_vec(128,
                                        lambda b, bl:
                                        _DLL.botan_srp6_server_session_step1(self.__obj,
                                                                             verifier, len(verifier),
                                                                             _ctype_str(group), _ctype_str(hsh),
                                                                             rng.handle_(),
                                                                             b, bl))
  
      def step2(self, a):
          return _call_fn_returning_vec(128, lambda k, kl:
                                        _DLL.botan_srp6_server_session_step2(self.__obj,
                                                                             a, len(a),
                                                                             k, kl))
  
  def generate_srp6_verifier(identifier, password, salt, group, hsh):
      return _call_fn_returning_vec(128, lambda v, vl:
                                    _DLL.botan_generate_srp6_verifier(_ctype_str(identifier),
                                                                      _ctype_str(password),
                                                                      salt, len(salt),
                                                                      _ctype_str(group),
                                                                      _ctype_str(hsh),
                                                                      v, vl))
  
  def srp6_client_agree(username, password, group, hsh, salt, b, rng):
      return _call_fn_returning_vec_pair(128, 128, lambda a, al, k, kl:
                                         _DLL.botan_srp6_client_agree(_ctype_str(username),
                                                                      _ctype_str(password),
                                                                      _ctype_str(group),
                                                                      _ctype_str(hsh),
                                                                      salt, len(salt),
                                                                      b, len(b),
                                                                      rng.handle_(),
                                                                      a, al,
                                                                      k, kl))
  