/*
 * COPYRIGHT (c) International Business Machines Corp. 2001-2017
 *
 * This program is provided under the terms of the Common Public License,
 * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
 * software constitutes recipient's acceptance of CPL-1.0 terms which can be
 * found in the file LICENSE file or at
 * https://opensource.org/licenses/cpl1.0.php
 */

// File:  mech_ssl3.c
//
// Mechanisms for SSL v3 support
//

#include <pthread.h>
#include <string.h>            // for memcmp() et al
#include <stdlib.h>
#include <stdio.h>

#include "pkcs11types.h"
#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "tok_spec_struct.h"
#include "trace.h"

CK_RV ssl3_kmd_process_mac_keys( STDLL_TokData_t   * tokdata,
				 SESSION           * sess,
                                 CK_ATTRIBUTE      * pTemplate,
                                 CK_ULONG            ulCount,
                                 CK_OBJECT_HANDLE  * client_handle,
                                 CK_BYTE           * client_value,
                                 CK_OBJECT_HANDLE  * server_handle,
                                 CK_BYTE           * server_value,
                                 CK_ULONG            mac_len );

CK_RV ssl3_kmd_process_write_keys( STDLL_TokData_t   * tokdata,
				   SESSION           * sess,
                                   CK_ATTRIBUTE      * pTemplate,
                                   CK_ULONG            ulCount,
                                   CK_KEY_TYPE         keytype,
                                   CK_OBJECT_HANDLE  * client_handle,
                                   CK_BYTE           * client_value,
                                   CK_OBJECT_HANDLE  * server_handle,
                                   CK_BYTE           * server_value,
                                   CK_ULONG            write_len );

// The 'ssl3_mac_*' routines are used with the following mechanisms
//
//    CKM_SSL3_MD5_MAC
//    CKM_SSL3_SHA1_MAC
//

//
//
CK_RV
ssl3_mac_sign( STDLL_TokData_t      * tokdata,
	       SESSION              * sess,
               CK_BBOOL               length_only,
               SIGN_VERIFY_CONTEXT  * ctx,
               CK_BYTE              * in_data,
               CK_ULONG               in_data_len,
               CK_BYTE              * out_data,
               CK_ULONG             * out_data_len )
{
   OBJECT          * key_obj  = NULL;
   CK_ATTRIBUTE    * attr     = NULL;
   CK_BYTE           hash[SHA1_HASH_SIZE];
   CK_BYTE         * key_data = NULL;
   CK_BYTE           inner[48], outer[48];
   DIGEST_CONTEXT    digest_ctx;
   CK_MECHANISM      digest_mech;
   CK_ULONG          key_bytes, hash_len, mac_len;
   CK_RV             rc;


   if (!sess || !ctx || !out_data_len){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   mac_len = *(CK_ULONG *)ctx->mech.pParameter;

   if (length_only == TRUE) {
      *out_data_len = mac_len;
      return CKR_OK;
   }

   if (*out_data_len < mac_len) {
      *out_data_len = mac_len;
      TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
      return CKR_BUFFER_TOO_SMALL;
   }

   memset( &digest_ctx, 0x0, sizeof(DIGEST_CONTEXT) );

   rc = object_mgr_find_in_map1( tokdata, ctx->key, &key_obj );
   if (rc != CKR_OK){
      TRACE_ERROR("Failed to acquire key from specified handle");
      if (rc == CKR_OBJECT_HANDLE_INVALID)
	 return CKR_KEY_HANDLE_INVALID;
      else
	 return rc;
   }

   rc = template_attribute_find( key_obj->template, CKA_VALUE, &attr );
   if (rc == FALSE){
      TRACE_ERROR("Could not find CKA_VALUE in the template\n");
      return CKR_FUNCTION_FAILED;
   }
   else {
      key_bytes = attr->ulValueLen;
      key_data  = attr->pValue;
   }

   // unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
   // we just append 48 bytes to the key data
   //
   memset( inner, 0x36, 48 );
   memset( outer, 0x5C, 48 );

   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      digest_mech.mechanism   = CKM_MD5;
   else
      digest_mech.mechanism   = CKM_SHA_1;
   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;


   // inner hash
   //
   rc = digest_mgr_init( tokdata, sess, &digest_ctx, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, key_data,
				  key_bytes );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC){
      rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, inner, 48 );
   }
   else {
      rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, inner, 40 );
   }
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, in_data, in_data_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &digest_ctx,  hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest final failed.\n");
      return rc;
   }
   memset( &digest_ctx, 0x0, sizeof(DIGEST_CONTEXT) );


   // outer hash
   //
   rc = digest_mgr_init( tokdata, sess, &digest_ctx, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, key_data, key_bytes );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, outer, 48 );
   else
      rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, outer, 40 );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, hash, hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &digest_ctx, hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest final failed.\n");
      return rc;
   }
   memcpy( out_data, hash, mac_len );
   *out_data_len = mac_len;

   return rc;
}


//
//
CK_RV
ssl3_mac_sign_update( STDLL_TokData_t      * tokdata,
		      SESSION              * sess,
                      SIGN_VERIFY_CONTEXT  * ctx,
                      CK_BYTE              * in_data,
                      CK_ULONG               in_data_len )
{
   OBJECT           * key_obj  = NULL;
   CK_ATTRIBUTE     * attr     = NULL;
   CK_BYTE          * key_data = NULL;
   SSL3_MAC_CONTEXT * context  = NULL;

   CK_BYTE           inner[48];
   CK_MECHANISM      digest_mech;
   CK_ULONG          key_bytes;
   CK_RV             rc;


   if (!sess || !ctx){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   context = (SSL3_MAC_CONTEXT *)ctx->context;

   if (context->flag == FALSE) {
      rc = object_mgr_find_in_map1( tokdata, ctx->key, &key_obj );
      if (rc != CKR_OK){
	 TRACE_ERROR("Failed to acquire key from specified handle");
	 if (rc == CKR_OBJECT_HANDLE_INVALID)
	    return CKR_KEY_HANDLE_INVALID;
	 else
	    return rc;
   }
      rc = template_attribute_find( key_obj->template, CKA_VALUE, &attr );
      if (rc == FALSE) {
	 TRACE_ERROR("Could not find CKA_VALUE in the template\n");
         return CKR_FUNCTION_FAILED;
      }
      else {
         key_bytes = attr->ulValueLen;
         key_data  = attr->pValue;
      }

      // unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
      // we just append 48 bytes to the key data
      //
      memset( inner, 0x36, 48 );

      if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
         digest_mech.mechanism   = CKM_MD5;
      else
         digest_mech.mechanism   = CKM_SHA_1;

      digest_mech.ulParameterLen = 0;
      digest_mech.pParameter     = NULL;

      // inner hash
      //
      rc = digest_mgr_init( tokdata, sess, &context->hash_context, &digest_mech );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest Init failed.\n");
         return rc;
      }
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, key_data, key_bytes );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest update failed.\n");
         return rc;
      }
      if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
         rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, inner, 48 );
      else
         rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, inner, 40 );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest update failed.\n");
         return rc;
      }
      context->flag = TRUE;
   }


   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, in_data, in_data_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest update failed.\n");
      return rc;
   }

   return CKR_OK;
}


//
//
CK_RV
ssl3_mac_sign_final( STDLL_TokData_t      * tokdata,
		     SESSION              * sess,
                     CK_BBOOL               length_only,
                     SIGN_VERIFY_CONTEXT  * ctx,
                     CK_BYTE              * out_data,
                     CK_ULONG             * out_data_len )
{
   OBJECT           * key_obj  = NULL;
   CK_ATTRIBUTE     * attr     = NULL;
   CK_BYTE          * key_data = NULL;
   CK_BYTE            hash[SHA1_HASH_SIZE];
   SSL3_MAC_CONTEXT * context  = NULL;

   CK_BYTE           outer[48];
   CK_MECHANISM      digest_mech;
   CK_ULONG          key_bytes, hash_len, mac_len;
   CK_RV             rc;


   if (!sess || !ctx || !out_data_len){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   mac_len = *(CK_ULONG *)ctx->mech.pParameter;

   if (length_only == TRUE) {
      *out_data_len = mac_len;
      return CKR_OK;
   }

   if (*out_data_len < mac_len) {
      *out_data_len = mac_len;
      TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
      return CKR_BUFFER_TOO_SMALL;
   }

   context = (SSL3_MAC_CONTEXT *)ctx->context;

   rc = object_mgr_find_in_map1( tokdata, ctx->key, &key_obj );
   if (rc != CKR_OK){
      TRACE_ERROR("Failed to acquire key from specified handle");
      if (rc == CKR_OBJECT_HANDLE_INVALID)
	 return CKR_KEY_HANDLE_INVALID;
      else
         return rc;
   }
   rc = template_attribute_find( key_obj->template, CKA_VALUE, &attr );
   if (rc == FALSE) {
      TRACE_ERROR("Could not find CKA_VALUE in the template\n");
      return CKR_FUNCTION_FAILED;
   }
   else {
      key_bytes = attr->ulValueLen;
      key_data  = attr->pValue;
   }

   // finish the inner hash
   //
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &context->hash_context, hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      return rc;
   }
   // now, do the outer hash
   //
   memset( &context->hash_context, 0x0, sizeof(SSL3_MAC_CONTEXT) );

   memset( outer, 0x5C, 48 );

   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      digest_mech.mechanism   = CKM_MD5;
   else
      digest_mech.mechanism   = CKM_SHA_1;

   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;

   rc = digest_mgr_init( tokdata, sess, &context->hash_context, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				  key_data, key_bytes );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				     outer, 48 );
   else
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				     outer, 40 );

   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, hash,
				  hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &context->hash_context,
				 hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      return rc;
   }
   memcpy( out_data, hash, mac_len );
   *out_data_len = mac_len;

   return rc;
}



// This routine could replace the HMAC verification routines
//
CK_RV
ssl3_mac_verify( STDLL_TokData_t      * tokdata,
		 SESSION              * sess,
                 SIGN_VERIFY_CONTEXT  * ctx,
                 CK_BYTE              * in_data,
                 CK_ULONG               in_data_len,
                 CK_BYTE              * signature,
                 CK_ULONG               sig_len )
{
   CK_BYTE              mac[SHA1_HASH_SIZE];
   SIGN_VERIFY_CONTEXT  mac_ctx;
   CK_ULONG             mac_len, len;
   CK_RV                rc;

   if (!sess || !ctx || !in_data || !signature){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   mac_len = *(CK_ULONG *)ctx->mech.pParameter;

   memset( &mac_ctx, 0, sizeof(SIGN_VERIFY_CONTEXT) );

   rc = sign_mgr_init( tokdata, sess, &mac_ctx, &ctx->mech, FALSE, ctx->key );
   if (rc != CKR_OK){
      TRACE_DEVEL("Sign Init failed.\n");
      goto error;
   }
   len = sizeof(mac);
   rc = sign_mgr_sign( tokdata, sess, FALSE, &mac_ctx,
                       in_data, in_data_len,
                       mac,     &len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Sign failed.\n");
      goto error;
   }
   if ((len != mac_len) || (len != sig_len)) {
      rc = CKR_SIGNATURE_LEN_RANGE;
      TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_LEN_RANGE));
      goto error;
   }

   if (memcmp(mac, signature, mac_len) != 0){
      TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
      rc = CKR_SIGNATURE_INVALID;
   }
error:
   sign_mgr_cleanup( &mac_ctx );
   return rc;
}


//
//
CK_RV
ssl3_mac_verify_update( STDLL_TokData_t      * tokdata,
			SESSION              * sess,
                        SIGN_VERIFY_CONTEXT  * ctx,
                        CK_BYTE              * in_data,
                        CK_ULONG               in_data_len )
{
   OBJECT           * key_obj  = NULL;
   CK_ATTRIBUTE     * attr     = NULL;
   CK_BYTE          * key_data = NULL;
   SSL3_MAC_CONTEXT * context  = NULL;

   CK_BYTE           inner[48];
   CK_MECHANISM      digest_mech;
   CK_ULONG          key_bytes;
   CK_RV             rc;


   if (!sess || !ctx){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   context = (SSL3_MAC_CONTEXT *)ctx->context;

   if (context->flag == FALSE) {
      rc = object_mgr_find_in_map1( tokdata, ctx->key, &key_obj );
      if (rc != CKR_OK){
	 TRACE_ERROR("Failed to acquire key from specified handle");
	 if (rc == CKR_OBJECT_HANDLE_INVALID)
	    return CKR_KEY_HANDLE_INVALID;
	else
            return rc;
      }
      rc = template_attribute_find( key_obj->template, CKA_VALUE, &attr );
      if (rc == FALSE) {
	 TRACE_ERROR("Could not find CKA_VALUE in the template\n");
         return CKR_FUNCTION_FAILED;
      }
      else {
         key_bytes = attr->ulValueLen;
         key_data  = attr->pValue;
      }

      // unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
      // we just append 48 bytes to the key data
      //
      memset( inner, 0x36, 48 );

      if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
         digest_mech.mechanism   = CKM_MD5;
      else
         digest_mech.mechanism   = CKM_SHA_1;

      digest_mech.ulParameterLen = 0;
      digest_mech.pParameter     = NULL;

      // inner hash
      //
      rc = digest_mgr_init( tokdata, sess, &context->hash_context, &digest_mech );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest Init failed.\n");
         return rc;
      }
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, key_data, key_bytes );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest Update failed.\n");
         return rc;
      }
      if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
         rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, inner, 48 );
      else
         rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, inner, 40 );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest Update failed.\n");
         return rc;
      }
      context->flag = TRUE;
   }

   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, in_data, in_data_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }

   return CKR_OK;
}


//
//
CK_RV
ssl3_mac_verify_final( STDLL_TokData_t      * tokdata,
		       SESSION              * sess,
                       SIGN_VERIFY_CONTEXT  * ctx,
                       CK_BYTE              * signature,
                       CK_ULONG               sig_len )
{
   OBJECT           * key_obj  = NULL;
   CK_ATTRIBUTE     * attr     = NULL;
   CK_BYTE          * key_data = NULL;
   SSL3_MAC_CONTEXT * context  = NULL;
   CK_BYTE            hash[SHA1_HASH_SIZE];

   CK_BYTE           outer[48];
   CK_MECHANISM      digest_mech;
   CK_ULONG          key_bytes, hash_len, mac_len;
   CK_RV             rc;


   if (!sess || !ctx || !signature){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   mac_len = *(CK_ULONG *)ctx->mech.pParameter;

   context = (SSL3_MAC_CONTEXT *)ctx->context;

   rc = object_mgr_find_in_map1( tokdata, ctx->key, &key_obj );
   if (rc != CKR_OK){
      TRACE_ERROR("Failed to acquire key from specified handle");
      if (rc == CKR_OBJECT_HANDLE_INVALID)
	 return CKR_KEY_HANDLE_INVALID;
      else
         return rc;
   }
   rc = template_attribute_find( key_obj->template, CKA_VALUE, &attr );
   if (rc == FALSE) {
      TRACE_ERROR("Could not find CKA_VALUE in the template\n");
      return CKR_FUNCTION_FAILED;
   }
   else {
      key_bytes = attr->ulValueLen;
      key_data  = attr->pValue;
   }

   // finish the inner hash
   //
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &context->hash_context,
				 hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      return rc;
   }
   // now, do the outer hash
   //
   memset( &context->hash_context, 0x0, sizeof(SSL3_MAC_CONTEXT) );

   memset( outer, 0x5C, 48 );

   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      digest_mech.mechanism   = CKM_MD5;
   else
      digest_mech.mechanism   = CKM_SHA_1;

   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;

   rc = digest_mgr_init( tokdata, sess, &context->hash_context, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				  key_data, key_bytes );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				     outer, 48 );
   else
      rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context,
				     outer, 40 );

   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &context->hash_context, hash,
				  hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   hash_len = sizeof(hash);
   rc = digest_mgr_digest_final( tokdata, sess, FALSE, &context->hash_context,
				 hash, &hash_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      return rc;
   }
   if ((mac_len != sig_len) || (mac_len > hash_len)){
      TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
      rc = CKR_SIGNATURE_INVALID;
   }
   else if (memcmp(signature, hash, sig_len) != 0){
      rc = CKR_SIGNATURE_INVALID;
      TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
   }

   return rc;
}


//
//
CK_RV
ckm_ssl3_pre_master_key_gen( STDLL_TokData_t *tokdata,
			     TEMPLATE     * tmpl,
                             CK_MECHANISM * mech )
{
   CK_ATTRIBUTE        * value_attr     = NULL;
   CK_ATTRIBUTE        * value_len_attr = NULL;
   CK_ATTRIBUTE        * key_type_attr  = NULL;
   CK_ATTRIBUTE        * class_attr     = NULL;
   CK_ATTRIBUTE        * local_attr     = NULL;
   CK_ATTRIBUTE        * derive_attr    = NULL;
   CK_VERSION          * version        = NULL;
   CK_BYTE               key[48];
   CK_ULONG              rc;


   rc = rng_generate( tokdata, key, 48 );
   if (rc != CKR_OK){
      TRACE_DEVEL("rng_generate failed.\n");
      return rc;
   }
   value_attr     = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + 48 );
   value_len_attr = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_ULONG) );
   key_type_attr  = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_KEY_TYPE) );
   class_attr     = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_OBJECT_CLASS) );
   local_attr     = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_BBOOL) );
   derive_attr    = (CK_ATTRIBUTE *)malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_BBOOL) );

   if (!value_attr || !value_len_attr || !key_type_attr ||
       !class_attr || !local_attr || !derive_attr) {
      if (value_attr)     free( value_attr );
      if (value_len_attr) free( value_len_attr );
      if (key_type_attr)  free( key_type_attr );
      if (class_attr)     free( class_attr );
      if (local_attr)     free( local_attr );
      if (derive_attr)    free( derive_attr );

      TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
      return CKR_HOST_MEMORY;
   }

   version = (CK_VERSION *)mech->pParameter;
   key[0] = version->major;
   key[1] = version->minor;

   value_attr->type         = CKA_VALUE;
   value_attr->ulValueLen   = 48;
   value_attr->pValue       = (CK_BYTE *)value_attr + sizeof(CK_ATTRIBUTE);
   memcpy( value_attr->pValue, key, 48 );

   value_len_attr->type         = CKA_VALUE_LEN;
   value_len_attr->ulValueLen   = sizeof(CK_ULONG);
   value_len_attr->pValue       = (CK_BYTE *)value_len_attr + sizeof(CK_ATTRIBUTE);
   *(CK_ULONG *)value_len_attr->pValue = 48;

   key_type_attr->type         = CKA_KEY_TYPE;
   key_type_attr->ulValueLen   = sizeof(CK_KEY_TYPE);
   key_type_attr->pValue       = (CK_BYTE *)key_type_attr + sizeof(CK_ATTRIBUTE);
   *(CK_ATTRIBUTE_TYPE *)key_type_attr = CKK_GENERIC_SECRET;

   class_attr->type         = CKA_CLASS;
   class_attr->ulValueLen   = sizeof(CK_OBJECT_CLASS);
   class_attr->pValue       = (CK_BYTE *)class_attr + sizeof(CK_ATTRIBUTE);
   *(CK_OBJECT_CLASS *)class_attr->pValue = CKO_SECRET_KEY;

   local_attr->type         = CKA_LOCAL;
   local_attr->ulValueLen   = sizeof(CK_BBOOL);
   local_attr->pValue       = (CK_BYTE *)local_attr + sizeof(CK_ATTRIBUTE);
   *(CK_BBOOL *)local_attr->pValue = TRUE;

   derive_attr->type         = CKA_DERIVE;
   derive_attr->ulValueLen   = sizeof(CK_BBOOL);
   derive_attr->pValue       = (CK_BYTE *)derive_attr + sizeof(CK_ATTRIBUTE);
   *(CK_BBOOL *)derive_attr->pValue = TRUE;

   template_update_attribute( tmpl, value_attr );
   template_update_attribute( tmpl, value_len_attr );
   template_update_attribute( tmpl, key_type_attr );
   template_update_attribute( tmpl, class_attr );
   template_update_attribute( tmpl, local_attr );
   template_update_attribute( tmpl, derive_attr );

   return CKR_OK;
}


//
//
static CK_RV
ssl3_sha_then_md5( STDLL_TokData_t * tokdata,
		   SESSION   * sess,
                   CK_BYTE   * secret,
                   CK_BYTE   * firstRandom,
                   CK_ULONG    firstRandomLen,
                   CK_BYTE   * secondRandom,
                   CK_ULONG    secondRandomLen,
                   CK_BYTE   * variableData,
                   CK_ULONG    variableDataLen,
                   CK_BYTE   * outBuff )
{
   DIGEST_CONTEXT digest_ctx;
   CK_MECHANISM   digest_mech;
   CK_BYTE        hash[SHA1_HASH_SIZE];
   CK_ULONG       len;
   CK_RV          rc;

   // SHA(variableData + secret + firstRandom + secondRandom)
   //
   memset( &digest_ctx, 0x0, sizeof(DIGEST_CONTEXT) );
   digest_mech.mechanism      = CKM_SHA_1;
   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;

   rc = digest_mgr_init( tokdata, sess, &digest_ctx, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess,
                                  &digest_ctx,
                                  variableData,
                                  variableDataLen );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, secret, 48 );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess,
                                  &digest_ctx,
                                  firstRandom,
                                  firstRandomLen );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess,
                                  &digest_ctx,
                                  secondRandom,
                                  secondRandomLen );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   len = sizeof(hash);
   rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash, &len);
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      return rc;
   }

   // MD5(secret + SHA(...))
   //
   memset( &digest_ctx, 0x0, sizeof(DIGEST_CONTEXT) );
   digest_mech.mechanism      = CKM_MD5;
   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;

   rc = digest_mgr_init( tokdata, sess, &digest_ctx, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, secret, 48 );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess, &digest_ctx, hash, len );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   len = sizeof(hash);
   rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash, &len);

   if (rc == CKR_OK){
      memcpy( outBuff, hash, len );
   }
   else
      TRACE_DEVEL("Digest Final failed.\n");

   return rc;
}

//
//
static CK_RV
ssl3_md5_only( STDLL_TokData_t * tokdata,
	       SESSION   * sess,
               CK_BYTE   * firstString,
               CK_ULONG    firstStringLen,
               CK_BYTE   * secondString,
               CK_ULONG    secondStringLen,
               CK_BYTE   * thirdString,
               CK_ULONG    thirdStringLen,
               CK_BYTE   * outBuff )
{
   DIGEST_CONTEXT digest_ctx;
   CK_MECHANISM   digest_mech;
   CK_BYTE        hash[MD5_HASH_SIZE];
   CK_ULONG       len;
   CK_RV          rc;

   // If firstString is not NULL,
   //
   // MD5(firstString + secondString + thirdString)
   //
   // If firstString is NULL
   //
   // MD5(secondString + thirdString)
   //
   memset( &digest_ctx, 0x0, sizeof(DIGEST_CONTEXT) );
   digest_mech.mechanism      = CKM_MD5;
   digest_mech.ulParameterLen = 0;
   digest_mech.pParameter     = NULL;

   rc = digest_mgr_init( tokdata, sess, &digest_ctx, &digest_mech );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Init failed.\n");
      return rc;
   }
   if (firstString != NULL) {
      rc = digest_mgr_digest_update( tokdata, sess,
                                     &digest_ctx,
                                     firstString,
                                     firstStringLen );
      if (rc != CKR_OK){
         TRACE_DEVEL("Digest Update failed.\n");
         return rc;
      }
   }

   rc = digest_mgr_digest_update( tokdata, sess,
                                  &digest_ctx,
                                  secondString,
                                  secondStringLen );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   rc = digest_mgr_digest_update( tokdata, sess,
                                  &digest_ctx,
                                  thirdString,
                                  thirdStringLen );
   if (rc != CKR_OK){
      TRACE_DEVEL("Digest Update failed.\n");
      return rc;
   }
   len = sizeof(hash);
   rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash, &len);

   if (rc == CKR_OK){
      TRACE_DEVEL("Digest Final failed.\n");
      memcpy( outBuff, hash, len );
   }

   return rc;
}

//
//
CK_RV
ssl3_master_key_derive( STDLL_TokData_t   * tokdata,
			SESSION           * sess,
                        CK_MECHANISM      * mech,
                        CK_OBJECT_HANDLE    base_key,
                        CK_ATTRIBUTE      * pTemplate,
                        CK_ULONG            ulCount,
                        CK_OBJECT_HANDLE  * handle )
{
   OBJECT        * derived_key_obj  = NULL;
   OBJECT        * base_key_obj     = NULL;
   CK_ATTRIBUTE  * attr             = NULL;
   CK_ATTRIBUTE  * value_attr       = NULL;
   CK_ATTRIBUTE  * value_len_attr   = NULL;
   CK_ATTRIBUTE  * always_sens_attr = NULL;
   CK_ATTRIBUTE  * extract_attr     = NULL;
   CK_BYTE       * base_key_value   = NULL;
   CK_BYTE         key_data[48];
   CK_ULONG        i, base_key_len;
   CK_BBOOL        flag;
   CK_RV           rc;

   CK_SSL3_MASTER_KEY_DERIVE_PARAMS  * params      = NULL;
   CK_SSL3_RANDOM_DATA               * random_data = NULL;


   if (!sess || !mech){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *)mech->pParameter;

   rc = object_mgr_find_in_map1( tokdata, base_key, &base_key_obj );
   if (rc != CKR_OK){
      TRACE_ERROR("Failed to acquire key from specified handle");
      if (rc == CKR_OBJECT_HANDLE_INVALID)
	 return CKR_KEY_HANDLE_INVALID;
      else
	 return rc;
   }
   rc = template_attribute_find( base_key_obj->template, CKA_VALUE, &attr );
   if (rc == FALSE){
      TRACE_ERROR("Could not find <the_attribute_name> in the template\n");
      return CKR_FUNCTION_FAILED;
   }
   else {
      base_key_len   = attr->ulValueLen;
      base_key_value = attr->pValue;

      if (base_key_len != 48){
	 TRACE_ERROR("The base key's length is not 48.\n");
         return CKR_KEY_FUNCTION_NOT_PERMITTED;
      }
   }


   // this mechanism implies the following attributes:
   //    CKA_CLASS     : CKO_SECRET_KEY
   //    CKA_KEY_TYPE  : CKK_GENERIC_SECRET
   //    CKA_VALUE_LEN : 48
   // but we need to make sure the caller didn't specify any
   // wacky values.  it would have been better if Cryptoki had forbidden
   // these attributes from appearing in the template
   //
   for (i=0, attr = pTemplate; i < ulCount; i++, attr++) {
      CK_OBJECT_CLASS  class;
      CK_KEY_TYPE      keytype;
      CK_ULONG         value_len;

      if (attr->type == CKA_CLASS) {
         class = *(CK_OBJECT_CLASS *)attr->pValue;
         if (class != CKO_SECRET_KEY){
	    TRACE_ERROR("This operation requires a secret key.\n");
	    return CKR_KEY_FUNCTION_NOT_PERMITTED;
         }
      }
      else if (attr->type == CKA_KEY_TYPE) {
         keytype = *(CK_KEY_TYPE *)attr->pValue;
         if (keytype != CKK_GENERIC_SECRET){
	    TRACE_ERROR("%s\n", ock_err(ERR_KEY_TYPE_INCONSISTENT));
            return CKR_KEY_TYPE_INCONSISTENT;
         }
      }
      else if (attr->type == CKA_VALUE_LEN) {
         value_len = *(CK_ULONG *)attr->pValue;
         if (value_len != 48){
	    TRACE_ERROR("The derived key's length is not 48.\n");
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }
   }

   memset( key_data, 0x0, sizeof(key_data) );

   random_data = (CK_SSL3_RANDOM_DATA *)(&params->RandomInfo);

   // derive the master key data
   //
   rc = ssl3_sha_then_md5( tokdata, sess,
                           base_key_value,
                           random_data->pClientRandom,
                           random_data->ulClientRandomLen,
                           random_data->pServerRandom,
                           random_data->ulServerRandomLen,
                           (unsigned char *)"A",
                           1,
                           key_data );

   if (rc != CKR_OK){
      TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
      goto error;
   }
   rc = ssl3_sha_then_md5( tokdata, sess,
                           base_key_value,
                           random_data->pClientRandom,
                           random_data->ulClientRandomLen,
                           random_data->pServerRandom,
                           random_data->ulServerRandomLen,
                           (unsigned char *)"BB",
                           2,
                           &key_data[16] );
   if (rc != CKR_OK){
      TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
      goto error;
   }
   rc = ssl3_sha_then_md5( tokdata, sess,
                           base_key_value,
                           random_data->pClientRandom,
                           random_data->ulClientRandomLen,
                           random_data->pServerRandom,
                           random_data->ulServerRandomLen,
                           (unsigned char *)"CCC",
                           3,
                           &key_data[32] );
   if (rc != CKR_OK){
      TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
      goto error;
   }
   // build the key skeleton
   //
   rc = object_mgr_create_skel( tokdata, sess,
                                pTemplate,       ulCount,
                                MODE_DERIVE,
                                CKO_SECRET_KEY,  CKK_GENERIC_SECRET,
                                &derived_key_obj );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
      goto error;
   }

   rc = build_attribute( CKA_VALUE, key_data, 48, &value_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
      goto error;
   }
   rc = build_attribute( CKA_VALUE_LEN, (CK_BYTE *)&base_key_len, sizeof(CK_ULONG), &value_len_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
      goto error;
   }

   //
   // now, adjust the CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE
   // attributes based on the corresponding values from the base key
   //

   // if base key has ALWAYS_SENSITIVE = FALSE, then new key does too
   // otherwise, the value of CKA_ALWAYS_SENSITIVE = CKA_SENSITIVE
   //
   rc = template_attribute_find( base_key_obj->template, CKA_ALWAYS_SENSITIVE, &attr );
   if (rc == FALSE) {
      TRACE_ERROR("Could not find CKA_ALWAYS_SENSITIVE in the template\n");
      rc = CKR_FUNCTION_FAILED;
      goto error;
   }

   flag = *(CK_BBOOL *)attr->pValue;
   if (flag == TRUE) {
      rc = template_attribute_find( derived_key_obj->template, CKA_SENSITIVE, &attr );
      if (rc == FALSE) {
         TRACE_ERROR("Could not find CKA_SENSITIVE in the template\n");
         rc = CKR_FUNCTION_FAILED;
         goto error;
      }
      flag = *(CK_BBOOL *)attr->pValue;
   }

   rc = build_attribute( CKA_ALWAYS_SENSITIVE, &flag, sizeof(CK_BBOOL), &always_sens_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_ALWAYS_SENSITIVE attribute.\n");
      goto error;
   }

   // if base key has NEVER_EXTRACTABLE = FASE, the new key does too
   // otherwise, the value of CKA_NEVER_EXTRACTABLE = !CKA_EXTRACTABLE
   //
   rc = template_attribute_find( base_key_obj->template, CKA_NEVER_EXTRACTABLE, &attr );
   if (rc == FALSE) {
      TRACE_DEVEL("Failed to build CKA_NEVER_EXTRACTABLE attribute.\n");
      rc = CKR_FUNCTION_FAILED;
      goto error;
   }

   flag = *(CK_BBOOL *)attr->pValue;
   if (flag == TRUE) {
      rc = template_attribute_find( derived_key_obj->template, CKA_EXTRACTABLE, &attr );
      if (rc == FALSE) {
         TRACE_DEVEL("Failed to build CKA_EXTRACTABLE attribute.\n");
         rc = CKR_FUNCTION_FAILED;
         goto error;
      }

      flag = *(CK_BBOOL *)attr->pValue;

      flag = (~flag) & 0x1;
   }

   rc = build_attribute( CKA_NEVER_EXTRACTABLE, &flag, sizeof(CK_BBOOL), &extract_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_NEVER_EXTRACTABLE attribute.\n");
      goto error;
   }
   template_update_attribute( derived_key_obj->template, value_attr );
   template_update_attribute( derived_key_obj->template, value_len_attr );
   template_update_attribute( derived_key_obj->template, always_sens_attr );
   template_update_attribute( derived_key_obj->template, extract_attr );

   // at this point, the derived key is fully constructed...assign an
   // object handle and store the key
   //
   rc = object_mgr_create_final( tokdata, sess, derived_key_obj, handle );
   if (rc != CKR_OK) {
      TRACE_DEVEL("Object Mgr create final failed.\n");
      object_free( derived_key_obj );
      return rc;  // do NOT goto error
   }

   // should we destroy the base key?  SSL3 says yes but that might
   // occur in a separate call to C_DestroyObject
   //

   return CKR_OK;

error:
   if (value_attr)        free( value_attr );
   if (value_len_attr)    free( value_len_attr );
   if (always_sens_attr)  free( always_sens_attr );
   if (extract_attr)      free( extract_attr );

   return rc;
}


//
//
CK_RV
ssl3_key_and_mac_derive( STDLL_TokData_t   * tokdata,
			 SESSION           * sess,
                         CK_MECHANISM      * mech,
                         CK_OBJECT_HANDLE    base_key,
                         CK_ATTRIBUTE      * pTemplate,
                         CK_ULONG            ulCount )
{
   OBJECT       * base_key_obj           = NULL;
   CK_ATTRIBUTE * attr                   = NULL;

   CK_BYTE     * client_MAC_key_value   = NULL;
   CK_BYTE     * server_MAC_key_value   = NULL;
   CK_BYTE     * client_write_key_value = NULL;
   CK_BYTE     * server_write_key_value = NULL;
   CK_BYTE     * client_IV              = NULL;
   CK_BYTE     * server_IV              = NULL;
   CK_KEY_TYPE   keytype                = 0xFFFFFFFF;
   CK_BYTE       variable_data[26];
   CK_BYTE       key_block[(16*26) + (4*16)];
   CK_ULONG      i, key_material_loop_count;
   CK_ULONG      iv_len = 0, MAC_len, write_len;
   CK_RV         rc;

   CK_BYTE     * base_key_value       = NULL;
   CK_BBOOL      base_sensitive;
   CK_BBOOL      base_always_sensitive;
   CK_BBOOL      base_extractable;
   CK_BBOOL      base_never_extractable;

   CK_OBJECT_HANDLE  client_MAC_handle   = 0;
   CK_OBJECT_HANDLE  server_MAC_handle   = 0;
   CK_OBJECT_HANDLE  client_write_handle = 0;
   CK_OBJECT_HANDLE  server_write_handle = 0;

   CK_SSL3_KEY_MAT_PARAMS  * params = NULL;

   ATTRIBUTE_PARSE_LIST  base_attrs[] = {
      {CKA_SENSITIVE        , &base_sensitive        , sizeof(CK_BBOOL), FALSE},
      {CKA_EXTRACTABLE      , &base_extractable      , sizeof(CK_BBOOL), FALSE},
      {CKA_ALWAYS_SENSITIVE , &base_always_sensitive , sizeof(CK_BBOOL), FALSE},
      {CKA_NEVER_EXTRACTABLE, &base_never_extractable, sizeof(CK_BBOOL), FALSE},
   };


   if (!sess || !mech){
      TRACE_ERROR("%s received bad argument(s)\n", __FUNCTION__);
      return CKR_FUNCTION_FAILED;
   }
   params = (CK_SSL3_KEY_MAT_PARAMS *)mech->pParameter;

   rc = object_mgr_find_in_map1( tokdata, base_key, &base_key_obj );
   if (rc != CKR_OK){
      TRACE_ERROR("Failed to acquire key from specified handle");
      if (rc == CKR_OBJECT_HANDLE_INVALID)
	  return CKR_KEY_HANDLE_INVALID;
      else
	  return rc;
   }
   rc = template_attribute_find( base_key_obj->template, CKA_VALUE, &attr );
   if (rc == FALSE){
      TRACE_ERROR("Could not find CKA_VALUE in the template\n");
      return CKR_FUNCTION_FAILED;
   }
   else
      base_key_value = attr->pValue;

   template_attribute_find_multiple( base_key_obj->template, base_attrs, 4 );

   for (i=0; i < 4; i++) {
      if (base_attrs[i].found == FALSE){
         TRACE_ERROR("Could not find attribute in the template\n");
         return CKR_FUNCTION_FAILED;
      }
   }

   // The SSL3 spec says the IVs are 16 bytes long in the exportable case.
   // For now, we'll barf if someone asks for an exportable output and asks
   // for more than 128 bits of IV...
   //
   if (params->bIsExport      != FALSE &&
       params->ulIVSizeInBits >  128){
      TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
      return CKR_MECHANISM_PARAM_INVALID;
   }
   // the template must specify the key type for the client and server keys
   //
   // also, CKA_SENSITIVE, CKA_ALWAYS_SENSITIVE, CKA_EXTRACTABLE and
   // CKA_NEVER_EXTRACTABLE, if present, are not allowed to differ from
   // the base key.  We also check for stupid stuff.
   //
   for (i=0, attr = pTemplate; i < ulCount; i++, attr++) {
      CK_BBOOL tmp;

      if (attr->type == CKA_KEY_TYPE)
         keytype = *(CK_KEY_TYPE *)attr->pValue;

      else if (attr->type == CKA_SENSITIVE) {
         tmp = *(CK_BBOOL *)attr->pValue;
         if (tmp != base_sensitive){
            TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }

      else if (attr->type == CKA_ALWAYS_SENSITIVE) {
         tmp = *(CK_BBOOL *)attr->pValue;
         if (tmp != base_always_sensitive){
            TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }

      else if (attr->type == CKA_EXTRACTABLE) {
         tmp = *(CK_BBOOL *)attr->pValue;
         if (tmp != base_extractable){
            TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }

      else if (attr->type == CKA_NEVER_EXTRACTABLE) {
         tmp = *(CK_BBOOL *)attr->pValue;
         if (tmp != base_never_extractable){
            TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }

      else if (attr->type == CKA_CLASS) {
         CK_OBJECT_CLASS cl = *(CK_OBJECT_CLASS *)attr->pValue;
         if (cl != CKO_SECRET_KEY){
            TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
            return CKR_TEMPLATE_INCONSISTENT;
         }
      }
   }

   // a key type must be specified for the client and server write keys
   //
   if (keytype == 0xFFFFFFFF){
      TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
      return CKR_TEMPLATE_INCOMPLETE;
   }

   // figure out how much key material we need to generate
   //
   key_material_loop_count = 2 * ((params->ulMacSizeInBits + 7) / 8) +
                             2 * ((params->ulKeySizeInBits + 7) / 8);

   if (params->bIsExport == FALSE)
      key_material_loop_count += 2 * ((params->ulIVSizeInBits + 7) / 8);

   // we stop at 'ZZZZ....'  presumably this is enough for all cases?
   //
   if (key_material_loop_count > 26 * 16){
      TRACE_DEVEL("key_material_loop_count is too big.\n");
      return CKR_FUNCTION_FAILED;
   }
   key_material_loop_count = (key_material_loop_count + 15) / 16;

   // generate the key material
   //
   for (i=0; i < key_material_loop_count; i++) {
      memset( variable_data, ('A' + i), i+1 );

      rc = ssl3_sha_then_md5( tokdata, sess,
                              base_key_value,
                              params->RandomInfo.pServerRandom,
                              params->RandomInfo.ulServerRandomLen,
                              params->RandomInfo.pClientRandom,
                              params->RandomInfo.ulClientRandomLen,
                              variable_data,
                              i+1,
                              &(key_block[i*16]) );
      if (rc != CKR_OK){
         TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
         goto error;
      }
   }

   // Break key material into pieces
   //
   MAC_len   = (params->ulMacSizeInBits + 7) / 8;
   write_len = (params->ulKeySizeInBits + 7) / 8;  // check this

   client_MAC_key_value = key_block;
   server_MAC_key_value = client_MAC_key_value + MAC_len;

   client_write_key_value = server_MAC_key_value + MAC_len;
   server_write_key_value = client_write_key_value + (params->ulKeySizeInBits + 7)/8;

   if (params->ulIVSizeInBits != 0) {
      iv_len    = (params->ulIVSizeInBits + 7) / 8;
      client_IV = server_write_key_value + write_len;
      server_IV = client_IV + iv_len;
   }

   // Exportable ciphers require additional processing
   //
   if (params->bIsExport == TRUE) {
      rc = ssl3_md5_only( tokdata, sess,
                          client_write_key_value,
                          (params->ulKeySizeInBits + 7)/8,
                          params->RandomInfo.pClientRandom,
                          params->RandomInfo.ulClientRandomLen,
                          params->RandomInfo.pServerRandom,
                          params->RandomInfo.ulServerRandomLen,
                          &(key_block[16*26]) );
      if (rc != CKR_OK){
	 TRACE_DEVEL("ssl3_md5_only failed.\n");
         goto error;
      }
      client_write_key_value = &(key_block[16*26]);

      rc = ssl3_md5_only( tokdata, sess,
                          server_write_key_value,
                          (params->ulKeySizeInBits + 7)/8,
                          params->RandomInfo.pServerRandom,
                          params->RandomInfo.ulServerRandomLen,
                          params->RandomInfo.pClientRandom,
                          params->RandomInfo.ulClientRandomLen,
                          &(key_block[16*26+16]) );
      if (rc != CKR_OK){
	 TRACE_DEVEL("ssl3_md5_only failed.\n");
         goto error;
      }
      server_write_key_value = &(key_block[16*26+16]);

      if (params->ulIVSizeInBits != 0)
      {
         rc = ssl3_md5_only( tokdata, sess,
                             NULL,
                             0,
                             params->RandomInfo.pClientRandom,
                             params->RandomInfo.ulClientRandomLen,
                             params->RandomInfo.pServerRandom,
                             params->RandomInfo.ulServerRandomLen,
                             &(key_block[16*26+2*16]) );
         if (rc != CKR_OK){
	    TRACE_DEVEL("ssl3_md5_only failed.\n");
            goto error;
         }
         client_IV = &(key_block[16*26+2*16]);

         rc = ssl3_md5_only( tokdata, sess,
                             NULL,
                             0,
                             params->RandomInfo.pServerRandom,
                             params->RandomInfo.ulServerRandomLen,
                             params->RandomInfo.pClientRandom,
                             params->RandomInfo.ulClientRandomLen,
                             &(key_block[16*26+3*16]) );
         if (rc != CKR_OK){
	    TRACE_DEVEL("ssl3_md5_only failed.\n");
            goto error;
         }
         server_IV = &(key_block[16*26+3*16]);
      }
   }


   rc = ssl3_kmd_process_mac_keys( tokdata, sess, pTemplate, ulCount,
                                   &client_MAC_handle, client_MAC_key_value,
                                   &server_MAC_handle, server_MAC_key_value,
                                   MAC_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("ssl3_kmd_process_mac_keys failed.\n");
      goto error;
   }

   rc = ssl3_kmd_process_write_keys( tokdata, sess, pTemplate, ulCount, keytype,
                                     &client_write_handle, client_write_key_value,
                                     &server_write_handle, server_write_key_value,
                                     write_len );
   if (rc != CKR_OK){
      TRACE_DEVEL("ssl3_kmd_process_write_keys failed.\n");
      goto error;
   }

   params->pReturnedKeyMaterial->hClientMacSecret = client_MAC_handle;
   params->pReturnedKeyMaterial->hServerMacSecret = server_MAC_handle;
   params->pReturnedKeyMaterial->hClientKey       = client_write_handle;
   params->pReturnedKeyMaterial->hServerKey       = server_write_handle;

   if (params->ulIVSizeInBits != 0) {
      if (params->pReturnedKeyMaterial->pIVClient)
         memcpy( params->pReturnedKeyMaterial->pIVClient, client_IV, iv_len );

      if (params->pReturnedKeyMaterial->pIVServer)
         memcpy( params->pReturnedKeyMaterial->pIVServer, server_IV, iv_len );

#if 0
      CK_BYTE *p1, *p2;

      p1 = (CK_BYTE *)malloc( iv_len );
      p2 = (CK_BYTE *)malloc( iv_len );

      if (!p1 || !p2) {
         rc = CKR_HOST_MEMORY;
         goto error;
      }

      memcpy( p1, client_IV, iv_len );
      memcpy( p2, server_IV, iv_len );

      params->pReturnedKeyMaterial->pIVClient = p1;
      params->pReturnedKeyMaterial->pIVServer = p2;
#endif
   }

error:
   return rc;
}


CK_RV
ssl3_kmd_process_mac_keys( STDLL_TokData_t   * tokdata,
			   SESSION           * sess,
                           CK_ATTRIBUTE      * pTemplate,
                           CK_ULONG            ulCount,
                           CK_OBJECT_HANDLE  * client_handle,
                           CK_BYTE           * client_value,
                           CK_OBJECT_HANDLE  * server_handle,
                           CK_BYTE           * server_value,
                           CK_ULONG            mac_len )
{
   OBJECT         * client_obj          = NULL;
   OBJECT         * server_obj          = NULL;
   CK_ATTRIBUTE   * client_val_attr     = NULL;
   CK_ATTRIBUTE   * client_val_len_attr = NULL;
   CK_ATTRIBUTE   * server_val_attr     = NULL;
   CK_ATTRIBUTE   * server_val_len_attr = NULL;
   CK_ATTRIBUTE   * attr                = NULL;
   CK_ATTRIBUTE   * new_attrs           = NULL;
   CK_ULONG      i, cnt;
   CK_ULONG      true_vals[]  = { CKA_SIGN, CKA_VERIFY, CKA_DERIVE };
   CK_ULONG      false_vals[] = { CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP };
   CK_RV         rc = 0;


   // for the MAC keys, we want the following default values:
   //    CKA_SIGN, CKA_VERIFY, CKA_DERIVE = TRUE
   //    CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP = FALSE
   //
   // attributes are added in sequential order so we stick the defaults
   // at the beginning so that they may be overridden by caller-specified
   // values.
   //
   new_attrs = (CK_ATTRIBUTE *)malloc((ulCount + 7) * (sizeof(CK_ATTRIBUTE)));
   if (!new_attrs)
      goto error;

   // we have to treat these attributes a bit differently.  normally, we allocate
   // the CK_ATTRIBUTE and the value with a single malloc and just point the pValue
   // member to the extra space.  we can't do that here because we have to "emulate"
   // the way attributes are passed in from the cryptoki application...as an array
   // of CK_ATTRIBUTEs with no extra space (that is, pValue must be allocated
   // separately).
   //
   attr = new_attrs;
   for (i=0; i < sizeof(true_vals) / sizeof(CK_ULONG); i++, attr++) {
      attr->type         = true_vals[i];
      attr->ulValueLen   = sizeof(CK_BBOOL);
      attr->pValue       = (CK_BBOOL *)malloc(sizeof(CK_BBOOL));
      if (!attr->pValue) {
         rc = CKR_HOST_MEMORY;
	 TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
         goto error;
      }
      *(CK_BBOOL *)attr->pValue = TRUE;
   }

   for (i=0; i < sizeof(false_vals) / sizeof(CK_ULONG); i++, attr++) {
      attr->type        = false_vals[i];
      attr->ulValueLen  = sizeof(CK_BBOOL);
      attr->pValue       = (CK_BBOOL *)malloc(sizeof(CK_BBOOL));
      if (!attr->pValue) {
         rc = CKR_HOST_MEMORY;
	 TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
         goto error;
      }
      *(CK_BBOOL *)attr->pValue = FALSE;
   }

   for (i=0, cnt=0; i < ulCount; i++) {
      if (pTemplate[i].type != CKA_KEY_TYPE &&
          pTemplate[i].type != CKA_VALUE    &&
          pTemplate[i].type != CKA_VALUE_LEN)
      {
         attr->type       = pTemplate[i].type;
         attr->ulValueLen = pTemplate[i].ulValueLen;
         attr->pValue     = (char *)malloc(attr->ulValueLen);
         if (!attr->pValue) {
            rc = CKR_HOST_MEMORY;
	    TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto error;
         }
         memcpy( attr->pValue, pTemplate[i].pValue, attr->ulValueLen );
         cnt++;
         attr++;
      }
   }

   ulCount = 7 + cnt;

   // create the key skeletons
   //
   rc = object_mgr_create_skel( tokdata, sess,
                                new_attrs,     ulCount,
                                MODE_DERIVE,
                                CKO_SECRET_KEY,
                                CKK_GENERIC_SECRET,
                                &client_obj );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
      goto error;
   }
   rc = object_mgr_create_skel( tokdata, sess,
                                new_attrs,    ulCount,
                                MODE_DERIVE,
                                CKO_SECRET_KEY,
                                CKK_GENERIC_SECRET,
                                &server_obj );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
      goto error;
   }
   for (i=0; i < ulCount; i++)
      if (new_attrs[i].pValue)  free( new_attrs[i].pValue );

   free( new_attrs );
   new_attrs = NULL;

   rc = build_attribute( CKA_VALUE, client_value, mac_len, &client_val_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
      goto error;
   }
   rc = build_attribute( CKA_VALUE, server_value, mac_len, &server_val_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
      goto error;
   }
   rc = build_attribute( CKA_VALUE_LEN, (CK_BYTE *)&mac_len, sizeof(CK_ULONG), &client_val_len_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
      goto error;
   }
   rc = build_attribute( CKA_VALUE_LEN, (CK_BYTE *)&mac_len, sizeof(CK_ULONG), &server_val_len_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
      goto error;
   }
   template_update_attribute( client_obj->template, client_val_attr );
   template_update_attribute( client_obj->template, client_val_len_attr );

   template_update_attribute( server_obj->template, server_val_attr );
   template_update_attribute( server_obj->template, server_val_len_attr );

   rc = object_mgr_create_final( tokdata, sess, client_obj, client_handle );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Final failed.\n");
      goto error;
   }
   rc = object_mgr_create_final( tokdata, sess, server_obj, server_handle );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Final failed.\n");
      goto error;
   }

   return CKR_OK;

error:
   *client_handle = 0;
   *server_handle = 0;

   if (client_obj) {
      object_free( client_obj );
      client_val_attr     = NULL;   // these get freed with the object
      client_val_len_attr = NULL;
   }

   if (server_obj) {
      object_free( server_obj );
      server_val_attr     = NULL;   // these get freed with the object
      server_val_len_attr = NULL;
   }

   if (client_val_attr)      free( client_val_attr );
   if (client_val_len_attr)  free( client_val_len_attr );
   if (server_val_attr)      free( server_val_attr );
   if (server_val_len_attr)  free( server_val_len_attr );

   if (new_attrs) {
      for (i=0; i < ulCount; i++) {
         if (new_attrs[i].pValue)
            free( new_attrs[i].pValue );
      }

      free( new_attrs );
   }

   return rc;
}


CK_RV
ssl3_kmd_process_write_keys( STDLL_TokData_t   * tokdata,
			     SESSION           * sess,
                             CK_ATTRIBUTE      * pTemplate,
                             CK_ULONG            ulCount,
                             CK_KEY_TYPE         keytype,
                             CK_OBJECT_HANDLE  * client_handle,
                             CK_BYTE           * client_value,
                             CK_OBJECT_HANDLE  * server_handle,
                             CK_BYTE           * server_value,
                             CK_ULONG            write_len )
{
   CK_ATTRIBUTE   * client_val_attr     = NULL;
   CK_ATTRIBUTE   * client_val_len_attr = NULL;
   CK_ATTRIBUTE   * server_val_attr     = NULL;
   CK_ATTRIBUTE   * server_val_len_attr = NULL;
   CK_ATTRIBUTE   * new_attrs           = NULL;
   CK_ATTRIBUTE   * attr                = NULL;
   OBJECT         * client_obj          = NULL;
   OBJECT         * server_obj          = NULL;
   CK_ULONG         i, cnt;
   CK_ULONG         true_vals[]  = { CKA_ENCRYPT, CKA_DECRYPT, CKA_DERIVE };
   CK_ULONG         false_vals[] = { CKA_SIGN, CKA_VERIFY, CKA_WRAP, CKA_UNWRAP };
   CK_RV            rc = CKR_HOST_MEMORY;

   // for the write keys, we want the following default values:
   //    CKA_ENCRYPT, CKA_DECRYPT, CKA_DERIVE = TRUE
   //    CKA_SIGN, CKA_VERIFY, CKA_WRAP, CKA_UNWRAP = FALSE
   //
   // attributes are added in sequential order so we stick the defaults
   // at the beginning so that they may be overridden by caller-specified
   // values.
   //
   new_attrs = (CK_ATTRIBUTE *)malloc((ulCount + 7) * (sizeof(CK_ATTRIBUTE)));
   if (!new_attrs)
      goto error;

   // we have to treat these attributes a bit differently.  normally, we allocate
   // the CK_ATTRIBUTE and the value with a single malloc and just point the pValue
   // member to the extra space.  we can't do that here because we have to "emulate"
   // the way attributes are passed in from the cryptoki application...as an array
   // of CK_ATTRIBUTEs with no extra space (that is, pValue must be allocated
   // separately).
   //
   attr = new_attrs;
   for (i=0; i < sizeof(true_vals) / sizeof(CK_ULONG); i++, attr++) {
      attr->type         = true_vals[i];
      attr->ulValueLen   = sizeof(CK_BBOOL);
      attr->pValue       = (CK_BBOOL *)malloc(sizeof(CK_BBOOL));
      if (!attr->pValue) {
	 TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
         goto error;
      }
      *(CK_BBOOL *)attr->pValue = TRUE;
   }

   for (i=0; i < sizeof(false_vals) / sizeof(CK_ULONG); i++, attr++) {
      attr->type         = false_vals[i];
      attr->ulValueLen   = sizeof(CK_BBOOL);
      attr->pValue       = (CK_BBOOL *)malloc(sizeof(CK_BBOOL));
      if (!attr->pValue) {
	 TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
         goto error;
      }
      *(CK_BBOOL *)attr->pValue = FALSE;
   }

   for (i=0, cnt=0; i < ulCount; i++) {
      if (pTemplate[i].type != CKA_KEY_TYPE &&
          pTemplate[i].type != CKA_VALUE    &&
          pTemplate[i].type != CKA_VALUE_LEN)
      {
         attr->type       = pTemplate[i].type;
         attr->ulValueLen = pTemplate[i].ulValueLen;
         attr->pValue     = (char *)malloc(attr->ulValueLen);
         if (!attr->pValue) {
	    TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto error;
         }
         memcpy( attr->pValue, pTemplate[i].pValue, attr->ulValueLen );
         cnt++;
         attr++;
      }
   }

   ulCount = 7 + cnt;

   rc = object_mgr_create_skel( tokdata, sess,
                                new_attrs, ulCount,
                                MODE_DERIVE,
                                CKO_SECRET_KEY,
                                keytype,
                                &client_obj );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
      goto error;
   }
   rc = object_mgr_create_skel( tokdata, sess,
                                new_attrs, ulCount,
                                MODE_DERIVE,
                                CKO_SECRET_KEY,
                                keytype,
                                &server_obj );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
      goto error;
   }
   for (i=0; i < ulCount; i++) {
      if (new_attrs[i].pValue)
         free( new_attrs[i].pValue );
   }

   free( new_attrs );
   new_attrs = NULL;

   rc  = build_attribute( CKA_VALUE, client_value, write_len, &client_val_attr );
   rc |= build_attribute( CKA_VALUE, server_value, write_len, &server_val_attr );
   if (rc != CKR_OK){
      TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
      goto error;
   }
   switch (keytype) {
      case CKK_GENERIC_SECRET:
      case CKK_DES:
      case CKK_DES2:
      case CKK_DES3:
      case CKK_RC2:
      case CKK_RC4:
      case CKK_RC5:
      case CKK_CAST:
      case CKK_CAST3:
      case CKK_CAST5:
         {
            rc  = build_attribute( CKA_VALUE_LEN, (CK_BYTE *)&write_len, sizeof(CK_ULONG), &client_val_len_attr );
            rc |= build_attribute( CKA_VALUE_LEN, (CK_BYTE *)&write_len, sizeof(CK_ULONG), &server_val_len_attr );
            if (rc != CKR_OK){
	       TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
               goto error;
            }
            rc  = template_validate_attribute( tokdata, client_obj->template,
                                               client_val_len_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_DERIVE );
            rc |= template_validate_attribute( tokdata, server_obj->template,
                                               server_val_len_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_DERIVE );

            // for these I use MODE_CREATE because I want to validate the value/length.
            // no othe modes are allowed to mess wiht CKA_VALUE (see for instance,
            // des_validate_attribute())
            //
            rc |= template_validate_attribute( tokdata, client_obj->template,
                                               client_val_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_CREATE );
            rc |= template_validate_attribute( tokdata, server_obj->template,
                                               server_val_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_CREATE );
            if (rc != CKR_OK){
	       TRACE_DEVEL("template_validate_attribute failed.\n");
               goto error;
            }
            template_update_attribute( client_obj->template, client_val_attr );
            template_update_attribute( server_obj->template, server_val_attr );
            template_update_attribute( client_obj->template, client_val_len_attr );
            template_update_attribute( server_obj->template, server_val_len_attr );

            // the object owns the attributes now...
            //
            client_val_attr = NULL;
            server_val_attr = NULL;
            client_val_len_attr = NULL;
            server_val_len_attr = NULL;
         }
         break;

      default:
         {
            rc  = template_validate_attribute( tokdata, client_obj->template,
                                               client_val_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_CREATE );
            rc |= template_validate_attribute( tokdata, server_obj->template,
                                               server_val_attr,
                                               CKO_SECRET_KEY,
                                               keytype,
                                               MODE_CREATE );
            if (rc != CKR_OK){
	       TRACE_DEVEL("template_validate_attribute failed.\n");
               goto error;
            }
            template_update_attribute( client_obj->template, client_val_attr );
            template_update_attribute( server_obj->template, server_val_attr );

            // the object owns the attributes now...
            //
            client_val_attr = NULL;
            server_val_attr = NULL;
         }
   }


   // finally, assign a handle to each key
   //
   rc = object_mgr_create_final( tokdata, sess, client_obj, client_handle );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Final failed.\n");
      goto error;
   }
   rc = object_mgr_create_final( tokdata, sess, server_obj, server_handle );
   if (rc != CKR_OK){
      TRACE_DEVEL("Object Mgr Create Final failed.\n");
      goto error;
   }
   return CKR_OK;

error:
   *client_handle = 0;
   *server_handle = 0;

   if (client_obj)
      object_free( client_obj );

   if (server_obj)
      object_free( server_obj );

   // the only way these guys are non-NULL is if they were created but
   // not yet to added to an object
   //
   if (client_val_attr)      free( client_val_attr );
   if (client_val_len_attr)  free( client_val_len_attr );
   if (server_val_attr)      free( server_val_attr );
   if (server_val_len_attr)  free( server_val_len_attr );

   if (new_attrs) {
      for (i=0; i < ulCount; i++) {
         if (new_attrs[i].pValue)
            free( new_attrs[i].pValue );
      }

      free( new_attrs );
   }

   return rc;
}
