text: /*
   * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
   *
   * Licensed under the OpenSSL license (the "License").  You may not use
   * this file except in compliance with the License.  You can obtain a copy
   * in the file LICENSE in the source distribution or at
   * https://www.openssl.org/source/license.html
   */
  
  #include <stdio.h>
  #include <string.h>
  #include BLIK_OPENSSL_V_openssl__crypto_h //original-code:<openssl/crypto.h>
  #include BLIK_OPENSSL_V_openssl__pem_h //original-code:<openssl/pem.h>
  #include BLIK_OPENSSL_U_internal__dso_h //original-code:"internal/dso.h"
  #include BLIK_OPENSSL_V_openssl__engine_h //original-code:<openssl/engine.h>
  #include BLIK_OPENSSL_V_openssl__ui_h //original-code:<openssl/ui.h>
  #include BLIK_OPENSSL_V_openssl__rand_h //original-code:<openssl/rand.h>
  #ifndef OPENSSL_NO_RSA
  # include BLIK_OPENSSL_V_openssl__rsa_h //original-code:<openssl/rsa.h>
  #endif
  #ifndef OPENSSL_NO_DH
  # include BLIK_OPENSSL_V_openssl__dh_h //original-code:<openssl/dh.h>
  #endif
  #include BLIK_OPENSSL_V_openssl__bn_h //original-code:<openssl/bn.h>
  
  #ifndef OPENSSL_NO_HW
  # ifndef OPENSSL_NO_HW_CHIL
  
  /*-
   * Attribution notice: nCipher have said several times that it's OK for
   * us to implement a general interface to their boxes, and recently declared
   * their HWCryptoHook to be public, and therefore available for us to use.
   * Thanks, nCipher.
   *
   * The hwcryptohook.h included here is from May 2000.
   * [Richard Levitte]
   */
  #  ifdef FLAT_INC
  #   include "hwcryptohook.h"
  #  else
  #   include "vendor_defns/hwcryptohook.h"
  #  endif
  
  #  define HWCRHK_LIB_NAME "CHIL engine"
  #  include "e_chil_err.c"
  
  static CRYPTO_RWLOCK *chil_lock;
  
  static int hwcrhk_destroy(ENGINE *e);
  static int hwcrhk_init(ENGINE *e);
  static int hwcrhk_finish(ENGINE *e);
  static int hwcrhk_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void));
  
  /* Functions to handle mutexes */
  static int hwcrhk_mutex_init(HWCryptoHook_Mutex *,
                               HWCryptoHook_CallerContext *);
  static int hwcrhk_mutex_lock(HWCryptoHook_Mutex *);
  static void hwcrhk_mutex_unlock(HWCryptoHook_Mutex *);
  static void hwcrhk_mutex_destroy(HWCryptoHook_Mutex *);
  
  /* BIGNUM stuff */
  static int hwcrhk_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                            const BIGNUM *m, BN_CTX *ctx);
  
  #  ifndef OPENSSL_NO_RSA
  /* RSA stuff */
  static int hwcrhk_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa,
                                BN_CTX *ctx);
  /* This function is aliased to mod_exp (with the mont stuff dropped). */
  static int hwcrhk_mod_exp_mont(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                                 const BIGNUM *m, BN_CTX *ctx,
                                 BN_MONT_CTX *m_ctx);
  static int hwcrhk_rsa_finish(RSA *rsa);
  #  endif
  
  #  ifndef OPENSSL_NO_DH
  /* DH stuff */
  /* This function is alised to mod_exp (with the DH and mont dropped). */
  static int hwcrhk_mod_exp_dh(const DH *dh, BIGNUM *r,
                               const BIGNUM *a, const BIGNUM *p,
                               const BIGNUM *m, BN_CTX *ctx,
                               BN_MONT_CTX *m_ctx);
  #  endif
  
  /* RAND stuff */
  static int hwcrhk_rand_bytes(unsigned char *buf, int num);
  static int hwcrhk_rand_status(void);
  
  /* KM stuff */
  static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
                                       UI_METHOD *ui_method,
                                       void *callback_data);
  static EVP_PKEY *hwcrhk_load_pubkey(ENGINE *eng, const char *key_id,
                                      UI_METHOD *ui_method,
                                      void *callback_data);
  
  /* Interaction stuff */
  static int hwcrhk_insert_card(const char *prompt_info,
                                const char *wrong_info,
                                HWCryptoHook_PassphraseContext * ppctx,
                                HWCryptoHook_CallerContext * cactx);
  static int hwcrhk_get_pass(const char *prompt_info,
                             int *len_io, char *buf,
                             HWCryptoHook_PassphraseContext * ppctx,
                             HWCryptoHook_CallerContext * cactx);
  static void hwcrhk_log_message(void *logstr, const char *message);
  
  /* The definitions for control commands specific to this engine */
  #  define HWCRHK_CMD_SO_PATH              ENGINE_CMD_BASE
  #  define HWCRHK_CMD_FORK_CHECK           (ENGINE_CMD_BASE + 1)
  #  define HWCRHK_CMD_THREAD_LOCKING       (ENGINE_CMD_BASE + 2)
  #  define HWCRHK_CMD_SET_USER_INTERFACE   (ENGINE_CMD_BASE + 3)
  #  define HWCRHK_CMD_SET_CALLBACK_DATA    (ENGINE_CMD_BASE + 4)
  static const ENGINE_CMD_DEFN hwcrhk_cmd_defns[] = {
      {HWCRHK_CMD_SO_PATH,
       "SO_PATH",
       "Specifies the path to the 'hwcrhk' shared library",
       ENGINE_CMD_FLAG_STRING},
      {HWCRHK_CMD_FORK_CHECK,
       "FORK_CHECK",
       "Turns fork() checking on (non-zero) or off (zero)",
       ENGINE_CMD_FLAG_NUMERIC},
      {HWCRHK_CMD_THREAD_LOCKING,
       "THREAD_LOCKING",
       "Turns thread-safe locking on (zero) or off (non-zero)",
       ENGINE_CMD_FLAG_NUMERIC},
      {HWCRHK_CMD_SET_USER_INTERFACE,
       "SET_USER_INTERFACE",
       "Set the global user interface (internal)",
       ENGINE_CMD_FLAG_INTERNAL},
      {HWCRHK_CMD_SET_CALLBACK_DATA,
       "SET_CALLBACK_DATA",
       "Set the global user interface extra data (internal)",
       ENGINE_CMD_FLAG_INTERNAL},
      {0, NULL, NULL, 0}
  };
  
  #  ifndef OPENSSL_NO_RSA
  /* Our internal RSA_METHOD that we provide pointers to */
  static RSA_METHOD hwcrhk_rsa = {
      "CHIL RSA method",
      NULL,
      NULL,
      NULL,
      NULL,
      hwcrhk_rsa_mod_exp,
      hwcrhk_mod_exp_mont,
      NULL,
      hwcrhk_rsa_finish,
      0,
      NULL,
      NULL,
      NULL,
      NULL
  };
  #  endif
  
  #  ifndef OPENSSL_NO_DH
  /* Our internal DH_METHOD that we provide pointers to */
  static DH_METHOD hwcrhk_dh = {
      "CHIL DH method",
      NULL,
      NULL,
      hwcrhk_mod_exp_dh,
      NULL,
      NULL,
      0,
      NULL,
      NULL
  };
  #  endif
  
  static RAND_METHOD hwcrhk_rand = {
      /* "CHIL RAND method", */
      NULL,
      hwcrhk_rand_bytes,
      NULL,
      NULL,
      hwcrhk_rand_bytes,
      hwcrhk_rand_status,
  };
  
  /* Constants used when creating the ENGINE */
  static const char *engine_hwcrhk_id = "chil";
  static const char *engine_hwcrhk_name = "CHIL hardware engine support";
  #  ifndef OPENSSL_NO_DYNAMIC_ENGINE
  /* Compatibility hack, the dynamic library uses this form in the path */
  static const char *engine_hwcrhk_id_alt = "ncipher";
  #  endif
  
  /* Internal stuff for HWCryptoHook */
  
  /* Some structures needed for proper use of thread locks */
  /*
   * hwcryptohook.h has some typedefs that turn struct HWCryptoHook_MutexValue
   * into HWCryptoHook_Mutex
   */
  struct HWCryptoHook_MutexValue {
      CRYPTO_RWLOCK *lock;
  };
  
  /*
   * hwcryptohook.h has some typedefs that turn struct
   * HWCryptoHook_PassphraseContextValue into HWCryptoHook_PassphraseContext
   */
  struct HWCryptoHook_PassphraseContextValue {
      UI_METHOD *ui_method;
      void *callback_data;
  };
  
  /*
   * hwcryptohook.h has some typedefs that turn struct
   * HWCryptoHook_CallerContextValue into HWCryptoHook_CallerContext
   */
  struct HWCryptoHook_CallerContextValue {
      pem_password_cb *password_callback; /* Deprecated! Only present for
                                           * backward compatibility! */
      UI_METHOD *ui_method;
      void *callback_data;
  };
  
  /*
   * The MPI structure in HWCryptoHook is pretty compatible with OpenSSL
   * BIGNUM's, so lets define a couple of conversion macros
   */
  #  define BN2MPI(mp, bn) \
      {mp.size = bn->top * sizeof(BN_ULONG); mp.buf = (unsigned char *)bn->d;}
  #  define MPI2BN(bn, mp) \
      {mp.size = bn->dmax * sizeof(BN_ULONG); mp.buf = (unsigned char *)bn->d;}
  
  static BIO *logstream = NULL;
  static int disable_mutex_callbacks = 0;
  
  /*
   * One might wonder why these are needed, since one can pass down at least a
   * UI_METHOD and a pointer to callback data to the key-loading functions. The
   * thing is that the ModExp and RSAImmed functions can load keys as well, if
   * the data they get is in a special, nCipher-defined format (hint: if you
   * look at the private exponent of the RSA data as a string, you'll see this
   * string: "nCipher KM tool key id", followed by some bytes, followed a key
   * identity string, followed by more bytes.  This happens when you use
   * "embed" keys instead of "hwcrhk" keys).  Unfortunately, those functions do
   * not take any passphrase or caller context, and our functions can't really
   * take any callback data either.  Still, the "insert_card" and
   * "get_passphrase" callbacks may be called down the line, and will need to
   * know what user interface callbacks to call, and having callback data from
   * the application may be a nice thing as well, so we need to keep track of
   * that globally.
   */
  static HWCryptoHook_CallerContext password_context = { NULL, NULL, NULL };
  
  /* Stuff to pass to the HWCryptoHook library */
  static HWCryptoHook_InitInfo hwcrhk_globals = {
      HWCryptoHook_InitFlags_SimpleForkCheck, /* Flags */
      &logstream,                 /* logstream */
      sizeof(BN_ULONG),           /* limbsize */
      0,                          /* mslimb first: false for BNs */
      -1,                         /* msbyte first: use native */
      0,                          /* Max mutexes, 0 = no small limit */
      0,                          /* Max simultaneous, 0 = default */
  
      /*
       * The next few are mutex stuff: we write wrapper functions around the OS
       * mutex functions.  We initialise them to 0 here, and change that to
       * actual function pointers in hwcrhk_init() if dynamic locks are
       * supported (that is, if the application programmer has made sure of
       * setting up callbacks bafore starting this engine) *and* if
       * disable_mutex_callbacks hasn't been set by a call to
       * ENGINE_ctrl(ENGINE_CTRL_CHIL_NO_LOCKING).
       */
      sizeof(HWCryptoHook_Mutex),
      0,
      0,
      0,
      0,
  
      /*
       * The next few are condvar stuff: we write wrapper functions round the
       * OS functions.  Currently not implemented and not and absolute
       * necessity even in threaded programs, therefore 0'ed.  Will hopefully
       * be implemented some day, since it enhances the efficiency of
       * HWCryptoHook.
       */
      0,                          /* sizeof(HWCryptoHook_CondVar), */
      0,                          /* hwcrhk_cv_init, */
      0,                          /* hwcrhk_cv_wait, */
      0,                          /* hwcrhk_cv_signal, */
      0,                          /* hwcrhk_cv_broadcast, */
      0,                          /* hwcrhk_cv_destroy, */
  
      hwcrhk_get_pass,            /* pass phrase */
      hwcrhk_insert_card,         /* insert a card */
      hwcrhk_log_message          /* Log message */
  };
  
  /* Now, to our own code */
  
  /*
   * This internal function is used by ENGINE_chil() and possibly by the
   * "dynamic" ENGINE support too
   */
  static int bind_helper(ENGINE *e)
  {
  #  ifndef OPENSSL_NO_RSA
      const RSA_METHOD *meth1;
  #  endif
  #  ifndef OPENSSL_NO_DH
      const DH_METHOD *meth2;
  #  endif
  
      chil_lock = CRYPTO_THREAD_lock_new();
      if (chil_lock == NULL) {
          HWCRHKerr(HWCRHK_F_BIND_HELPER, ERR_R_MALLOC_FAILURE);
          return 0;
      }
  
      if (!ENGINE_set_id(e, engine_hwcrhk_id) ||
          !ENGINE_set_name(e, engine_hwcrhk_name) ||
  #  ifndef OPENSSL_NO_RSA
          !ENGINE_set_RSA(e, &hwcrhk_rsa) ||
  #  endif
  #  ifndef OPENSSL_NO_DH
          !ENGINE_set_DH(e, &hwcrhk_dh) ||
  #  endif
          !ENGINE_set_RAND(e, &hwcrhk_rand) ||
          !ENGINE_set_destroy_function(e, hwcrhk_destroy) ||
          !ENGINE_set_init_function(e, hwcrhk_init) ||
          !ENGINE_set_finish_function(e, hwcrhk_finish) ||
          !ENGINE_set_ctrl_function(e, hwcrhk_ctrl) ||
          !ENGINE_set_load_privkey_function(e, hwcrhk_load_privkey) ||
          !ENGINE_set_load_pubkey_function(e, hwcrhk_load_pubkey) ||
          !ENGINE_set_cmd_defns(e, hwcrhk_cmd_defns))
          return 0;
  
  #  ifndef OPENSSL_NO_RSA
      /*
       * We know that the "PKCS1_OpenSSL()" functions hook properly to the
       * cswift-specific mod_exp and mod_exp_crt so we use those functions. NB:
       * We don't use ENGINE_openssl() or anything "more generic" because
       * something like the RSAref code may not hook properly, and if you own
       * one of these cards then you have the right to do RSA operations on it
       * anyway!
       */
      meth1 = RSA_PKCS1_OpenSSL();
      hwcrhk_rsa.rsa_pub_enc = meth1->rsa_pub_enc;
      hwcrhk_rsa.rsa_pub_dec = meth1->rsa_pub_dec;
      hwcrhk_rsa.rsa_priv_enc = meth1->rsa_priv_enc;
      hwcrhk_rsa.rsa_priv_dec = meth1->rsa_priv_dec;
  #  endif
  
  #  ifndef OPENSSL_NO_DH
      /* Much the same for Diffie-Hellman */
      meth2 = DH_OpenSSL();
      hwcrhk_dh.generate_key = meth2->generate_key;
      hwcrhk_dh.compute_key = meth2->compute_key;
  #  endif
  
      /* Ensure the hwcrhk error handling is set up */
      ERR_load_HWCRHK_strings();
  
      return 1;
  }
  
  #  ifdef OPENSSL_NO_DYNAMIC_ENGINE
  static ENGINE *engine_chil(void)
  {
      ENGINE *ret = ENGINE_new();
      if (ret == NULL)
          return NULL;
      if (!bind_helper(ret)) {
          ENGINE_free(ret);
          return NULL;
      }
      return ret;
  }
  
  void ENGINE_load_chil(void)
  {
      /* Copied from eng_[openssl|dyn].c */
      ENGINE *toadd = engine_chil();
      if (!toadd)
          return;
      ENGINE_add(toadd);
      ENGINE_free(toadd);
      ERR_clear_error();
  }
  #  endif
  
  /*
   * This is a process-global DSO handle used for loading and unloading the
   * HWCryptoHook library. NB: This is only set (or unset) during an init() or
   * finish() call (reference counts permitting) and they're operating with
   * global locks, so this should be thread-safe implicitly.
   */
  static DSO *hwcrhk_dso = NULL;
  static HWCryptoHook_ContextHandle hwcrhk_context = 0;
  #  ifndef OPENSSL_NO_RSA
  /* Index for KM handle.  Not really used yet. */
  static int hndidx_rsa = -1;
  #  endif
  
  /*
   * These are the function pointers that are (un)set when the library has
   * successfully (un)loaded.
   */
  static HWCryptoHook_Init_t *p_hwcrhk_Init = NULL;
  static HWCryptoHook_Finish_t *p_hwcrhk_Finish = NULL;
  static HWCryptoHook_ModExp_t *p_hwcrhk_ModExp = NULL;
  #  ifndef OPENSSL_NO_RSA
  static HWCryptoHook_RSA_t *p_hwcrhk_RSA = NULL;
  #  endif
  static HWCryptoHook_RandomBytes_t *p_hwcrhk_RandomBytes = NULL;
  #  ifndef OPENSSL_NO_RSA
  static HWCryptoHook_RSALoadKey_t *p_hwcrhk_RSALoadKey = NULL;
  static HWCryptoHook_RSAGetPublicKey_t *p_hwcrhk_RSAGetPublicKey = NULL;
  static HWCryptoHook_RSAUnloadKey_t *p_hwcrhk_RSAUnloadKey = NULL;
  #  endif
  static HWCryptoHook_ModExpCRT_t *p_hwcrhk_ModExpCRT = NULL;
  
  /* Used in the DSO operations. */
  static const char *HWCRHK_LIBNAME = NULL;
  static void free_HWCRHK_LIBNAME(void)
  {
      OPENSSL_free(HWCRHK_LIBNAME);
      HWCRHK_LIBNAME = NULL;
  }
  
  static const char *get_HWCRHK_LIBNAME(void)
  {
      if (HWCRHK_LIBNAME)
          return HWCRHK_LIBNAME;
      return "nfhwcrhk";
  }
  
  static long set_HWCRHK_LIBNAME(const char *name)
  {
      free_HWCRHK_LIBNAME();
      return (((HWCRHK_LIBNAME = OPENSSL_strdup(name)) != NULL) ? 1 : 0);
  }
  
  static const char *n_hwcrhk_Init = "HWCryptoHook_Init";
  static const char *n_hwcrhk_Finish = "HWCryptoHook_Finish";
  static const char *n_hwcrhk_ModExp = "HWCryptoHook_ModExp";
  #  ifndef OPENSSL_NO_RSA
  static const char *n_hwcrhk_RSA = "HWCryptoHook_RSA";
  #  endif
  static const char *n_hwcrhk_RandomBytes = "HWCryptoHook_RandomBytes";
  #  ifndef OPENSSL_NO_RSA
  static const char *n_hwcrhk_RSALoadKey = "HWCryptoHook_RSALoadKey";
  static const char *n_hwcrhk_RSAGetPublicKey = "HWCryptoHook_RSAGetPublicKey";
  static const char *n_hwcrhk_RSAUnloadKey = "HWCryptoHook_RSAUnloadKey";
  #  endif
  static const char *n_hwcrhk_ModExpCRT = "HWCryptoHook_ModExpCRT";
  
  /*
   * HWCryptoHook library functions and mechanics - these are used by the
   * higher-level functions further down. NB: As and where there's no error
   * checking, take a look lower down where these functions are called, the
   * checking and error handling is probably down there.
   */
  
  /* utility function to obtain a context */
  static int get_context(HWCryptoHook_ContextHandle * hac,
                         HWCryptoHook_CallerContext * cac)
  {
      char tempbuf[1024];
      HWCryptoHook_ErrMsgBuf rmsg;
  
      rmsg.buf = tempbuf;
      rmsg.size = sizeof(tempbuf);
  
      *hac = p_hwcrhk_Init(&hwcrhk_globals, sizeof(hwcrhk_globals), &rmsg, cac);
      if (!*hac)
          return 0;
      return 1;
  }
  
  /* similarly to release one. */
  static void release_context(HWCryptoHook_ContextHandle hac)
  {
      p_hwcrhk_Finish(hac);
  }
  
  /* Destructor (complements the "ENGINE_chil()" constructor) */
  static int hwcrhk_destroy(ENGINE *e)
  {
      free_HWCRHK_LIBNAME();
      ERR_unload_HWCRHK_strings();
      CRYPTO_THREAD_lock_free(chil_lock);
      return 1;
  }
  
  /* (de)initialisation functions. */
  static int hwcrhk_init(ENGINE *e)
  {
      HWCryptoHook_Init_t *p1;
      HWCryptoHook_Finish_t *p2;
      HWCryptoHook_ModExp_t *p3;
  #  ifndef OPENSSL_NO_RSA
      HWCryptoHook_RSA_t *p4;
      HWCryptoHook_RSALoadKey_t *p5;
      HWCryptoHook_RSAGetPublicKey_t *p6;
      HWCryptoHook_RSAUnloadKey_t *p7;
  #  endif
      HWCryptoHook_RandomBytes_t *p8;
      HWCryptoHook_ModExpCRT_t *p9;
  
      if (hwcrhk_dso != NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_INIT, HWCRHK_R_ALREADY_LOADED);
          goto err;
      }
      /* Attempt to load libnfhwcrhk.so/nfhwcrhk.dll/whatever. */
      hwcrhk_dso = DSO_load(NULL, get_HWCRHK_LIBNAME(), NULL, 0);
      if (hwcrhk_dso == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_INIT, HWCRHK_R_DSO_FAILURE);
          goto err;
      }
  
  #define BINDIT(t, name) (t *)DSO_bind_func(hwcrhk_dso, name)
      if ((p1 = BINDIT(HWCryptoHook_Init_t, n_hwcrhk_Init)) == NULL
          || (p2 = BINDIT(HWCryptoHook_Finish_t, n_hwcrhk_Finish)) == NULL
          || (p3 = BINDIT(HWCryptoHook_ModExp_t, n_hwcrhk_ModExp)) == NULL
  #  ifndef OPENSSL_NO_RSA
          || (p4 = BINDIT(HWCryptoHook_RSA_t, n_hwcrhk_RSA)) == NULL
          || (p5 = BINDIT(HWCryptoHook_RSALoadKey_t, n_hwcrhk_RSALoadKey)) == NULL
          || (p6 = BINDIT(HWCryptoHook_RSAGetPublicKey_t, n_hwcrhk_RSAGetPublicKey)) == NULL
          || (p7 = BINDIT(HWCryptoHook_RSAUnloadKey_t, n_hwcrhk_RSAUnloadKey)) == NULL
  #  endif
          || (p8 = BINDIT(HWCryptoHook_RandomBytes_t, n_hwcrhk_RandomBytes)) == NULL
          || (p9 = BINDIT(HWCryptoHook_ModExpCRT_t, n_hwcrhk_ModExpCRT)) == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_INIT, HWCRHK_R_DSO_FAILURE);
          goto err;
      }
      /* Copy the pointers */
      p_hwcrhk_Init = p1;
      p_hwcrhk_Finish = p2;
      p_hwcrhk_ModExp = p3;
  #  ifndef OPENSSL_NO_RSA
      p_hwcrhk_RSA = p4;
      p_hwcrhk_RSALoadKey = p5;
      p_hwcrhk_RSAGetPublicKey = p6;
      p_hwcrhk_RSAUnloadKey = p7;
  #  endif
      p_hwcrhk_RandomBytes = p8;
      p_hwcrhk_ModExpCRT = p9;
  
      /*
       * Check if the application decided to support dynamic locks, and if it
       * does, use them.
       */
      if (disable_mutex_callbacks == 0) {
          hwcrhk_globals.mutex_init = hwcrhk_mutex_init;
          hwcrhk_globals.mutex_acquire = hwcrhk_mutex_lock;
          hwcrhk_globals.mutex_release = hwcrhk_mutex_unlock;
          hwcrhk_globals.mutex_destroy = hwcrhk_mutex_destroy;
      }
  
      /*
       * Try and get a context - if not, we may have a DSO but no accelerator!
       */
      if (!get_context(&hwcrhk_context, &password_context)) {
          HWCRHKerr(HWCRHK_F_HWCRHK_INIT, HWCRHK_R_UNIT_FAILURE);
          goto err;
      }
      /* Everything's fine. */
  #  ifndef OPENSSL_NO_RSA
      if (hndidx_rsa == -1)
          hndidx_rsa = RSA_get_ex_new_index(0,
                                            "nFast HWCryptoHook RSA key handle",
                                            NULL, NULL, NULL);
  #  endif
      return 1;
   err:
      DSO_free(hwcrhk_dso);
      hwcrhk_dso = NULL;
      p_hwcrhk_Init = NULL;
      p_hwcrhk_Finish = NULL;
      p_hwcrhk_ModExp = NULL;
  #  ifndef OPENSSL_NO_RSA
      p_hwcrhk_RSA = NULL;
      p_hwcrhk_RSALoadKey = NULL;
      p_hwcrhk_RSAGetPublicKey = NULL;
      p_hwcrhk_RSAUnloadKey = NULL;
  #  endif
      p_hwcrhk_ModExpCRT = NULL;
      p_hwcrhk_RandomBytes = NULL;
      return 0;
  }
  
  static int hwcrhk_finish(ENGINE *e)
  {
      int to_return = 1;
      free_HWCRHK_LIBNAME();
      if (hwcrhk_dso == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_FINISH, HWCRHK_R_NOT_LOADED);
          to_return = 0;
          goto err;
      }
      release_context(hwcrhk_context);
      if (!DSO_free(hwcrhk_dso)) {
          HWCRHKerr(HWCRHK_F_HWCRHK_FINISH, HWCRHK_R_DSO_FAILURE);
          to_return = 0;
          goto err;
      }
   err:
      BIO_free(logstream);
      hwcrhk_dso = NULL;
      p_hwcrhk_Init = NULL;
      p_hwcrhk_Finish = NULL;
      p_hwcrhk_ModExp = NULL;
  #  ifndef OPENSSL_NO_RSA
      p_hwcrhk_RSA = NULL;
      p_hwcrhk_RSALoadKey = NULL;
      p_hwcrhk_RSAGetPublicKey = NULL;
      p_hwcrhk_RSAUnloadKey = NULL;
  #  endif
      p_hwcrhk_ModExpCRT = NULL;
      p_hwcrhk_RandomBytes = NULL;
      return to_return;
  }
  
  static int hwcrhk_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
  {
      int to_return = 1;
  
      switch (cmd) {
      case HWCRHK_CMD_SO_PATH:
          if (hwcrhk_dso) {
              HWCRHKerr(HWCRHK_F_HWCRHK_CTRL, HWCRHK_R_ALREADY_LOADED);
              return 0;
          }
          if (p == NULL) {
              HWCRHKerr(HWCRHK_F_HWCRHK_CTRL, ERR_R_PASSED_NULL_PARAMETER);
              return 0;
          }
          return set_HWCRHK_LIBNAME((const char *)p);
      case ENGINE_CTRL_SET_LOGSTREAM:
          {
              BIO *bio = (BIO *)p;
  
              CRYPTO_THREAD_write_lock(chil_lock);
              BIO_free(logstream);
              logstream = NULL;
              if (BIO_up_ref(bio))
                  logstream = bio;
              else
                  HWCRHKerr(HWCRHK_F_HWCRHK_CTRL, HWCRHK_R_BIO_WAS_FREED);
          }
          CRYPTO_THREAD_unlock(chil_lock);
          break;
      case ENGINE_CTRL_SET_PASSWORD_CALLBACK:
          CRYPTO_THREAD_write_lock(chil_lock);
          password_context.password_callback = (pem_password_cb *)f;
          CRYPTO_THREAD_unlock(chil_lock);
          break;
      case ENGINE_CTRL_SET_USER_INTERFACE:
      case HWCRHK_CMD_SET_USER_INTERFACE:
          CRYPTO_THREAD_write_lock(chil_lock);
          password_context.ui_method = (UI_METHOD *)p;
          CRYPTO_THREAD_unlock(chil_lock);
          break;
      case ENGINE_CTRL_SET_CALLBACK_DATA:
      case HWCRHK_CMD_SET_CALLBACK_DATA:
          CRYPTO_THREAD_write_lock(chil_lock);
          password_context.callback_data = p;
          CRYPTO_THREAD_unlock(chil_lock);
          break;
          /*
           * this enables or disables the "SimpleForkCheck" flag used in the
           * initialisation structure.
           */
      case ENGINE_CTRL_CHIL_SET_FORKCHECK:
      case HWCRHK_CMD_FORK_CHECK:
          CRYPTO_THREAD_write_lock(chil_lock);
          if (i)
              hwcrhk_globals.flags |= HWCryptoHook_InitFlags_SimpleForkCheck;
          else
              hwcrhk_globals.flags &= ~HWCryptoHook_InitFlags_SimpleForkCheck;
          CRYPTO_THREAD_unlock(chil_lock);
          break;
          /*
           * This will prevent the initialisation function from "installing"
           * the mutex-handling callbacks, even if they are available from
           * within the library (or were provided to the library from the
           * calling application). This is to remove any baggage for
           * applications not using multithreading.
           */
      case ENGINE_CTRL_CHIL_NO_LOCKING:
          CRYPTO_THREAD_write_lock(chil_lock);
          disable_mutex_callbacks = 1;
          CRYPTO_THREAD_unlock(chil_lock);
          break;
      case HWCRHK_CMD_THREAD_LOCKING:
          CRYPTO_THREAD_write_lock(chil_lock);
          disable_mutex_callbacks = ((i == 0) ? 0 : 1);
          CRYPTO_THREAD_unlock(chil_lock);
          break;
  
          /* The command isn't understood by this engine */
      default:
          HWCRHKerr(HWCRHK_F_HWCRHK_CTRL,
                    HWCRHK_R_CTRL_COMMAND_NOT_IMPLEMENTED);
          to_return = 0;
          break;
      }
  
      return to_return;
  }
  
  static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
                                       UI_METHOD *ui_method,
                                       void *callback_data)
  {
  #  ifndef OPENSSL_NO_RSA
      RSA *rtmp = NULL;
  #  endif
      EVP_PKEY *res = NULL;
  #  ifndef OPENSSL_NO_RSA
      HWCryptoHook_MPI e, n;
      HWCryptoHook_RSAKeyHandle *hptr;
  #  endif
  #  if !defined(OPENSSL_NO_RSA)
      char tempbuf[1024];
      HWCryptoHook_ErrMsgBuf rmsg;
      HWCryptoHook_PassphraseContext ppctx;
  #  endif
  
  #  if !defined(OPENSSL_NO_RSA)
      rmsg.buf = tempbuf;
      rmsg.size = sizeof(tempbuf);
  #  endif
  
      if (!hwcrhk_context) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_NOT_INITIALISED);
          goto err;
      }
  #  ifndef OPENSSL_NO_RSA
      hptr = OPENSSL_malloc(sizeof(*hptr));
      if (hptr == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, ERR_R_MALLOC_FAILURE);
          goto err;
      }
      ppctx.ui_method = ui_method;
      ppctx.callback_data = callback_data;
      if (p_hwcrhk_RSALoadKey(hwcrhk_context, key_id, hptr, &rmsg, &ppctx)) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_CHIL_ERROR);
          ERR_add_error_data(1, rmsg.buf);
          goto err;
      }
      if (!*hptr) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_NO_KEY);
          goto err;
      }
  #  endif
  #  ifndef OPENSSL_NO_RSA
      rtmp = RSA_new_method(eng);
      RSA_set_ex_data(rtmp, hndidx_rsa, (char *)hptr);
      rtmp->e = BN_new();
      rtmp->n = BN_new();
      rtmp->flags |= RSA_FLAG_EXT_PKEY;
      MPI2BN(rtmp->e, e);
      MPI2BN(rtmp->n, n);
      if (p_hwcrhk_RSAGetPublicKey(*hptr, &n, &e, &rmsg)
          != HWCRYPTOHOOK_ERROR_MPISIZE) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_CHIL_ERROR);
          ERR_add_error_data(1, rmsg.buf);
          goto err;
      }
  
      bn_expand2(rtmp->e, e.size / sizeof(BN_ULONG));
      bn_expand2(rtmp->n, n.size / sizeof(BN_ULONG));
      MPI2BN(rtmp->e, e);
      MPI2BN(rtmp->n, n);
  
      if (p_hwcrhk_RSAGetPublicKey(*hptr, &n, &e, &rmsg)) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_CHIL_ERROR);
          ERR_add_error_data(1, rmsg.buf);
          goto err;
      }
      rtmp->e->top = e.size / sizeof(BN_ULONG);
      bn_fix_top(rtmp->e);
      rtmp->n->top = n.size / sizeof(BN_ULONG);
      bn_fix_top(rtmp->n);
  
      res = EVP_PKEY_new();
      if (res == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY, HWCRHK_R_CHIL_ERROR);
          goto err;
      }
      EVP_PKEY_assign_RSA(res, rtmp);
  #  endif
  
      if (res == NULL)
          HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PRIVKEY,
                    HWCRHK_R_PRIVATE_KEY_ALGORITHMS_DISABLED);
  
      return res;
   err:
  #  ifndef OPENSSL_NO_RSA
      RSA_free(rtmp);
  #  endif
      return NULL;
  }
  
  static EVP_PKEY *hwcrhk_load_pubkey(ENGINE *eng, const char *key_id,
                                      UI_METHOD *ui_method, void *callback_data)
  {
      EVP_PKEY *res = NULL;
  
  #  ifndef OPENSSL_NO_RSA
      res = hwcrhk_load_privkey(eng, key_id, ui_method, callback_data);
  #  endif
  
      if (res)
          switch (res->type) {
  #  ifndef OPENSSL_NO_RSA
          case EVP_PKEY_RSA:
              {
                  RSA *rsa = NULL;
  
                  CRYPTO_THREAD_write_lock(chil_lock);
                  rsa = res->pkey.rsa;
                  res->pkey.rsa = RSA_new();
                  res->pkey.rsa->n = rsa->n;
                  res->pkey.rsa->e = rsa->e;
                  rsa->n = NULL;
                  rsa->e = NULL;
                  CRYPTO_THREAD_unlock(chil_lock);
                  RSA_free(rsa);
              }
              break;
  #  endif
          default:
              HWCRHKerr(HWCRHK_F_HWCRHK_LOAD_PUBKEY,
                        HWCRHK_R_CTRL_COMMAND_NOT_IMPLEMENTED);
              goto err;
          }
  
      return res;
   err:
      EVP_PKEY_free(res);
      return NULL;
  }
  
  /* A little mod_exp */
  static int hwcrhk_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                            const BIGNUM *m, BN_CTX *ctx)
  {
      char tempbuf[1024];
      HWCryptoHook_ErrMsgBuf rmsg;
      /*
       * Since HWCryptoHook_MPI is pretty compatible with BIGNUM's, we use them
       * directly, plus a little macro magic.  We only thing we need to make
       * sure of is that enough space is allocated.
       */
      HWCryptoHook_MPI m_a, m_p, m_n, m_r;
      int to_return, ret;
  
      to_return = 0;              /* expect failure */
      rmsg.buf = tempbuf;
      rmsg.size = sizeof(tempbuf);
  
      if (!hwcrhk_context) {
          HWCRHKerr(HWCRHK_F_HWCRHK_MOD_EXP, HWCRHK_R_NOT_INITIALISED);
          goto err;
      }
      /* Prepare the params */
      bn_expand2(r, m->top);      /* Check for error !! */
      BN2MPI(m_a, a);
      BN2MPI(m_p, p);
      BN2MPI(m_n, m);
      MPI2BN(r, m_r);
  
      /* Perform the operation */
      ret = p_hwcrhk_ModExp(hwcrhk_context, m_a, m_p, m_n, &m_r, &rmsg);
  
      /* Convert the response */
      r->top = m_r.size / sizeof(BN_ULONG);
      bn_fix_top(r);
  
      if (ret < 0) {
          /*
           * FIXME: When this error is returned, HWCryptoHook is telling us
           * that falling back to software computation might be a good thing.
           */
          if (ret == HWCRYPTOHOOK_ERROR_FALLBACK) {
              HWCRHKerr(HWCRHK_F_HWCRHK_MOD_EXP, HWCRHK_R_REQUEST_FALLBACK);
          } else {
              HWCRHKerr(HWCRHK_F_HWCRHK_MOD_EXP, HWCRHK_R_REQUEST_FAILED);
          }
          ERR_add_error_data(1, rmsg.buf);
          goto err;
      }
  
      to_return = 1;
   err:
      return to_return;
  }
  
  #  ifndef OPENSSL_NO_RSA
  static int hwcrhk_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa,
                                BN_CTX *ctx)
  {
      char tempbuf[1024];
      HWCryptoHook_ErrMsgBuf rmsg;
      HWCryptoHook_RSAKeyHandle *hptr;
      int to_return = 0, ret;
  
      rmsg.buf = tempbuf;
      rmsg.size = sizeof(tempbuf);
  
      if (!hwcrhk_context) {
          HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP, HWCRHK_R_NOT_INITIALISED);
          goto err;
      }
  
      /*
       * This provides support for nForce keys.  Since that's opaque data all
       * we do is provide a handle to the proper key and let HWCryptoHook take
       * care of the rest.
       */
      if ((hptr =
           (HWCryptoHook_RSAKeyHandle *) RSA_get_ex_data(rsa, hndidx_rsa))
          != NULL) {
          HWCryptoHook_MPI m_a, m_r;
  
          if (!rsa->n) {
              HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                        HWCRHK_R_MISSING_KEY_COMPONENTS);
              goto err;
          }
  
          /* Prepare the params */
          bn_expand2(r, rsa->n->top); /* Check for error !! */
          BN2MPI(m_a, I);
          MPI2BN(r, m_r);
  
          /* Perform the operation */
          ret = p_hwcrhk_RSA(m_a, *hptr, &m_r, &rmsg);
  
          /* Convert the response */
          r->top = m_r.size / sizeof(BN_ULONG);
          bn_fix_top(r);
  
          if (ret < 0) {
              /*
               * FIXME: When this error is returned, HWCryptoHook is telling us
               * that falling back to software computation might be a good
               * thing.
               */
              if (ret == HWCRYPTOHOOK_ERROR_FALLBACK) {
                  HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                            HWCRHK_R_REQUEST_FALLBACK);
              } else {
                  HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                            HWCRHK_R_REQUEST_FAILED);
              }
              ERR_add_error_data(1, rmsg.buf);
              goto err;
          }
      } else {
          HWCryptoHook_MPI m_a, m_p, m_q, m_dmp1, m_dmq1, m_iqmp, m_r;
  
          if (!rsa->p || !rsa->q || !rsa->dmp1 || !rsa->dmq1 || !rsa->iqmp) {
              HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                        HWCRHK_R_MISSING_KEY_COMPONENTS);
              goto err;
          }
  
          /* Prepare the params */
          bn_expand2(r, rsa->n->top); /* Check for error !! */
          BN2MPI(m_a, I);
          BN2MPI(m_p, rsa->p);
          BN2MPI(m_q, rsa->q);
          BN2MPI(m_dmp1, rsa->dmp1);
          BN2MPI(m_dmq1, rsa->dmq1);
          BN2MPI(m_iqmp, rsa->iqmp);
          MPI2BN(r, m_r);
  
          /* Perform the operation */
          ret = p_hwcrhk_ModExpCRT(hwcrhk_context, m_a, m_p, m_q,
                                   m_dmp1, m_dmq1, m_iqmp, &m_r, &rmsg);
  
          /* Convert the response */
          r->top = m_r.size / sizeof(BN_ULONG);
          bn_fix_top(r);
  
          if (ret < 0) {
              /*
               * FIXME: When this error is returned, HWCryptoHook is telling us
               * that falling back to software computation might be a good
               * thing.
               */
              if (ret == HWCRYPTOHOOK_ERROR_FALLBACK) {
                  HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                            HWCRHK_R_REQUEST_FALLBACK);
              } else {
                  HWCRHKerr(HWCRHK_F_HWCRHK_RSA_MOD_EXP,
                            HWCRHK_R_REQUEST_FAILED);
              }
              ERR_add_error_data(1, rmsg.buf);
              goto err;
          }
      }
      /*
       * If we're here, we must be here with some semblance of success :-)
       */
      to_return = 1;
   err:
      return to_return;
  }
  #  endif
  
  #  ifndef OPENSSL_NO_RSA
  /* This function is aliased to mod_exp (with the mont stuff dropped). */
  static int hwcrhk_mod_exp_mont(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                                 const BIGNUM *m, BN_CTX *ctx,
                                 BN_MONT_CTX *m_ctx)
  {
      return hwcrhk_mod_exp(r, a, p, m, ctx);
  }
  
  static int hwcrhk_rsa_finish(RSA *rsa)
  {
      HWCryptoHook_RSAKeyHandle *hptr;
  
      hptr = RSA_get_ex_data(rsa, hndidx_rsa);
      if (hptr) {
          p_hwcrhk_RSAUnloadKey(*hptr, NULL);
          OPENSSL_free(hptr);
          RSA_set_ex_data(rsa, hndidx_rsa, NULL);
      }
      return 1;
  }
  
  #  endif
  
  #  ifndef OPENSSL_NO_DH
  /* This function is aliased to mod_exp (with the dh and mont dropped). */
  static int hwcrhk_mod_exp_dh(const DH *dh, BIGNUM *r,
                               const BIGNUM *a, const BIGNUM *p,
                               const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
  {
      return hwcrhk_mod_exp(r, a, p, m, ctx);
  }
  #  endif
  
  /* Random bytes are good */
  static int hwcrhk_rand_bytes(unsigned char *buf, int num)
  {
      char tempbuf[1024];
      HWCryptoHook_ErrMsgBuf rmsg;
      int to_return = 0;          /* assume failure */
      int ret;
  
      rmsg.buf = tempbuf;
      rmsg.size = sizeof(tempbuf);
  
      if (!hwcrhk_context) {
          HWCRHKerr(HWCRHK_F_HWCRHK_RAND_BYTES, HWCRHK_R_NOT_INITIALISED);
          goto err;
      }
  
      ret = p_hwcrhk_RandomBytes(hwcrhk_context, buf, num, &rmsg);
      if (ret < 0) {
          /*
           * FIXME: When this error is returned, HWCryptoHook is telling us
           * that falling back to software computation might be a good thing.
           */
          if (ret == HWCRYPTOHOOK_ERROR_FALLBACK) {
              HWCRHKerr(HWCRHK_F_HWCRHK_RAND_BYTES, HWCRHK_R_REQUEST_FALLBACK);
          } else {
              HWCRHKerr(HWCRHK_F_HWCRHK_RAND_BYTES, HWCRHK_R_REQUEST_FAILED);
          }
          ERR_add_error_data(1, rmsg.buf);
          goto err;
      }
      to_return = 1;
   err:
      return to_return;
  }
  
  static int hwcrhk_rand_status(void)
  {
      return 1;
  }
  
  /*
   * Mutex calls: since the HWCryptoHook model closely follows the POSIX model
   * these just wrap the POSIX functions and add some logging.
   */
  
  static int hwcrhk_mutex_init(HWCryptoHook_Mutex * mt,
                               HWCryptoHook_CallerContext * cactx)
  {
      mt->lock = CRYPTO_THREAD_lock_new();
      if (mt->lock == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_MUTEX_INIT, ERR_R_MALLOC_FAILURE);
          return 1;               /* failure */
      }
      return 0;                   /* success */
  }
  
  static int hwcrhk_mutex_lock(HWCryptoHook_Mutex * mt)
  {
      CRYPTO_THREAD_write_lock(mt->lock);
      return 0;
  }
  
  static void hwcrhk_mutex_unlock(HWCryptoHook_Mutex * mt)
  {
      CRYPTO_THREAD_unlock(mt->lock);
  }
  
  static void hwcrhk_mutex_destroy(HWCryptoHook_Mutex * mt)
  {
      CRYPTO_THREAD_lock_free(mt->lock);
  }
  
  static int hwcrhk_get_pass(const char *prompt_info,
                             int *len_io, char *buf,
                             HWCryptoHook_PassphraseContext * ppctx,
                             HWCryptoHook_CallerContext * cactx)
  {
      pem_password_cb *callback = NULL;
      void *callback_data = NULL;
      UI_METHOD *ui_method = NULL;
      /*
       * Despite what the documentation says prompt_info can be an empty
       * string.
       */
      if (prompt_info && !*prompt_info)
          prompt_info = NULL;
  
      if (cactx) {
          if (cactx->ui_method)
              ui_method = cactx->ui_method;
          if (cactx->password_callback)
              callback = cactx->password_callback;
          if (cactx->callback_data)
              callback_data = cactx->callback_data;
      }
      if (ppctx) {
          if (ppctx->ui_method) {
              ui_method = ppctx->ui_method;
              callback = NULL;
          }
          if (ppctx->callback_data)
              callback_data = ppctx->callback_data;
      }
      if (callback == NULL && ui_method == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_GET_PASS, HWCRHK_R_NO_CALLBACK);
          return -1;
      }
  
      if (ui_method) {
          UI *ui = UI_new_method(ui_method);
          if (ui) {
              int ok;
              char *prompt = UI_construct_prompt(ui,
                                                 "pass phrase", prompt_info);
  
              ok = UI_add_input_string(ui, prompt,
                                       UI_INPUT_FLAG_DEFAULT_PWD,
                                       buf, 0, (*len_io) - 1);
              UI_add_user_data(ui, callback_data);
              UI_ctrl(ui, UI_CTRL_PRINT_ERRORS, 1, 0, 0);
  
              if (ok >= 0)
                  do {
                      ok = UI_process(ui);
                  }
                  while (ok < 0 && UI_ctrl(ui, UI_CTRL_IS_REDOABLE, 0, 0, 0));
  
              if (ok >= 0)
                  *len_io = strlen(buf);
  
              UI_free(ui);
              OPENSSL_free(prompt);
          }
      } else {
          *len_io = callback(buf, *len_io, 0, callback_data);
      }
      if (!*len_io)
          return -1;
      return 0;
  }
  
  static int hwcrhk_insert_card(const char *prompt_info,
                                const char *wrong_info,
                                HWCryptoHook_PassphraseContext * ppctx,
                                HWCryptoHook_CallerContext * cactx)
  {
      int ok = -1;
      UI *ui;
      void *callback_data = NULL;
      UI_METHOD *ui_method = NULL;
  
      if (cactx) {
          if (cactx->ui_method)
              ui_method = cactx->ui_method;
          if (cactx->callback_data)
              callback_data = cactx->callback_data;
      }
      if (ppctx) {
          if (ppctx->ui_method)
              ui_method = ppctx->ui_method;
          if (ppctx->callback_data)
              callback_data = ppctx->callback_data;
      }
      if (ui_method == NULL) {
          HWCRHKerr(HWCRHK_F_HWCRHK_INSERT_CARD, HWCRHK_R_NO_CALLBACK);
          return -1;
      }
  
      ui = UI_new_method(ui_method);
  
      if (ui) {
          char answer = '\0';
          char buf[BUFSIZ];
          /*
           * Despite what the documentation says wrong_info can be an empty
           * string.
           */
          if (wrong_info && *wrong_info)
              BIO_snprintf(buf, sizeof(buf) - 1,
                           "Current card: \"%s\"\n", wrong_info);
          else
              buf[0] = 0;
          ok = UI_dup_info_string(ui, buf);
          if (ok >= 0 && prompt_info) {
              BIO_snprintf(buf, sizeof(buf) - 1,
                           "Insert card \"%s\"", prompt_info);
              ok = UI_dup_input_boolean(ui, buf,
                                        "\n then hit <enter> or C<enter> to cancel\n",
                                        "\r\n", "Cc", UI_INPUT_FLAG_ECHO,
                                        &answer);
          }
          UI_add_user_data(ui, callback_data);
  
          if (ok >= 0)
              ok = UI_process(ui);
          UI_free(ui);
  
          if (ok == -2 || (ok >= 0 && answer == 'C'))
              ok = 1;
          else if (ok < 0)
              ok = -1;
          else
              ok = 0;
      }
      return ok;
  }
  
  static void hwcrhk_log_message(void *logstr, const char *message)
  {
      BIO *lstream = NULL;
  
      if (logstr)
          lstream = *(BIO **)logstr;
      if (lstream) {
          BIO_printf(lstream, "%s\n", message);
      }
  }
  
  /*
   * This stuff is needed if this ENGINE is being compiled into a
   * self-contained shared-library.
   */
  #  ifndef OPENSSL_NO_DYNAMIC_ENGINE
  static int bind_fn(ENGINE *e, const char *id)
  {
      if (id && (strcmp(id, engine_hwcrhk_id) != 0) &&
          (strcmp(id, engine_hwcrhk_id_alt) != 0))
          return 0;
      if (!bind_helper(e))
          return 0;
      return 1;
  }
  
  IMPLEMENT_DYNAMIC_CHECK_FN()
      IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
  #  endif                        /* OPENSSL_NO_DYNAMIC_ENGINE */
  # endif                         /* !OPENSSL_NO_HW_CHIL */
  #endif                          /* !OPENSSL_NO_HW */
  