openssl/crypto/engine/hw_keyclient.c
2001-11-21 03:49:51 +00:00

1015 lines
29 KiB
C

/* 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 */
#ifdef WIN32
"IP:127.0.0.1:9001" /* win32 uses this by default */
#else
"UNIX:/tmp/kclient" /* default address string */
#endif
};
/* 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) >= (time_t)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 */