]> granicus.if.org Git - openssl/commitdiff
Another ENGINE that's been working in 0.9.6-engine for a while that will
authorGeoff Thorpe <geoff@openssl.org>
Sat, 17 Nov 2001 05:29:25 +0000 (05:29 +0000)
committerGeoff Thorpe <geoff@openssl.org>
Sat, 17 Nov 2001 05:29:25 +0000 (05:29 +0000)
be included for 0.9.6c-engine.

crypto/engine/Makefile.ssl
crypto/engine/engine.h
crypto/engine/engine_err.c
crypto/engine/engine_int.h
crypto/engine/engine_list.c
crypto/engine/hw_keyclient.c [new file with mode: 0644]
crypto/engine/vendor_defns/keyclient.h [new file with mode: 0644]

index 1e50aa4000993184a5d0eed2f5e9d9cd0b2b0bca..d165cbf3932fd9fc2058d7f32c7a37574a04d2f7 100644 (file)
@@ -24,10 +24,10 @@ APPS=
 LIB=$(TOP)/libcrypto.a
 LIBSRC= engine_err.c engine_lib.c engine_list.c engine_openssl.c \
        hw_atalla.c hw_cswift.c hw_ncipher.c hw_aep.c hw_sureware.c \
-       hw_ubsec.c
+       hw_ubsec.c hw_keyclient.c
 LIBOBJ= engine_err.o engine_lib.o engine_list.o engine_openssl.o \
        hw_atalla.o hw_cswift.o hw_ncipher.o hw_aep.o hw_sureware.o \
-       hw_ubsec.o
+       hw_ubsec.o hw_keyclient.o
 
 SRC= $(LIBSRC)
 
@@ -217,6 +217,26 @@ hw_cswift.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
 hw_cswift.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
 hw_cswift.o: ../../include/openssl/symhacks.h ../cryptlib.h engine_int.h
 hw_cswift.o: vendor_defns/cswift.h
+hw_keyclient.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
+hw_keyclient.o: ../../include/openssl/blowfish.h ../../include/openssl/bn.h
+hw_keyclient.o: ../../include/openssl/buffer.h ../../include/openssl/cast.h
+hw_keyclient.o: ../../include/openssl/crypto.h ../../include/openssl/des.h
+hw_keyclient.o: ../../include/openssl/dh.h ../../include/openssl/dsa.h
+hw_keyclient.o: ../../include/openssl/dso.h ../../include/openssl/e_os.h
+hw_keyclient.o: ../../include/openssl/e_os2.h ../../include/openssl/engine.h
+hw_keyclient.o: ../../include/openssl/err.h ../../include/openssl/evp.h
+hw_keyclient.o: ../../include/openssl/idea.h ../../include/openssl/lhash.h
+hw_keyclient.o: ../../include/openssl/md2.h ../../include/openssl/md4.h
+hw_keyclient.o: ../../include/openssl/md5.h ../../include/openssl/mdc2.h
+hw_keyclient.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+hw_keyclient.o: ../../include/openssl/opensslconf.h
+hw_keyclient.o: ../../include/openssl/opensslv.h ../../include/openssl/rand.h
+hw_keyclient.o: ../../include/openssl/rc2.h ../../include/openssl/rc4.h
+hw_keyclient.o: ../../include/openssl/rc5.h ../../include/openssl/ripemd.h
+hw_keyclient.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
+hw_keyclient.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+hw_keyclient.o: ../../include/openssl/symhacks.h ../cryptlib.h engine_int.h
+hw_keyclient.o: vendor_defns/keyclient.h
 hw_ncipher.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
 hw_ncipher.o: ../../include/openssl/blowfish.h ../../include/openssl/bn.h
 hw_ncipher.o: ../../include/openssl/buffer.h ../../include/openssl/cast.h
index 6cc6f5208ac566db2d6d41dee21cb6088454ea54..8b502c0873230ee7d61bb5845b290626153d683c 100644 (file)
@@ -276,9 +276,6 @@ int ENGINE_set_default_BN_mod_exp_crt(ENGINE *e);
  * ENGINE_METHOD_*** defines above. */
 int ENGINE_set_default(ENGINE *e, unsigned int flags);
 
-/* Obligatory error function. */
-void ERR_load_ENGINE_strings(void);
-
 /*
  * Error codes for all engine functions. NB: We use "generic"
  * function names instead of per-implementation ones because this
@@ -293,6 +290,7 @@ void ERR_load_ENGINE_strings(void);
 /* The following lines are auto generated by the script mkerr.pl. Any changes
  * made after this point may be overwritten when the script is next run.
  */
+void ERR_load_ENGINE_strings(void);
 
 /* Error codes for the ENGINE functions. */
 
@@ -362,6 +360,28 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_F_HWCRHK_MOD_EXP_CRT                     138
 #define ENGINE_F_HWCRHK_RAND_BYTES                      139
 #define ENGINE_F_HWCRHK_RSA_MOD_EXP                     140
+#define ENGINE_F_KC_INT_DSA_PRIV                        213
+#define ENGINE_F_KC_INT_DSA_VERIFY                      214
+#define ENGINE_F_KC_INT_RSA_PRIV                        215
+#define ENGINE_F_KC_INT_RSA_PUB                                 216
+#define ENGINE_F_KEYCLIENT_CHECK_GLOBAL                         217
+#define ENGINE_F_KEYCLIENT_DSA_FINISH                   218
+#define ENGINE_F_KEYCLIENT_DSA_INIT                     219
+#define ENGINE_F_KEYCLIENT_DSA_SIGN                     220
+#define ENGINE_F_KEYCLIENT_DSA_VERIFY                   221
+#define ENGINE_F_KEYCLIENT_FINISH                       222
+#define ENGINE_F_KEYCLIENT_GET_DSA_CTX                  223
+#define ENGINE_F_KEYCLIENT_GET_RSA_CTX                  224
+#define ENGINE_F_KEYCLIENT_INIT                                 225
+#define ENGINE_F_KEYCLIENT_PADDING                      226
+#define ENGINE_F_KEYCLIENT_RSA_FINISH                   227
+#define ENGINE_F_KEYCLIENT_RSA_INIT                     228
+#define ENGINE_F_KEYCLIENT_RSA_PRIV_DEC                         229
+#define ENGINE_F_KEYCLIENT_RSA_PRIV_ENC                         230
+#define ENGINE_F_KEYCLIENT_RSA_PUB_DEC                  231
+#define ENGINE_F_KEYCLIENT_RSA_PUB_ENC                  232
+#define ENGINE_F_KEYCLIENT_SET_DSA_CTX                  233
+#define ENGINE_F_KEYCLIENT_SET_RSA_CTX                  234
 #define ENGINE_F_LOG_MESSAGE                            141
 #define ENGINE_F_SUREWAREHK_CTRL                        209
 #define ENGINE_F_SUREWAREHK_DH_GEN_KEY                  210
@@ -406,6 +426,8 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_R_ID_OR_NAME_MISSING                     108
 #define ENGINE_R_INIT_FAILED                            109
 #define ENGINE_R_INTERNAL_LIST_ERROR                    110
+#define ENGINE_R_INVALID_PADDING                        137
+#define ENGINE_R_KEY_TOO_LARGE                          138
 #define ENGINE_R_MISSING_KEY_COMPONENTS                         111
 #define ENGINE_R_MOD_EXP_CRT_FAILED                     134
 #define ENGINE_R_MOD_EXP_FAILED                                 131
@@ -413,6 +435,7 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_R_NOT_LOADED                             112
 #define ENGINE_R_NO_CALLBACK                            127
 #define ENGINE_R_NO_CONTROL_FUNCTION                    120
+#define ENGINE_R_NO_INDEX                               139
 #define ENGINE_R_NO_KEY                                         124
 #define ENGINE_R_NO_LOAD_FUNCTION                       125
 #define ENGINE_R_NO_REFERENCE                           130
@@ -430,4 +453,3 @@ void ERR_load_ENGINE_strings(void);
 }
 #endif
 #endif
-
index 8c23e993b749ab60789257c3f4ad23ce3440df75..e469662425aa7ae608457ab6f0a72408fbeb2973 100644 (file)
@@ -131,6 +131,28 @@ static ERR_STRING_DATA ENGINE_str_functs[]=
 {ERR_PACK(0,ENGINE_F_HWCRHK_MOD_EXP_CRT,0),    "HWCRHK_MOD_EXP_CRT"},
 {ERR_PACK(0,ENGINE_F_HWCRHK_RAND_BYTES,0),     "HWCRHK_RAND_BYTES"},
 {ERR_PACK(0,ENGINE_F_HWCRHK_RSA_MOD_EXP,0),    "HWCRHK_RSA_MOD_EXP"},
+{ERR_PACK(0,ENGINE_F_KC_INT_DSA_PRIV,0),       "KC_INT_DSA_PRIV"},
+{ERR_PACK(0,ENGINE_F_KC_INT_DSA_VERIFY,0),     "KC_INT_DSA_VERIFY"},
+{ERR_PACK(0,ENGINE_F_KC_INT_RSA_PRIV,0),       "KC_INT_RSA_PRIV"},
+{ERR_PACK(0,ENGINE_F_KC_INT_RSA_PUB,0),        "KC_INT_RSA_PUB"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_CHECK_GLOBAL,0),        "KEYCLIENT_CHECK_GLOBAL"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_DSA_FINISH,0),  "KEYCLIENT_DSA_FINISH"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_DSA_INIT,0),    "KEYCLIENT_DSA_INIT"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_DSA_SIGN,0),    "KEYCLIENT_DSA_SIGN"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_DSA_VERIFY,0),  "KEYCLIENT_DSA_VERIFY"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_FINISH,0),      "KEYCLIENT_FINISH"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_GET_DSA_CTX,0), "KEYCLIENT_GET_DSA_CTX"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_GET_RSA_CTX,0), "KEYCLIENT_GET_RSA_CTX"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_INIT,0),        "KEYCLIENT_INIT"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_PADDING,0),     "KEYCLIENT_PADDING"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_FINISH,0),  "KEYCLIENT_RSA_FINISH"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_INIT,0),    "KEYCLIENT_RSA_INIT"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_PRIV_DEC,0),        "KEYCLIENT_RSA_PRIV_DEC"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_PRIV_ENC,0),        "KEYCLIENT_RSA_PRIV_ENC"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_PUB_DEC,0), "KEYCLIENT_RSA_PUB_DEC"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_RSA_PUB_ENC,0), "KEYCLIENT_RSA_PUB_ENC"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_SET_DSA_CTX,0), "KEYCLIENT_SET_DSA_CTX"},
+{ERR_PACK(0,ENGINE_F_KEYCLIENT_SET_RSA_CTX,0), "KEYCLIENT_SET_RSA_CTX"},
 {ERR_PACK(0,ENGINE_F_LOG_MESSAGE,0),   "LOG_MESSAGE"},
 {ERR_PACK(0,ENGINE_F_SUREWAREHK_CTRL,0),       "SUREWAREHK_CTRL"},
 {ERR_PACK(0,ENGINE_F_SUREWAREHK_DH_GEN_KEY,0), "SUREWAREHK_DH_GEN_KEY"},
@@ -178,6 +200,8 @@ static ERR_STRING_DATA ENGINE_str_reasons[]=
 {ENGINE_R_ID_OR_NAME_MISSING             ,"'id' or 'name' missing"},
 {ENGINE_R_INIT_FAILED                    ,"init failed"},
 {ENGINE_R_INTERNAL_LIST_ERROR            ,"internal list error"},
+{ENGINE_R_INVALID_PADDING                ,"invalid padding"},
+{ENGINE_R_KEY_TOO_LARGE                  ,"key too large"},
 {ENGINE_R_MISSING_KEY_COMPONENTS         ,"missing key components"},
 {ENGINE_R_MOD_EXP_CRT_FAILED             ,"mod exp crt failed"},
 {ENGINE_R_MOD_EXP_FAILED                 ,"mod exp failed"},
@@ -185,6 +209,7 @@ static ERR_STRING_DATA ENGINE_str_reasons[]=
 {ENGINE_R_NOT_LOADED                     ,"not loaded"},
 {ENGINE_R_NO_CALLBACK                    ,"no callback"},
 {ENGINE_R_NO_CONTROL_FUNCTION            ,"no control function"},
+{ENGINE_R_NO_INDEX                       ,"no index"},
 {ENGINE_R_NO_KEY                         ,"no key"},
 {ENGINE_R_NO_LOAD_FUNCTION               ,"no load function"},
 {ENGINE_R_NO_REFERENCE                   ,"no reference"},
index 42df1059783ea91bb85a036d24741c2a78b9a549..f4f6abe3927e705e2caf131d363c55ee187ce361 100644 (file)
@@ -165,6 +165,11 @@ ENGINE *ENGINE_sureware();
 /* Returns a structure of ubsec methods. */
 ENGINE *ENGINE_ubsec();
 #endif /* !NO_HW_UBSEC */
+
+#ifndef NO_HW_KEYCLIENT
+/* Returns a structure of keyclient methods. */
+ENGINE *ENGINE_keyclient();
+#endif /* !NO_HW_KEYCLIENT */
 #endif /* !NO_HW */
 
 #ifdef  __cplusplus
index 977ef3dc5f0a9d42b85b2dd086c29c39b8133204..6d1c3b35a1510d082ac0fa5704a215093b583b63 100644 (file)
@@ -210,6 +210,10 @@ static int engine_internal_check(void)
        if(!engine_list_add(ENGINE_ubsec()))
                return 0;
 #endif /* !NO_HW_UBSEC */
+#ifndef NO_HW_KEYCLIENT
+       if(!engine_list_add(ENGINE_keyclient()))
+               return 0;
+#endif /* !NO_HW_KEYCLIENT */
 #endif /* !NO_HW */
        engine_list_flag = 1;
        return 1;
diff --git a/crypto/engine/hw_keyclient.c b/crypto/engine/hw_keyclient.c
new file mode 100644 (file)
index 0000000..257d243
--- /dev/null
@@ -0,0 +1,1010 @@
+/* crypto/engine/hw_keyclient.c */
+/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
+ * project 2001.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <stdio.h>
+#include <openssl/crypto.h>
+#include "cryptlib.h"
+#include <openssl/dso.h>
+#include "engine_int.h"
+#include <openssl/engine.h>
+
+#ifndef NO_HW
+#ifndef NO_HW_KEYCLIENT
+
+#ifdef FLAT_INC
+#include "keyclient.h"
+#else
+#include "vendor_defns/keyclient.h"
+#endif
+
+
+/********************************/
+/* Static function declarations */
+/********************************/
+
+/* ENGINE level stuff */
+static int keyclient_init(void);
+static int keyclient_finish(void);
+/* static int keyclient_ctrl(int cmd, long i, void *p, void (*f)()); */
+
+/* RSA stuff */
+static int keyclient_rsa_pub_enc(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding);
+static int keyclient_rsa_pub_dec(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding);
+static int keyclient_rsa_priv_enc(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding);
+static int keyclient_rsa_priv_dec(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding);
+static int keyclient_rsa_init(RSA *rsa);
+static int keyclient_rsa_finish(RSA *rsa);
+
+/* DSA stuff */
+static DSA_SIG *keyclient_dsa_sign(const unsigned char *dgst, int dlen,
+               DSA *dsa);
+static int keyclient_dsa_verify(const unsigned char *dgst, int dgst_len,
+               DSA_SIG *sig, DSA *dsa);
+static int keyclient_dsa_init(DSA *dsa);
+static int keyclient_dsa_finish(DSA *dsa);
+
+/* DH stuff */
+/* ... */
+
+/* Our internal RSA_METHOD that we provide pointers to */
+static RSA_METHOD keyclient_rsa =
+       {
+       "KeyClient RSA method",
+       keyclient_rsa_pub_enc,
+       keyclient_rsa_pub_dec,
+       keyclient_rsa_priv_enc,
+       keyclient_rsa_priv_dec,
+       NULL,
+       NULL,
+       keyclient_rsa_init,
+       keyclient_rsa_finish,
+       0,
+       NULL,
+       NULL,
+       NULL
+       };
+
+/* Our internal DSA_METHOD that we provide pointers to */
+static DSA_METHOD keyclient_dsa =
+       {
+       "KeyClient DSA method",
+       keyclient_dsa_sign,
+       NULL,
+       keyclient_dsa_verify,
+       NULL,
+       NULL,
+       keyclient_dsa_init,
+       keyclient_dsa_finish,
+       0,
+       NULL
+       };
+
+/* Our internal DH_METHOD that we provide pointers to */
+/* ... */
+
+/* Our ENGINE structure. */
+static ENGINE engine_keyclient =
+        {
+       "keyclient",
+       "CryptoApps distributed crypto client support",
+       &keyclient_rsa,
+       &keyclient_dsa,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       keyclient_init,
+       keyclient_finish,
+       NULL, /* no ctrl() */
+       NULL, /* no load_privkey() */
+       NULL, /* no load_pubkey() */
+       RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY, /* flags */
+       0, 0, /* no references */
+       NULL, NULL /* unlinked */
+        };
+
+/* As this is only ever called once, there's no need for locking (indeed - the
+ * lock will already be held by our caller!!!) */
+ENGINE *ENGINE_keyclient()
+       {
+       return &engine_keyclient;
+       }
+
+static DSO *keyclient_dso = NULL;
+
+/* These are the function pointers that are (un)set when the library has
+ * successfully (un)loaded. */
+t_keyclient_bind_symbols *p_keyclient_bind_symbols = NULL;
+keyclient_symbol_table kc =
+       { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+/* Used in the DSO operations. */
+static const char *KEYCLIENT_LIBNAME = "ksclient";
+static const char *KEYCLIENT_F1 = "keyclient_bind_symbols";
+
+/* If a key isn't supported or there is a failure, we won't retry until at least
+ * this many seconds later. */
+static unsigned int keyclient_retry_period = 3;
+
+/* When a key isn't supported or there is a failure, we work locally (or fail)
+ * for the above retry period. If this value is non-zero, we will log a message
+ * to stderr each time such a retry/hibernation takes place. */
+static int display_warnings = 0;
+
+/****************************/
+/* Internal structure types */
+/****************************/
+
+typedef enum {
+       /* Haven't determined yet if the keyclient_ctx we're using will support
+        * this key. */
+       kc_discover,
+       /* The keyclient_ctx supports this key. */
+       kc_present,
+       /* The keyserver does *not* have this key. Will check again in a few
+        * seconds. */
+       kc_absent,
+       /* Receiving errors of some kind, will rollback to software (or fail)
+        * for a few seconds and try again. */
+       kc_error
+} kc_exist_state;
+
+/* This structure is embedded in each key */
+typedef struct _kc_per_key_ctx {
+       /* The keyclient_ctx as used by the keyclient API */
+       keyclient_ctx *ctx;
+       /* In the event of "local" operation, this is the engine to do it. */
+       ENGINE *fallback;
+       /* Initialise-on-first-use flag */
+       int initialised;
+       /* What state we are currently in. */
+       kc_exist_state exist_state;
+       /* When the "kc_absent" or "kc_err" state arrives, this time value is
+        * set. After "keyclient_retry_seconds", a retry will be attempted. */
+       time_t retry_marker;
+       /* If doing a private key operation and the keyclient_ctx does not
+        * support this key, do we act locally rather than failing? */
+       int private;
+       /* If doing a public key operation and the keyclient_ctx does not
+        * support this key, do we embed the key or act locally? */
+       enum {
+               kc_embed,
+               kc_local
+       } public;
+       /* DER encoding of the public key */
+       keyclient_key_t public_der;
+       /* SHA1 hash of the public key modulus */
+       unsigned char public_sha1[SHA_DIGEST_LENGTH];
+} kc_per_key_ctx;
+
+#define KC_MAX_ADDRESS_SIZE 100
+
+/* This structure is kept globally */
+typedef struct _kc_global_ctx {
+       /* Has this been initialised yet? */
+       int initialised;
+       /* The current "context of choice" */
+       keyclient_ctx *ctx;
+       /* The address of the current "context of choice", must be
+        * null-terminated! */
+       char address[KC_MAX_ADDRESS_SIZE];
+} kc_global_ctx;
+
+static kc_global_ctx kc_global =
+       {
+       0, /* not initialised */
+       NULL, /* no context yet */
+       "UNIX:/tmp/kclient" /* default address string */
+       };
+
+/* In a no-threading environment where file-descriptor limits won't be a
+ * problem, we might prefer;
+ *     KC_FLAG_PERSISTENT_CONN | KC_FLAG_PID_CHECK | \
+ *     KC_FLAG_PERSISTENT_RETRY | KC_FLAG_PERSISTENT_LATE
+ * But in general we can't assume that. */
+unsigned int kc_create_flags = KC_FLAG_NO_LOCKING;
+
+/* Setup default global context */
+static int keyclient_global_check()
+       {
+       if(kc_global.initialised)
+               return 1;
+       if(kc.keyclient_create(&kc_global.ctx, kc_global.address,
+                       kc_create_flags, NULL) != KC_RET_OK)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_CHECK_GLOBAL,ENGINE_R_UNIT_FAILURE);
+               return 0;
+               }
+       kc_global.initialised = 1;
+       return 1;
+       }
+
+/* Where in the CRYPTO_EX_DATA stack we stick our per-key contexts */
+static int kc_rsa_ex_data_idx = -1;
+static int kc_dsa_ex_data_idx = -1;
+
+/* Convert OpenSSL padding enumerated type to keyclient enumerated type */
+static int keyclient_padding(int padding)
+       {
+       switch(padding)
+               {
+       case RSA_PKCS1_PADDING:
+               return KC_PADDING_RSA_PKCS1;
+       case RSA_SSLV23_PADDING:
+               return KC_PADDING_RSA_SSLV23;
+       case RSA_NO_PADDING:
+               return KC_PADDING_NONE;
+       case RSA_PKCS1_OAEP_PADDING:
+               return KC_PADDING_RSA_PKCS1_OAEP;
+       default:
+               break;
+               }
+       ENGINEerr(ENGINE_F_KEYCLIENT_PADDING,ENGINE_R_INVALID_PADDING);
+       return -1;
+       }
+
+/* (de)initialisation functions. */
+static int keyclient_init(void)
+       {
+       if(keyclient_dso != NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_INIT,ENGINE_R_ALREADY_LOADED);
+               goto err;
+               }
+       /* First, ensure we have index numbers we can use for our RSA and DSA
+        * key contexts */
+       if(kc_rsa_ex_data_idx == -1)
+               kc_rsa_ex_data_idx = RSA_get_ex_new_index(
+                               0, NULL, NULL, NULL, NULL);
+       if(kc_dsa_ex_data_idx == -1)
+               kc_dsa_ex_data_idx = DSA_get_ex_new_index(
+                               0, NULL, NULL, NULL, NULL);
+       if((kc_rsa_ex_data_idx == -1) || (kc_dsa_ex_data_idx == -1))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_INIT,ENGINE_R_NO_INDEX);
+               goto err;
+               }
+       /* Attempt to load libksclient.so/ksclient.dll/whatever. */
+       keyclient_dso = DSO_load(NULL, KEYCLIENT_LIBNAME, NULL,
+               DSO_FLAG_NAME_TRANSLATION);
+       if(keyclient_dso == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_INIT,ENGINE_R_DSO_FAILURE);
+               goto err;
+               }
+       if(!(p_keyclient_bind_symbols = (t_keyclient_bind_symbols *)
+                               DSO_bind_func(keyclient_dso, KEYCLIENT_F1)))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_INIT,ENGINE_R_DSO_FAILURE);
+               goto err;
+               }
+       p_keyclient_bind_symbols(&kc);
+       if(!keyclient_global_check())
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_INIT,ENGINE_R_UNIT_FAILURE);
+               goto err;
+               }
+       /* Everything should be fine. */
+       return 1;
+err:
+       if(keyclient_dso)
+               DSO_free(keyclient_dso);
+       keyclient_dso = NULL;
+       p_keyclient_bind_symbols = NULL;
+       return 0;
+       }
+
+static int keyclient_finish(void)
+       {
+       if(keyclient_dso == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_FINISH,ENGINE_R_NOT_LOADED);
+               return 0;
+               }
+       if(!DSO_free(keyclient_dso))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_FINISH,ENGINE_R_DSO_FAILURE);
+               return 0;
+               }
+       keyclient_dso = NULL;
+       p_keyclient_bind_symbols = NULL;
+       return 1;
+       }
+
+/* Space-saving functions. These two functions, although having odd prototypes,
+ * save some code duplication in the "kc_int_[rsa|dsa]_[pub|priv]" functions. */
+
+static void kc_should_retry_util(kc_exist_state *s, time_t retry)
+       {
+       if((*s == kc_error) || (*s == kc_absent))
+               {
+               time_t now = time(NULL);
+               if((now - retry) >= keyclient_retry_period)
+                       *s = kc_discover;
+               }
+       }
+
+static int kc_post_op_util(KC_RET ret, kc_exist_state *s, time_t *retry)
+       {
+       if(ret == KC_RET_OK)
+               {
+               *s = kc_present;
+               return 1;
+               }
+       if(ret == KC_RET_SOFT_NO_SUCH_KEY)
+               {
+               *s = kc_absent;
+               *retry = time(NULL);
+               }
+       else
+               {
+               /* We'll set the state and retry_marker so that another genuine
+                * attempt at communication is attempted later on. */
+               *s = kc_error;
+               *retry = time(NULL);
+               /* We could drill further into ret at this point to provide more
+                * meaningful errors - but we don't. */
+               if(display_warnings)
+                       fprintf(stderr, "WARNING: Keyclient engine experiencing"
+                               " errors. Will try to continue.\n");
+               }
+       return 0;
+       }
+
+static int kc_post_embed_util(KC_RET ret, kc_exist_state *s, time_t *retry)
+       {
+       if(ret == KC_RET_OK)
+               {
+               *s = kc_absent;
+               return 1;
+               }
+       /* We'll set the state and retry_marker so that another genuine attempt
+        * at communication is attempted later on. */
+       *s = kc_error;
+       *retry = time(NULL);
+       /* We could drill further into ret at this point to provide more
+        * meaningful errors - but for now, just use something base-level. */
+       if(display_warnings)
+               fprintf(stderr, "WARNING: Keyclient engine experiencing"
+                       " errors. Will try to continue.\n");
+       return 0;
+       }
+
+/*************/
+/* RSA STUFF */
+/*************/
+
+/* Set a key's per-key context. */
+static int keyclient_set_rsa_ctx(RSA *rsa, kc_per_key_ctx *ctx)
+       {
+       if(kc_rsa_ex_data_idx == -1)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_SET_RSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return 0;
+               }
+       return CRYPTO_set_ex_data(&rsa->ex_data, kc_rsa_ex_data_idx,
+                       (void *)ctx);
+       }
+
+/* Retrieve the pointer to a key's per-key context. */
+static kc_per_key_ctx *keyclient_get_rsa_ctx(RSA *rsa)
+       {
+       unsigned char *ptr, *bnbin = NULL;
+       EVP_MD_CTX md_ctx;
+       int bin_size;
+       kc_per_key_ctx *ctx;
+
+
+       if((kc_rsa_ex_data_idx == -1) || ((ctx =
+                       (kc_per_key_ctx *)CRYPTO_get_ex_data(&rsa->ex_data,
+                               kc_rsa_ex_data_idx)) == NULL))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return NULL;
+               }
+       if(ctx->initialised)
+               return ctx;
+       if((rsa->n == NULL) || (BN_num_bytes(rsa->n) < 32))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return NULL;
+               }
+       bin_size = BN_num_bytes(rsa->n);
+       ctx->exist_state = kc_discover;
+       /* Choose whether to fallback to software for failed private key
+        * operations. */
+       if(rsa->dmp1)
+               ctx->private = 1;
+       else
+               ctx->private = 0;
+       /* If the public key is large enough, we will send it to the keyserver
+        * if it doesn't have it. Otherwise, the ASN workload isn't worth the
+        * hassle and we act locally. */
+       if(RSA_size(rsa) > 128)
+               ctx->public = kc_embed;
+       else
+               ctx->public = kc_local;
+       /* We have no "retry" situation yet so set the retry_marker to 1970 or
+        * something. */
+       ctx->retry_marker = (time_t)0;
+       /* Produce the key embedding */
+       ctx->public_der.key_type = KC_KEY_RSA;
+       ctx->public_der.der_len = i2d_RSAPublicKey(rsa, NULL);
+       if(ctx->public_der.der_len > KC_MAX_PUBKEY_ASN)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,ENGINE_R_KEY_TOO_LARGE);
+               return NULL;
+               }
+       ptr = ctx->public_der.der;
+       i2d_RSAPublicKey(rsa, &ptr);
+       /* Now the keyhash */
+       if((bnbin = OPENSSL_malloc(bin_size)) == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,ERR_R_MALLOC_FAILURE);
+               return NULL;
+               }
+       BN_bn2bin(rsa->n, bnbin);
+       EVP_DigestInit(&md_ctx, EVP_sha1());
+       EVP_DigestUpdate(&md_ctx, bnbin, bin_size);
+       EVP_DigestFinal(&md_ctx, ctx->public_sha1, NULL);
+       OPENSSL_free(bnbin);
+       /* Now setup the software fallback */
+       if((ctx->fallback = ENGINE_by_id("openssl")) == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,ENGINE_R_NO_SUCH_ENGINE);
+               return NULL;
+               }
+       /* And grab a functional reference (NB: We reuse bin_size as a temp) */
+       bin_size = ENGINE_init(ctx->fallback);
+       /* Release the original structural reference */
+       ENGINE_free(ctx->fallback);
+       if(!bin_size)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,ENGINE_R_INIT_FAILED);
+               return NULL;
+               }
+       /* Finally, duplicate the global keyclient context */
+       if(kc.keyclient_dup(kc_global.ctx) != KC_RET_OK)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_RSA_CTX,ENGINE_R_UNIT_FAILURE);
+               return NULL;
+               }
+       ctx->ctx = kc_global.ctx;
+       /* Success! */
+       ctx->initialised = 1;
+       keyclient_set_rsa_ctx(rsa, ctx);
+       return ctx;
+       }
+
+static int keyclient_rsa_init(RSA *rsa)
+       {
+       kc_per_key_ctx *ctx = OPENSSL_malloc(sizeof(kc_per_key_ctx));
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_INIT,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       /* Success! */
+       ctx->initialised = 0;
+       keyclient_set_rsa_ctx(rsa, ctx);
+       return 1;
+       }
+
+static int keyclient_rsa_finish(RSA *rsa)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_rsa_ctx(rsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_FINISH,
+                               ENGINE_R_NOT_INITIALISED);
+               return 0;
+               }
+       /* Destroy the "ctx"'s contents before destroying it */
+       if(!ENGINE_finish(ctx->fallback))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_FINISH,ENGINE_R_FINISH_FAILED);
+               return 0;
+               }
+       if(kc.keyclient_release(ctx->ctx) != KC_RET_OK)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_FINISH,ENGINE_R_UNIT_FAILURE);
+               return 0;
+               }
+       /* Great, remove the context from ex_data, and free it */
+       keyclient_set_rsa_ctx(rsa, NULL);
+       OPENSSL_free(ctx);
+       return 1;
+       }
+
+static int kc_int_rsa_pub(RSA *rsa, kc_per_key_ctx *ctx,
+               unsigned char *from, int flen, unsigned char *to,
+               int tolen, int padding, int is_dec)
+       {
+       KC_RET ret;
+       const RSA_METHOD *rsa_meth;
+       RSA rsa_copy;
+       keyclient_op_t op;
+       keyclient_pad_t pad;
+       unsigned int len = tolen;
+       /* If we're in a "down" moment and need to periodically try again, check
+        * the time. */
+       kc_should_retry_util(&ctx->exist_state, ctx->retry_marker);
+       op = (is_dec ? KC_RSA_PUB_DECRYPT : KC_RSA_PUB_ENCRYPT);
+       pad = keyclient_padding(padding);
+       /* The obvious preference is to use "keyhash"ing */
+       if((ctx->exist_state == kc_discover) ||
+                       (ctx->exist_state == kc_present))
+               {
+               ret = kc.keyclient_keyop(ctx->ctx, op, from, flen, to, &len,
+                               pad, ctx->public_sha1);
+               if(kc_post_op_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       return len;
+               }
+       /* Now we try "embedding" */
+       if((ctx->exist_state != kc_error) && (ctx->public == kc_embed))
+               {
+               /* The above code branch may have modified "len" */
+               len = tolen;
+               ret = kc.keyclient_pubkeyop(ctx->ctx, op, from, flen, to, &len,
+                               pad, &ctx->public_der);
+               if(kc_post_embed_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       return len;
+               }
+       /* This copying trickery is to prevent the fallback implementation
+        * actually using our bignum functions. */
+       memcpy(&rsa_copy, rsa, sizeof(RSA));
+       rsa_copy.engine = ctx->fallback;
+       rsa_meth = ENGINE_get_RSA(ctx->fallback);
+       if(!rsa_meth)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_RSA_PUB,ENGINE_R_REQUEST_FALLBACK);
+               return -1;
+               }
+       if(is_dec)
+               return rsa_meth->rsa_pub_dec(flen, from, to, &rsa_copy, pad);
+       return rsa_meth->rsa_pub_enc(flen, from, to, &rsa_copy, pad);
+       }
+
+static int kc_int_rsa_priv(RSA *rsa, kc_per_key_ctx *ctx,
+               unsigned char *from, int flen, unsigned char *to,
+               int tolen, int padding, int is_dec)
+       {
+       KC_RET ret;
+       const RSA_METHOD *rsa_meth;
+       RSA rsa_copy;
+       keyclient_op_t op;
+       keyclient_pad_t pad;
+       unsigned int len = tolen;
+       /* If we're in a "down" moment and need to periodically try again, check
+        * the time. */
+       kc_should_retry_util(&ctx->exist_state, ctx->retry_marker);
+       op = (is_dec ? KC_RSA_PRIV_DECRYPT : KC_RSA_PRIV_ENCRYPT);
+       pad = keyclient_padding(padding);
+       /* The obvious preference is to use "keyhash"ing */
+       if((ctx->exist_state == kc_discover) ||
+                       (ctx->exist_state == kc_present))
+               {
+               ret = kc.keyclient_keyop(ctx->ctx, op, from, flen, to, &len,
+                               pad, ctx->public_sha1);
+               if(kc_post_op_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       return len;
+               }
+       /* If we prohibit "local" operation, we fail */
+       if(!ctx->private)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_RSA_PRIV,ENGINE_R_UNIT_FAILURE);
+               return -1;
+               }
+       /* This copying trickery is to prevent the fallback implementation
+        * actually using our bignum functions. */
+       memcpy(&rsa_copy, rsa, sizeof(RSA));
+       rsa_copy.engine = ctx->fallback;
+       rsa_meth = ENGINE_get_RSA(ctx->fallback);
+       if(!rsa_meth)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_RSA_PRIV,ENGINE_R_REQUEST_FALLBACK);
+               return -1;
+               }
+       if(is_dec)
+               return rsa_meth->rsa_priv_dec(flen, from, to, &rsa_copy, pad);
+       return rsa_meth->rsa_priv_enc(flen, from, to, &rsa_copy, pad);
+       }
+
+static int keyclient_rsa_pub_enc(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_rsa_ctx(rsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_PUB_ENC,
+                               ENGINE_R_NOT_INITIALISED);
+               return -1;
+               }
+       return kc_int_rsa_pub(rsa, ctx, from, flen, to,
+                       BN_num_bytes(rsa->n), padding, 0);
+       }
+
+static int keyclient_rsa_pub_dec(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_rsa_ctx(rsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_PUB_DEC,
+                               ENGINE_R_NOT_INITIALISED);
+               return -1;
+               }
+       return kc_int_rsa_pub(rsa, ctx, from, flen, to,
+                       BN_num_bytes(rsa->n), padding, 1);
+       }
+
+static int keyclient_rsa_priv_enc(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_rsa_ctx(rsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_PRIV_ENC,
+                               ENGINE_R_NOT_INITIALISED);
+               return -1;
+               }
+       return kc_int_rsa_priv(rsa, ctx, from, flen, to,
+                       BN_num_bytes(rsa->n), padding, 0);
+       }
+
+static int keyclient_rsa_priv_dec(int flen, unsigned char *from,
+               unsigned char *to, RSA *rsa, int padding)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_rsa_ctx(rsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_RSA_PRIV_DEC,
+                               ENGINE_R_NOT_INITIALISED);
+               return -1;
+               }
+       return kc_int_rsa_priv(rsa, ctx, from, flen, to,
+                       BN_num_bytes(rsa->n), padding, 1);
+       }
+
+/*************/
+/* DSA STUFF */
+/*************/
+
+/* Set a key's per-key context. */
+static int keyclient_set_dsa_ctx(DSA *dsa, kc_per_key_ctx *ctx)
+       {
+       if(kc_dsa_ex_data_idx == -1)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_SET_DSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return 0;
+               }
+       return CRYPTO_set_ex_data(&dsa->ex_data, kc_dsa_ex_data_idx,
+                       (void *)ctx);
+       }
+
+/* Retrieve the pointer to a key's per-key context. */
+static kc_per_key_ctx *keyclient_get_dsa_ctx(DSA *dsa)
+       {
+       unsigned char *ptr, *bnbin = NULL;
+       EVP_MD_CTX md_ctx;
+       int bin_size;
+       kc_per_key_ctx *ctx;
+
+       if((kc_dsa_ex_data_idx == -1) || ((ctx =
+                       (kc_per_key_ctx *)CRYPTO_get_ex_data(&dsa->ex_data,
+                               kc_dsa_ex_data_idx)) == NULL))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return NULL;
+               }
+       if(ctx->initialised)
+               return ctx;
+       if((dsa->pub_key == NULL) || (BN_num_bytes(dsa->pub_key) < 32))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,
+                               ENGINE_R_NOT_INITIALISED);
+               return NULL;
+               }
+       bin_size = BN_num_bytes(dsa->pub_key);
+       ctx->exist_state = kc_discover;
+       /* Choose whether to fallback to software for failed private key
+        * operations. */
+       if(dsa->priv_key)
+               ctx->private = 1;
+       else
+               ctx->private = 0;
+       /* If the public key is large enough, we will send it to the keyserver
+        * if it doesn't have it. Otherwise, the ASN workload isn't worth the
+        * hassle and we act locally. */
+       if(BN_num_bytes(dsa->pub_key) >= 64)
+               ctx->public = kc_embed;
+       else
+               ctx->public = kc_local;
+       /* We have no "retry" situation yet so set the retry_marker to 1970 or
+        * something. */
+       ctx->retry_marker = (time_t)0;
+       /* Produce the key embedding */
+       ctx->public_der.key_type = KC_KEY_DSA;
+       ctx->public_der.der_len = i2d_DSAPublicKey(dsa, NULL);
+       if(ctx->public_der.der_len > KC_MAX_PUBKEY_ASN)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,ENGINE_R_KEY_TOO_LARGE);
+               return NULL;
+               }
+       ptr = ctx->public_der.der;
+       i2d_DSAPublicKey(dsa, &ptr);
+       /* Now the keyhash */
+       if((bnbin = OPENSSL_malloc(bin_size)) == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,ERR_R_MALLOC_FAILURE);
+               return NULL;
+               }
+       BN_bn2bin(dsa->pub_key, bnbin);
+       EVP_DigestInit(&md_ctx, EVP_sha1());
+       EVP_DigestUpdate(&md_ctx, bnbin, bin_size);
+       EVP_DigestFinal(&md_ctx, ctx->public_sha1, NULL);
+       OPENSSL_free(bnbin);
+       /* Now setup the software fallback */
+       if((ctx->fallback = ENGINE_by_id("openssl")) == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,ENGINE_R_NO_SUCH_ENGINE);
+               return NULL;
+               }
+       /* And grab a functional reference (NB: We reuse bin_size as a temp) */
+       bin_size = ENGINE_init(ctx->fallback);
+       /* Release the original structural reference */
+       ENGINE_free(ctx->fallback);
+       if(!bin_size)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,ENGINE_R_INIT_FAILED);
+               return NULL;
+               }
+       /* Finally duplicate the global keyclient context */
+       if(kc.keyclient_dup(kc_global.ctx) != KC_RET_OK)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_GET_DSA_CTX,ENGINE_R_UNIT_FAILURE);
+               return NULL;
+               }
+       ctx->ctx = kc_global.ctx;
+       /* Success! */
+       ctx->initialised = 1;
+       keyclient_set_dsa_ctx(dsa, ctx);
+       return ctx;
+       }
+
+static int keyclient_dsa_init(DSA *dsa)
+       {
+       kc_per_key_ctx *ctx = OPENSSL_malloc(sizeof(kc_per_key_ctx));
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_INIT,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       /* Success! */
+       ctx->initialised = 0;
+       keyclient_set_dsa_ctx(dsa, ctx);
+       return 1;
+       }
+
+static int keyclient_dsa_finish(DSA *dsa)
+       {
+       kc_per_key_ctx *ctx = keyclient_get_dsa_ctx(dsa);
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_FINISH,
+                               ENGINE_R_NOT_INITIALISED);
+               return 0;
+               }
+       /* Destroy the "ctx"'s contents before destroying it */
+       if(!ENGINE_finish(ctx->fallback))
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_FINISH,ENGINE_R_FINISH_FAILED);
+               return 0;
+               }
+       if(kc.keyclient_release(ctx->ctx) != KC_RET_OK)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_FINISH,ENGINE_R_UNIT_FAILURE);
+               return 0;
+               }
+       /* Great, remove the context from ex_data, and free it */
+       keyclient_set_dsa_ctx(dsa, NULL);
+       OPENSSL_free(ctx);
+       return 1;
+       }
+
+static DSA_SIG *keyclient_dsa_sign(const unsigned char *dgst, int dlen,
+               DSA *dsa)
+       {
+       KC_RET ret;
+       const DSA_METHOD *dsa_meth;
+       DSA dsa_copy;
+       unsigned char result[256];
+       unsigned int result_len = sizeof(result);
+       unsigned char *cptr = NULL;
+       DSA_SIG *to_return = NULL;
+       keyclient_op_t op = KC_DSA_SIGN;
+       keyclient_pad_t pad = KC_PADDING_DSA;
+       kc_per_key_ctx *ctx = keyclient_get_dsa_ctx(dsa);
+
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_SIGN,ENGINE_R_NOT_INITIALISED);
+               return NULL;
+               }
+       /* If we're in a "down" moment and need to periodically try again, check
+        * the time. */
+       kc_should_retry_util(&ctx->exist_state, ctx->retry_marker);
+       /* The obvious preference is to use "keyhash"ing */
+       if((ctx->exist_state == kc_discover) ||
+                       (ctx->exist_state == kc_present))
+               {
+               ret = kc.keyclient_keyop(ctx->ctx, op, dgst, dlen,
+                               result, &result_len, pad, ctx->public_sha1);
+               if(kc_post_op_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       {
+                       /* We parse the result as an ASN-encoded signature */
+                       cptr = result;
+                       to_return = d2i_DSA_SIG(NULL, &cptr, result_len);
+                       return to_return;
+                       }
+               }
+       /* If we prohibit "local" operation, we fail */
+       if(!ctx->private)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_DSA_PRIV,ENGINE_R_UNIT_FAILURE);
+               return NULL;
+               }
+       /* This copying trickery is to prevent the fallback implementation
+        * actually using our bignum functions. */
+       memcpy(&dsa_copy, dsa, sizeof(DSA));
+       dsa_copy.engine = ctx->fallback;
+       dsa_meth = ENGINE_get_DSA(ctx->fallback);
+       if(!dsa_meth)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_DSA_PRIV,ENGINE_R_REQUEST_FALLBACK);
+               return NULL;
+               }
+       return dsa_meth->dsa_do_sign(dgst, dlen, &dsa_copy);
+       }
+
+static int keyclient_dsa_verify(const unsigned char *dgst, int dgst_len,
+               DSA_SIG *sig, DSA *dsa)
+       {
+       KC_RET ret;
+       const DSA_METHOD *dsa_meth;
+       DSA dsa_copy;
+       unsigned char response;
+       unsigned int response_len = 1;
+       unsigned char *ptr;
+       unsigned int flen;
+       unsigned char *from = NULL;
+       keyclient_op_t op = KC_DSA_VERIFY;
+       keyclient_pad_t pad = KC_PADDING_DSA;
+       kc_per_key_ctx *ctx = keyclient_get_dsa_ctx(dsa);
+
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_VERIFY,
+                               ENGINE_R_NOT_INITIALISED);
+               return -1;
+               }
+       flen = i2d_DSA_SIG(sig, NULL);
+       if((from = OPENSSL_malloc(flen + dgst_len + 1)) == NULL)
+               {
+               ENGINEerr(ENGINE_F_KEYCLIENT_DSA_VERIFY,ERR_R_MALLOC_FAILURE);
+               return -1;
+               }
+       ptr = from;
+       flen = i2d_DSA_SIG(sig, &ptr);
+       /* Some versions of OpenSSL have a bug where 'ptr' is not incremented in
+        * this case. */
+       if(ptr == from)
+               ptr += flen;
+       memcpy(ptr, dgst, dgst_len);
+       flen += dgst_len;
+       /* If we're in a "down" moment and need to periodically try again, check
+        * the time. */
+       kc_should_retry_util(&ctx->exist_state, ctx->retry_marker);
+       /* The obvious preference is to use "keyhash"ing */
+       if((ctx->exist_state == kc_discover) ||
+                       (ctx->exist_state == kc_present))
+               {
+               ret = kc.keyclient_keyop(ctx->ctx, op, from, flen, &response,
+                               &response_len, pad, ctx->public_sha1);
+               if(kc_post_op_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       {
+                       OPENSSL_free(from);
+                       return response_len;
+                       }
+               }
+       /* Now we try "embedding" */
+       if((ctx->exist_state != kc_error) && (ctx->public == kc_embed))
+               {
+               /* The above code branch could change "response_len" */
+               response_len = 1;
+               ret = kc.keyclient_pubkeyop(ctx->ctx, op, from, flen, &response,
+                               &response_len, pad, &ctx->public_der);
+               OPENSSL_free(from);
+               if(kc_post_embed_util(ret, &ctx->exist_state, &ctx->retry_marker))
+                       return response_len;
+               }
+       /* This copying trickery is to prevent the fallback implementation
+        * actually using our bignum functions. */
+       OPENSSL_free(from);
+       memcpy(&dsa_copy, dsa, sizeof(DSA));
+       dsa_copy.engine = ctx->fallback;
+       dsa_meth = ENGINE_get_DSA(ctx->fallback);
+       if(!dsa_meth)
+               {
+               ENGINEerr(ENGINE_F_KC_INT_DSA_VERIFY,ENGINE_R_REQUEST_FALLBACK);
+               return -1;
+               }
+       return dsa_meth->dsa_do_verify(dgst, dgst_len, sig, &dsa_copy);
+       }
+
+#endif /* !NO_HW_KEYCLIENT */
+#endif /* !NO_HW */
diff --git a/crypto/engine/vendor_defns/keyclient.h b/crypto/engine/vendor_defns/keyclient.h
new file mode 100644 (file)
index 0000000..66d0ab6
--- /dev/null
@@ -0,0 +1,214 @@
+#ifndef HEADER_KEYCLIENT_H
+#define HEADER_KEYCLIENT_H
+
+/* Stop name-munging before it begins */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This header is consistent with the "libclient" header but has been modified
+ * and pruned (esp. virtually all explanations and comments have gone) for
+ * inclusion in openssl. */
+
+#if 0 /* Not used in openssl */
+/* The obligatory nonsense associated with porting shared libraries to win32 */
+#if defined(KSCLIENT_BUILD)
+#define KC_INTERFACE __declspec(dllexport)
+#elif defined(KSCLIENT_USE)
+#define KC_INTERFACE __declspec(dllimport)
+#else
+#define KC_INTERFACE
+#endif
+#else
+#define KC_INTERFACE
+#endif
+
+/* 
+ * TYPES
+ */
+
+typedef struct _keyclient_ctx  keyclient_ctx;
+
+typedef struct _ctx_locking_table {
+       /* These functions return zero for failure. */
+       int (*ctx_lock)(const void *, unsigned int);
+       int (*ctx_unlock)(const void *, unsigned int);
+} ctx_locking_table;
+
+typedef struct _global_locking_table {
+       void (*lock)(void);
+       void (*unlock)(void);
+} global_locking_table;
+
+#define KC_MAX_PUBKEY_ASN 512  /* If the key doesn't fit, don't try */
+typedef struct _keyclient_key_t {
+       enum {
+               KC_KEY_RSA = 0,
+               KC_KEY_DSA = 1
+       } key_type;
+       unsigned int der_len;
+       unsigned char der[KC_MAX_PUBKEY_ASN];
+} keyclient_key_t;
+
+/*
+ * ENUMS
+ */
+
+/* Different "operation" types */
+typedef enum {
+       KC_FIRST_OP_INDEX = 0,
+       /* RSA operations */
+       KC_RSA_FIRST_OP = KC_FIRST_OP_INDEX,
+       KC_RSA_PUB_ENCRYPT = KC_RSA_FIRST_OP,
+       KC_RSA_PUB_DECRYPT,
+       KC_RSA_PRIV_ENCRYPT,
+       KC_RSA_PRIV_DECRYPT,
+       KC_RSA_SIGN,
+       KC_RSA_VERIFY,
+       KC_RSA_LAST_OP = KC_RSA_VERIFY,
+       /* DSA operations */
+       KC_DSA_FIRST_OP,
+       KC_DSA_SIGN = KC_DSA_FIRST_OP,
+       KC_DSA_VERIFY,
+       KC_DSA_LAST_OP = KC_DSA_VERIFY,
+       /* Round it out */
+       KC_LAST_OP_INDEX = KC_DSA_LAST_OP
+} keyclient_op_t;
+
+/* Different "padding" types */
+typedef enum {
+       KC_FIRST_PAD_INDEX = 0,
+       /* No padding (works for RSA and DSA) */
+       KC_PADDING_NONE = KC_FIRST_PAD_INDEX,
+       KC_PADDING_DSA = KC_PADDING_NONE,
+       /* RSA padding schemes */
+       KC_PADDING_RSA_PKCS1,
+       KC_PADDING_RSA_SSLV23,
+       KC_PADDING_RSA_PKCS1_OAEP,
+       /* Round it out */
+       KC_LAST_PAD_INDEX = KC_PADDING_RSA_PKCS1_OAEP
+} keyclient_pad_t;
+
+/* Different "return" types */
+typedef enum {
+       KC_RET_OK = 0,
+       KC_RET_ERR_INTERNAL, /* Bug */
+       KC_RET_ERR_BAD_ADDRESS, /* Bad address string */
+       KC_RET_ERR_NO_CONNECT, /* Can not connect to the address */
+       KC_RET_ERR_MEM, /* Memory error, insufficient space or some such */
+       KC_RET_ERR_REF_MISUSE, /* Reference count corruption */
+       KC_RET_ERR_INVALID_LOCKS, /* Caller provided inconsistent callbacks */
+       KC_RET_ERR_REQUEST_ENCODING, /* Error encoding the request */
+       KC_RET_ERR_RESPONSE_DECODING, /* Error decoding the response */
+       KC_RET_ERR_SELECT, /* Error in underlying select() call */
+       KC_RET_ERR_NETWORK_IO, /* Error in network I/O calls */
+       KC_RET_ERR_CTX_LOCK, /* lock() callback returned failure! */
+       KC_RET_ERR_CTX_UNLOCK, /* unlock() callback returned failure! */
+       KC_RET_ERR_REQUEST_SPACE, /* The request can not fit in the provided space */
+       KC_RET_ERR_RESULT_SPACE, /* The response can not fit in the provided space */
+       KC_RET_ERR_RESPONSE_MISMATCH, /* We received someone else's response? */
+       KC_RET_ERR_PRIVATE_EMBED, /* Cannot embed private keys in requests */
+       KC_RET_ERR_FLAG_COMBO, /* A bad combination of bitwise flags was used */
+       KC_RET_ERR_NULL1, /* Parameter 1 was NULL */
+       KC_RET_ERR_NULL2, /* Parameter 2 was NULL */
+       KC_RET_ERR_NULL3, /* Parameter 3 was NULL */
+       KC_RET_ERR_NULL4, /* Parameter 4 was NULL */
+       KC_RET_ERR_NULL5, /* Parameter 5 was NULL */
+       KC_RET_ERR_NULL6, /* Parameter 6 was NULL */
+       KC_RET_ERR_NULL7, /* Parameter 7 was NULL */
+       KC_RET_ERR_NULL8, /* Parameter 8 was NULL */
+       KC_RET_ERR_NON_NULL1, /* Parameter 1 was *not* NULL */
+       KC_RET_ERR_NON_NULL2, /* Parameter 2 was *not* NULL */
+       KC_RET_ERR_NON_NULL3, /* Parameter 3 was *not* NULL */
+       KC_RET_ERR_NON_NULL4, /* Parameter 4 was *not* NULL */
+       KC_RET_ERR_NON_NULL5, /* Parameter 5 was *not* NULL */
+       KC_RET_ERR_NON_NULL6, /* Parameter 6 was *not* NULL */
+       KC_RET_ERR_NON_NULL7, /* Parameter 7 was *not* NULL */
+       KC_RET_ERR_NON_NULL8, /* Parameter 8 was *not* NULL */
+       KC_RET_ERR_BAD_RANGE1, /* Parameter 1 was out of range */
+       KC_RET_ERR_BAD_RANGE2, /* Parameter 2 was out of range */
+       KC_RET_ERR_BAD_RANGE3, /* Parameter 3 was out of range */
+       KC_RET_ERR_BAD_RANGE4, /* Parameter 4 was out of range */
+       KC_RET_ERR_BAD_RANGE5, /* Parameter 5 was out of range */
+       KC_RET_ERR_BAD_RANGE6, /* Parameter 6 was out of range */
+       KC_RET_ERR_BAD_RANGE7, /* Parameter 7 was out of range */
+       KC_RET_ERR_BAD_RANGE8, /* Parameter 8 was out of range */
+       /* These errors are all "soft" - if keyclient_keyop() returns one of
+        * these values, then the operation completed successfully but the
+        * underlying payload of the response had its internal "error" value set
+        * to something other than KS_OP_ERR_OK. */
+       KC_RET_SOFT_UNKNOWN_OPERATION = 200, /* The keyserver doesn't support that */
+       KC_RET_SOFT_NO_SUCH_KEY, /* The keyserver doesn't have that key */
+       KC_RET_SOFT_NO_DATA, /* Data was required but was not supplied */
+       KC_RET_SOFT_OP_FAILED, /* The key operation on the server did not work */
+       KC_RET_SOFT_MISC /* An error that is not currently aliased was received */
+} KC_RET;
+
+
+/* Context flags (used in keyclient_create) */
+
+#define KC_FLAG_USE_LOCKING            0x01
+#define KC_FLAG_NO_LOCKING             0x02
+#define KC_FLAG_PERSISTENT_CONN                0x04
+#define KC_FLAG_PID_CHECK              0x08
+#define KC_FLAG_PERSISTENT_RETRY       0x10
+#define KC_FLAG_PERSISTENT_LATE                0x20
+
+
+/*
+ * FUNCTIONS
+ */
+
+KC_INTERFACE KC_RET keyclient_set_global_locks(const global_locking_table *locking);
+typedef KC_RET t_keyclient_set_global_locks(const global_locking_table *locking);
+
+KC_INTERFACE KC_RET keyclient_create(keyclient_ctx **ctx, const char *target_string,
+               unsigned int flags, const ctx_locking_table *locking);
+typedef KC_RET t_keyclient_create(keyclient_ctx **ctx, const char *target_string,
+               unsigned int flags, const ctx_locking_table *locking);
+
+KC_INTERFACE KC_RET keyclient_release(keyclient_ctx *ctx);
+typedef KC_RET t_keyclient_release(keyclient_ctx *ctx);
+
+KC_INTERFACE KC_RET keyclient_dup(keyclient_ctx *ctx);
+typedef KC_RET t_keyclient_dup(keyclient_ctx *ctx);
+
+KC_INTERFACE KC_RET keyclient_get_uid(const keyclient_ctx *ctx, unsigned int *uid);
+typedef KC_RET t_keyclient_get_uid(const keyclient_ctx *ctx, unsigned int *uid);
+
+KC_INTERFACE KC_RET keyclient_keyop(keyclient_ctx *ctx, keyclient_op_t operation,
+               const unsigned char *data, unsigned int data_len,
+               unsigned char *result, unsigned int *result_len,
+               keyclient_pad_t padding, const unsigned char *keyhash);
+typedef KC_RET t_keyclient_keyop(keyclient_ctx *ctx, keyclient_op_t operation,
+               const unsigned char *data, unsigned int data_len,
+               unsigned char *result, unsigned int *result_len,
+               keyclient_pad_t padding, const unsigned char *keyhash);
+
+KC_INTERFACE KC_RET keyclient_pubkeyop(keyclient_ctx *ctx, keyclient_op_t operation,
+               const unsigned char *data, unsigned int data_len,
+               unsigned char *result, unsigned int *result_len,
+               keyclient_pad_t padding, const keyclient_key_t *pubkey);
+typedef KC_RET t_keyclient_pubkeyop(keyclient_ctx *ctx, keyclient_op_t operation,
+               const unsigned char *data, unsigned int data_len,
+               unsigned char *result, unsigned int *result_len,
+               keyclient_pad_t padding, const keyclient_key_t *pubkey);
+
+typedef struct _keyclient_symbol_table {
+       t_keyclient_set_global_locks *keyclient_set_global_locks;
+       t_keyclient_create *keyclient_create;
+       t_keyclient_release *keyclient_release;
+       t_keyclient_dup *keyclient_dup;
+       t_keyclient_get_uid *keyclient_get_uid;
+       t_keyclient_keyop *keyclient_keyop;
+       t_keyclient_pubkeyop *keyclient_pubkeyop;
+} keyclient_symbol_table;
+
+KC_INTERFACE void keyclient_bind_symbols(keyclient_symbol_table *funcs);
+typedef void t_keyclient_bind_symbols(keyclient_symbol_table *funcs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif