diff --git a/include/electronic-id/electronic-id.hpp b/include/electronic-id/electronic-id.hpp index cf72a55..b2306ee 100644 --- a/include/electronic-id/electronic-id.hpp +++ b/include/electronic-id/electronic-id.hpp @@ -47,6 +47,8 @@ class ElectronicID HrvEID, BelEID, CzeEID, + LuxtrustV2, + LuxEID, #ifdef _WIN32 MsCryptoApiEID, #endif diff --git a/src/electronic-id.cpp b/src/electronic-id.cpp index da93ea0..0a29dd3 100644 --- a/src/electronic-id.cpp +++ b/src/electronic-id.cpp @@ -110,6 +110,118 @@ const std::map SUPPORTED_ATRS { {{0x3b, 0x7e, 0x94, 0x00, 0x00, 0x80, 0x25, 0xd2, 0x03, 0x10, 0x01, 0x00, 0x56, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00}, constructor}, + // LuxtrustV2 + {{0x3B, 0x7D, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x83, + 0x00, 0x90, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0x7F, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, + 0x00, 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0xFF, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xB0, 0x00, 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0x8F, 0x00, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0x88, 0x00, 0x01, 0xE1, 0xF3, 0x5E, 0x11, 0x00, 0x87, 0x95, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0, + 0x00, 0x04, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00}, + constructor}, + // LuxEID + {{0x3b, 0xff, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xb0, 0x00, 0x04, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x04, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3b, 0x88, 0x80, 0x01, 0x00, 0x88, 0x3c, 0x1f, 0x77, 0x81, 0x95, 0x00, 0xc1}, + constructor}, + // LuxEID + {{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0, + 0x00, 0x05, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00}, + constructor}, + // LuxEID + {{0x3b, 0xff, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xb0, 0x00, 0x05, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x05, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + constructor}, + // LuxEID + {{0x3b, 0x88, 0x80, 0x01, 0xe1, 0xf3, 0x5e, 0x11, 0x77, 0xa1, 0x97, 0x00, 0x15}, + constructor}, +}; + +// Masked cards. +const std::map MASKED_ATRS { + // LuxtrustV2 + {{0x3B, 0x7D, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x83, + 0x00, 0x90, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0xFF, 0xFF}}, + // LuxEID + {{0x3B, 0x7F, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, + 0x00, 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + // LuxEID + {{0x3B, 0xFF, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xB0, 0x00, 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3B, 0x8F, 0x00, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3B, 0x88, 0x00, 0x01, 0xE1, 0xF3, 0x5E, 0x11, 0x00, 0x87, 0x95, 0x00, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0, + 0x00, 0x04, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + // LuxEID + {{0x3b, 0xff, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xb0, 0x00, 0x04, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x04, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3b, 0x88, 0x80, 0x01, 0x00, 0x88, 0x3c, 0x1f, 0x77, 0x81, 0x95, 0x00, 0xc1}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + // LuxEID + {{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0, + 0x00, 0x05, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + // LuxEID + {{0x3b, 0xff, 0x00, 0x00, 0x00, 0x81, 0x31, 0x00, 0x43, 0x80, 0x31, 0x80, 0x65, + 0xb0, 0x00, 0x05, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, + 0x05, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}}, + // LuxEID + {{0x3b, 0x88, 0x80, 0x01, 0xe1, 0xf3, 0x5e, 0x11, 0x77, 0xa1, 0x97, 0x00, 0x15}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, }; inline std::string byteVectorToHexString(const byte_vector& bytes) @@ -137,22 +249,46 @@ const auto SUPPORTED_ALGORITHMS = std::map { namespace electronic_id { +pcsc_cpp::byte_vector applyMask(const pcsc_cpp::byte_vector& atr, const pcsc_cpp::byte_vector& mask) +{ + pcsc_cpp::byte_vector result(atr.size()); + for (size_t i = 0; i < atr.size(); ++i) { + result[i] = atr[i] & mask[i]; + } + return result; +} + bool isCardSupported(const pcsc_cpp::byte_vector& atr) { - return SUPPORTED_ATRS.count(atr); + if (SUPPORTED_ATRS.count(atr)) { + return true; + } + for (const auto& [maskedAtr, mask] : MASKED_ATRS) { + if (applyMask(atr, mask) == maskedAtr) { + return SUPPORTED_ATRS.count(maskedAtr); + } + } + return false; } ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader) { - try { + if (SUPPORTED_ATRS.count(reader.cardAtr)) { const auto& eidConstructor = SUPPORTED_ATRS.at(reader.cardAtr); return eidConstructor(reader); - } catch (const std::out_of_range&) { - // It should be verified that the card is supported with isCardSupported() before - // calling getElectronicID(), so it is a programming error if out_of_range occurs here. - THROW(ProgrammingError, - "Card with ATR '" + byteVectorToHexString(reader.cardAtr) + "' is not supported"); } + + for (const auto& [maskedAtr, mask] : MASKED_ATRS) { + if (applyMask(reader.cardAtr, mask) == maskedAtr && SUPPORTED_ATRS.count(maskedAtr)) { + const auto& eidConstructor = SUPPORTED_ATRS.at(maskedAtr); + return eidConstructor(reader); + } + } + + // It should be verified that the card is supported with isCardSupported() before + // calling getElectronicID(), so it is a programming error if out_of_range occurs here. + THROW(ProgrammingError, + "Card with ATR '" + byteVectorToHexString(reader.cardAtr) + "' is not supported"); } bool ElectronicID::isSupportedSigningHashAlgorithm(const HashAlgorithm hashAlgo) const diff --git a/src/electronic-ids/pkcs11/Pkcs11ElectronicID.cpp b/src/electronic-ids/pkcs11/Pkcs11ElectronicID.cpp index c5a5bd0..b8d376a 100644 --- a/src/electronic-ids/pkcs11/Pkcs11ElectronicID.cpp +++ b/src/electronic-ids/pkcs11/Pkcs11ElectronicID.cpp @@ -72,11 +72,15 @@ inline fs::path lithuanianPKCS11ModulePath() inline fs::path croatianPkcs11ModulePath() { #ifdef _WIN32 - return programFilesPath() / L"AKD/eID Middleware/pkcs11/AkdEidPkcs11_64.dll"; + fs::path certiliaPath = programFilesPath() / L"AKD/Certilia Middleware/pkcs11/AkdEidPkcs11_64.dll"; + fs::path eidPath = programFilesPath() / L"AKD/eID Middleware/pkcs11/AkdEidPkcs11_64.dll"; + return fs::exists(certiliaPath) ? certiliaPath : eidPath; #elif defined __APPLE__ - return "/Library/AKD/eID Middleware/pkcs11/libEidPkcs11.so"; // NB! Not tested. + return "/usr/local/lib/pkcs11/libEidPkcs11.dylib"; // NB! Not tested. #else // Linux - return "/usr/lib/akd/eidmiddleware/pkcs11/libEidPkcs11.so"; + fs::path certiliaPath = "/usr/lib/akd/certiliamiddleware/pkcs11/libEidPkcs11.so"; + fs::path eidPath = "/usr/lib/akd/eidmiddleware/pkcs11/libEidPkcs11.so"; + return fs::exists(certiliaPath) ? certiliaPath : eidPath; #endif } @@ -103,6 +107,18 @@ inline fs::path czechPkcs11ModulePath() #endif } +inline fs::path luxembourgPkcs11ModulePath() +{ +#ifdef _WIN32 + return programFilesPath() / L"Gemalto/Classic Client/BIN/gclib.dll"; +#elif defined __APPLE__ + return "/Library/Frameworks/Pkcs11ClassicClient.framework/Versions/A/Pkcs11ClassicClient/" + "libgclib.dylib"; +#else // Linux + return "/usr/lib/pkcs11/libgclib.so"; +#endif +} + const std::map SUPPORTED_PKCS11_MODULES { // EstEID configuration is here only for testing, // it is not enabled in getElectronicID(). @@ -156,6 +172,26 @@ const std::map SUPPORTED_PKCS11_MO true, false, }}, + {ElectronicID::Type::LuxtrustV2, + { + "LuxtrustV2 eID (PKCS#11)"s, // name + ElectronicID::Type::LuxtrustV2, // type + luxembourgPkcs11ModulePath().make_preferred(), // path + + 3, + true, + false, + }}, + {ElectronicID::Type::LuxEID, + { + "Luxembourg eID (PKCS#11)"s, // name + ElectronicID::Type::LuxEID, // type + luxembourgPkcs11ModulePath().make_preferred(), // path + + 3, + true, + true, + }}, }; const Pkcs11ElectronicIDModule& getModule(ElectronicID::Type eidType) @@ -176,11 +212,14 @@ Pkcs11ElectronicID::Pkcs11ElectronicID(ElectronicID::Type type) : { REQUIRE_NON_NULL(manager) + const bool checkExtKeyUsage = + (type != ElectronicID::Type::LuxtrustV2 && type != ElectronicID::Type::LuxEID); + bool seenAuthToken = false; bool seenSigningToken = false; for (const auto& token : manager->tokens()) { - const auto certType = certificateType(token.cert); + const auto certType = certificateType(token.cert, checkExtKeyUsage); if (certType.isAuthentication()) { authToken = token; seenAuthToken = true; diff --git a/src/electronic-ids/x509.hpp b/src/electronic-ids/x509.hpp index 9d04b6d..f113c06 100644 --- a/src/electronic-ids/x509.hpp +++ b/src/electronic-ids/x509.hpp @@ -8,6 +8,8 @@ #include #include +#include + namespace electronic_id { @@ -37,7 +39,8 @@ inline bool hasClientAuthExtendedKeyUsage(EXTENDED_KEY_USAGE* usage) noexcept return false; } -inline CertificateType certificateType(const pcsc_cpp::byte_vector& cert) +inline CertificateType certificateType(const pcsc_cpp::byte_vector& cert, + const bool checkExtKeyUsage = true) { auto x509 = make_x509(cert); auto keyUsage = extension(x509.get(), NID_key_usage, ASN1_BIT_STRING_free); @@ -52,6 +55,10 @@ inline CertificateType certificateType(const pcsc_cpp::byte_vector& cert) static const int KEY_USAGE_DIGITAL_SIGNATURE = 0; if (ASN1_BIT_STRING_get_bit(keyUsage.get(), KEY_USAGE_DIGITAL_SIGNATURE)) { + if (!checkExtKeyUsage) { + return CertificateType::AUTHENTICATION; + } + if (auto extKeyUsage = extension(x509.get(), NID_ext_key_usage, EXTENDED_KEY_USAGE_free); extKeyUsage && hasClientAuthExtendedKeyUsage(extKeyUsage.get())) { return CertificateType::AUTHENTICATION;