--- /dev/null
+/* 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 */
--- /dev/null
+#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