aboutsummaryrefslogblamecommitdiff
path: root/pkcs11.c
blob: 52e5e476985018047885d18e8c8cdfb6b98e65a6 (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-2016, NORDUnet A/S
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * 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
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <sqlite3.h>

#include <hal.h>

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

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

#include "pkcs11.h"

#include "attributes.h"

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

#define P11_ONE_AND_ONLY_SLOT   0

/*
 * 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       0
#endif

#ifndef DEBUG_PKCS11
#define DEBUG_PKCS11    0
#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 */
  hal_digest_algorithm_t
    digest_algorithm,                   /* Hash algorithm for C_Digest*() */
    sign_digest_algorithm,              /* Hash algorithm for C_Sign*() */
    verify_digest_algorithm;            /* Hash algorithm for C_Verify*() */
  CK_OBJECT_HANDLE
    sign_key_handle,                    /* Private key for C_Sign*() */
    verify_key_handle;                  /* Public  key for C_Verify() */
  hal_hash_handle_t
    digest_handle,                      /* Hash state for C_Digest*() */
    sign_digest_handle,                 /* Hash state for C_Sign*() */
    verify_digest_handle;               /* 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)

/*
 * Digest algorithm to use when computing a key hashes.  This doesn't
 * need to be particularly secure, we're just using it to generate
 * reasonably unique identifier strings from public keys.  We use
 * SHA-1 for this because that's what most X.509 implementations use
 * for this purpose.
 */

#define P11_KEY_HASH_ALGORITHM  hal_digest_algorithm_sha1



/*
 * 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, "\n%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, "\nEntering function %s\n", #_name_)

#else  /* DEBUG_PKCS11 > 1 */

#define ENTER_PUBLIC_FUNCTION(_name_)

#endif  /* DEBUG_PKCS11 > 1 */

/*
 * Error checking for libhal calls.
 */

#define hal_whine(_expr_)            (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK))
#define hal_whine_allow(_expr_, ...) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, __VA_ARGS__, HAL_OK))
#define hal_check(_expr_)            (hal_whine(_expr_) == HAL_OK)

#if DEBUG_HAL

static inline hal_error_t _hal_whine(const hal_error_t err,
                                     const char * const expr,
                                     const char * const file,
                                     const unsigned line, ...)
{
  va_list ap;
  int ok = 0;
  hal_error_t code;

  va_start(ap, line);
  do {
    code = va_arg(ap, hal_error_t);
    ok |= (err == code);
  } while (code != HAL_OK);
  va_end(ap);

  if (!ok)
    fprintf(stderr, "\n%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err));

  return err;
}

#else /* DEBUG_HAL */

#define _hal_whine(_expr_, ...) (_expr_)

#endif /* DEBUG_HAL */

/*
 * Error translation fun for the entire family!
 */

#if DEBUG_PKCS11 || DEBUG_HAL

#define hal_p11_error_case(_hal_err_, _p11_err_) \
  case _hal_err_: fprintf(stderr, "\n%s:%u: Mapping %s to %s\n", file, line, #_hal_err_, #_p11_err_); return _p11_err_;

#else

#define hal_p11_error_case(_hal_err_, _p11_err_) \
  case _hal_err_: return _p11_err_;

#endif

#define p11_error_from_hal(_hal_err_) \
  (_p11_error_from_hal((_hal_err_), __FILE__, __LINE__))

#define p11_whine_from_hal(_expr_) \
  (_p11_error_from_hal(_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK), __FILE__, __LINE__))

static CK_RV _p11_error_from_hal(const hal_error_t err, const char * const file, const unsigned line)
{
  switch (err) {
    hal_p11_error_case(HAL_ERROR_PIN_INCORRECT,         CKR_PIN_INCORRECT);
    hal_p11_error_case(HAL_ERROR_INVALID_SIGNATURE,     CKR_SIGNATURE_INVALID);

    /*
     * More here later, first see if this compiles.
     */

  case HAL_OK:
    return CKR_OK;

  default:
#if DEBUG_PKCS11 || DEBUG_HAL
    fprintf(stderr, "\n%s:%u: Mapping unhandled HAL error to CKR_FUNCTION_FAILED\n", file, line);
#endif
    return CKR_FUNCTION_FAILED;
  }
}

#undef hal_p11_error_case



/*
 * SQL utilities.
 */

/*
 * Debugging control.
 */

#ifndef DEBUG_SQL
#define DEBUG_SQL       1
#endif

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

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

/*
 * SQL database.
 */

static sqlite3 *sqldb = NULL;

/*
 * Error checking for SQLite calls.
 */

#if DEBUG_SQL

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

#else  /* DEBUG_SQL */

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

#endif  /* DEBUG_SQL */

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

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

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

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

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

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

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

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

  return 0;
}

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

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

  assert(sqldb == NULL);

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

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

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

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

  return ok && sql_exec(schema);
}

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

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

  sqldb = NULL;
  return 1;
}

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

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

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

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

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

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

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

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

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

  int err = sqlite3_finalize(*q);

  if (err != SQLITE_OK)
    return err;

  *q = NULL;
  return SQLITE_OK;
}



/*
 * 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_curve_name_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_CURVE_P256;

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

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

  else
    return 0;

  return 1;
}

/*
 * Extract libhal-compatible client and session identifiers from a session.
 *
 * libhal's session identifiers are deliberately chosen to be in the same
 * numeric range as PKCS #11's, so we can just use them directly.
 *
 * libhal's client identifiers are multiplexing extension handled elsewhere,
 * for our purposes using constant client identifier of zero will do.
 */

static inline hal_client_handle_t p11_session_hal_client(const p11_session_t * const session)
{
  hal_client_handle_t handle = {0};
  return handle;
}

static inline hal_session_handle_t p11_session_hal_session(const p11_session_t * const session)
{
  hal_session_handle_t handle = {session->handle};
  return handle;
}



/*
 * 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 so we
 * can simplify things a bit by reusing libhal's flag values.
 */

static void p11_attribute_apply_keyusage(hal_key_flags_t *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 = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE;
    break;
  case CKA_ENCRYPT:             /* Encrypt bulk data (seldom used) */
  case CKA_DECRYPT:             /* Bulk decryption (seldom used) */
    flag = HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT;
    break;
  case CKA_WRAP:                /* Wrap key (normal way of doing encryption) */
  case CKA_UNWRAP:              /* Unwrap key (normal way of doing decryption) */
    flag = HAL_KEY_FLAG_USAGE_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.
   *
   * If the attribute is flagged as sensitive in the descriptor, we
   * don't store it in SQL.  Generally, this only arises for private
   * key components of objects created with C_CreateObject(), but in
   * theory there are some corner cases in which a user could choose
   * to mark a private key as extractable and not sensitive, so we
   * might have to back-fill missing values in those cases if anyone
   * ever thinks up a sane reason for supporting them.  For now, assume
   * that private keys are bloody well supposed to be private.
   */

  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 (p11_attribute_is_sensitive(descriptor, type))
      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;
  }

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

/*
 * Bind PKCS #11 objects to keystore objects.
 *
 * This requires storing the pkey type (supplied) and the SKI
 * (calcualted from supplied public key DER) of the key.  We return
 * the calculated SKI so that the caller can use it as the pkey name.
 *
 * We don't work with pkey handles here because that would create a
 * chicken-and-egg problem, given that the values calculated and
 * stored by this function are what we use to look up a pkey given
 * a PKCS #11 key object.
 */

static int p11_object_bind_pkey(const p11_session_t * const session,
                                const hal_key_type_t pkey_type_1,
                                const hal_key_type_t pkey_type_2,
                                const CK_OBJECT_HANDLE object_handle_1,
                                const CK_OBJECT_HANDLE object_handle_2,
                                const uint8_t * const der, const size_t der_len,
                                uint8_t *ski, const size_t ski_len)
{
  assert(session != NULL && der != NULL && ski != NULL);

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

  const char *flavor_1 = is_token_handle(object_handle_1) ? "token" : "session";
  const char *flavor_2 = is_token_handle(object_handle_2) ? "token" : "session";

  hal_hash_handle_t hash = {HAL_HANDLE_NONE};

  int ok = hal_check(hal_rpc_hash_initialize(p11_session_hal_client(session),
                                             p11_session_hal_session(session),
                                             &hash, P11_KEY_HASH_ALGORITHM, NULL, 0));
  if (ok)
    ok = hal_check(hal_rpc_hash_update(hash, der, der_len));

  if (hash.handle != HAL_HANDLE_NONE)
    ok = hal_check(hal_rpc_hash_finalize(hash, ski, ski_len)) && ok;

  if (!ok)
    return 0;

  sqlite3_stmt *q1 = NULL, *q2 = NULL;

  ok = (sql_check_ok(sql_prepare(&q1, update_format, flavor_1, flavor_1, flavor_1))     &&
        sql_check_ok(sqlite3_bind_int64(q1, 1, pkey_type_1))                            &&
        sql_check_ok(sqlite3_bind_blob( q1, 2, ski, ski_len, NULL))                     &&
        sql_check_ok(sqlite3_bind_int64(q1, 3, object_handle_1))                        &&
        sql_check_done(sqlite3_step(q1)));

  if (ok && object_handle_2 != CK_INVALID_HANDLE)
    ok = (sql_check_ok(sql_prepare(&q2, update_format, flavor_2, flavor_2, flavor_2))   &&
          sql_check_ok(sqlite3_bind_int64(q2, 1, pkey_type_2))                          &&
          sql_check_ok(sqlite3_bind_blob( q2, 2, ski, ski_len, NULL))                   &&
          sql_check_ok(sqlite3_bind_int64(q2, 3, object_handle_2))                      &&
          sql_check_done(sqlite3_step(q2)));

  sqlite3_finalize(q1);
  sqlite3_finalize(q2);
  return ok;
}

/*
 * Create pkeys to go with PKCS #11 key objects loaded by C_CreateObject().
 */

static inline int p11_object_create_rsa_public_key(const p11_session_t * const session,
                                                   const CK_OBJECT_HANDLE object_handle,
                                                   const hal_key_flags_t flags)
{
  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";

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  sqlite3_stmt *q = NULL;
  size_t ski_len = 0;

  int ok
    = (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len))              &&
       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, sizeof(keybuf),
                                         sqlite3_column_blob( q, 0),
                                         sqlite3_column_bytes(q, 0),
                                         sqlite3_column_blob( q, 1),
                                         sqlite3_column_bytes(q, 1))));

  if (ok) {
    uint8_t der[hal_rsa_public_key_to_der_len(key)], ski[ski_len];
    ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der)))                     &&
          p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PUBLIC, HAL_KEY_TYPE_NONE,
                               object_handle, CK_INVALID_HANDLE,
                               der, sizeof(der), ski, sizeof(ski))                              &&
          hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                      p11_session_hal_session(session),
                                      &pkey, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
                                      ski, sizeof(ski), der, sizeof(der), flags)));
  }

  (void) hal_rpc_pkey_close(pkey);
  sqlite3_finalize(q);
  return ok;
}

static inline int p11_object_create_ec_public_key(const p11_session_t * const session,
                                                  const CK_OBJECT_HANDLE object_handle,
                                                  const hal_key_flags_t flags)
{
  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";

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_ecdsa_key_t_size];
  hal_ecdsa_key_t *key = NULL;
  hal_curve_name_t curve;
  sqlite3_stmt *q = NULL;
  size_t ski_len = 0;

  int ok
    = (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len))              &&
       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, sizeof(keybuf),
                                            sqlite3_column_blob( q, 1),
                                            sqlite3_column_bytes(q, 1),
                                            curve)));

  if (ok) {
    uint8_t der[hal_ecdsa_public_key_to_der_len(key)], ski[ski_len];
    ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der)))                   &&
          p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PUBLIC, HAL_KEY_TYPE_NONE,
                               object_handle, CK_INVALID_HANDLE,
                               der, sizeof(der), ski, sizeof(ski))                              &&
          hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                      p11_session_hal_session(session),
                                      &pkey, HAL_KEY_TYPE_EC_PUBLIC, curve,
                                      ski, sizeof(ski), der, sizeof(der), flags)));
  }

  (void) hal_rpc_pkey_close(pkey);
  sqlite3_finalize(q);
  return ok;
}

static inline int p11_object_create_rsa_private_key(const p11_session_t * const session,
                                                    const CK_OBJECT_HANDLE object_handle,
                                                    const hal_key_flags_t flags,
                                                    const CK_ATTRIBUTE_PTR const template,
                                                    const CK_ULONG template_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";

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  sqlite3_stmt *q = NULL;
  size_t ski_len = 0;

  const uint8_t *cka_private_exponent = NULL;   size_t cka_private_exponent_len = 0;
  const uint8_t *cka_prime_1 = NULL;            size_t cka_prime_1_len = 0;
  const uint8_t *cka_prime_2 = NULL;            size_t cka_prime_2_len = 0;
  const uint8_t *cka_exponent_1 = NULL;         size_t cka_exponent_1_len = 0;
  const uint8_t *cka_exponent_2 = NULL;         size_t cka_exponent_2_len = 0;
  const uint8_t *cka_coefficient = NULL;        size_t cka_coefficient_len = 0;

  for (int i = 0; i < template_len; i++) {
    switch (template[i].type) {
    case CKA_PRIVATE_EXPONENT:
      cka_private_exponent = template[i].pValue;      	cka_private_exponent_len = template[i].ulValueLen;
      break;
    case CKA_PRIME_1:
      cka_prime_1 = template[i].pValue;                	cka_prime_1_len = template[i].ulValueLen;
      break;
    case CKA_PRIME_2:
      cka_prime_2 = template[i].pValue;                	cka_prime_2_len = template[i].ulValueLen;
      break;
    case CKA_EXPONENT_1:
      cka_exponent_1 = template[i].pValue;             	cka_exponent_1_len = template[i].ulValueLen;
      break;
    case CKA_EXPONENT_2:
      cka_exponent_2 = template[i].pValue;             	cka_exponent_2_len = template[i].ulValueLen;
      break;
    case CKA_COEFFICIENT:
      cka_coefficient = template[i].pValue;            	cka_coefficient_len = template[i].ulValueLen;
      break;
    }
  }

  int ok
    = (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len))              &&
       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_private(&key, keybuf, sizeof(keybuf),
                                          sqlite3_column_blob( q, 0),   sqlite3_column_bytes(q, 0),
                                          sqlite3_column_blob( q, 1),   sqlite3_column_bytes(q, 1),
                                          cka_private_exponent,         cka_private_exponent_len,
                                          cka_prime_1,                  cka_prime_1_len,
                                          cka_prime_2,                  cka_prime_2_len,
                                          cka_coefficient,              cka_coefficient_len,
                                          cka_exponent_1,               cka_exponent_1_len,
                                          cka_exponent_2,               cka_exponent_2_len)));

  if (ok) {
    const size_t private_len = hal_rsa_private_key_to_der_len(key);
    const size_t public_len = hal_rsa_public_key_to_der_len(key);
    uint8_t der[public_len > private_len ? public_len : private_len], ski[ski_len];
    ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der)))                     &&
          p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_NONE,
                               object_handle, CK_INVALID_HANDLE,
                               der, public_len, ski, sizeof(ski))                               &&
          hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der)))                    &&
          hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                      p11_session_hal_session(session),
                                      &pkey, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE,
                                      ski, sizeof(ski), der, private_len, flags)));
    memset(der, 0, sizeof(der));
  }

  memset(keybuf, 0, sizeof(keybuf));
  (void) hal_rpc_pkey_close(pkey);
  sqlite3_finalize(q);
  return ok;
}

static inline int p11_object_create_ec_private_key(const p11_session_t * const session,
                                                   const CK_OBJECT_HANDLE object_handle,
                                                   const hal_key_flags_t flags,
                                                   const CK_ATTRIBUTE_PTR const template,
                                                   const CK_ULONG template_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";

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_ecdsa_key_t_size];
  hal_ecdsa_key_t *key = NULL;
  hal_curve_name_t curve;
  sqlite3_stmt *q = NULL;
  size_t ski_len = 0;
  const uint8_t *ecpoint = NULL;
  size_t ecpoint_len = 0;

  const uint8_t *cka_value = NULL; size_t cka_value_len = 0;

  for (int i = 0; i < template_len; i++)
    if (template[i].type == CKA_VALUE)
      cka_value = template[i].pValue, cka_value_len = template[i].ulValueLen;

  int ok
    = (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len))              &&
       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)	&&
       ((ecpoint_len = sqlite3_column_bytes(q, 1)) & 1) != 0                                    &&
       *(ecpoint = sqlite3_column_blob( q, 1)) == 0x04                                          &&
       hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve,
                                            ecpoint + 1 + 0 * ecpoint_len / 2,  ecpoint_len / 2,
                                            ecpoint + 1 + 0 * ecpoint_len / 2,  ecpoint_len / 2,
                                            cka_value,                          cka_value_len)));

  if (ok) {
    const size_t private_len = hal_ecdsa_private_key_to_der_len(key);
    const size_t public_len = hal_ecdsa_public_key_to_der_len(key);
    uint8_t der[public_len > private_len ? public_len : private_len], ski[ski_len];
    ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der)))                   &&
          p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PRIVATE, HAL_KEY_TYPE_NONE,
                               object_handle, CK_INVALID_HANDLE,
                               der, public_len, ski, sizeof(ski))                               &&
          hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der)))                  &&
          hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                      p11_session_hal_session(session),
                                      &pkey, HAL_KEY_TYPE_EC_PRIVATE, curve,
                                      ski, sizeof(ski), der, private_len, flags)));
    memset(der, 0, sizeof(der));
  }

  memset(keybuf, 0, sizeof(keybuf));
  (void) hal_rpc_pkey_close(pkey);
  sqlite3_finalize(q);
  return ok;
}

/*
 * Given a PKCS #11 object, obtain a libhal pkey handle.
 */

static int p11_object_get_pkey_handle(const p11_session_t * const session,
                                      const CK_OBJECT_HANDLE object_handle,
                                      hal_pkey_handle_t *pkey_handle)
{
  static const char select_format[] =
    " SELECT hal_pkey_type, hal_pkey_ski FROM %s_object NATURAL JOIN object WHERE object_handle = ?1";

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

  assert(pkey_handle != NULL);

  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_INTEGER               ||
      sqlite3_column_type(q, 1) != SQLITE_BLOB)
    goto fail;

  const hal_key_type_t pkey_type = sqlite3_column_int64(q, 0);
  const uint8_t * const ski      = sqlite3_column_blob( q, 1);
  const size_t ski_len           = sqlite3_column_bytes(q, 1);

  ok = hal_check(hal_rpc_pkey_find(p11_session_hal_client(session), p11_session_hal_session(session),
                                   pkey_handle, pkey_type, ski, ski_len, flags));

 fail:
  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);
  (void) hal_rpc_hash_finalize(session->digest_handle, NULL, 0);
  (void) hal_rpc_hash_finalize(session->sign_digest_handle, NULL, 0);
  (void) hal_rpc_hash_finalize(session->verify_digest_handle, NULL, 0);
  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);

  /* Deleting last session also logs us out */
  if (p11_sessions == NULL)
    logged_in_as = not_logged_in;

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

  logged_in_as = not_logged_in;

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

/*
 * 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, "\np11_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, "\n[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.
 */

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,
                                          hal_key_flags_t *public_flags,
                                          const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                          const CK_ULONG ulPrivateKeyAttributeCount,
                                          const p11_descriptor_t * const private_descriptor,
                                          hal_key_flags_t *private_flags)
{
  CK_RV rv = CKR_OK;
  int i;

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

  *public_flags = *private_flags = 0;

  /*
   * 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_flags, 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_flags, 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_flags != *private_flags || *public_flags == 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_OBJECT_HANDLE public_handle,
                                       const hal_key_flags_t public_flags,
                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                       const CK_ULONG ulPrivateKeyAttributeCount,
                                       const CK_OBJECT_HANDLE private_handle,
                                       const hal_key_flags_t private_flags)
{
  const uint8_t *public_exponent = const_0x010001;
  size_t public_exponent_len = sizeof(const_0x010001);
  hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
  CK_ULONG keysize = 0;
  size_t ski_len = 0;
  CK_RV rv;
  int i;

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

  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;

  if (!hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)))
    lose(CKR_FUNCTION_FAILED);

  if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session),
                                           p11_session_hal_session(session),
                                           &pkey1, (const uint8_t *) "", 0, keysize,
                                           public_exponent, public_exponent_len,
                                           private_flags)))
    lose(CKR_FUNCTION_FAILED);

  {
    uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_rsa_key_t_size], ski[ski_len];
    size_t der_len, modulus_len;
    hal_rsa_key_t *key = NULL;

    const hal_key_type_t pkey2_type = is_token_handle(public_handle) ? HAL_KEY_TYPE_RSA_PRIVATE : HAL_KEY_TYPE_RSA_PUBLIC;

    if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der)))              ||
        !p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PRIVATE, pkey2_type,
                              private_handle, public_handle,
                              der, sizeof(der), ski, sizeof(ski))                               ||
        !hal_check(hal_rpc_pkey_rename(pkey1, ski, sizeof(ski)))                                ||
        !hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))     ||
        !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, NULL, 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);

    if (!is_token_handle(public_handle) &&
        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                     p11_session_hal_session(session),
                                     &pkey2, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
                                     ski, sizeof(ski), der, der_len, public_flags)))
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;

 fail:
  hal_rpc_pkey_close(pkey1);
  hal_rpc_pkey_close(pkey2);
  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_OBJECT_HANDLE public_handle,
                                 const hal_key_flags_t public_flags,
                                 const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                 const CK_ULONG ulPrivateKeyAttributeCount,
                                 const CK_OBJECT_HANDLE private_handle,
                                 const hal_key_flags_t private_flags)
{
  hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
  const CK_BYTE *params = NULL;
  hal_curve_name_t curve;
  size_t params_len;
  size_t ski_len = 0;
  CK_RV rv;
  int i;

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

  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;

  if (!hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)))
    lose(CKR_FUNCTION_FAILED);

  if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session),
                                          p11_session_hal_session(session),
                                          &pkey1, (const uint8_t *) "", 0,
                                          curve, private_flags))                ||
      !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 der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_ecdsa_key_t_size], ski[ski_len];
    hal_ecdsa_key_t *key = NULL;
    size_t der_len;

    const hal_key_type_t pkey2_type = is_token_handle(public_handle) ? HAL_KEY_TYPE_EC_PRIVATE : HAL_KEY_TYPE_EC_PUBLIC;

    if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der)))              ||
        !p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PRIVATE, pkey2_type,
                              private_handle, public_handle,
                              der, sizeof(der), ski, sizeof(ski))                               ||
        !hal_check(hal_rpc_pkey_rename(pkey1, ski, sizeof(ski)))                                ||
        !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_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);

    if (!is_token_handle(public_handle) &&
        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                     p11_session_hal_session(session),
                                     &pkey2, HAL_KEY_TYPE_EC_PUBLIC, curve,
                                     ski, sizeof(ski), der, der_len, public_flags)))
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;

 fail:
  hal_rpc_pkey_close(pkey1);
  hal_rpc_pkey_close(pkey2);
  return rv;
}

/*
 * Key pair generation.  This needs a mechanism-specific function to
 * do the inner bits, but there's a lot of boilerplate.
 *
 * We have no real way to protect session objects, so we don't allow
 * them to hold private keys.  PKCS #11 wants us to report this kind
 * of restriction as a template inconsistency.
 */

static CK_RV generate_keypair(p11_session_t *session,
                              const CK_MECHANISM_PTR pMechanism,
                              CK_RV (*mechanism_handler)(p11_session_t *session,

                                                         const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                                         const CK_ULONG ulPublicKeyAttributeCount,
                                                         const CK_OBJECT_HANDLE public_handle,
                                                         const hal_key_flags_t public_flags,

                                                         const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                                         const CK_ULONG ulPrivateKeyAttributeCount,
                                                         const CK_OBJECT_HANDLE private_handle,
                                                         const hal_key_flags_t private_flags),
                              const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                              const CK_ULONG ulPublicKeyAttributeCount,
                              const p11_descriptor_t * const public_descriptor,
                              CK_OBJECT_HANDLE_PTR phPublicKey,
                              const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                              const CK_ULONG ulPrivateKeyAttributeCount,
                              const p11_descriptor_t * const private_descriptor,
                              CK_OBJECT_HANDLE_PTR phPrivateKey)
{
  CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
  CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
  handle_flavor_t public_handle_flavor = handle_flavor_session_object;
  handle_flavor_t private_handle_flavor = handle_flavor_session_object;
  hal_key_flags_t public_flags = 0;
  hal_key_flags_t private_flags = 0;
  CK_RV rv;
  int i;

  rv = p11_check_keypair_attributes(session,
                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,  public_descriptor,  &public_flags,
                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, &private_flags);
  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 (public_handle_flavor == handle_flavor_session_object)
    public_flags |= HAL_KEY_FLAG_PROXIMATE;

  if (private_handle_flavor == handle_flavor_session_object)
    return CKR_TEMPLATE_INCONSISTENT;

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

  rv = mechanism_handler(session,
                         pPublicKeyTemplate,  ulPublicKeyAttributeCount,  public_handle,  public_flags,
                         pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_handle, private_flags);
  if (rv != 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 p11_session_t * const session,
                           const hal_digest_algorithm_t algorithm,
                           hal_hash_handle_t *handle,
                           const uint8_t * const data, const size_t data_len)
{
  assert(algorithm != hal_digest_algorithm_none && handle != NULL && data != NULL);

  if (handle->handle == HAL_HANDLE_NONE) {
    switch (hal_rpc_hash_initialize(p11_session_hal_client(session),
                                    p11_session_hal_session(session),
                                    handle, algorithm, NULL, 0)) {
    case HAL_OK:
      break;
    case HAL_ERROR_ALLOCATION_FAILURE:
      return CKR_HOST_MEMORY;
    default:
      return CKR_FUNCTION_FAILED;
    }
  }

  if (!hal_check(hal_rpc_hash_update(*handle, data, data_len)))
    return CKR_FUNCTION_FAILED;

  return CKR_OK;
}

/*
 * Finish using a digest context, if we haven't already.
 */

static void digest_cleanup(hal_hash_handle_t *handle)
{
  assert(handle != NULL);
  if (handle->handle == HAL_HANDLE_NONE)
    return;
  (void) hal_rpc_hash_finalize(*handle, NULL, 0);
  handle->handle = HAL_HANDLE_NONE;
}

/*
 * Compute the length of a signature based on the key.  We could get
 * this via the RPC API, but its probably faster to look in the local
 * attribute database.  Rewrite this later if this proves incorrect.
 */

static int get_signature_len(const CK_OBJECT_HANDLE object_handle,
                             const hal_pkey_handle_t pkey,
                             size_t *signature_len)
{
  assert(signature_len != NULL);

  CK_KEY_TYPE cka_key_type;
  hal_curve_name_t curve;
  CK_BYTE oid[20];
  CK_ULONG len;

  if (!p11_attribute_get_ulong(object_handle, CKA_KEY_TYPE, &cka_key_type))
    return 0;

  switch (cka_key_type) {

  case CKK_RSA:
    if (!p11_attribute_get(object_handle, CKA_MODULUS, NULL, &len, 0))
      return 0;
    *signature_len = len;
    return 1;

  case CKK_EC:
    if (!p11_attribute_get(object_handle, CKA_EC_PARAMS, oid, &len, sizeof(oid)) ||
        !ec_curve_oid_to_name(oid, len, &curve))
      return 0;
    switch (curve) {
    case HAL_CURVE_P256: *signature_len = 64;  return 1;
    case HAL_CURVE_P384: *signature_len = 96;  return 1;
    case HAL_CURVE_P521: *signature_len = 132; return 1;
    default:                                   return 0;
   }
  }

  return 0;
}

/*
 * Generate a signature using the libhal RPC API.
 */

static CK_RV sign_hal_rpc(p11_session_t *session,
                          CK_BYTE_PTR pData,
                          CK_ULONG ulDataLen,
                          CK_BYTE_PTR pSignature,
                          CK_ULONG_PTR pulSignatureLen)
{
  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  size_t signature_len;
  CK_RV rv;

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

  if (!p11_object_get_pkey_handle(session, session->sign_key_handle, &pkey))
    lose(CKR_FUNCTION_FAILED);

  if (!get_signature_len(session->sign_key_handle, pkey, &signature_len))
    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)
    rv = CKR_OK;
  else
    rv = p11_whine_from_hal(hal_rpc_pkey_sign(p11_session_hal_session(session), pkey, session->sign_digest_handle,
                                              pData, ulDataLen, pSignature, &signature_len, signature_len));
  /* Fall through */

 fail:
  hal_rpc_pkey_close(pkey);
  return rv;
}

/*
 * Verify a signature using the libhal RPC API.
 */

static CK_RV verify_hal_rpc(p11_session_t *session,
                            CK_BYTE_PTR pData,
                            CK_ULONG ulDataLen,
                            CK_BYTE_PTR pSignature,
                            CK_ULONG ulSignatureLen)
{
  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  CK_RV rv;

  assert(session != NULL);

  if (!p11_object_get_pkey_handle(session, session->verify_key_handle, &pkey))
    lose(CKR_FUNCTION_FAILED);

  rv = p11_whine_from_hal(hal_rpc_pkey_verify(p11_session_hal_session(session), pkey, session->verify_digest_handle,
                                              pData, ulDataLen, pSignature, ulSignatureLen));
  /* Fall through */

 fail:
  hal_rpc_pkey_close(pkey);
  return rv;
}

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



/*
 * 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;
  int initialized_rpc = 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 libhal RPC channel.
   */

  if (!hal_check(hal_rpc_client_init()))
    lose(CKR_GENERAL_ERROR);

  initialized_rpc = 1;

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

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

  initialized_sql = 1;

#if USE_POSIX
  initialized_pid = getpid();
#endif

  return CKR_OK;

 fail:

  if (initialized_sql)
    sql_fini();

  if (initialized_rpc)
    hal_rpc_client_close();

  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 any of the rest of this fails.
   */

  hal_rpc_client_close();

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

#if USE_POSIX
  initialized_pid = 0;
#endif

  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            = (CK_ULONG) hal_rpc_min_pin_length;
  pInfo->ulMinPinLen            = (CK_ULONG) hal_rpc_max_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);

  const hal_client_handle_t client = {HAL_HANDLE_NONE};
  p11_session_t *session;
  hal_user_t user = HAL_USER_NONE;
  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:
    user = HAL_USER_NORMAL;
    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);
    user = HAL_USER_SO;
    break;
  case CKU_CONTEXT_SPECIFIC:
    lose(CKR_OPERATION_NOT_INITIALIZED);
  default:
    lose(CKR_USER_TYPE_INVALID);
  }

  /*
   * Try to log in the HSM.
   */

  if ((rv = p11_whine_from_hal(hal_rpc_login(client, user, (char *) pPin, ulPinLen))) != CKR_OK)
    goto fail;

  /*
   * 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:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

  const hal_client_handle_t client = {HAL_HANDLE_NONE};
  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());

  if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK)
    goto fail;

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

  CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
  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;

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

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

  if (flavor == handle_flavor_session_object && *cka_class == CKO_PRIVATE_KEY)
    lose(CKR_TEMPLATE_INCONSISTENT);

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

  if ((handle = p11_object_create(session, flavor, pTemplate, ulCount, descriptor, NULL)) == CK_INVALID_HANDLE)
    lose(CKR_FUNCTION_FAILED);

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

  hal_key_flags_t flags = flavor == handle_flavor_session_object ? HAL_KEY_FLAG_PROXIMATE : 0;

  for (int i = 0; i < ulCount; i++)
    p11_attribute_apply_keyusage(&flags, pTemplate[i].type, pTemplate[i].pValue);

  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA &&
      !p11_object_create_rsa_public_key(session, handle, flags))
    lose(CKR_FUNCTION_FAILED);

  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC &&
      !p11_object_create_ec_public_key(session, handle, flags))
    lose(CKR_FUNCTION_FAILED);

  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA &&
      !p11_object_create_rsa_private_key(session, handle, flags, pTemplate, ulCount))
    lose(CKR_FUNCTION_FAILED);

  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC &&
      !p11_object_create_ec_private_key(session, handle, flags, pTemplate, ulCount))
    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 = ?)";


  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  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 (p11_object_get_pkey_handle(session, hObject, &pkey) && !hal_check(hal_rpc_pkey_delete(pkey)))
    lose(CKR_FUNCTION_FAILED);

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

  hal_digest_algorithm_t algorithm;
  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_algorithm != hal_digest_algorithm_none)
    lose(CKR_OPERATION_ACTIVE);

  switch (pMechanism->mechanism) {
  case CKM_SHA_1:       algorithm = hal_digest_algorithm_sha1;   break;
  case CKM_SHA256:      algorithm = hal_digest_algorithm_sha256; break;
  case CKM_SHA384:      algorithm = hal_digest_algorithm_sha384; break;
  case CKM_SHA512:      algorithm = hal_digest_algorithm_sha512; break;
  default:              lose(CKR_MECHANISM_INVALID);
  }

  session->digest_algorithm = algorithm;
  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;
  size_t digest_len;
  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_algorithm == hal_digest_algorithm_none)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  if (session->digest_handle.handle != HAL_HANDLE_NONE)
    lose(CKR_OPERATION_ACTIVE);

  if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len)))
    lose(CKR_FUNCTION_FAILED);

  rv = *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulDigestLen = digest_len;

  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, session->digest_algorithm,
                          &session->digest_handle, pData, ulDataLen)) != CKR_OK)
    goto fail;

  if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen)))
    lose(CKR_FUNCTION_FAILED);

  rv = CKR_OK;                  /* Fall through */

 fail:
  if (session != NULL) {
    digest_cleanup(&session->digest_handle);
    session->digest_algorithm = hal_digest_algorithm_none;
  }
  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_algorithm == hal_digest_algorithm_none)
    lose(CKR_OPERATION_NOT_INITIALIZED);

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

  return mutex_unlock(p11_global_mutex);

 fail:
  if (session != NULL) {
    digest_cleanup(&session->digest_handle);
    session->digest_algorithm = hal_digest_algorithm_none;
  }
  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;
  size_t digest_len;
  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_algorithm == hal_digest_algorithm_none || session->digest_handle.handle == HAL_HANDLE_NONE)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len)))
    lose(CKR_FUNCTION_FAILED);

  rv = *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulDigestLen = digest_len;

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

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

  if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen)))
    lose(CKR_FUNCTION_FAILED);

  rv = CKR_OK;                  /* Fall through */

 fail:
  if (session != NULL) {
    digest_cleanup(&session->digest_handle);
    session->digest_algorithm = hal_digest_algorithm_none;
  }
  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_algorithm != hal_digest_algorithm_none)
    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_algorithm = hal_digest_algorithm_none;
    break;
  case CKM_SHA1_RSA_PKCS:
    session->sign_digest_algorithm = hal_digest_algorithm_sha1;
    break;
  case CKM_SHA256_RSA_PKCS:
  case CKM_ECDSA_SHA256:
    session->sign_digest_algorithm = hal_digest_algorithm_sha256;
    break;
  case CKM_SHA384_RSA_PKCS:
  case CKM_ECDSA_SHA384:
    session->sign_digest_algorithm = hal_digest_algorithm_sha384;
    break;
  case CKM_SHA512_RSA_PKCS:
  case CKM_ECDSA_SHA512:
    session->sign_digest_algorithm = hal_digest_algorithm_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_algorithm = hal_digest_algorithm_none;
  }
  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_handle.handle != HAL_HANDLE_NONE)
    lose(CKR_OPERATION_ACTIVE);

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

  if (session->sign_digest_algorithm != hal_digest_algorithm_none && pSignature != NULL) {
    if ((rv = digest_update(session, session->sign_digest_algorithm,
                            &session->sign_digest_handle, pData, ulDataLen)) != CKR_OK)
      goto fail;
    pData = NULL;
    ulDataLen = 0;
  }

  switch (key_type) {

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

  default:
    lose(CKR_FUNCTION_FAILED);
  }
                                /* Fall through */
 fail:
  if (session != NULL && pSignature != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->sign_digest_handle);
  }

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

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

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

  if (session->sign_digest_algorithm == hal_digest_algorithm_none)
    lose(CKR_FUNCTION_FAILED);

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

  return mutex_unlock(p11_global_mutex);

 fail:
  if (session != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->sign_digest_handle);
  }

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

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

  if (session->sign_key_handle == CK_INVALID_HANDLE || session->sign_digest_handle.handle == HAL_HANDLE_NONE)
    lose(CKR_OPERATION_NOT_INITIALIZED);

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

  switch (key_type) {

  case CKK_RSA:
  case CKK_EC:
    rv = sign_hal_rpc(session, NULL, 0, pSignature, pulSignatureLen);
    break;

  default:
    lose(CKR_FUNCTION_FAILED);
  }
                                /* Fall through */
 fail:
  if (session != NULL && pSignature != NULL) {
    session->sign_key_handle = CK_INVALID_HANDLE;
    session->sign_digest_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->sign_digest_handle);
  }

  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_algorithm != hal_digest_algorithm_none)
    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_algorithm = hal_digest_algorithm_none;
    break;
  case CKM_SHA1_RSA_PKCS:
    session->verify_digest_algorithm = hal_digest_algorithm_sha1;
    break;
  case CKM_SHA256_RSA_PKCS:
  case CKM_ECDSA_SHA256:
    session->verify_digest_algorithm = hal_digest_algorithm_sha256;
    break;
  case CKM_SHA384_RSA_PKCS:
  case CKM_ECDSA_SHA384:
    session->verify_digest_algorithm = hal_digest_algorithm_sha384;
    break;
  case CKM_SHA512_RSA_PKCS:
  case CKM_ECDSA_SHA512:
    session->verify_digest_algorithm = hal_digest_algorithm_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_algorithm = hal_digest_algorithm_none;
  }
  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 || pSignature == NULL)
    lose(CKR_ARGUMENTS_BAD);

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

  if (session->verify_digest_algorithm != hal_digest_algorithm_none) {
    if ((rv = digest_update(session, session->verify_digest_algorithm,
                            &session->verify_digest_handle, pData, ulDataLen)) != CKR_OK)
      goto fail;
    pData = NULL;
    ulDataLen = 0;
  }

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

  switch (key_type) {

  case CKK_RSA:
  case CKK_EC:
    rv = verify_hal_rpc(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_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->verify_digest_handle);
  }

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

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

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

  if (session->verify_digest_algorithm == hal_digest_algorithm_none)
    lose(CKR_FUNCTION_FAILED);

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

  return mutex_unlock(p11_global_mutex);

 fail:
  if (session != NULL) {
    session->verify_key_handle = CK_INVALID_HANDLE;
    session->verify_digest_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->verify_digest_handle);
  }

  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

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

  if (session->verify_key_handle == CK_INVALID_HANDLE || session->verify_digest_handle.handle == HAL_HANDLE_NONE)
    lose(CKR_OPERATION_NOT_INITIALIZED);

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

  switch (key_type) {

  case CKK_RSA:
  case CKK_EC:
    rv = verify_hal_rpc(session, NULL, 0, pSignature, ulSignatureLen);
    break;

  default:
    lose(CKR_FUNCTION_FAILED);
  }

 fail:                          /* Fall through */

  if (session != NULL) {
    session->verify_key_handle = CK_INVALID_HANDLE;
    session->verify_digest_algorithm = hal_digest_algorithm_none;
    digest_cleanup(&session->verify_digest_handle);
  }

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

  case CKM_EC_KEY_PAIR_GEN:
    rv = generate_keypair(session, pMechanism, generate_keypair_ec,
                          pPublicKeyTemplate,  ulPublicKeyAttributeCount,  &p11_descriptor_ec_public_key,  phPublicKey,
                          pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_ec_private_key, phPrivateKey);
    break;

  default:
    lose(CKR_MECHANISM_INVALID);
  }

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

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

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

  if (!hal_check(hal_rpc_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;

#if 0
  /*
   * Perhaps revisit this after adding an RPC call to let us check
   * which cores are available.  For now, given that we now have
   * software core support for these hash algorithms, this test isn't
   * particularly useful.
   */

  hal_digest_algorithm_t algorithm = hal_digest_algorithm_none;
  CK_RV rv = CKR_OK;

  switch (type) {

  case CKM_SHA_1:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA_1_HMAC:
  case CKM_ECDSA_SHA1:
    algorithm = hal_digest_algorithm_sha1;
    break;

  case CKM_SHA256:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA256_HMAC:
  case CKM_ECDSA_SHA256:
    algorithm = hal_digest_algorithm_sha256;
    break;

  case CKM_SHA384:
  case CKM_SHA384_RSA_PKCS:
  case CKM_SHA384_HMAC:
  case CKM_ECDSA_SHA384:
    algorithm = hal_digest_algorithm_sha384;
    break;

  case CKM_SHA512:
  case CKM_SHA512_RSA_PKCS:
  case CKM_SHA512_HMAC:
  case CKM_ECDSA_SHA512:
    algorithm = hal_digest_algorithm_sha512;
    break;

  default:
    break;
  }

  if (algorithm != hal_digest_algorithm_none && (rv = digest_available(algorithm)) != CKR_OK)
    return rv;
#endif

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