/*	$NetBSD: bsafe_link.c,v 1.1.2.1 1999/12/04 16:58:11 he Exp $	*/

#if defined(BSAFE) || defined(DNSSAFE)
static const char rcsid[] = "$Header: /cvsroot/basesrc/dist/bind/lib/dst/bsafe_link.c,v 1.1.2.1 1999/12/04 16:58:11 he Exp $";

/*
 * Portions Copyright (c) 1995-1998 by Trusted Information Systems, 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 TRUSTED INFORMATION SYSTEMS
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
 * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
 */
/* 
 * This file contains two components 
 * 1. Interface to the BSAFE library to allow compilation of Bind 
 *    with TIS/DNSSEC when BSAFE is not available 
 *    all calls to BSAFE are contained inside this file.
 * 2. The glue to connvert RSA KEYS to and from external formats
 */
#include "port_before.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <sys/param.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include "dst_internal.h"

#  ifdef __STDC__
#    define PROTOTYPES 1
#  else
#    define PROTOTYPES 0
#  endif

#  ifdef BSAFE
#    include <aglobal.h>
#    include <bsafe.h>
#  else
#    include <global.h>
#    include <bsafe2.h>
#    include <bigmaxes.h>
#  endif

#include "port_after.h"

typedef struct bsafekey {
	char *rk_signer;
	B_KEY_OBJ rk_Private_Key;
	B_KEY_OBJ rk_Public_Key;
} RSA_Key;

#ifndef MAX_RSA_MODULUS_BITS
#define MAX_RSA_MODULUS_BITS 4096
#define MAX_RSA_MODULUS_LEN (MAX_RSA_MODULUS_BITS/8)
#define MAX_RSA_PRIME_LEN (MAX_RSA_MODULUS_LEN/2)
#endif

#define NULL_SURRENDER (A_SURRENDER_CTX *)NULL_PTR
#define NULL_RANDOM (B_ALGORITHM_OBJ)NULL_PTR

B_ALGORITHM_METHOD *CHOOSER[] =
{
	&AM_MD5,
	&AM_MD5_RANDOM,
	&AM_RSA_KEY_GEN,
	&AM_RSA_ENCRYPT,
	&AM_RSA_DECRYPT,
	&AM_RSA_CRT_ENCRYPT,
	&AM_RSA_CRT_DECRYPT,
	(B_ALGORITHM_METHOD *) NULL_PTR
};

static u_char pkcs1[] =
{
	0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,
	0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00,
	0x04, 0x10
};

static int dst_bsafe_md5digest(const int mode, B_ALGORITHM_OBJ *digest_obj,
			       const u_char *data, const int len,
			       u_char *digest, const int digest_len);

static int dst_bsafe_key_size(RSA_Key *r_key);

static int dst_bsafe_sign(const int mode, DST_KEY *dkey, void **context,
			  const u_char *data, const int len,
			  u_char *signature, const int sig_len);
static int dst_bsafe_verify(const int mode, DST_KEY *dkey, void **context,
			    const u_char *data, const int len,
			    const u_char *signature, const int sig_len);
static int dst_bsafe_to_dns_key(const DST_KEY *in_key, u_char *out_str,
				const int out_len);
static int dst_bsafe_from_dns_key(DST_KEY *s_key, const u_char *key,
				  const int len);
static int dst_bsafe_key_to_file_format(const DST_KEY *key, char *buff,
					const int buff_len);
static int dst_bsafe_key_from_file_format(DST_KEY *d_key,
					  const char *buff,
					  const int buff_len);
static int dst_bsafe_generate_keypair(DST_KEY *key, int exp);
static int dst_bsafe_compare_keys(const DST_KEY *key1, const DST_KEY *key2);
static void *dst_bsafe_free_key_structure(void *key);

/*
 * dst_bsafe_init()  Function to answer set up function pointers for 
 *	   BSAFE/DNSSAFE related functions 
 */
int
dst_bsafe_init()
{
	if (dst_t_func[KEY_RSA] != NULL)
		return (1);
	dst_t_func[KEY_RSA] = malloc(sizeof(struct dst_func));
	if (dst_t_func[KEY_RSA] == NULL)
		return (0);
	memset(dst_t_func[KEY_RSA], 0, sizeof(struct dst_func));
	dst_t_func[KEY_RSA]->sign = dst_bsafe_sign;
	dst_t_func[KEY_RSA]->verify = dst_bsafe_verify;
	dst_t_func[KEY_RSA]->compare = dst_bsafe_compare_keys;
	dst_t_func[KEY_RSA]->generate = dst_bsafe_generate_keypair;
	dst_t_func[KEY_RSA]->destroy = dst_bsafe_free_key_structure;
	dst_t_func[KEY_RSA]->from_dns_key = dst_bsafe_from_dns_key;
	dst_t_func[KEY_RSA]->to_dns_key = dst_bsafe_to_dns_key;
	dst_t_func[KEY_RSA]->from_file_fmt = dst_bsafe_key_from_file_format;
	dst_t_func[KEY_RSA]->to_file_fmt = dst_bsafe_key_to_file_format;
	return (1);
}

/*
 * dst_bsafe_sign
 *     Call BSAFE signing functions to sign a block of data.
 *     There are three steps to signing, INIT (initialize structures), 
 *     UPDATE (hash (more) data), FINAL (generate a signature).  This
 *     routine performs one or more of these steps.
 * Parameters
 *     mode	SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
 *     dkey      structure holds context for a sign done in multiple calls.
 *     context   the context to use for this computation
 *     data	data to be signed.
 *     len	 length in bytes of data.
 *     priv_key    key to use for signing.
 *     signature   location to store signature.
 *     sig_len     size in bytes of signature field.
 * returns 
 *	N  Success on SIG_MODE_FINAL = returns signature length in bytes
 *	0  Success on SIG_MODE_INIT  and UPDATE
 *	 <0  Failure
 */

static int
dst_bsafe_sign(const int mode, DST_KEY *dkey, void **context,
	       const u_char *data, const int len, 
	       u_char *signature, const int sig_len)
{
	u_int sign_len = 0;
	int status = 0;
	B_ALGORITHM_OBJ *md5_ctx = NULL;
	int w_bytes = 0;
	u_int u_bytes = 0;
	u_char work_area[NS_MD5RSA_MAX_SIZE];

	if (mode & SIG_MODE_INIT) { 
		md5_ctx = (B_ALGORITHM_OBJ *) malloc(sizeof(B_ALGORITHM_OBJ));
		if ((status = B_CreateAlgorithmObject(md5_ctx)))
			return (-1);
		if ((status = B_SetAlgorithmInfo(*md5_ctx, AI_MD5, NULL)))
			return (-1);
	}
	else if (context) 
		md5_ctx = (B_ALGORITHM_OBJ *) *context;
	if (md5_ctx == NULL) 
		return (-1);

	w_bytes = dst_bsafe_md5digest(mode, md5_ctx, 
				      data, len,work_area, sizeof(work_area));
        if (w_bytes < 0 || (mode & SIG_MODE_FINAL)) {
		B_DestroyAlgorithmObject(md5_ctx);
		SAFE_FREE(md5_ctx);
		if (w_bytes < 0)
			return (w_bytes);
	}

	if (mode & SIG_MODE_FINAL) {
		RSA_Key *key;
		int ret = 0;
		B_ALGORITHM_OBJ rsaEncryptor = (B_ALGORITHM_OBJ) NULL_PTR;
		
		if (dkey == NULL || dkey->dk_KEY_struct == NULL)
			return (-1);
		key = (RSA_Key *) dkey->dk_KEY_struct;
		if (key == NULL || key->rk_Private_Key == NULL)
			return (-1);

		if ((status = B_CreateAlgorithmObject(&rsaEncryptor)))
			return (SIGN_FINAL_FAILURE);
		if ((status = B_SetAlgorithmInfo(rsaEncryptor,
						 AI_PKCS_RSAPrivate,
						 NULL_PTR)))

			ret = SIGN_FINAL_FAILURE;
		if (ret == 0 && 
		    (status = B_EncryptInit(rsaEncryptor,
					    key->rk_Private_Key,
					    CHOOSER, NULL_SURRENDER)))
			ret = SIGN_FINAL_FAILURE;
		if (ret == 0 &&
		    (status = B_EncryptUpdate(rsaEncryptor, signature,
					      &u_bytes, sig_len, pkcs1,
					      sizeof(pkcs1), NULL_PTR,
					      NULL_SURRENDER)))
			ret = SIGN_FINAL_FAILURE;
		if (ret == 0 &&
		    (status = B_EncryptUpdate(rsaEncryptor, signature,
					      &u_bytes, sig_len, work_area,
					      w_bytes, NULL_PTR,
					      NULL_SURRENDER)))
			ret = SIGN_FINAL_FAILURE;

		if (ret == 0 &&
		    (status = B_EncryptFinal(rsaEncryptor, signature + u_bytes,
					     &sign_len, sig_len - u_bytes,
					     NULL_PTR, NULL_SURRENDER)))
			ret = SIGN_FINAL_FAILURE;
		B_DestroyAlgorithmObject(&rsaEncryptor);
		if (ret != 0) 
			return (ret);

	}
	else {
		if (context == NULL) 
			return (-1);
		*context = (void *) md5_ctx;
	}
	return (sign_len);
}


/*
 * Dst_bsafe_verify 
 *     Calls BSAFE verification routines.  There are three steps to 
 *     verification, INIT (initialize structures), UPDATE (hash (more) data), 
 *     FINAL (generate a signature).  This routine performs one or more of 
 *     these steps.
 * Parameters
 *     mode	SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
 *     dkey      structure holds context for a verify done in multiple calls.
 *     context   the context to use for this computation
 *     data	data signed.
 *     len	 length in bytes of data.
 *     pub_key     key to use for verify.
 *     signature   signature.
 *     sig_len     length in bytes of signature.
 * returns 
 *     0  Success 
 *    <0  Failure
 */

static int
dst_bsafe_verify(const int mode, DST_KEY *dkey, void **context,
		 const u_char *data, const int len,
		 const u_char *signature, const int sig_len)
{
	B_ALGORITHM_OBJ *md5_ctx = NULL;
	u_char digest[DST_HASH_SIZE];
	u_char work_area[DST_HASH_SIZE + sizeof(pkcs1)];
	int status = 0, w_bytes = 0;
	u_int u_bytes = 0;

	if (mode & SIG_MODE_INIT) { 
		md5_ctx = (B_ALGORITHM_OBJ *) malloc(sizeof(B_ALGORITHM_OBJ));
		if ((status = B_CreateAlgorithmObject(md5_ctx)))
			return (-1);
		if ((status = B_SetAlgorithmInfo(*md5_ctx, AI_MD5, NULL)))
			return (-1);
	}
	else if (context) 
		md5_ctx = (B_ALGORITHM_OBJ *) *context;
	if (md5_ctx == NULL) 
		return (-1);

	w_bytes = dst_bsafe_md5digest(mode, md5_ctx, data, len, 
				      digest, sizeof(digest));

	if (w_bytes < 0 || (mode & SIG_MODE_FINAL)) {
		B_DestroyAlgorithmObject(md5_ctx);
		SAFE_FREE(md5_ctx);
		if (w_bytes < 0)
			return (-1);
	}

	if (mode & SIG_MODE_FINAL) {
		RSA_Key *key;
		int ret = 0;
		B_ALGORITHM_OBJ rsaEncryptor = (B_ALGORITHM_OBJ) NULL_PTR;

		if (dkey == NULL || dkey->dk_KEY_struct == NULL)
			return (-1);
		key = (RSA_Key *) dkey->dk_KEY_struct;
		if (key->rk_Public_Key == NULL)
			return (-2);
		if (rsaEncryptor == NULL_PTR) {
			if ((status = B_CreateAlgorithmObject(&rsaEncryptor)))
				ret = SIGN_FINAL_FAILURE;
			if (ret == 0 &&
			    (status = B_SetAlgorithmInfo(rsaEncryptor,
							 AI_PKCS_RSAPublic,
							 NULL_PTR)))
				ret = VERIFY_FINAL_FAILURE;
		}
		if (ret == 0 &&
		    (status = B_DecryptInit(rsaEncryptor, key->rk_Public_Key,
					    CHOOSER, NULL_SURRENDER)))
			ret = VERIFY_FINAL_FAILURE;

		if (ret == 0 && 
		    (status = B_DecryptUpdate(rsaEncryptor, work_area,
					      &u_bytes, 0,
					      (u_char *) signature, sig_len,
					      NULL_PTR, NULL_SURRENDER)))
			ret = VERIFY_FINAL_FAILURE;

		if (ret == 0 && 
		    (status = B_DecryptFinal(rsaEncryptor, work_area + u_bytes,
					     &u_bytes,
					     sizeof(work_area) - u_bytes,
					     NULL_PTR, NULL_SURRENDER)))
			ret = VERIFY_FINAL_FAILURE;
		B_DestroyAlgorithmObject(&rsaEncryptor);
		/* skip PKCS#1 header in output from Decrypt function */
		if (ret)
			return (ret);
		ret = memcmp(digest, &work_area[sizeof(pkcs1)], w_bytes);
		if (ret == 0)
			return(0);
		else
			return(VERIFY_FINAL_FAILURE);
	}
	else {
		if (context == NULL) 
			return (-1);
		*context = (void *) md5_ctx;
	}
	return (0);
}


/*
 * dst_bsafe_to_dns_key
 *     Converts key from RSA to DNS distribution format
 *     This function gets in a pointer to the public key and a work area
 *     to write the key into.
 * Parameters
 *     public    KEY structure 
 *     out_str   buffer to write encoded key into 
 *     out_len   size of out_str
 * Return
 *	N >= 0 length of encoded key 
 *	n < 0  error 
 */

static int
dst_bsafe_to_dns_key(const DST_KEY *in_key, u_char *out_str,
		     const int out_len)
{
	B_KEY_OBJ public;
	A_RSA_KEY *pub = NULL;
	u_char *op = out_str;
	int n = 0;

	if (in_key == NULL || in_key->dk_KEY_struct == NULL ||
	    out_len <= 0 || out_str == NULL)
		return (-1);
	public = (B_KEY_OBJ)((RSA_Key *) in_key->dk_KEY_struct)->rk_Public_Key;

	n = B_GetKeyInfo((POINTER *) &pub, public, KI_RSAPublic);

	if (out_len < pub->exponent.len) /* not enough space */
		return (-1);
	if (pub->exponent.len < 256)  /* key exponent is <= 2040 bits */
		*op++ = (u_int8_t) pub->exponent.len;
	else {                       /*  key exponent is > 2040 bits */
		u_int16_t e = (u_int16_t) pub->exponent.len;
		*op++ = 0;          /* 3 byte lenght field */
		dst_s_put_int16(op, e);
		op += sizeof(e);
		n = 2;
	}
	n += pub->exponent.len;
	memcpy(op, pub->exponent.data, n);
	op += n;
	n++;

	if ((out_len - n) > pub->modulus.len) {
		/*copy exponent */
		memcpy(op, pub->modulus.data, pub->modulus.len);
		n += pub->modulus.len;
	}
	else 
		n = -1;
	return (n);
}


/*
 * dst_bsafe_from_dns_key
 *     Converts from a DNS KEY RR format to an RSA KEY. 
 * Parameters
 *     len    Length in bytes of DNS key
 *     key    DNS key
 *     name   Key name
 *     s_key  DST structure that will point to the RSA key this routine
 *		will build.
 * Return
 *     0   The input key, s_key or name was null.
 *     1   Success
 */
static int
dst_bsafe_from_dns_key(DST_KEY *s_key, const u_char *key, const int len)
{
	int bytes;
	const u_char *key_ptr;
	RSA_Key *r_key;
	A_RSA_KEY *public;

	if (s_key == NULL || len < 0 || key == NULL)
		return (0);

	r_key = (RSA_Key *) s_key->dk_KEY_struct;
	if (r_key != NULL)	/* do not reuse */
		s_key->dk_func->destroy(r_key);

	if (len == 0)
		return (1);

	if ((r_key = (RSA_Key *) malloc(sizeof(RSA_Key))) == NULL) {
		EREPORT(("dst_bsafe_from_dns_key(): Memory allocation error 1"));
		return (0);
	}
	memset(r_key, 0, sizeof(RSA_Key));
	s_key->dk_KEY_struct = (void *) r_key;
	r_key->rk_signer = strdup(s_key->dk_key_name);

	if (B_CreateKeyObject(&r_key->rk_Public_Key) != 0) {
		EREPORT(("dst_bsafe_from_dns_key(): Memory allocation error 3"));
		s_key->dk_func->destroy(r_key);
		return (0);
	}
	key_ptr = key;
	bytes = (int) *key_ptr++;	/* length of exponent in bytes */
	if (bytes == 0)  {             /* special case for long exponents */
		bytes = (int) dst_s_get_int16(key_ptr);
		key_ptr += sizeof(u_int16_t);
	}
	if (bytes > MAX_RSA_MODULUS_LEN) { 
		dst_bsafe_free_key_structure(r_key);
		return (-1);
	}
	if ((public = (A_RSA_KEY *) malloc(sizeof(A_RSA_KEY))) == NULL)
		return (0);
	memset(public, 0, sizeof(*public));
	public->exponent.len = bytes;
	if ((public->exponent.data = (u_char *) malloc(bytes)) == NULL)
		return (0);
	memcpy(public->exponent.data, key_ptr, bytes);

	key_ptr += bytes;	/* beginning of modulus */
	bytes = len - bytes - 1;	/* length of modulus */

	if (bytes > MAX_RSA_MODULUS_LEN) { 
		dst_bsafe_free_key_structure(r_key);
		return (-1);
	}
	public->modulus.len = bytes;
	if ((public->modulus.data = (u_char *) malloc(bytes)) == NULL)
		return (0);
	memcpy(public->modulus.data, key_ptr, bytes);

	B_SetKeyInfo(r_key->rk_Public_Key, KI_RSAPublic, (POINTER) public);

	s_key->dk_id = (u_int16_t)
		dst_s_get_int16(&public->modulus.data[public->modulus.len - 3]);
	s_key->dk_key_size = dst_bsafe_key_size(r_key);
	SAFE_FREE(public->modulus.data);
	SAFE_FREE(public->exponent.data);
	SAFE_FREE(public);	
	return (1);
}


/*
 *  dst_bsafe_key_to_file_format
 *	Encodes an RSA Key into the portable file format.
 *  Parameters 
 *	rkey      RSA KEY structure 
 *	buff      output buffer
 *	buff_len  size of output buffer 
 *  Return
 *	0  Failure - null input rkey
 *     -1  Failure - not enough space in output area
 *	N  Success - Length of data returned in buff
 */

static int
dst_bsafe_key_to_file_format(const DST_KEY *key, char *buff,
			     const int buff_len)
{
	char *bp;
	int len, b_len;
	B_KEY_OBJ rkey;
	A_PKCS_RSA_PRIVATE_KEY *private = NULL;

	if (key == NULL || key->dk_KEY_struct == NULL)	/* no output */
		return (0);
	if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str))
		return (-1);	/* no OR not enough space in output area */

	rkey = (B_KEY_OBJ)((RSA_Key *) key->dk_KEY_struct)->rk_Private_Key;

	B_GetKeyInfo((POINTER *) &private, rkey, KI_PKCS_RSAPrivate);

	    memset(buff, 0, buff_len);	/* just in case */
	/* write file header */
	sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_RSA, "RSA");

	bp = strchr(buff, '\0');
	b_len = buff_len - (bp - buff);
	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Modulus: ",
					       private->modulus.data,
					       private->modulus.len)) <= 0)
		return (-1);

	bp += len;
	b_len -= len;
	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "PublicExponent: ",
					       private->publicExponent.data,
					 private->publicExponent.len)) <= 0)
		return (-2);

	bp += len;
	b_len -= len;
	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "PrivateExponent: ",
					       private->privateExponent.data,
					private->privateExponent.len)) <= 0)
		return (-3);
	bp += len;
	b_len -= len;

	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime1: ",
					       private->prime[0].data,
					       private->prime[0].len)) < 0)
		return (-4);
	bp += len;
	b_len -= len;

	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime2: ",
					       private->prime[1].data,
					       private->prime[1].len)) < 0)
		return (-5);
	bp += len;
	b_len -= len;

	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Exponent1: ",
					     private->primeExponent[0].data,
					private->primeExponent[0].len)) < 0)
		return (-6);
	bp += len;
	b_len -= len;

	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Exponent2: ",
					     private->primeExponent[1].data,
					private->primeExponent[1].len)) < 0)
		return (-7);
	bp += len;
	b_len -= len;

	if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Coefficient: ",
					       private->coefficient.data,
					     private->coefficient.len)) < 0)
		return (-8);
	bp += len;
	b_len -= len;
	return (buff_len - b_len);
}


/*
 * dst_bsafe_key_from_file_format
 *     Converts contents of a private key file into a private RSA key. 
 * Parameters 
 *     RSA_Key    structure to put key into 
 *     buff       buffer containing the encoded key 
 *     buff_len   the length of the buffer
 * Return
 *     n >= 0 Foot print of the key converted 
 *     n <  0 Error in conversion 
 */

static int
dst_bsafe_key_from_file_format(DST_KEY *d_key, const char *buff,
			       const int buff_len)
{
	int status;
	char s[RAW_KEY_SIZE];
	int len, s_len = sizeof(s);
	int tag = -1;
	const char *p = buff;
	RSA_Key *b_key;
	A_RSA_KEY *public;
	A_PKCS_RSA_PRIVATE_KEY *private;

	if (d_key == NULL || buff == NULL || buff_len <= 0)
		return (-1);

	b_key = (RSA_Key *) malloc(sizeof(RSA_Key));
	public = (A_RSA_KEY *) malloc(sizeof(A_RSA_KEY));
	private = (A_PKCS_RSA_PRIVATE_KEY *)
		malloc(sizeof(A_PKCS_RSA_PRIVATE_KEY));
	if (b_key == NULL || private == NULL || public == NULL) {
		SAFE_FREE(b_key);
		SAFE_FREE(public);
		SAFE_FREE(private);
		return (-2);
	}
	memset(b_key, 0, sizeof(*b_key));
	memset(public, 0, sizeof(A_RSA_KEY));
	memset(private, 0, sizeof(A_PKCS_RSA_PRIVATE_KEY));
	d_key->dk_KEY_struct = (void *) b_key;
	if (!dst_s_verify_str(&p, "Modulus: "))
		return (-3);
	memset(s, 0, s_len);
	if ((len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len)) == 0)
		return (-4);

	private->modulus.len = len;
	if ((private->modulus.data = malloc(len)) == NULL)
		return (-5);
	memcpy(private->modulus.data, s + s_len - len, len);

	while (*(++p) && p < (const char *) &buff[buff_len]) {
		if (dst_s_verify_str(&p, "PublicExponent: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len)))
				return (-5);
			private->publicExponent.len = len;
			if ((private->publicExponent.data = malloc(len))
			    == NULL)
				return (-6);
			memcpy(private->publicExponent.data,
			       s + s_len - len, len);
		} else if (dst_s_verify_str(&p, "PrivateExponent: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len)))
				return (-6);
			private->privateExponent.len = len;
			if ((private->privateExponent.data = malloc(len))
			    == NULL)
				return (-7);
			memcpy(private->privateExponent.data, s + s_len - len,
			       len);
		} else if (dst_s_verify_str(&p, "Prime1: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s,
							MAX_RSA_PRIME_LEN)))
				return (-7);
			private->prime[0].len = len;
			if ((private->prime[0].data = malloc(len)) == NULL)
				return (-8);
			memcpy(private->prime[0].data,
			       s + MAX_RSA_PRIME_LEN - len, len);
		} else if (dst_s_verify_str(&p, "Prime2: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s,
							MAX_RSA_PRIME_LEN)))
				return (-8);
			private->prime[1].len = len;
			if ((private->prime[1].data = malloc(len)) == NULL)
				return (-9);
			memcpy(private->prime[1].data,
			       s + MAX_RSA_PRIME_LEN - len, len);
		} else if (dst_s_verify_str(&p, "Exponent1: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s,
							MAX_RSA_PRIME_LEN)))
				return (-9);
			private->primeExponent[0].len = len;
			if ((private->primeExponent[0].data = malloc(len))
			    == NULL)
				return (-10);
			memcpy(private->primeExponent[0].data,
			       s + MAX_RSA_PRIME_LEN - len, len);
		} else if (dst_s_verify_str(&p, "Exponent2: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s,
							MAX_RSA_PRIME_LEN)))
				return (-10);
			private->primeExponent[1].len = len;
			if ((private->primeExponent[1].data = malloc(len))
			    == NULL)
				return (-11);
			memcpy(private->primeExponent[1].data,
			       s + MAX_RSA_PRIME_LEN - len, len);
		} else if (dst_s_verify_str(&p, "Coefficient: ")) {
			if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s,
							MAX_RSA_PRIME_LEN)))
				return (-11);
			private->coefficient.len = len;
			if ((private->coefficient.data = malloc(len)) == NULL)
				return (-12);
			memcpy(private->coefficient.data,
			       s + MAX_RSA_PRIME_LEN - len, len);
		} else {
			EREPORT(("Decode_RSAKey(): Bad keyword %s\n", p));
			return (-12);
		}
	}			/* while p */

	public->modulus.len = private->modulus.len;
	if ((public->modulus.data = (u_char *) malloc(public->modulus.len)) ==
	    NULL)
		return (-13);
	memcpy(public->modulus.data, private->modulus.data,
	       private->modulus.len);

	public->exponent.len = private->publicExponent.len;
	if ((public->exponent.data = (u_char *) malloc(public->exponent.len))
	    == NULL)
		return (-14);
	memcpy(public->exponent.data, private->publicExponent.data,
	       private->publicExponent.len);

	status = B_CreateKeyObject(&(b_key->rk_Public_Key));
	if (status)
		return (-1);
	status = B_SetKeyInfo(b_key->rk_Public_Key, KI_RSAPublic,
			      (POINTER) public);
	if (status)
		return (-1);

	status = B_CreateKeyObject(&b_key->rk_Private_Key);
	if (status)
		return (-1);
	status = B_SetKeyInfo(b_key->rk_Private_Key, KI_PKCS_RSAPrivate,
			      (POINTER) private);
	if (status)
		return (-1);

	tag = (int)(u_int16_t)
		dst_s_get_int16(&public->modulus.data[public->modulus.len - 3]);
	d_key->dk_key_size = dst_bsafe_key_size(b_key);

	SAFE_FREE(private->modulus.data);
	SAFE_FREE(private->publicExponent.data);
	SAFE_FREE(private->privateExponent.data);
	SAFE_FREE(private->prime[0].data);
	SAFE_FREE(private->prime[1].data);
	SAFE_FREE(private->primeExponent[0].data);
	SAFE_FREE(private->primeExponent[1].data);
	SAFE_FREE(private->coefficient.data);
	SAFE_FREE(private);	/* is this the right thing to do ??? XXXX */
	SAFE_FREE(public->modulus.data);
	SAFE_FREE(public->exponent.data);
	SAFE_FREE(public);
	return (tag);
}


/*
 * dst_bsafe_free_key_structure
 *     Frees all dynamicly allocated structures in RSA_Key.
 */

static void *
dst_bsafe_free_key_structure(void *key)
{
	RSA_Key *r_key = (RSA_Key *) key;
	if (r_key != NULL) {
		if (r_key->rk_Private_Key)
			B_DestroyKeyObject(&r_key->rk_Private_Key);
		if (r_key->rk_Public_Key)
			B_DestroyKeyObject(&r_key->rk_Public_Key);
		SAFE_FREE2(r_key->rk_signer, strlen(r_key->rk_signer));
		SAFE_FREE(r_key);
	}
	return (NULL);
}


/*
 *  dst_bsafe_generate_keypair
 *	Generates unique keys that are hard to predict.
 *  Parameters
 *	key    generic Key structure
 *	exp    the public exponent
 *  Return 
 *	0 Failure 
 *	1 Success
 */

static int
dst_bsafe_generate_keypair(DST_KEY *key, int exp)
{
	int i, status;
	B_KEY_OBJ private;
	B_KEY_OBJ public;
	B_ALGORITHM_OBJ keypairGenerator;
	B_ALGORITHM_OBJ randomAlgorithm;
	A_RSA_KEY_GEN_PARAMS keygenParams;
	char exponent[4];
	int exponent_len;
	RSA_Key *rsa;
	POINTER randomSeed = NULL_PTR;
	int randomSeedLen;
	A_RSA_KEY *pk_access = NULL;

	if (key == NULL || key->dk_alg != KEY_RSA)
		return (0);

	if ((rsa = (RSA_Key *) malloc(sizeof(RSA_Key))) == NULL) {
		EREPORT(("dst_bsafe_generate_keypair: Memory allocation error 3"));
		return (0);
	}
	memset(rsa, 0, sizeof(*rsa));

	if ((status = B_CreateAlgorithmObject(&keypairGenerator)) != 0)
		return (0);

	keygenParams.modulusBits = key->dk_key_size;

	/* exp = 0 or 1 are special (mean 3 or F4) */
	if (exp == 0)
		exp = 3;
	else if (exp == 1)
		exp = 65537;

	/* Now encode the exponent and its length */
	if (exp < 256) {
		exponent_len = 1;
		exponent[0] = exp;
	} else if (exp < (1 << 16)) {
		exponent_len = 2;
		exponent[0] = exp >> 8;
		exponent[1] = exp;
	} else if (exp < (1 << 24)) {
		exponent_len = 3;
		exponent[0] = exp >> 16;
		exponent[1] = exp >> 8;
		exponent[2] = exp;
	} else {
		exponent_len = 4;
		exponent[0] = exp >> 24;
		exponent[1] = exp >> 16;
		exponent[2] = exp >> 8;
		exponent[3] = exp;
	}

	if ((keygenParams.publicExponent.data = (u_char *) malloc(exponent_len))
	    == NULL)
		return (0);
	memcpy(keygenParams.publicExponent.data, exponent, exponent_len);
	keygenParams.publicExponent.len = exponent_len;
	if ((status = B_SetAlgorithmInfo
	     (keypairGenerator, AI_RSAKeyGen, (POINTER) &keygenParams)) != 0)
		return (0);

	if ((status = B_GenerateInit(keypairGenerator, CHOOSER,
				     NULL_SURRENDER)) != 0)
		return (0);

	if ((status = B_CreateKeyObject(&public)) != 0)
		return (0);

	if ((status = B_CreateKeyObject(&private)) != 0)
		return (0);

	if ((status = B_CreateAlgorithmObject(&randomAlgorithm)) != 0)
		return (0);

	if ((status = B_SetAlgorithmInfo(randomAlgorithm, AI_MD5Random,
					 NULL_PTR))
	    != 0)
		return (0);

	if ((status = B_RandomInit(randomAlgorithm, CHOOSER,
				   NULL_SURRENDER)) != 0)
		return (0);

	randomSeedLen = 256;
	if ((randomSeed = malloc(randomSeedLen)) == NULL)
		return (0);
	if ((status = (randomSeed == NULL_PTR)) != 0)
		return (0);

	/* gets random seed from /dev/random if present, generates random
	 * values if it is not present. 
	 * first fill the buffer with semi random data 
	 * then fill as much as possible with good random data 
	 */
	i = dst_random(DST_RAND_SEMI, randomSeedLen, randomSeed);
	i += dst_random(DST_RAND_KEY,  randomSeedLen, randomSeed);

	if (i <= randomSeedLen) {
		SAFE_FREE(rsa);
		return(0);
	}
	if ((status = B_RandomUpdate(randomAlgorithm, randomSeed, 
				     randomSeedLen, NULL_SURRENDER)) != 0) {
		SAFE_FREE(rsa);
		return (0);
	}
	SAFE_FREE2(randomSeed, randomSeedLen);
	if ((status = B_GenerateKeypair(keypairGenerator, public, private,
					randomAlgorithm, NULL_SURRENDER))
	    != 0) {
		SAFE_FREE(rsa);
		return (0);
	}
	rsa->rk_signer = strdup(key->dk_key_name);
	rsa->rk_Private_Key = private;
	rsa->rk_Public_Key = public;
	key->dk_KEY_struct = (void *) rsa;

	/* fill in the footprint on generate key */
	B_GetKeyInfo((POINTER *) &pk_access, public, KI_RSAPublic);
	key->dk_id = (u_int16_t)
		dst_s_get_int16(&pk_access->modulus.data[pk_access->modulus.len - 3]);
	return (1);
}


/************************************************************************** 
 *  dst_bsafe_compare_keys
 *	Compare two keys for equality.
 *  Return
 *	0	  The keys are equal
 *	NON-ZERO   The keys are not equal
 */

static int
dst_s_bsafe_itemcmp(ITEM i1, ITEM i2)
{
	if (i1.len != i2.len || memcmp (i1.data, i2.data, i1.len))
		return (1);
	else
		return (0);
}

static int
dst_bsafe_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
{
	int status, s1 = 0, s2 = 0;
	RSA_Key *rkey1 = (RSA_Key *) key1->dk_KEY_struct;
	RSA_Key *rkey2 = (RSA_Key *) key2->dk_KEY_struct;
	A_RSA_KEY *public1 = NULL, *public2 = NULL;
	A_PKCS_RSA_PRIVATE_KEY *p1 = NULL, *p2 = NULL;

	if (rkey1 == NULL && rkey2 == NULL) 
		return(0);
	else if (rkey1 == NULL) 
		return (1);
	else if (rkey2 == NULL)
		return (2);

	if (rkey1->rk_Public_Key) 
		B_GetKeyInfo((POINTER *) &public1, rkey1->rk_Public_Key, 
			     KI_RSAPublic);
	if (rkey2->rk_Public_Key) 
		B_GetKeyInfo((POINTER *) &public2, rkey2->rk_Public_Key, 
			     KI_RSAPublic);
	if (public1 == NULL && public2 == NULL)
		return (0);
	else if (public1 == NULL || public2 == NULL)
		return (1);

	status = dst_s_bsafe_itemcmp(public1->modulus, public2->modulus) ||
		 dst_s_bsafe_itemcmp(public1->exponent, public2->exponent);

	if (status) 
		return (status);

	if (rkey1->rk_Private_Key == NULL || rkey2->rk_Private_Key == NULL)
		/* if neither or only one is private key consider identical */
		return (status);  
	if (rkey1->rk_Private_Key)
		s1 = B_GetKeyInfo((POINTER *) &p1, rkey1->rk_Private_Key,
				  KI_PKCS_RSAPrivate);
	if (rkey2->rk_Private_Key)
		s2 = B_GetKeyInfo((POINTER *) &p2, rkey2->rk_Private_Key,
				  KI_PKCS_RSAPrivate);
	if (p1 == NULL || p2 == NULL) 
		return (0);

	status = dst_s_bsafe_itemcmp(p1->modulus, p2->modulus) ||
		dst_s_bsafe_itemcmp (p1->publicExponent, 
				     p2->publicExponent) ||
		dst_s_bsafe_itemcmp (p1->privateExponent, 
				     p2->privateExponent) ||
		dst_s_bsafe_itemcmp (p1->prime[0], p2->prime[0]) ||
		dst_s_bsafe_itemcmp (p1->prime[1], p2->prime[1]) ||
		dst_s_bsafe_itemcmp (p1->primeExponent[0], 
				     p2->primeExponent[0])||
		dst_s_bsafe_itemcmp (p1->primeExponent[1], 
				     p2->primeExponent[1])||
		dst_s_bsafe_itemcmp (p1->coefficient, p2->coefficient);
	return (status);
}


/* 
 * dst_bsafe_key_size() 
 * Function to calculate how the size of the key in bits
 */
static int
dst_bsafe_key_size(RSA_Key *r_key)
{
	int size;
	A_PKCS_RSA_PRIVATE_KEY *private = NULL;

	if (r_key == NULL)
		return (-1);
	if (r_key->rk_Private_Key)
		B_GetKeyInfo((POINTER *) &private, r_key->rk_Private_Key,
			     KI_PKCS_RSAPrivate);
	else if (r_key->rk_Public_Key)
		B_GetKeyInfo((POINTER *) &private, r_key->rk_Public_Key,
			     KI_RSAPublic);
	size = dst_s_calculate_bits(private->modulus.data,
				    private->modulus.len * 8);
	return (size);
}

/* 
 * dst_bsafe_md5digest(): function to digest data using MD5 digest function 
 * if needed 
 */
static int
dst_bsafe_md5digest(const int mode, B_ALGORITHM_OBJ *digest_obj,
		    const u_char *data, const int len,
		    u_char *digest, const int digest_len)
{
	int status = 0;
	u_int work_size = 0;

	if (digest_obj == NULL || *digest_obj == NULL) {
		printf("NO digest obj\n");
		exit(-33);
	}

	if ((mode & SIG_MODE_INIT) &&
	    (status = B_DigestInit(*digest_obj, (B_KEY_OBJ) NULL,
				   CHOOSER, NULL_SURRENDER)))
		return (SIGN_INIT_FAILURE);

	if ((mode & SIG_MODE_UPDATE) && data && (len > 0) &&
	    (status = B_DigestUpdate(*digest_obj, (u_char *) data, len,
				     NULL_SURRENDER)))
		return (SIGN_UPDATE_FAILURE);

	if (mode & SIG_MODE_FINAL) {
		if (digest == NULL ||
		    (status = B_DigestFinal(*digest_obj, digest, &work_size,
					    digest_len, NULL_SURRENDER)))
			return (SIGN_FINAL_FAILURE);
		return (work_size);
	}
	return (0);
}

/* 
 * just use the standard memory functions for bsafe 
 */
void
T_free(POINTER block)
{
	free(block);
}

POINTER
T_malloc(unsigned int len)
{
	return (malloc(len));
}

int
T_memcmp(POINTER firstBlock, POINTER secondBlock, unsigned int len)
{
	return (memcmp(firstBlock, secondBlock, len));
}

void
T_memcpy(POINTER output, POINTER input, unsigned int len)
{
	memcpy(output, input, len);
}

void
T_memmove(POINTER output, POINTER input, unsigned int len)
{
	memmove(output, input, len);
}

void
T_memset(POINTER output, int value, unsigned int len)
{
	memset(output, value, len);
}

POINTER
T_realloc(POINTER block, unsigned int len)
{
	return (realloc(block, len));
}

#else  /* BSAFE NOT available */
int
dst_bsafe_init()
{
	return (0);
}
#endif /* BSAFE */
