Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support of Luxembourg cards #89

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/electronic-id/electronic-id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class ElectronicID
HrvEID,
BelEID,
CzeEID,
LuxtrustV2,
LuxEID,
#ifdef _WIN32
MsCryptoApiEID,
#endif
Expand Down
150 changes: 143 additions & 7 deletions src/electronic-id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,118 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
{{0x3b, 0x7e, 0x94, 0x00, 0x00, 0x80, 0x25, 0xd2, 0x03, 0x10, 0x01, 0x00, 0x56, 0x00, 0x00,
0x00, 0x02, 0x02, 0x00},
constructor<ElectronicID::Type::CzeEID>},
// LuxtrustV2
{{0x3B, 0x7D, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x83,
0x00, 0x90, 0x00},
constructor<ElectronicID::Type::LuxtrustV2>},
// LuxEID
{{0x3B, 0x7F, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0,
0x00, 0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// 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<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3B, 0x8F, 0x00, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00,
0x03, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3B, 0x88, 0x00, 0x01, 0xE1, 0xF3, 0x5E, 0x11, 0x00, 0x87, 0x95, 0x00, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0,
0x00, 0x04, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// 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<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00,
0x04, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3b, 0x88, 0x80, 0x01, 0x00, 0x88, 0x3c, 0x1f, 0x77, 0x81, 0x95, 0x00, 0xc1},
constructor<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3b, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0,
0x00, 0x05, 0x00, 0x00, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// 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<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3B, 0x8F, 0x80, 0x01, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x00,
0x05, 0x00, 0x00, 0x12, 0x0F, 0xFF, 0x82, 0x90, 0x00, 0x00},
constructor<ElectronicID::Type::LuxEID>},
// LuxEID
{{0x3b, 0x88, 0x80, 0x01, 0xe1, 0xf3, 0x5e, 0x11, 0x77, 0xa1, 0x97, 0x00, 0x15},
constructor<ElectronicID::Type::LuxEID>},
};

// Masked cards.
const std::map<byte_vector, byte_vector> 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)
Expand Down Expand Up @@ -137,22 +249,46 @@ const auto SUPPORTED_ALGORITHMS = std::map<std::string, HashAlgorithm> {
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.contains(atr);
if (SUPPORTED_ATRS.contains(atr)) {
return true;
}
for (const auto& [maskedAtr, mask] : MASKED_ATRS) {
if (applyMask(atr, mask) == maskedAtr) {
return SUPPORTED_ATRS.contains(maskedAtr);
}
}
return false;
}

ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader)
{
try {
if (SUPPORTED_ATRS.contains(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.contains(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
Expand Down
37 changes: 36 additions & 1 deletion src/electronic-ids/pkcs11/Pkcs11ElectronicID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@
#endif
}

inline fs::path luxembourgPkcs11ModulePath()

Check notice

Code scanning / CodeQL

Unused static function Note

Static function luxembourgPkcs11ModulePath is unreachable
{
#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<ElectronicID::Type, Pkcs11ElectronicIDModule> SUPPORTED_PKCS11_MODULES {
// EstEID configuration is here only for testing,
// it is not enabled in getElectronicID().
Expand Down Expand Up @@ -156,6 +168,26 @@
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)
Expand All @@ -176,11 +208,14 @@
{
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;
Expand Down
9 changes: 8 additions & 1 deletion src/electronic-ids/x509.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <openssl/x509v3.h>
#include <openssl/err.h>

#include <optional>

namespace electronic_id
{

Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
Loading