aboutsummaryrefslogblamecommitdiff
path: root/pkcs11.c
blob: 0be75138a2a334b6864083c655feb4e08e38f2a8 (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 <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

/*
 * How many sessions and object handles to allow.  We could do this
 * with dynamic memory, but static arrays are simpler and faster.  We
 * don't expect all that many sessions, and slots in the object table
 * are cheap.
 */

#ifndef P11_MAX_SESSION_HANDLES
#define P11_MAX_SESSION_HANDLES (64)
#endif

#ifndef P11_MAX_OBJECT_HANDLES
#define P11_MAX_OBJECT_HANDLES  (4096)
#endif

/*
 * Manufacturer ID, version numbers (hardware, firmware, software), etc.
 * Some of this really should be coming from RPC queries.
 */

#warning Figure out IDs and version numbers
#define P11_MANUFACTURER_ID     "Cryptech Project"
#define P11_TOKEN_LABEL		"Cryptech Token"
#define P11_BOARD_MODEL		"Alpha Board"
#define P11_BOARD_SERIAL	"007"
#define P11_LIBRARY_DESCRIPTION	"libcryptech-pkcs11.so"
#define	P11_SLOT_DESCRIPTION	"Cryptech Alpha slot"
#define P11_VERSION_HW_MAJOR    0
#define P11_VERSION_HW_MINOR    3
#define P11_VERSION_FW_MAJOR    3
#define P11_VERSION_FW_MINOR    0
#define P11_VERSION_SW_MAJOR    3
#define P11_VERSION_SW_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 sessions.  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 */
  CK_STATE state;                       /* State (CKS_*) of this session */
  CK_NOTIFY notify;                     /* Notification callback */
  CK_VOID_PTR application;              /* Application data */
  hal_pkey_attribute_t *find_query;     /* FindObject*() query state */
  unsigned find_query_token : 1;        /* Find query for token objects in progress */
  unsigned find_query_session : 1;      /* Find query for session objects in progress */
  unsigned find_query_n : 30;           /* Number of entries in find_query */
  hal_uuid_t find_query_previous_uuid;  /* Previous UUID for find queries */
  unsigned find_query_state;            /* hal_rpc_pkey_match() internal state */
  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 objects.  These are pretty simple, as they're really just
 * mappings from PKCS #11's naming scheme to libhal UUIDs, with a little
 * extra fun for PKCS #11 "session" objects.
 */

typedef struct p11_object {
  CK_OBJECT_HANDLE  handle;             /* Object handle */
  CK_SESSION_HANDLE session;            /* Associated session (if any) */
  hal_uuid_t        uuid;               /* libhal key UUID */
} p11_object_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 which live on the
 * token, ie, in non-volatile storage) and session object handles
 * (handles for objects which live only as long as the session does),
 * and we steal two bits of the handle as as flags to distinguish
 * between these three kinds handles.  We sub-divide the rest of a
 * handle into a nonce (well, a lame one -- for now this is just a
 * counter, if this becomes an issue we could do better) and an array
 * index into the relevant table.
 */

typedef enum {
  handle_flavor_none            = 0, /* Matches CK_INVALID_HANDLE */
  handle_flavor_session         = 1,
  handle_flavor_token_object    = 2,
  handle_flavor_session_object  = 3
} handle_flavor_t;

#define HANDLE_MASK_FLAVOR      (0xc0000000)
#define HANDLE_MASK_NONCE       (0x3fff0000)
#define HANDLE_MASK_INDEX       (0x0000ffff)



/*
 * 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 and object handles for this application.
 */

static p11_session_t p11_sessions     [P11_MAX_SESSION_HANDLES];
static p11_object_t  p11_objects      [P11_MAX_OBJECT_HANDLES];
static unsigned      p11_object_uuids [P11_MAX_OBJECT_HANDLES];

static unsigned p11_sessions_in_use, p11_objects_in_use;

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

/*
 * All (?) public functions should test whether we've been initialized or not.
 * Where practical, we bury this check in other boilerplate.
 */

#if USE_POSIX

#define p11_uninitialized()     (!initialized_pid)

#else

#define p11_uninitialized()     (0)

#endif

/*
 * Handle unsupported functions.
 */

#define UNSUPPORTED_FUNCTION(_name_)            \
  do {                                          \
    ENTER_PUBLIC_FUNCTION(_name_);              \
    if (p11_uninitialized())                    \
      return CKR_CRYPTOKI_NOT_INITIALIZED;      \
    return CKR_FUNCTION_NOT_SUPPORTED;          \
  } while (0)



/*
 * 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.
 *
 * Since the locking code depends on initialization,
 * attempting to lock anything when not initialized
 * is a failure, by definition.
 */

#define mutex_lock_or_return_failure(_m_)       \
  do {                                          \
    if (p11_uninitialized())                    \
      return CKR_CRYPTOKI_NOT_INITIALIZED;      \
    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 */



/*
 * Bit mask twiddling utilities.
 */

static inline CK_ULONG mask_pos(const CK_ULONG mask)
{
  return mask & ~(mask - 1);    /* Finds least significant bit set in mask */
}

static inline CK_ULONG mask_ldb(const CK_ULONG mask, const CK_ULONG value)
{
  return (value & mask) / mask_pos(mask);
}

static inline CK_ULONG mask_dpb(const CK_ULONG mask, const CK_ULONG value)
{
  return (value * mask_pos(mask)) & mask;
}

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

/*
 * Handle utilities.
 */

static inline CK_ULONG handle_compose(const handle_flavor_t flavor,
                                      const unsigned nonce,
                                      const unsigned index)
{
  return (mask_dpb(HANDLE_MASK_FLAVOR, flavor) |
          mask_dpb(HANDLE_MASK_NONCE,  nonce)  |
          mask_dpb(HANDLE_MASK_INDEX,  index));
}

static inline handle_flavor_t handle_flavor(const CK_ULONG handle)
{
  return (handle_flavor_t) mask_ldb(HANDLE_MASK_FLAVOR, handle);
}

static inline unsigned handle_index(const CK_ULONG handle)
{
  return mask_ldb(HANDLE_MASK_INDEX, handle);
}



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



/*
 * Attribute methods.
 */

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

/*
 * Idiom for combination of p11_attribute_find_value_in_template() and
 * p11_find_attribute_in_descriptor().
 */

static const void *
p11_attribute_find_value_in_template_or_descriptor(const p11_descriptor_t *descriptor,
                                                   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);
  if (i >= 0)
    return template[i].pValue;
  const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type);
  assert(atd != NULL);
  return atd->value;
}

/*
 * Set attributes for a newly-created or newly-uploaded HSM key.
 */

static int p11_attributes_set(const hal_pkey_handle_t pkey,
                              const CK_ATTRIBUTE_PTR template,
                              const CK_ULONG template_length,
                              const p11_descriptor_t * const descriptor,
                              const hal_pkey_attribute_t *extra,
                              const unsigned extra_length)
{
  assert(template != NULL && descriptor != NULL && (extra_length == 0 || extra != NULL));

  /*
   * 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 as an attribute.  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.
   */

  hal_pkey_attribute_t attributes[template_length + descriptor->n_attributes + extra_length];
  unsigned n = 0;

  for (int 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 (n >= sizeof(attributes) / sizeof(*attributes))
      return 0;

    attributes[n].type   = type;
    attributes[n].value  = val;
    attributes[n].length = len;
    n++;
  }

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

  for (int 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 (n >= sizeof(attributes) / sizeof(*attributes))
      return 0;

    attributes[n].type   = type;
    attributes[n].value  = val;
    attributes[n].length = len;
    n++;
  }

  /*
   * Finally, add any attributes provided by the calling function itself.
   */

  for (int i = 0; i < extra_length; i++) {
    if (n >= sizeof(attributes) / sizeof(*attributes))
      return 0;

    attributes[n].type   = extra[i].type;
    attributes[n].value  = extra[i].value;
    attributes[n].length = extra[i].length;
    n++;
  }

  /*
   * Set all those attributes.
   */

  if (!hal_check(hal_rpc_pkey_set_attributes(pkey, attributes, n)))
    return 0;

  return 1;
}

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



/*
 * Access rights.
 */

static CK_RV p11_check_read_access(const p11_session_t *session,
                                   const CK_BBOOL cka_private,
                                   const CK_BBOOL cka_token)
{
  if (session == NULL)
    return CKR_SESSION_HANDLE_INVALID;

  switch (session->state) {

  case CKS_RO_PUBLIC_SESSION:
    /* RO access to public token objects, RW access to public session objects */
    return (cka_private) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK;

  case CKS_RO_USER_FUNCTIONS:
    /* RO access to all token objects, RW access to all session objects */
    return CKR_OK;

  case CKS_RW_PUBLIC_SESSION:
    /* RW access all public objects */
    return (cka_private) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK;

  case CKS_RW_USER_FUNCTIONS:
    /* RW acess to all objects */
    return CKR_OK;

  case CKS_RW_SO_FUNCTIONS:
    /* RW access to public token objects only */
    return (cka_private || ! cka_token) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK;
  }

  return CKR_SESSION_HANDLE_INVALID;
}

static CK_RV p11_check_write_access(const p11_session_t *session,
                                    const CK_BBOOL cka_private,
                                    const CK_BBOOL cka_token)
{
  if (session == NULL)
    return CKR_SESSION_HANDLE_INVALID;

  switch (session->state) {

  case CKS_RO_PUBLIC_SESSION:
    /* RO access to public token objects, RW access to public session objects */
    return (cka_private || cka_token) ? CKR_USER_NOT_LOGGED_IN : CKR_OK;

  case CKS_RO_USER_FUNCTIONS:
    /* RO access to all token objects, RW access to all session objects */
    return (cka_token) ? CKR_SESSION_READ_ONLY : CKR_OK;

  case CKS_RW_PUBLIC_SESSION:
    /* RW access all public objects */
    return (cka_private) ? CKR_USER_NOT_LOGGED_IN : CKR_OK;

  case CKS_RW_USER_FUNCTIONS:
    /* RW acess to all objects */
    return CKR_OK;

  case CKS_RW_SO_FUNCTIONS:
    /* RW access to public token objects only */
    return (cka_private || ! cka_token) ? CKR_USER_NOT_LOGGED_IN : CKR_OK;
  }

  return CKR_SESSION_HANDLE_INVALID;
}



/*
 * Object methods.
 */

/*
 * Look up an object's UUID in the object index table, return
 * indication of whether it's present or not and the position it
 * should occupy within the index table in either case.
 *
 * NB: *where is a position in p11_object_uuids[], not p11_objects[].
 */

static int p11_object_uuid_bsearch(const hal_uuid_t * const uuid, int *where)
{
  assert(uuid != NULL && where != NULL);

  int lo = -1;
  int hi = p11_objects_in_use;

  for (;;) {
    int m = (lo + hi) / 2;
    if (hi == 0 || m == lo) {
      *where = hi;
      return 0;
    }
    const int cmp = hal_uuid_cmp(uuid, &p11_objects[p11_object_uuids[m]].uuid);
    if (cmp < 0)
      hi = m;
    else if (cmp > 0)
      lo = m;
    else {
      *where = m;
      return 1;
    }
  }
}

/*
 * Allocate a new object.
 */

static CK_OBJECT_HANDLE p11_object_allocate(const handle_flavor_t flavor,
                                            const hal_uuid_t *uuid,
                                            const p11_session_t *session)
{
  if (uuid == NULL)
    return CK_INVALID_HANDLE;

  if (flavor != handle_flavor_token_object && flavor != handle_flavor_session_object)
    return CK_INVALID_HANDLE;

  int where;

  if (p11_object_uuid_bsearch(uuid, &where)) {
    assert(where >= 0 && where < p11_objects_in_use);
    const CK_OBJECT_HANDLE handle = p11_objects[p11_object_uuids[where]].handle;
    return handle_flavor(handle) == flavor ? handle : CK_INVALID_HANDLE;
  }

  if (p11_objects_in_use >= sizeof(p11_objects) / sizeof(*p11_objects))
    return CK_INVALID_HANDLE;

  static unsigned next_index, nonce;
  const unsigned  last_index = next_index;
  p11_object_t *object = NULL;

  do {

    next_index = (next_index + 1) % (sizeof(p11_objects) / sizeof(*p11_objects));

    if (next_index == last_index)
      return CK_INVALID_HANDLE;

    if (next_index == 0)
      ++nonce;

    object = &p11_objects[next_index];

  } while (object->handle != CK_INVALID_HANDLE);

  object->handle = handle_compose(flavor, nonce, next_index);
  object->uuid = *uuid;
  object->session = flavor == handle_flavor_session_object ? session->handle : CK_INVALID_HANDLE;

  if (where < p11_objects_in_use)
    memmove(&p11_object_uuids[where + 1], &p11_object_uuids[where],
            (p11_objects_in_use - where) * sizeof(*p11_object_uuids));

  p11_object_uuids[where] = next_index;

  p11_objects_in_use++;

  return object->handle;
}

/*
 * Free an object.
 */

static void p11_object_free(p11_object_t *object)
{
  if (object == NULL)
    return;

  int where;

  if (p11_objects_in_use > 0 &&
      p11_object_uuid_bsearch(&object->uuid, &where) &&
      --p11_objects_in_use > where)
    memmove(&p11_object_uuids[where], &p11_object_uuids[where + 1],
            (p11_objects_in_use - where) * sizeof(*p11_object_uuids));

  memset(object, 0, sizeof(*object));
}

/*
 * Find an object given its UUID.
 */

static p11_object_t *p11_object_by_uuid(const hal_uuid_t * const uuid)
{
  int where;

  if (uuid == NULL || !p11_object_uuid_bsearch(uuid, &where))
    return NULL;

  assert(where >= 0 && where < p11_objects_in_use);

  p11_object_t *object = &p11_objects[p11_object_uuids[where]];

  if (handle_flavor(object->handle) != handle_flavor_session_object &&
      handle_flavor(object->handle) != handle_flavor_token_object)
    return NULL;

  return object;
}

/*
 * Find an object given its handle.
 */

static p11_object_t *p11_object_by_handle(const CK_OBJECT_HANDLE object_handle)
{
  if (handle_flavor(object_handle) != handle_flavor_session_object &&
      handle_flavor(object_handle) != handle_flavor_token_object)
    return NULL;

  const unsigned index = handle_index(object_handle);

  if (index >= sizeof(p11_objects) / sizeof(*p11_objects))
    return NULL;

  p11_object_t *object = &p11_objects[index];

  if (object->handle != object_handle)
    return NULL;

  return object;
}

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

static inline handle_flavor_t p11_object_flavor_from_cka_token(const CK_BBOOL *bbool)
{
  return (bbool != NULL && *bbool) ? handle_flavor_token_object : handle_flavor_session_object;
}

/*
 * Open the HSM pkey object (if any) corresponding to the PKCS #11 handle.
 */

static int p11_object_pkey_open(const p11_session_t *session,
                                const CK_OBJECT_HANDLE object_handle,
                                hal_pkey_handle_t *pkey)
{
  const p11_object_t *object = p11_object_by_handle(object_handle);

  return (session != NULL && pkey != NULL && object != NULL &&
          hal_check(hal_rpc_pkey_open(p11_session_hal_client(session),
                                      p11_session_hal_session(session),
                                      pkey, &object->uuid)));
}

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

static int p11_object_create_rsa_public_key(const p11_session_t * const session,
                                            const handle_flavor_t flavor,
                                            const CK_ATTRIBUTE_PTR template,
                                            const CK_ULONG template_len,
                                            const p11_descriptor_t * const descriptor,
                                            CK_OBJECT_HANDLE_PTR phObject,
                                            const hal_key_flags_t flags)
{
  const hal_pkey_attribute_t extra[] = {
    {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}
  };

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  hal_uuid_t uuid;

  const uint8_t *cka_modulus = NULL;
  size_t cka_modulus_len = 0;
  const uint8_t *cka_public_exponent = const_0x010001;
  size_t cka_public_exponent_len = sizeof(const_0x010001);

  for (int i = 0; i < template_len; i++) {
    const void * const val = template[i].pValue;
    const size_t       len = template[i].ulValueLen;
    switch (template[i].type) {
    case CKA_MODULUS:          cka_modulus          = val; cka_modulus_len          = len; break;
    case CKA_PUBLIC_EXPONENT:  cka_public_exponent  = val; cka_public_exponent_len  = len; break;
    }
  }

  int ok = hal_check(hal_rsa_key_load_public(&key, keybuf, sizeof(keybuf),
                                             cka_modulus, cka_modulus_len,
                                             cka_public_exponent, cka_public_exponent_len));

  if (ok) {
    uint8_t der[hal_rsa_public_key_to_der_len(key)];
    ok = (hal_check(hal_rsa_public_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, &uuid, der, sizeof(der), flags)));
  }

  if (ok)
    ok = p11_attributes_set(pkey, template, template_len, descriptor,
                            extra, sizeof(extra)/sizeof(*extra));

  if (ok) {
    *phObject = p11_object_allocate(flavor, &uuid, session);
    ok = *phObject != CK_INVALID_HANDLE;
  }

  if (!ok && pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_delete(pkey);
  else
    (void) hal_rpc_pkey_close(pkey);

  return ok;
}

static int p11_object_create_ec_public_key(const p11_session_t * const session,
                                           const handle_flavor_t flavor,
                                           const CK_ATTRIBUTE_PTR template,
                                           const CK_ULONG template_len,
                                           const p11_descriptor_t * const descriptor,
                                           CK_OBJECT_HANDLE_PTR phObject,
                                           const hal_key_flags_t flags)
{
  const hal_pkey_attribute_t extra[] = {
    {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}
  };

  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;
  hal_uuid_t uuid;

  const uint8_t *cka_ec_point  = NULL;  size_t cka_ec_point_len  = 0;
  const uint8_t *cka_ec_params = NULL;  size_t cka_ec_params_len = 0;

  for (int i = 0; i < template_len; i++) {
    const void * const val = template[i].pValue;
    const size_t       len = template[i].ulValueLen;
    switch (template[i].type) {
    case CKA_EC_POINT:  cka_ec_point  = val; cka_ec_point_len  = len; break;
    case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break;
    }
  }

  int ok
    = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) &&
       hal_check(hal_ecdsa_key_from_ecpoint(&key, keybuf, sizeof(keybuf),
                                            cka_ec_point, cka_ec_point_len,
                                            curve)));

  if (ok) {
    uint8_t der[hal_ecdsa_public_key_to_der_len(key)];
    ok = (hal_check(hal_ecdsa_public_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, &uuid, der, sizeof(der), flags)));
  }

  if (ok)
    ok = p11_attributes_set(pkey, template, template_len, descriptor,
                            extra, sizeof(extra)/sizeof(*extra));

  if (ok) {
    *phObject = p11_object_allocate(flavor, &uuid, session);
    ok = *phObject != CK_INVALID_HANDLE;
  }

  if (!ok && pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_delete(pkey);
  else
    (void) hal_rpc_pkey_close(pkey);

  return ok;
}

static int p11_object_create_rsa_private_key(const p11_session_t * const session,
                                             const handle_flavor_t flavor,
                                             const CK_ATTRIBUTE_PTR template,
                                             const CK_ULONG template_len,
                                             const p11_descriptor_t * const descriptor,
                                             CK_OBJECT_HANDLE_PTR phObject,
                                             const hal_key_flags_t flags)
{
  const hal_pkey_attribute_t extra[] = {
    {.type = CKA_LOCAL,             .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)},
    {.type = CKA_ALWAYS_SENSITIVE,  .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)},
    {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}
  };

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  uint8_t keybuf[hal_rsa_key_t_size];
  hal_rsa_key_t *key = NULL;
  hal_uuid_t uuid;

  const uint8_t *cka_modulus          = NULL;   size_t cka_modulus_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;

  const uint8_t *cka_public_exponent = const_0x010001;
  size_t cka_public_exponent_len = sizeof(const_0x010001);

  for (int i = 0; i < template_len; i++) {
    const void * const val = template[i].pValue;
    const size_t       len = template[i].ulValueLen;
    switch (template[i].type) {
    case CKA_MODULUS:          cka_modulus          = val; cka_modulus_len          = len; break;
    case CKA_PUBLIC_EXPONENT:  cka_public_exponent  = val; cka_public_exponent_len  = len; break;
    case CKA_PRIVATE_EXPONENT: cka_private_exponent = val; cka_private_exponent_len = len; break;
    case CKA_PRIME_1:          cka_prime_1          = val; cka_prime_1_len          = len; break;
    case CKA_PRIME_2:          cka_prime_2          = val; cka_prime_2_len          = len; break;
    case CKA_EXPONENT_1:       cka_exponent_1       = val; cka_exponent_1_len       = len; break;
    case CKA_EXPONENT_2:       cka_exponent_2       = val; cka_exponent_2_len       = len; break;
    case CKA_COEFFICIENT:      cka_coefficient      = val; cka_coefficient_len      = len; break;
    }
  }

  int ok = hal_check(hal_rsa_key_load_private(&key, keybuf, sizeof(keybuf),
                                              cka_modulus,                  cka_modulus_len,
                                              cka_public_exponent,          cka_public_exponent_len,
                                              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) {
    uint8_t der[hal_rsa_private_key_to_der_len(key)];
    ok = (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, &uuid, der, sizeof(der), flags)));
    memset(der, 0, sizeof(der));
  }

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

  if (ok)
    ok = p11_attributes_set(pkey, template, template_len, descriptor,
                            extra, sizeof(extra)/sizeof(*extra));

  if (ok) {
    *phObject = p11_object_allocate(flavor, &uuid, session);
    ok = *phObject != CK_INVALID_HANDLE;
  }

  if (!ok && pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_delete(pkey);
  else
    (void) hal_rpc_pkey_close(pkey);

  return ok;
}

static int p11_object_create_ec_private_key(const p11_session_t * const session,
                                            const handle_flavor_t flavor,
                                            const CK_ATTRIBUTE_PTR template,
                                            const CK_ULONG template_len,
                                            const p11_descriptor_t * const descriptor,
                                            CK_OBJECT_HANDLE_PTR phObject,
                                            const hal_key_flags_t flags)
{
  const hal_pkey_attribute_t extra[] = {
    {.type = CKA_LOCAL,             .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)},
    {.type = CKA_ALWAYS_SENSITIVE,  .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)},
    {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}
  };

  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;
  hal_uuid_t uuid;

  const uint8_t *cka_value     = NULL;  size_t cka_value_len     = 0;
  const uint8_t *cka_ec_point  = NULL;  size_t cka_ec_point_len  = 0;
  const uint8_t *cka_ec_params = NULL;  size_t cka_ec_params_len = 0;

  for (int i = 0; i < template_len; i++) {
    const void * const val = template[i].pValue;
    const size_t       len = template[i].ulValueLen;
    switch (template[i].type) {
    case CKA_VALUE:     cka_value     = val; cka_value_len     = len; break;
    case CKA_EC_POINT:  cka_ec_point  = val; cka_ec_point_len  = len; break;
    case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break;
    }
  }

  int ok
    = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) &&
       hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve,
                                            cka_ec_point + 1 + 0 * cka_ec_point_len / 2,
                                            cka_ec_point_len / 2,
                                            cka_ec_point + 1 + 1 * cka_ec_point_len / 2,
                                            cka_ec_point_len / 2,
                                            cka_value,
                                            cka_value_len)));

  if (ok) {
    uint8_t der[hal_ecdsa_private_key_to_der_len(key)];
    ok = (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, &uuid, der, sizeof(der), flags)));
    memset(der, 0, sizeof(der));
  }

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

  if (ok)
    ok = p11_attributes_set(pkey, template, template_len, descriptor,
                            extra, sizeof(extra)/sizeof(*extra));

  if (ok) {
    *phObject = p11_object_allocate(flavor, &uuid, session);
    ok = *phObject != CK_INVALID_HANDLE;
  }

  if (!ok && pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_delete(pkey);
  else
    (void) hal_rpc_pkey_close(pkey);

  return ok;
}



/*
 * Session methods.
 */

/*
 * Create a new session.
 */

static p11_session_t *p11_session_allocate(void)
{
  static unsigned next_index, nonce;
  const unsigned  last_index = next_index;
  p11_session_t *session = NULL;

  if (p11_sessions_in_use >= sizeof(p11_sessions) / sizeof(*p11_sessions))
    return NULL;

  do {

    next_index = (next_index + 1) % (sizeof(p11_sessions) / sizeof(*p11_sessions));

    if (next_index == last_index)
      return NULL;

    if (next_index == 0)
      ++nonce;

    session = &p11_sessions[next_index];

  } while (session->handle != CK_INVALID_HANDLE);

  memset(session, 0, sizeof(*session));
  session->handle = handle_compose(handle_flavor_session, nonce, next_index);
  p11_sessions_in_use++;
  return session;
}

/*
 * Free a session.
 */

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

  assert(p11_sessions_in_use > 0);

  if (session->find_query)
    free(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);

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

  if (--p11_sessions_in_use == 0)
    logged_in_as = not_logged_in;
}

/*
 * Find a session.
 */

static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
{
  if (handle_flavor(session_handle) != handle_flavor_session)
    return NULL;

  const unsigned index = handle_index(session_handle);

  if (index >= sizeof(p11_sessions) / sizeof(*p11_sessions))
    return NULL;

  p11_session_t *session = &p11_sessions[index];

  if (session->handle != session_handle)
    return NULL;

  return session;
}

/*
 * Iterate over session handles.  Start with CK_INVALID_HANDLE,
 * returns CK_INVALID_HANDLE when done.
 *
 * This does not verify the provided session handle, because we want
 * to be able to modify the sessions this finds, including deleting
 * them (which invalidates the session handle).  Don't trust the
 * returned handle until it has been blessed by p11_session_find().
 */

static CK_SESSION_HANDLE p11_session_handle_iterate(const CK_SESSION_HANDLE session_handle)
{
  unsigned index;

  if (session_handle == CK_INVALID_HANDLE)
    index = 0;

  else if (handle_flavor(session_handle) == handle_flavor_session)
    index = handle_index(session_handle) + 1;

  else
    return CK_INVALID_HANDLE;

  for (; index < sizeof(p11_sessions) / sizeof(*p11_sessions); index++)
    if (handle_flavor(p11_sessions[index].handle) == handle_flavor_session)
      return p11_sessions[index].handle;

  return CK_INVALID_HANDLE;
}

/*
 * Same thing, but return session objects instead of session handles.
 * This is just syntactic sugar around a common idiom.
 */

static p11_session_t *p11_session_iterate(p11_session_t *session)
{
  const CK_SESSION_HANDLE handle = session == NULL ? CK_INVALID_HANDLE : session->handle;
  return p11_session_find(p11_session_handle_iterate(handle));
}

/*
 * Delete all sessions.  Have to use p11_session_handle_iterate() here.
 */

static void p11_session_free_all(void)
{
  for (CK_SESSION_HANDLE handle = p11_session_handle_iterate(CK_INVALID_HANDLE);
       handle != CK_INVALID_HANDLE; handle = p11_session_handle_iterate(handle))
    p11_session_free(p11_session_find(handle));
}

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

static int p11_session_consistent_login(void)
{
  switch (logged_in_as) {

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

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

  case logged_in_as_so:
    for (p11_session_t *session = p11_session_iterate(NULL);
         session != NULL; session = p11_session_iterate(session))
      if (session->state != CKS_RW_SO_FUNCTIONS)
        return 0;
    return 1;

  default:
    return 0;
  }
}



/*
 * PKCS #11 likes space-padded rather than null-terminated strings.
 * This requires minor antics so that we can use a printf()-like API
 * while neither overflowing the caller's buffer nor truncating the
 * output if it happens to be exactly the target length.
 */

static int psnprintf(void *buffer_, size_t size, const char *format, ...)
{
  char buffer[size + 1];
  size_t n;
  va_list ap;

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

  for (size_t i = n; i < size; i++)
    buffer[i] = ' ';

  memcpy(buffer_, buffer, size);

  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 and that
 * the session's current login state allows access with this template.
 */

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 * const cka_private
    = p11_attribute_find_value_in_template_or_descriptor(descriptor, CKA_PRIVATE,
                                                         template, template_length);
  const CK_BBOOL * const cka_token
    = p11_attribute_find_value_in_template_or_descriptor(descriptor, CKA_TOKEN,
                                                         template, template_length);
  CK_RV rv;

  assert(cka_private != NULL && cka_token != NULL);

  /*
   * Morass of session-state-specific restrictions on which objects we
   * can even see, much less modify.  Callers of this function need RW
   * acecss to the object in question, which simplifies this a bit.
   */

  if ((rv = p11_check_write_access(session, *cka_private, *cka_token)) != CKR_OK)
    goto fail;

  for (int 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 (int 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;

  const CK_BBOOL * public_cka_private = NULL, * public_cka_token = NULL;
  const CK_BBOOL *private_cka_private = NULL, *private_cka_token = NULL;
  const CK_BBOOL *private_cka_extractable = NULL;

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

    if (type == CKA_TOKEN)
      public_cka_token = val;

    if (type == CKA_PRIVATE)
      public_cka_private = val;

    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;

    if (type == CKA_TOKEN)
      private_cka_token = val;

    if (type == CKA_PRIVATE)
      private_cka_private = val;

    if (type == CKA_EXTRACTABLE)
      private_cka_extractable = val;

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

  /*
   * Pass PKCS #11's weird notion of "public" objects through to HSM.
   */

  if (public_cka_private != NULL && ! *public_cka_private)
    *public_flags |= HAL_KEY_FLAG_PUBLIC;

  if (private_cka_private != NULL && ! *private_cka_private)
    *private_flags |= HAL_KEY_FLAG_PUBLIC;

  /*
   * Pass extractability through to HSM.  Public keys are always extractable.
   */

  *public_flags |= HAL_KEY_FLAG_EXPORTABLE;

  if (private_cka_extractable != NULL && *private_cka_extractable)
    *private_flags |= HAL_KEY_FLAG_EXPORTABLE;

  /*
   * Check that all required attributes have been specified,
   * and that our current session state allows this access.
   */

  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 handle_flavor_t public_handle_flavor,
                                       const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                       const CK_ULONG ulPublicKeyAttributeCount,
                                       const p11_descriptor_t *public_descriptor,
                                       CK_OBJECT_HANDLE_PTR phPublicKey,
                                       const hal_key_flags_t public_flags,
                                       const handle_flavor_t private_handle_flavor,
                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                       const CK_ULONG ulPrivateKeyAttributeCount,
                                       const p11_descriptor_t *private_descriptor,
                                       CK_OBJECT_HANDLE_PTR phPrivateKey,
                                       const hal_key_flags_t private_flags,
                                       const CK_MECHANISM_PTR pMechanism)
{
  hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE};
  const uint8_t *public_exponent = const_0x010001;
  size_t public_exponent_len = sizeof(const_0x010001);
  hal_uuid_t public_uuid, private_uuid;
  CK_ULONG keysize = 0;
  CK_RV rv;

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

  for (int 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_pkey_generate_rsa(p11_session_hal_client(session),
                                             p11_session_hal_session(session),
                                             &private_pkey, &private_uuid, keysize,
                                             public_exponent, public_exponent_len,
                                             private_flags)))
      lose(CKR_FUNCTION_FAILED);

    uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_rsa_key_t_size];
    size_t der_len, modulus_len;
    hal_rsa_key_t *key = NULL;

    if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der)))       ||
        !hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))     ||
        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                     p11_session_hal_session(session),
                                     &public_pkey, &public_uuid, der, der_len, public_flags))   ||
        !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))))
      lose(CKR_FUNCTION_FAILED);

    const hal_pkey_attribute_t extra[] = {
      {.type  = CKA_LOCAL,
       .value = &const_CK_TRUE,         .length = sizeof(const_CK_TRUE)},
      {.type  = CKA_KEY_GEN_MECHANISM,
       .value = &pMechanism->mechanism, .length = sizeof(pMechanism->mechanism)},
      {.type  = CKA_MODULUS,
       .value  = modulus,               .length = modulus_len}
    };

    if (!p11_attributes_set(private_pkey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                            private_descriptor, extra, sizeof(extra)/sizeof(*extra))            ||
        !p11_attributes_set(public_pkey, pPublicKeyTemplate, ulPublicKeyAttributeCount,
                            public_descriptor, extra, sizeof(extra)/sizeof(*extra)))
      lose(CKR_FUNCTION_FAILED);

    *phPrivateKey = p11_object_allocate(private_handle_flavor, &private_uuid, session);
    *phPublicKey  = p11_object_allocate(public_handle_flavor,  &public_uuid,  session);

    if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE)
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;

 fail:
  hal_rpc_pkey_close(private_pkey);
  hal_rpc_pkey_close(public_pkey);
  return rv;
}

/*
 * CKM_EC_KEY_PAIR_GEN key pair generation handler.
 */

static CK_RV generate_keypair_ec(p11_session_t *session,
                                 const handle_flavor_t public_handle_flavor,
                                 const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                 const CK_ULONG ulPublicKeyAttributeCount,
                                 const p11_descriptor_t *public_descriptor,
                                 CK_OBJECT_HANDLE_PTR phPublicKey,
                                 const hal_key_flags_t public_flags,
                                 const handle_flavor_t private_handle_flavor,
                                 const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                 const CK_ULONG ulPrivateKeyAttributeCount,
                                 const p11_descriptor_t *private_descriptor,
                                 CK_OBJECT_HANDLE_PTR phPrivateKey,
                                 const hal_key_flags_t private_flags,
                                 const CK_MECHANISM_PTR pMechanism)
{
  hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE};
  const CK_BYTE *params = NULL;
  hal_curve_name_t curve;
  size_t params_len;
  hal_uuid_t public_uuid, private_uuid;
  CK_RV rv;

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

  for (int 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_pkey_generate_ec(p11_session_hal_client(session),
                                            p11_session_hal_session(session),
                                            &private_pkey, &private_uuid,
                                            curve, private_flags)))
      lose(CKR_FUNCTION_FAILED);

    uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_ecdsa_key_t_size];
    hal_ecdsa_key_t *key = NULL;
    size_t der_len;

    if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der)))       ||
        !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))   ||
        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                     p11_session_hal_session(session),
                                     &public_pkey, &public_uuid, der, der_len, public_flags)))
      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))))
      lose(CKR_FUNCTION_FAILED);

    const hal_pkey_attribute_t extra[] = {
      {.type  = CKA_LOCAL,
       .value = &const_CK_TRUE,         .length = sizeof(const_CK_TRUE)},
      {.type  = CKA_KEY_GEN_MECHANISM,
       .value = &pMechanism->mechanism, .length = sizeof(pMechanism->mechanism)},
      {.type  = CKA_EC_PARAMS,
       .value  = params,                .length = params_len},
      {.type  = CKA_EC_POINT,
       .value = point,                  .length = sizeof(point)}
    };

    if (!p11_attributes_set(private_pkey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                            private_descriptor, extra, sizeof(extra)/sizeof(*extra) - 1)         ||
        !p11_attributes_set(public_pkey, pPublicKeyTemplate, ulPublicKeyAttributeCount,
                            public_descriptor, extra, sizeof(extra)/sizeof(*extra)))
      lose(CKR_FUNCTION_FAILED);

    *phPrivateKey = p11_object_allocate(private_handle_flavor, &private_uuid, session);
    *phPublicKey  = p11_object_allocate(public_handle_flavor,  &public_uuid,  session);

    if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE)
      lose(CKR_FUNCTION_FAILED);
  }

  rv = CKR_OK;

 fail:
  hal_rpc_pkey_close(private_pkey);
  hal_rpc_pkey_close(public_pkey);
  return rv;
}

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

static CK_RV generate_keypair(p11_session_t *session,
                              const CK_MECHANISM_PTR pMechanism,
                              CK_RV (*mechanism_handler)(p11_session_t *session,
                                                         const handle_flavor_t public_handle_flavor,
                                                         const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                                         const CK_ULONG ulPublicKeyAttributeCount,
                                                         const p11_descriptor_t *public_descriptor,
                                                         CK_OBJECT_HANDLE_PTR phPublicKey,
                                                         const hal_key_flags_t public_flags,
                                                         const handle_flavor_t private_handle_flavor,
                                                         const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                                         const CK_ULONG ulPrivateKeyAttributeCount,
                                                         const p11_descriptor_t *private_descriptor,
                                                         CK_OBJECT_HANDLE_PTR phPrivateKey,
                                                         const hal_key_flags_t private_flags,
                                                         const CK_MECHANISM_PTR pMechanism),
                              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)
{
  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;

  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 (int i = 0; i < ulPublicKeyAttributeCount; i++)
    if (pPublicKeyTemplate[i].type == CKA_TOKEN)
      public_handle_flavor = p11_object_flavor_from_cka_token(pPublicKeyTemplate[i].pValue);

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

  if (public_handle_flavor == handle_flavor_token_object)
    public_flags  |= HAL_KEY_FLAG_TOKEN;

  if (private_handle_flavor == handle_flavor_token_object)
    private_flags |= HAL_KEY_FLAG_TOKEN;

  return mechanism_handler(session,
                           public_handle_flavor,  pPublicKeyTemplate,  ulPublicKeyAttributeCount,
                           public_descriptor,     phPublicKey,         public_flags,
                           private_handle_flavor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
                           private_descriptor,    phPrivateKey,        private_flags,
                           pMechanism);
}

/*
 * 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)
{
  const CK_BBOOL *cka_private = NULL;
  const CK_BBOOL *cka_token = NULL;
  CK_RV rv = CKR_OK;
  int i;

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

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

    if (type == CKA_TOKEN)
      cka_token = val;

    if (type == CKA_PRIVATE)
      cka_private = val;
  }

  /*
   * Check that all required attributes have been specified,
   * and that our current session state allows this access.
   */

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

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

  hal_pkey_attribute_t attribute;
  uint8_t attribute_buffer[sizeof(CK_KEY_TYPE)];
  hal_curve_name_t curve;
  CK_BYTE oid[20];

  attribute.type = CKA_KEY_TYPE;
  if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1,
                                             attribute_buffer, sizeof(attribute_buffer))))
    return 0;

  switch (*(CK_KEY_TYPE*)attribute.value) {

  case CKK_RSA:
    attribute.type = CKA_MODULUS;
    if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, NULL, 0)) ||
        attribute.length == HAL_PKEY_ATTRIBUTE_NIL)
      return 0;
    *signature_len = attribute.length;
    return 1;

  case CKK_EC:
    attribute.type = CKA_EC_PARAMS;
    if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, oid, sizeof(oid))) ||
        !ec_curve_oid_to_name(attribute.value, attribute.length, &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_pkey_open(session, session->sign_key_handle, &pkey))
    lose(CKR_FUNCTION_FAILED);

  if (!get_signature_len(pkey, &signature_len))
    lose(CKR_FUNCTION_FAILED);

  rv = pSignature != NULL && signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulSignatureLen = signature_len;

  if (pSignature != NULL && rv == CKR_OK)
    rv = p11_whine_from_hal(hal_rpc_pkey_sign(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_pkey_open(session, session->verify_key_handle, &pkey))
    lose(CKR_FUNCTION_FAILED);

  rv = p11_whine_from_hal(hal_rpc_pkey_verify(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;
  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);

#if USE_POSIX
  initialized_pid = getpid();
#endif

  return CKR_OK;

 fail:
  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_free_all();

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

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;

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

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

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  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),          P11_TOKEN_LABEL);
  psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), P11_MANUFACTURER_ID);
  psnprintf(pInfo->model,          sizeof(pInfo->model),          P11_BOARD_MODEL);
  psnprintf(pInfo->serialNumber,   sizeof(pInfo->serialNumber),   P11_BOARD_SERIAL);

  pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED;

#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_FW_MAJOR;
  pInfo->firmwareVersion.minor  = P11_VERSION_FW_MINOR;

  /*
   * Eventually we expect Cryptech devices to have their own hardware
   * clocks, in which case we'd set CKF_CLOCK_ON_TOKEN and
   * pInfo->utcTime.  Hardware not implemented yet, so not here either.
   */

  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_allocate()) == 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;

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

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

  p11_session_free(session);

 fail:
  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_free_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};
  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);

  /*
   * Figure out which PIN we're checking.
   * We don't (yet?) support CKU_CONTEXT_SPECIFIC.
   *
   * We don't currently support re-login without an intervening
   * logout, so reject the login attempt if we're already logged in.
   *
   * 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:
    switch (logged_in_as) {
    case not_logged_in:         break;
    case logged_in_as_user:     lose(CKR_USER_ALREADY_LOGGED_IN);
    case logged_in_as_so:       lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
    }
    user = HAL_USER_NORMAL;
    break;
  case CKU_SO:
    switch (logged_in_as) {
    case not_logged_in:         break;
    case logged_in_as_so:       lose(CKR_USER_ALREADY_LOGGED_IN);
    case logged_in_as_user:     lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
    }
    for (p11_session_t *session = p11_session_iterate(NULL);
         session != NULL; session = p11_session_iterate(session))
      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 (p11_session_t *session = p11_session_iterate(NULL);
       session != NULL; session = p11_session_iterate(session)) {
    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 = NULL;
  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 ((session = p11_session_find(hSession)) == NULL)
    lose(CKR_SESSION_HANDLE_INVALID);

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

  /*
   * Delete any private session objects, clear handles for all private
   * objects, and whack every existing session into the right state.
   */

  {
    assert(p11_session_consistent_login());

    const hal_pkey_attribute_t attrs[] = {
      {.type = CKA_PRIVATE, .value = &const_CK_TRUE, .length = sizeof(const_CK_TRUE)}
    };

    hal_uuid_t uuids[64];
    unsigned n, state;

    for (p11_session_t *session = p11_session_iterate(NULL);
         session != NULL; session = p11_session_iterate(session)) {

      memset(uuids, 0, sizeof(uuids));
      state = 0;
      do {

        rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session),
                                                   p11_session_hal_session(session),
                                                   HAL_KEY_TYPE_NONE, HAL_CURVE_NONE,
                                                   HAL_KEY_FLAG_TOKEN, 0,
                                                   attrs, sizeof(attrs)/sizeof(*attrs), &state,
                                                   uuids, &n, sizeof(uuids)/sizeof(*uuids),
                                                   &uuids[sizeof(uuids)/sizeof(*uuids) - 1]));
        if (rv != CKR_OK)
          goto fail;

        for (int i = 0; i < n; i++) {
          p11_object_free(p11_object_by_uuid(&uuids[i]));
          hal_pkey_handle_t pkey;
          rv = p11_whine_from_hal(hal_rpc_pkey_open(p11_session_hal_client(session),
                                                    p11_session_hal_session(session),
                                                    &pkey, &uuids[i]));
          if (rv != CKR_OK)
            goto fail;
          if ((rv = p11_whine_from_hal(hal_rpc_pkey_delete(pkey))) != CKR_OK) {
            (void) hal_rpc_pkey_close(pkey);
            goto fail;
          }
        }

      } while (n == sizeof(uuids)/sizeof(*uuids));
    }

    memset(uuids, 0, sizeof(uuids));
    state = 0;
    do {

      rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session),
                                                 p11_session_hal_session(session),
                                                 HAL_KEY_TYPE_NONE, HAL_CURVE_NONE,
                                                 HAL_KEY_FLAG_TOKEN, HAL_KEY_FLAG_TOKEN,
                                                 attrs, sizeof(attrs)/sizeof(*attrs), &state,
                                                 uuids, &n, sizeof(uuids)/sizeof(*uuids),
                                                 &uuids[sizeof(uuids)/sizeof(*uuids) - 1]));
      if (rv != CKR_OK)
        goto fail;

      for (int i = 0; i < n; i++)
        p11_object_free(p11_object_by_uuid(&uuids[i]));

    } while (n == sizeof(uuids)/sizeof(*uuids));

    for (p11_session_t *session = p11_session_iterate(NULL);
         session != NULL; session = p11_session_iterate(session)) {
      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;

      }
    }

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

    logged_in_as = not_logged_in;

    assert(p11_session_consistent_login());
  }

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

  p11_session_t *session;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

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

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

  const CK_OBJECT_CLASS * const cka_class       = p11_attribute_find_value_in_template(CKA_CLASS,       pTemplate, ulCount);
  const CK_KEY_TYPE     * const cka_key_type    = p11_attribute_find_value_in_template(CKA_KEY_TYPE,    pTemplate, ulCount);
  const CK_BBOOL        * const cka_token       = p11_attribute_find_value_in_template(CKA_TOKEN,       pTemplate, ulCount);
  const CK_BBOOL        * const cka_private     = p11_attribute_find_value_in_template(CKA_PRIVATE,     pTemplate, ulCount);
  const CK_BBOOL        * const cka_extractable = p11_attribute_find_value_in_template(CKA_EXTRACTABLE, 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 = p11_object_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);
  }

  hal_key_flags_t flags = flavor == handle_flavor_token_object ? HAL_KEY_FLAG_TOKEN : 0;

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

  if (cka_private != NULL && ! *cka_private)
    flags |= HAL_KEY_FLAG_PUBLIC;

  if (*cka_class == CKO_PUBLIC_KEY || (cka_extractable != NULL && *cka_extractable))
    flags |= HAL_KEY_FLAG_EXPORTABLE;

  int (*handler)(const p11_session_t *session,
                 const handle_flavor_t flavor,
                 const CK_ATTRIBUTE_PTR pTemplate,
                 const CK_ULONG ulCount,
                 const p11_descriptor_t * const descriptor,
                 CK_OBJECT_HANDLE_PTR phObject,
                 const hal_key_flags_t flags) = NULL;

  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA)
    handler = p11_object_create_rsa_public_key;

  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC)
    handler = p11_object_create_ec_public_key;

  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA)
    handler = p11_object_create_rsa_private_key;

  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC)
    handler = p11_object_create_ec_private_key;

  if (handler == NULL)
    lose(CKR_FUNCTION_FAILED);

  if (!handler(session, flavor, pTemplate, ulCount, descriptor, phObject, flags))
    lose(CKR_FUNCTION_FAILED);

  return mutex_unlock(p11_global_mutex);

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

  uint8_t attributes_buffer[2 * sizeof(CK_BBOOL)];
  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  hal_pkey_attribute_t attributes[] = {
    [0].type = CKA_PRIVATE,
    [1].type = CKA_TOKEN
  };
  CK_BBOOL cka_private;
  CK_BBOOL cka_token;
  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

  session = p11_session_find(hSession);

  if (!p11_object_pkey_open(session, hObject, &pkey))
    lose(CKR_FUNCTION_FAILED);

  if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes),
                                             attributes_buffer, sizeof(attributes_buffer))))
    lose(CKR_KEY_HANDLE_INVALID);

  cka_private  = *(CK_BBOOL*) attributes[0].value;
  cka_token    = *(CK_BBOOL*) attributes[1].value;

  if ((rv = p11_check_write_access(session, cka_private, cka_token)) != CKR_OK)
    goto fail;

  if (!hal_check(hal_rpc_pkey_delete(pkey)))
    lose(CKR_FUNCTION_FAILED);

  p11_object_free(p11_object_by_handle(hObject));

 fail:
  if (pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_close(pkey);
  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);

  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  const p11_descriptor_t *descriptor = NULL;
  CK_BBOOL cka_extractable, cka_sensitive;
  CK_OBJECT_CLASS cka_class;
  CK_KEY_TYPE cka_key_type;
  CK_BBOOL cka_private;
  CK_BBOOL cka_token;
  int sensitive_object = 0;
  p11_session_t *session;
  CK_RV rv;

  mutex_lock_or_return_failure(p11_global_mutex);

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

  session = p11_session_find(hSession);

  if (!p11_object_pkey_open(session, hObject, &pkey))
    lose(CKR_OBJECT_HANDLE_INVALID);

  {
    hal_pkey_attribute_t attributes[] = {
      [0].type = CKA_CLASS,
      [1].type = CKA_PRIVATE,
      [2].type = CKA_TOKEN,
      [3].type = CKA_KEY_TYPE
    };
    uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + 2 * sizeof(CK_BBOOL) + sizeof(CK_KEY_TYPE)];

    if (!hal_check(hal_rpc_pkey_get_attributes(pkey,
                                               attributes, sizeof(attributes)/sizeof(*attributes),
                                               attributes_buffer, sizeof(attributes_buffer))))
      lose(CKR_OBJECT_HANDLE_INVALID);

    cka_class    = *(CK_OBJECT_CLASS*) attributes[0].value;
    cka_private  = *(CK_BBOOL*)        attributes[1].value;
    cka_token    = *(CK_BBOOL*)        attributes[2].value;
    cka_key_type = *(CK_KEY_TYPE*)     attributes[3].value;

    if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK)
      goto fail;

    descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type);
  }

  if (cka_class == CKO_PRIVATE_KEY || cka_class == CKO_SECRET_KEY) {
    hal_pkey_attribute_t attributes[] = {
      [0].type = CKA_EXTRACTABLE,
      [1].type = CKA_SENSITIVE
    };
    uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE)];

    if (!hal_check(hal_rpc_pkey_get_attributes(pkey,
                                               attributes, sizeof(attributes)/sizeof(*attributes),
                                               attributes_buffer, sizeof(attributes_buffer))))
      lose(CKR_OBJECT_HANDLE_INVALID);

    cka_extractable = *(CK_BBOOL*) attributes[0].value;
    cka_sensitive   = *(CK_BBOOL*) attributes[1].value;

    sensitive_object = cka_sensitive || !cka_extractable;
  }

  {
    hal_pkey_attribute_t attributes[ulCount];

    memset(attributes, 0, sizeof(attributes));

    for (int i = 0; i < ulCount; i++)
      attributes[i].type = pTemplate[i].type;

    if (!hal_check(hal_rpc_pkey_get_attributes(pkey,
                                               attributes, sizeof(attributes)/sizeof(*attributes),
                                               NULL, 0)))
      lose(CKR_OBJECT_HANDLE_INVALID);

    rv = CKR_OK;

    size_t attributes_buffer_len = 0;

    for (int i = 0; i < ulCount; i++) {
      if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) {
        pTemplate[i].ulValueLen = -1;
        rv = CKR_ATTRIBUTE_SENSITIVE;
        continue;
      }
      if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) {
        pTemplate[i].ulValueLen = -1;
        rv = CKR_ATTRIBUTE_TYPE_INVALID;
        continue;
      }
      if (pTemplate[i].pValue == NULL) {
        pTemplate[i].ulValueLen = attributes[i].length;
        continue;
      }
      if (pTemplate[i].ulValueLen < attributes[i].length) {
        pTemplate[i].ulValueLen = -1;
        rv = CKR_BUFFER_TOO_SMALL;
        continue;
      }
      attributes_buffer_len += attributes[i].length;
    }

    if (attributes_buffer_len == 0)
      goto fail;

    uint8_t attributes_buffer[attributes_buffer_len];
    unsigned n = 0;

    for (int i = 0; i < ulCount; i++)
      if (pTemplate[i].pValue != NULL && pTemplate[i].ulValueLen != -1)
        attributes[n++].type = pTemplate[i].type;

    if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, n,
                                               attributes_buffer, sizeof(attributes_buffer))))
      lose(CKR_OBJECT_HANDLE_INVALID);

    for (int i = 0; i < n; i++) {
      int j = p11_attribute_find_in_template(attributes[i].type, pTemplate, ulCount);

      if (j < 0 || pTemplate[j].ulValueLen == -1 || pTemplate[j].ulValueLen < attributes[i].length)
        lose(CKR_FUNCTION_FAILED);

      memcpy(pTemplate[j].pValue, attributes[i].value, attributes[i].length);
      pTemplate[j].ulValueLen = attributes[i].length;
    }
  }

 fail:
  if (pkey.handle != HAL_HANDLE_NONE) {
    if (rv == CKR_OK)
      rv = p11_whine_from_hal(hal_rpc_pkey_close(pkey));
    else
      (void) hal_rpc_pkey_close(pkey);
  }
  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);

  const size_t attributes_len = sizeof(hal_pkey_attribute_t) * (ulCount + 1);
  size_t len = attributes_len;
  CK_BBOOL *cka_private = NULL;
  CK_BBOOL *cka_token = NULL;
  p11_session_t *session;
  CK_RV rv = CKR_OK;
  uint8_t *mem;

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

  assert(!session->find_query_token && !session->find_query_session && !session->find_query_state);

  for (int i = 0; i < ulCount; i++) {
    if (pTemplate[i].pValue == NULL || pTemplate[i].ulValueLen == 0)
      lose(CKR_ARGUMENTS_BAD);
    len += pTemplate[i].ulValueLen;
  }

  if ((mem = malloc(len)) == NULL)
    lose(CKR_HOST_MEMORY);

  session->find_query = (hal_pkey_attribute_t *) mem;
  mem += attributes_len;

  for (int i = 0; i < ulCount; i++) {
    len = pTemplate[i].ulValueLen;
    session->find_query[i].type   = pTemplate[i].type;
    session->find_query[i].value  = mem;
    session->find_query[i].length = len;
    memcpy(mem, pTemplate[i].pValue, len);
    mem += len;
  }

  cka_private = p11_attribute_find_value_in_template(CKA_PRIVATE, pTemplate, ulCount);
  cka_token   = p11_attribute_find_value_in_template(CKA_TOKEN,   pTemplate, ulCount);

  session->find_query_n       = ulCount;
  session->find_query_token   = cka_token == NULL ||  *cka_token;
  session->find_query_session = cka_token == NULL || !*cka_token;
  session->find_query_state   = 0;
  memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid));

  /*
   * Quietly enforce object privacy even if template tries to bypass,
   * per PCKS #11 specification.
   */

  if (logged_in_as != logged_in_as_user && cka_private == NULL) {
    session->find_query[ulCount].type   = CKA_PRIVATE;
    session->find_query[ulCount].value  = &const_CK_FALSE;
    session->find_query[ulCount].length = sizeof(const_CK_FALSE);
    session->find_query_n++;
  }

  if (logged_in_as != logged_in_as_user && cka_private != NULL && *cka_private) {
    int i = p11_attribute_find_in_template(CKA_PRIVATE, pTemplate, ulCount);
    assert(i >= 0 && i < ulCount);
    session->find_query[i].value  = &const_CK_FALSE;
    session->find_query[i].length = sizeof(const_CK_FALSE);
  }

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

  *pulObjectCount = 0;

  while (*pulObjectCount < ulMaxObjectCount &&
         (session->find_query_token || session->find_query_session)) {
    hal_uuid_t uuids[ulMaxObjectCount - *pulObjectCount];
    handle_flavor_t flavor;
    hal_key_flags_t flags;
    unsigned n;

    if (session->find_query_token) {
      flavor = handle_flavor_token_object;
      flags  = HAL_KEY_FLAG_TOKEN;
    }
    else {
      flavor = handle_flavor_session_object;
      flags  = 0;
    }

    rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session),
                                               p11_session_hal_session(session),
                                               HAL_KEY_TYPE_NONE, HAL_CURVE_NONE,
                                               HAL_KEY_FLAG_TOKEN, flags,
                                               session->find_query, session->find_query_n,
                                               &session->find_query_state,
                                               uuids, &n, sizeof(uuids)/sizeof(*uuids),
                                               &session->find_query_previous_uuid));
      if (rv != CKR_OK)
        goto fail;

      for (int i = 0; i < n; i++) {
        phObject[*pulObjectCount] = p11_object_allocate(flavor, &uuids[i], session);
        if (phObject[*pulObjectCount] == CK_INVALID_HANDLE)
          lose(CKR_FUNCTION_FAILED);
        ++*pulObjectCount;
      }

      if (n == sizeof(uuids)/sizeof(*uuids)) {
        memcpy(&session->find_query_previous_uuid, &uuids[n - 1],
               sizeof(session->find_query_previous_uuid));
      }

      else {
        memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid));
        session->find_query_state = 0;

        if (session->find_query_token)
          session->find_query_token = 0;
        else
          session->find_query_session = 0;
      }
  }

 fail:
  mutex_unlock_return_with_rv(rv, p11_global_mutex);
}

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

  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 (session->find_query == NULL)
    lose(CKR_OPERATION_NOT_INITIALIZED);

  free(session->find_query);

  session->find_query = NULL;
  session->find_query_n = 0;
  session->find_query_token = 0;
  session->find_query_session = 0;
  session->find_query_state = 0;
  memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid));

 fail:
  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_SHA224:      algorithm = HAL_DIGEST_ALGORITHM_SHA224; 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 = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulDigestLen = digest_len;

  if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL)
    mutex_unlock_return_with_rv(rv, p11_global_mutex);

  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 = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;

  *pulDigestLen = digest_len;

  if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL)
    mutex_unlock_return_with_rv(rv, p11_global_mutex);

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

  uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE) + 3 * sizeof(CK_BBOOL)];
  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  hal_pkey_attribute_t attributes[] = {
    [0].type = CKA_KEY_TYPE,
    [1].type = CKA_SIGN,
    [2].type = CKA_PRIVATE,
    [3].type = CKA_TOKEN
  };
  CK_KEY_TYPE cka_key_type;
  CK_BBOOL cka_sign;
  CK_BBOOL cka_private;
  CK_BBOOL cka_token;
  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

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

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

  if (session->sign_key_handle != CK_INVALID_HANDLE ||
      session->sign_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE)
    lose(CKR_OPERATION_ACTIVE);

  if (!p11_object_pkey_open(session, hKey, &pkey))
    lose(CKR_KEY_HANDLE_INVALID);

  if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes),
                                             attributes_buffer, sizeof(attributes_buffer))))
    lose(CKR_KEY_HANDLE_INVALID);

  cka_key_type = *(CK_KEY_TYPE*) attributes[0].value;
  cka_sign     = *(CK_BBOOL*)    attributes[1].value;
  cka_private  = *(CK_BBOOL*)    attributes[2].value;
  cka_token    = *(CK_BBOOL*)    attributes[3].value;

  if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK)
    goto fail;

  if (!cka_sign)
    lose(CKR_KEY_FUNCTION_NOT_PERMITTED);

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA224_RSA_PKCS:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA384_RSA_PKCS:
  case CKM_SHA512_RSA_PKCS:
    if (cka_key_type != CKK_RSA)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  case CKM_ECDSA:
  case CKM_ECDSA_SHA224:
  case CKM_ECDSA_SHA256:
  case CKM_ECDSA_SHA384:
  case CKM_ECDSA_SHA512:
    if (cka_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_SHA224_RSA_PKCS:
  case CKM_ECDSA_SHA224:
    session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224;
    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;
  }

  rv = CKR_OK;

 fail:
  if (pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_close(pkey);
  if (rv != CKR_OK && 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_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 (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;
  }

  rv = sign_hal_rpc(session, pData, ulDataLen, pSignature, pulSignatureLen);

                                /* Fall through */
 fail:
  if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) {
    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_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);

  rv = sign_hal_rpc(session, NULL, 0, pSignature, pulSignatureLen);

                                /* Fall through */
 fail:
  if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) {
    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);

  uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE) + 3 * sizeof(CK_BBOOL)];
  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
  hal_pkey_attribute_t attributes[] = {
    [0].type = CKA_KEY_TYPE,
    [1].type = CKA_VERIFY,
    [2].type = CKA_PRIVATE,
    [3].type = CKA_TOKEN
  };
  CK_KEY_TYPE cka_key_type;
  CK_BBOOL cka_verify;
  CK_BBOOL cka_private;
  CK_BBOOL cka_token;

  p11_session_t *session;
  CK_RV rv = CKR_OK;

  mutex_lock_or_return_failure(p11_global_mutex);

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

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

  if (session->verify_key_handle != CK_INVALID_HANDLE ||
      session->verify_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE)
    lose(CKR_OPERATION_ACTIVE);

  if (!p11_object_pkey_open(session, hKey, &pkey))
    lose(CKR_KEY_HANDLE_INVALID);

  if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes),
                                             attributes_buffer, sizeof(attributes_buffer))))
    lose(CKR_KEY_HANDLE_INVALID);

  cka_key_type = *(CK_KEY_TYPE*) attributes[0].value;
  cka_verify   = *(CK_BBOOL*)    attributes[1].value;
  cka_private  = *(CK_BBOOL*)    attributes[2].value;
  cka_token    = *(CK_BBOOL*)    attributes[3].value;

  if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK)
    goto fail;

  if (!cka_verify)
    lose(CKR_KEY_FUNCTION_NOT_PERMITTED);

  switch (pMechanism->mechanism) {
  case CKM_RSA_PKCS:
  case CKM_SHA1_RSA_PKCS:
  case CKM_SHA224_RSA_PKCS:
  case CKM_SHA256_RSA_PKCS:
  case CKM_SHA384_RSA_PKCS:
  case CKM_SHA512_RSA_PKCS:
    if (cka_key_type != CKK_RSA)
      lose(CKR_KEY_TYPE_INCONSISTENT);
    break;
  case CKM_ECDSA:
  case CKM_ECDSA_SHA224:
  case CKM_ECDSA_SHA256:
  case CKM_ECDSA_SHA384:
  case CKM_ECDSA_SHA512:
    if (cka_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_SHA224_RSA_PKCS:
  case CKM_ECDSA_SHA224:
    session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224;
    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;
  }

  rv = CKR_OK;

 fail:
  if (pkey.handle != HAL_HANDLE_NONE)
    (void) hal_rpc_pkey_close(pkey);
  if (rv != CKR_OK && 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_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;
  }

  rv = verify_hal_rpc(session, pData, ulDataLen, pSignature, ulSignatureLen);

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

  rv = verify_hal_rpc(session, NULL, 0, pSignature, ulSignatureLen);

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

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 (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  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_SHA224_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_SHA224:
  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_SHA224:
  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_SHA224_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);
}

CK_RV C_GetInfo(CK_INFO_PTR pInfo)
{
  ENTER_PUBLIC_FUNCTION(C_GetInfo);

  if (pInfo == NULL)
    return CKR_ARGUMENTS_BAD;

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  memset(pInfo, 0, sizeof(*pInfo));
  pInfo->cryptokiVersion.major = 2;
  pInfo->cryptokiVersion.minor = 30;
  psnprintf(pInfo->manufacturerID,     sizeof(pInfo->manufacturerID),     P11_MANUFACTURER_ID);
  psnprintf(pInfo->libraryDescription, sizeof(pInfo->libraryDescription), P11_LIBRARY_DESCRIPTION);
  pInfo->libraryVersion.major = P11_VERSION_SW_MAJOR;
  pInfo->libraryVersion.minor = P11_VERSION_SW_MINOR;

  return CKR_OK;
}

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

  if (pInfo == NULL)
    return CKR_ARGUMENTS_BAD;

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    return CKR_SLOT_ID_INVALID;

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  memset(pInfo, 0, sizeof(*pInfo));
  psnprintf(pInfo->slotDescription, sizeof(pInfo->slotDescription), P11_SLOT_DESCRIPTION);
  psnprintf(pInfo->manufacturerID,  sizeof(pInfo->manufacturerID),  P11_MANUFACTURER_ID);
  pInfo->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT;
  pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR;
  pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR;
  pInfo->firmwareVersion.major = P11_VERSION_FW_MAJOR;
  pInfo->firmwareVersion.minor = P11_VERSION_FW_MINOR;
  return CKR_OK;
}

CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
                         CK_MECHANISM_TYPE_PTR pMechanismList,
                         CK_ULONG_PTR pulCount)
{
  static const CK_MECHANISM_TYPE mechanisms[] = {
                       CKM_ECDSA_SHA224,       CKM_ECDSA_SHA256,    CKM_ECDSA_SHA384,    CKM_ECDSA_SHA512,    CKM_ECDSA,    CKM_EC_KEY_PAIR_GEN,
    CKM_SHA1_RSA_PKCS, CKM_SHA224_RSA_PKCS,    CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_PKCS_KEY_PAIR_GEN,
    CKM_SHA_1,         CKM_SHA224, 	       CKM_SHA256,          CKM_SHA384,          CKM_SHA512,
#if 0
    /* libhal support these but pkcs11 doesn't, yet */
    CKM_SHA_1_HMAC,    CKM_SHA224_HMAC,        CKM_SHA256_HMAC,     CKM_SHA384_HMAC,     CKM_SHA512_HMAC,
#endif
  };
  const CK_ULONG mechanisms_len = sizeof(mechanisms)/sizeof(*mechanisms);

  ENTER_PUBLIC_FUNCTION(C_GetMechanismList);

  if (pulCount == NULL)
    return CKR_ARGUMENTS_BAD;

  if (slotID != P11_ONE_AND_ONLY_SLOT)
    return CKR_SLOT_ID_INVALID;

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  CK_RV rv = CKR_OK;

  if (pMechanismList != NULL && *pulCount < mechanisms_len)
    rv = CKR_BUFFER_TOO_SMALL;

  else if (pMechanismList != NULL)
    memcpy(pMechanismList, mechanisms, sizeof(mechanisms));

  *pulCount = mechanisms_len;

  return rv;
}

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

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  return CKR_RANDOM_SEED_NOT_SUPPORTED;
}



/*
 * Legacy functions.  These are basically just unimplemented functions
 * which return a different error code to keep test suites happy.
 */

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

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  return CKR_FUNCTION_NOT_PARALLEL;
}

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

  if (p11_uninitialized())
    return CKR_CRYPTOKI_NOT_INITIALIZED;

  return CKR_FUNCTION_NOT_PARALLEL;
}



/*
 * 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)
{
  UNSUPPORTED_FUNCTION(C_GenerateKey);
}

CK_RV C_InitToken(CK_SLOT_ID slotID,
                  CK_UTF8CHAR_PTR pPin,
                  CK_ULONG ulPinLen,
                  CK_UTF8CHAR_PTR pLabel)
{
  UNSUPPORTED_FUNCTION(C_InitToken);
}

CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
                CK_UTF8CHAR_PTR pPin,
                CK_ULONG ulPinLen)
{
  UNSUPPORTED_FUNCTION(C_InitPIN);
}

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

CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
                          CK_BYTE_PTR pOperationState,
                          CK_ULONG_PTR pulOperationStateLen)
{
  UNSUPPORTED_FUNCTION(C_GetOperationState);
}

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

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

CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
                      CK_OBJECT_HANDLE hObject,
                      CK_ULONG_PTR pulSize)
{
  UNSUPPORTED_FUNCTION(C_GetObjectSize);
}

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

CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism,
                    CK_OBJECT_HANDLE hKey)
{
  UNSUPPORTED_FUNCTION(C_EncryptInit);
}

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

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

CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pLastEncryptedPart,
                     CK_ULONG_PTR pulLastEncryptedPartLen)
{
  UNSUPPORTED_FUNCTION(C_EncryptFinal);
}

CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
                    CK_MECHANISM_PTR pMechanism,
                    CK_OBJECT_HANDLE hKey)
{
  UNSUPPORTED_FUNCTION(C_DecryptInit);
}

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

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

CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
                     CK_BYTE_PTR pLastPart,
                     CK_ULONG_PTR pulLastPartLen)
{
  UNSUPPORTED_FUNCTION(C_DecryptFinal);
}

CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
                  CK_OBJECT_HANDLE hKey)
{
  UNSUPPORTED_FUNCTION(C_DigestKey);
}

CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
                        CK_MECHANISM_PTR pMechanism,
                        CK_OBJECT_HANDLE hKey)
{
  UNSUPPORTED_FUNCTION(C_SignRecoverInit);
}

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

CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
                          CK_MECHANISM_PTR pMechanism,
                          CK_OBJECT_HANDLE hKey)
{
  UNSUPPORTED_FUNCTION(C_VerifyRecoverInit);
}

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

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

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

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

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

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)
{
  UNSUPPORTED_FUNCTION(C_WrapKey);
}

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)
{
  UNSUPPORTED_FUNCTION(C_UnwrapKey);
}

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)
{
  UNSUPPORTED_FUNCTION(C_DeriveKey);
}

CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
                         CK_SLOT_ID_PTR pSlot,
                         CK_VOID_PTR pRserved)
{
  UNSUPPORTED_FUNCTION(C_WaitForSlotEvent);
}

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