aboutsummaryrefslogblamecommitdiff
path: root/pkcs11.c
blob: 255069a92df9666e7eed26eccb7f8b786bc572a3 (plain) (tree)















































                                                                        
                




















































                                                                           






                         
                 
                         


                    
                         

      









                                                                






                                              











                                                    

                   


                  



                    






                    




                                                                      
  





                                                                     
  



                                                                      

   







                                                                           
                             





                                                                  














































                                                                               








                                                                      






















                                                                     



                                                                  






                                                                     

   

                






                                                                         


















                                                                         
                                                    






                                     




                                   
 



                                                                         


                      

                                                                         

                       








                                                                         
  
                                   

   
             
 
                                                                                                                   


                    
                                                                                      
           

 
                                                                             
 
                      
 


                                              



  































                                                                                       




                                                 

             





                                                 




                                            
                













































































































                                                               
                         



  














































                                                                  



















                                                      

























































                                                                   
  


                                                                   

   


                                                   
 
                                 
 

                       
 


                   
 
 

  



                                                                      
  



                                                                      

   
                         
 

                                      
 

                                  
 
                         
 

                                                                  
 

                                       
 






                                                                  
 
                                      

   

                      







































































































































































































































                                                                                                                            


                                                                 

   



                                                








                                                                                                                 
                                     


                                                                     
                                     


                                                                                 
                                    


















































































                                                                                                             



                                                            




















                                                                                       
                                                                 










                                                                                                      









                                                                  



                                                                        

                                                                  


























                                                                  






                                                                                                  
             




































































                                                                                    

                                                







                                                                          

                                                                          

                                                                          


                                                                          









                                                                          

                                                                          


                                                                          


                                                                          











































































                                                                                                                  
                            
  




                                                                      

   

                                                                               
 

                                  

                                     

                                                                                       
 
                                                                                  
                                                                            
                                                


                         


                                                                                  

                                                                                  

                                                            
                                                                                  

                                                                                  
                                                                                  

                                                                                  

              


         
                                      




                      
                            
  


                                                                  
  
                                                         

   


                                                                                   
 

                                                                                                   
 




                                                                            

    
                                               

     





                                                                  
 









                                                           

                                   
 










                                                                                              

 







































                                                                                                         















                                                    











                                                    
                                               
































































                                                                              





                                                                       





                                                            


                                      


















                                                                  

   

                                                 

                                         


                                           

                         










                                     





































































































                                                                                                                          




                                                                                                   




















































                                                                                                               


                                                                



















                                                                      
                                                        
  

                                                                      










                                                                                            





























                                                                                                          










                                                                                                           










                                                                     











































                                                                                                   







                                          
                       
















                                                                                                                      
















                                                                    
                                                                                  




















                                                                     



     

                                                                    
     
                                                                    


                                   
                                                          

     










                                                                                                  
                                                                         




                                                                                                  

                              
                         
















                                                  
                                    






                            
  


























































































                                                                                             














                                                                                           
                                                                           

                             









                                                                                           







































                                                                                             
 







                                         

                                      
                                         
                          

           



































                                                                      
                  




                                                               


                                 
                                                                    





                                                              



                                                                     

       







                                                   
                




                                             

                          
     


    












                                                                     








                                                                
                  
                            
 



                             



                







                                       

                                    

                    


                             
                                                 
 










                                  
                            

    











                                                                   



                                                                

                                           


                                                                      

                                                                




















                                                       

                                       

                                               
                                                    


















                                             

                                        



                                                                        


























































































                                                                      

                                       




                                                                 
                                                 
 




































                                                                                       


                                                      




                               
                                        




                                                

                                        

           
                                                 
 
                                    
 
                                                    



                                           

                                            


                                      
                                                 
 

                           
                                        






                                         

                                 


                                                                 
                         

                         
                    
 
                                                 
 
                   
                            







                                                                      
                                     

    




                                                                    
                                         

    
                                         
                                                  
    



                                                                     




                      

                                                                          
                                           






                                        

    



                                                                       

     





















                                                                             
 
   















                                                                              
                              
   

    

                                                                     






















                                                                                        
      
                      
                                                    



                                          

                                  
                         
                    
 
                                                 
 






                                                                      
                                     
 

                                    




























                                                                          
      
                                                    




                                                 

                                         



                                                  


                                                                                            
 
                         
                         

                    
                                                 

                                       



                                                                                          

                                                                  


                                                                  

                              

                                                                  




                                       
                                                    






                                                     

                                             





                                                                      
                         





                                            
           

             
                                                 
 


                            

                                       



























                                                                                         

              

































                                                                                        
                                                    





                                                   

                                           





























                                                                                        
                         



                                      
                                                 

                                                     































                                                                                            






                                                                      


                                                                  


                                                                  



                                                                  


                              


                                                                    


                                          




                                                                  































                                                                                
                                                    






                                                  

                                       
                         


                         
                                                 

                                                     





































                                                                                           
                                                    



                                                    

                                            


                                            
                         


                         
                                                 

                                                     








                                               

                                                                          




                                       
                                                    




                                               

                                      
                         
                    
 
                                                 


                                                     

                         
                            
 
                                         
                               

                                  



                                                                            
                                                    

   



                                                                      
 
                                        

      
                                                    







                                          

                                  

                         
 
                                                 


                                                     

                                            
                            
 
                                         
                                        
 
                                                                                                 
 
                                                            
 

                                          
 
                                 

                               


                                                                    
 





                                                                                  



                                                  

                                      
                                                    





                                             

                                    
                         
                    
 
                                                 


                                                     

                         
                            
 
                                                                                               
                               



                                                                                      






                                                                   
                                  




                                                                                              


                                                             
                                        

      


                                                 
   
                                                    







                                          

                                

                                     
                         
                       

           
                                                 


                                                     

                                               
                            
 
                                                    
                                        
 



                                                                
 


                                                                    
 



                                                                 
 

                                                                        
 
                                                                        
 
                                   
 


                                          

   

                                 
 
                                                





                                                                                        
 






                                                                                         






























                                                                                         
 

                            
 

                                                                                                   
 

                                                                                      
 






















































                                                                                              

                                                                   


                                                  




                                                                                        
                                
 
                                                                                            





                                                
                                                                                            
                

   



                                                  
                        

                                                   
   
 
                         
 
                                                    


  





                                                                    








                                                             

                                           

                         
 
                                                 


                                                     



                                                            
                            

                                  
 
                                 





                                                                                   
          
                                
   

      
                                                    





                                                  

                                          
                         

                    
                                                 


                                                     

                         
                            
 
                                                          


                              
                                                    

 















                                                                      

                                            






























                                                  
 


  











                                                                      




                                                



                                       
 
                                  



                                    


                                           



                                       



                                                              



                                            
 



                                         



                                     



                                           



                                    





                                          



                                    


                                                  



                                          



                                                            



                                             





                                                              



                                             




                                                   



                                        





                                                    



                                      



                                                 



                                         




                                                     



                                             



                                                



                                       





                                                 



                                    





                                                       



                                         



                                                          



                                        



                                                



                                       





                                            



                                    





                                                  



                                         



                                                 



                                        



                                                



                                        


                                             



                                     



                                               



                                       



                                              



                                      



                                               



                                     



                                                    



                                           





                                                 



                                       



                                                



                                        



                                               



                                       



                                                      



                                             





                                                 



                                         





                                                             



                                               





                                                        



                                               





                                                           



                                             





                                                        



                                               






                                              



                                    








                                                  



                                     






                                              



                                     



                                              



                                      

                                                     



                                             

                                                  



                                          



                                              



                                            











                                                                            
/* 
 * pkcs11.c
 * --------
 *
 * This is a partial implementation of PKCS #11 on top of Cryptlib on
 * top of a HAL connecting to the Cryptech FPGA cores.
 *
 * This is still at a very early stage and should not (yet?) be used
 * for any serious purpose.  Among other things, it's not yet entirely
 * clear whether this approach really is workable.
 *
 * Author: Rob Austein
 * Copyright (c) 2015, SUNET
 * 
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h> 
#include <assert.h>

#include <sqlite3.h>

#include <hal.h>

/*
 * Magic PKCS #11 macros that must be defined before including
 * pkcs11.h.  For now these are only the Unix versions, add others
 * later (which may require minor refactoring).
 */

#define CK_PTR                                          *
#define CK_DEFINE_FUNCTION(returnType, name)            returnType name
#define CK_DECLARE_FUNCTION(returnType, name)           returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name)   returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name)          returnType (* name)
#ifndef NULL_PTR
#define NULL_PTR                                        NULL
#endif

#include "pkcs11.h"
#include "attributes.h"

/*
 * This PKCS #11 implementation is hardwired with one slot, the token
 * for which is always present (so we return the same answer
 * regardless of the value of tokenPresent).
 */

#define P11_ONE_AND_ONLY_SLOT   0

/*
 * Placeholders for PIN length limits.  Figure out real values later.
 */

#warning Figure out PIN length limits
#define P11_MIN_PIN_LENGTH      16
#define P11_MAX_PIN_LENGTH      4096

/*
 * Version numbers.  Placeholders for now.  Cryptlib has a version
 * number, but from PKCS #11's point of view, Cryptlib is part of the
 * "hardware", and we're probably going to need something other than
 * Cryptlib's version number for the hardware, because we have to
 * represent the version number of the attached Cryptech FPGA cores.
 *
 * Software version number is just the version of this PKCS #11
 * implementation.  Probably.
 */

#warning Figure out hardware and software version numbers
#define P11_VERSION_SW_MAJOR    0
#define P11_VERSION_SW_MINOR    0
#define P11_VERSION_HW_MAJOR    0
#define P11_VERSION_HW_MINOR    0

/*
 * Debugging control.
 */

#ifndef DEBUG_SQL
#define DEBUG_SQL       1
#endif

#ifndef DEBUG_HAL
#define DEBUG_HAL       1
#endif

#ifndef DEBUG_PKCS11
#define DEBUG_PKCS11    2
#endif

/*
 * Default filename for SQL database lives.  Can be overriden at
 * runtime by setting PKCS11_DATABASE environment variable.
 */

#ifndef SQL_DATABASE
#define SQL_DATABASE ".cryptech-pkcs11.db"
#endif

/*
 * Whether to include POSIX-specific features.
 */

#ifndef USE_POSIX
#define USE_POSIX 1
#endif

/*
 * Whether to use POSIX threads.
 */

#ifndef USE_PTHREADS
#define USE_PTHREADS USE_POSIX
#endif

#if USE_PTHREADS && !USE_POSIX
#error Can not use POSIX threads without using POSIX
#endif

#if USE_POSIX
#include <unistd.h>
#include <errno.h>
#endif

#if USE_PTHREADS
#include <pthread.h>
#endif



/*
 * PKCS #11 session.
 */

/*
 * At present we have no concept of encryption or signature algorithms
 * in libhal, as we only support RSA and AES.  For PKCS #11 purposes
 * we can figure out what kind of key we're looking at from attributes
 * like CKA_KEY_TYPE, so it's just something we look up given the key
 * object handle.
 *
 * At the moment we don't need to keep any signature or digest state
 * in the session structure, which is good since the current hash
 * cores don't allow us to extract state anyway.  This makes it
 * impossible to implement the incremental operations
 * (C_DigestUpdate(), C_SignUpdate()) but also simplifies our current
 * task.
 *
 * General idea is that we have separate descriptors/handles/state for
 * each operation that we're allowed to do in parallel, so sign,
 * verify, digest, encrypt, decrypt, wrapkey, and unwrapkey all need
 * separate slots in the session structure.  Add these as we go.
 */

typedef struct p11_session {
  CK_SESSION_HANDLE handle;             /* Session handle */
  struct p11_session *link;             /* Next session in list */
  CK_STATE state;                       /* State (CKS_*) of this session */
  CK_NOTIFY notify;                     /* Notification callback */
  CK_VOID_PTR application;              /* Application data */
  sqlite3_stmt *find_query;             /* FindObject*() query state */
  int find_query_done;                  /* find_query has terminated */
  const hal_hash_descriptor_t
    *digest_descriptor,                 /* Hash for C_Digest*() */
    *sign_digest_descriptor,            /* Hash for C_Sign*() */
    *verify_digest_descriptor;          /* Hash for C_Verify*() */
  CK_OBJECT_HANDLE
    sign_key_handle,                    /* Key  for C_Sign*() */
    verify_key_handle;                  /* Key  for C_Verify() */
} p11_session_t;

/*
 * PKCS #11 handle management.  PKCS #11 has two kinds of handles:
 * session handles and object handles.  We subdivide object handles
 * into token object handles (handles for objects that live on the
 * token) and session object handles (handles for objects that live
 * only as long as the session does), and we steal a bit of the object
 * handle as a flag to distinguish between our two kinds of object
 * handles, considerably simplifing the objected-related SQL code.
 */

typedef enum {
  handle_flavor_session,
  handle_flavor_token_object,
  handle_flavor_session_object
} handle_flavor_t;

#define FLAG_HANDLE_TOKEN               0x80000000

#define is_token_handle(_handle_)       (((_handle_) & FLAG_HANDLE_TOKEN) != 0)



/*
 * Current logged-in user.
 */

static enum {
  not_logged_in,
  logged_in_as_user,
  logged_in_as_so
} logged_in_as = not_logged_in;

/*
 * PKCS #11 sessions for this application.
 */

static p11_session_t *p11_sessions;

/*
 * SQL database.
 */

static sqlite3 *sqldb = NULL;

/*
 * Next PKCS #11 handle to allocate.  We use a single handle space for
 * both session and object handles, and we just keep incrementing
 * until it wraps, to reduce the amount of time we have to spend
 * on SQL probes to avoid handle conflicts.
 */

static CK_ULONG next_handle;

/*
 * Mutex callbacks.
 */

static CK_CREATEMUTEX  mutex_cb_create;
static CK_DESTROYMUTEX mutex_cb_destroy;
static CK_LOCKMUTEX    mutex_cb_lock;
static CK_UNLOCKMUTEX  mutex_cb_unlock;

/*
 * Global mutex.  We may want something finer grained later, but this
 * will suffice to comply with the API requirements.
 */

static CK_VOID_PTR p11_global_mutex;

/*
 * (POSIX-specific) process which last called C_Initialize().
 */

#if USE_POSIX
static pid_t initialized_pid;
#endif



/*
 * Syntactic sugar for functions returning CK_RV complex enough to
 * need cleanup actions on failure.  Also does very basic logging for
 * debug-by-printf().
 *
 * NB: This uses a variable ("rv") and a goto target ("fail") which
 * must be defined in the calling environment.  We could make these
 * arguments to the macro, but doing so would make the code less
 * readable without significantly reducing the voodoo factor.
 */

#if DEBUG_PKCS11

#define lose(_ck_rv_code_)                                              \
  do {                                                                  \
    rv = (_ck_rv_code_);                                                \
    fprintf(stderr, "%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_);  \
    goto fail;                                                          \
  } while (0)

#else  /* DEBUG_PKCS11 */

#define lose(_ck_rv_code_)                                              \
  do {                                                                  \
    rv = (_ck_rv_code_);                                                \
    goto fail;                                                          \
  } while (0)

#endif  /* DEBUG_PKCS11 */

/*
 * More debug-by-printf() support.  One would like to consider this a
 * relic of the previous millenium, but, sadly, broken debugging
 * environments are still all too common.
 */

#if DEBUG_PKCS11 > 1

#define ENTER_PUBLIC_FUNCTION(_name_) \
  fprintf(stderr, "Entering function %s\n", #_name_)

#else  /* DEBUG_PKCS11 > 1 */

#define ENTER_PUBLIC_FUNCTION(_name_)

#endif  /* DEBUG_PKCS11 > 1 */

/*
 * Error checking for SQLite calls.
 */

#if DEBUG_SQL

#define sql_whine(_expr_)                                               \
  (fprintf(stderr, "%s:%u: %s returned %s\n",                           \
           __FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)),         \
   sql_breakpoint())

#else  /* DEBUG_SQL */

#define sql_whine(_expr_)                                               \
  ((void) 0)

#endif  /* DEBUG_SQL */

#define sql_check(_good_, _expr_)                                       \
  ((_expr_) == (_good_) ? 1 : (sql_whine(_expr_), 0))

#define sql_check_ok(_expr_)    sql_check(SQLITE_OK, _expr_)
#define sql_check_row(_expr_)   sql_check(SQLITE_ROW, _expr_)
#define sql_check_done(_expr_)  sql_check(SQLITE_DONE, _expr_)
#define sql_whine_step()        sql_whine(sqlite3_step())

/*
 * Error checking for libhal calls.
 */

#if DEBUG_HAL

static int _hal_check(const hal_error_t err, const char * const expr, const char * const file, const unsigned line)
{
  if (err == HAL_OK)
    return 1;
  fprintf(stderr, "%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err));
  return 0;
}

#define hal_check(_expr_) (_hal_check((_expr_), #_expr_, __FILE__, __LINE__))

#else  /* DEBUG_HAL */

#define hal_check(_expr_) ((_expr_) == HAL_OK)

#endif  /* DEBUG_HAL */



/*
 * Thread mutex utilities.  We need to handle three separate cases:
 *
 * 1) User doesn't care about mutexes;
 * 2) User wants us to use "OS" mutexes;
 * 3) User wants us to use user-specified mutexs.
 *
 * For "OS" mutexes, read POSIX Threads mutexes, at least for now.
 *
 * PKCS #11 sort of has a fourth case, but it's really just license
 * for us to pick either the second or third case at whim.
 *
 * To simplify the rest of the API, we provide a POSIX-based
 * implementation which uses the same API an user-provided mutex
 * implementation would be required to use, use null function pointers
 * to represent the case where the user doesn't need mutexes at all,
 * and wrap the whole thing in trivial macros to insulate the rest of
 * the code from the grotty details.
 */

/*
 * Basic macros.
 */

#define mutex_create(_m_)   (mutex_cb_create  == NULL ? CKR_OK : mutex_cb_create(_m_))
#define mutex_destroy(_m_)  (mutex_cb_destroy == NULL ? CKR_OK : mutex_cb_destroy(_m_))
#define mutex_lock(_m_)     (mutex_cb_lock    == NULL ? CKR_OK : mutex_cb_lock(_m_))
#define mutex_unlock(_m_)   (mutex_cb_unlock  == NULL ? CKR_OK : mutex_cb_unlock(_m_))

/*
 * Slightly higher-level macros for common operations.
 */

#define mutex_lock_or_return_failure(_m_)       \
  do {                                          \
    CK_RV _rv = mutex_lock(_m_);                \
    if (_rv != CKR_OK)                          \
      return _rv;                               \
  } while (0)

#define mutex_unlock_return_with_rv(_rv_, _m_)  \
  do {                                          \
    CK_RV _rv1 = _rv_;                          \
    CK_RV _rv2 = mutex_unlock(_m_);             \
    return _rv1 == CKR_OK ? _rv2 : _rv1;        \
  } while (0)

/*
 * Mutex implementation using POSIX mutexes.
 */

#if USE_PTHREADS

static CK_RV posix_mutex_create(CK_VOID_PTR_PTR ppMutex)
{
  pthread_mutex_t *m = NULL;
  CK_RV rv;

  if (ppMutex == NULL)
    lose(CKR_GENERAL_ERROR);

  if ((m = malloc(sizeof(*m))) == NULL)
    lose(CKR_HOST_MEMORY);

  switch (pthread_mutex_init(m, NULL)) {

  case 0:
    *ppMutex = m;
    return CKR_OK;

  case ENOMEM:
    lose(CKR_HOST_MEMORY);

  default:
    lose(CKR_GENERAL_ERROR);
  }  

 fail:
  if (m != NULL)
    free(m);
  return rv;
}

static CK_RV posix_mutex_destroy(CK_VOID_PTR pMutex)
{
  CK_RV rv;

  if (pMutex == NULL)
    lose(CKR_MUTEX_BAD);

  switch (pthread_mutex_destroy(pMutex)) {

  case 0:
    free(pMutex);
    return CKR_OK;

  case EINVAL:
    lose(CKR_MUTEX_BAD);

  case EBUSY:
    /*
     * PKCS #11 mutex semantics are a bad match for POSIX here,
     * leaving us only the nuclear option.  Feh.  Fall through.
     */

  default:
    lose(CKR_GENERAL_ERROR);
  }

 fail:
  return rv;
}

static CK_RV posix_mutex_lock(CK_VOID_PTR pMutex)
{
  CK_RV rv;

  if (pMutex == NULL)
    lose(CKR_MUTEX_BAD);

  switch (pthread_mutex_lock(pMutex)) {

  case 0:
    return CKR_OK;

  case EINVAL:
    lose(CKR_MUTEX_BAD);

  default:
    lose(CKR_GENERAL_ERROR);
  }

 fail:
  return rv;
}

static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex)
{
  CK_RV rv;

  if (pMutex == NULL)
    lose(CKR_MUTEX_BAD);

  switch (pthread_mutex_unlock(pMutex)) {

  case 0:
    return CKR_OK;

  case EINVAL:
    lose(CKR_MUTEX_BAD);

  case EPERM:
    lose(CKR_MUTEX_NOT_LOCKED);

  default:
    lose(CKR_GENERAL_ERROR);
  }

 fail:
  return rv;
}

#endif /* USE_PTHREADS */



/*
 * SQL utilities.
 */

/*
 * Hook on which to hang a debugger breakpoint on SQL errors.
 */

#if DEBUG_SQL
static void sql_breakpoint(void)
{
  fprintf(stderr, "[sql_breakpoint]\n");
}
#endif

/*
 * Execute SQL code that doesn't require a prepared query.
 */

static int sql_exec(const char *cmd)
{
  char *msg = NULL;

  if (sql_check_ok(sqlite3_exec(sqldb, cmd, NULL, NULL, &msg)))
    return 1;

#if DEBUG_SQL
  if (msg != NULL)
    fprintf(stderr, "[%s]\n", msg);
#endif

  return 0;
}

/*
 * Initialize SQL.  This includes loading our schema, portions of
 * which live in the temp (memory) database thus always need to be
 * created on startup.
 */

static int sql_init(void)
{
  static const char schema[] =
#include "schema.h"
    ;

  assert(sqldb == NULL);

  const char * const env  = getenv("PKCS11_DATABASE");
  const char * const home = getenv("HOME");
  const char * const base = SQL_DATABASE;
  int ok;

  if (env != NULL) {
    ok = sql_check_ok(sqlite3_open(env, &sqldb));
  }

  else if (home == NULL) {
    ok = sql_check_ok(sqlite3_open(base, &sqldb));
  }

  else {
    char fn[strlen(home) + strlen(base) + 2];
    snprintf(fn, sizeof(fn), "%s/%s", home, base);
    ok = sql_check_ok(sqlite3_open(fn, &sqldb));
  }

  return ok && sql_exec(schema);
}

/*
 * Shut down SQL.
 *
 * Yes, this can return failure, although it's not clear what we're
 * meant to do about that if the application is going to shut down
 * regardless of what we do.  I guess we could loop retrying a few
 * times for errors like SQLITE_BUSY, but that's about it.
 */

static int sql_fini(void)
{
  if (!sql_check_ok(sqlite3_close(sqldb)))
    return 0;

  sqldb = NULL;
  return 1;
}

/*
 * GCC attribute declaration to help catch format string errors,
 * ignored by other compilers.
 */

#ifdef __GNUC__
static int sql_prepare(sqlite3_stmt **q,
                       const char *format, ...)
  __attribute__ ((format (printf, 2, 3)));
#endif

/*
 * Prepare an SQLite3 query, using vsnprintf() to format the query.
 *
 *             WARNING WARNING WARNING WARNING
 *
 * Do not use this formatting mechanism for anything involving
 * user-supplied data.  It's only intended to handle things like
 * selecting between two parallel table structures or queries using
 * manifest constants that are only available in C header files.
 */

static int sql_prepare(sqlite3_stmt **q, const char *format, ...)
{
  char buffer[2048];
  va_list ap;
  size_t n;

  va_start(ap, format);
  n = vsnprintf(buffer, sizeof(buffer), format, ap);
  va_end(ap);

  if (n >= sizeof(buffer))
    return SQLITE_TOOBIG;

  return sqlite3_prepare_v2(sqldb, buffer, -1, q, NULL);
}

/*
 * This idiom occurs frequently, bundle it so we have the option of
 * doing it along with the normal conditional control flow that SQL
 * queries seem to follow.
 */

static int sql_finalize_and_clear(sqlite3_stmt **q)
{
  assert(q != NULL);

  int err = sqlite3_finalize(*q);

  if (err != SQLITE_OK)
    return err;

  *q = NULL;
  return SQLITE_OK;
}



/*
 * Initialize KEK.  If we had proper hardware support the KEK would be
 * living in special RAM where we could wipe it if anything triggered
 * our tamper circuitry.  But we have no such at the moment, so we
 * have no good place to store the KEK.
 *
 * So we store it in the SQL database, which kind of defeats the point
 * of wrapping private keys that live in the same database -- except
 * that we're trying to get all the other bits right so that we can
 * just move the KEK to secure memory once we have it.
 */

static int kek_init(void)
{
  static const char test_kek[] =
    " SELECT kek IS NULL FROM global";

  static const char set_kek[] =
    " UPDATE global SET kek = ?1";

  sqlite3_stmt *q = NULL;

  int ok = (sql_check_ok(sql_prepare(&q, test_kek))             &&
            sql_check_row(sqlite3_step(q)));

  if (ok && sqlite3_column_int(q, 0)) {
    uint8_t kekbuf[bitsToBytes(256)];

    ok = (hal_check(hal_get_random(kekbuf, sizeof(kekbuf)))	&&
          sql_check_ok(sql_finalize_and_clear(&q))              &&
          sql_check_ok(sql_prepare(&q, set_kek))		&&
          sql_check_ok(sqlite3_bind_blob(q, 1, kekbuf,
                                         sizeof(kekbuf),
                                         NULL))                 &&
          sql_check_done(sqlite3_step(q)));

    memset(kekbuf, 0, sizeof(kekbuf));
  }

  sqlite3_finalize(q);
  return ok;
}



/*
 * Find an unused handle.
 *
 * Note that zero is an excluded value (CK_INVALID_HANDLE), hence the
 * slightly odd arithmetic.
 *
 * For object handles, we steal the high-order bit to flag whether the
 * handle represents a session object or token object.
 */

static CK_ULONG p11_allocate_unused_handle(const handle_flavor_t flavor)
{
  static const char select_format[] =
    " SELECT %s_id FROM %s WHERE %s_handle = ?";

  const char *table = flavor == handle_flavor_session ? "session" : "object";
  sqlite3_stmt *q = NULL;
  CK_ULONG handle;
  int ret;

  if (!sql_check_ok(sql_prepare(&q, select_format, table, table, table)))
    goto fail;

  for (;;) {

    handle = ++next_handle;
    next_handle %= 0xFFFFFFFF;

    switch (flavor) {
    case handle_flavor_session:
      break;
    case handle_flavor_token_object:
      handle |= FLAG_HANDLE_TOKEN;
      break;
    case handle_flavor_session_object:
      handle &= ~FLAG_HANDLE_TOKEN;
      break;
    }

    assert(handle != CK_INVALID_HANDLE);

    if (!sql_check_ok(sqlite3_reset(q)) ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, handle)))
      goto fail;

    if ((ret = sqlite3_step(q)) == SQLITE_ROW)
      continue;

    if (ret == SQLITE_DONE)
      break;

    sql_whine_step();
    goto fail;

  }

  sqlite3_finalize(q);
  return handle;

 fail:
  sqlite3_finalize(q);
  return CK_INVALID_HANDLE;
}

/*
 * Translate CKA_TOKEN value to handle flavor.
 */

static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool)
{
  assert(bbool != NULL);
  return  *bbool ? handle_flavor_token_object : handle_flavor_session_object;
}



/*
 * Attribute methods.
 */

/*
 * Set an attribute for a given object.
 *
 * It would be trivial to generalize this to take a CK_ATTRIBUTE_PTR
 * template instead of a single attribute, at the cost of losing the
 * const specifiers (CK_ATTRIBUTE_PTR has an internal non-const void*).
 */

static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle,
                             const CK_ATTRIBUTE_TYPE type,
                             const void * const value,
                             const CK_ULONG length)
{
  static const char insert_format[] =
    " INSERT OR REPLACE INTO %s_attribute (%s_object_id, type, value)"
    " VALUES ((SELECT %s_object_id FROM object WHERE object_handle = ?1), ?2, ?3)";

  const char *flavor = is_token_handle(object_handle) ? "token" : "session";

  sqlite3_stmt *q = NULL;
  int ok = 0;

  if (!sql_check_ok(sql_prepare(&q, insert_format, flavor, flavor, flavor))     ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                    ||
      !sql_check_ok(sqlite3_bind_int64(q, 2, type))                             ||
      !sql_check_ok(sqlite3_bind_blob( q, 3, value, length, NULL))              ||
      !sql_check_done(sqlite3_step(q)))
    goto fail;

  ok = 1;

 fail:
  sqlite3_finalize(q);
  return ok;
}

/*
 * Get a single attribute from a given object.
 *
 * This could easily be generalized to take a CK_ATTRIBUTE_PTR, at the
 * cost of more complicated error semantics.
 */

static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle,
                             const CK_ATTRIBUTE_TYPE type,
                             void *value,
                             CK_ULONG *length,
                             const CK_ULONG maxlength)
{
  static const char select_format[] =
    " SELECT value FROM %s_attribute NATURAL JOIN object"
    " WHERE object_handle = ?1 AND type = ?2";

  const char *flavor = is_token_handle(object_handle) ? "token" : "session";

  sqlite3_stmt *q = NULL;
  int ret, ok = 0;
  CK_ULONG len;

  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
      !sql_check_ok(sqlite3_bind_int64(q, 2, type)))
    goto fail;

  ret = sqlite3_step(q);

  if (ret == SQLITE_DONE)
    goto fail;

  if (ret != SQLITE_ROW) {
    sql_whine_step();
    goto fail;
  }

  len = sqlite3_column_bytes(q, 0);

  if (length != NULL)
    *length = len;

  if (value != NULL && maxlength < len)
    goto fail;
  
  if (value != NULL)
    memcpy(value, sqlite3_column_blob(q, 0), len);

  ok = 1;

 fail:
  sqlite3_finalize(q);
  return ok;
}

/*
 * Wrappers to set and get CK_BBOOL and CK_ULONG values.
 */

static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
{
  return p11_attribute_set(object_handle, type, &value, sizeof(value));
}

static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value)
{
  return p11_attribute_set(object_handle, type, &value, sizeof(value));
}

static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
{
  CK_ULONG length;
  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
}

static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
{
  CK_ULONG length;
  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
}

/*
 * Find an attribute in a CK_ATTRIBUTE_PTR template.  Returns index
 * into template, or -1 if not found.
 */

static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type,
                                          const CK_ATTRIBUTE_PTR template,
                                          const CK_ULONG length)
{
  int i;

  if (template != NULL)
    for (i = 0; i < length; i++)
      if (template[i].type == type)
        return i;

  return -1;
}

/*
 * Map a keyusage-related attribute to a keyusage bit flag.
 *
 * Assumes that calling code has already checked whether this
 * attribute is legal for this object class, that attribute which
 * should be CK_BBOOLs are of the correct length, etcetera.
 *
 * To handle all the possible permutations of specified and default
 * values, it may be necessary to defer calling this method until
 * after the default and mandatory values have been merged into the
 * values supplied by the application-supplied template.
 *
 * Semantics of the flags follow RFC 5280 4.2.1.3, numeric values
 * don't matter particularly as we only use them internally.
 */

#define KEYUSAGE_DIGITALSIGNATURE	(1 << 0)
#define KEYUSAGE_KEYENCIPHERMENT	(1 << 1)
#define KEYUSAGE_DATAENCIPHERMENT	(1 << 2)

static void p11_attribute_apply_keyusage(unsigned *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value)
{
  unsigned flag;

  assert(keyusage != NULL && value != NULL);

  switch (type) {
  case CKA_SIGN:                /* Generate signature */
  case CKA_VERIFY:              /* Verify signature */
    flag = KEYUSAGE_DIGITALSIGNATURE;
    break;
  case CKA_ENCRYPT:             /* Encrypt bulk data (seldom used) */
  case CKA_DECRYPT:             /* Bulk decryption (seldom used) */
    flag = KEYUSAGE_DATAENCIPHERMENT;
    break;
  case CKA_WRAP:                /* Wrap key (normal way of doing encryption) */
  case CKA_UNWRAP:              /* Unwrap key (normal way of doing decryption) */
    flag = KEYUSAGE_KEYENCIPHERMENT;
    break;
  default:
    return;                     /* Attribute not related to key usage */
  }

  if (*value)
    *keyusage |=  flag;
  else
    *keyusage &= ~flag;
}



/*
 * Descriptor methods.  Descriptors are generated at compile time by
 * an auxiliary Python script, see attributes.* for details.
 */

/*
 * Return the descriptor associated with a particular object class and
 * key type.
 */

static const p11_descriptor_t *p11_descriptor_from_key_type(const CK_OBJECT_CLASS object_class,
                                                            const CK_KEY_TYPE key_type)
{
  int i;

  for (i = 0; i < sizeof(p11_descriptor_keyclass_map)/sizeof(*p11_descriptor_keyclass_map); i++) {
    const p11_descriptor_keyclass_map_t * const m = &p11_descriptor_keyclass_map[i];
    if (m->object_class == object_class && m->key_type == key_type)
      return m->descriptor;
  }

  return NULL;
}

/*
 * Find the entry for a particular attribute in a descriptor.
 */

static const p11_attribute_descriptor_t *p11_find_attribute_in_descriptor(const p11_descriptor_t *descriptor,
                                                                          const CK_ATTRIBUTE_TYPE type)
{
  int i;

  if (descriptor != NULL && descriptor->attributes != NULL)
    for (i = 0; i < descriptor->n_attributes; i++)
      if (descriptor->attributes[i].type == type)
        return &descriptor->attributes[i];

  return NULL;
}

/*
 * Check whether an attribute is marked as sensitive.  If we don't
 * recognize the attribute, report it as sensitive (safer than the
 * alternative).
 */

static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor,
                                      const CK_ATTRIBUTE_TYPE type)
{
  const p11_attribute_descriptor_t *a = p11_find_attribute_in_descriptor(descriptor, type);
  return a == NULL || (a->flags & P11_DESCRIPTOR_SENSITIVE) != 0;
}



/*
 * Object methods.
 */

/*
 * Check access rights for an object.
 */

typedef enum { p11_object_access_read, p11_object_access_write } p11_object_access_t;

static CK_RV p11_object_check_rights(const p11_session_t *session,
                                     const CK_OBJECT_HANDLE object_handle,
                                     const p11_object_access_t rights)
{
  static const char object_exists_query[] =
    " SELECT count(*) FROM object WHERE object_handle = ?1";

  static const char session_object_query[] =
    " SELECT session_handle FROM session NATURAL JOIN object WHERE object_handle = ?1";

  CK_BBOOL object_is_private;
  sqlite3_stmt *q = NULL;
  CK_RV rv;

  if (session == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  /*
   * Read-only sessions are, um, read-only.
   */

  switch (session->state) {
  case CKS_RO_PUBLIC_SESSION:
  case CKS_RO_USER_FUNCTIONS:
    if (rights == p11_object_access_write)
      lose(CKR_SESSION_READ_ONLY);
  }

  /*
   * Private objects don't exist for sessions in the wrong state.
   */

  switch (session->state) {
  case CKS_RO_PUBLIC_SESSION:
  case CKS_RW_PUBLIC_SESSION:
  case CKS_RW_SO_FUNCTIONS:
    if (!p11_attribute_get_bbool(object_handle, CKA_PRIVATE, &object_is_private) || object_is_private)
      lose(CKR_OBJECT_HANDLE_INVALID);
  }

  /*
   * Does the object even exist?
   */

  if (!sql_check_ok(sql_prepare(&q, object_exists_query))       ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
      !sql_check_row(sqlite3_step(q))                           ||
      !sqlite3_column_int(q, 0))
    lose(CKR_OBJECT_HANDLE_INVALID);

  /*
   * Session objects are only visible to the session which created them.
   */

  if (!is_token_handle(object_handle)                           &&
      (!sql_check_ok(sql_finalize_and_clear(&q))                ||
       !sql_check_ok(sql_prepare(&q, session_object_query))     ||
       !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))   ||
       !sql_check_row(sqlite3_step(q))                          ||
       sqlite3_column_int64(q, 0) != session->handle))
    lose(CKR_OBJECT_HANDLE_INVALID);

  /*
   * Ran out of reasons to reject, guess we should allow it.
   */

  rv = CKR_OK;

 fail:
  sqlite3_finalize(q);
  return rv;
}

/*
 * Delete all private objects, probably because user logged out.
 *
 * In the case of token objects, the object itself remains in the
 * token, we're just deleting our handle for the object.
 *
 * In the case of session objects, the object itself goes away.
 */

static int p11_object_delete_all_private(void)
{
  static const char delete_format[] =
    " WITH"
    "  s AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00'),"
    "  t AS (SELECT token_object_id   FROM token_attribute   WHERE type = %u AND value <> X'00')"
    " DELETE FROM object WHERE token_object_id IN t OR session_object_id IN s";

  sqlite3_stmt *q = NULL;
  int ok = 0;

  if (!sql_check_ok(sql_prepare(&q, delete_format, CKA_PRIVATE, CKA_PRIVATE))   ||
      !sql_check_done(sqlite3_step(q)))
    goto fail;

  ok = 1;

 fail:
  sqlite3_finalize(q);
  return ok;
}

/*
 * Create a new object.
 *
 * This is a bit nasty due to the SQL foreign key constraints and the
 * different handling required for session and token objects.
 */

static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
                                          const handle_flavor_t flavor,
                                          const CK_ATTRIBUTE_PTR template,
                                          const CK_ULONG template_length,
                                          const p11_descriptor_t * const descriptor,
                                          const CK_MECHANISM_PTR mechanism)
{
  static const char insert_object[] =
    " INSERT INTO object (object_handle)"
    " VALUES (?)";

  static const char insert_token_object[] =
    " INSERT INTO token_object DEFAULT VALUES";

  static const char insert_session_object[] =
    " INSERT INTO session_object (object_id) VALUES (?)";

  static const char update_object_session_object[] =
    " UPDATE object SET"
    "   session_id = (SELECT session_id FROM session WHERE session_handle = ?1),"
    "   session_object_id = ?2"
    " WHERE object_id = ?3";

  static const char update_object_token_object[] =
    " UPDATE object SET token_object_id = ?1 WHERE object_id = ?2";

  static const char insert_token_attribute[] =
    " INSERT OR REPLACE INTO token_attribute (token_object_id, type, value)"
    " VALUES (?1, ?2, ?3)";

  static const char insert_session_attribute[] =
    " INSERT OR REPLACE INTO session_attribute (session_object_id, type, value)"
    " VALUES (?1, ?2, ?3)";

  CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(flavor);;
  sqlite3_int64 object_id, session_object_id, token_object_id;
  sqlite3_stmt *q = NULL;
  int i, ok = 0;

  assert(session != NULL && template != NULL && descriptor != NULL &&
         (flavor == handle_flavor_token_object ||
          flavor == handle_flavor_session_object));

  if (!sql_check_ok(sql_prepare(&q, insert_object))             ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
      !sql_check_done(sqlite3_step(q)))
    goto fail;

  object_id = sqlite3_last_insert_rowid(sqldb);

  if (!sql_check_ok(sql_finalize_and_clear(&q)))
    goto fail;

  switch (flavor) {

  case handle_flavor_token_object:
    if (!sql_check_ok(sql_prepare(&q, insert_token_object))             ||
        !sql_check_done(sqlite3_step(q)))
      goto fail;
    token_object_id = sqlite3_last_insert_rowid(sqldb);
    if (!sql_check_ok(sql_finalize_and_clear(&q))                       ||
        !sql_check_ok(sql_prepare(&q, update_object_token_object))      ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id))        ||
        !sql_check_ok(sqlite3_bind_int64(q, 2, object_id))              ||
        !sql_check_done(sqlite3_step(q))                                ||
        !sql_check_ok(sql_finalize_and_clear(&q))                       ||
        !sql_check_ok(sql_prepare(&q, insert_token_attribute))          ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)))
      goto fail;
    break;

  case handle_flavor_session_object:
    if (!sql_check_ok(sql_prepare(&q, insert_session_object))           ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, object_id))              ||
        !sql_check_done(sqlite3_step(q)))
      goto fail;
    session_object_id = sqlite3_last_insert_rowid(sqldb);
    if (!sql_check_ok(sql_finalize_and_clear(&q))                       ||
        !sql_check_ok(sql_prepare(&q, update_object_session_object))    ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))        ||
        !sql_check_ok(sqlite3_bind_int64(q, 2, session_object_id))      ||
        !sql_check_ok(sqlite3_bind_int64(q, 3, object_id))              ||
        !sql_check_done(sqlite3_step(q))                                ||
        !sql_check_ok(sql_finalize_and_clear(&q))                       ||
        !sql_check_ok(sql_prepare(&q, insert_session_attribute))        ||
        !sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id)))
      goto fail;
    break;

  default:                      /* Suppress GCC warning */
    goto fail;
  }

  /*
   * Now populate attributes, starting with the application's
   * template, which we assume has already been blessed by the API
   * function that called this method.
   */

  for (i = 0; i < template_length; i++) {
    const CK_ATTRIBUTE_TYPE type = template[i].type;
    const void *             val = template[i].pValue;
    const int                len = template[i].ulValueLen;

    if (!sql_check_ok(sqlite3_reset(q))                         ||
        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
        !sql_check_done(sqlite3_step(q)))
      goto fail;
  }

  /*
   * Next, add defaults from the descriptor.
   */

  for (i = 0; i < descriptor->n_attributes; i++) {
    const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type;
    const void *             val = descriptor->attributes[i].value;
    const int                len = descriptor->attributes[i].length;
    const unsigned         flags = descriptor->attributes[i].flags;

    if (val == NULL && (flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0)
      val = "";

    if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0)
      continue;

    if (!sql_check_ok(sqlite3_reset(q))                         ||
        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
        !sql_check_done(sqlite3_step(q)))
      goto fail;
  }

  /*
   * Finally, add generation mechanism attributes as needed.
   */
  
  if (mechanism != NULL &&
      (!sql_check_ok(sqlite3_reset(q))                                                                          ||
       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL))                                                       ||
       !sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL))                     ||
       !sql_check_done(sqlite3_step(q))                                                                         ||
       !sql_check_ok(sqlite3_reset(q))                                                                          ||
       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM))                                           ||
       !sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL))       ||
       !sql_check_done(sqlite3_step(q))))
    goto fail;

  /*
   * If we made it past all that, we're happy.
   */

  ok = 1;

 fail:
  sqlite3_finalize(q);
  return ok ? object_handle : CK_INVALID_HANDLE;
}

/*
 * Store an RSA private key.
 *
 * Write the key as PKCS #1.5 RSAPrivateKey DER, encrypt that using
 * AES key wrap, and store the result as an SQL blob.
 *
 * We jump through a few minor hoops to let us do all the encoding and
 * wrapping in place in a single buffer.
 */

static int p11_object_set_rsa_private_key(const CK_OBJECT_HANDLE object_handle,
                                          const hal_rsa_key_t key)
{
  static const char select_kek[] =
    " SELECT kek FROM global";

  static const char update_format[] =
    " UPDATE %s_object SET private_key = ?1"
    " WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle = ?2)";

  uint8_t wrapbuf[hal_aes_keywrap_ciphertext_length(hal_rsa_key_to_der_len(key))];
  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
  size_t der_len, wrapbuf_len = sizeof(wrapbuf);
  sqlite3_stmt *q = NULL;
  int ok = 0;

  if (!sql_check_ok(sql_prepare(&q, select_kek))                                ||
      !sql_check_row(sqlite3_step(q))                                           ||
      sqlite3_column_type(q, 0) == SQLITE_NULL                                  ||
      !hal_check(hal_rsa_key_to_der(key, wrapbuf+8, &der_len,
                                    sizeof(wrapbuf)-8))                         ||
      !hal_check(hal_aes_keywrap(sqlite3_column_blob(q, 0),
                                 sqlite3_column_bytes(q, 0),
                                 wrapbuf+8, der_len, wrapbuf, &wrapbuf_len))	||
      !sql_check_ok(sql_finalize_and_clear(&q))                                 ||
      !sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor))     ||
      !sql_check_ok(sqlite3_bind_blob( q, 1, wrapbuf, wrapbuf_len, NULL))       ||
      !sql_check_ok(sqlite3_bind_int64(q, 2, object_handle))                    ||
      !sql_check_done(sqlite3_step(q)))
    goto fail;

  ok = 1;

 fail:
  memset(wrapbuf, 0, sizeof(wrapbuf));
  sqlite3_finalize(q);
  return ok;
}

/*
 * Fetch an RSA private key.
 *
 * Retrieve SQL blob from the object, unwrap that to get the DER
 * encoding of a PKCS #1.5 RSAPrivateKey object, load the key from
 * that.
 *
 * If the key isn't set, we return success with null key.
 */

static int p11_object_get_rsa_private_key(const CK_OBJECT_HANDLE object_handle,
                                          hal_rsa_key_t *key,
                                          uint8_t *keybuf, const size_t keybuf_len)
{
  static const char select_format[] =
    " SELECT kek, private_key FROM global, %s_object NATURAL JOIN object WHERE object_handle = ?1";

  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
  sqlite3_stmt *q = NULL;
  int ok;

  assert(key != NULL && keybuf != NULL);

  /*
   * Pull everything we need from the database.
   */

  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
      !sql_check_row(sqlite3_step(q))                           ||
      sqlite3_column_type(q, 0) == SQLITE_NULL) {
    ok = 0;
  }

  else if (sqlite3_column_type(q, 1) == SQLITE_NULL) {
    key->key = NULL;
    ok = 1;
  }

  else {
    const uint8_t * const kek  = sqlite3_column_blob(q, 0);
    const uint8_t * const pkey = sqlite3_column_blob(q, 1);
    const size_t kek_len  = sqlite3_column_bytes(q, 0);
    const size_t pkey_len = sqlite3_column_bytes(q, 1);
    size_t  wrapbuf_len = pkey_len;
    uint8_t wrapbuf[pkey_len];

    ok = (hal_check(hal_aes_keyunwrap(kek, kek_len, pkey, pkey_len, wrapbuf, &wrapbuf_len)) &&
          hal_check(hal_rsa_key_from_der(key, keybuf, keybuf_len, wrapbuf, wrapbuf_len)));

    memset(wrapbuf, 0, sizeof(wrapbuf));
  }

  if (!ok || key->key == NULL)
    memset(keybuf, 0, keybuf_len);

  sqlite3_finalize(q);
  return ok;
}

#warning Revisit return semantics of p11_object_get_rsa_private_key() and p11_object_get_rsa_public_key()

/*
 * Fetch an RSA public key.
 *
 * Public keys aren't stored separately the way that private keys are,
 * so we're looking for the public components so we can load them into
 * a key objet.
 */

static int p11_object_get_rsa_public_key(const CK_OBJECT_HANDLE object_handle,
                                         hal_rsa_key_t *key,
                                         uint8_t *keybuf, const size_t keybuf_len)
{
  static const char select_format[] =
    " WITH a (type, value) "
    "   AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
    " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";

  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
  sqlite3_stmt *q = NULL;

  assert(key != NULL && keybuf != NULL);

  const int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor,
                                           CKA_MODULUS, CKA_PUBLIC_EXPONENT))     &&
                  sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))           &&
                  sql_check_row(sqlite3_step(q))                                  &&
                  sqlite3_column_type(q, 0) == SQLITE_BLOB                        &&
                  sqlite3_column_type(q, 1) == SQLITE_BLOB                        &&
                  hal_check(hal_rsa_key_load_public(key, keybuf, keybuf_len,
                                                    sqlite3_column_blob( q, 0),
                                                    sqlite3_column_bytes(q, 0),
                                                    sqlite3_column_blob( q, 1),
                                                    sqlite3_column_bytes(q, 1))));

  sqlite3_finalize(q);
  return ok;
}



/*
 * Session methods.
 */

/*
 * Create a new session.
 */

static p11_session_t *p11_session_new(void)
{
  p11_session_t *session = malloc(sizeof(*session));
  if (session == NULL)
    return NULL;
  memset(session, 0, sizeof(*session));
  return session;
}

/*
 * Free a session.
 */

static void p11_session_free(p11_session_t *session)
{
  if (session == NULL)
    return;

  sql_finalize_and_clear(&session->find_query);

  free(session);
}

/*
 * Assign a handle to a session and add the session to SQL.
 */

static int p11_session_add(p11_session_t *session)
{
  static const char insert_session[] =
    " INSERT INTO session (session_handle) VALUES (?)";

  sqlite3_stmt *q = NULL;
  int ok = 0;

  assert(session != NULL);

  session->handle = p11_allocate_unused_handle(handle_flavor_session);

  if (!sql_check_ok(sql_prepare(&q, insert_session))            ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))  ||
      !sql_check_done(sqlite3_step(q)))
    goto fail;

  session->link = p11_sessions;
  p11_sessions = session;
  ok = 1;

 fail:
  sqlite3_finalize(q);
  return ok;
}

/*
 * Find a session.
 *
 * Since we don't expect the total number of sessions to be all that
 * high, we use a linked list with a move-to-the-front search.  Some
 * of the other session methods assume this behavior, so be careful if
 * you decide to change it.
 */

static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
{
  p11_session_t **link, *session;

  for (link = &p11_sessions;
       (session = *link) != NULL && session->handle != session_handle;
       link = &session->link)
    ;

  if (session != NULL && link != &p11_sessions) {
    *link = session->link;
    session->link = p11_sessions;
    p11_sessions = session;
  }

  return session;
}

/*
 * Delete a session: remove it from SQL and free the session data
 * structure.
 *
 * This method assumes the move-to-the-front behavior of
 * p11_session_find().
 */

static CK_RV p11_session_delete(const CK_SESSION_HANDLE session_handle)
{
  static const char delete_session[] =
    " DELETE FROM session WHERE session_handle = ?";

  p11_session_t *session = p11_session_find(session_handle);
  sqlite3_stmt *q = NULL;
  CK_RV rv = CKR_OK;

  if (session == NULL)
    return CKR_SESSION_HANDLE_INVALID;

  if (!sql_check_ok(sql_prepare(&q, delete_session))            ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle))   ||
      !sql_check_done(sqlite3_step(q)))
    lose(CKR_FUNCTION_FAILED);

  /* Check that move-to-the-front behaved as expected */
  assert(p11_sessions == session);

  p11_sessions = session->link;
  p11_session_free(session);

 fail:
  sqlite3_finalize(q);
  return rv;
}

/*
 * Delete all sessions.
 */

#warning Should this also clear the object table?

static CK_RV p11_session_delete_all(void)
{
  static const char delete_all_sessions[] =
    " DELETE FROM session";

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  if (!sql_exec(delete_all_sessions))
    lose(CKR_FUNCTION_FAILED);

  while (p11_sessions != NULL) {
    session = p11_sessions;
    p11_sessions = session->link;
    p11_session_free(session);
  }

 fail:
  return rv;
}

/*
 * Check session database against login state for consistency.
 *
 * This is mostly useful in assertions.
 */

static int p11_session_consistent_login(void)
{
  p11_session_t *session;

  switch (logged_in_as) {

  case not_logged_in:
    for (session = p11_sessions; session != NULL; session = session->link)
      if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION)
        return 0;
    return 1;

  case logged_in_as_user:
    for (session = p11_sessions; session != NULL; session = session->link)
      if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS)
        return 0;
    return 1;

  case logged_in_as_so:
    for (session = p11_sessions; session != NULL; session = session->link)
      if (session->state != CKS_RW_SO_FUNCTIONS)
        return 0;
    return 1;

  default:
    return 0;
  }
}



/*
 * PKCS #11 likes space-padded rather than null-terminated strings.
 */

static int psnprintf(void *buffer_, size_t size, const char *format, ...)
{
  char *buffer = buffer_;
  size_t i, n;
  va_list ap;

  va_start(ap, format);
  i = n = vsnprintf(buffer, size, format, ap);
  va_end(ap);

  while (i < size)
    buffer[i++] = ' ';

  return n;
}



/*
 * Template checking and key generation.
 *
 * This may need refactoring at some point, eg, when we add support
 * for C_CreateObject().
 */

/*
 * First pass: called once per template entry during initial pass over
 * template to handle generic checks that apply regardless of
 * attribute type.
 */

static CK_RV p11_check_keypair_attributes_check_template_1(const CK_ATTRIBUTE_TYPE type,
                                                           const void * const val,
                                                           const size_t len,
                                                           const p11_descriptor_t * const descriptor)
{
  const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type);
  CK_RV rv;

  /* Attribute not allowed or not allowed for key generation */
  if (atd == NULL || (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0)
    lose(CKR_ATTRIBUTE_TYPE_INVALID);

  /* NULL or wrong-sized attribute values */
  if (val == NULL || (atd->size != 0 && len != atd->size))
    lose(CKR_ATTRIBUTE_VALUE_INVALID);

  /* Attributes which only the SO user is allowed to set to CK_TRUE */
  if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val)
      lose(CKR_ATTRIBUTE_VALUE_INVALID);

  /* Attributes which don't match mandatory values */
  if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0)
    lose(CKR_TEMPLATE_INCONSISTENT);

  rv = CKR_OK;

 fail:
#if DEBUG_PKCS11
  if (rv != CKR_OK)
    fprintf(stderr, "p11_check_keypair_attributes_check_template_1() rejected attribute 0x%08lx\n",
            (unsigned long) type);
#endif
  return rv;
}

/*
 * Second pass: called once per template to check that each attribute
 * required for that template has been specified exactly once.
 */

static CK_RV p11_check_keypair_attributes_check_template_2(const p11_session_t *session,
                                                           const p11_descriptor_t * const descriptor,
                                                           const CK_ATTRIBUTE_PTR template,
                                                           const CK_ULONG template_length)
{
  const CK_BBOOL *object_is_private;
  CK_RV rv;
  int i, j;

  /*
   * Some session states aren't allowed to play with private objects.
   */

  switch (session->state) {
  case CKS_RO_PUBLIC_SESSION:
  case CKS_RW_PUBLIC_SESSION:
  case CKS_RW_SO_FUNCTIONS:
    if ((i = p11_attribute_find_in_template(CKA_PRIVATE, template, template_length)) >= 0) {
      assert(template[i].pValue != NULL);
      object_is_private = template[i].pValue;
    }
    else {
      const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, CKA_PRIVATE);
      assert(atd != NULL && atd->value != NULL);
      object_is_private = atd->value;
    }
    if (*object_is_private)
      lose(CKR_TEMPLATE_INCONSISTENT);
  }

  for (i = 0; i < descriptor->n_attributes; i++) {
    const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i];
    const int required_by_api  = (atd->flags & P11_DESCRIPTOR_REQUIRED_BY_GENERATE) != 0;
    const int forbidden_by_api = (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0;
    const int in_descriptor    = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL;
    const int pos_in_template  = p11_attribute_find_in_template(atd->type, template, template_length);

    /* Multiple entries for same attribute */
    if (pos_in_template >= 0)
      for (j = pos_in_template + 1; j < template_length; j++)
        if (template[j].type == atd->type)
          lose(CKR_TEMPLATE_INCONSISTENT);

    /* Required attribute missing from template */
    if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) {
#if DEBUG_PKCS11
      fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type);
#endif
      lose(CKR_TEMPLATE_INCOMPLETE);
    }
  }

  rv = CKR_OK;

 fail:
  return rv;
}

/*
 * Mechanism-independent checks for templates and descriptors when
 * generating new keypairs.
 *
 * PKCS #11 gives the application far too much rope (including but not
 * limited to the ability to supply completely unrelated templates for
 * public and private keys in a keypair), so we need to do a fair
 * amount of checking.  We automate as much of the dumb stuff as
 * possible through the object descriptor.
 * 
 * Key usage handling here is based on RFC 5280 4.2.1.3.
 *
 * PKCS #11 suggests but does not require CKA_ID values for public and
 * private key to match.
 */

static CK_RV p11_check_keypair_attributes(const p11_session_t *session,
                                          const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                          const CK_ULONG ulPublicKeyAttributeCount,
                                          const p11_descriptor_t * const public_descriptor,
                                          const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                          const CK_ULONG ulPrivateKeyAttributeCount,
                                          const p11_descriptor_t * const private_descriptor)
{
  unsigned public_keyusage = 0, private_keyusage = 0;
  CK_RV rv = CKR_OK;
  int i;

  assert(session             != NULL &&
         pPublicKeyTemplate  != NULL && public_descriptor  != NULL &&
         pPrivateKeyTemplate != NULL && private_descriptor != NULL);

  /*
   * Read-only sessions can't create keys, doh.
   */

  switch (session->state) {
  case CKS_RO_PUBLIC_SESSION:
  case CKS_RO_USER_FUNCTIONS:
    lose(CKR_SESSION_READ_ONLY);
  }

  /*
   * Check values provided in the public and private templates.
   */

  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
    const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
    const void * const       val = pPublicKeyTemplate[i].pValue;
    const size_t             len = pPublicKeyTemplate[i].ulValueLen;

    if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, public_descriptor)) != CKR_OK)
      goto fail;

    p11_attribute_apply_keyusage(&public_keyusage, type, val);
  }

  for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
    const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
    const void * const       val = pPrivateKeyTemplate[i].pValue;
    const size_t             len = pPrivateKeyTemplate[i].ulValueLen;

    if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, private_descriptor)) != CKR_OK)
      goto fail;

    p11_attribute_apply_keyusage(&private_keyusage, type, val);
  }

  /*
   * We insist that keyusage be specified for both public and private
   * key, and that they match.  May not need to be this strict.
   */

  if (public_keyusage != private_keyusage || public_keyusage == 0)
    lose(CKR_TEMPLATE_INCONSISTENT);

  /*
   * Check that all required attributes have been specified.
   */

  if ((rv = p11_check_keypair_attributes_check_template_2(session,
                                                          public_descriptor,
                                                          pPublicKeyTemplate,
                                                          ulPublicKeyAttributeCount))  != CKR_OK ||
      (rv = p11_check_keypair_attributes_check_template_2(session,
                                                          private_descriptor,
                                                          pPrivateKeyTemplate, 
                                                          ulPrivateKeyAttributeCount)) != CKR_OK)
    goto fail;

  /*
   * If we get this far, we're happy.  Maybe.
   */

  rv = CKR_OK;

 fail:
  return rv;
}

/*
 * CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation implemetation.
 *
 * Much mechanism-independent code has already been factored out of
 * this function, no doubt much remains that will require further
 * refactoring once we implement other mechanisms.
 */

static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
                                       const CK_MECHANISM_PTR pMechanism,
                                       const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                       const CK_ULONG ulPublicKeyAttributeCount,
                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                       const CK_ULONG ulPrivateKeyAttributeCount,
                                       CK_OBJECT_HANDLE_PTR phPublicKey,
                                       CK_OBJECT_HANDLE_PTR phPrivateKey)
{
  CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
  CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
  handle_flavor_t public_handle_flavor = handle_flavor_session_object;
  handle_flavor_t private_handle_flavor = handle_flavor_session_object;
  uint8_t
    keybuf[hal_rsa_key_t_size],
    modulus[hal_rsa_key_t_size/8],
    public_exponent[hal_rsa_key_t_size/8];
  size_t
    modulus_len,
    public_exponent_len;
  hal_rsa_key_t key = { NULL };
  CK_ULONG keysize = 0;
  CK_RV rv;
  int i;

  /*
   * Do mechanism-independent checks before anything else.
   */

  rv = p11_check_keypair_attributes(session,
                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,  &p11_descriptor_rsa_public_key,
                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key);
  if (rv != CKR_OK)
    return rv;

  assert(session             != NULL && pMechanism   != NULL &&
         pPublicKeyTemplate  != NULL && phPublicKey  != NULL && 
         pPrivateKeyTemplate != NULL && phPrivateKey != NULL);

  /*
   * Grab values and perform mechanism-specific checks.
   */

  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
    const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
    const void * const       val = pPublicKeyTemplate[i].pValue;
    const size_t             len = pPublicKeyTemplate[i].ulValueLen;

    assert(val != NULL);

    switch (type) {

    case CKA_TOKEN:             /* Object stored on token */
      public_handle_flavor = p11_handle_flavor_from_cka_token(val);
      continue;

    case CKA_MODULUS_BITS:      /* Keysize in bits -- only allow multiples of 8 */
      keysize = *(CK_ULONG *) val;
      if ((keysize & 7) != 0)
        return CKR_ATTRIBUTE_VALUE_INVALID;
      continue;

    }
  }

  for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
    const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
    const void * const       val = pPrivateKeyTemplate[i].pValue;
    const size_t             len = pPrivateKeyTemplate[i].ulValueLen;

    assert (val != NULL);

    switch (type) {

    case CKA_TOKEN:             /* Object stored on token */
      private_handle_flavor = p11_handle_flavor_from_cka_token(val);
      continue;

    }
  }

  /*
   * We require a key size, and if either key is a token object, the
   * other must be too.
   */
  if (keysize == 0 || public_handle_flavor != private_handle_flavor)
    return CKR_TEMPLATE_INCOMPLETE;

  /*
   * Create the PKCS #11 objects and generate the keypair.
   */

  if (!sql_exec("BEGIN")                                                                        ||
      (public_handle = p11_object_create(session, public_handle_flavor,
                                         pPublicKeyTemplate, ulPublicKeyAttributeCount,
                                         &p11_descriptor_rsa_public_key,
                                         pMechanism)) == CK_INVALID_HANDLE                      ||
      (private_handle = p11_object_create(session, private_handle_flavor,
                                          pPrivateKeyTemplate,  ulPrivateKeyAttributeCount,
                                          &p11_descriptor_rsa_private_key,
                                          pMechanism)) == CK_INVALID_HANDLE			||
      !p11_attribute_get(public_handle, CKA_PUBLIC_EXPONENT,
                         public_exponent, &public_exponent_len, sizeof(public_exponent))	||
      !hal_check(hal_rsa_key_gen(&key, keybuf, sizeof(keybuf), keysize/8,
                                 public_exponent, public_exponent_len))        			||
      !p11_object_set_rsa_private_key(private_handle, key)                                      ||
      !hal_check(hal_rsa_key_get_modulus(key, modulus, &modulus_len, sizeof(modulus)))          ||
      !p11_attribute_set(public_handle,  CKA_MODULUS, modulus, modulus_len)                     ||
      !p11_attribute_set(private_handle, CKA_MODULUS, modulus, modulus_len))
    lose(CKR_FUNCTION_FAILED);

  hal_rsa_key_clear(key);

  /*
   * Commit the SQL transaction.
   */

  if (!sql_exec("COMMIT"))
    lose(CKR_FUNCTION_FAILED);

  /*
   * All went well, return handles and we're done.
   */
  *phPublicKey  = public_handle;
  *phPrivateKey = private_handle;
  return CKR_OK;

 fail:

  memset(keybuf, 0, sizeof(keybuf));

  if (!sql_exec("ROLLBACK"))
    rv = CKR_GENERAL_ERROR;

  return rv;
}

/*
 * Construct a PKCS #1 DigestInfo object.  This requires some (very
 * basic) ASN.1 encoding, which we perform inline.
 */

static int pkcs1_construct_digestinfo(const hal_hash_descriptor_t * const desc,
                                      const uint8_t * const data, const size_t data_len,
                                      uint8_t *digest_info, const size_t digest_info_len)
{
  uint8_t statebuf[desc->hash_state_length];
  hal_hash_state_t state = { NULL };
  uint8_t *d = digest_info;

  /*
   * Make sure size of output buffer is right.  Caller is responsible
   * for supplying the right length, the check here is just paranoia.
   *
   * This encoder will fail if the DigestInfo object is more than
   * 129 octets long.  Rewrite if and when we need to support
   * digests or OIDs long enough for that to be an issue.
   */

  assert(digest_info_len == desc->digest_length + desc->digest_algorithm_id_length + 4);
  assert(digest_info_len < 130);

  *d++ = 0x30;                /* SEQUENCE */
  *d++ = (uint8_t) (digest_info_len - 2);

  memcpy(d, desc->digest_algorithm_id, desc->digest_algorithm_id_length);
  d += desc->digest_algorithm_id_length;

  *d++ = 0x04;                /* OCTET STRING */
  *d++ = (uint8_t) desc->digest_length;

  assert(digest_info + digest_info_len == d + desc->digest_length);

  const int ok = (hal_check(hal_hash_initialize(desc, &state, statebuf, sizeof(statebuf))) &&
                  hal_check(hal_hash_update(state, data, data_len))                        &&
                  hal_check(hal_hash_finalize(state, d, desc->digest_length)));

  memset(statebuf, 0, sizeof(statebuf));
  if (!ok)
    memset(digest_info, 0, digest_info_len);
  return ok;
}

/*
 * Pad an octet string with PKCS #1.5 padding for use with RSA.
 *
 * For the moment, this only handles type 01 encryption blocks, thus
 * is only suitable for use with signature and verification.  If and
 * when we add support for encryption and decryption, this function
 * should be extended to take an argument specifying the block type
 * and include support for generating type 02 encryption blocks.
 * Other than the block type code, the only difference is the padding
 * value: for type 01 it's constant (0xFF), for type 02 it should be
 * non-zero random bytes from the CSPRNG.
 */

static int pkcs1_5_pad(const uint8_t * const data, const size_t data_len,
                       uint8_t *block, const size_t block_len)
{
  assert(data != NULL && block != NULL);

  /*
   * Congregation will now please turn to RFC 2313 8.1 as we
   * construct a PKCS #1.5 type 01 encryption block.
   */

  if (data_len > block_len - 11)
    return 0;

  block[0] = 0x00;
  block[1] = 0x01;

  /* This is where we'd use non-zero random bytes if constructing a type 02 block. */
  memset(block + 2, 0xFF, block_len - 3 - data_len);

  block[block_len - data_len - 1] = 0x00;
  memcpy(block + block_len - data_len, data, data_len);

#if DEBUG_PKCS11 > 1
  fprintf(stderr, "[PKCS #1.5 block_len %lu data_len %lu block ", block_len, data_len);
  for (int i = 0; i < block_len; i++)
    fprintf(stderr, "%s%02x", i == 0 ? "" : ":", block[i]);
  fprintf(stderr, "]\n");
#endif

  return 1;
}

/*
 * Sign a PKCS #1 DigestInfo using an RSA key and PKCS #1.5 padding.
 *
 * As explained in RFC 3447, the RSASP1 (signature generation)
 * operation is the same mathematical operation as the RSADP
 * (decryption) operation (both use the private key as exponent).
 */

static CK_RV sign_rsa_pkcs(hal_rsa_key_t key,
                           const uint8_t * const digest_info, const size_t digest_info_len,
                           uint8_t *signature, const size_t signature_len)
{
  CK_RV rv;

  assert(digest_info != NULL && signature != NULL);

  if (!pkcs1_5_pad(digest_info, digest_info_len, signature, signature_len))
    lose(CKR_DATA_LEN_RANGE);

  if (!hal_check(hal_rsa_decrypt(key, signature, signature_len, signature, signature_len)))
    lose(CKR_FUNCTION_FAILED);

  return CKR_OK;

 fail:
  memset(signature, 0, signature_len);
  return rv;
}

/*
 * Verify a PKCS #1.5 padded RSA signature.
 *
 * We don't bother decoding the ASN.1, we just generate the type 01
 * encryption block we expect and compare it with what we got.
 *
 * Using constant-time comparision code for this is probably
 * unnecessary, but it's also harmless.
 */

static CK_RV verify_rsa_pkcs(hal_rsa_key_t key,
                             const uint8_t * const digest_info, const size_t digest_info_len,
                             const uint8_t * const signature, const size_t signature_len)
{
  assert(digest_info != NULL && signature != NULL);

  uint8_t expected[signature_len], received[signature_len];
  unsigned diff = 0;
  CK_RV rv;

  if (!pkcs1_5_pad(digest_info, digest_info_len, expected, sizeof(expected)))
    lose(CKR_DATA_LEN_RANGE);

  if (!hal_check(hal_rsa_encrypt(key, signature, signature_len, received, sizeof(received))))
    lose(CKR_FUNCTION_FAILED);

  for (int i = 0; i < signature_len; i++)
    diff |= expected[i] ^ received[i];

  if (diff != 0)
    lose(CKR_SIGNATURE_INVALID);

  rv = CKR_OK;

 fail:
  memset(expected, 0, sizeof(expected));
  memset(received, 0, sizeof(received));
  return rv;
}




/*
 * PKCS #11 API functions.
 */

CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
{
  ENTER_PUBLIC_FUNCTION(C_Initialize);

  CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
  int initialized_sql = 0;
  CK_RV rv;

  /*
   * We'd like to detect the error of calling this method more than
   * once in a single process without an intervening call to
   * C_Finalize(), but there's no completely portable way to do that
   * when faced with things like the POSIX fork() system call.  For
   * the moment, we use a POSIX-specific check, but may need to
   * generalize this for other platforms.
   */

#if USE_POSIX
  if (initialized_pid == getpid())
    lose(CKR_CRYPTOKI_ALREADY_INITIALIZED);
#endif

  /*
   * Sort out what the user wants to do about mutexes.  Default is not
   * to use mutexes at all.
   *
   * There's a chicken and egg problem here: setting up the global
   * mutex and mutex function pointers creates a race condition, and
   * there's no obvious action we can take which is robust in the face
   * of pathological behavior by the caller such as simultaneous calls
   * to this method with incompatible mutex primitives.
   *
   * Given that (a) it's an error to call this method more than once
   * in the same process without an intervening F_Finalize() call, and
   * given that (b) we haven't actually promised to do any kind of
   * locking at all until this method returns CKR_OK, we punt
   * responsibility for this pathological case back to the caller.
   */

  mutex_cb_create  = NULL;
  mutex_cb_destroy = NULL;
  mutex_cb_lock    = NULL;
  mutex_cb_unlock  = NULL;

  if (a != NULL) {

    const int functions_provided = ((a->CreateMutex  != NULL) +
                                    (a->DestroyMutex != NULL) +
                                    (a->LockMutex    != NULL) +
                                    (a->UnlockMutex  != NULL));

    /*
     * Reserved is, um, reserved.
     * Mutex parameters must either all be present or all be absent.
     */

    if (a->pReserved != NULL || (functions_provided & 3) != 0)
      lose(CKR_ARGUMENTS_BAD);

    /*
     * If the user provided mutex functions, use them.  Otherwise, if
     * the user wants locking, use POSIX mutexes or return an error
     * depending on whether we have POSIX mutexes available.
     * Otherwise, we don't need to use mutexes.
     */

    if (functions_provided) {
      mutex_cb_create  = a->CreateMutex;
      mutex_cb_destroy = a->DestroyMutex;
      mutex_cb_lock    = a->LockMutex;
      mutex_cb_unlock  = a->UnlockMutex;
    }

    else if ((a->flags & CKF_OS_LOCKING_OK) != 0) {
#if USE_PTHREADS
      mutex_cb_create  = posix_mutex_create;
      mutex_cb_destroy = posix_mutex_destroy;
      mutex_cb_lock    = posix_mutex_lock;
      mutex_cb_unlock  = posix_mutex_unlock;
#else
      lose(CKR_CANT_LOCK);
#endif
    }
  }

  /*
   * Now that we know which mutex implementation to use, set up a
   * global mutex.  We may want something finer grained later, but
   * this is enough to preserve the basic API semantics.
   *
   * Open question whether we should lock at this point, given that
   * until we return we haven't promised to do locking.  Skip for now
   * as it's simpler, fix later if it turns out to be a problem.
   */

  if ((rv = mutex_create(&p11_global_mutex)) != CKR_OK)
    goto fail;

  /*
   * Initialize SQLite3, opening the database(s) and loading the
   * schema and views.
   */

  if (!sql_init())
    lose(CKR_GENERAL_ERROR);

  initialized_sql = 1;

  if (!kek_init())
    lose(CKR_GENERAL_ERROR);

#if USE_POSIX
  initialized_pid = getpid();
#endif

  return CKR_OK;

 fail:

  if (initialized_sql)
    sql_fini();

  return rv;
}

CK_RV C_Finalize(CK_VOID_PTR pReserved)
{
  ENTER_PUBLIC_FUNCTION(C_Finalize);

  CK_RV rv = CKR_OK;

  if (pReserved != NULL)
    return CKR_ARGUMENTS_BAD;

  mutex_lock_or_return_failure(p11_global_mutex);

  /*
   * Destroy all current sessions.
   */

  p11_session_delete_all();

  /*
   * Shut down SQLite3.
   */

  if (!sql_fini())
    lose(CKR_GENERAL_ERROR);

  /*
   * By this point we're pretty well committed to shutting down, so
   * there's not much to be done if these mutex operations fail.
   */

  rv =  mutex_unlock(p11_global_mutex);
  (void) mutex_destroy(p11_global_mutex);
  p11_global_mutex = NULL;
  return rv;

 fail:
  (void) mutex_unlock(p11_global_mutex);
  return rv;
}

CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
{
  ENTER_PUBLIC_FUNCTION(C_GetFunctionList);

  /*
   * Use pkcs11f.h to build dispatch vector for C_GetFunctionList().
   * This should be const, but that's not what PKCS #11 says, oh well.
   *
   * This doesn't touch anything requiring locks, nor should it.
   */

  static CK_FUNCTION_LIST ck_function_list = {
    { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
#define CK_PKCS11_FUNCTION_INFO(name) name,
#include "pkcs11f.h"
#undef  CK_PKCS11_FUNCTION_INFO
  };

  if (ppFunctionList == NULL)
    return CKR_ARGUMENTS_BAD;

  *ppFunctionList = &ck_function_list;

  return CKR_OK;
}

CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
                    CK_SLOT_ID_PTR pSlotList,
                    CK_ULONG_PTR pulCount)
{
  ENTER_PUBLIC_FUNCTION(C_GetSlotList);

  /*
   * We only have one slot, and it's hardwired.
   * No locking required here as long as this holds.
   */

  if (pulCount == NULL)
    return CKR_ARGUMENTS_BAD;

  if (pSlotList != NULL && *pulCount < 1)
    return CKR_BUFFER_TOO_SMALL;

  *pulCount = 1;

  if (pSlotList != NULL)
    pSlotList[0] = P11_ONE_AND_ONLY_SLOT;

  return CKR_OK;
}

CK_RV C_GetTokenInfo(CK_SLOT_ID slotID,
                     CK_TOKEN_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetTokenInfo);

  /*
   * No locking required here as long as we're just returning constants.
   */

  if (pInfo == NULL)
    return CKR_ARGUMENTS_BAD;

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    return CKR_SLOT_ID_INVALID;

  memset(pInfo, 0, sizeof(*pInfo));

  /*
   * No real idea (yet) how we get many of the following parameters.
   * See cryptlib's CRYPT_DEVINFO_* attributes for some hints.
   *
   * pInfo->label is supposed to be set when the token is initialized.
   * Not yet sure what that means in our context, but need something
   * here or the libhsm test programs will bomb trying to find the
   * right token, so hard-wire something for now.
   */

  psnprintf(pInfo->label, sizeof(pInfo->label),
            "Cryptech Token");

  psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID),
            "Cryptech Project");

  psnprintf(pInfo->model, sizeof(pInfo->model),
            "%04x%04x%04x%04x",
            P11_VERSION_HW_MAJOR, P11_VERSION_HW_MINOR,
            P11_VERSION_SW_MAJOR, P11_VERSION_SW_MINOR);

  psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber),
            "007");

  pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED;

#warning Have not yet sorted out token flags
#if 0
    CKF_RNG
    CKF_WRITE_PROTECTED
    CKF_LOGIN_REQUIRED
    CKF_USER_PIN_INITIALIZED
    CKF_RESTORE_KEY_NOT_NEEDED
    CKF_CLOCK_ON_TOKEN
    CKF_PROTECTED_AUTHENTICATION_PATH
    CKF_DUAL_CRYPTO_OPERATIONS
    CKF_TOKEN_INITIALIZED
    CKF_SECONDARY_AUTHENTICATION
    CKF_USER_PIN_COUNT_LOW
    CKF_USER_PIN_FINAL_TRY
    CKF_USER_PIN_LOCKED
    CKF_USER_PIN_TO_BE_CHANGED
    CKF_SO_PIN_COUNT_LOW
    CKF_SO_PIN_FINAL_TRY
    CKF_SO_PIN_LOCKED
    CKF_SO_PIN_TO_BE_CHANGED
    CKF_ERROR_STATE
#endif

#warning Much of the TOKEN_INFO we return is nonsense
  pInfo->ulMaxSessionCount      = CK_EFFECTIVELY_INFINITE;
  pInfo->ulSessionCount         = CK_UNAVAILABLE_INFORMATION;
  pInfo->ulMaxRwSessionCount    = CK_EFFECTIVELY_INFINITE;
  pInfo->ulRwSessionCount       = CK_UNAVAILABLE_INFORMATION;
  pInfo->ulMaxPinLen            = P11_MAX_PIN_LENGTH;
  pInfo->ulMinPinLen            = P11_MIN_PIN_LENGTH;
  pInfo->ulTotalPublicMemory    = CK_UNAVAILABLE_INFORMATION;
  pInfo->ulFreePublicMemory     = CK_UNAVAILABLE_INFORMATION;
  pInfo->ulTotalPrivateMemory   = CK_UNAVAILABLE_INFORMATION;
  pInfo->ulFreePrivateMemory    = CK_UNAVAILABLE_INFORMATION;
  pInfo->hardwareVersion.major  = P11_VERSION_HW_MAJOR;
  pInfo->hardwareVersion.minor  = P11_VERSION_HW_MINOR;
  pInfo->firmwareVersion.major  = P11_VERSION_SW_MAJOR;
  pInfo->firmwareVersion.minor  = P11_VERSION_SW_MINOR;

#warning Need to sort out hardware clock
#if 0
  /*
   * Eventually we expect cryptech devices to have their own hardware
   * clocks.  Not implemented yet.
   */
  pInfo->utcTime;
#endif

  return CKR_OK;
}

CK_RV C_OpenSession(CK_SLOT_ID slotID,
                    CK_FLAGS flags,
                    CK_VOID_PTR pApplication,
                    CK_NOTIFY Notify,
                    CK_SESSION_HANDLE_PTR phSession)
{
  ENTER_PUBLIC_FUNCTION(C_OpenSession);

  const int parallel_session = (flags & CKF_SERIAL_SESSION) == 0;
  const int read_only_session = (flags & CKF_RW_SESSION) == 0;
  p11_session_t *session = NULL;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    lose(CKR_SLOT_ID_INVALID);

  if (phSession == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (parallel_session)
    lose(CKR_SESSION_PARALLEL_NOT_SUPPORTED);

  if ((session = p11_session_new()) == NULL)
    lose(CKR_HOST_MEMORY);

  switch (logged_in_as) {

  case not_logged_in:
    session->state = read_only_session ? CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
    break;

  case logged_in_as_user:
    session->state = read_only_session ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
    break;

  case logged_in_as_so:
    if (read_only_session)
      lose(CKR_SESSION_READ_WRITE_SO_EXISTS);
    session->state = CKS_RW_SO_FUNCTIONS;
    break;
  }    

  session->notify = Notify;
  session->application = pApplication;

  if (!p11_session_add(session))
    lose(CKR_FUNCTION_FAILED);

  assert(p11_session_consistent_login());

  if ((rv = mutex_unlock(p11_global_mutex)) != CKR_OK)
    goto fail;

  *phSession = session->handle;
  return CKR_OK;

 fail:
  p11_session_free(session);
  (void) mutex_unlock(p11_global_mutex);
  return rv;
}

CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
{
  ENTER_PUBLIC_FUNCTION(C_CloseSession);

  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

  rv = p11_session_delete(hSession);

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
{
  ENTER_PUBLIC_FUNCTION(C_CloseAllSessions);

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    return CKR_SLOT_ID_INVALID;

  mutex_lock_or_return_failure(p11_global_mutex);

  p11_session_delete_all();

  return mutex_unlock(p11_global_mutex);
}

CK_RV C_Login(CK_SESSION_HANDLE hSession,
              CK_USER_TYPE userType,
              CK_UTF8CHAR_PTR pPin,
              CK_ULONG ulPinLen)
{
  ENTER_PUBLIC_FUNCTION(C_Login);

  static const char pin_query_format[] =
    " SELECT pbkdf2_iterations, %s_pin, %s_pin_salt from global";

  p11_session_t *session;
  sqlite3_stmt *q = NULL;
  const char *pin_type;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if (pPin == NULL)
    lose(CKR_ARGUMENTS_BAD);

  /*
   * Mind, I don't really know why this function takes a session
   * handle, given that the semantics don't seem to call upon us to do
   * anything special for "this" session.
   */

  if (p11_session_find(hSession) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  /*
   * We don't currently support re-login without an intervening
   * logout, so reject the login attempt if we're already logged in.
   */

  if (logged_in_as != not_logged_in)
    lose(CKR_USER_ALREADY_LOGGED_IN);    

  /*
   * Figure out which PIN we're checking.
   * We don't (yet?) support CKU_CONTEXT_SPECIFIC.
   *
   * Read-only SO is an illegal state, so reject the login attempt if
   * we have any read-only sessions and we're trying to log in as SO.
   */

  switch (userType) {
  case CKU_USER:
    pin_type = "user";
    break;
  case CKU_SO:
    for (session = p11_sessions; session != NULL; session = session->link)
      if (session->state == CKS_RO_PUBLIC_SESSION)
        lose(CKR_SESSION_READ_ONLY_EXISTS);
    pin_type = "so";
    break;
  case CKU_CONTEXT_SPECIFIC:
    lose(CKR_OPERATION_NOT_INITIALIZED);
  default:
    lose(CKR_USER_TYPE_INVALID);
  }

  /*
   * Look up the PIN and make sure it's set.
   *
   * Not obvious what error we should return if SO PIN isn't set, for
   * now consider this state "locked" (because it hasn't been set yet).
   */

  if (!sql_check_ok(sql_prepare(&q, pin_query_format, pin_type, pin_type)) ||
      !sql_check_row(sqlite3_step(q)))
    lose(CKR_FUNCTION_FAILED);

  if (sqlite3_column_type(q, 1) == SQLITE_NULL ||
      sqlite3_column_type(q, 2) == SQLITE_NULL) {
    switch (userType) {
    case CKU_USER:
      lose(CKR_USER_PIN_NOT_INITIALIZED);
    case CKU_SO:
      lose(CKR_PIN_LOCKED);
    default:
      lose(CKR_USER_TYPE_INVALID);
    }
  }

  /*
   * Run PBKDF2 over the supplied PIN and compare results.
   *
   * Probably not really necessary to use constant-time string
   * comparison, but it's harmless and cheap, so we might as well.
   */

  {
    const unsigned iterations = sqlite3_column_int(q, 0);
    const uint8_t * const pin  = sqlite3_column_blob(q, 1);
    const uint8_t * const salt = sqlite3_column_blob(q, 2);
    const size_t pin_len  = sqlite3_column_bytes(q, 1);
    const size_t salt_len = sqlite3_column_bytes(q, 2);
    uint8_t pinbuf[pin_len];
    unsigned diff = 0;

    if (!hal_check(hal_pbkdf2(hal_hash_sha256, pPin, ulPinLen, salt, salt_len,
                              pinbuf, sizeof(pinbuf), iterations)))
      lose(CKR_FUNCTION_FAILED);

    for (int i = 0; i < pin_len; i++)
      diff |= pin[i] ^ pinbuf[i];

    if (diff != 0)
      lose(CKR_PIN_INCORRECT);
  }

  /*
   * If we get here, the PIN was OK.  Update global login state, then
   * whack every session into the correct new state.
   */

  assert(p11_session_consistent_login());

  logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user;

  for (session = p11_sessions; session != NULL; session = session->link) {
    switch (session->state) {

    case CKS_RO_PUBLIC_SESSION:
      assert(userType == CKU_USER);
      session->state = CKS_RO_USER_FUNCTIONS;
      continue;

    case CKS_RW_PUBLIC_SESSION:
      session->state = userType == CKU_SO ? CKS_RW_SO_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
      continue;

    }
  }

  assert(p11_session_consistent_login());

 fail:
  sqlite3_finalize(q);
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_Logout(CK_SESSION_HANDLE hSession)
{
  ENTER_PUBLIC_FUNCTION(C_Logout);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  /*
   * Mind, I don't really know why this function takes a session
   * handle, given that the semantics don't seem to call upon us to do
   * anything special for "this" session.
   */

  if (p11_session_find(hSession) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (logged_in_as == not_logged_in)
    lose(CKR_USER_NOT_LOGGED_IN);

  /*
   * Update global login state, then delete any private objects and
   * whack every existing session into the right state.
   */

  assert(p11_session_consistent_login());

  logged_in_as = not_logged_in;

  p11_object_delete_all_private();

  for (session = p11_sessions; session != NULL; session = session->link) {
    switch (session->state) {

    case CKS_RO_USER_FUNCTIONS:
      session->state = CKS_RO_PUBLIC_SESSION;
      continue;

    case CKS_RW_USER_FUNCTIONS:
    case CKS_RW_SO_FUNCTIONS:
      session->state = CKS_RW_PUBLIC_SESSION;
      continue;

    }
  }

  assert(p11_session_consistent_login());

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
                      CK_OBJECT_HANDLE hObject)
{
  ENTER_PUBLIC_FUNCTION(C_DestroyObject);

  static const char delete_object[] =
    " DELETE FROM object WHERE object_handle = ?";

  static const char delete_token_object[] =
    " DELETE FROM token_object"
    " WHERE token_object_id = (SELECT token_object_id FROM object WHERE object_handle = ?)";


  p11_session_t *session;
  sqlite3_stmt *q = NULL;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  session = p11_session_find(hSession);

  if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK)
    goto fail;

  if (is_token_handle(hObject)                                  &&
      (!sql_check_ok(sql_prepare(&q, delete_token_object))      ||
       !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))         ||
       !sql_check_done(sqlite3_step(q))                         ||
       !sql_check_ok(sql_finalize_and_clear(&q))))
    lose(CKR_FUNCTION_FAILED);

  if (!sql_check_ok(sql_prepare(&q, delete_object))             ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))          ||
      !sql_check_done(sqlite3_step(q)))
    lose(CKR_FUNCTION_FAILED);

 fail:
  sqlite3_finalize(q);
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
                          CK_OBJECT_HANDLE hObject,
                          CK_ATTRIBUTE_PTR pTemplate,
                          CK_ULONG ulCount)
{
  ENTER_PUBLIC_FUNCTION(C_GetAttributeValue);

  static const char select_format[] =
    " SELECT value FROM %s_attribute NATURAL JOIN object"
    " WHERE object_handle = ?1 AND type = ?2";

  const char *flavor = is_token_handle(hObject) ? "token" : "session";

  p11_session_t *session;
  const p11_descriptor_t *descriptor = NULL;
  CK_BBOOL cka_sensitive, cka_extractable;
  CK_OBJECT_CLASS cka_class;
  CK_KEY_TYPE cka_key_type;
  int sensitive_object = 0;
  sqlite3_stmt *q = NULL;
  CK_RV rv;
  int ret, i;

  mutex_lock_or_return_failure(p11_global_mutex);

  if (pTemplate == NULL)
    lose(CKR_ARGUMENTS_BAD);

  session = p11_session_find(hSession);

  if ((rv = p11_object_check_rights(session, hObject, p11_object_access_read)) != CKR_OK)
    goto fail;

  if (!p11_attribute_get_ulong(hObject, CKA_CLASS, &cka_class))
    lose(CKR_OBJECT_HANDLE_INVALID);

  switch (cka_class) {

  case CKO_PRIVATE_KEY:
  case CKO_SECRET_KEY:
    if (!p11_attribute_get_bbool(hObject, CKA_EXTRACTABLE, &cka_extractable) ||
        !p11_attribute_get_bbool(hObject, CKA_SENSITIVE,   &cka_sensitive))
      lose(CKR_OBJECT_HANDLE_INVALID);

    sensitive_object = cka_sensitive || !cka_extractable;

    /* Fall through */

  case CKO_PUBLIC_KEY:
    if (!p11_attribute_get_ulong(hObject, CKA_KEY_TYPE, &cka_key_type))
      lose(CKR_OBJECT_HANDLE_INVALID);
    descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type);
  }

  if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) ||
      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)))
    lose(CKR_FUNCTION_FAILED);

  rv = CKR_OK;

  for (i = 0; i < ulCount; i++) {

    if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) {
      pTemplate[i].ulValueLen = -1;
      rv = CKR_ATTRIBUTE_SENSITIVE;
    }

    else if (!sql_check_ok(sqlite3_reset(q)) ||
             !sql_check_ok(sqlite3_bind_int64(q, 2, pTemplate[i].type)) ||
             (ret = sqlite3_step(q)) != SQLITE_ROW) {
      if (ret != SQLITE_DONE)
        sql_whine_step();
      pTemplate[i].ulValueLen = -1;
      rv = CKR_ATTRIBUTE_TYPE_INVALID;
    }

    else if (pTemplate[i].pValue == NULL) {
      pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
    }

    else if (pTemplate[i].ulValueLen >= sqlite3_column_bytes(q, 0)) {
      pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
      memcpy(pTemplate[i].pValue, sqlite3_column_blob(q, 0), pTemplate[i].ulValueLen);
    }

    else {
      pTemplate[i].ulValueLen = -1;
      rv = CKR_BUFFER_TOO_SMALL;
    }

  }

 fail:
  sqlite3_finalize(q);
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
                        CK_ATTRIBUTE_PTR pTemplate,
                        CK_ULONG ulCount)
{
  ENTER_PUBLIC_FUNCTION(C_FindObjectsInit);

  static const char select_missing[] =
    " WITH"
    "   known AS (SELECT token_object_id FROM object WHERE token_object_id IS NOT NULL)"
    " SELECT token_object_id FROM token_object WHERE token_object_id NOT IN known";

  static const char insert_missing[] =
    " INSERT INTO object (object_handle, token_object_id) VALUES (?1, ?2)";

  static const char create_format[] =
    " CREATE TEMPORARY TABLE findobjects_%lu AS"
    " SELECT object_id FROM object NATURAL LEFT JOIN session"
    " WHERE session_handle IS NULL OR session_handle = ?1";

  static const char drop_format[] =
    " DROP TABLE IF EXISTS findobjects_%lu";

  static const char delete_format[] =
    " WITH"
    "   matches AS (SELECT object_id"
    "                 FROM object NATURAL JOIN session_attribute"
    "                 WHERE type = ?1 AND value = ?2"
    "               UNION"
    "               SELECT object_id"
    "                 FROM object NATURAL JOIN token_attribute"
    "                 WHERE type = ?1 AND value = ?2)"
    " DELETE FROM findobjects_%lu WHERE object_id NOT IN matches";

  static const char select_format[] =
    " SELECT object_handle FROM findobjects_%lu NATURAL JOIN object ORDER BY object_id";

  p11_session_t *session;
  sqlite3_stmt *q1 = NULL, *q2 = NULL;
  CK_RV rv = CKR_OK;
  int i, ret;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (ulCount > 0  && pTemplate == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->find_query != NULL)
    lose(CKR_OPERATION_ACTIVE);

  /*
   * Assign handles to any token objects that don't have them yet.
   */

  if (!sql_check_ok(sql_prepare(&q1, select_missing))   ||
      !sql_check_ok(sql_prepare(&q2, insert_missing)))
    lose(CKR_FUNCTION_FAILED);

  while ((ret = sqlite3_step(q1)) == SQLITE_ROW) {
    sqlite3_int64 token_object_id = sqlite3_column_int64(q1, 0);
    CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(handle_flavor_token_object);

    if (!sql_check_ok(sqlite3_reset(q2))                                ||
        !sql_check_ok(sqlite3_bind_int64(q2, 1, object_handle))         ||
        !sql_check_ok(sqlite3_bind_int64(q2, 2, token_object_id))       ||
        !sql_check_done(sqlite3_step(q2)))
      lose(CKR_FUNCTION_FAILED);
  }

  if (ret != SQLITE_DONE) {
    sql_whine_step();
    lose(CKR_FUNCTION_FAILED);
  }

  /*
   * Create a temporary table to hold this session's FindObjects
   * state.  Populate this with every object this session knows about,
   * then prune based on login status and whatever filter attributes
   * the caller supplied.
   */

  if (!sql_check_ok(sql_finalize_and_clear(&q1))                ||
      !sql_check_ok(sql_finalize_and_clear(&q2))                ||
      !sql_check_ok(sql_prepare(&q1, drop_format, hSession))    ||
      !sql_check_done(sqlite3_step(q1))                         ||
      !sql_check_ok(sql_prepare(&q2, create_format, hSession))  ||
      !sql_check_ok(sqlite3_bind_int64(q2, 1, hSession))        ||
      !sql_check_done(sqlite3_step(q2))                         ||
      !sql_check_ok(sql_finalize_and_clear(&q1))                ||
      !sql_check_ok(sql_finalize_and_clear(&q2))                ||
      !sql_check_ok(sql_prepare(&q1, delete_format, hSession)))
    lose(CKR_FUNCTION_FAILED);

  /*
   * If we're not logged in as the regular user, run an extra filter
   * cycle to remove all private objects before we get to the
   * caller-supplied template.
   */

  if (logged_in_as != logged_in_as_user) {
    if (!sql_check_ok(sqlite3_bind_int64(q1, 1, CKA_PRIVATE))   ||
        !sql_check_ok(sqlite3_bind_blob( q1, 2,
                                         &const_CK_FALSE,
                                         sizeof(const_CK_FALSE),
                                         NULL))                 ||
        !sql_check_done(sqlite3_step(q1)))
      lose(CKR_FUNCTION_FAILED);
  }

  /*
   * Filter through the caller-supplied template.
   *
   * NB: This doesn't support some of the more obscure searches, such
   * as searches for sessions or hardware features.  Too much rope
   * already, worry about those if we ever really need them.
   */

  for (i = 0; i < ulCount; i++)
    if (!sql_check_ok(sqlite3_reset(q1))                                ||
        !sql_check_ok(sqlite3_bind_int64(q1, 1, pTemplate[i].type))     ||
        !sql_check_ok(sqlite3_bind_blob( q1, 2, pTemplate[i].pValue,
                                        pTemplate[i].ulValueLen, NULL)) ||
        !sql_check_done(sqlite3_step(q1)))
      lose(CKR_FUNCTION_FAILED);

  /*
   * Stash a prepared query in the session object which will return
   * whatever object handles survived all that filtering.
   */

  if (!sql_check_ok(sql_prepare(&session->find_query, select_format, hSession)))
    lose(CKR_FUNCTION_FAILED);  
  session->find_query_done = 0;

 fail:
  sqlite3_finalize(q1);
  sqlite3_finalize(q2);
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
                    CK_OBJECT_HANDLE_PTR phObject,
                    CK_ULONG ulMaxObjectCount,
                    CK_ULONG_PTR pulObjectCount)
{
  ENTER_PUBLIC_FUNCTION(C_FindObjects);

  p11_session_t *session;
  int i, ret = SQLITE_OK;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (session->find_query == NULL)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  if (phObject == NULL || pulObjectCount == NULL)
    lose(CKR_ARGUMENTS_BAD);

  /*
   * C_FindObjectsInit() did all the heavy lifting, we just have to
   * return the resulting handles.
   */

  i = 0;

  if (!session->find_query_done)
    while (i < ulMaxObjectCount && (ret = sqlite3_step(session->find_query)) == SQLITE_ROW)
      phObject[i++] = (CK_OBJECT_HANDLE) sqlite3_column_int64(session->find_query, 0);

  switch (ret) {

  case SQLITE_DONE:
    session->find_query_done = 1;
    break;

  case SQLITE_OK:
  case SQLITE_ROW:
    break;

  default:
    sql_whine_step();
    lose(CKR_FUNCTION_FAILED);

  }

  *pulObjectCount = i;

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{
  ENTER_PUBLIC_FUNCTION(C_FindObjectsFinal);

  static const char drop_format[] =
    " DROP TABLE IF EXISTS findobjects_%lu";

  p11_session_t *session;
  sqlite3_stmt *q = NULL;
  CK_RV rv = CKR_OK;
  
  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (session->find_query == NULL)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  /*
   * Clean up result query and temporary table.
   */

  if (!sql_check_ok(sql_finalize_and_clear(&session->find_query))       ||
      !sql_check_ok(sql_prepare(&q, drop_format, hSession))             ||
      !sql_check_done(sqlite3_step(q)))
    lose(CKR_FUNCTION_FAILED);

 fail:
  sqlite3_finalize(q);
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,
                   CK_MECHANISM_PTR pMechanism)
{
  ENTER_PUBLIC_FUNCTION(C_DigestInit);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pMechanism == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->digest_descriptor != NULL)
    lose(CKR_OPERATION_ACTIVE);

  switch (pMechanism->mechanism) {
  case CKM_SHA_1:       session->digest_descriptor = hal_hash_sha1;   break;
  case CKM_SHA256:      session->digest_descriptor = hal_hash_sha256; break;
  case CKM_SHA384:      session->digest_descriptor = hal_hash_sha384; break;
  case CKM_SHA512:      session->digest_descriptor = hal_hash_sha512; break;
  default:              lose(CKR_MECHANISM_INVALID);
  }

  if (!hal_check(hal_hash_core_present(session->digest_descriptor))) {
    session->digest_descriptor = NULL;
    lose(CKR_MECHANISM_INVALID);
  }

  return mutex_unlock(p11_global_mutex);

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_Digest(CK_SESSION_HANDLE hSession,
               CK_BYTE_PTR pData,
               CK_ULONG ulDataLen,
               CK_BYTE_PTR pDigest,
               CK_ULONG_PTR pulDigestLen)
{
  ENTER_PUBLIC_FUNCTION(C_Digest);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pData == NULL || pulDigestLen == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->digest_descriptor == NULL)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  rv = *pulDigestLen < session->digest_descriptor->digest_length ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulDigestLen = session->digest_descriptor->digest_length;

  if (pDigest == NULL)
    return mutex_unlock(p11_global_mutex);

  if (rv == CKR_BUFFER_TOO_SMALL)
    lose(CKR_BUFFER_TOO_SMALL);

  {
    uint8_t statebuf[session->digest_descriptor->hash_state_length];
    hal_hash_state_t state = { NULL };

    if (!hal_check(hal_hash_initialize(session->digest_descriptor,
                                       &state, statebuf, sizeof(statebuf)))     ||
        !hal_check(hal_hash_update(state, pData, ulDataLen))                    ||
        !hal_check(hal_hash_finalize(state, pDigest, *pulDigestLen)))
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;                  /* Fall through */

 fail:
  if (session != NULL)
    session->digest_descriptor = NULL;
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
                 CK_MECHANISM_PTR pMechanism,
                 CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_SignInit);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pMechanism == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->sign_key_handle != CK_INVALID_HANDLE || session->sign_digest_descriptor != NULL)
    lose(CKR_OPERATION_ACTIVE);

  if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
    goto fail;

  /*
   * Will need to check key algorithm type here once we add support
   * for signature algorithms other than RSA.
   */

  session->sign_key_handle = hKey;

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:            session->sign_digest_descriptor = NULL;                 break;
  case CKM_SHA1_RSA_PKCS:       session->sign_digest_descriptor = hal_hash_sha1;        break;
  case CKM_SHA256_RSA_PKCS:     session->sign_digest_descriptor = hal_hash_sha256;      break;
  case CKM_SHA384_RSA_PKCS:     session->sign_digest_descriptor = hal_hash_sha384;      break;
  case CKM_SHA512_RSA_PKCS:     session->sign_digest_descriptor = hal_hash_sha512;      break;
  default:                      return CKR_MECHANISM_INVALID;
  }

  return mutex_unlock(p11_global_mutex);

 fail:
  if (session != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_descriptor = NULL;
  }
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_Sign(CK_SESSION_HANDLE hSession,
             CK_BYTE_PTR pData,
             CK_ULONG ulDataLen,
             CK_BYTE_PTR pSignature,
             CK_ULONG_PTR pulSignatureLen)
{
  ENTER_PUBLIC_FUNCTION(C_Sign);

  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t key = { NULL };
  p11_session_t *session;
  size_t signature_len;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pData == NULL || pulSignatureLen == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->sign_key_handle == CK_INVALID_HANDLE)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  /*
   * From here down this function is RSA-specific, and will need
   * rewriting when we add support for other algorithms.
   */

  if (!p11_object_get_rsa_private_key(session->sign_key_handle,
                                      &key, keybuf, sizeof(keybuf)))
    lose(CKR_FUNCTION_FAILED);

  /*
   * Retrieve signature length.  For RSA this is just the modulus
   * length, other algorithms will need a more generic solution.
   */

  if (!hal_check(hal_rsa_key_get_modulus(key, NULL, &signature_len, 0)))
    lose(CKR_FUNCTION_FAILED);

  rv = signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulSignatureLen = signature_len;

  if (pSignature == NULL) {
    hal_rsa_key_clear(key);
    return mutex_unlock(p11_global_mutex);
  }

  if (rv == CKR_BUFFER_TOO_SMALL)
    lose(CKR_BUFFER_TOO_SMALL);

  if (session->sign_digest_descriptor != NULL) {
    uint8_t digest_info[session->sign_digest_descriptor->digest_length + 4 +
                        session->sign_digest_descriptor->digest_algorithm_id_length];

    if (!pkcs1_construct_digestinfo(session->sign_digest_descriptor,
                                    pData, ulDataLen, digest_info, sizeof(digest_info)))
      lose(CKR_FUNCTION_FAILED);

    rv = sign_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, signature_len);
    memset(digest_info, 0, sizeof(digest_info));
    if (rv != CKR_OK)
      goto fail;
  }

  else {
    if ((rv = sign_rsa_pkcs(key, pData, ulDataLen, pSignature, signature_len)) != CKR_OK)
      goto fail;
  }

  rv = CKR_OK;                  /* Fall through */

 fail:

  if (session != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_descriptor = NULL;
  }

  hal_rsa_key_clear(key);

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
                   CK_MECHANISM_PTR pMechanism,
                   CK_OBJECT_HANDLE hKey )
{
  ENTER_PUBLIC_FUNCTION(C_VerifyInit);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pMechanism == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->verify_key_handle != CK_INVALID_HANDLE || session->verify_digest_descriptor != NULL)
    lose(CKR_OPERATION_ACTIVE);

  if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
    goto fail;

  /*
   * Will need to check key algorithm type here once we add support
   * for signature algorithms other than RSA.
   */

  session->verify_key_handle = hKey;

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:            session->verify_digest_descriptor = NULL;               break;
  case CKM_SHA1_RSA_PKCS:       session->verify_digest_descriptor = hal_hash_sha1;      break;
  case CKM_SHA256_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha256;    break;
  case CKM_SHA384_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha384;    break;
  case CKM_SHA512_RSA_PKCS:     session->verify_digest_descriptor = hal_hash_sha512;	break;
  default:                      return CKR_MECHANISM_INVALID;
  }

  return mutex_unlock(p11_global_mutex);

 fail:
  if (session != NULL) {
    session->verify_key_handle = CK_INVALID_HANDLE;
    session->verify_digest_descriptor = NULL;
  }
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_Verify(CK_SESSION_HANDLE hSession,
               CK_BYTE_PTR pData,
               CK_ULONG ulDataLen,
               CK_BYTE_PTR pSignature,
               CK_ULONG ulSignatureLen)
{
  ENTER_PUBLIC_FUNCTION(C_Verify);

  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t key = { NULL };
  p11_session_t *session;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pData == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->verify_key_handle == CK_INVALID_HANDLE)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  /*
   * From here down this function is RSA-specific, and will need
   * rewriting when we add support for other algorithms.
   */

  if (!p11_object_get_rsa_public_key(session->verify_key_handle,
                                     &key, keybuf, sizeof(keybuf)))
    lose(CKR_FUNCTION_FAILED);

  if (session->verify_digest_descriptor != NULL) {
    uint8_t digest_info[session->verify_digest_descriptor->digest_length + 4 +
                        session->verify_digest_descriptor->digest_algorithm_id_length];

    if (!pkcs1_construct_digestinfo(session->verify_digest_descriptor,
                                    pData, ulDataLen, digest_info, sizeof(digest_info)))
      lose(CKR_FUNCTION_FAILED);

    rv = verify_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, ulSignatureLen);
    memset(digest_info, 0, sizeof(digest_info));
    if (rv != CKR_OK)
      goto fail;
  }

  else {
    if ((rv = verify_rsa_pkcs(key, pData, ulDataLen, pSignature, ulSignatureLen)) != CKR_OK)
      goto fail;
  }

  rv = CKR_OK;                  /* Fall through */

 fail:

  if (session != NULL) {
    session->verify_key_handle = CK_INVALID_HANDLE;
    session->verify_digest_descriptor = NULL;
  }

  hal_rsa_key_clear(key);

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

/*
 * If there's any method in this entire package which really needs a
 * more complex mutex structure than the single global mutex, it's
 * probably this one.  Key generation can take a looooong time.
 * Drive off that bridge when we get to it.
 */

CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
                        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)
{
  ENTER_PUBLIC_FUNCTION(C_GenerateKeyPair);

  p11_session_t *session;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (pMechanism          == NULL ||
      pPublicKeyTemplate  == NULL || phPublicKey  == NULL ||
      pPrivateKeyTemplate == NULL || phPrivateKey == NULL)
    lose(CKR_ARGUMENTS_BAD);

  switch (pMechanism->mechanism) {

  case CKM_RSA_PKCS_KEY_PAIR_GEN:
    rv = generate_keypair_rsa_pkcs(session, pMechanism,
                                   pPublicKeyTemplate, ulPublicKeyAttributeCount,
                                   pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                                   phPublicKey, phPrivateKey);
    break;

  default:
    lose(CKR_MECHANISM_INVALID);
  }

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
                       CK_BYTE_PTR RandomData,
                       CK_ULONG ulRandomLen)
{
  ENTER_PUBLIC_FUNCTION(C_GenerateRandom);

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  if ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

  if (RandomData == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (!hal_check(hal_get_random(RandomData, ulRandomLen)))
    lose(CKR_FUNCTION_FAILED);

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

/*
 * Supply information about a particular mechanism.  We may want a
 * more generic structure for this, for the moment, just answer the
 * questions hsmbully is asking.
 *
 * Not really sure whether I should be setting CKF_HW here or not, RSA
 * is a mix of hardware and software at the moment, but I'm also a
 * little unclear on what "the device" means in this context, so let's
 * just say that if it's implemented by libhal or the Verilog hiding
 * behind libhal, it's implemented in hardware.
 */

CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
                         CK_MECHANISM_TYPE type,
                         CK_MECHANISM_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetMechanismInfo);

  /*
   * No locking here, no obvious need for it.
   */

  if (pInfo == NULL)
    return CKR_ARGUMENTS_BAD;

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    return CKR_SLOT_ID_INVALID;

  switch (type) {
    
  case CKM_RSA_PKCS_KEY_PAIR_GEN:
    pInfo->ulMinKeySize = 1024;
    pInfo->ulMaxKeySize = 8192;
    pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR;
    break;

  case CKM_RSA_PKCS:
    pInfo->ulMinKeySize = 1024;
    pInfo->ulMaxKeySize = 8192;
    pInfo->flags = CKF_HW | CKF_SIGN;
    break;

  default:
    return CKR_MECHANISM_INVALID;
  }

  return CKR_OK;
}




/*
 * Stubs for unsupported functions below here.  Per the PKCS #11
 * specification, it's OK to skip implementing almost any function in
 * the API, but if one does so, one must provide a stub which returns
 * CKR_FUNCTION_NOT_SUPPORTED, because every slot in the dispatch
 * vector must be populated.  We could reuse a single stub for all the
 * unimplemented slots, but the type signatures wouldn't match, which
 * would require some nasty casts I'd rather avoid.
 *
 * Many of these functions would be straightforward to implement, but
 * there are enough bald yaks in this saga already.
 */

CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism,
                    CK_ATTRIBUTE_PTR pTemplate,
                    CK_ULONG ulCount,
                    CK_OBJECT_HANDLE_PTR phKey)
{
  ENTER_PUBLIC_FUNCTION(C_GenerateKey);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetInfo(CK_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetInfo);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetSlotInfo(CK_SLOT_ID slotID,
                    CK_SLOT_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetSlotInfo);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
                         CK_MECHANISM_TYPE_PTR pMechanismList,
                         CK_ULONG_PTR pulCount)
{
  ENTER_PUBLIC_FUNCTION(C_GetMechanismList);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_InitToken(CK_SLOT_ID slotID,
                  CK_UTF8CHAR_PTR pPin,
                  CK_ULONG ulPinLen,
                  CK_UTF8CHAR_PTR pLabel)
{
  ENTER_PUBLIC_FUNCTION(C_InitToken);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
                CK_UTF8CHAR_PTR pPin,
                CK_ULONG ulPinLen)
{
  ENTER_PUBLIC_FUNCTION(C_InitPIN);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
               CK_UTF8CHAR_PTR pOldPin,
               CK_ULONG ulOldLen,
               CK_UTF8CHAR_PTR pNewPin,
               CK_ULONG ulNewLen)
{
  ENTER_PUBLIC_FUNCTION(C_SetPIN);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
                       CK_SESSION_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetSessionInfo);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
                          CK_BYTE_PTR pOperationState,
                          CK_ULONG_PTR pulOperationStateLen)
{
  ENTER_PUBLIC_FUNCTION(C_GetOperationState);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession,
                          CK_BYTE_PTR pOperationState,
                          CK_ULONG ulOperationStateLen,
                          CK_OBJECT_HANDLE hEncryptionKey,
                          CK_OBJECT_HANDLE hAuthenticationKey)
{
  ENTER_PUBLIC_FUNCTION(C_SetOperationState);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
                     CK_ATTRIBUTE_PTR pTemplate,
                     CK_ULONG ulCount,
                     CK_OBJECT_HANDLE_PTR phObject)
{
  ENTER_PUBLIC_FUNCTION(C_CreateObject);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,
                   CK_OBJECT_HANDLE hObject,
                   CK_ATTRIBUTE_PTR pTemplate,
                   CK_ULONG ulCount,
                   CK_OBJECT_HANDLE_PTR phNewObject)
{
  ENTER_PUBLIC_FUNCTION(C_CopyObject);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
                      CK_OBJECT_HANDLE hObject,
                      CK_ULONG_PTR pulSize)
{
  ENTER_PUBLIC_FUNCTION(C_GetObjectSize);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
                          CK_OBJECT_HANDLE hObject,
                          CK_ATTRIBUTE_PTR pTemplate,
                          CK_ULONG ulCount)
{
  ENTER_PUBLIC_FUNCTION(C_SetAttributeValue);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism,
                    CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_EncryptInit);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,
                CK_BYTE_PTR pData,
                CK_ULONG ulDataLen,
                CK_BYTE_PTR pEncryptedData,
                CK_ULONG_PTR pulEncryptedDataLen)
{
  ENTER_PUBLIC_FUNCTION(C_Encrypt);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pPart,
                      CK_ULONG ulPartLen,
                      CK_BYTE_PTR pEncryptedPart,
                      CK_ULONG_PTR pulEncryptedPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_EncryptUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pLastEncryptedPart,
                     CK_ULONG_PTR pulLastEncryptedPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_EncryptFinal);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism,
                    CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_DecryptInit);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_Decrypt(CK_SESSION_HANDLE hSession,
                CK_BYTE_PTR pEncryptedData,
                CK_ULONG ulEncryptedDataLen,
                CK_BYTE_PTR pData,
                CK_ULONG_PTR pulDataLen)
{
  ENTER_PUBLIC_FUNCTION(C_Decrypt);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pEncryptedPart,
                      CK_ULONG ulEncryptedPartLen,
                      CK_BYTE_PTR pPart,
                      CK_ULONG_PTR pulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DecryptUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pLastPart,
                     CK_ULONG_PTR pulLastPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DecryptFinal);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pPart,
                     CK_ULONG ulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DigestUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
                  CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_DigestKey);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
                    CK_BYTE_PTR pDigest,
                    CK_ULONG_PTR pulDigestLen)
{
  ENTER_PUBLIC_FUNCTION(C_DigestFinal);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
                   CK_BYTE_PTR pPart,
                   CK_ULONG ulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_SignUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
                  CK_BYTE_PTR pSignature,
                  CK_ULONG_PTR pulSignatureLen)
{
  ENTER_PUBLIC_FUNCTION(C_SignFinal);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
                        CK_MECHANISM_PTR pMechanism,
                        CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_SignRecoverInit);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignRecover(CK_SESSION_HANDLE hSession,
                    CK_BYTE_PTR pData,
                    CK_ULONG ulDataLen,
                    CK_BYTE_PTR pSignature,
                    CK_ULONG_PTR pulSignatureLen)
{
  ENTER_PUBLIC_FUNCTION(C_SignRecover);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pPart,
                     CK_ULONG ulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_VerifyUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
                    CK_BYTE_PTR pSignature,
                    CK_ULONG ulSignatureLen)
{
  ENTER_PUBLIC_FUNCTION(C_VerifyFinal);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
                          CK_MECHANISM_PTR pMechanism,
                          CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_VerifyRecoverInit);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,
                      CK_BYTE_PTR pSignature,
                      CK_ULONG ulSignatureLen,
                      CK_BYTE_PTR pData,
                      CK_ULONG_PTR pulDataLen)
{
  ENTER_PUBLIC_FUNCTION(C_VerifyRecover);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,
                            CK_BYTE_PTR pPart,
                            CK_ULONG ulPartLen,
                            CK_BYTE_PTR pEncryptedPart,
                            CK_ULONG_PTR pulEncryptedPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DigestEncryptUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
                            CK_BYTE_PTR pEncryptedPart,
                            CK_ULONG ulEncryptedPartLen,
                            CK_BYTE_PTR pPart,
                            CK_ULONG_PTR pulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DecryptDigestUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,
                          CK_BYTE_PTR pPart,
                          CK_ULONG ulPartLen,
                          CK_BYTE_PTR pEncryptedPart,
                          CK_ULONG_PTR pulEncryptedPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_SignEncryptUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
                            CK_BYTE_PTR pEncryptedPart,
                            CK_ULONG ulEncryptedPartLen,
                            CK_BYTE_PTR pPart,
                            CK_ULONG_PTR pulPartLen)
{
  ENTER_PUBLIC_FUNCTION(C_DecryptVerifyUpdate);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
                CK_MECHANISM_PTR pMechanism,
                CK_OBJECT_HANDLE hWrappingKey,
                CK_OBJECT_HANDLE hKey,
                CK_BYTE_PTR pWrappedKey,
                CK_ULONG_PTR pulWrappedKeyLen)
{
  ENTER_PUBLIC_FUNCTION(C_WrapKey);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
                  CK_MECHANISM_PTR pMechanism,
                  CK_OBJECT_HANDLE hUnwrappingKey,
                  CK_BYTE_PTR pWrappedKey,
                  CK_ULONG ulWrappedKeyLen,
                  CK_ATTRIBUTE_PTR pTemplate,
                  CK_ULONG ulAttributeCount,
                  CK_OBJECT_HANDLE_PTR phKey)
{
  ENTER_PUBLIC_FUNCTION(C_UnwrapKey);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,
                  CK_MECHANISM_PTR pMechanism,
                  CK_OBJECT_HANDLE hBaseKey,
                  CK_ATTRIBUTE_PTR pTemplate,
                  CK_ULONG ulAttributeCount,
                  CK_OBJECT_HANDLE_PTR phKey)
{
  ENTER_PUBLIC_FUNCTION(C_DeriveKey);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
                   CK_BYTE_PTR pSeed,
                   CK_ULONG ulSeedLen)
{
  ENTER_PUBLIC_FUNCTION(C_SeedRandom);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
{
  ENTER_PUBLIC_FUNCTION(C_GetFunctionStatus);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
{
  ENTER_PUBLIC_FUNCTION(C_CancelFunction);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
                         CK_SLOT_ID_PTR pSlot,
                         CK_VOID_PTR pRserved)
{
  ENTER_PUBLIC_FUNCTION(C_WaitForSlotEvent);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

/*
 * "Any programmer who fails to comply with the standard naming, formatting,
 *  or commenting conventions should be shot.  If it so happens that it is
 *  inconvenient to shoot him, then he is to be politely requested to recode
 *  his program in adherence to the above standard."
 *                      -- Michael Spier, Digital Equipment Corporation
 *
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */