From 3d60fe9bafbf633e3a7811c11227baebb17878a4 Mon Sep 17 00:00:00 2001 From: Brian Wellington Date: Wed, 14 Feb 2001 20:26:48 +0000 Subject: [PATCH] 740. [port] Handle openssl library mismatches slightly better. --- CHANGES | 2 + lib/dns/Makefile.in | 5 +- lib/dns/sec/dst/Makefile.in | 8 +- lib/dns/sec/dst/dst_api.c | 6 +- lib/dns/sec/dst/openssl_link.c | 468 ++--------------------------- lib/dns/sec/dst/openssldsa_link.c | 473 ++++++++++++++++++++++++++++++ 6 files changed, 508 insertions(+), 454 deletions(-) create mode 100644 lib/dns/sec/dst/openssldsa_link.c diff --git a/CHANGES b/CHANGES index 11d9757a8d..56c292c3af 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ + 740. [port] Handle openssl library mismatches slightly better. + 739. [port] Look for /dev/random in configure, rather than assuming it will be there for only a predefined set of OSes. diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index d929a68a62..7340f55753 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.121 2001/01/17 01:22:15 bwelling Exp $ +# $Id: Makefile.in,v 1.122 2001/02/14 20:26:43 bwelling Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -53,7 +53,8 @@ DSTOBJS = sec/dst/dst_api.@O@ \ sec/dst/dst_result.@O@ sec/dst/gssapi_link.@O@ \ sec/dst/gssapictx.@O@ sec/dst/hmac_link.@O@ \ sec/dst/key.@O@ sec/dst/openssl_link.@O@ \ - sec/dst/openssldh_link.@O@ sec/dst/opensslrsa_link.@O@ + sec/dst/openssldh_link.@O@ sec/dst/openssldsa_link.@O@ \ + sec/dst/opensslrsa_link.@O@ OPENSSLOBJS = sec/openssl/a_bitstr.@O@ sec/openssl/a_bytes.@O@ \ sec/openssl/a_enum.@O@ sec/openssl/a_gentm.@O@ \ diff --git a/lib/dns/sec/dst/Makefile.in b/lib/dns/sec/dst/Makefile.in index 8ce5d73b5a..55f0b68dac 100644 --- a/lib/dns/sec/dst/Makefile.in +++ b/lib/dns/sec/dst/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.23 2001/01/24 01:34:14 bwelling Exp $ +# $Id: Makefile.in,v 1.24 2001/02/14 20:26:45 bwelling Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -34,12 +34,14 @@ LIBS = @LIBS@ OBJS = dst_api.@O@ dst_lib.@O@ dst_parse.@O@ \ dst_result.@O@ gssapi_link.@O@ gssapictx.@O@ \ hmac_link.@O@ key.@O@ \ - openssl_link.@O@ openssldh_link.@O@ opensslrsa_link.@O@ + openssl_link.@O@ openssldh_link.@O@ \ + openssldsa_link.@O@ opensslrsa_link.@O@ SRCS = dst_api.c dst_lib.c dst_parse.c \ dst_result.c gssapi_link.c gssapictx.c \ hmac_link.c key.c \ - openssl_link.c openssldh_link.c opensslrsa_link.c + openssl_link.c openssldh_link.c \ + openssldsa_link.c opensslrsa_link.c SUBDIRS = include TARGETS = ${OBJS} diff --git a/lib/dns/sec/dst/dst_api.c b/lib/dns/sec/dst/dst_api.c index 6678561b90..300f8ed54b 100644 --- a/lib/dns/sec/dst/dst_api.c +++ b/lib/dns/sec/dst/dst_api.c @@ -19,7 +19,7 @@ /* * Principal Author: Brian Wellington - * $Id: dst_api.c,v 1.73 2001/01/27 04:33:18 bwelling Exp $ + * $Id: dst_api.c,v 1.74 2001/02/14 20:26:46 bwelling Exp $ */ #include @@ -101,7 +101,9 @@ dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) { * Avoid assertions by using a local memory context and not checking * for leaks on exit. */ - RETERR(isc_mem_create(0, 0, &dst_memory_pool)); + result = isc_mem_create(0, 0, &dst_memory_pool); + if (result != ISC_R_SUCCESS) + return (result); isc_mem_setdestroycheck(dst_memory_pool, ISC_FALSE); isc_entropy_attach(ectx, &dst_entropy_pool); dst_entropy_flags = eflags; diff --git a/lib/dns/sec/dst/openssl_link.c b/lib/dns/sec/dst/openssl_link.c index 984946a2bf..f3712e4982 100644 --- a/lib/dns/sec/dst/openssl_link.c +++ b/lib/dns/sec/dst/openssl_link.c @@ -19,7 +19,7 @@ /* * Principal Author: Brian Wellington - * $Id: openssl_link.c,v 1.42 2001/01/24 02:22:58 bwelling Exp $ + * $Id: openssl_link.c,v 1.43 2001/02/14 20:26:47 bwelling Exp $ */ #if defined(OPENSSL) @@ -29,454 +29,18 @@ #include #include #include -#include #include #include #include -#include - #include "dst_internal.h" -#include "dst_parse.h" -#include #include +#include static RAND_METHOD *rm = NULL; -static isc_mutex_t locks[CRYPTO_NUM_LOCKS]; - -static isc_result_t openssldsa_todns(const dst_key_t *key, isc_buffer_t *data); - -static isc_result_t -openssldsa_createctx(dst_key_t *key, dst_context_t *dctx) { - isc_sha1_t *sha1ctx; - - UNUSED(key); - - sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t)); - isc_sha1_init(sha1ctx); - dctx->opaque = sha1ctx; - return (ISC_R_SUCCESS); -} - -static void -openssldsa_destroyctx(dst_context_t *dctx) { - isc_sha1_t *sha1ctx = dctx->opaque; - - if (sha1ctx != NULL) { - isc_sha1_invalidate(sha1ctx); - isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t)); - dctx->opaque = NULL; - } -} - -static isc_result_t -openssldsa_adddata(dst_context_t *dctx, const isc_region_t *data) { - isc_sha1_t *sha1ctx = dctx->opaque; - - isc_sha1_update(sha1ctx, data->base, data->length); - return (ISC_R_SUCCESS); -} - -static int -BN_bn2bin_fixed(BIGNUM *bn, unsigned char *buf, int size) { - int bytes = size - BN_num_bytes(bn); - while (bytes-- > 0) - *buf++ = 0; - BN_bn2bin(bn, buf); - return (size); -} - -static isc_result_t -openssldsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { - isc_sha1_t *sha1ctx = dctx->opaque; - dst_key_t *key = dctx->key; - DSA *dsa = key->opaque; - DSA_SIG *dsasig; - isc_region_t r; - unsigned char digest[ISC_SHA1_DIGESTLENGTH]; - - isc_buffer_availableregion(sig, &r); - if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1) - return (ISC_R_NOSPACE); - - isc_sha1_final(sha1ctx, digest); - - dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa); - if (dsasig == NULL) - return (DST_R_SIGNFAILURE); - - *r.base++ = (key->key_size - 512)/64; - BN_bn2bin_fixed(dsasig->r, r.base, ISC_SHA1_DIGESTLENGTH); - r.base += ISC_SHA1_DIGESTLENGTH; - BN_bn2bin_fixed(dsasig->s, r.base, ISC_SHA1_DIGESTLENGTH); - r.base += ISC_SHA1_DIGESTLENGTH; - DSA_SIG_free(dsasig); - isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1); - - return (ISC_R_SUCCESS); -} - -static isc_result_t -openssldsa_verify(dst_context_t *dctx, const isc_region_t *sig) { - isc_sha1_t *sha1ctx = dctx->opaque; - dst_key_t *key = dctx->key; - DSA *dsa = key->opaque; - DSA_SIG *dsasig; - int status = 0; - unsigned char digest[ISC_SHA1_DIGESTLENGTH]; - unsigned char *cp = sig->base; - - isc_sha1_final(sha1ctx, digest); - - if (sig->length < 2 * ISC_SHA1_DIGESTLENGTH + 1) - return (DST_R_VERIFYFAILURE); - - cp++; /* Skip T */ - dsasig = DSA_SIG_new(); - dsasig->r = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); - cp += ISC_SHA1_DIGESTLENGTH; - dsasig->s = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); - cp += ISC_SHA1_DIGESTLENGTH; - - status = DSA_do_verify(digest, ISC_SHA1_DIGESTLENGTH, dsasig, dsa); - DSA_SIG_free(dsasig); - if (status == 0) - return (DST_R_VERIFYFAILURE); - - return (ISC_R_SUCCESS); -} - -static isc_boolean_t -openssldsa_compare(const dst_key_t *key1, const dst_key_t *key2) { - int status; - DSA *dsa1, *dsa2; - - dsa1 = (DSA *) key1->opaque; - dsa2 = (DSA *) key2->opaque; - - if (dsa1 == NULL && dsa2 == NULL) - return (ISC_TRUE); - else if (dsa1 == NULL || dsa2 == NULL) - return (ISC_FALSE); - - status = BN_cmp(dsa1->p, dsa2->p) || - BN_cmp(dsa1->q, dsa2->q) || - BN_cmp(dsa1->g, dsa2->g) || - BN_cmp(dsa1->pub_key, dsa2->pub_key); - - if (status != 0) - return (ISC_FALSE); - - if (dsa1->priv_key != NULL || dsa2->priv_key != NULL) { - if (dsa1->priv_key == NULL || dsa2->priv_key == NULL) - return (ISC_FALSE); - if (BN_cmp(dsa1->priv_key, dsa2->priv_key)) - return (ISC_FALSE); - } - return (ISC_TRUE); -} - -static isc_result_t -openssldsa_generate(dst_key_t *key, int unused) { - DSA *dsa; - unsigned char dns_array[DST_KEY_MAXSIZE]; - unsigned char rand_array[ISC_SHA1_DIGESTLENGTH]; - isc_buffer_t dns; - isc_result_t result; - isc_region_t r; - - UNUSED(unused); - - result = dst__entropy_getdata(rand_array, sizeof(rand_array), - ISC_FALSE); - if (result != ISC_R_SUCCESS) - return (result); - - dsa = DSA_generate_parameters(key->key_size, rand_array, - ISC_SHA1_DIGESTLENGTH, NULL, NULL, - NULL, NULL); - - if (dsa == NULL) - return (DST_R_OPENSSLFAILURE); - - if (DSA_generate_key(dsa) == 0) { - DSA_free(dsa); - return (DST_R_OPENSSLFAILURE); - } - dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; - - key->opaque = dsa; - - isc_buffer_init(&dns, dns_array, sizeof(dns_array)); - result = openssldsa_todns(key, &dns); - if (result != ISC_R_SUCCESS) { - DSA_free(dsa); - return (result); - } - isc_buffer_usedregion(&dns, &r); - key->key_id = dst_region_computeid(&r, key->key_alg); - - return (ISC_R_SUCCESS); -} - -static isc_boolean_t -openssldsa_isprivate(const dst_key_t *key) { - DSA *dsa = (DSA *) key->opaque; - return (ISC_TF(dsa != NULL && dsa->priv_key != NULL)); -} - -static isc_boolean_t -openssldsa_issymmetric(void) { - return (ISC_FALSE); -} - -static void -openssldsa_destroy(dst_key_t *key) { - DSA *dsa = key->opaque; - DSA_free(dsa); - key->opaque = NULL; -} - - -static isc_result_t -openssldsa_todns(const dst_key_t *key, isc_buffer_t *data) { - DSA *dsa; - isc_region_t r; - int dnslen; - unsigned int t, p_bytes; - - REQUIRE(key->opaque != NULL); - - dsa = (DSA *) key->opaque; - - isc_buffer_availableregion(data, &r); - - t = (BN_num_bytes(dsa->p) - 64) / 8; - if (t > 8) - return (DST_R_INVALIDPUBLICKEY); - p_bytes = 64 + 8 * t; - - dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH; - if (r.length < (unsigned int) dnslen) - return (ISC_R_NOSPACE); - - *r.base++ = t; - BN_bn2bin_fixed(dsa->q, r.base, ISC_SHA1_DIGESTLENGTH); - r.base += ISC_SHA1_DIGESTLENGTH; - BN_bn2bin_fixed(dsa->p, r.base, key->key_size/8); - r.base += p_bytes; - BN_bn2bin_fixed(dsa->g, r.base, key->key_size/8); - r.base += p_bytes; - BN_bn2bin_fixed(dsa->pub_key, r.base, key->key_size/8); - r.base += p_bytes; - - isc_buffer_add(data, dnslen); - - return (ISC_R_SUCCESS); -} - -static isc_result_t -openssldsa_fromdns(dst_key_t *key, isc_buffer_t *data) { - DSA *dsa; - isc_region_t r; - unsigned int t, p_bytes; - isc_mem_t *mctx = key->mctx; - - UNUSED(mctx); - - isc_buffer_remainingregion(data, &r); - if (r.length == 0) - return (ISC_R_SUCCESS); - - dsa = DSA_new(); - if (dsa == NULL) - return (ISC_R_NOMEMORY); - dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; - - t = (unsigned int) *r.base++; - if (t > 8) { - DSA_free(dsa); - return (DST_R_INVALIDPUBLICKEY); - } - p_bytes = 64 + 8 * t; - - if (r.length < 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) { - DSA_free(dsa); - return (DST_R_INVALIDPUBLICKEY); - } - - dsa->q = BN_bin2bn(r.base, ISC_SHA1_DIGESTLENGTH, NULL); - r.base += ISC_SHA1_DIGESTLENGTH; - - dsa->p = BN_bin2bn(r.base, p_bytes, NULL); - r.base += p_bytes; - - dsa->g = BN_bin2bn(r.base, p_bytes, NULL); - r.base += p_bytes; - - dsa->pub_key = BN_bin2bn(r.base, p_bytes, NULL); - r.base += p_bytes; - - isc_buffer_remainingregion(data, &r); - r.length = 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes; - key->key_id = dst_region_computeid(&r, key->key_alg); - key->key_size = p_bytes * 8; - - isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes); - - key->opaque = (void *) dsa; - - return (ISC_R_SUCCESS); -} - - -static isc_result_t -openssldsa_tofile(const dst_key_t *key, const char *directory) { - int cnt = 0; - DSA *dsa; - dst_private_t priv; - unsigned char bufs[5][128]; - - if (key->opaque == NULL) - return (DST_R_NULLKEY); - - dsa = (DSA *) key->opaque; - - priv.elements[cnt].tag = TAG_DSA_PRIME; - priv.elements[cnt].length = BN_num_bytes(dsa->p); - BN_bn2bin(dsa->p, bufs[cnt]); - priv.elements[cnt].data = bufs[cnt]; - cnt++; - - priv.elements[cnt].tag = TAG_DSA_SUBPRIME; - priv.elements[cnt].length = BN_num_bytes(dsa->q); - BN_bn2bin(dsa->q, bufs[cnt]); - priv.elements[cnt].data = bufs[cnt]; - cnt++; - - priv.elements[cnt].tag = TAG_DSA_BASE; - priv.elements[cnt].length = BN_num_bytes(dsa->g); - BN_bn2bin(dsa->g, bufs[cnt]); - priv.elements[cnt].data = bufs[cnt]; - cnt++; - - priv.elements[cnt].tag = TAG_DSA_PRIVATE; - priv.elements[cnt].length = BN_num_bytes(dsa->priv_key); - BN_bn2bin(dsa->priv_key, bufs[cnt]); - priv.elements[cnt].data = bufs[cnt]; - cnt++; - - priv.elements[cnt].tag = TAG_DSA_PUBLIC; - priv.elements[cnt].length = BN_num_bytes(dsa->pub_key); - BN_bn2bin(dsa->pub_key, bufs[cnt]); - priv.elements[cnt].data = bufs[cnt]; - cnt++; - - priv.nelements = cnt; - return (dst__privstruct_writefile(key, &priv, directory)); -} - -static isc_result_t -openssldsa_fromfile(dst_key_t *key, const dns_keytag_t id, const char *filename) -{ - dst_private_t priv; - isc_result_t ret; - isc_buffer_t dns; - isc_region_t r; - unsigned char dns_array[1024]; - int i; - DSA *dsa = NULL; - isc_mem_t *mctx = key->mctx; -#define DST_RET(a) {ret = a; goto err;} - - /* read private key file */ - ret = dst__privstruct_parsefile(key, id, filename, mctx, &priv); - if (ret != ISC_R_SUCCESS) - return (ret); - - dsa = DSA_new(); - if (dsa == NULL) - DST_RET(ISC_R_NOMEMORY); - dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; - key->opaque = dsa; - - for (i=0; i < priv.nelements; i++) { - BIGNUM *bn; - bn = BN_bin2bn(priv.elements[i].data, - priv.elements[i].length, NULL); - if (bn == NULL) - DST_RET(ISC_R_NOMEMORY); - - switch (priv.elements[i].tag) { - case TAG_DSA_PRIME: - dsa->p = bn; - break; - case TAG_DSA_SUBPRIME: - dsa->q = bn; - break; - case TAG_DSA_BASE: - dsa->g = bn; - break; - case TAG_DSA_PRIVATE: - dsa->priv_key = bn; - break; - case TAG_DSA_PUBLIC: - dsa->pub_key = bn; - break; - } - } - dst__privstruct_free(&priv, mctx); - - key->key_size = BN_num_bits(dsa->p); - isc_buffer_init(&dns, dns_array, sizeof(dns_array)); - ret = openssldsa_todns(key, &dns); - if (ret != ISC_R_SUCCESS) - DST_RET(ret); - isc_buffer_usedregion(&dns, &r); - key->key_id = dst_region_computeid(&r, key->key_alg); - - if (key->key_id != id) - DST_RET(DST_R_INVALIDPRIVATEKEY); - - return (ISC_R_SUCCESS); - - err: - openssldsa_destroy(key); - dst__privstruct_free(&priv, mctx); - memset(&priv, 0, sizeof(priv)); - return (ret); -} - -static dst_func_t openssldsa_functions = { - openssldsa_createctx, - openssldsa_destroyctx, - openssldsa_adddata, - openssldsa_sign, - openssldsa_verify, - NULL, /* computesecret */ - openssldsa_compare, - NULL, /* paramcompare */ - openssldsa_generate, - openssldsa_isprivate, - openssldsa_issymmetric, - openssldsa_destroy, - openssldsa_todns, - openssldsa_fromdns, - openssldsa_tofile, - openssldsa_fromfile, -}; - -isc_result_t -dst__openssldsa_init(dst_func_t **funcp) { - REQUIRE(funcp != NULL && *funcp == NULL); - *funcp = &openssldsa_functions; - return (ISC_R_SUCCESS); -} - -void -dst__openssldsa_destroy(void) { -} +static isc_mutex_t *locks = NULL; +static int nlocks; static int entropy_get(unsigned char *buf, int num) { @@ -522,14 +86,20 @@ id_callback(void) { } isc_result_t -dst__openssl_init(void) { +dst__openssl_init() { isc_result_t result; CRYPTO_set_mem_functions(dst__mem_alloc, dst__mem_realloc, dst__mem_free); - result = isc_mutexblock_init(locks, CRYPTO_NUM_LOCKS); - if (result != ISC_R_SUCCESS) + nlocks = CRYPTO_num_locks(); + locks = dst__mem_alloc(sizeof(isc_mutex_t) * nlocks); + if (locks == NULL) + return (ISC_R_NOMEMORY); + result = isc_mutexblock_init(locks, nlocks); + if (result != ISC_R_SUCCESS) { + dst__mem_free(locks); return (result); + } CRYPTO_set_locking_callback(lock_callback); CRYPTO_set_id_callback(id_callback); rm = dst__mem_alloc(sizeof(RAND_METHOD)); @@ -546,10 +116,14 @@ dst__openssl_init(void) { } void -dst__openssl_destroy(void) { - RUNTIME_CHECK(isc_mutexblock_destroy(locks, CRYPTO_NUM_LOCKS) == - ISC_R_SUCCESS); - dst__mem_free(rm); +dst__openssl_destroy() { + if (locks != NULL) { + RUNTIME_CHECK(isc_mutexblock_destroy(locks, nlocks) == + ISC_R_SUCCESS); + dst__mem_free(locks); + } + if (rm != NULL) + dst__mem_free(rm); } #endif /* OPENSSL */ diff --git a/lib/dns/sec/dst/openssldsa_link.c b/lib/dns/sec/dst/openssldsa_link.c new file mode 100644 index 0000000000..6373884787 --- /dev/null +++ b/lib/dns/sec/dst/openssldsa_link.c @@ -0,0 +1,473 @@ +/* + * Portions Copyright (C) 1999-2001 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM AND + * NETWORK ASSOCIATES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM OR NETWORK + * ASSOCIATES BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: openssldsa_link.c,v 1.1 2001/02/14 20:26:48 bwelling Exp $ */ + +#if defined(OPENSSL) + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_parse.h" + +#include + +static isc_result_t openssldsa_todns(const dst_key_t *key, isc_buffer_t *data); + +static isc_result_t +openssldsa_createctx(dst_key_t *key, dst_context_t *dctx) { + isc_sha1_t *sha1ctx; + + UNUSED(key); + + sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t)); + isc_sha1_init(sha1ctx); + dctx->opaque = sha1ctx; + return (ISC_R_SUCCESS); +} + +static void +openssldsa_destroyctx(dst_context_t *dctx) { + isc_sha1_t *sha1ctx = dctx->opaque; + + if (sha1ctx != NULL) { + isc_sha1_invalidate(sha1ctx); + isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t)); + dctx->opaque = NULL; + } +} + +static isc_result_t +openssldsa_adddata(dst_context_t *dctx, const isc_region_t *data) { + isc_sha1_t *sha1ctx = dctx->opaque; + + isc_sha1_update(sha1ctx, data->base, data->length); + return (ISC_R_SUCCESS); +} + +static int +BN_bn2bin_fixed(BIGNUM *bn, unsigned char *buf, int size) { + int bytes = size - BN_num_bytes(bn); + while (bytes-- > 0) + *buf++ = 0; + BN_bn2bin(bn, buf); + return (size); +} + +static isc_result_t +openssldsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + isc_sha1_t *sha1ctx = dctx->opaque; + dst_key_t *key = dctx->key; + DSA *dsa = key->opaque; + DSA_SIG *dsasig; + isc_region_t r; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + + isc_buffer_availableregion(sig, &r); + if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1) + return (ISC_R_NOSPACE); + + isc_sha1_final(sha1ctx, digest); + + dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa); + if (dsasig == NULL) + return (DST_R_SIGNFAILURE); + + *r.base++ = (key->key_size - 512)/64; + BN_bn2bin_fixed(dsasig->r, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + BN_bn2bin_fixed(dsasig->s, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + DSA_SIG_free(dsasig); + isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + isc_sha1_t *sha1ctx = dctx->opaque; + dst_key_t *key = dctx->key; + DSA *dsa = key->opaque; + DSA_SIG *dsasig; + int status = 0; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + unsigned char *cp = sig->base; + + isc_sha1_final(sha1ctx, digest); + + if (sig->length < 2 * ISC_SHA1_DIGESTLENGTH + 1) + return (DST_R_VERIFYFAILURE); + + cp++; /* Skip T */ + dsasig = DSA_SIG_new(); + dsasig->r = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); + cp += ISC_SHA1_DIGESTLENGTH; + dsasig->s = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); + cp += ISC_SHA1_DIGESTLENGTH; + + status = DSA_do_verify(digest, ISC_SHA1_DIGESTLENGTH, dsasig, dsa); + DSA_SIG_free(dsasig); + if (status == 0) + return (DST_R_VERIFYFAILURE); + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + DSA *dsa1, *dsa2; + + dsa1 = (DSA *) key1->opaque; + dsa2 = (DSA *) key2->opaque; + + if (dsa1 == NULL && dsa2 == NULL) + return (ISC_TRUE); + else if (dsa1 == NULL || dsa2 == NULL) + return (ISC_FALSE); + + status = BN_cmp(dsa1->p, dsa2->p) || + BN_cmp(dsa1->q, dsa2->q) || + BN_cmp(dsa1->g, dsa2->g) || + BN_cmp(dsa1->pub_key, dsa2->pub_key); + + if (status != 0) + return (ISC_FALSE); + + if (dsa1->priv_key != NULL || dsa2->priv_key != NULL) { + if (dsa1->priv_key == NULL || dsa2->priv_key == NULL) + return (ISC_FALSE); + if (BN_cmp(dsa1->priv_key, dsa2->priv_key)) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + +static isc_result_t +openssldsa_generate(dst_key_t *key, int unused) { + DSA *dsa; + unsigned char dns_array[DST_KEY_MAXSIZE]; + unsigned char rand_array[ISC_SHA1_DIGESTLENGTH]; + isc_buffer_t dns; + isc_result_t result; + isc_region_t r; + + UNUSED(unused); + + result = dst__entropy_getdata(rand_array, sizeof(rand_array), + ISC_FALSE); + if (result != ISC_R_SUCCESS) + return (result); + + dsa = DSA_generate_parameters(key->key_size, rand_array, + ISC_SHA1_DIGESTLENGTH, NULL, NULL, + NULL, NULL); + + if (dsa == NULL) + return (DST_R_OPENSSLFAILURE); + + if (DSA_generate_key(dsa) == 0) { + DSA_free(dsa); + return (DST_R_OPENSSLFAILURE); + } + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + + key->opaque = dsa; + + isc_buffer_init(&dns, dns_array, sizeof(dns_array)); + result = openssldsa_todns(key, &dns); + if (result != ISC_R_SUCCESS) { + DSA_free(dsa); + return (result); + } + isc_buffer_usedregion(&dns, &r); + key->key_id = dst_region_computeid(&r, key->key_alg); + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldsa_isprivate(const dst_key_t *key) { + DSA *dsa = (DSA *) key->opaque; + return (ISC_TF(dsa != NULL && dsa->priv_key != NULL)); +} + +static isc_boolean_t +openssldsa_issymmetric(void) { + return (ISC_FALSE); +} + +static void +openssldsa_destroy(dst_key_t *key) { + DSA *dsa = key->opaque; + DSA_free(dsa); + key->opaque = NULL; +} + + +static isc_result_t +openssldsa_todns(const dst_key_t *key, isc_buffer_t *data) { + DSA *dsa; + isc_region_t r; + int dnslen; + unsigned int t, p_bytes; + + REQUIRE(key->opaque != NULL); + + dsa = (DSA *) key->opaque; + + isc_buffer_availableregion(data, &r); + + t = (BN_num_bytes(dsa->p) - 64) / 8; + if (t > 8) + return (DST_R_INVALIDPUBLICKEY); + p_bytes = 64 + 8 * t; + + dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH; + if (r.length < (unsigned int) dnslen) + return (ISC_R_NOSPACE); + + *r.base++ = t; + BN_bn2bin_fixed(dsa->q, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + BN_bn2bin_fixed(dsa->p, r.base, key->key_size/8); + r.base += p_bytes; + BN_bn2bin_fixed(dsa->g, r.base, key->key_size/8); + r.base += p_bytes; + BN_bn2bin_fixed(dsa->pub_key, r.base, key->key_size/8); + r.base += p_bytes; + + isc_buffer_add(data, dnslen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + DSA *dsa; + isc_region_t r; + unsigned int t, p_bytes; + isc_mem_t *mctx = key->mctx; + + UNUSED(mctx); + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + dsa = DSA_new(); + if (dsa == NULL) + return (ISC_R_NOMEMORY); + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + + t = (unsigned int) *r.base++; + if (t > 8) { + DSA_free(dsa); + return (DST_R_INVALIDPUBLICKEY); + } + p_bytes = 64 + 8 * t; + + if (r.length < 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) { + DSA_free(dsa); + return (DST_R_INVALIDPUBLICKEY); + } + + dsa->q = BN_bin2bn(r.base, ISC_SHA1_DIGESTLENGTH, NULL); + r.base += ISC_SHA1_DIGESTLENGTH; + + dsa->p = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + dsa->g = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + dsa->pub_key = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + isc_buffer_remainingregion(data, &r); + r.length = 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes; + key->key_id = dst_region_computeid(&r, key->key_alg); + key->key_size = p_bytes * 8; + + isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes); + + key->opaque = (void *) dsa; + + return (ISC_R_SUCCESS); +} + + +static isc_result_t +openssldsa_tofile(const dst_key_t *key, const char *directory) { + int cnt = 0; + DSA *dsa; + dst_private_t priv; + unsigned char bufs[5][128]; + + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + dsa = (DSA *) key->opaque; + + priv.elements[cnt].tag = TAG_DSA_PRIME; + priv.elements[cnt].length = BN_num_bytes(dsa->p); + BN_bn2bin(dsa->p, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_SUBPRIME; + priv.elements[cnt].length = BN_num_bytes(dsa->q); + BN_bn2bin(dsa->q, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_BASE; + priv.elements[cnt].length = BN_num_bytes(dsa->g); + BN_bn2bin(dsa->g, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PRIVATE; + priv.elements[cnt].length = BN_num_bytes(dsa->priv_key); + BN_bn2bin(dsa->priv_key, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PUBLIC; + priv.elements[cnt].length = BN_num_bytes(dsa->pub_key); + BN_bn2bin(dsa->pub_key, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.nelements = cnt; + return (dst__privstruct_writefile(key, &priv, directory)); +} + +static isc_result_t +openssldsa_fromfile(dst_key_t *key, const dns_keytag_t id, const char *filename) +{ + dst_private_t priv; + isc_result_t ret; + isc_buffer_t dns; + isc_region_t r; + unsigned char dns_array[1024]; + int i; + DSA *dsa = NULL; + isc_mem_t *mctx = key->mctx; +#define DST_RET(a) {ret = a; goto err;} + + /* read private key file */ + ret = dst__privstruct_parsefile(key, id, filename, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + dsa = DSA_new(); + if (dsa == NULL) + DST_RET(ISC_R_NOMEMORY); + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + key->opaque = dsa; + + for (i=0; i < priv.nelements; i++) { + BIGNUM *bn; + bn = BN_bin2bn(priv.elements[i].data, + priv.elements[i].length, NULL); + if (bn == NULL) + DST_RET(ISC_R_NOMEMORY); + + switch (priv.elements[i].tag) { + case TAG_DSA_PRIME: + dsa->p = bn; + break; + case TAG_DSA_SUBPRIME: + dsa->q = bn; + break; + case TAG_DSA_BASE: + dsa->g = bn; + break; + case TAG_DSA_PRIVATE: + dsa->priv_key = bn; + break; + case TAG_DSA_PUBLIC: + dsa->pub_key = bn; + break; + } + } + dst__privstruct_free(&priv, mctx); + + key->key_size = BN_num_bits(dsa->p); + isc_buffer_init(&dns, dns_array, sizeof(dns_array)); + ret = openssldsa_todns(key, &dns); + if (ret != ISC_R_SUCCESS) + DST_RET(ret); + isc_buffer_usedregion(&dns, &r); + key->key_id = dst_region_computeid(&r, key->key_alg); + + if (key->key_id != id) + DST_RET(DST_R_INVALIDPRIVATEKEY); + + return (ISC_R_SUCCESS); + + err: + openssldsa_destroy(key); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static dst_func_t openssldsa_functions = { + openssldsa_createctx, + openssldsa_destroyctx, + openssldsa_adddata, + openssldsa_sign, + openssldsa_verify, + NULL, /* computesecret */ + openssldsa_compare, + NULL, /* paramcompare */ + openssldsa_generate, + openssldsa_isprivate, + openssldsa_issymmetric, + openssldsa_destroy, + openssldsa_todns, + openssldsa_fromdns, + openssldsa_tofile, + openssldsa_fromfile, +}; + +isc_result_t +dst__openssldsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL && *funcp == NULL); + *funcp = &openssldsa_functions; + return (ISC_R_SUCCESS); +} + +void +dst__openssldsa_destroy(void) { +} + +#endif /* OPENSSL */