aboutsummaryrefslogblamecommitdiff
path: root/pkcs11.c
blob: 83d0ed4f1cce08820e350c7dcb963269013e65ac (plain) (tree)
1
2
3
4
5
6
7
8
9
  


           

                                                                      


                            


















                                                                       
                                                                   



                                                                   





                                             
                   

                   
                
















                                                                           
 
                       
                       









                                                                     
                                          











                                                               


                     
                 
                         


                    
                         

      
  






                                              











                                                    

                   


                  



                    






                    




                                                                      
  



                                                                      

   







                                                                           
                             





                                                                  
                  


                                                                        








































                                                                               








                                                                      






















                                                                     



                                                                  






                                                                     

   

                






                                                                         


















                                                                         
                                                    






                                     
  
                                   

   
             
 
                                                                                                                   


                    
                                                                                      
           

 
                                                                             
 
                      
 


                                              



  































                                                                                       




                                                 

             





                                                 




                                            
                






















                                                        
   





















































































                                                               
                         



  






























                                                                                                          



                                                                      
  



                                                                      

   
                         
 

                                      
 

                                  
 
                         
 

                                                                  
 

                                       
 






                                                                  
 
                                      

   

                      




































































































































































                                                                                   
 


















                                                                                                                            

     




                                                                                                                            

      




















                                                                                                                       
                       
                                    






                                   












                                                                                  









                                                                   


                                                                 

   



                                                








                                                                                                                 
                                     


                                                                     
                                     


                                                                                 
                                    


















































































                                                                                                             



                                                            




















                                                                                       
                                                                 










                                                                                                      









                                                                  



                                                                        

                                                                  


























                                                                  






                                                                                                  
             




































































                                                                                    

                                                







                                                                          

                                                                          

                                                                          


                                                                          









                                                                          

                                                                          


                                                                          


                                                                          



















































                                                                                            
 






















                                                                                                                  
                       
  

                                                                   


                                                                      

   



                                                                                                                           
 

                                  

                                     

                                                                                       
 
                                                                  
                                                                            
                                                


                         

                 


                                                                                  
                                                                                  

                                                            
                                                                                  

                                                                                  
                                                                                  

                                                                                  

              


         
                                      




                      
















































                                                                               
  
                                                                
                                    
  
                                                         

   




                                                                                                                         
 

                                                                                                   
 




                                                                            

    
                                               

     





                                                                  
 
                                                      
                







                                                           

                                   
 
                                                                                              
                                                                              



                                        
                          



                                  

 


























































                                                                                                                   





                                                                      
                


                                                                              
                                                             



























                                                                                               








































                                                                                               















                                                    











                                                    
                                               


                                                  































































                                                                              





                                                                       





                                                            


                                      


















                                                                  

   

                                                 

                                         


                                           

                         










                                     
































































                                                                                             
                                                                   







                                                                      




                                                                            




                                                                                                    
                                                        













                                                                                                                          

                                 


              

                   
                                                                                                 
      







                                                                     





                                                                            












                                                                     
                                                                                                                     









                                                                                                               

                                                                    










                                                                                                        


                                                                


















                                                                      
  
                                                        
  

                                                                      










                                                                                            

























                                                                     

                                                                                    


                                                              






                                                                     

                                                                                    


                                                               










                                                                     


                                                            











                                                                                     












                                             
                                                         


                                                              



                                                                                  

                                                                             
 


                                                      
                            
                       
                     


           
                                                             




                                                                    


                   
                                                                                  




                                           



                                


     










                                                                           
 












































                                                                                           


                   


                       
               


     
                                                        

                                   
                                    
 








































































                                                                                                         
                                                                                       
                                                                                              

                              



                                                                                                
 




                                                                              



                              




                                 

                            


            






























































                                                                                        

 
  


























                                                                             




                                                                               
                                                              

                                                                                         
                                                               












                                                                                        

                           










                                                                         
                                                                     





































                                                                                     

                                                                 








                                                           
                                       





                                                                 




                                                        
 


                                     

           
                                                     
 


                                                                                                               
 

                                                                    

                              
































                                                                                                        

      
                         


            









                                                                   




                                                     
 


                                     
 





                                                                                                               
                    
 











                                                                      
                                    



                                                                 

                             
                                                                                               

                              
                                          




                                      
                                                  

      
                         


            



























































                                                                                              
                                                                                                   


                                                                                          

                                            

















































                                                                                                                         







                                         

                                      
                                         
                          

           



































                                                                      
                  




                                                               


                                 
                                                                    





                                                              



                                                                     

       







                                                   
                




                                             

                          
     


    












                                                                     








                                                                
                  
                            
 



                             



                







                                       

                                    

                    


                             
                                                 
 










                                  
                            

    











                                                                   



                                                                

                                           


                                                                      

                                                                




















                                                       

                                       

                                               
                                                    


















                                             

                                        



                                                                        









                                                                    















































































                                                                      

                                       




                                                                 
                                                 
 


























                                                                                       
   








                                         


                                                      




                               
                                        




                                                

                                        

           
                                                 
 
                                    
 
                                                    



                                           

                                            


                                      
                                                 
 

                           
                                        






                                         

                                 
                                        
                                                                 
 
                         

                         
                    
 
                                                 
 
                   
                            







                                                                      
                                     

    




                                                                    
                                     

    
                                         
                                                  
    



                                                                     




                      

                                                                          
                                           






                                        

    



                                                                       

     





















                                                                             
 
   















                                                                              
                              
   

    

                                                                     






















                                                                                        
      
                      
                                                    



                                          

                                  
                         
                    
 
                                                 
 






                                                                      
                                     
 

                                    




























                                                                          
      
                                                    

 




















































































                                                                                                                   


                                                 

                                         



                                                  


                                                                                            
 
                         
                         

                    
                                                 

                                       



                                                                                          

                                                                  


                                                                  

                              

                                                                  




                                       
                                                    






                                                     

                                             





                                                                      
                         





                                            
           

             
                                                 
 


                            

                                       



























                                                                                         

              

































                                                                                        
                                                    





                                                   

                                           





























                                                                                        
                         



                                      
                                                 

                                                     































                                                                                            






                                                                      


                                                                  


                                                                  



                                                                  


                              


                                                                    


                                          




                                                                  

























                                                                                
                              




                               
                                                    






                                                  

                                       
                         


                         
                                                 

                                                     





































                                                                                           
                                                    



                                                    

                                            


                                            
                         

                         
 
                                                 

                                                     








                                               

                                                                          




                                       
                                                    




                                               

                                      
                         
                    
 
                                                 


                                                     

                         
                            
 
                                         
                               

                                  



                                                                            
                                                    

   



                                                                      
 
                                        

      
                                                    







                                          

                                  

                         
 
                                                 


                                                     

                                            
                            
 
                                         
                                        
 
                                    

                               
                                                                                                 
 
                                                            
 

                                          
 
                                 

                               


                                                                             
 

                                                                                   



                                                  

                                             
                                      
   
                                                    

 






















                                                     



                                                       



























                                                     
                                                                          
























                                                                                                 



                                             

                                    
                         


                            
                    
 
                                                 


                                                     

                         
                            
 
                                                                                               
                               



                                                                                      



























                                                                  


                                  
                                  




















                                                      

   
                                        

      


                                                 
   
                                                    







                                          

                                
                         
                       

           
                                                 


                                                     

                                               
                            
 
                                                    
                                        
 

                                         
 
                                                                      


                                                                                    
 
                                                                                  
                              
 
                     
 


                                                                               
 



                                                                            

                              

   
                                                  
 
                                              

                                                 
                                                  

   









                                                    


                            





                                                     
 

                            
 

                                                                                                   
 

                                                                                      
 

                                                                  

                                                                  























                                         



                                    




















                                                        



















                                                    
                         
                       












                                                      



                                                                                      
 
                                                                                    

                              
                     
 


                                                                                
 



                                                                             

                              
   

                                                  
 
                        

                                                   
                                                    
   
 
                                                    


  





                                                                    








                                                             

                                           

                         
 
                                                 


                                                     



                                                            
                            

                                  
 
                                 
















                                                                          

          
          
                                
   

      
                                                    





                                                  

                                          
                         

                    
                                                 


                                                     

                         
                            
 
                                                          


                              
                                                    

 


                                                                   
                                                        











                                                                      

                                            

                                    

                                   
 










                                             
 


                         
                      
                                                       





                                   
                        
                                                         





                                   
                        
                                                         





                                   
                        
                                                         








                                   
                                 

                                      


                                                  





                                                                                                       
                    








                                                  








                                                                                                       






                                       

          









                                                                           






                                 
































                                                     
 


  











                                                                      




                                                



                                       
 
                                  



                                    


                                           



                                       



                                                              



                                            
 



                                         



                                     



                                           



                                    





                                          



                                    
 


                                                            



                                             





                                                              



                                             
 




                                                    



                                      



                                                 



                                         




                                                     



                                             



                                                



                                       





                                                 



                                    





                                                       



                                         



                                                          



                                        



                                                



                                       





                                            



                                    





                                                  



                                         



                                                 



                                        
 

                                             



                                     
 


                                              



                                      



                                               



                                     



                                                    



                                           





                                                 



                                       



                                                



                                        



                                               



                                       



                                                      



                                             





                                                 



                                         





                                                             



                                               





                                                        



                                               





                                                           



                                             





                                                        



                                               






                                              



                                    








                                                  



                                     






                                              



                                     



                                              



                                      

                                                     



                                             

                                                  



                                          



                                              



                                            











                                                                            
/*
 * pkcs11.c
 * --------
 *
 * This is a partial implementation of PKCS #11 on top of the Cryptech
 * libhal library connecting to the Cryptech FPGA cores.
 *
 * 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 <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"
#include "sql_common.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

/*
 * Version numbers.  Placeholders for now.
 *
 * 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_HAL
#define DEBUG_HAL       1
#endif

#ifndef DEBUG_PKCS11
#define DEBUG_PKCS11    1
#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.
 *
 * 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() */
  hal_hash_state_t
    *digest_state,                      /* Hash state for C_Digest*() */
    *sign_digest_state,                 /* Hash state for C_Sign*() */
    *verify_digest_state;               /* Hash state 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;

/*
 * 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 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 */



/*
 * Translate between libhal EC curve names and OIDs.
 */
#warning Perhaps this should be  a utility routine in libhal instead of here

static int ec_curve_oid_to_name(const uint8_t * const oid, const size_t oid_len, hal_ecdsa_curve_t *curve)
{
  static uint8_t ec_curve_oid_p256[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 };
  static uint8_t ec_curve_oid_p384[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 };
  static uint8_t ec_curve_oid_p521[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 };

  if (oid == NULL || curve == NULL)
    return 0;

  else if (oid_len == sizeof(ec_curve_oid_p256) && memcmp(oid, ec_curve_oid_p256, oid_len) == 0)
    *curve = HAL_ECDSA_CURVE_P256;

  else if (oid_len == sizeof(ec_curve_oid_p384) && memcmp(oid, ec_curve_oid_p384, oid_len) == 0)
    *curve = HAL_ECDSA_CURVE_P384;

  else if (oid_len == sizeof(ec_curve_oid_p521) && memcmp(oid, ec_curve_oid_p521, oid_len) == 0)
    *curve = HAL_ECDSA_CURVE_P521;

  else
    return 0;

  return 1;
}



/*
 * 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));
}

#if 0

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));
}

#endif

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)
{
  if (template != NULL)
    for (int i = 0; i < length; i++)
      if (template[i].type == type)
        return i;

  return -1;
}

/*
 * Find an attribute in a CK_ATTRIBUTE_PTR template.  Returns pointer
 * to attribute value, or NULL if not found.
 */

static void *p11_attribute_find_value_in_template(const CK_ATTRIBUTE_TYPE type,
                                                  const CK_ATTRIBUTE_PTR template,
                                                  const CK_ULONG length)
{
  const int i = p11_attribute_find_in_template(type, template, length);
  return i < 0 ? NULL : template[i].pValue;
}

/*
 * 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 a private key.
 *
 * Write the key as 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_generic_private_key(const CK_OBJECT_HANDLE object_handle,
                                              const void * const key,
                                              const size_t key_der_len,
                                              hal_error_t (*to_der)(const void * const, uint8_t *, size_t *, const size_t))
{
  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(key_der_len)];
  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;

  assert(to_der);

  if (!sql_check_ok(sql_prepare(&q, select_kek))                                ||
      !sql_check_row(sqlite3_step(q))                                           ||
      sqlite3_column_type(q, 0) == SQLITE_NULL                                  ||
      !hal_check(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;
}

/*
 * Store an RSA private key.
 *
 * Use p11_object_set_generic_private_key() to wrap the PKCS #1.5
 * RSAPrivateKey encoding of the private key.
 */

static hal_error_t p11_object_encode_rsa_key(const void * const key,
                                             uint8_t * der,
                                             size_t *der_len,
                                             const size_t der_max)
{
  return hal_rsa_key_to_der(key, der, der_len, der_max);
}

static int p11_object_set_rsa_private_key(const CK_OBJECT_HANDLE object_handle,
                                          const hal_rsa_key_t * const key)
{
  return p11_object_set_generic_private_key(object_handle,
                                            key,
                                            hal_rsa_key_to_der_len(key),
                                            p11_object_encode_rsa_key);
}

/*
 * Store an EC private key.
 *
 * Use p11_object_set_generic_private_key() to wrap the RFC 5915
 * ECPrivateKey encoding of the private key.
 */

static hal_error_t p11_object_encode_ec_key(const void * const key,
                                            uint8_t * der,
                                            size_t *der_len,
                                            const size_t der_max)
{
  return hal_ecdsa_key_to_der(key, der, der_len, der_max);
}

static int p11_object_set_ec_private_key(const CK_OBJECT_HANDLE object_handle,
                                         const hal_ecdsa_key_t * const key)
{
  return p11_object_set_generic_private_key(object_handle,
                                            key,
                                            hal_ecdsa_key_to_der_len(key),
                                            p11_object_encode_ec_key);
}

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

static int p11_object_get_generic_private_key(const CK_OBJECT_HANDLE object_handle,
                                              void **key,
                                              uint8_t *keybuf, const size_t keybuf_len,
                                              hal_error_t (*from_der)(void **, void *,
                                                                      const size_t, const uint8_t * const, const size_t))
{
  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 = 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(from_der(key, keybuf, keybuf_len, wrapbuf, wrapbuf_len)));

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

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

  sqlite3_finalize(q);
  return ok;
}

/*
 * Fetch an RSA private key.
 *
 * Use p11_object_get_generic_private_key() to unwrap the DER encoding
 * of a PKCS #1.5 RSAPrivateKey object.
 */

static hal_error_t p11_object_decode_rsa_key(void **key_,
                                             void *keybuf, const size_t keybuf_len,
                                             const uint8_t * const der, const size_t der_len)
{
  assert(key_ != NULL);
  hal_rsa_key_t *key = NULL;
  hal_error_t err = hal_rsa_key_from_der(&key, keybuf, keybuf_len, der, der_len);
  *key_ = key;
  return err;
}

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)
{
  assert(key != NULL);
  void *key_ = NULL;
  int ok = p11_object_get_generic_private_key(object_handle, &key_, keybuf, keybuf_len, p11_object_decode_rsa_key);
  *key = key_;
  return ok;
}

/*
 * Fetch an EC private key.
 *
 * Use p11_object_get_generic_private_key() to unwrap the DER encoding
 * of an RFC 5915 ECPrivateKey object.
 */

static hal_error_t p11_object_decode_ec_key(void **key_,
                                             void *keybuf, const size_t keybuf_len,
                                             const uint8_t * const der, const size_t der_len)
{
  assert(key_ != NULL);
  hal_ecdsa_key_t *key = NULL;
  hal_error_t err = hal_ecdsa_key_from_der(&key, keybuf, keybuf_len, der, der_len);
  *key_ = key;
  return err;
}

static int p11_object_get_ec_private_key(const CK_OBJECT_HANDLE object_handle,
                                          hal_ecdsa_key_t **key,
                                          uint8_t *keybuf, const size_t keybuf_len)
{
  assert(key != NULL);
  void *key_ = NULL;
  int ok = p11_object_get_generic_private_key(object_handle, &key_, keybuf, keybuf_len, p11_object_decode_ec_key);
  *key = key_;
  return ok;
}

#warning Revisit return semantics of p11_object_get_*_private_key() and p11_object_*_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 object.
 */

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;
}

/*
 * Fetch an EC 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 object.
 */

static int p11_object_get_ec_public_key(const CK_OBJECT_HANDLE object_handle,
                                        hal_ecdsa_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;
  hal_ecdsa_curve_t curve;

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

  const int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor,
                                           CKA_EC_PARAMS, CKA_EC_POINT))        &&
                  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                      &&
                  ec_curve_oid_to_name(sqlite3_column_blob( q, 0),
                                       sqlite3_column_bytes(q, 0),
                                       &curve)                                  &&
                  hal_check(hal_ecdsa_key_from_ecpoint(key, keybuf, keybuf_len,
                                                       sqlite3_column_blob( q, 1),
                                                       sqlite3_column_bytes(q, 1),
                                                       curve)));

  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);
  hal_hash_cleanup(&session->digest_state);
  hal_hash_cleanup(&session->sign_digest_state);
  hal_hash_cleanup(&session->verify_digest_state);
  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.
 *
 * The checking code needs refactoring to support 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_template_check_1(const CK_ATTRIBUTE_TYPE type,
                                  const void * const val,
                                  const size_t len,
                                  const p11_descriptor_t * const descriptor,
                                  unsigned long forbidden_flag)
{
  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 & forbidden_flag) != 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);

#warning Add _LATCH checks here? 

  rv = CKR_OK;

 fail:
#if DEBUG_PKCS11
  if (rv != CKR_OK)
    fprintf(stderr, "p11_template_check_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_template_check_2(const p11_session_t *session,
                                  const p11_descriptor_t * const descriptor,
                                  const CK_ATTRIBUTE_PTR template,
                                  const CK_ULONG template_length,
                                  unsigned long required_flag,
                                  unsigned long forbidden_flag)
{
  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 ((object_is_private = p11_attribute_find_value_in_template(CKA_PRIVATE, template, template_length)) == NULL) {
      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 & required_flag) != 0;
    const int forbidden_by_api = (atd->flags & forbidden_flag) != 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_template_check_1(type, val, len, public_descriptor,
                                   P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != 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_template_check_1(type, val, len, private_descriptor,
                                   P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != 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_template_check_2(session,
                                 public_descriptor,
                                 pPublicKeyTemplate,
                                 ulPublicKeyAttributeCount,
                                 P11_DESCRIPTOR_REQUIRED_BY_GENERATE,
                                 P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE))  != CKR_OK ||
      (rv = p11_template_check_2(session,
                                 private_descriptor,
                                 pPrivateKeyTemplate,
                                 ulPrivateKeyAttributeCount,
                                 P11_DESCRIPTOR_REQUIRED_BY_GENERATE,
                                 P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != 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 handler.
 */

static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
                                       const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                       const CK_ULONG ulPublicKeyAttributeCount,
                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                       const CK_ULONG ulPrivateKeyAttributeCount,
                                       const CK_OBJECT_HANDLE private_handle,
                                       const CK_OBJECT_HANDLE public_handle)
{
  const uint8_t *public_exponent = const_0x010001;
  size_t public_exponent_len = sizeof(const_0x010001);
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  CK_ULONG keysize = 0;
  size_t modulus_len;
  CK_RV rv;
  int i;

  assert(pPublicKeyTemplate  != NULL && pPrivateKeyTemplate);

  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;

    switch (type) {

    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;

    case CKA_PUBLIC_EXPONENT:
      public_exponent = val;
      public_exponent_len = len;
      continue;
    }
  }

  if (keysize == 0)
    return CKR_TEMPLATE_INCOMPLETE;

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

  if (!hal_check(hal_rsa_key_gen(&key, keybuf, sizeof(keybuf), keysize / 8,
                                 public_exponent, public_exponent_len)))
    lose(CKR_FUNCTION_FAILED);

  if (!p11_object_set_rsa_private_key(private_handle, key))
    lose(CKR_FUNCTION_FAILED);

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

  {
    uint8_t modulus[modulus_len];

    if (!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);
  }

  rv = CKR_OK;

 fail:
  hal_rsa_key_clear(key);
  return rv;
}

/*
 * CKM_EC_KEY_PAIR_GEN key pair generation handler.
 */

static CK_RV generate_keypair_ec(p11_session_t *session,
                                       const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                       const CK_ULONG ulPublicKeyAttributeCount,
                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                       const CK_ULONG ulPrivateKeyAttributeCount,
                                       const CK_OBJECT_HANDLE private_handle,
                                       const CK_OBJECT_HANDLE public_handle)
{
  uint8_t keybuf[hal_ecdsa_key_t_size];
  hal_ecdsa_key_t *key = NULL;
  const CK_BYTE *params = NULL;
  hal_ecdsa_curve_t curve;
  size_t params_len;
  CK_RV rv;
  int i;

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

  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;

    switch (type) {

    case CKA_EC_PARAMS:
      params = val;
      params_len = len;
      continue;
    }
  }

  if (!ec_curve_oid_to_name(params, params_len, &curve))
    return CKR_TEMPLATE_INCOMPLETE;

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

  if (!hal_check(hal_ecdsa_key_gen(&key, keybuf, sizeof(keybuf), curve))        ||
      !p11_object_set_ec_private_key(private_handle, key)                       ||
      !p11_attribute_set(public_handle,  CKA_EC_PARAMS, params, params_len)     ||
      !p11_attribute_set(private_handle, CKA_EC_PARAMS, params, params_len))
    lose(CKR_FUNCTION_FAILED);

  {
    uint8_t point[hal_ecdsa_key_to_ecpoint_len(key)];

    if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point)))   ||
        !p11_attribute_set(public_handle,  CKA_EC_POINT, point, sizeof(point)))
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;

 fail:
  hal_ecdsa_key_clear(key);
  return rv;
}

/*
 * Key pair generation.  This needs a mechanism-specific function to
 * do the inner bits, but there's a lot of boilerplate.
 */

static CK_RV generate_keypair(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_RV (*mechanism_handler)(p11_session_t *session,
                                                         const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                                         const CK_ULONG ulPublicKeyAttributeCount,
                                                         const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                                         const CK_ULONG ulPrivateKeyAttributeCount,
                                                         const CK_OBJECT_HANDLE private_handle,
                                                         const CK_OBJECT_HANDLE public_handle),
                              const p11_descriptor_t * const public_descriptor,
                              const p11_descriptor_t * const private_descriptor)
{
  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;
  CK_RV rv;
  int i;

  rv = p11_check_keypair_attributes(session,
                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,  public_descriptor,
                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor);
  if (rv != CKR_OK)
    return rv;

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

  for (i = 0; i < ulPublicKeyAttributeCount; i++)
    if (pPublicKeyTemplate[i].type == CKA_TOKEN)
      public_handle_flavor = p11_handle_flavor_from_cka_token(pPublicKeyTemplate[i].pValue);

  for (i = 0; i < ulPrivateKeyAttributeCount; i++)
    if (pPrivateKeyTemplate[i].type == CKA_TOKEN)
      private_handle_flavor = p11_handle_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue);

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

  if ((public_handle = p11_object_create(session, public_handle_flavor,
                                         pPublicKeyTemplate, ulPublicKeyAttributeCount,
                                         public_descriptor, pMechanism)) == CK_INVALID_HANDLE)
    lose(CKR_FUNCTION_FAILED);

  if ((private_handle = p11_object_create(session, private_handle_flavor,
                                          pPrivateKeyTemplate,  ulPrivateKeyAttributeCount,
                                          private_descriptor, pMechanism)) == CK_INVALID_HANDLE)
    lose(CKR_FUNCTION_FAILED);

  if ((rv = mechanism_handler(session,
                              pPublicKeyTemplate, ulPublicKeyAttributeCount,
                              pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                              private_handle, public_handle)) != CKR_OK)
    goto fail;

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

  *phPublicKey  = public_handle;
  *phPrivateKey = private_handle;
  return CKR_OK;

 fail:
  if (!sql_exec("ROLLBACK"))
    rv = CKR_GENERAL_ERROR;
  return rv;
}

/*
 * Mechanism-independent checks for templates and descriptors when
 * import objects via C_CreateObject().
 *
 * Fun question exactly how calling code knows what descriptor to
 * pass.  p11_descriptor_from_key_type() will suffice for key objects.
 * Drive off that bridge when we get to it.
 */

static CK_RV p11_check_create_attributes(const p11_session_t *session,
                                         const CK_ATTRIBUTE_PTR pTemplate,
                                         const CK_ULONG ulCount,
                                         const p11_descriptor_t * const descriptor)
{
  CK_RV rv = CKR_OK;
  int i;

  assert(session != NULL && pTemplate != NULL && descriptor != NULL);

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

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

  /*
   * Check values provided in the template.
   */

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

    if ((rv = p11_template_check_1(type, val, len, descriptor,
                                   P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK)
      goto fail;
  }

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

  if ((rv = p11_template_check_2(session, descriptor, pTemplate, ulCount,
                                 P11_DESCRIPTOR_REQUIRED_BY_CREATEOBJECT,
                                 P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK)
    goto fail;

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

  rv = CKR_OK;

 fail:
  return rv;
}




/*
 * Add data to a digest.
 */

static CK_RV digest_update(const hal_hash_descriptor_t * const descriptor,
                           hal_hash_state_t **state,
                           const uint8_t * const data, const size_t data_len)
{
  assert(descriptor != NULL && state != NULL && data != NULL);

  if (*state == NULL) {
    switch (hal_hash_initialize(descriptor, state, NULL, 0)) {
    case HAL_OK:
      break;
    case HAL_ERROR_ALLOCATION_FAILURE:
      return CKR_HOST_MEMORY;
    default:
      return CKR_FUNCTION_FAILED;
    }
  }

  if (!hal_check(hal_hash_update(*state, data, data_len)))
    return CKR_FUNCTION_FAILED;

  return CKR_OK;
}

/*
 * 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,
                                      hal_hash_state_t *state,
                                      uint8_t *digest_info, const size_t digest_info_len)
{
  assert(desc != NULL && state != NULL && digest_info != NULL);

  /*
   * 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);

  uint8_t *d = digest_info;

  *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);

  return hal_check(hal_hash_finalize(state, d, desc->digest_length));
}

/*
 * 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 ",
          (unsigned long) block_len, (unsigned long) data_len);
  for (int i = 0; i < block_len; i++)
    fprintf(stderr, "%s%02x", i == 0 ? "" : ":", block[i]);
  fprintf(stderr, "]\n");
#endif

  return 1;
}

/*
 * Generate an RSA PKCS #1.5 signature.
 *
 * 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(p11_session_t *session,
                           CK_BYTE_PTR pData,
                           CK_ULONG ulDataLen,
                           CK_BYTE_PTR pSignature,
                           CK_ULONG_PTR pulSignatureLen)
{
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  size_t signature_len;
  CK_RV rv;

  assert(session != NULL && pulSignatureLen != NULL);

  const hal_hash_descriptor_t * const desc = session->sign_digest_descriptor;
  const size_t digest_info_len = desc == NULL ? 1 : desc->digest_length + 4 + desc->digest_algorithm_id_length;
  uint8_t digest_info[digest_info_len];

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

  /*
   * Retrieve signature length, which is just the the modulus length.
   */

  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 && rv == CKR_BUFFER_TOO_SMALL)
    lose(CKR_BUFFER_TOO_SMALL);

  if (pSignature != NULL && desc != NULL) {

    if (!pkcs1_construct_digestinfo(desc, session->sign_digest_state, digest_info, sizeof(digest_info)))
      lose(CKR_FUNCTION_FAILED);

    pData = digest_info;
    ulDataLen = sizeof(digest_info);
  }

  if (pSignature != NULL) {

    if (!pkcs1_5_pad(pData, ulDataLen, pSignature, signature_len))
      lose(CKR_DATA_LEN_RANGE);

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

  rv = CKR_OK;                  /* Fall through */

 fail:
  hal_rsa_key_clear(key);
  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(p11_session_t *session,
                             CK_BYTE_PTR pData,
                             CK_ULONG ulDataLen,
                             CK_BYTE_PTR pSignature,
                             CK_ULONG ulSignatureLen)
{
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  CK_RV rv;

  assert(session != NULL);

  const hal_hash_descriptor_t * const desc = session->verify_digest_descriptor;
  const size_t digest_info_len = desc == NULL ? 1 : desc->digest_length + 4 + desc->digest_algorithm_id_length;
  uint8_t digest_info[digest_info_len];
  uint8_t expected[ulSignatureLen], received[ulSignatureLen];
  unsigned diff = 0;

  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) {

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

    pData = digest_info;
    ulDataLen = sizeof(digest_info);

  }

  if (!pkcs1_5_pad(pData, ulDataLen, expected, sizeof(expected)))
    lose(CKR_DATA_LEN_RANGE);

  if (!hal_check(hal_rsa_encrypt(key, pSignature, ulSignatureLen, received, sizeof(received))))
    lose(CKR_FUNCTION_FAILED);

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

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

  rv = CKR_OK;                  /* Fall through */

 fail:
  hal_rsa_key_clear(key);
  return rv;
}

#warning May need to do something about truncating oversized hashes for ECDSA, see PKCS11 spec

/*
 * Generate an ECDSA signature.
 */

static CK_RV sign_ecdsa(p11_session_t *session,
                        CK_BYTE_PTR pData,
                        CK_ULONG ulDataLen,
                        CK_BYTE_PTR pSignature,
                        CK_ULONG_PTR pulSignatureLen)
{
  uint8_t keybuf[hal_ecdsa_key_t_size];
  hal_ecdsa_key_t *key = NULL;
  hal_ecdsa_curve_t curve;
  size_t signature_len;
  CK_RV rv;

  assert(session != NULL && pulSignatureLen != NULL);

  const hal_hash_descriptor_t * const desc = session->sign_digest_descriptor;
  const size_t digest_len = desc == NULL ? 1 : desc->digest_length;
  uint8_t digest[digest_len];

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

  /*
   * Signature length is determined by curve parameters.
   */

  if (!hal_check(hal_ecdsa_key_get_curve(key, &curve)))
    lose(CKR_FUNCTION_FAILED);

  switch (curve) {
  case HAL_ECDSA_CURVE_P256: signature_len = 256; break;
  case HAL_ECDSA_CURVE_P384: signature_len = 384; break;
  case HAL_ECDSA_CURVE_P521: signature_len = 521; break;
  default: lose(CKR_FUNCTION_FAILED);
  }

  signature_len = ((signature_len + 7) / 8) * 2;

  rv = signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulSignatureLen = signature_len;

  if (pSignature != NULL && rv == CKR_BUFFER_TOO_SMALL)
    lose(CKR_BUFFER_TOO_SMALL);

  if (pSignature != NULL && desc != NULL) {

    if (!hal_check(hal_hash_finalize(session->sign_digest_state, digest, sizeof(digest))))
      lose(CKR_FUNCTION_FAILED);

    pData = digest;
    ulDataLen = sizeof(digest);
  }

  if (pSignature != NULL && !hal_check(hal_ecdsa_sign(key, pData, ulDataLen,
                                                      pSignature, &signature_len, *pulSignatureLen,
                                                      HAL_ECDSA_SIGNATURE_FORMAT_PKCS11)))
    lose(CKR_FUNCTION_FAILED);

  assert(signature_len == *pulSignatureLen);

  rv = CKR_OK;                  /* Fall through */

 fail:
  hal_ecdsa_key_clear(key);
  return rv;
}

/*
 * Verify an ECDSA signature.
 */

static CK_RV verify_ecdsa(p11_session_t *session,
                          CK_BYTE_PTR pData,
                          CK_ULONG ulDataLen,
                          CK_BYTE_PTR pSignature,
                          CK_ULONG ulSignatureLen)
{
  uint8_t keybuf[hal_ecdsa_key_t_size];
  hal_ecdsa_key_t *key = NULL;
  CK_RV rv;

  assert(session != NULL && pSignature != NULL);

  const hal_hash_descriptor_t * const desc = session->verify_digest_descriptor;
  const size_t digest_len = desc == NULL ? 1 : desc->digest_length;
  uint8_t digest[digest_len];

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

  if (session->verify_digest_descriptor != NULL) {

    if (!hal_check(hal_hash_finalize(session->verify_digest_state, digest, sizeof(digest))))
      lose(CKR_FUNCTION_FAILED);

    pData = digest;
    ulDataLen = sizeof(digest);

  }

  if (!hal_check(hal_ecdsa_verify(key, pData, ulDataLen, pSignature, ulSignatureLen, HAL_ECDSA_SIGNATURE_FORMAT_PKCS11)))
    lose(CKR_SIGNATURE_INVALID);

  rv = CKR_OK;                  /* Fall through */

 fail:
  hal_ecdsa_key_clear(key);
  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.
   *
   * 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_CreateObject(CK_SESSION_HANDLE hSession,
                     CK_ATTRIBUTE_PTR pTemplate,
                     CK_ULONG ulCount,
                     CK_OBJECT_HANDLE_PTR phObject)
{
  ENTER_PUBLIC_FUNCTION(C_CreateObject);

  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 (pTemplate == NULL || phObject == NULL)
    lose(CKR_ARGUMENTS_BAD);

  const CK_OBJECT_CLASS * const cka_class = p11_attribute_find_value_in_template(CKA_CLASS,    pTemplate, ulCount);
  const CK_KEY_TYPE * const cka_key_type  = p11_attribute_find_value_in_template(CKA_KEY_TYPE, pTemplate, ulCount);
  const CK_BBOOL * const cka_token        = p11_attribute_find_value_in_template(CKA_TOKEN,    pTemplate, ulCount);

  if (cka_class == NULL)
    lose(CKR_TEMPLATE_INCOMPLETE);

  switch (*cka_class) {
  case CKO_PUBLIC_KEY:
  case CKO_PRIVATE_KEY:
  case CKO_SECRET_KEY:
    break;
  default:
    lose(CKR_TEMPLATE_INCONSISTENT);
  }

  if (cka_key_type == NULL)
    lose(CKR_TEMPLATE_INCOMPLETE);

  const p11_descriptor_t * const
    descriptor = p11_descriptor_from_key_type(*cka_class, *cka_key_type);

  if (descriptor == NULL)
    lose(CKR_TEMPLATE_INCONSISTENT);

  if ((rv = p11_check_create_attributes(session, pTemplate, ulCount, descriptor)) != CKR_OK)
    goto fail;

  switch (session->state) {
  case CKS_RO_PUBLIC_SESSION:
  case CKS_RO_USER_FUNCTIONS:
    if (cka_token != NULL && *cka_token)
      lose(CKR_SESSION_READ_ONLY);
  }

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

  const handle_flavor_t flavor
    = cka_token == NULL ? handle_flavor_session_object : p11_handle_flavor_from_cka_token(cka_token);

  CK_OBJECT_HANDLE handle = p11_object_create(session, flavor, pTemplate, ulCount, descriptor, NULL);

  if (!p11_attribute_set_bbool(handle, CKA_LOCAL, CK_FALSE))
    lose(CKR_FUNCTION_FAILED);

  switch (*cka_class) {
  case CKO_PRIVATE_KEY:
  case CKO_SECRET_KEY:
    if (!p11_attribute_set_bbool(handle, CKA_ALWAYS_SENSITIVE,  CK_FALSE) || 
        !p11_attribute_set_bbool(handle, CKA_NEVER_EXTRACTABLE, CK_FALSE))
      lose(CKR_FUNCTION_FAILED);
  }

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

  *phObject = handle;

  return mutex_unlock(p11_global_mutex);

 fail:
  if (!sql_exec("ROLLBACK"))
    rv = CKR_GENERAL_ERROR;
  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);

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

  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);

  if ((rv = digest_update(session->digest_descriptor, &session->digest_state,
                          pData, ulDataLen)) != CKR_OK)
    goto fail;

  if (!hal_check(hal_hash_finalize(session->digest_state, pDigest, *pulDigestLen)))
    lose(CKR_FUNCTION_FAILED);

  rv = CKR_OK;                  /* Fall through */

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

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

  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 (pPart == NULL)
    lose(CKR_ARGUMENTS_BAD);

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

  if (!session->digest_descriptor->can_restore_state)
    lose(CKR_FUNCTION_FAILED);

  if ((rv = digest_update(session->digest_descriptor,
                          &session->digest_state,
                          pPart, ulPartLen)) != CKR_OK)
    goto fail;

  return mutex_unlock(p11_global_mutex);

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

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

  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 (pulDigestLen == NULL)
    lose(CKR_ARGUMENTS_BAD);

  if (session->digest_descriptor == NULL || session->digest_state == 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);

  if (!hal_check(hal_hash_finalize(session->digest_state, pDigest, *pulDigestLen)))
    lose(CKR_FUNCTION_FAILED);

  rv = CKR_OK;                  /* Fall through */

 fail:
  if (session != NULL) {
    hal_hash_cleanup(&session->digest_state);
    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_OBJECT_CLASS key_class;
  CK_KEY_TYPE key_type;
  CK_BBOOL key_sign;
  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;

  if (!p11_attribute_get_ulong(hKey, CKA_CLASS,    &key_class)  ||
      !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type)   ||
      !p11_attribute_get_bbool(hKey, CKA_SIGN,     &key_sign)   ||
      key_class != CKO_PRIVATE_KEY)
    lose(CKR_KEY_HANDLE_INVALID);

  if (!key_sign)
    lose(CKR_KEY_FUNCTION_NOT_PERMITTED);

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA384_RSA_PKCS:     
  case CKM_SHA512_RSA_PKCS:
    if (key_type != CKK_RSA)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  case CKM_ECDSA:
  case CKM_ECDSA_SHA256:
  case CKM_ECDSA_SHA384:
  case CKM_ECDSA_SHA512:
    if (key_type != CKK_EC)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  default:
    return CKR_MECHANISM_INVALID;
  }

  session->sign_key_handle = hKey;

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_ECDSA:
    session->sign_digest_descriptor = NULL;
    break;
  case CKM_SHA1_RSA_PKCS:
    session->sign_digest_descriptor = hal_hash_sha1;
    break;
  case CKM_SHA256_RSA_PKCS:
  case CKM_ECDSA_SHA256:
    session->sign_digest_descriptor = hal_hash_sha256;
    break;
  case CKM_SHA384_RSA_PKCS:     
  case CKM_ECDSA_SHA384:
    session->sign_digest_descriptor = hal_hash_sha384;
    break;
  case CKM_SHA512_RSA_PKCS:     
  case CKM_ECDSA_SHA512:
    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);

  p11_session_t *session;
  CK_KEY_TYPE key_type;
  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);

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

  if (session->sign_digest_descriptor != NULL && pSignature != NULL &&
      (rv = digest_update(session->sign_digest_descriptor,
                          &session->sign_digest_state, pData, ulDataLen)) != CKR_OK)
    goto fail;

  if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type))
    lose(CKR_FUNCTION_FAILED);

  switch (key_type) {

  case CKK_RSA:
    rv = sign_rsa_pkcs(session, pData, ulDataLen, pSignature, pulSignatureLen);
    break;

  case CKK_EC:
    rv = sign_ecdsa(session, pData, ulDataLen, pSignature, pulSignatureLen);
    break;

  default:
    lose(CKR_FUNCTION_FAILED);
  }

 fail:                          /* Fall through */

  if (session != NULL && pSignature != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_descriptor = NULL;
    hal_hash_cleanup(&session->sign_digest_state);
  }

  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_OBJECT_CLASS key_class;
  CK_KEY_TYPE key_type;
  CK_BBOOL key_verify;
  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;

  if (!p11_attribute_get_ulong(hKey, CKA_CLASS,    &key_class)  ||
      !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type)   ||
      !p11_attribute_get_bbool(hKey, CKA_VERIFY,   &key_verify) ||
      key_class != CKO_PUBLIC_KEY)
    lose(CKR_KEY_HANDLE_INVALID);

  if (!key_verify)
    lose(CKR_KEY_FUNCTION_NOT_PERMITTED);

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA384_RSA_PKCS:     
  case CKM_SHA512_RSA_PKCS:
    if (key_type != CKK_RSA)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  case CKM_ECDSA:
  case CKM_ECDSA_SHA256:
  case CKM_ECDSA_SHA384:
  case CKM_ECDSA_SHA512:
    if (key_type != CKK_EC)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  default:
    return CKR_MECHANISM_INVALID;
  }

  session->verify_key_handle = hKey;

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_ECDSA:
    session->verify_digest_descriptor = NULL;
    break;
  case CKM_SHA1_RSA_PKCS:
    session->verify_digest_descriptor = hal_hash_sha1;
    break;
  case CKM_SHA256_RSA_PKCS:
  case CKM_ECDSA_SHA256:
    session->verify_digest_descriptor = hal_hash_sha256;
    break;
  case CKM_SHA384_RSA_PKCS:
  case CKM_ECDSA_SHA384:
    session->verify_digest_descriptor = hal_hash_sha384;
    break;
  case CKM_SHA512_RSA_PKCS:
  case CKM_ECDSA_SHA512:
    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);

  p11_session_t *session;
  CK_KEY_TYPE key_type;
  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);

  if (session->verify_digest_descriptor != NULL &&
      (rv = digest_update(session->verify_digest_descriptor,
                          &session->verify_digest_state, pData, ulDataLen)) != CKR_OK)
    goto fail;

  if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type))
    lose(CKR_FUNCTION_FAILED);

  switch (key_type) {

  case CKK_RSA:
    rv = verify_rsa_pkcs(session, pData, ulDataLen, pSignature, ulSignatureLen);
    break;

  case CKK_EC:
    rv = verify_ecdsa(session, pData, ulDataLen, pSignature, ulSignatureLen);
    break;

  default:
    lose(CKR_FUNCTION_FAILED);
  }
  
 fail:                          /* Fall through */

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

  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(session, pMechanism,
                          pPublicKeyTemplate, ulPublicKeyAttributeCount,
                          pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                          phPublicKey, phPrivateKey,
                          generate_keypair_rsa_pkcs,
                          &p11_descriptor_rsa_public_key,
                          &p11_descriptor_rsa_private_key);
    break;

  case CKM_EC_KEY_PAIR_GEN:
    rv = generate_keypair(session, pMechanism,
                          pPublicKeyTemplate, ulPublicKeyAttributeCount,
                          pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                          phPublicKey, phPrivateKey,
                          generate_keypair_ec,
                          &p11_descriptor_ec_public_key,
                          &p11_descriptor_ec_private_key);
    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 that applications we care about are 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);

  const CK_ULONG rsa_key_min = 1024;
  const CK_ULONG rsa_key_max = 8192;
  const CK_ULONG ec_key_min  = 256;
  const CK_ULONG ec_key_max  = 521;

  /*
   * 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_SHA_1:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA_1_HMAC:
  case CKM_ECDSA_SHA1:
    if (hal_hash_core_present(hal_hash_sha1) != HAL_OK)
      return CKR_MECHANISM_INVALID;
    break;

  case CKM_SHA256:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA256_HMAC:
  case CKM_ECDSA_SHA256:
    if (hal_hash_core_present(hal_hash_sha256) != HAL_OK)
      return CKR_MECHANISM_INVALID;
    break;

  case CKM_SHA384:
  case CKM_SHA384_RSA_PKCS:
  case CKM_SHA384_HMAC:
  case CKM_ECDSA_SHA384:
    if (hal_hash_core_present(hal_hash_sha384) != HAL_OK)
      return CKR_MECHANISM_INVALID;
    break;

  case CKM_SHA512:
  case CKM_SHA512_RSA_PKCS:
  case CKM_SHA512_HMAC:
  case CKM_ECDSA_SHA512:
    if (hal_hash_core_present(hal_hash_sha512) != HAL_OK)
      return CKR_MECHANISM_INVALID;
    break;

  default:
    break;
  }

  switch (type) {

  case CKM_RSA_PKCS_KEY_PAIR_GEN:
    pInfo->ulMinKeySize = rsa_key_min;
    pInfo->ulMaxKeySize = rsa_key_max;
    pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR;
    break;

  case CKM_EC_KEY_PAIR_GEN:
    pInfo->ulMinKeySize = ec_key_min;
    pInfo->ulMaxKeySize = ec_key_max;
    pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS;
    break;

  case CKM_RSA_PKCS:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA384_RSA_PKCS:
  case CKM_SHA512_RSA_PKCS:
    pInfo->ulMinKeySize = rsa_key_min;
    pInfo->ulMaxKeySize = rsa_key_max;
    pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY;
    break;

  case CKM_ECDSA:
  case CKM_ECDSA_SHA256:
  case CKM_ECDSA_SHA384:
  case CKM_ECDSA_SHA512:
    pInfo->ulMinKeySize = ec_key_min;
    pInfo->ulMaxKeySize = ec_key_max;
    pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS;
    break;

  case CKM_SHA_1:
  case CKM_SHA256:
  case CKM_SHA384:
  case CKM_SHA512:
    pInfo->ulMinKeySize = 0;
    pInfo->ulMaxKeySize = 0;
    pInfo->flags = CKF_HW | CKF_DIGEST;
    break;

#if 0
    /*
     * We have Verilog and libhal for these, but no PKCS #11 support (yet).
     */
  case CKM_SHA_1_HMAC:
  case CKM_SHA256_HMAC:
  case CKM_SHA384_HMAC:
  case CKM_SHA512_HMAC:
#endif

  default:
    return CKR_MECHANISM_INVALID;
  }

  return CKR_OK;
}

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

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

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

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

  pInfo->slotID = P11_ONE_AND_ONLY_SLOT;
  pInfo->state = session->state;
  pInfo->flags = CKF_SERIAL_SESSION;
  pInfo->ulDeviceError = 0;

  switch (session->state) {
  case CKS_RW_PUBLIC_SESSION:
  case CKS_RW_SO_FUNCTIONS:
  case CKS_RW_USER_FUNCTIONS:
    pInfo->flags |= CKF_RW_SESSION;
  default:
    break;
  }

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}



/*
 * 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_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_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_DigestKey(CK_SESSION_HANDLE hSession,
                  CK_OBJECT_HANDLE hKey)
{
  ENTER_PUBLIC_FUNCTION(C_DigestKey);
  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:
 */