auxiliaries are now in cryptaux.hxx; some get methods for openssl::X509
This commit is contained in:
		| @@ -39,7 +39,7 @@ int main(int argc, char const*const*const argv) try { | |||||||
|                <<"label: \""<<info.label<<'"'<<std::endl |                <<"label: \""<<info.label<<'"'<<std::endl | ||||||
|                <<"manufacturerID: \""<<info.manufacturerID<<'"'<<std::endl |                <<"manufacturerID: \""<<info.manufacturerID<<'"'<<std::endl | ||||||
|                <<"model: \""<<info.model<<'"'<<std::endl |                <<"model: \""<<info.model<<'"'<<std::endl | ||||||
|                <<"serialNumber: \""<<cryptoki::readable(info.serialNumber) |                <<"serialNumber: \""<<crypto::readable(info.serialNumber) | ||||||
|                <<'"'<<std::endl |                <<'"'<<std::endl | ||||||
|                <<"flags: \""<<info.flags<<'"'<<std::endl |                <<"flags: \""<<info.flags<<'"'<<std::endl | ||||||
|                <<"maxSessionCount: \""<<info.maxSessionCount<<'"'<<std::endl |                <<"maxSessionCount: \""<<info.maxSessionCount<<'"'<<std::endl | ||||||
| @@ -57,7 +57,7 @@ int main(int argc, char const*const*const argv) try { | |||||||
|                <<'.'<<(int)info.hardwareVersion.minor<<'"'<<std::endl |                <<'.'<<(int)info.hardwareVersion.minor<<'"'<<std::endl | ||||||
|                <<"firmwareVersion: \""<<(int)info.firmwareVersion.major |                <<"firmwareVersion: \""<<(int)info.firmwareVersion.major | ||||||
|                <<'.'<<(int)info.firmwareVersion.minor<<'"'<<std::endl |                <<'.'<<(int)info.firmwareVersion.minor<<'"'<<std::endl | ||||||
|                <<"utcTime: \""<<cryptoki::readable(info.utcTime) |                <<"utcTime: \""<<crypto::readable(info.utcTime) | ||||||
|                <<'"'<<std::endl; |                <<'"'<<std::endl; | ||||||
|       cryptoki::MechanismList mechs(it->mechanismlist()); |       cryptoki::MechanismList mechs(it->mechanismlist()); | ||||||
|       for (cryptoki::MechanismList::iterator it2(mechs.begin()); |       for (cryptoki::MechanismList::iterator it2(mechs.begin()); | ||||||
| @@ -80,7 +80,7 @@ int main(int argc, char const*const*const argv) try { | |||||||
|         for (cryptoki::AttributeMap::iterator it(attrs.begin()); |         for (cryptoki::AttributeMap::iterator it(attrs.begin()); | ||||||
|              it!=attrs.end(); ++it) { |              it!=attrs.end(); ++it) { | ||||||
|           std::cout<<" - attribute: "<<it->second.name()<<"=\"" |           std::cout<<" - attribute: "<<it->second.name()<<"=\"" | ||||||
|                    <<cryptoki::readable(it->second.value)<<'"'<<std::endl; |                    <<crypto::readable(it->second.value)<<'"'<<std::endl; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       std::cout<<"**** Success"<<std::endl; |       std::cout<<"**** Success"<<std::endl; | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								src/cryptaux.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/cryptaux.hxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | /*! @file | ||||||
|  |  | ||||||
|  |     @id $Id$ | ||||||
|  | */ | ||||||
|  | //       1         2         3         4         5         6         7         8 | ||||||
|  | // 45678901234567890123456789012345678901234567890123456789012345678901234567890 | ||||||
|  |  | ||||||
|  | #ifndef __CRYPTAUX_HXX__ | ||||||
|  | #define __CRYPTAUX_HXX__ | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <sstream> | ||||||
|  | #include <iomanip> | ||||||
|  |  | ||||||
|  | /*! @defgroup gcrypto Auxiliary Crypto-Functions */ | ||||||
|  | //@{ | ||||||
|  |  | ||||||
|  | //! @see gcrypto | ||||||
|  | namespace crypto { | ||||||
|  |    | ||||||
|  |   static const std::string LETTER_CHARS | ||||||
|  |       ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); | ||||||
|  |   static const std::string NUMBER_CHARS | ||||||
|  |       ("0123456789"); | ||||||
|  |   //! Contains @c @ in addition to standard characters. | ||||||
|  |   static const std::string GRAFIC_CHARS | ||||||
|  |       ("!\"#%&'()*+,-./:;<=>?[\\]^_{|}~@"); | ||||||
|  |   static const std::string BLANK_CHARS | ||||||
|  |       (" "); | ||||||
|  |   static const std::string VALID_CHARS | ||||||
|  |       (LETTER_CHARS+NUMBER_CHARS+GRAFIC_CHARS+BLANK_CHARS); | ||||||
|  |    | ||||||
|  |   inline std::string hex(const std::string& data, | ||||||
|  |                          std::string::size_type len=20) { | ||||||
|  |     std::stringstream res; | ||||||
|  |     std::string::size_type pos(0); | ||||||
|  |     for (std::string::const_iterator it(data.begin()); it!=data.end(); ++it) { | ||||||
|  |       res<<std::hex<<std::setfill('0')<<std::setw(2) | ||||||
|  |          <<(unsigned int)(unsigned char)*it; | ||||||
|  |       ++pos; | ||||||
|  |       if (pos%len==0 || pos==data.size()) { | ||||||
|  |         res<<std::string(2*(len-(pos-1)%len), ' '); | ||||||
|  |         for (std::string::size_type i(pos-(pos-1)%len-1); i<pos; ++i) | ||||||
|  |           res<<(VALID_CHARS.find(data[i])==std::string::npos?'.':data[i]); | ||||||
|  |         if (pos!=data.size()) res<<std::endl; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return res.str(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   inline std::string readable(const std::string& data, | ||||||
|  |                               std::string::size_type len=20) { | ||||||
|  |     if (!data.size()) | ||||||
|  |       return "<empty>"; | ||||||
|  |     else if (data.find_first_not_of(VALID_CHARS)<data.size()) | ||||||
|  |       return hex(data); | ||||||
|  |     else | ||||||
|  |       return "\""+data+"\""; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //@} | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -240,15 +240,24 @@ namespace cryptoki { | |||||||
|   } |   } | ||||||
|    |    | ||||||
|   //---------------------------------------------------------------------------- |   //---------------------------------------------------------------------------- | ||||||
|   Object Session::createCertificate(const std::string& derSubject, |   Object Session::create(const openssl::X509& cert) { | ||||||
|                                     const std::string& desValue) { |  | ||||||
|     AttributeList attrs; |     AttributeList attrs; | ||||||
|  |     /* | ||||||
|     attrs.push_back(Attribute(CKA_CLASS) |     attrs.push_back(Attribute(CKA_CLASS) | ||||||
|                     .from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)); |                     .from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)); | ||||||
|     attrs.push_back(Attribute(CKA_CERTIFICATE_TYPE) |     attrs.push_back(Attribute(CKA_CERTIFICATE_TYPE) | ||||||
|                     .from<CK_CERTIFICATE_TYPE>(CKC_X_509)); |                     .from<CK_CERTIFICATE_TYPE>(CKC_X_509)); | ||||||
|     attrs.push_back(Attribute(CKA_SUBJECT, derSubject)); |     attrs.push_back(Attribute(CKA_SUBJECT, derSubject)); | ||||||
|     attrs.push_back(Attribute(CKA_VALUE, desValue)); |     attrs.push_back(Attribute(CKA_VALUE, desValue)); | ||||||
|  |         */ | ||||||
|  |     return create(attrs); | ||||||
|  |   } | ||||||
|  |   Object Session::create(const openssl::PrivateKey& key) { | ||||||
|  |     AttributeList attrs; | ||||||
|  |     return create(attrs); | ||||||
|  |   } | ||||||
|  |   Object Session::create(const openssl::PKCS12& p12) { | ||||||
|  |     AttributeList attrs; | ||||||
|     return create(attrs); |     return create(attrs); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| // 45678901234567890123456789012345678901234567890123456789012345678901234567890 | // 45678901234567890123456789012345678901234567890123456789012345678901234567890 | ||||||
|  |  | ||||||
| // interface | // interface | ||||||
|  | #include <openssl.hxx> | ||||||
| #include <pkcs11/apiclient.h> | #include <pkcs11/apiclient.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| @@ -20,6 +21,7 @@ | |||||||
| #include <cstring> // memset | #include <cstring> // memset | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <cryptaux.hxx> | ||||||
|  |  | ||||||
| #include <iostream> // debug | #include <iostream> // debug | ||||||
|  |  | ||||||
| @@ -46,46 +48,6 @@ namespace cryptoki { | |||||||
|   //! @addtogroup cryptokitypes |   //! @addtogroup cryptokitypes | ||||||
|   //@{ |   //@{ | ||||||
|  |  | ||||||
|   static const std::string LETTER_CHARS |  | ||||||
|       ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); |  | ||||||
|   static const std::string NUMBER_CHARS |  | ||||||
|       ("0123456789"); |  | ||||||
|   //! Contains @c @ in addition to standard characters. |  | ||||||
|   static const std::string GRAFIC_CHARS |  | ||||||
|       ("!\"#%&'()*+,-./:;<=>?[\\]^_{|}~@"); |  | ||||||
|   static const std::string BLANK_CHARS |  | ||||||
|       (" "); |  | ||||||
|   static const std::string VALID_CHARS |  | ||||||
|       (LETTER_CHARS+NUMBER_CHARS+GRAFIC_CHARS+BLANK_CHARS); |  | ||||||
|    |  | ||||||
|   inline std::string hex(const std::string& data, |  | ||||||
|                          std::string::size_type len=20) { |  | ||||||
|     std::stringstream res; |  | ||||||
|     std::string::size_type pos(0); |  | ||||||
|     for (std::string::const_iterator it(data.begin()); it!=data.end(); ++it) { |  | ||||||
|       res<<std::hex<<std::setfill('0')<<std::setw(2) |  | ||||||
|          <<(unsigned int)(unsigned char)*it; |  | ||||||
|       ++pos; |  | ||||||
|       if (pos%len==0 || pos==data.size()) { |  | ||||||
|         res<<std::string(2*(len-(pos-1)%len), ' '); |  | ||||||
|         for (std::string::size_type i(pos-(pos-1)%len-1); i<pos; ++i) |  | ||||||
|           res<<(VALID_CHARS.find(data[i])==std::string::npos?'.':data[i]); |  | ||||||
|         if (pos!=data.size()) res<<std::endl; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return res.str(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   inline std::string readable(const std::string& data, |  | ||||||
|                               std::string::size_type len=20) { |  | ||||||
|     if (!data.size()) |  | ||||||
|       return "<empty>"; |  | ||||||
|     else if (data.find_first_not_of(VALID_CHARS)<data.size()) |  | ||||||
|       return hex(data); |  | ||||||
|     else |  | ||||||
|       return "\""+data+"\""; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   inline std::string string(CK_ULONG num) { |   inline std::string string(CK_ULONG num) { | ||||||
|     switch (num) { |     switch (num) { | ||||||
|       case CK_UNAVAILABLE_INFORMATION: return "-"; |       case CK_UNAVAILABLE_INFORMATION: return "-"; | ||||||
| @@ -256,7 +218,7 @@ namespace cryptoki { | |||||||
|               case CKO_VENDOR_DEFINED: return "VENDOR_DEFINED"; |               case CKO_VENDOR_DEFINED: return "VENDOR_DEFINED"; | ||||||
|               default: "unknown"; |               default: "unknown"; | ||||||
|             } |             } | ||||||
|           default: return readable(value); |           default: return crypto::readable(value); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       template<typename TYPE> Attribute from(const TYPE& v) { |       template<typename TYPE> Attribute from(const TYPE& v) { | ||||||
| @@ -904,8 +866,11 @@ namespace cryptoki { | |||||||
|       ObjectList find(const AttributeList& attrs=AttributeList()); |       ObjectList find(const AttributeList& attrs=AttributeList()); | ||||||
|  |  | ||||||
|       //! Create a new Certificate Object. |       //! Create a new Certificate Object. | ||||||
|       Object createCertificate(const std::string& derSubject, |       Object create(const openssl::X509& cert); | ||||||
|                                const std::string& desValue); |       //! Create a new PrivateKey Object. | ||||||
|  |       Object create(const openssl::PrivateKey& key); | ||||||
|  |       //! Create a new Certificate and optional PrivateKey Object. | ||||||
|  |       Object create(const openssl::PKCS12& p12); | ||||||
|        |        | ||||||
|       //@} |       //@} | ||||||
|        |        | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| ##       1         2         3         4         5         6         7         8 | ##       1         2         3         4         5         6         7         8 | ||||||
| ## 45678901234567890123456789012345678901234567890123456789012345678901234567890 | ## 45678901234567890123456789012345678901234567890123456789012345678901234567890 | ||||||
|  |  | ||||||
| include_HEADERS = pcsc.hxx cryptoki.hxx openssl.hxx | include_HEADERS = pcsc.hxx cryptoki.hxx openssl.hxx cryptaux.hxx | ||||||
| pkcs11_HEADERS = pkcs11/pkcs11.h pkcs11/pkcs11types.h pkcs11/apiclient.h | pkcs11_HEADERS = pkcs11/pkcs11.h pkcs11/pkcs11types.h pkcs11/apiclient.h | ||||||
| pkcs11dir = ${includedir}/pkcs11 | pkcs11dir = ${includedir}/pkcs11 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,10 +10,12 @@ | |||||||
|  |  | ||||||
| #include <openssl/pkcs12.h> | #include <openssl/pkcs12.h> | ||||||
| #include <openssl/x509.h> | #include <openssl/x509.h> | ||||||
|  |  | ||||||
| #include "openssl/bio.h" | #include "openssl/bio.h" | ||||||
| #include "openssl/ssl.h" | #include "openssl/ssl.h" | ||||||
| #include <openssl/err.h> | #include <openssl/err.h> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include <cryptaux.hxx> | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
|  |  | ||||||
| /*! @defgroup gopenssl C++ Wrapper around OpenSSL API */ | /*! @defgroup gopenssl C++ Wrapper around OpenSSL API */ | ||||||
| @@ -85,6 +87,13 @@ namespace openssl { | |||||||
|       } |       } | ||||||
|   }; |   }; | ||||||
|   //---------------------------------------------------------------------------- |   //---------------------------------------------------------------------------- | ||||||
|  |   class x509_decoding_failed: public x509_error { | ||||||
|  |     public: | ||||||
|  |       x509_decoding_failed(const std::string& der) throw(): | ||||||
|  |           x509_error("certificate decoding failed:\n"+crypto::readable(der)) { | ||||||
|  |       } | ||||||
|  |   }; | ||||||
|  |   //---------------------------------------------------------------------------- | ||||||
|   class undefined_certificate: public x509_error { |   class undefined_certificate: public x509_error { | ||||||
|     public: |     public: | ||||||
|       undefined_certificate() throw(): |       undefined_certificate() throw(): | ||||||
| @@ -199,6 +208,13 @@ namespace openssl { | |||||||
|       X509(): _x509(X509_new()) { |       X509(): _x509(X509_new()) { | ||||||
|         if (!_x509) throw allocation_failed(); |         if (!_x509) throw allocation_failed(); | ||||||
|       } |       } | ||||||
|  |       //! Initialize from DER encoded cerificate. | ||||||
|  |       X509(const std::string& der): _x509(0) { | ||||||
|  |         const unsigned char* c((const unsigned char*)der.begin().operator->()); | ||||||
|  |         if (!(_x509=d2i_X509(0, &c, der.size())) || | ||||||
|  |             (const char*)c!=der.begin().operator->()+der.size()) | ||||||
|  |           throw x509_decoding_failed(der); | ||||||
|  |       } | ||||||
|       X509(const X509& o): _x509(0) { |       X509(const X509& o): _x509(0) { | ||||||
|         unsigned char* d(0); |         unsigned char* d(0); | ||||||
|         int len(i2d_X509(o._x509, &d)); |         int len(i2d_X509(o._x509, &d)); | ||||||
| @@ -222,18 +238,58 @@ namespace openssl { | |||||||
|         int len(i2d_X509(o._x509, &d)); |         int len(i2d_X509(o._x509, &d)); | ||||||
|         if (!len) throw x509_copy_failed(); |         if (!len) throw x509_copy_failed(); | ||||||
|         const unsigned char* d2(d); |         const unsigned char* d2(d); | ||||||
|         free(d); |  | ||||||
|         _x509 = d2i_X509(0, &d2, len); |         _x509 = d2i_X509(0, &d2, len); | ||||||
|  |         OPENSSL_free(d); | ||||||
|         if (!_x509) throw x509_copy_failed(); |         if (!_x509) throw x509_copy_failed(); | ||||||
|       } |       } | ||||||
|       //! Get DER encoded subject. |       //! Get DER encoded subject. | ||||||
|       std::string subjectDER() const { |       std::string subjectDER() const { | ||||||
|         char* c(0); |         unsigned char* c(0); | ||||||
|         int len(i2d_X509_NAME(X509_get_subject_name(_x509), &c)); |         int len(i2d_X509_NAME(X509_get_subject_name(_x509), &c)); | ||||||
|         std::String res(c, len); |         std::string res((char*)c, len); | ||||||
|         OPENSSL_free(c); |         OPENSSL_free(c); | ||||||
|         return res; |         return res; | ||||||
|       } |       } | ||||||
|  |       //! Get DER encoded issuer. | ||||||
|  |       std::string issuerDER() const { | ||||||
|  |         unsigned char* c(0); | ||||||
|  |         int len(i2d_X509_NAME(X509_get_issuer_name(_x509), &c)); | ||||||
|  |         std::string res((char*)c, len); | ||||||
|  |         OPENSSL_free(c); | ||||||
|  |         return res; | ||||||
|  |       } | ||||||
|  |       //! Get DER encoded value. | ||||||
|  |       std::string valueDER() const { | ||||||
|  |         unsigned char* c(0); | ||||||
|  |         int len(i2d_X509(_x509, &c)); | ||||||
|  |         std::string res((char*)c, len); | ||||||
|  |         OPENSSL_free(c); | ||||||
|  |         return res; | ||||||
|  |       } | ||||||
|  |       //! Get serial number. | ||||||
|  |       std::string serial() const { | ||||||
|  |         /* @bug http://albistechnologies.com reports: «could be a | ||||||
|  |             failure in openSSL: len too short by 1 if serial number | ||||||
|  |             starts with 00 ASN1_INTEGER* ser = | ||||||
|  |             X509_get_serialNumber(_x509);» */ | ||||||
|  |         ASN1_INTEGER* ser(X509_get_serialNumber(_x509)); | ||||||
|  |         //! @todo requires memory free? | ||||||
|  |         /*! @todo ser->type?!? http://albistechnologies.com prepends | ||||||
|  |             tag and length in the first two char-fields. */ | ||||||
|  |         return std::string((char*)ser->data, ser->length); | ||||||
|  |       } | ||||||
|  |       //! Get id. | ||||||
|  |       std::string id() const { | ||||||
|  |         unsigned char c[SHA_DIGEST_LENGTH]; | ||||||
|  |         SHA1(_x509->cert_info->key->public_key->data, | ||||||
|  |              _x509->cert_info->key->public_key->length, | ||||||
|  |              c); | ||||||
|  |         return std::string((char*)c, SHA_DIGEST_LENGTH); | ||||||
|  |       } | ||||||
|  |       //! Get label. | ||||||
|  |       std::string label() const { | ||||||
|  |         return std::string(); | ||||||
|  |       } | ||||||
|     private: |     private: | ||||||
|       ::X509* _x509; |       ::X509* _x509; | ||||||
|   }; |   }; | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/pcsc.hxx
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/pcsc.hxx
									
									
									
									
									
								
							| @@ -10,6 +10,8 @@ | |||||||
| #ifndef PCSC_HXX | #ifndef PCSC_HXX | ||||||
| #define PCSC_HXX | #define PCSC_HXX | ||||||
|  |  | ||||||
|  | #include <cryptaux.hxx> | ||||||
|  |  | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| #ifdef WIN32 | #ifdef WIN32 | ||||||
| @@ -68,25 +70,11 @@ | |||||||
|     best fits the common needs. */ |     best fits the common needs. */ | ||||||
| //@{ | //@{ | ||||||
| /*! @defgroup pcsclib PCSC C++ Library */ | /*! @defgroup pcsclib PCSC C++ Library */ | ||||||
| /*! @defgroup pcsctypes PCSC Types and Auxiliary */ |  | ||||||
| /*! @defgroup pcscexceptions PCSC Exceptions */ | /*! @defgroup pcscexceptions PCSC Exceptions */ | ||||||
|  |  | ||||||
| //! @see gpcsc | //! @see gpcsc | ||||||
| namespace pcsc { | namespace pcsc { | ||||||
|  |  | ||||||
|   //! @addtogroup pcsctypes |  | ||||||
|   //@{ |  | ||||||
|    |  | ||||||
|   std::string hex(const std::string& data) { |  | ||||||
|     std::stringstream res; |  | ||||||
|     for (std::string::const_iterator it(data.begin()); it!=data.end(); ++it) |  | ||||||
|       res<<std::hex<<std::setfill('0')<<std::setw(2) |  | ||||||
|          <<(unsigned int)(unsigned char)*it; |  | ||||||
|     return res.str(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   //@} |  | ||||||
|  |  | ||||||
|   //============================================================================ |   //============================================================================ | ||||||
|   //! @addtogroup pcsclib |   //! @addtogroup pcsclib | ||||||
|   //@{ |   //@{ | ||||||
| @@ -236,7 +224,7 @@ namespace pcsc { | |||||||
|             check(SCardTransmit(_id, &rPci, |             check(SCardTransmit(_id, &rPci, | ||||||
|                                 (unsigned char*)in.c_str(), in.size(), |                                 (unsigned char*)in.c_str(), in.size(), | ||||||
|                                 0, buff, &len), |                                 0, buff, &len), | ||||||
|                   "smartcard transmit message "+hex(in)); |                   "smartcard transmit message "+crypto::hex(in)); | ||||||
|             return std::string((char*)buff, len); |             return std::string((char*)buff, len); | ||||||
|           } |           } | ||||||
|  |  | ||||||
| @@ -312,7 +300,7 @@ namespace pcsc { | |||||||
|             check(SCardConnect(_connection._id, strconv(name).c_str(), |             check(SCardConnect(_connection._id, strconv(name).c_str(), | ||||||
|                                mode, protocol, |                                mode, protocol, | ||||||
|                                &_id, &_protocol), |                                &_id, &_protocol), | ||||||
|                   "connect smartcard \""+name+"\" ("+hex(name)+")"); |                   "connect smartcard \""+name+"\" ("+crypto::hex(name)+")"); | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           //! forbidden |           //! forbidden | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user