@@ -26,6 +26,7 @@ use cryptoki::object::ObjectClass;
2626use cryptoki:: object:: ObjectHandle ;
2727use cryptoki:: session:: Session ;
2828use cryptoki:: session:: UserType ;
29+ use cryptoki:: slot:: SlotInfo ;
2930use cryptoki:: slot:: TokenInfo ;
3031use rsa:: pkcs1:: EncodeRsaPublicKey ;
3132use rustls:: sign:: Signer ;
@@ -35,6 +36,7 @@ use rustls::SignatureScheme;
3536use serde:: Deserialize ;
3637use serde:: Serialize ;
3738use tracing:: debug;
39+ use tracing:: error;
3840use tracing:: trace;
3941use tracing:: warn;
4042
@@ -181,8 +183,8 @@ impl Cryptoki {
181183 self . open_session ( uri, CryptokiSessionType :: ReadOnly )
182184 }
183185
184- fn open_session_rw < ' a > ( & ' a self , uri : Option < & ' a str > ) -> anyhow:: Result < CryptokiSession < ' a > > {
185- self . open_session ( uri, CryptokiSessionType :: ReadWrite )
186+ fn _open_session_rw < ' a > ( & ' a self , uri : Option < & ' a str > ) -> anyhow:: Result < CryptokiSession < ' a > > {
187+ self . open_session ( uri, CryptokiSessionType :: _ReadWrite )
186188 }
187189
188190 fn open_session < ' a > (
@@ -203,6 +205,9 @@ impl Cryptoki {
203205 Err ( e) => e. into_inner ( ) ,
204206 } ;
205207
208+ debug ! ( slots = ?get_all_slots_info( & context) ) ;
209+ debug ! ( tokens = ?get_all_token_info( & context) ) ;
210+
206211 let slots_with_tokens = context. get_slots_with_token ( ) ?;
207212 let tokens: Result < Vec < _ > , _ > = slots_with_tokens
208213 . iter ( )
@@ -232,18 +237,40 @@ impl Cryptoki {
232237
233238 let session = match session_type {
234239 CryptokiSessionType :: ReadOnly => context. open_ro_session ( slot) ?,
235- CryptokiSessionType :: ReadWrite => context. open_rw_session ( slot) ?,
240+ CryptokiSessionType :: _ReadWrite => context. open_rw_session ( slot) ?,
236241 } ;
237242
238243 session. login ( UserType :: User , Some ( & self . config . pin ) ) ?;
239244 let session_info = session. get_session_info ( ) ?;
240245 debug ! ( ?session_info, "Opened a readonly session" ) ;
241246
242- Ok ( CryptokiSession {
247+ let session = CryptokiSession {
243248 session,
244249 token_info,
245250 uri_attributes,
246- } )
251+ } ;
252+
253+ let template = [ ] ;
254+ let objects = session. session . find_objects ( & template) ;
255+ match objects {
256+ Err ( err) => {
257+ error ! ( ?template, ?err, "failed to find objects" ) ;
258+ }
259+ Ok ( objects) => {
260+ let objects = objects
261+ . into_iter ( )
262+ . flat_map ( |o| {
263+ let uri = session. export_object_uri ( o) . inspect_err (
264+ |err| error ! ( ?err, object = ?o, "failed to read properties of object" ) ,
265+ ) ;
266+ uri. map ( |u| ( o, u) ) . ok ( )
267+ } )
268+ . collect :: < Vec < _ > > ( ) ;
269+ trace ! ( ?objects, "Objects found in the token" ) ;
270+ }
271+ }
272+
273+ Ok ( session)
247274 }
248275
249276 fn request_uri < ' a > (
@@ -268,6 +295,45 @@ impl Cryptoki {
268295 }
269296}
270297
298+ fn get_all_slots_info ( cryptoki : & Pkcs11 ) -> Vec < SlotInfo > {
299+ let slots = match cryptoki. get_all_slots ( ) {
300+ Ok ( slots) => slots,
301+ Err ( err) => {
302+ error ! ( ?err, "failed to get slots" ) ;
303+ return vec ! [ ] ;
304+ }
305+ } ;
306+ slots
307+ . into_iter ( )
308+ . flat_map ( |s| {
309+ cryptoki
310+ . get_slot_info ( s)
311+ . inspect_err ( |err| error ! ( slot = ?s, ?err, "failed to read slot info from slot" ) )
312+ . ok ( )
313+ } )
314+ . collect :: < Vec < _ > > ( )
315+ }
316+
317+ fn get_all_token_info ( cryptoki : & Pkcs11 ) -> Vec < TokenInfo > {
318+ let slots = match cryptoki. get_slots_with_token ( ) {
319+ Ok ( slots) => slots,
320+ Err ( err) => {
321+ error ! ( ?err, "failed to get slots" ) ;
322+ return vec ! [ ] ;
323+ }
324+ } ;
325+
326+ slots
327+ . into_iter ( )
328+ . flat_map ( |s| {
329+ cryptoki
330+ . get_token_info ( s)
331+ . inspect_err ( |err| error ! ( slot = ?s, ?err, "failed to read token info from slot" ) )
332+ . ok ( )
333+ } )
334+ . collect :: < Vec < _ > > ( )
335+ }
336+
271337/// A cryptoki session opened with a token.
272338struct CryptokiSession < ' a > {
273339 session : Session ,
@@ -278,7 +344,7 @@ struct CryptokiSession<'a> {
278344#[ derive( Debug , Clone , Copy ) ]
279345enum CryptokiSessionType {
280346 ReadOnly ,
281- ReadWrite ,
347+ _ReadWrite ,
282348}
283349
284350impl CryptokiSession < ' _ > {
@@ -442,6 +508,41 @@ impl CryptokiSession<'_> {
442508
443509 Ok ( pubkey_pem)
444510 }
511+
512+ fn export_object_uri ( & self , object : ObjectHandle ) -> anyhow:: Result < String > {
513+ let template = & [ AttributeType :: Id , AttributeType :: Label ] ;
514+ let attrs = self . session . get_attributes ( object, template) ?. into_iter ( ) ;
515+
516+ let mut key_uri = export_session_uri ( & self . token_info ) ;
517+
518+ for attr in attrs {
519+ match attr {
520+ Attribute :: Id ( id) => {
521+ key_uri. push ( ';' ) ;
522+ key_uri. push_str ( "id=" ) ;
523+ // from RFC section 2.3: Note that the value of the "id" attribute SHOULD NOT be encoded as UTF-8 because it can
524+ // contain non-textual data, instead it SHOULD be entirely percent-encoded
525+ for byte in & id {
526+ key_uri. push_str ( percent_encoding:: percent_encode_byte ( * byte) ) ;
527+ }
528+ }
529+ Attribute :: Label ( label) => {
530+ let label = std:: str:: from_utf8 ( & label) . context ( "label should be utf-8" ) ?;
531+ key_uri. push ( ';' ) ;
532+ key_uri. push_str ( "object=" ) ;
533+ let label = uri:: percent_encode ( label) ;
534+ key_uri. push_str ( & label) ;
535+ }
536+ other => warn ! ( asked = ?template, got= ?other, "Got invalid attribute" ) ,
537+ }
538+ }
539+
540+ // omit the "type" attribute since its not relevant when used as device.key_uri, which is intended use for this produced value
541+
542+ anyhow:: ensure!( key_uri. starts_with( "pkcs11:" ) ) ;
543+
544+ Ok ( key_uri)
545+ }
445546}
446547
447548#[ derive( Debug ) ]
@@ -687,6 +788,36 @@ fn get_ec_mechanism(session: &Session, key: ObjectHandle) -> anyhow::Result<SigS
687788 }
688789}
689790
791+ /// Generates PKCS11 URI of the selected token.
792+ ///
793+ /// The generated URI attempts to be similar to URIs generated by gnutls. Notably, "slot-description", "slot-id", and
794+ /// "slot-manufacturer" attributes are missing from gnutls URIs, perhaps for portability (URI points to the same thing
795+ /// even if token is reinserted into a different slot).
796+ fn export_session_uri ( token_info : & TokenInfo ) -> String {
797+ let mut uri = String :: from ( "pkcs11:" ) ;
798+
799+ uri. push_str ( "model=" ) ;
800+ let model = uri:: percent_encode ( token_info. model ( ) ) ;
801+ uri. push_str ( & model) ;
802+
803+ uri. push ( ';' ) ;
804+ uri. push_str ( "manufacturer=" ) ;
805+ let manufacturer = uri:: percent_encode ( token_info. manufacturer_id ( ) ) ;
806+ uri. push_str ( & manufacturer) ;
807+
808+ uri. push ( ';' ) ;
809+ uri. push_str ( "serial=" ) ;
810+ let serial = uri:: percent_encode ( token_info. serial_number ( ) ) ;
811+ uri. push_str ( & serial) ;
812+
813+ uri. push ( ';' ) ;
814+ uri. push_str ( "token=" ) ;
815+ let token = uri:: percent_encode ( token_info. label ( ) ) ;
816+ uri. push_str ( & token) ;
817+
818+ uri
819+ }
820+
690821#[ cfg( test) ]
691822mod tests {
692823 use super :: * ;
0 commit comments