/*
             Common Public License Version 0.5

             THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF
             THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE,
             REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
             RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

             1. DEFINITIONS

             "Contribution" means: 
                   a) in the case of the initial Contributor, the
                   initial code and documentation distributed under
                   this Agreement, and 

                   b) in the case of each subsequent Contributor:
                   i) changes to the Program, and
                   ii) additions to the Program;

                   where such changes and/or additions to the Program
                   originate from and are distributed by that
                   particular Contributor. A Contribution 'originates'
                   from a Contributor if it was added to the Program
                   by such Contributor itself or anyone acting on such
                   Contributor's behalf. Contributions do not include
                   additions to the Program which: (i) are separate
                   modules of software distributed in conjunction with
                   the Program under their own license agreement, and
                   (ii) are not derivative works of the Program.


             "Contributor" means any person or entity that distributes
             the Program.

             "Licensed Patents " mean patent claims licensable by a
             Contributor which are necessarily infringed by the use or
             sale of its Contribution alone or when combined with the
             Program. 

             "Program" means the Contributions distributed in
             accordance with this Agreement.

             "Recipient" means anyone who receives the Program under
             this Agreement, including all Contributors.

             2. GRANT OF RIGHTS

                   a) Subject to the terms of this Agreement, each
                   Contributor hereby grants Recipient a
                   non-exclusive, worldwide, royalty-free copyright
                   license to reproduce, prepare derivative works of,
                   publicly display, publicly perform, distribute and
                   sublicense the Contribution of such Contributor, if
                   any, and such derivative works, in source code and
                   object code form.

                   b) Subject to the terms of this Agreement, each
                   Contributor hereby grants Recipient a
                   non-exclusive, worldwide, royalty-free patent
                   license under Licensed Patents to make, use, sell,
                   offer to sell, import and otherwise transfer the
                   Contribution of such Contributor, if any, in source
                   code and object code form. This patent license
                   shall apply to the combination of the Contribution
                   and the Program if, at the time the Contribution is
                   added by the Contributor, such addition of the
                   Contribution causes such combination to be covered
                   by the Licensed Patents. The patent license shall
                   not apply to any other combinations which include
                   the Contribution. No hardware per se is licensed
                   hereunder.

                   c) Recipient understands that although each
                   Contributor grants the licenses to its
                   Contributions set forth herein, no assurances are
                   provided by any Contributor that the Program does
                   not infringe the patent or other intellectual
                   property rights of any other entity. Each
                   Contributor disclaims any liability to Recipient
                   for claims brought by any other entity based on
                   infringement of intellectual property rights or
                   otherwise. As a condition to exercising the rights
                   and licenses granted hereunder, each Recipient
                   hereby assumes sole responsibility to secure any
                   other intellectual property rights needed, if any.

                   For example, if a third party patent license is
                   required to allow Recipient to distribute the
                   Program, it is Recipient's responsibility to
                   acquire that license before distributing the
                   Program.

                   d) Each Contributor represents that to its
                   knowledge it has sufficient copyright rights in its
                   Contribution, if any, to grant the copyright
                   license set forth in this Agreement.

             3. REQUIREMENTS

             A Contributor may choose to distribute the Program in
             object code form under its own license agreement, provided
             that:
                   a) it complies with the terms and conditions of
                   this Agreement; and

                   b) its license agreement:
                   i) effectively disclaims on behalf of all
                   Contributors all warranties and conditions, express
                   and implied, including warranties or conditions of
                   title and non-infringement, and implied warranties
                   or conditions of merchantability and fitness for a
                   particular purpose;

                   ii) effectively excludes on behalf of all
                   Contributors all liability for damages, including
                   direct, indirect, special, incidental and
                   consequential damages, such as lost profits;

                   iii) states that any provisions which differ from
                   this Agreement are offered by that Contributor
                   alone and not by any other party; and

                   iv) states that source code for the Program is
                   available from such Contributor, and informs
                   licensees how to obtain it in a reasonable manner
                   on or through a medium customarily used for
                   software exchange.

             When the Program is made available in source code form:
                   a) it must be made available under this Agreement;
                   and
                   b) a copy of this Agreement must be included with
                   each copy of the Program. 

             Contributors may not remove or alter any copyright notices
             contained within the Program.

             Each Contributor must identify itself as the originator of
             its Contribution, if any, in a manner that reasonably
             allows subsequent Recipients to identify the originator of
             the Contribution. 


             4. COMMERCIAL DISTRIBUTION

             Commercial distributors of software may accept certain
             responsibilities with respect to end users, business
             partners and the like. While this license is intended to
             facilitate the commercial use of the Program, the
             Contributor who includes the Program in a commercial
             product offering should do so in a manner which does not
             create potential liability for other Contributors.
             Therefore, if a Contributor includes the Program in a
             commercial product offering, such Contributor ("Commercial
             Contributor") hereby agrees to defend and indemnify every
             other Contributor ("Indemnified Contributor") against any
             losses, damages and costs (collectively "Losses") arising
             from claims, lawsuits and other legal actions brought by a
             third party against the Indemnified Contributor to the
             extent caused by the acts or omissions of such Commercial
             Contributor in connection with its distribution of the
             Program in a commercial product offering. The obligations
             in this section do not apply to any claims or Losses
             relating to any actual or alleged intellectual property
             infringement. In order to qualify, an Indemnified
             Contributor must: a) promptly notify the Commercial
             Contributor in writing of such claim, and b) allow the
             Commercial Contributor to control, and cooperate with the
             Commercial Contributor in, the defense and any related
             settlement negotiations. The Indemnified Contributor may
             participate in any such claim at its own expense.


             For example, a Contributor might include the Program in a
             commercial product offering, Product X. That Contributor
             is then a Commercial Contributor. If that Commercial
             Contributor then makes performance claims, or offers
             warranties related to Product X, those performance claims
             and warranties are such Commercial Contributor's
             responsibility alone. Under this section, the Commercial
             Contributor would have to defend claims against the other
             Contributors related to those performance claims and
             warranties, and if a court requires any other Contributor
             to pay any damages as a result, the Commercial Contributor
             must pay those damages.


             5. NO WARRANTY

             EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE
             PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
             WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
             IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
             CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
             FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
             responsible for determining the appropriateness of using
             and distributing the Program and assumes all risks
             associated with its exercise of rights under this
             Agreement, including but not limited to the risks and
             costs of program errors, compliance with applicable laws,
             damage to or loss of data, programs or equipment, and
             unavailability or interruption of operations. 

             6. DISCLAIMER OF LIABILITY
             EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER
             RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY
             FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
             OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
             LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
             LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
             OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE
             OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
             POSSIBILITY OF SUCH DAMAGES.

             7. GENERAL

             If any provision of this Agreement is invalid or
             unenforceable under applicable law, it shall not affect
             the validity or enforceability of the remainder of the
             terms of this Agreement, and without further action by the
             parties hereto, such provision shall be reformed to the
             minimum extent necessary to make such provision valid and
             enforceable.


             If Recipient institutes patent litigation against a
             Contributor with respect to a patent applicable to
             software (including a cross-claim or counterclaim in a
             lawsuit), then any patent licenses granted by that
             Contributor to such Recipient under this Agreement shall
             terminate as of the date such litigation is filed. In
             addition, If Recipient institutes patent litigation
             against any entity (including a cross-claim or
             counterclaim in a lawsuit) alleging that the Program
             itself (excluding combinations of the Program with other
             software or hardware) infringes such Recipient's
             patent(s), then such Recipient's rights granted under
             Section 2(b) shall terminate as of the date such
             litigation is filed.

             All Recipient's rights under this Agreement shall
             terminate if it fails to comply with any of the material
             terms or conditions of this Agreement and does not cure
             such failure in a reasonable period of time after becoming
             aware of such noncompliance. If all Recipient's rights
             under this Agreement terminate, Recipient agrees to cease
             use and distribution of the Program as soon as reasonably
             practicable. However, Recipient's obligations under this
             Agreement and any licenses granted by Recipient relating
             to the Program shall continue and survive. 

             Everyone is permitted to copy and distribute copies of
             this Agreement, but in order to avoid inconsistency the
             Agreement is copyrighted and may only be modified in the
             following manner. The Agreement Steward reserves the right
             to publish new versions (including revisions) of this
             Agreement from time to time. No one other than the
             Agreement Steward has the right to modify this Agreement.

             IBM is the initial Agreement Steward. IBM may assign the
             responsibility to serve as the Agreement Steward to a
             suitable separate entity. Each new version of the
             Agreement will be given a distinguishing version number.
             The Program (including Contributions) may always be
             distributed subject to the version of the Agreement under
             which it was received. In addition, after a new version of
             the Agreement is published, Contributor may elect to
             distribute the Program (including its Contributions) under
             the new version. Except as expressly stated in Sections
             2(a) and 2(b) above, Recipient receives no rights or
             licenses to the intellectual property of any Contributor
             under this Agreement, whether expressly, by implication,
             estoppel or otherwise. All rights in the Program not
             expressly granted under this Agreement are reserved.


             This Agreement is governed by the laws of the State of New
             York and the intellectual property laws of the United
             States of America. No party to this Agreement will bring a
             legal action under this Agreement more than one year after
             the cause of action arose. Each party waives its rights to
             a jury trial in any resulting litigation. 



*/

/* (C) COPYRIGHT International Business Machines Corp. 2001,2002          */

 
/***************************************************************************
                          Change Log
                          ==========
       4/25/03    Kapil Sood (kapil@corrent.com)
                  Added DH key pair generation and DH shared key derivation
                  functions.
 
 
 
****************************************************************************/

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <strings.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>

#include "pkcs11types.h"
#include "stdll.h"

#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "tok_spec_struct.h"
#include "pkcs32.h"

#include "../api/apiproto.h"

#define UCHAR  unsigned char

/* Declared in obj_mgr.c */
extern pthread_rwlock_t obj_list_rw_mutex;

char *pk_dir;
void SC_SetFunctionList(void);

#define SESSION_MGR_FIND(x) session_mgr_find(x) /* All these need to
						 * get the lock */

/* Maximum number of supported devices (rather arbitrary) */
#define PKW_MAX_DEVICES                10

// Netscape/SSL is fairly timing-sensitive so can't always use a debugger
//
// If the CRYPTOKI_DEBUG environment variable is defined, information
// about each successful PKCS#11 call made is written to the file named
// in that environment variable.
//
// If the CRYPTOKI_PROFILE environment variable is defined, information
// about the amount of time spent in each PKCS#11 API during a "run"
//
// If the CRYPTOKI_DEBUG environment variable is defined, information
// about each successful PKCS#11 call made is written to the file named
// in that environment variable.
//
// If the CRYPTOKI_PROFILE environment variable is defined, information
// about the amount of time spent in each PKCS#11 API during a "run"
// (i.e., from _DLL_InitTerm init call to _DLL_InitTerm term call) is
// appended to the file named in that environment variable.
//
// If the CRYPTOKI_STATS_FILE environment variable is defined, information
// about various internal metrics at the end of a "run" is appended to
// the file named in that environment variable.  The CRYPTOKI_STATS
// environment variable specifies the argument(s) that are passed to
// the function that returns the metrics to specify which metric(s) are
// returned.
//
#define MAXFILENAME 1024

static char *debugfilepathbuffer;
static int debugon = 1;
int  debugfile = 0;
#define FFLUSH(x) 

pid_t  initedpid=0;  // for initialized pid

CK_ULONG  usage_count = 0; // variable for number of times the DLL has
			   // been used.

CK_C_INITIALIZE_ARGS cinit_args = { NULL, NULL, NULL, NULL, 0, NULL };

extern void stlogterm();
extern void stloginit();
extern void stlogit2(int type,char *fmt, ...);

CK_BBOOL
st_Initialized()
{
	if (initialized == FALSE ) return FALSE;
	return TRUE;
}

#ifdef SPINXPL
extern int spinxplfd;
extern int spin_created;
#endif

void
Fork_Initializer(void)
{
	stlogterm();
        stloginit(); // Initialize Logging so we can capture
		     // EVERYTHING

#ifdef SPINXPL
	spinxplfd = -1;
	spin_created = 0;
#endif

	// Force logout.  This cleans out the private session and list
	// and cleans out the private object map
	session_mgr_logout_all();

	// Clean out the public object map
	// First parm is no longer used..
	object_mgr_purge_map((SESSION *)0xFFFF, PUBLIC);
	object_mgr_purge_map((SESSION *)0xFFFF, PRIVATE);

	// This should clear the entire session list out
	session_mgr_close_all_sessions();

	next_session_handle = 1; // Make is so sessions start with 1
	next_object_handle = 1;

	// Clean out the global login state variable
	// When implemented...  Although logout_all should clear this up.

	// Once the object_map is flushed, the obj_lists (public and
	// private) are both just linked lists that have to be freed
	// up...  //logit("%s:%d: tokenobj publ 0x%08x priv
	// 0x%08x",__FILE__,__LINE__,publ_token_obj_list,
	// priv_token_obj_list);
	while (priv_token_obj_list) {
		priv_token_obj_list = dlist_remove_node(priv_token_obj_list,
							priv_token_obj_list);
	}

	while (publ_token_obj_list) {
		publ_token_obj_list = 
			dlist_remove_node(publ_token_obj_list,
					  publ_token_obj_list);
	}

	// Need to do something to prevent the shared memory from
	// having the objects loaded again.... The most likely place
	// is in the obj_mgr file where the object is added to shared
	// memory (object_mgr_add_to_shm) a query should be done to
	// the appropriate object list....
}

#ifdef ALLLOCK
#define LOCKIT   pthread_mutex_lock(&native_mutex)
#define LLOCK
#define UNLOCKIT   pthread_mutex_unlock(&native_mutex)
#else
#ifdef DEBLOCK
#define LOCKIT
#define LLOCK   pthread_mutex_lock(&native_mutex)
#define UNLOCKIT   pthread_mutex_unlock(&native_mutex)
#else
#define LOCKIT
#define LLOCK
#define UNLOCKIT
#endif
#endif

int
APISlot2Local(snum)
CK_SLOT_ID  snum;
{
	return(token_specific.t_slot2local(snum));
}


#define  SLT_CHECK  \
   CK_SLOT_ID     slot_id; \
   int            sid1; \
 \
   if ( (sid1 = APISlot2Local(sid)) != -1 ){ \
      slot_id = sid1; \
   } else { \
      return CKR_ARGUMENTS_BAD; \
   }


#define SESSION_HANDLE   sSession.sessionh
#define SLOTID    APISlot2Local(sSession.slotID)

#define SESS_SET \
   CK_SESSION_HANDLE  hSession; \
\
   hSession = sSession.sessionh;


// More efficient long reverse
inline CK_ULONG long_reverse(CK_ULONG x)
{
#ifdef _POWER   // Power Architecture requires reversal to talk to adapter
	return (
		((0x000000FF & x)<<24) |
		((0x0000FF00 & x)<<8) |
		((0x00FF0000 & x)>>8) |
		((0xFF000000 & x)>>24) );
#else
	return (x); // Others don't require  reversal.
#endif

}

// verify that the mech specified is in the
// mech list for this token... Common code requires this 
// to be added
CK_RV 
validate_mechanism(CK_MECHANISM_PTR  pMechanism)
{
	CK_ULONG i;   
	for (i=0; i< mech_list_len;i++){
		if (pMechanism->mechanism == mech_list[i].mech_type) {
			return CKR_OK;
		}
	}
	st_err_log(28, __FILE__, __LINE__);
	return CKR_MECHANISM_INVALID;
}

#define VALID_MECH(p) \
   if ( validate_mechanism(p) != CKR_OK){ \
      rc = CKR_MECHANISM_INVALID; \
      goto done; \
   } \

// Defines to allow NT code to work correctly
#define WaitForSingleObject(x,y)  pthread_mutex_lock(&(x))
#define ReleaseMutex(x)           pthread_mutex_unlock(&(x))

void
init_data_store(char *directory)
{
	char *pkdir;
	if ( (pkdir = getenv("PKCS_APP_STORE")) != NULL){
		pk_dir =  (char *) malloc(strlen(pkdir)+1024);
		memset(pk_dir, 0, strlen(pkdir)+1024);
		sprintf(pk_dir,"%s/%s",pkdir,SUB_DIR);
	} else {
		pk_dir  = (char *)malloc(strlen(directory)+25);
		memset(pk_dir, 0, strlen(directory)+25);
		sprintf(pk_dir,"%s",directory);

	}
}

/* In an STDLL this is called once for each card in the system
 * therefore the initialized only flags certain one time things
 * However in the case of a lightened accelerator, the cards are all
 * agregated together in a single token.  Therefore the correlator
 * should be a list of device names which have either the correct clu
 * or the crypt light adapter... */
CK_RV
ST_Initialize(void **FunctionList,
	      CK_SLOT_ID SlotNumber,
	      char *Correlator)
{
	int    i;
	CK_RV  rc = CKR_OK;
	struct passwd  *pw,*epw; // SAB XXX XXX
	uid_t    userid,euserid;

	stlogterm();
	stloginit();

	// Check for root user or Group PKCS#11 Membershp
	// Only these are allowed.
	userid = getuid();
	euserid = geteuid();

	if (userid != 0 && euserid != 0) { // Root or effective Root
					   // is ok
		struct group *grp;
		int   rc = 0;
		gid_t  gid,egid;
		grp = getgrnam("pkcs11");
		if (grp) {
			// Check for member of group..
			// SAB get login seems to not work with some
			// instances of application invocations
			// (particularly when forked).  So we need to
			// get the group informatiion.  Really need to
			// take the uid and map it to a name.
			pw = getpwuid(userid);
			epw = getpwuid(euserid);
			gid = getgid();
			egid = getegid();
         
			if ( gid == grp->gr_gid || egid == grp->gr_gid){
				rc = 1;
			} else {
				i = 0;
				while (grp->gr_mem[i]) {
					if (pw) {
						if (strncmp(pw->pw_name, 
							    grp->gr_mem[i],
							    strlen(pw->pw_name))
						    == 0 ) {
							rc = 1;
							break;
						}
					}
					if (epw) {
						if (strncmp(epw->pw_name,
							    grp->gr_mem[i],
							    strlen(epw->pw_name))
						    == 0 ){
							rc = 1;
							break;
						}
					}
					i++;
				}
			}
			if (rc == 0 ){
				st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
				return CKR_FUNCTION_FAILED;
			}
		} else {
			st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
			return CKR_FUNCTION_FAILED;
		}
	}

	// assume that the upper API prevents multiple calls of initialize
	// since that only happens on C_Initialize and that is the
	// resonsibility of the upper layer..
	initialized = FALSE; /// So the rest of the code works correctly

	// If we're not already initialized, grab the mutex and do the
	// initialization.  Check to see if another thread did so while we
	// were waiting...
	//
	// One of the things we do during initialization is create the mutex for
	// PKCS#11 operations; until we do so, we have to use the native mutex...
	//
	WaitForSingleObject( native_mutex, INFINITE );

	// SAB need to call Fork_Initializer here
	// instead of at the end of the loop...
	// it may also need to call destroy of the following 3 mutexes..
	// it may not matter...
	Fork_Initializer();


	MY_CreateMutex( &pkcs_mutex      );
	MY_CreateMutex( &obj_list_mutex  );
	if (pthread_rwlock_init(&obj_list_rw_mutex, NULL)) {
		st_err_log(145, __FILE__, __LINE__);
	}
	MY_CreateMutex( &sess_list_mutex );
	MY_CreateMutex( &login_mutex     );


	if ( (debugfilepathbuffer = getenv( "CRYPTOKI_DEBUG")) != NULL) {
		debugon=1;
	}

	init_data_store((char *)PK_DIR);


	// Handle global initialization issues first if we have not
	// been initialized.
	if (st_Initialized() == FALSE){
#if SYSVSEM
		xproclock = (void *)&xprocsemid;
		CreateXProcLock(xproclock);
#endif
		if ( (rc = attach_shm()) != CKR_OK) {
			st_err_log(144, __FILE__, __LINE__);
			goto done;
		}
      

		nv_token_data = &global_shm->nv_token_data;

		stloginit();

		initialized = TRUE;
		initedpid = getpid();
		SC_SetFunctionList();

		// Always call the token_specific_init function....
		rc =  token_specific.t_init(Correlator,SlotNumber);
		if (rc != 0) {   // Zero means success, right?!?
			*FunctionList = NULL;
			st_err_log(145, __FILE__, __LINE__);
			goto done;
		}
	}

	// SAB XXX FIXME FIXME  check return code... for all these...
	rc = load_token_data();
	if (rc != CKR_OK) {
		*FunctionList = NULL;
		st_err_log(145, __FILE__, __LINE__);
		goto done;
	}


	load_public_token_objects();
	XProcLock( xproclock );
	global_shm->publ_loaded = TRUE;
	XProcUnLock( xproclock );

	init_slotInfo();

	usage_count++;
	(*FunctionList) = &function_list;

 done:
	ReleaseMutex( native_mutex );
	if (rc != 0)
		st_err_log(145, __FILE__, __LINE__);
	return rc;
}

// What does this really have to do in this new token...  probably
// need to close the adapters that are opened, and clear the other
// stuff
CK_RV SC_Finalize( CK_SLOT_ID sid )
{
	CK_RV          rc;
	SLT_CHECK;
	if (st_Initialized() == FALSE) {	
		st_err_log(72, __FILE__, __LINE__);
		return CKR_CRYPTOKI_NOT_INITIALIZED;
	}
	rc = MY_LockMutex( &pkcs_mutex );
	if (rc != CKR_OK){
		st_err_log(146, __FILE__, __LINE__);
		return rc;
	} 
	// If somebody else has taken care of things, leave...
	if (st_Initialized() == FALSE) {
		MY_UnlockMutex( &pkcs_mutex ); // ? Somebody else has
					       // also destroyed the
					       // mutex...
		st_err_log(72, __FILE__, __LINE__);
		return CKR_CRYPTOKI_NOT_INITIALIZED;
	}
	usage_count--;
	if (usage_count == 0){
		initialized = FALSE;
	}
	session_mgr_close_all_sessions();
	object_mgr_purge_token_objects();
	detach_shm();
	// close spin lock file
	if (spin_created)
	  close(spinxplfd);
	if ( token_specific.t_final != NULL) {
		token_specific.t_final();
	}
	rc = MY_UnlockMutex( &pkcs_mutex );
	if (rc != CKR_OK){
		st_err_log(147, __FILE__, __LINE__);
		return rc;
	}
	return CKR_OK;
}

void
copy_token_contents_sensibly(CK_TOKEN_INFO_PTR pInfo, TOKEN_DATA *nv_token_data)
{
	memcpy(pInfo, &nv_token_data->token_info, sizeof(CK_TOKEN_INFO_32));
	pInfo->flags = nv_token_data->token_info.flags;

	if ( nv_token_data->token_info.ulMaxSessionCount == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulMaxSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulMaxSessionCount = nv_token_data->token_info.ulMaxSessionCount;
	}
	if ( nv_token_data->token_info.ulSessionCount == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulSessionCount = nv_token_data->token_info.ulSessionCount;
	}
	if ( nv_token_data->token_info.ulMaxRwSessionCount == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulMaxRwSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulMaxRwSessionCount = 	nv_token_data->token_info.ulMaxRwSessionCount;
	}
	if ( nv_token_data->token_info.ulRwSessionCount == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulRwSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulRwSessionCount = nv_token_data->token_info.ulRwSessionCount;
	}

	pInfo->ulMaxPinLen = nv_token_data->token_info.ulMaxPinLen;
	pInfo->ulMinPinLen = nv_token_data->token_info.ulMinPinLen;

	if ( nv_token_data->token_info.ulTotalPublicMemory == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulTotalPublicMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulTotalPublicMemory = nv_token_data->token_info.ulTotalPublicMemory;
	}
	if ( nv_token_data->token_info.ulFreePublicMemory == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulFreePublicMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulFreePublicMemory = nv_token_data->token_info.ulFreePublicMemory;
	}
	if ( nv_token_data->token_info.ulTotalPrivateMemory == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulTotalPrivateMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulTotalPrivateMemory = nv_token_data->token_info.ulTotalPrivateMemory;
	}
	if ( nv_token_data->token_info.ulFreePrivateMemory == (CK_ULONG_32)CK_UNAVAILABLE_INFORMATION ) {
	  pInfo->ulFreePrivateMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION;
	} else {
	  pInfo->ulFreePrivateMemory = nv_token_data->token_info.ulFreePrivateMemory;
	}

	pInfo->hardwareVersion = nv_token_data->token_info.hardwareVersion;
	pInfo->firmwareVersion = nv_token_data->token_info.firmwareVersion;
	pInfo->flags = long_reverse(pInfo->flags);
	pInfo->ulMaxSessionCount = long_reverse(pInfo->ulMaxSessionCount);
	pInfo->ulSessionCount = long_reverse(pInfo->ulSessionCount);
	pInfo->ulMaxRwSessionCount = long_reverse(pInfo->ulMaxRwSessionCount);
	pInfo->ulRwSessionCount = long_reverse(pInfo->ulRwSessionCount);
	pInfo->ulMaxPinLen = long_reverse(pInfo->ulMaxPinLen);
	pInfo->ulMinPinLen = long_reverse(pInfo->ulMinPinLen);
	pInfo->ulTotalPublicMemory = long_reverse(pInfo->ulTotalPublicMemory);
	pInfo->ulFreePublicMemory = long_reverse(pInfo->ulFreePublicMemory);
	pInfo->ulTotalPrivateMemory = long_reverse(pInfo->ulTotalPrivateMemory);
	pInfo->ulFreePrivateMemory = long_reverse(pInfo->ulFreePrivateMemory);	
}

CK_RV SC_GetTokenInfo( CK_SLOT_ID         sid,
                       CK_TOKEN_INFO_PTR  pInfo )
{
	CK_RV             rc = CKR_OK;
	time_t now;
	SLT_CHECK;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pInfo) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto done;
	}
	if (slot_id > MAX_SLOT_ID) {
		st_err_log(2, __FILE__, __LINE__); 
		rc = CKR_SLOT_ID_INVALID;
		goto done;
	}
/* TODO: This should always be enabled; eliminate the PKCS64 flag */
#ifdef PKCS64
	copy_token_contents_sensibly(pInfo, nv_token_data);
#else
	memcpy( pInfo, &nv_token_data->token_info, sizeof(CK_TOKEN_INFO) );
#endif


	// Set the time
	now = time ((time_t *)NULL);
	strftime( (char *)pInfo->utcTime, 16, "%X", localtime(&now) );

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x\n", "C_GetTokenInfo", rc );

	}

	UNLOCKIT;
	return rc;
}

CK_RV SC_WaitForSlotEvent( CK_FLAGS        flags,
			   CK_SLOT_ID_PTR  pSlot,
			   CK_VOID_PTR     pReserved )
{
	if (st_Initialized() == FALSE){
		st_err_log(72, __FILE__, __LINE__);
		return CKR_CRYPTOKI_NOT_INITIALIZED;
	}
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/**
 * For Netscape we want to not support the SSL3 mechs since the native
 * ones perform much better.  Force those slots to be RSA... it's ugly
 * but it works.
 */
static void
netscape_hack(CK_MECHANISM_TYPE_PTR mech_arr_ptr, CK_ULONG count)
{
	char *envrn;
	CK_ULONG i;
	if ((envrn = getenv("NS_SERVER_HOME")) != NULL) {
		for (i = 0; i < count; i++){
			switch (mech_arr_ptr[i]) {
			case CKM_SSL3_PRE_MASTER_KEY_GEN:
			case CKM_SSL3_MASTER_KEY_DERIVE:
			case CKM_SSL3_KEY_AND_MAC_DERIVE:
			case CKM_SSL3_MD5_MAC:
			case CKM_SSL3_SHA1_MAC:
				mech_arr_ptr[i] = CKM_RSA_PKCS;
				break;
			}
		}
	}
}

void mechanism_list_transformations(CK_MECHANISM_TYPE_PTR mech_arr_ptr,
				    CK_ULONG_PTR count_ptr)
{
#ifndef NO_NETSCAPE_HACK
	netscape_hack(mech_arr_ptr, (*count_ptr));
#endif /* #ifndef NO_NETSCAPE_HACK */
}

/**
 * Get the mechanism type list for the current token.
 */
CK_RV SC_GetMechanismList(CK_SLOT_ID sid,
                          CK_MECHANISM_TYPE_PTR pMechList,
                          CK_ULONG_PTR count)
{
	CK_RV rc = CKR_OK;
	SLT_CHECK;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto out;
	}
	if (count == NULL) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto out;
	}
	if (slot_id > MAX_SLOT_ID) {
		st_err_log(2, __FILE__, __LINE__); 
		rc = CKR_SLOT_ID_INVALID;
		goto out;
	}
	if (!token_specific.t_get_mechanism_list) {
		st_err_log(4, __FILE__, __LINE__);
		rc = CKR_GENERAL_ERROR;
		goto out;
	}
	rc = token_specific.t_get_mechanism_list(pMechList, count);
	if (rc == CKR_OK) {
		/* To accomodate certain special cases, we may need to
		 * make adjustments to the token's mechanism list. */
		mechanism_list_transformations(pMechList, count);
	}
 out:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x, # mechanisms:  %d\n",
			 "C_GetMechanismList", rc, *count );
	}
	UNLOCKIT;
	return rc;
}

/**
 * Get the mechanism info for the current type and token.
 */
CK_RV SC_GetMechanismInfo(CK_SLOT_ID sid,
                          CK_MECHANISM_TYPE type,
                          CK_MECHANISM_INFO_PTR pInfo)
{
	CK_RV rc = CKR_OK;
	SLT_CHECK;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto out;
	}
	if (pInfo == NULL) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto out;
	}
	if (slot_id > MAX_SLOT_ID) {
		st_err_log(2, __FILE__, __LINE__); 
		rc = CKR_SLOT_ID_INVALID;
		goto out;
	}
	if (!token_specific.t_get_mechanism_info) {
		st_err_log(4, __FILE__, __LINE__);
		rc = CKR_GENERAL_ERROR;
		goto out;
	}
	rc = token_specific.t_get_mechanism_info(type, pInfo);
 out:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x, mech type = 0x%08x\n",
			 "C_GetMechanismInfo", rc, type );
	}
	
	UNLOCKIT;
	return rc;
}

int delete_all_files_in_dir(char *full_dir_path)
{
	return 0;
}

/* This routine should only be called if no other processes are
 * attached to the token.  we need to somehow check that this is the
 * only process Meta API should prevent this since it knows session
 * states in the shared memory. */
CK_RV SC_InitToken( CK_SLOT_ID   sid,
                    CK_CHAR_PTR  pPin,
                    CK_ULONG     ulPinLen,
                    CK_CHAR_PTR  pLabel )
{
	CK_RV      rc = CKR_OK;
	int local_rc = 0;
	CK_BYTE    hash_sha[SHA1_HASH_SIZE];
	CK_SLOT_ID slotID;
	char *s = NULL;
	char *pk_full_path = NULL;
	SLT_CHECK;
	slotID = slot_id;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pPin || !pLabel) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	if (nv_token_data->token_info.flags & CKF_SO_PIN_LOCKED) {
		st_err_log(37, __FILE__, __LINE__);
		rc = CKR_PIN_LOCKED;
		goto done;
	}
	rc = compute_sha( pPin, ulPinLen, hash_sha );
	if (memcmp(nv_token_data->so_pin_sha, hash_sha, SHA1_HASH_SIZE) != 0) {
		st_err_log(33, __FILE__, __LINE__);
		rc = CKR_PIN_INCORRECT;
		goto done;
	}
	rc  = rng_generate( master_key, 3 * DES_KEY_SIZE );
	if (rc != CKR_OK) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto done;
	}
	// Before we reconstruct all the data, we should delete the
	// token objects from the filesystem.
	//
	// Construct a string to delete the token objects.
	//
	object_mgr_destroy_token_objects();
#if 0 /* TODO: Implement delete_all_files_in_dir() */
	local_rc = asprintf(&pk_full_path, "%s/%s", pk_dir, PK_LITE_OBJ_DIR);
	if (local_rc == -1) {
		rc = CKR_HOST_MEMORY;
		goto out;
	}
	local_rc = delete_all_files_in_dir(pk_full_path);
	if (local_rc == -1) {
		rc = CKR_FUNCTION_FAILED;
		goto out;
	}
#endif
	local_rc = asprintf(&s, "%s %s/%s/* > /dev/null 2>&1", DEL_CMD, pk_dir,
			    PK_LITE_OBJ_DIR);
	if (local_rc == -1) {
		rc = CKR_HOST_MEMORY;
		goto out;
	}
	system(s);
	free(s);
	s = NULL;
	// META This should be fine since the open session checking
	// should occur at the API not the STDLL
	init_token_data();
	init_slotInfo();
	memcpy( nv_token_data->token_info.label, pLabel, 32 );
	memcpy( nv_token_data->so_pin_sha, hash_sha, SHA1_HASH_SIZE);
	nv_token_data->token_info.flags |= CKF_TOKEN_INITIALIZED;
	rc = save_token_data();
	if (rc != CKR_OK){
		st_err_log(104, __FILE__, __LINE__, __FUNCTION__);
		goto done;
	}
	rc = save_masterkey_so();
	if (rc != CKR_OK){
		st_err_log(149, __FILE__, __LINE__, __FUNCTION__);
		goto done;
	}
 done:
 out:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x\n", "C_InitToken",
			 rc );
	}
	UNLOCKIT;
	if (pk_full_path) {
		free(pk_full_path);
	}
	return rc;
}


//
//
CK_RV SC_InitPIN( ST_SESSION_HANDLE  sSession,
                  CK_CHAR_PTR        pPin,
                  CK_ULONG           ulPinLen )
{
	SESSION         * sess = NULL;
	CK_BYTE           hash_sha[SHA1_HASH_SIZE];
	CK_BYTE           hash_md5[MD5_HASH_SIZE];
	CK_RV             rc = CKR_OK;
	SESS_SET;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pPin) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	if (pin_locked(&sess->session_info, nv_token_data->token_info.flags)
	    == TRUE) {
		st_err_log(37, __FILE__, __LINE__);
		rc = CKR_PIN_LOCKED;
		goto done;
	}
	if (sess->session_info.state != CKS_RW_SO_FUNCTIONS) {
		st_err_log(57, __FILE__, __LINE__);
		rc = CKR_USER_NOT_LOGGED_IN;
		goto done;
	}
	if ((ulPinLen < MIN_PIN_LEN) || (ulPinLen > MAX_PIN_LEN)) {
		st_err_log(35, __FILE__, __LINE__); 
		rc = CKR_PIN_LEN_RANGE;
		goto done;
	}
	// compute the SHA and MD5 hashes of the user pin
	rc  = compute_sha( pPin, ulPinLen, hash_sha );
	rc |= compute_md5( pPin, ulPinLen, hash_md5 );
	if (rc != CKR_OK){
		st_err_log(148, __FILE__, __LINE__); 	
		goto done;
	}
	rc = XProcLock( xproclock );
	if (rc != CKR_OK){
		st_err_log(150, __FILE__, __LINE__);
		goto done;
	}
	memcpy(nv_token_data->user_pin_sha, hash_sha, SHA1_HASH_SIZE);
	nv_token_data->token_info.flags |= CKF_USER_PIN_INITIALIZED;
	nv_token_data->token_info.flags &= ~(CKF_USER_PIN_TO_BE_CHANGED);
	nv_token_data->token_info.flags &= ~(CKF_USER_PIN_LOCKED);
	XProcUnLock(xproclock);
	memcpy( user_pin_md5, hash_md5, MD5_HASH_SIZE  );
	rc = save_token_data();
	if (rc != CKR_OK){
		st_err_log(104, __FILE__, __LINE__);
		goto done;
	}
	rc = save_masterkey_user();
	if (rc != CKR_OK){
		st_err_log(149, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  session = %08x\n", "C_InitPin", rc, 
			 hSession);
	}
	UNLOCKIT;
	return rc;
}

CK_RV SC_SetPIN( ST_SESSION_HANDLE  sSession,
                 CK_CHAR_PTR        pOldPin,
                 CK_ULONG           ulOldLen,
                 CK_CHAR_PTR        pNewPin,
                 CK_ULONG           ulNewLen )
{
	SESSION *sess = NULL;
	CK_BYTE old_hash_sha[SHA1_HASH_SIZE];
	CK_BYTE new_hash_sha[SHA1_HASH_SIZE];
	CK_BYTE hash_md5[MD5_HASH_SIZE];
	CK_RV rc = CKR_OK;
	SESS_SET;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	if (pin_locked(&sess->session_info, 
		       nv_token_data->token_info.flags) == TRUE) {
		st_err_log(37, __FILE__, __LINE__);
		rc = CKR_PIN_LOCKED;
		goto done;
	}
	if ((ulNewLen < MIN_PIN_LEN) || (ulNewLen > MAX_PIN_LEN)) {
		st_err_log(35, __FILE__, __LINE__); 
		rc = CKR_PIN_LEN_RANGE;
		goto done;
	}
	rc = compute_sha( pOldPin, ulOldLen, old_hash_sha );
	if (rc != CKR_OK){
		st_err_log(148, __FILE__, __LINE__); 	
		goto done;
	}
	/* From the PKCS#11 2.20 spec: "C_SetPIN modifies the PIN of
	 * the user that is currently logged in, or the CKU_USER PIN
	 * if the session is not logged in."  A non R/W session fails
	 * with CKR_SESSION_READ_ONLY.
	 */
	if ((sess->session_info.state == CKS_RW_USER_FUNCTIONS) ||
	    (sess->session_info.state == CKS_RW_PUBLIC_SESSION)) {
		if (memcmp(nv_token_data->user_pin_sha, old_hash_sha, 
			   SHA1_HASH_SIZE) != 0) {
			st_err_log(33, __FILE__, __LINE__); 	
			rc = CKR_PIN_INCORRECT;
			goto done;
		}
		rc  = compute_sha( pNewPin, ulNewLen, new_hash_sha );
		rc |= compute_md5( pNewPin, ulNewLen, hash_md5 );
		if (rc != CKR_OK){
			st_err_log(148, __FILE__, __LINE__); 	
			goto done;
		}
		/* The old PIN matches, now make sure its different
		 * than the new and is not the default. */
		if ((memcmp(old_hash_sha, new_hash_sha, SHA1_HASH_SIZE) == 0) ||
		    (memcmp(new_hash_sha, default_user_pin_sha, SHA1_HASH_SIZE)
		     == 0)) {
			st_err_log(34, __FILE__, __LINE__);
			rc = CKR_PIN_INVALID;
			goto done;
		}
		rc = XProcLock( xproclock );
		if (rc != CKR_OK){
			st_err_log(150, __FILE__, __LINE__);
			goto done;
		}
		memcpy(nv_token_data->user_pin_sha, new_hash_sha,
		       SHA1_HASH_SIZE);
		memcpy(user_pin_md5, hash_md5, MD5_HASH_SIZE);
		nv_token_data->token_info.flags &=
			~(CKF_USER_PIN_TO_BE_CHANGED);
		XProcUnLock( xproclock );
		rc = save_token_data();
		if (rc != CKR_OK){
			st_err_log(104, __FILE__, __LINE__);
			goto done;
		}
		rc = save_masterkey_user();
	} else if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) {
		if (memcmp(nv_token_data->so_pin_sha, old_hash_sha, 
			   SHA1_HASH_SIZE) != 0) {
			rc = CKR_PIN_INCORRECT;
			st_err_log(33, __FILE__, __LINE__);
			goto done;
		}
		rc = compute_sha(pNewPin, ulNewLen, new_hash_sha);
		rc |= compute_md5(pNewPin, ulNewLen, hash_md5);
		if (rc != CKR_OK){
			st_err_log(148, __FILE__, __LINE__);
			goto done;
		}
		/* The old PIN matches, now make sure its different
		 * than the new and is not the default. */
		if ((memcmp(old_hash_sha, new_hash_sha, SHA1_HASH_SIZE) == 0) ||
		    (memcmp(new_hash_sha, default_so_pin_sha, SHA1_HASH_SIZE)
		     == 0)) {
			st_err_log(34, __FILE__, __LINE__);
			rc = CKR_PIN_INVALID;
			goto done;
		}
		rc = XProcLock( xproclock );
		if (rc != CKR_OK){
			st_err_log(150, __FILE__, __LINE__);
			goto done;
		}
		memcpy(nv_token_data->so_pin_sha, new_hash_sha, SHA1_HASH_SIZE);
		memcpy( so_pin_md5, hash_md5, MD5_HASH_SIZE );
		nv_token_data->token_info.flags &= ~(CKF_SO_PIN_TO_BE_CHANGED);
		XProcUnLock( xproclock );
		rc = save_token_data();
		if (rc != CKR_OK){
			st_err_log(104, __FILE__, __LINE__);
			goto done;
		}
		rc = save_masterkey_so();
	} else {
		st_err_log(142, __FILE__, __LINE__);
		rc = CKR_SESSION_READ_ONLY;
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  session = %08x\n", "C_SetPin", rc,
			 hSession );
	}
	UNLOCKIT;
	if (rc != CKR_SESSION_READ_ONLY && rc != CKR_OK)
		st_err_log(149, __FILE__, __LINE__);
	return rc;
}

CK_RV SC_OpenSession(CK_SLOT_ID             sid,
		     CK_FLAGS               flags,
		     CK_SESSION_HANDLE_PTR  phSession)
{
	SESSION              * sess;
	CK_BBOOL               locked = FALSE;
	CK_RV                  rc = CKR_OK;
	SLT_CHECK;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (phSession == NULL) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto done;
	}
	if (slot_id > MAX_SLOT_ID) {
		st_err_log(2, __FILE__, __LINE__); 
		rc = CKR_SLOT_ID_INVALID;
		goto done;
	}
	flags |= CKF_SERIAL_SESSION;
	if ((flags & CKF_RW_SESSION) == 0) {
		if (session_mgr_so_session_exists()) {
			st_err_log(45, __FILE__, __LINE__); 
			rc = CKR_SESSION_READ_WRITE_SO_EXISTS;
			goto done;
		}
	}
	// Get the mutex because we may modify the pid_list
	rc = MY_LockMutex( &pkcs_mutex );
	if (rc != CKR_OK){
		st_err_log(146, __FILE__, __LINE__); 
		goto done;
	}
	locked = TRUE;
	token_specific.t_session(slot_id);
	MY_UnlockMutex( &pkcs_mutex );
	locked = FALSE;
	rc = session_mgr_new( flags, &sess );
	if (rc != CKR_OK){
		st_err_log(152, __FILE__, __LINE__); 
		goto done;
	}
	*phSession = sess->handle;
	// Set the correct slot ID here. Was hard coded to 1. - KEY
	sess->session_info.slotID = sid;
 done:
	if (locked)
		MY_UnlockMutex( &pkcs_mutex );
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x  ", "C_OpenSession",
			 rc);
		if (rc == CKR_OK)
			stlogit2(debugfile, "sess = %d", 
				 ((sess == NULL) ? -1 : (CK_LONG)sess->handle));
		stlogit2(debugfile, "\n");
	}
	UNLOCKIT;
	return rc;
}

CK_RV SC_CloseSession( ST_SESSION_HANDLE  sSession )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	rc = session_mgr_close_session( sess );
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x  sess = %d\n",
			 "C_CloseSession", rc, hSession );
	}
	UNLOCKIT;
	return rc;
}

CK_RV SC_CloseAllSessions( CK_SLOT_ID  sid )
{
	CK_RV rc = CKR_OK;
	SLT_CHECK;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	rc = session_mgr_close_all_sessions();
	if (rc != CKR_OK){
		st_err_log(153, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x  slot = %d\n",
			 "C_CloseAllSessions", rc, slot_id );
	}
	UNLOCKIT;
	return rc;
}

CK_RV SC_GetSessionInfo( ST_SESSION_HANDLE   sSession,
			 CK_SESSION_INFO_PTR pInfo )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pInfo) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	memcpy( pInfo, &sess->session_info, sizeof(CK_SESSION_INFO) );

 done:
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  session = %08d\n",
			 "C_GetSessionInfo", hSession );
	}
	UNLOCKIT;
	return rc;
}

CK_RV SC_GetOperationState( ST_SESSION_HANDLE  sSession,
			    CK_BYTE_PTR        pOperationState,
			    CK_ULONG_PTR       pulOperationStateLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET;
	LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pulOperationStateLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	if (!pOperationState)
		length_only = TRUE;

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}


	rc = session_mgr_get_op_state( sess, length_only,
				       pOperationState,
				       pulOperationStateLen );
	if (rc != CKR_OK){
		st_err_log(154, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  session = %08x\n", "C_GetOperationState", rc, hSession );
	}

	UNLOCKIT;
	return rc;
}


//
//
CK_RV SC_SetOperationState( ST_SESSION_HANDLE  sSession,
			    CK_BYTE_PTR        pOperationState,
			    CK_ULONG           ulOperationStateLen,
			    CK_OBJECT_HANDLE   hEncryptionKey,
			    CK_OBJECT_HANDLE   hAuthenticationKey )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pOperationState || (ulOperationStateLen == 0)) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	rc = session_mgr_set_op_state( sess,
				       hEncryptionKey,  hAuthenticationKey,
				       pOperationState, ulOperationStateLen );

	if (rc != CKR_OK){
		st_err_log(154, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  session = %08x\n", "C_SetOperationState", rc, hSession );
	}

	UNLOCKIT;
	return rc;
}



//
//
CK_RV SC_Login( ST_SESSION_HANDLE   sSession,
                CK_USER_TYPE        userType,
                CK_CHAR_PTR         pPin,
                CK_ULONG            ulPinLen )
{
	SESSION        * sess = NULL;
	CK_FLAGS_32    * flags = NULL;
	CK_BYTE          hash_sha[SHA1_HASH_SIZE];
	CK_RV            rc = CKR_OK;

	SESS_SET
		LOCKIT;
	// In v2.11, logins should be exclusive, since token
	// specific flags may need to be set for a bad login. - KEY
	rc = MY_LockMutex( &login_mutex );
	if (rc != CKR_OK){
	        st_err_log(146, __FILE__, __LINE__);
		return CKR_FUNCTION_FAILED;
	}
	  
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	
	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	flags = &nv_token_data->token_info.flags;

	if (!pPin || ulPinLen > MAX_PIN_LEN) {
		set_login_flags(userType, flags);
		st_err_log(33, __FILE__, __LINE__);
		rc = CKR_PIN_INCORRECT;
		goto done;
	}
	
	// PKCS #11 v2.01 requires that all sessions have the same login status:
	//    --> all sessions are public, all are SO or all are USER
	//
	if (userType == CKU_USER) {
		if (session_mgr_so_session_exists()){
			st_err_log(60, __FILE__, __LINE__);
			rc = CKR_USER_ANOTHER_ALREADY_LOGGED_IN;
		}
		if (session_mgr_user_session_exists()){
			st_err_log(56, __FILE__, __LINE__);
			rc = CKR_USER_ALREADY_LOGGED_IN;
		}
	}
	else if (userType == CKU_SO) {
		if (session_mgr_user_session_exists()){
			st_err_log(60, __FILE__, __LINE__);
			rc = CKR_USER_ANOTHER_ALREADY_LOGGED_IN;
		}
		if (session_mgr_so_session_exists()){
			st_err_log(56, __FILE__, __LINE__);
			rc = CKR_USER_ALREADY_LOGGED_IN;
		}
		if (session_mgr_readonly_exists()){
			st_err_log(142, __FILE__, __LINE__);
			rc = CKR_SESSION_READ_ONLY_EXISTS;
		}
	}
	else {
		rc = CKR_USER_TYPE_INVALID;
		st_err_log(59, __FILE__, __LINE__);
	}
	if (rc != CKR_OK)
		goto done;

	if (userType == CKU_USER) {
		if (*flags & CKF_USER_PIN_LOCKED) {
			st_err_log(37, __FILE__, __LINE__);
			rc = CKR_PIN_LOCKED;
			goto done;
		}

		if (memcmp(nv_token_data->user_pin_sha,
			   "00000000000000000000", SHA1_HASH_SIZE) == 0) {
			st_err_log(33, __FILE__, __LINE__);
			rc = CKR_USER_PIN_NOT_INITIALIZED;
			goto done;
		}

		rc = compute_sha( pPin, ulPinLen, hash_sha );
		if (memcmp(nv_token_data->user_pin_sha, hash_sha, SHA1_HASH_SIZE) != 0) {
			set_login_flags(userType, flags);
			st_err_log(33, __FILE__, __LINE__);
			rc = CKR_PIN_INCORRECT;
			goto done;
		}
		/* Successful login, clear flags */
		*flags &=	~(CKF_USER_PIN_LOCKED |
				  CKF_USER_PIN_FINAL_TRY |
				  CKF_USER_PIN_COUNT_LOW);

		compute_md5( pPin, ulPinLen, user_pin_md5 );
		memset( so_pin_md5, 0x0, MD5_HASH_SIZE );

		rc = load_masterkey_user();
		if (rc != CKR_OK){
			st_err_log(155, __FILE__, __LINE__);
			goto done;
		}
		rc = load_private_token_objects();

		XProcLock( xproclock );
		global_shm->priv_loaded = TRUE;
		XProcUnLock( xproclock );

	}
	else {
		if (*flags & CKF_SO_PIN_LOCKED) {
			st_err_log(37, __FILE__, __LINE__);
			rc = CKR_PIN_LOCKED;
			goto done;
		}
		rc = compute_sha( pPin, ulPinLen, hash_sha );
		if (memcmp(nv_token_data->so_pin_sha, hash_sha, SHA1_HASH_SIZE) != 0) {
			set_login_flags(userType, flags);
			st_err_log(33, __FILE__, __LINE__);
			rc = CKR_PIN_INCORRECT;
			goto done;
		}
		/* Successful login, clear flags */
		*flags &= 	~(CKF_SO_PIN_LOCKED | 
				  CKF_SO_PIN_FINAL_TRY | 
				  CKF_SO_PIN_COUNT_LOW);

		compute_md5( pPin, ulPinLen, so_pin_md5 );
		memset( user_pin_md5, 0x0, MD5_HASH_SIZE );

		rc = load_masterkey_so();
		if (rc != CKR_OK) {
			st_err_log(155, __FILE__, __LINE__);
		}
	}

	rc = session_mgr_login_all( userType );
	if (rc != CKR_OK) {
		st_err_log(174, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x\n", "C_Login", rc );
	}

	UNLOCKIT;
	save_token_data();
	MY_UnlockMutex( &login_mutex );
	return rc;
}


//
//
CK_RV SC_Logout( ST_SESSION_HANDLE  sSession )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;

	SESS_SET
	
		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	
	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	
	// all sessions have the same state so we just have to check one
	//
	if (session_mgr_public_session_exists()) {
		st_err_log(57, __FILE__, __LINE__);
		rc = CKR_USER_NOT_LOGGED_IN;
		goto done;
	}

	rc = session_mgr_logout_all();
	if (rc != CKR_OK){
		st_err_log(57, __FILE__, __LINE__);
	}
	memset( user_pin_md5, 0x0, MD5_HASH_SIZE );
	memset( so_pin_md5,   0x0, MD5_HASH_SIZE );
	
	object_mgr_purge_private_token_objects();
	
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = 0x%08x\n", "C_Logout", rc );
	}
   
	UNLOCKIT; return rc;
}


// This is a Leeds-Lite solution so we have to store objects on the host.
//
CK_RV SC_CreateObject( ST_SESSION_HANDLE    sSession,
		       CK_ATTRIBUTE_PTR     pTemplate,
		       CK_ULONG             ulCount,
		       CK_OBJECT_HANDLE_PTR phObject )
{
	SESSION               * sess = NULL;
	CK_ULONG                i;
	CK_RV                   rc = CKR_OK;
	SESS_SET

		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = object_mgr_add( sess, pTemplate, ulCount, phObject );
	if (rc != CKR_OK) {
		st_err_log(157, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x\n", "C_CreateObject", rc );

		for (i = 0; i < ulCount; i++) {
			if (pTemplate[i].type == CKA_CLASS)
				stlogit2(debugfile, "%28s:  0x%02x\n", "Object Type", *(CK_ULONG *)pTemplate[i].pValue );
		}
		if (rc == CKR_OK)
			stlogit2(debugfile, "%28s:  %d\n", "Handle", *phObject );

	}

	UNLOCKIT; return rc;
}



//
//
CK_RV  SC_CopyObject( ST_SESSION_HANDLE    sSession,
		      CK_OBJECT_HANDLE     hObject,
		      CK_ATTRIBUTE_PTR     pTemplate,
		      CK_ULONG             ulCount,
		      CK_OBJECT_HANDLE_PTR phNewObject )
{
	SESSION              * sess = NULL;
	CK_RV                  rc = CKR_OK;
	SESS_SET

		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
   
	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = object_mgr_copy( sess, pTemplate, ulCount, hObject, phNewObject );
	if (rc != CKR_OK) {
		st_err_log(158, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, old handle = %d, new handle = %d\n", "C_CopyObject", rc, hObject, *phNewObject );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DestroyObject( ST_SESSION_HANDLE  sSession,
			CK_OBJECT_HANDLE   hObject )
{
	SESSION               * sess = NULL;
	CK_RV                   rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = object_mgr_destroy_object( sess, hObject );
	if (rc != CKR_OK){
		st_err_log(182, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, handle = %d\n", "C_DestroyObject", rc, hObject );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_GetObjectSize( ST_SESSION_HANDLE  sSession,
			CK_OBJECT_HANDLE   hObject,
			CK_ULONG_PTR       pulSize )
{
	SESSION               * sess = NULL;
	CK_RV                   rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	rc = object_mgr_get_object_size( hObject, pulSize );
	if (rc != CKR_OK){
		st_err_log(184, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, handle = %d\n", "C_GetObjectSize", rc, hObject );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_GetAttributeValue( ST_SESSION_HANDLE  sSession,
			    CK_OBJECT_HANDLE   hObject,
			    CK_ATTRIBUTE_PTR   pTemplate,
			    CK_ULONG           ulCount )
{
	SESSION        * sess = NULL;
	CK_ATTRIBUTE   * attr = NULL;
	CK_BYTE        * ptr  = NULL;
	CK_ULONG         i;
	CK_RV            rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	rc = object_mgr_get_attribute_values( sess, hObject, pTemplate, ulCount );
	if (rc != CKR_OK){
		st_err_log(159, __FILE__, __LINE__);
	}


 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, handle = %d\n", "C_GetAttributeValue", rc, hObject );

		attr = pTemplate;
		for (i = 0; i < ulCount; i++, attr++) {
			ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV  SC_SetAttributeValue( ST_SESSION_HANDLE    sSession,
			     CK_OBJECT_HANDLE     hObject,
			     CK_ATTRIBUTE_PTR     pTemplate,
			     CK_ULONG             ulCount )
{
	SESSION       * sess = NULL;
	CK_ATTRIBUTE  * attr = NULL;
	CK_ULONG        i;
	CK_RV           rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	rc = object_mgr_set_attribute_values( sess, hObject, pTemplate, ulCount);
	if (rc != CKR_OK){
		st_err_log(161, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, handle = %d\n", "C_SetAttributeValue", rc, hObject );

		attr = pTemplate;
		for (i = 0; i < ulCount; i++, attr++) {
			CK_BYTE *ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_FindObjectsInit( ST_SESSION_HANDLE   sSession,
			  CK_ATTRIBUTE_PTR    pTemplate,
			  CK_ULONG            ulCount )
{
	SESSION        * sess  = NULL;
	CK_ATTRIBUTE   * attr = NULL;
	CK_ULONG         i;
	CK_RV            rc = CKR_OK;
	SESS_SET

		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->find_active == TRUE) {
		st_err_log(31, __FILE__, __LINE__);
		rc = CKR_OPERATION_ACTIVE;
		goto done;
	}

	rc = object_mgr_find_init( sess, pTemplate, ulCount );
	if (rc != CKR_OK){
		st_err_log(185, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x\n", "C_FindObjectsInit", rc );

		attr = pTemplate;
		for (i = 0; i < ulCount; i++, attr++) {
			CK_BYTE *ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_FindObjects( ST_SESSION_HANDLE     sSession,
		      CK_OBJECT_HANDLE_PTR  phObject,
		      CK_ULONG              ulMaxObjectCount,
		      CK_ULONG_PTR          pulObjectCount )
{
	SESSION    * sess  = NULL;
	CK_ULONG     count = 0;
	CK_RV        rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!phObject || !pulObjectCount) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->find_active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!sess->find_list) {
		st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_FUNCTION_FAILED;
		goto done;
	}
	count = MIN(ulMaxObjectCount, (sess->find_count - sess->find_idx));

	memcpy( phObject, sess->find_list + sess->find_idx, count * sizeof(CK_OBJECT_HANDLE) );
	*pulObjectCount = count;

	sess->find_idx += count;
	rc = CKR_OK;

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, returned %d objects\n", "C_FindObjects", rc, count );
	}

	UNLOCKIT; return rc;
}



//
//
CK_RV SC_FindObjectsFinal( ST_SESSION_HANDLE  sSession )
{
	SESSION     * sess = NULL;
	CK_RV         rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->find_active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (sess->find_list)
		free( sess->find_list );

	sess->find_list   = NULL;
	sess->find_len    = 0;
	sess->find_idx    = 0;
	sess->find_active = FALSE;

	rc = CKR_OK;

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x\n", "C_FindObjectsFinal", rc );
	}

	UNLOCKIT; return rc;
}



//
//
CK_RV SC_EncryptInit( ST_SESSION_HANDLE  sSession,
		      CK_MECHANISM_PTR   pMechanism,
		      CK_OBJECT_HANDLE   hKey )
{
	SESSION               * sess = NULL;
	CK_RV                   rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->encr_ctx.active == TRUE) {
		st_err_log(31, __FILE__, __LINE__);
		rc = CKR_OPERATION_ACTIVE;
		goto done;
	}

	rc = encr_mgr_init( sess, &sess->encr_ctx, OP_ENCRYPT_INIT, pMechanism, hKey );
	if (rc != CKR_OK) {
		st_err_log(98, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, key = %d, mech = 0x%x\n", "C_EncryptInit", rc,(sess == NULL)?-1:(CK_LONG)sess->handle, hKey, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_Encrypt( ST_SESSION_HANDLE  sSession,
		  CK_BYTE_PTR        pData,
		  CK_ULONG           ulDataLen,
		  CK_BYTE_PTR        pEncryptedData,
		  CK_ULONG_PTR       pulEncryptedDataLen )
{
	SESSION        * sess = NULL;
	CK_BBOOL         length_only = FALSE;
	CK_RV            rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pData || !pulEncryptedDataLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->encr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pEncryptedData)
		length_only = TRUE;

	rc = encr_mgr_encrypt( sess,           length_only,
			       &sess->encr_ctx,
			       pData,          ulDataLen,
			       pEncryptedData, pulEncryptedDataLen );
	if (rc != CKR_OK) {
		st_err_log(99, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		encr_mgr_cleanup( &sess->encr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, amount = %d\n", "C_Encrypt", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_EncryptUpdate( ST_SESSION_HANDLE  sSession,
			CK_BYTE_PTR        pPart,
			CK_ULONG           ulPartLen,
			CK_BYTE_PTR        pEncryptedPart,
			CK_ULONG_PTR       pulEncryptedPartLen )
{
	SESSION        * sess = NULL;
	CK_BBOOL         length_only = FALSE;
	CK_RV            rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pPart || !pulEncryptedPartLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->encr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pEncryptedPart)
		length_only = TRUE;

	rc = encr_mgr_encrypt_update( sess,           length_only,
				      &sess->encr_ctx,
				      pPart,          ulPartLen,
				      pEncryptedPart, pulEncryptedPartLen );
	if (rc != CKR_OK) {
		st_err_log(176, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
		encr_mgr_cleanup( &sess->encr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, amount = %d\n", "C_EncryptUpdate", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulPartLen );
	}

	UNLOCKIT; return rc;
}


// I think RSA goofed when designing the specification for C_EncryptFinal.
// This function is supposed to follow the Cryptoki standard that if
// pLastEncryptedPart == NULL then the user is requesting only the length
// of the output.
//
// But it's quite possible that no output will be returned (say the user
// specifies a total of 64 bytes of input data throughout the multi-part
// encryption).  The same thing can happen during an EncryptUpdate.
//
// ie:
//
//    1) user calls C_EncryptFinal to get the needed length
//       --> we return "0 bytes required"
//    2) user passes in a NULL pointer for pLastEncryptedPart
//       --> we think the user is requesting the length again <--
//
// So the user needs to pass in a non-NULL pointer even though we're not
// going to return anything in it.  It would have been cleaner if RSA would
// have simply included a "give-me-the-length-only flag" as an argument.
//
//
CK_RV SC_EncryptFinal( ST_SESSION_HANDLE  sSession,
		       CK_BYTE_PTR        pLastEncryptedPart,
		       CK_ULONG_PTR       pulLastEncryptedPartLen )
{
	SESSION     * sess = NULL;
	CK_BBOOL      length_only = FALSE;
	CK_RV         rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pulLastEncryptedPartLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->encr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pLastEncryptedPart)
		length_only = TRUE;

	rc = encr_mgr_encrypt_final( sess,  length_only, &sess->encr_ctx,
				     pLastEncryptedPart, pulLastEncryptedPartLen );
	if (rc != CKR_OK) {
		st_err_log(177, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		encr_mgr_cleanup( &sess->encr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d\n", "C_EncryptFinal", rc, (sess == NULL)?-1:(CK_LONG)sess->handle );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DecryptInit( ST_SESSION_HANDLE  sSession,
		      CK_MECHANISM_PTR   pMechanism,
		      CK_OBJECT_HANDLE   hKey )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->decr_ctx.active == TRUE) {
		st_err_log(31, __FILE__, __LINE__);
		rc = CKR_OPERATION_ACTIVE;
		goto done;
	}

	rc = decr_mgr_init( sess, &sess->decr_ctx, OP_DECRYPT_INIT, pMechanism, hKey );
	if (rc != CKR_OK) {
		st_err_log(179, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, key = %d, mech = 0x%x\n", "C_DecryptInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, hKey, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_Decrypt( ST_SESSION_HANDLE  sSession,
		  CK_BYTE_PTR        pEncryptedData,
		  CK_ULONG           ulEncryptedDataLen,
		  CK_BYTE_PTR        pData,
		  CK_ULONG_PTR       pulDataLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pEncryptedData || !pulDataLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->decr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pData)
		length_only = TRUE;

	rc = decr_mgr_decrypt( sess,           length_only,
			       &sess->decr_ctx,
			       pEncryptedData, ulEncryptedDataLen,
			       pData,          pulDataLen );
	if (rc != CKR_OK) {
		st_err_log(100, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		decr_mgr_cleanup( &sess->decr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, amount = %d\n", "C_Decrypt", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulEncryptedDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DecryptUpdate( ST_SESSION_HANDLE  sSession,
			CK_BYTE_PTR        pEncryptedPart,
			CK_ULONG           ulEncryptedPartLen,
			CK_BYTE_PTR        pPart,
			CK_ULONG_PTR       pulPartLen )
{
	SESSION   * sess = NULL;
	CK_BBOOL    length_only = FALSE;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pEncryptedPart || !pulPartLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->decr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pPart)
		length_only = TRUE;

	rc = decr_mgr_decrypt_update( sess,           length_only,
				      &sess->decr_ctx,
				      pEncryptedPart, ulEncryptedPartLen,
				      pPart,          pulPartLen );
	if (rc != CKR_OK) {
		st_err_log(180, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
		decr_mgr_cleanup( &sess->decr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, amount = %d\n", "C_DecryptUpdate", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulEncryptedPartLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DecryptFinal( ST_SESSION_HANDLE  sSession,
		       CK_BYTE_PTR        pLastPart,
		       CK_ULONG_PTR       pulLastPartLen )
{
	SESSION   * sess = NULL;
	CK_BBOOL    length_only = FALSE;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pulLastPartLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->decr_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pLastPart)
		length_only = TRUE;

	rc = decr_mgr_decrypt_final( sess,      length_only,
				     &sess->decr_ctx,
				     pLastPart, pulLastPartLen );

	if (rc != CKR_OK) {
		st_err_log(181, __FILE__, __LINE__);
	}
 done:
	LLOCK;
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		decr_mgr_cleanup( &sess->decr_ctx );

	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, amount = %d\n", "C_DecryptFinal", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, *pulLastPartLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DigestInit( ST_SESSION_HANDLE  sSession,
		     CK_MECHANISM_PTR   pMechanism )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pMechanism) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	VALID_MECH(pMechanism);


	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->digest_ctx.active == TRUE) {
		st_err_log(31, __FILE__, __LINE__);
		rc = CKR_OPERATION_ACTIVE;
		goto done;
	}

	rc = digest_mgr_init( sess, &sess->digest_ctx, pMechanism );
	if (rc != CKR_OK) {
		st_err_log(123, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_DigestInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_Digest( ST_SESSION_HANDLE  sSession,
		 CK_BYTE_PTR        pData,
		 CK_ULONG           ulDataLen,
		 CK_BYTE_PTR        pDigest,
		 CK_ULONG_PTR       pulDigestLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	// Netscape has been known to pass a null pData to DigestUpdate
	// but never for Digest.  It doesn't really make sense to allow it here
	//
	if (!pData || !pulDigestLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->digest_ctx.active == FALSE) {
		st_err_log(85, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pDigest)
		length_only = TRUE;

	rc = digest_mgr_digest( sess,    length_only,
				&sess->digest_ctx,
				pData,   ulDataLen,
				pDigest, pulDigestLen );
	if (rc != CKR_OK) {
		st_err_log(124, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		digest_mgr_cleanup( &sess->digest_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_Digest", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DigestUpdate( ST_SESSION_HANDLE  sSession,
		       CK_BYTE_PTR        pPart,
		       CK_ULONG           ulPartLen )
{
	SESSION  * sess = NULL;
	CK_RV      rc   = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	// Netscape has been known to pass a null pPart with ulPartLen == 0...
	//
	if (!pPart && ulPartLen != 0) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->digest_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (pPart){
		rc = digest_mgr_digest_update( sess, &sess->digest_ctx, pPart, ulPartLen );
		if (rc != CKR_OK) {
			st_err_log(124, __FILE__, __LINE__);
		}
	}
 done:
	if (rc != CKR_OK)
		digest_mgr_cleanup( &sess->digest_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_DigestUpdate", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulPartLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DigestKey( ST_SESSION_HANDLE  sSession,
		    CK_OBJECT_HANDLE   hKey )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET

		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->digest_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	rc = digest_mgr_digest_key( sess, &sess->digest_ctx, hKey );
	if (rc != CKR_OK){ 
		st_err_log(124, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_OK)
		digest_mgr_cleanup( &sess->digest_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, key = %d\n", "C_DigestKey", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, hKey );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DigestFinal( ST_SESSION_HANDLE  sSession,
		      CK_BYTE_PTR        pDigest,
		      CK_ULONG_PTR       pulDigestLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pulDigestLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->digest_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pDigest)
		length_only = TRUE;

	rc = digest_mgr_digest_final( sess,    length_only,
				      &sess->digest_ctx,
				      pDigest, pulDigestLen );
	if (rc != CKR_OK){ 
		st_err_log(126, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		digest_mgr_cleanup( &sess->digest_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d\n", "C_DigestFinal", rc, (sess == NULL)?-1:(CK_LONG)sess->handle );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SignInit( ST_SESSION_HANDLE  sSession,
		   CK_MECHANISM_PTR   pMechanism,
		   CK_OBJECT_HANDLE   hKey )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism ){
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}
	VALID_MECH(pMechanism);

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->sign_ctx.active == TRUE) {
		rc = CKR_OPERATION_ACTIVE;
		st_err_log(31, __FILE__, __LINE__);
		goto done;
	}

	rc = sign_mgr_init( sess, &sess->sign_ctx, pMechanism, FALSE, hKey );
	if (rc != CKR_OK){ 
		st_err_log(127, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_SignInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_Sign( ST_SESSION_HANDLE  sSession,
	       CK_BYTE_PTR        pData,
	       CK_ULONG           ulDataLen,
	       CK_BYTE_PTR        pSignature,
	       CK_ULONG_PTR       pulSignatureLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pData || !pulSignatureLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->sign_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pSignature)
		length_only = TRUE;

	rc = sign_mgr_sign( sess,       length_only,
			    &sess->sign_ctx,
			    pData,      ulDataLen,
			    pSignature, pulSignatureLen );
	if (rc != CKR_OK){ 
		st_err_log(171, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		sign_mgr_cleanup( &sess->sign_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_Sign", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SignUpdate( ST_SESSION_HANDLE  sSession,
		     CK_BYTE_PTR        pPart,
		     CK_ULONG           ulPartLen )
{
	SESSION  * sess = NULL;
	CK_RV      rc   = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pPart) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->sign_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	rc = sign_mgr_sign_update( sess, &sess->sign_ctx, pPart, ulPartLen );
	if (rc != CKR_OK){ 
		st_err_log(128, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_OK)
		sign_mgr_cleanup( &sess->sign_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_SignUpdate", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulPartLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SignFinal( ST_SESSION_HANDLE  sSession,
		    CK_BYTE_PTR        pSignature,
		    CK_ULONG_PTR       pulSignatureLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pulSignatureLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->sign_ctx.active == FALSE) {
		st_err_log(32, __FILE__, __LINE__);
		rc = CKR_OPERATION_NOT_INITIALIZED;
		goto done;
	}

	if (!pSignature)
		length_only = TRUE;

	rc = sign_mgr_sign_final( sess,       length_only,
				  &sess->sign_ctx,
				  pSignature, pulSignatureLen );
	if (rc != CKR_OK){ 
		st_err_log(129, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		sign_mgr_cleanup( &sess->sign_ctx );

	LLOCK;
	if (debugfile)
	{
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d\n", "C_SignFinal", rc, (sess == NULL)?-1:(CK_LONG)sess->handle );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SignRecoverInit( ST_SESSION_HANDLE  sSession,
			  CK_MECHANISM_PTR   pMechanism,
			  CK_OBJECT_HANDLE   hKey )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pMechanism ){
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->sign_ctx.active == TRUE) {
		rc = CKR_OPERATION_ACTIVE;
		st_err_log(31, __FILE__, __LINE__);
		goto done;
	}

	rc = sign_mgr_init( sess, &sess->sign_ctx, pMechanism, TRUE, hKey );
	if (rc != CKR_OK){ 
		st_err_log(127, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_SignRecoverInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SignRecover( ST_SESSION_HANDLE  sSession,
		      CK_BYTE_PTR        pData,
		      CK_ULONG           ulDataLen,
		      CK_BYTE_PTR        pSignature,
		      CK_ULONG_PTR       pulSignatureLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		if (st_Initialized() == FALSE) {
			st_err_log(72, __FILE__, __LINE__);
			LOCKIT;
			rc = CKR_CRYPTOKI_NOT_INITIALIZED;
			goto done;
		}

	if (!pData || !pulSignatureLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if ((sess->sign_ctx.active == FALSE) || (sess->sign_ctx.recover == FALSE)) {
		rc = CKR_OPERATION_NOT_INITIALIZED;
		st_err_log(32, __FILE__, __LINE__);
		goto done;
	}

	if (!pSignature)
		length_only = TRUE;

	rc = sign_mgr_sign_recover( sess,       length_only,
				    &sess->sign_ctx,
				    pData,      ulDataLen,
				    pSignature, pulSignatureLen );
	if (rc != CKR_OK){ 
		st_err_log(186, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		sign_mgr_cleanup( &sess->sign_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_SignRecover", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_VerifyInit( ST_SESSION_HANDLE  sSession,
		     CK_MECHANISM_PTR   pMechanism,
		     CK_OBJECT_HANDLE   hKey )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pMechanism ){
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->verify_ctx.active == TRUE) {
		rc = CKR_OPERATION_ACTIVE;
		st_err_log(31, __FILE__, __LINE__);
		goto done;
	}

	rc = verify_mgr_init( sess, &sess->verify_ctx, pMechanism, FALSE, hKey );
	if (rc != CKR_OK){ 
		st_err_log(167, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_VerifyInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_Verify( ST_SESSION_HANDLE  sSession,
		 CK_BYTE_PTR        pData,
		 CK_ULONG           ulDataLen,
		 CK_BYTE_PTR        pSignature,
		 CK_ULONG           ulSignatureLen )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pData || !pSignature) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->verify_ctx.active == FALSE) {
		rc = CKR_OPERATION_NOT_INITIALIZED;
		st_err_log(32, __FILE__, __LINE__);
		goto done;
	}

	rc = verify_mgr_verify( sess,
				&sess->verify_ctx,
				pData,      ulDataLen,
				pSignature, ulSignatureLen );
	if (rc != CKR_OK){ 
		st_err_log(168, __FILE__, __LINE__);
	}

 done:
	verify_mgr_cleanup( &sess->verify_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_Verify", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulDataLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_VerifyUpdate( ST_SESSION_HANDLE  sSession,
		       CK_BYTE_PTR        pPart,
		       CK_ULONG           ulPartLen )
{
	SESSION  * sess = NULL;
	CK_RV      rc   = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pPart) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->verify_ctx.active == FALSE) {
		rc = CKR_OPERATION_NOT_INITIALIZED;
		st_err_log(32, __FILE__, __LINE__);
		goto done;
	}

	rc = verify_mgr_verify_update( sess, &sess->verify_ctx, pPart, ulPartLen );
	if (rc != CKR_OK){ 
		st_err_log(169, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_OK)
		verify_mgr_cleanup( &sess->verify_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, datalen = %d\n", "C_VerifyUpdate", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, ulPartLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_VerifyFinal( ST_SESSION_HANDLE  sSession,
		      CK_BYTE_PTR        pSignature,
		      CK_ULONG           ulSignatureLen )
{
	SESSION  * sess = NULL;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pSignature) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (sess->verify_ctx.active == FALSE) {
		rc = CKR_OPERATION_NOT_INITIALIZED;
		st_err_log(32, __FILE__, __LINE__);
		goto done;
	}

	rc = verify_mgr_verify_final( sess, &sess->verify_ctx, pSignature, ulSignatureLen );
	if (rc != CKR_OK){ 
		st_err_log(170, __FILE__, __LINE__);
	}

 done:
	verify_mgr_cleanup( &sess->verify_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d\n", "C_VerifyFinal", rc, (sess == NULL)?-1:(CK_LONG)sess->handle );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_VerifyRecoverInit( ST_SESSION_HANDLE  sSession,
			    CK_MECHANISM_PTR   pMechanism,
			    CK_OBJECT_HANDLE   hKey )
{
	SESSION   * sess = NULL;
	CK_RV       rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}
	if (!pMechanism ){
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	if (sess->verify_ctx.active == TRUE) {
		rc = CKR_OPERATION_ACTIVE;
		st_err_log(31, __FILE__, __LINE__);
		goto done;
	}

	rc = verify_mgr_init( sess, &sess->verify_ctx, pMechanism, TRUE, hKey );
	if (rc != CKR_OK){ 
		st_err_log(167, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_VerifyRecoverInit", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_VerifyRecover( ST_SESSION_HANDLE  sSession,
			CK_BYTE_PTR        pSignature,
			CK_ULONG           ulSignatureLen,
			CK_BYTE_PTR        pData,
			CK_ULONG_PTR       pulDataLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pSignature || !pulDataLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if ((sess->verify_ctx.active == FALSE) || (sess->verify_ctx.recover == FALSE)) {
		rc = CKR_OPERATION_NOT_INITIALIZED;
		st_err_log(32, __FILE__, __LINE__);
		goto done;
	}


	if (!pData)
		length_only = TRUE;

	rc = verify_mgr_verify_recover( sess,       length_only,
					&sess->verify_ctx,
					pSignature, ulSignatureLen,
					pData,      pulDataLen );
	if (rc != CKR_OK){ 
		st_err_log(187, __FILE__, __LINE__);
	}

 done:
	if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE))
		verify_mgr_cleanup( &sess->verify_ctx );

	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, recover len = %d, length_only = %d\n", "C_VerifyRecover", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, *pulDataLen, length_only );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DigestEncryptUpdate( ST_SESSION_HANDLE  sSession,
			      CK_BYTE_PTR        pPart,
			      CK_ULONG           ulPartLen,
			      CK_BYTE_PTR        pEncryptedPart,
			      CK_ULONG_PTR       pulEncryptedPartLen )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}


//
//
CK_RV SC_DecryptDigestUpdate( ST_SESSION_HANDLE  sSession,
			      CK_BYTE_PTR        pEncryptedPart,
			      CK_ULONG           ulEncryptedPartLen,
			      CK_BYTE_PTR        pPart,
			      CK_ULONG_PTR       pulPartLen )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}

	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}


//
//
CK_RV SC_SignEncryptUpdate( ST_SESSION_HANDLE  sSession,
			    CK_BYTE_PTR        pPart,
			    CK_ULONG           ulPartLen,
			    CK_BYTE_PTR        pEncryptedPart,
			    CK_ULONG_PTR       pulEncryptedPartLen )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}


//
//
CK_RV SC_DecryptVerifyUpdate( ST_SESSION_HANDLE  sSession,
			      CK_BYTE_PTR        pEncryptedPart,
			      CK_ULONG           ulEncryptedPartLen,
			      CK_BYTE_PTR        pPart,
			      CK_ULONG_PTR       pulPartLen )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}


//
//
CK_RV SC_GenerateKey( ST_SESSION_HANDLE     sSession,
		      CK_MECHANISM_PTR      pMechanism,
		      CK_ATTRIBUTE_PTR      pTemplate,
		      CK_ULONG              ulCount,
		      CK_OBJECT_HANDLE_PTR  phKey )
{
	SESSION       * sess = NULL;
	CK_RV           rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism || !phKey || (pTemplate == NULL && ulCount != 0)) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = key_mgr_generate_key( sess, pMechanism, pTemplate, ulCount, phKey );
	if (rc != CKR_OK){ 
		st_err_log(91, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		CK_ATTRIBUTE *attr = pTemplate;
		CK_ULONG      i;

		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, handle = %d, mech = %x\n", "C_GenerateKey", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, *phKey, pMechanism->mechanism );

		for (i = 0; i < ulCount; i++, attr++) {
			CK_BYTE *ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_GenerateKeyPair( ST_SESSION_HANDLE     sSession,
			  CK_MECHANISM_PTR      pMechanism,
			  CK_ATTRIBUTE_PTR      pPublicKeyTemplate,
			  CK_ULONG              ulPublicKeyAttributeCount,
			  CK_ATTRIBUTE_PTR      pPrivateKeyTemplate,
			  CK_ULONG              ulPrivateKeyAttributeCount,
			  CK_OBJECT_HANDLE_PTR  phPublicKey,
			  CK_OBJECT_HANDLE_PTR  phPrivateKey )
{
	SESSION       * sess = NULL;
	CK_RV           rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism || !phPublicKey || !phPrivateKey ||
	    (!pPublicKeyTemplate && (ulPublicKeyAttributeCount != 0)) ||
	    (!pPrivateKeyTemplate && (ulPrivateKeyAttributeCount != 0)))
	{
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = key_mgr_generate_key_pair( sess,                pMechanism,
					pPublicKeyTemplate,  ulPublicKeyAttributeCount,
					pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
					phPublicKey,         phPrivateKey );
	if (rc != CKR_OK){ 
		st_err_log(91, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		CK_ATTRIBUTE *attr = NULL;
		CK_ULONG      i;

		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, mech = %x\n", "C_GenerateKeyPair", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, pMechanism->mechanism );

		if (rc == CKR_OK) {
			stlogit2(debugfile, "   Public  handle:  %d\n", *phPublicKey );
			stlogit2(debugfile, "   Private handle:  %d\n", *phPrivateKey );
		}

		stlogit2(debugfile, "   Public Template:\n");

		attr = pPublicKeyTemplate;
		for (i = 0; i < ulPublicKeyAttributeCount; i++, attr++) {
			CK_BYTE *ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

		stlogit2(debugfile, "   Private Template:\n");

		attr = pPrivateKeyTemplate;
		for (i = 0; i < ulPrivateKeyAttributeCount; i++, attr++) {
			CK_BYTE *ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_WrapKey( ST_SESSION_HANDLE  sSession,
		  CK_MECHANISM_PTR   pMechanism,
		  CK_OBJECT_HANDLE   hWrappingKey,
		  CK_OBJECT_HANDLE   hKey,
		  CK_BYTE_PTR        pWrappedKey,
		  CK_ULONG_PTR       pulWrappedKeyLen )
{
	SESSION  * sess = NULL;
	CK_BBOOL   length_only = FALSE;
	CK_RV      rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism || !pulWrappedKeyLen) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	if (!pWrappedKey)
		length_only = TRUE;

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = key_mgr_wrap_key( sess,         length_only,
			       pMechanism,
			       hWrappingKey, hKey,
			       pWrappedKey,  pulWrappedKeyLen );
	if (rc != CKR_OK){ 
		st_err_log(188, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, encrypting key = %d, wrapped key = %d\n", "C_WrapKey", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, hWrappingKey, hKey );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_UnwrapKey( ST_SESSION_HANDLE     sSession,
		    CK_MECHANISM_PTR      pMechanism,
		    CK_OBJECT_HANDLE      hUnwrappingKey,
		    CK_BYTE_PTR           pWrappedKey,
		    CK_ULONG              ulWrappedKeyLen,
		    CK_ATTRIBUTE_PTR      pTemplate,
		    CK_ULONG              ulCount,
		    CK_OBJECT_HANDLE_PTR  phKey )
{
	SESSION        * sess = NULL;
	CK_ATTRIBUTE   * attr = NULL;
	CK_BYTE        * ptr  = NULL;
	CK_ULONG         i;
	CK_RV            rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism || !pWrappedKey ||
	    (!pTemplate && ulCount != 0) || !phKey)
	{
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = key_mgr_unwrap_key( sess,           pMechanism,
				 pTemplate,      ulCount,
				 pWrappedKey,    ulWrappedKeyLen,
				 hUnwrappingKey, phKey );
	if (rc != CKR_OK){ 
		st_err_log(189, __FILE__, __LINE__);
	}

 done:
//   if (rc == CKR_OBJECT_HANDLE_INVALID)  brkpt();
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, decrypting key = %d, unwrapped key = %d\n", "C_UnwrapKey", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, hUnwrappingKey, *phKey );

		attr = pTemplate;
		for (i = 0; i < ulCount; i++, attr++) {
			ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_DeriveKey( ST_SESSION_HANDLE     sSession,
		    CK_MECHANISM_PTR      pMechanism,
		    CK_OBJECT_HANDLE      hBaseKey,
		    CK_ATTRIBUTE_PTR      pTemplate,
		    CK_ULONG              ulCount,
		    CK_OBJECT_HANDLE_PTR  phKey )
{
	SESSION        * sess = NULL;
	CK_ATTRIBUTE   * attr = NULL;
	CK_BYTE        * ptr  = NULL;
	CK_ULONG         i;
	CK_RV            rc = CKR_OK;
	SESS_SET


		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pMechanism || (!pTemplate && ulCount != 0)) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}
	VALID_MECH(pMechanism);

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	if (pin_expired(&sess->session_info, nv_token_data->token_info.flags) == TRUE) {
		st_err_log(36, __FILE__, __LINE__);
		rc = CKR_PIN_EXPIRED;
		goto done;
	}
   
	rc = key_mgr_derive_key( sess,      pMechanism,
				 hBaseKey,  phKey,
				 pTemplate, ulCount );
	if (rc != CKR_OK){ 
		st_err_log(190, __FILE__, __LINE__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, sess = %d, base key = %d, mech = %x\n", "C_DeriveKey", rc, (sess == NULL)?-1:(CK_LONG)sess->handle, hBaseKey, pMechanism->mechanism );

		if (rc == CKR_OK) {
			switch (pMechanism->mechanism) {
			case CKM_SSL3_KEY_AND_MAC_DERIVE:
			{
				CK_SSL3_KEY_MAT_PARAMS *pReq;
				CK_SSL3_KEY_MAT_OUT    *pPtr;
				pReq = (CK_SSL3_KEY_MAT_PARAMS *)pMechanism->pParameter;
				pPtr = pReq->pReturnedKeyMaterial;

				stlogit2(debugfile, "   Client MAC key:  %d\n", pPtr->hClientMacSecret );
				stlogit2(debugfile, "   Server MAC key:  %d\n", pPtr->hServerMacSecret );
				stlogit2(debugfile, "   Client Key:      %d\n", pPtr->hClientKey );
				stlogit2(debugfile, "   Server Key:      %d\n", pPtr->hServerKey );
			}
			break;

			case CKM_DH_PKCS_DERIVE:
			{
				stlogit2(debugfile, "   DH Shared Secret:  \n" );
			}
			break ;

			default:
				stlogit2(debugfile, "   Derived key:     %d\n", *phKey );
			}
		}


		attr = pTemplate;
		for (i = 0; i < ulCount; i++, attr++) {
			ptr = (CK_BYTE *)attr->pValue;

			stlogit2(debugfile, "   %3d:  Attribute type:  0x%08x\n", i, attr->type );
			stlogit2(debugfile, "         Value Length:    %08d\n",   attr->ulValueLen );

			if (attr->ulValueLen != (CK_ULONG)(-1) && (ptr != NULL))
				stlogit2(debugfile, "         First 4 bytes:   %02x %02x %02x %02x", ptr[0], ptr[1], ptr[2], ptr[3] );

			stlogit2(debugfile, "\n\n");
		}

	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_SeedRandom( ST_SESSION_HANDLE  sSession,
		     CK_BYTE_PTR        pSeed,
		     CK_ULONG           ulSeedLen )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	return CKR_OK;
}


//
//
CK_RV SC_GenerateRandom( ST_SESSION_HANDLE  sSession,
			 CK_BYTE_PTR        pRandomData,
			 CK_ULONG           ulRandomLen )
{
	SESSION *sess = NULL;
	CK_RV    rc = CKR_OK;
	SESS_SET

		LOCKIT;
	if (st_Initialized() == FALSE) {
		st_err_log(72, __FILE__, __LINE__);
		rc = CKR_CRYPTOKI_NOT_INITIALIZED;
		goto done;
	}

	if (!pRandomData && ulRandomLen != 0) {
		st_err_log(5, __FILE__, __LINE__, __FUNCTION__);
		rc = CKR_ARGUMENTS_BAD;
		goto done;
	}

	sess = SESSION_MGR_FIND( hSession );
	if (!sess) {
		st_err_log(40, __FILE__, __LINE__);
		rc = CKR_SESSION_HANDLE_INVALID;
		goto done;
	}

	rc = rng_generate( pRandomData, ulRandomLen );
	if (rc != CKR_OK){ 
		st_err_log(130, __FILE__, __LINE__, __FUNCTION__);
	}

 done:
	LLOCK;
	if (debugfile) {
		stlogit2(debugfile, "%-25s:  rc = %08x, %d bytes\n", "C_GenerateRandom", rc, ulRandomLen );
	}

	UNLOCKIT; return rc;
}


//
//
CK_RV SC_GetFunctionStatus( ST_SESSION_HANDLE  sSession )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	st_err_log(17, __FILE__, __LINE__);
	return CKR_FUNCTION_NOT_PARALLEL;
}


//
//
CK_RV SC_CancelFunction( ST_SESSION_HANDLE  sSession )
{
	SESS_SET
		if (st_Initialized() == FALSE){
			st_err_log(72, __FILE__, __LINE__);
			return CKR_CRYPTOKI_NOT_INITIALIZED;
		}
	st_err_log(17, __FILE__, __LINE__);
	return CKR_FUNCTION_NOT_PARALLEL;
}

//
//
CK_RV QueryTweakValues( void )
{
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}


//
//
CK_RV UpdateTweakValues( void )
{
	st_err_log(142, __FILE__, __LINE__, __FUNCTION__);
	return CKR_FUNCTION_NOT_SUPPORTED;
}

// Added for AIX work
void
SC_SetFunctionList(void){

	function_list.ST_Initialize          = (void *)ST_Initialize;
	function_list.ST_GetTokenInfo        = SC_GetTokenInfo;
	function_list.ST_GetMechanismList    = SC_GetMechanismList;
	function_list.ST_GetMechanismInfo    = SC_GetMechanismInfo;
	function_list.ST_InitToken           = SC_InitToken;
	function_list.ST_InitPIN             = SC_InitPIN;
	function_list.ST_SetPIN              = SC_SetPIN;
	function_list.ST_OpenSession         = SC_OpenSession;
	function_list.ST_CloseSession        = SC_CloseSession;
	function_list.ST_GetSessionInfo      = SC_GetSessionInfo;
	function_list.ST_GetOperationState   = SC_GetOperationState;
	function_list.ST_SetOperationState   = SC_SetOperationState;
	function_list.ST_Login               = SC_Login;
	function_list.ST_Logout              = SC_Logout;
	function_list.ST_CreateObject        = SC_CreateObject;
	function_list.ST_CopyObject          = SC_CopyObject;
	function_list.ST_DestroyObject       = SC_DestroyObject;
	function_list.ST_GetObjectSize       = SC_GetObjectSize;
	function_list.ST_GetAttributeValue   = SC_GetAttributeValue;
	function_list.ST_SetAttributeValue   = SC_SetAttributeValue;
	function_list.ST_FindObjectsInit     = SC_FindObjectsInit;
	function_list.ST_FindObjects         = SC_FindObjects;
	function_list.ST_FindObjectsFinal    = SC_FindObjectsFinal;
	function_list.ST_EncryptInit         = SC_EncryptInit;
	function_list.ST_Encrypt             = SC_Encrypt;
	function_list.ST_EncryptUpdate       = SC_EncryptUpdate;
	function_list.ST_EncryptFinal        = SC_EncryptFinal;
	function_list.ST_DecryptInit         = SC_DecryptInit;
	function_list.ST_Decrypt             = SC_Decrypt;
	function_list.ST_DecryptUpdate       = SC_DecryptUpdate;
	function_list.ST_DecryptFinal        = SC_DecryptFinal;
	function_list.ST_DigestInit          = SC_DigestInit;
	function_list.ST_Digest              = SC_Digest;
	function_list.ST_DigestUpdate        = SC_DigestUpdate;
	function_list.ST_DigestKey           = SC_DigestKey;
	function_list.ST_DigestFinal         = SC_DigestFinal;
	function_list.ST_SignInit            = SC_SignInit;
	function_list.ST_Sign                = SC_Sign;
	function_list.ST_SignUpdate          = SC_SignUpdate;
	function_list.ST_SignFinal           = SC_SignFinal;
	function_list.ST_SignRecoverInit     = SC_SignRecoverInit;
	function_list.ST_SignRecover         = SC_SignRecover;
	function_list.ST_VerifyInit          = SC_VerifyInit;
	function_list.ST_Verify              = SC_Verify;
	function_list.ST_VerifyUpdate        = SC_VerifyUpdate;
	function_list.ST_VerifyFinal         = SC_VerifyFinal;
	function_list.ST_VerifyRecoverInit   = SC_VerifyRecoverInit;
	function_list.ST_VerifyRecover       = SC_VerifyRecover;
	function_list.ST_DigestEncryptUpdate = NULL; // SC_DigestEncryptUpdate;
	function_list.ST_DecryptDigestUpdate = NULL; // SC_DecryptDigestUpdate;
	function_list.ST_SignEncryptUpdate   = NULL; //SC_SignEncryptUpdate;
	function_list.ST_DecryptVerifyUpdate = NULL; // SC_DecryptVerifyUpdate;
	function_list.ST_GenerateKey         = SC_GenerateKey;
	function_list.ST_GenerateKeyPair     = SC_GenerateKeyPair;
	function_list.ST_WrapKey             = SC_WrapKey;
	function_list.ST_UnwrapKey           = SC_UnwrapKey;
	function_list.ST_DeriveKey           = SC_DeriveKey;
	function_list.ST_SeedRandom          = SC_SeedRandom ;
	function_list.ST_GenerateRandom      = SC_GenerateRandom;
	function_list.ST_GetFunctionStatus   = NULL; // SC_GetFunctionStatus;
	function_list.ST_CancelFunction      = NULL; // SC_CancelFunction;
}
