[{"content":"LibreMiddleware ships a native C++ digital signing engine that supports five signature formats, four conformance levels, and hardware-backed signing via PKCS#11. This guide shows downstream consumers how to integrate it through the public LibreSCRS::Signing API.\nThe signing engine itself lives in the internal libresign library under lib/libresign/ — every header there is gated by LIBRESCRS_INTERNAL_BUILD and is not part of the supported consumer surface. Downstream code links against the LibreSCRS::Signing CMake alias and includes only \u0026lt;LibreSCRS/Signing/…\u0026gt; headers.\nSignature Formats and Levels #The engine produces signatures conforming to the EU eIDAS / ETSI baseline profiles:\nFormat Standard Input Output Packaging PAdES ETSI EN 319 142 PDF Signed PDF Enveloped CAdES ETSI EN 319 122 Any file .p7s (PKCS#7/CMS) Detached XAdES ETSI EN 319 132 Any file .xml (XML-DSIG) Enveloped or detached JAdES ETSI EN 319 182 Any file .json (JWS) Detached ASiC-E ETSI EN 319 162 Any file(s) .asice (ZIP container) Detached (XAdES inside) Each format supports four levels of increasing assurance:\nLevel Content Requires B-B Basic signature Signing certificate only B-T B-B + trusted timestamp TSA server B-LT B-T + revocation data (CRL/OCSP) TSA + revocation sources B-LTA B-LT + archive timestamp TSA + revocation sources CMake Integration #LibreMiddleware is designed to be consumed via CMake FetchContent. Signing support is enabled by default. Tags use the bare X.Y.Z form (no v prefix):\ninclude(FetchContent) FetchContent_Declare( LibreMiddleware GIT_REPOSITORY https://github.com/LibreSCRS/LibreMiddleware.git GIT_TAG 4.0.0 ) FetchContent_MakeAvailable(LibreMiddleware) target_link_libraries(MyApp PRIVATE LibreSCRS::Signing LibreSCRS::Trust LibreSCRS::Plugin LibreSCRS::SmartCard LibreSCRS::Auth ) For local development, point to a local checkout instead of fetching from Git:\ncmake -B build -DFETCHCONTENT_SOURCE_DIR_LIBREMIDDLEWARE=/path/to/LibreMiddleware Build Options # Option Default Description BUILD_SIGNING ON Enable digital signing support (LibreSCRS::Signing) SIGNING_BACKEND native Backend selection: native, dss, or both. The DSS backend is a test oracle and is deprecated for production use The build exports LIBREMIDDLEWARE_HAS_SIGNING so downstream projects can conditionally compile signing features.\nMinimal Signing Example #The example below performs a complete PAdES B-T sign against a card discovered by the plugin registry. It uses only the public API — every include is from \u0026lt;LibreSCRS/…\u0026gt;.\n#include \u0026lt;LibreSCRS/Auth/CredentialProvider.h\u0026gt; #include \u0026lt;LibreSCRS/Plugin/CardPluginService.h\u0026gt; #include \u0026lt;LibreSCRS/Secure/String.h\u0026gt; #include \u0026lt;LibreSCRS/Signing/SigningRequest.h\u0026gt; #include \u0026lt;LibreSCRS/Signing/SigningResult.h\u0026gt; #include \u0026lt;LibreSCRS/Signing/SigningService.h\u0026gt; #include \u0026lt;LibreSCRS/Signing/TsaProvider.h\u0026gt; #include \u0026lt;LibreSCRS/SmartCard/CardSession.h\u0026gt; #include \u0026lt;LibreSCRS/SmartCard/MonitorService.h\u0026gt; #include \u0026lt;LibreSCRS/Trust/TrustConfig.h\u0026gt; #include \u0026lt;LibreSCRS/Trust/TrustStoreService.h\u0026gt; #include \u0026lt;iostream\u0026gt; namespace lsc = LibreSCRS; int main() { // 1. Build the trust store. The factory is noexcept and returns a // usable service even when the network is unreachable — bundled // and (optionally) system anchors are available immediately while // eager Trusted-List fetches run on internal worker threads. lsc::Trust::TrustConfig trustConfig; trustConfig.cacheDirectory = \u0026#34;/var/cache/myapp/tl-cache\u0026#34;; // trustConfig.sources.push_back({...}); // optional EU LOTL / national TLs auto trustResult = lsc::Trust::TrustStoreService::create(std::move(trustConfig)); if (!trustResult) { std::cerr \u0026lt;\u0026lt; \u0026#34;Trust store init failed: \u0026#34; \u0026lt;\u0026lt; trustResult.error().userMessage.defaultText \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 1; } std::shared_ptr\u0026lt;lsc::Trust::TrustStoreService\u0026gt; trust = *trustResult; // 2. Construct the SigningService. The TsaProvider is invoked at // sign time for B-T / B-LT / B-LTA levels; staticTsa() is a // convenience factory that always returns the same URL with no // authentication. Pass a default-constructed TsaProvider{} to // disable TSA — B-B signing still works, higher levels then fail // with Status::TsaUnreachable. auto tsa = lsc::Signing::staticTsa(\u0026#34;http://timestamp.digicert.com\u0026#34;); auto signingService = std::make_shared\u0026lt;lsc::Signing::SigningService\u0026gt;(trust, std::move(tsa)); // 3. Discover a card plugin + open a session. CardPluginService scans // the configured plugin directory; MonitorService observes PC/SC // events. Real apps subscribe to MonitorService and look up a // plugin via findPluginForCard(atr) once a card has arrived; the // helpers below collapse that flow into a single app-specific // bootstrap step. lsc::Plugin::CardPluginService plugins{\u0026#34;/usr/local/lib/librescrs/plugins\u0026#34;}; lsc::SmartCard::MonitorService monitor; auto session = openFirstSession(monitor, plugins); // app-specific helper if (!session) { std::cerr \u0026lt;\u0026lt; \u0026#34;No card available\\n\u0026#34;; return 1; } auto atr = session-\u0026gt;atr(); // std::span\u0026lt;const std::uint8_t\u0026gt; auto cardPlugin = plugins.findPluginForCard(atr); if (!cardPlugin) { std::cerr \u0026lt;\u0026lt; \u0026#34;No plugin matches this card\u0026#39;s ATR\\n\u0026#34;; return 1; } // 4. Build the signing request. The engine reads from inputFile and // writes the signed payload to outputFile — there is no // byte-buffer ingestion seam on the public API. auto request = lsc::Signing::SigningRequest::Builder{} .inputFile(\u0026#34;document.pdf\u0026#34;) .outputFile(\u0026#34;document-signed.pdf\u0026#34;) .format(lsc::Signing::SignatureFormat::Pades) .level(lsc::Signing::SignatureLevel::B_T) .build(); // 5. PIN provider — invoked by the service when the card requires it. // The provider receives an AuthRequirement describing what to // collect and returns a CredentialResult. In a GUI host this // typically pops a PIN dialog; in batch tools it reads from a // secure prompt or an env var. lsc::Auth::CredentialProvider pinProvider = [](const lsc::Auth::AuthRequirement\u0026amp;) { lsc::Secure::String pin{\u0026#34;1234\u0026#34;}; // host-collected, cleansed on dtor return lsc::Auth::CredentialResult::ok({{\u0026#34;pin\u0026#34;, std::move(pin)}}); }; // 6. Sign. The call blocks for the duration of the operation (PIN // verify + card APDU sign + optional TSA round-trip). GUI hosts // run this on a worker thread. auto result = signingService-\u0026gt;sign(request, std::move(pinProvider), cardPlugin, session); if (result.status != lsc::Signing::SigningResult::Status::Ok) { std::cerr \u0026lt;\u0026lt; \u0026#34;Signing failed: \u0026#34; \u0026lt;\u0026lt; result.userMessage.defaultText \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 1; } // 7. Success — the signed document is on disk at the path the engine // wrote to (i.e. the outputFile() the request was built with). std::cout \u0026lt;\u0026lt; \u0026#34;Signed: \u0026#34; \u0026lt;\u0026lt; result.outputPath-\u0026gt;string() \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 0; } API Reference #All public types live under the LibreSCRS::* PascalCase namespaces. Every type referenced below has its full Doxygen contract in the corresponding header under include/LibreSCRS/.\nLibreSCRS::Signing::SigningService #Public entry point. Constructed once with the trust lifecycle owner and a TSA callback; reused for many sign() calls.\nConstruction:\nSigningService(std::shared_ptr\u0026lt;Trust::TrustStoreService\u0026gt; trustService, TsaProvider tsa); Signing call (4-arg, [[nodiscard]]):\nSigningResult sign(const SigningRequest\u0026amp; request, Auth::CredentialProvider credentialProvider, std::shared_ptr\u0026lt;Plugin::CardPlugin\u0026gt; cardPlugin, std::shared_ptr\u0026lt;SmartCard::CardSession\u0026gt; session); The call is blocking and thread-safe across distinct (cardPlugin, session) pairs. A null plugin or session, or an empty credentialProvider, returns SigningResult::Status::InvalidRequest rather than throwing.\nLibreSCRS::Trust::TrustStoreService #Lifecycle owner of the trust store. Factory is noexcept and returns std::expected\u0026lt;std::shared_ptr\u0026lt;TrustStoreService\u0026gt;, CreateError\u0026gt;. Eager Trusted-List fetches run on internal worker threads; consumers observe completion via status(), addObserver(), or the blocking waitForEagerFetches().\nLibreSCRS::Signing::SigningRequest #Immutable signing parameters built through the inner Builder. The engine is file-path based — pass inputFile() and outputFile(); there is no byte-buffer overload on the public API. Key builder methods:\nBuilder method Description inputFile(std::filesystem::path) Source document on disk outputFile(std::filesystem::path) Destination path the engine writes to format(SignatureFormat) Pades / Cades / Xades / Jades / AsicE level(SignatureLevel) B_B / B_T / B_LT / B_LTA packaging(PackagingMode) Enveloped or Detached reason / location / contactInfo PDF signature dictionary fields (ISO 32000-1 §12.8.1) certificateLabel(std::string) PKCS#11 key alias when the card carries more than one visualParams(VisualSignatureParams\u0026amp;\u0026amp;) PAdES visual signature overlay tsaOverride(TsaProvider) Per-request TSA override; pair with staticTsa(url) for a fixed URL Builder::build() is rvalue-qualified; finalise with std::move(builder).build() and wrap the call in a try/catch for std::invalid_argument if you set fields conditionally.\nLibreSCRS::Signing::SigningResult # Field Type Description status Status enum Always set; check before reading other fields outputPath std::optional\u0026lt;std::filesystem::path\u0026gt; Path the signed document was written to on success userMessage LocalizedText Translator-friendly user-facing message; mandatory in 4.0 diagnosticDetail std::optional\u0026lt;std::string\u0026gt; Developer-facing diagnostic for logs Status values: Ok, InvalidRequest, TrustStoreUnavailable, UserCancelled, PinVerificationFailed, CardBlocked, TsaUnreachable, SigningEngineError. The enum is append-only; consumer switch statements must include a default branch.\nLibreSCRS::Auth::CredentialProvider #SyncProvider\u0026lt;CredentialResult, AuthRequirement\u0026gt; — a host-supplied callable that maps an AuthRequirement (what the card needs) to a CredentialResult (filled credentials, a user-cancel, or a provider error). The signing service invokes the provider at most once per card unlock.\nLibreSCRS::Plugin::CardPlugin and LibreSCRS::SmartCard::CardSession #The plugin drives card-specific operations (signing APDUs, key discovery) and is obtained from CardPluginService. The session encapsulates an open PC/SC channel and is obtained either through the monitor flow or directly via CardSession::open(readerName, plugin). The signing service holds shared ownership of both for the duration of the call.\nPKCS#11 Token Support #The signing engine accesses private keys through the CardPlugin / CardSession abstraction. Under the hood the plugin layer talks to the card via the LibreSCRS PKCS#11 module (librescrs-pkcs11.so) plus appropriate card-specific drivers — CardEdge, PKCS#15, PIV, or OpenSC.\nDownstream consumers that already drive a PKCS#11 module directly can do so independently of LibreSCRS::Signing; the 4.0 public signing API is intentionally PKCS#11-agnostic at the seam (plugin + session).\nThe PIN never persists across the sign() call — it is delivered to the card via CredentialProvider (which the host is expected to back with zero-on-destruct storage such as LibreSCRS::Secure::Buffer / LibreSCRS::Secure::String) and discarded immediately after C_Login.\nPDF Input Tolerance #For PAdES signing, the engine follows Adobe Acrobat Implementation Notes §H.3 when ingesting PDF input, matching the behaviour of Acrobat, Foxit, qpdf, and pdfinfo:\nUp to 1024 bytes of non-PDF prefix before the %PDF- header are tolerated and stripped (e.g. multipart/form-data wrappers from web-form uploads). Trailing data after the last %%EOF is stripped (an optional single CR/LF is preserved). When startxref points to an offset that does not contain the xref keyword (common after a prefix strip, or a generator bug), the engine falls back to scanning the last ~10 KB for a standalone xref keyword and retries. If the first 1024 bytes contain no %PDF- header the input is still rejected with a structured InvalidRequest result. No caller-side changes are needed — the tolerance is applied internally during PAdES ingestion.\nError Handling #SigningService::sign() returns a SigningResult rather than throwing. Inspect result.status and result.userMessage on failure:\nauto result = signingService-\u0026gt;sign(request, pinProvider, cardPlugin, session); if (result.status != LibreSCRS::Signing::SigningResult::Status::Ok) { using S = LibreSCRS::Signing::SigningResult::Status; switch (result.status) { case S::TrustStoreUnavailable: /* TL fetch / config rejected */ break; case S::InvalidRequest: /* null plugin/session, empty cb */ break; case S::UserCancelled: /* provider returned cancel */ break; case S::PinVerificationFailed: /* wrong PIN */ break; case S::CardBlocked: /* signing PIN blocked by card */ break; case S::TsaUnreachable: /* B-T+ requested, TSA failed */ break; case S::SigningEngineError: /* libresign / APDU pipeline */ break; default: break; // append-only enum; keep default } log(result.userMessage.defaultText); if (result.diagnosticDetail) { log(*result.diagnosticDetail); } } TrustStoreService::create() is similarly [[nodiscard]] noexcept and returns std::expected\u0026lt;…, CreateError\u0026gt;. Checking the result up front keeps failure paths explicit; no exception ever propagates across the public API surface.\n","date":null,"permalink":"https://librescrs.github.io/developer-guide/signing-integration/","section":"Developer Guide","summary":"","title":"Signing Integration Guide"},{"content":"This page describes the internal architecture of the LibreMiddleware signing engine for contributors who want to understand how the system works, add new signature formats, or debug signing issues.\nLooking to consume the signing engine from your own application? The public seam is LibreSCRS::Signing::SigningService — see Signing Integration Guide. The rest of this page covers what lives behind that seam.\nPublic seam: LibreSCRS::Signing #Downstream consumers never touch libresign::*. They link against LibreSCRS::Signing and call:\nSigningService(std::shared_ptr\u0026lt;Trust::TrustStoreService\u0026gt;, TsaProvider); SigningResult sign(const SigningRequest\u0026amp;, Auth::CredentialProvider, std::shared_ptr\u0026lt;Plugin::CardPlugin\u0026gt;, std::shared_ptr\u0026lt;SmartCard::CardSession\u0026gt;); LibreSCRS::Signing::SigningService is a thin pimpl-backed facade that performs request validation, resolves the trust snapshot from the injected TrustStoreService, drives the credential provider, and forwards to the internal libresign::SigningService. The libresign types described below are reachable only through this facade.\nTrust lifecycle (4.0): LibreSCRS::Trust::TrustStoreService #The trust subsystem has its own lifecycle owner separate from signing. TrustStoreService::create(TrustConfig) is [[nodiscard]] noexcept and returns std::expected\u0026lt;std::shared_ptr\u0026lt;TrustStoreService\u0026gt;, CreateError\u0026gt;. The factory builds the local anchor set synchronously (bundled thirdparty/certs/ + optional OS root store) and kicks off all configured Trusted-List fetches on internal worker threads. The signing service holds a shared_ptr\u0026lt;TrustStoreService\u0026gt; for its full lifetime, so eager TL fetches populate the same TrustStore that sign() observes; lazy fetches that happen during a sign() call merge into the same store, ensuring the certificate viewer, plugin registry, and signing service all share one trust universe.\nConsumers observe fetch progress via three orthogonal mechanisms:\nstatus() returns an AggregateStatus snapshot (Loading / Ready / Degraded). addObserver(cb) registers a callback for state transitions — the GUI-friendly path. waitForEagerFetches(deadline, token) blocks until every eager source settles; intended for tests, CLI tooling, and headless consumers. The service is move-disabled and reference-shared, so the same trust universe is naturally consumed by multiple signing services or non-signing tools (certificate viewer, plugin registry) running in the same process.\nEnd-to-End Signing Flow #The complete flow from user action to signed document:\n1. User clicks \u0026#34;Sign\u0026#34; in LibreCelik └─ SignPage wizard collects: document, format, level, TSA, visual params 2. LibreCelik builds SigningRequest and calls SigningService::sign() └─ PIN passed as span\u0026lt;const uint8_t\u0026gt; from SecureBuffer 3. NativeSigningService::sign() ├─ Opens Pkcs11Token (loads PKCS#11 module, logs in with PIN) ├─ Reads signing certificate + chain from token └─ Dispatches to format module based on request.format 4. Format module (e.g., PAdESModule::sign()) ├─ Prepares the signature container (PDF incremental save, CMS, XML, JSON, or ZIP) ├─ Computes document digest (SHA-256) ├─ Calls Pkcs11Token::sign(hash) → raw signature bytes from card ├─ Embeds signature + certificate chain into container └─ If level \u0026gt;= B-T: calls TSAClient::timestamp(hash) └─ Sends RFC 3161 request to TSA server └─ Embeds TimeStampToken in signature 5. If level \u0026gt;= B-LT: ├─ RevocationClient fetches CRL + OCSP responses for certificate chain └─ Format module embeds revocation data in signature 6. If level == B-LTA: └─ Archive timestamp added over entire signature + revocation data 7. NativeSigningService returns SigningResult { success, signedDocument } 8. LibreCelik saves the signed document to disk Module Structure #The signing engine lives in lib/libresign/ within LibreMiddleware. It is organized into three layers:\nPublic headers live under include/libresign/ (top-level types) and include/libresign/native/ (native backend classes). Implementations and backend-internal helpers live under src/.\nCore Service Layer # File Purpose include/libresign/signing_service.h SigningService abstract interface — configure(), sign(), isAvailable() include/libresign/signing_service_factory.h Factory function createSigningService(Backend) include/libresign/types.h Data types: SigningRequest, SigningResult, TrustConfig, TSAConfig, enums include/libresign/trust_store_manager.h TrustStoreManager — aggregates system, bundled, and TL-derived certificates across signing and non-signing consumers Format Modules #Each signature format is implemented as an independent module. All modules follow the same pattern: accept a document + certificate + signing callback, produce signed output bytes.\nModule File Standard PAdES src/native/pades_module.cpp PDF incremental save with CMS signature CAdES src/native/cades_module.cpp Detached CMS/PKCS#7 signature XAdES src/native/xades_module.cpp XML Digital Signature with XAdES properties JAdES src/native/jades_module.cpp JSON Web Signature with JAdES header ASiC-E src/native/asic_module.cpp ZIP container with XAdES signature (uses miniz) Format modules receive a Pkcs11Token\u0026amp; reference for signing operations. The token handles PKCS#11 session management, key lookup, and raw signing internally.\nInfrastructure # Component Files Purpose Pkcs11Token include/libresign/native/pkcs11_token.h + src/native/pkcs11_token.cpp PKCS#11 session management — module loading, login, key lookup, raw sign, certificate extraction TSAClient include/libresign/native/tsa_client.h + src/native/tsa_client.cpp RFC 3161 timestamp requests via HTTP (libcurl) RevocationClient include/libresign/native/revocation_client.h + src/native/revocation_client.cpp CRL and OCSP fetching for B-LT/B-LTA levels SigningProvider include/libresign/native/signing_provider.h + src/native/signing_provider.cpp Abstraction over Pkcs11Token for downstream consumers TrustedListParser include/libresign/native/trusted_list_parser.h + src/native/trusted_list_parser.cpp XML parser for EU Trusted Lists (LOTL and TL) TlCache (internal) src/native/tl_cache.h/.cpp Disk cache for downloaded Trusted List XML files TlSignatureVerifier (internal) src/native/tl_signature_verifier.h/.cpp XML-DSIG verification of Trusted List signatures PinnedTlCerts (internal) src/native/pinned_tl_certs.h/.cpp Compiled-in LOTL signing certificates used to bootstrap trust PDF parser (internal) src/native/pdf_parser.h/.cpp Minimal PDF parser for PAdES incremental save — finds xref, appends signature dictionary OpenSSL RAII (internal) src/native/openssl_raii.h RAII wrappers for OpenSSL types (BIO, X509, EVP_PKEY, etc.) Trust Model #The signing engine uses a three-tier trust model for certificate validation:\nTier 1: System Certificates #The operating system\u0026rsquo;s default certificate store. Used as the root of trust for TLS connections (TSA, CRL/OCSP endpoints) and as a fallback for certificate chain building.\nTier 2: Bundled Certificates #Certificates shipped with LibreMiddleware in thirdparty/certs/. These include root CAs for Serbian government PKI infrastructure that may not be in system stores. Used for card certificate chain verification.\nTier 3: Trusted List-Derived Certificates #Certificates extracted from EU Trusted Lists (TL/LOTL). The engine downloads and parses the EU List of Trusted Lists, follows links to national trusted lists, and extracts signing CA certificates. These are used for B-LT and B-LTA validation — they provide the trust anchors that connect the signer\u0026rsquo;s certificate to an EU-recognized trust service provider.\nAuthentication chain: The LOTL itself is signed. The engine verifies the LOTL signature using pinned certificates (pinned_tl_certs.cpp) that are compiled into the library. National TLs are verified using certificates found in the LOTL. This creates a chain: pinned cert verifies LOTL signature, LOTL provides certs that verify national TL signatures, national TLs provide trust service certificates.\nPinned LOTL signing certificates (compiled in) └─ verify → EU LOTL XML signature └─ contains → national TL signing certificates └─ verify → national TL XML signatures └─ contain → trust service provider certificates └─ validate → signer\u0026#39;s certificate chain DSS Validation Oracle #The project includes a DSS (Digital Signature Services) backend that can be used as a validation oracle in tests. DSS is the EU reference implementation for signature creation and validation, maintained by the European Commission.\nWhat it does: The DSS backend delegates signing to a running DSS server via REST API. This is useful for cross-validating that signatures produced by the native engine are accepted by the EU reference implementation.\nTest usage: When SIGNING_BACKEND=both is set, tests can create a signature with the native backend and validate it with DSS, or vice versa. This catches subtle format compliance issues that unit tests alone would miss.\nNote: The DSS backend is deprecated for production use. It exists solely as a test oracle. The native backend is the production signing engine.\nData Flow: LibreCelik to LibreMiddleware #LibreCelik (the GUI) and LibreMiddleware (the engine) are separate projects with a clean boundary. Here is how signing data crosses that boundary:\n┌─────────────────────────────────────────────────┐ │ LibreCelik (GUI) │ │ │ │ ┌────────────┐ ┌──────────────────────────┐ │ │ │ SignPage │───▶│ SigningRequest │ │ │ │ (wizard) │ │ ┌─ document bytes │ │ │ │ │ │ ├─ format (PAdES/CAdES/..)│ │ │ │ Collects: │ │ ├─ level (B-B/B-T/..) │ │ │ │ - file path │ │ ├─ TSA URL │ │ │ │ - format │ │ ├─ visual sig params │ │ │ │ - level │ │ └─ PIN (SecureBuffer) │ │ │ │ - PIN │ └──────────┬───────────────┘ │ │ └────────────┘ │ │ │ ▼ │ ├───────────────────────────────┬──────────────────┤ │ LibreMiddleware │ │ │ │ │ │ ┌────────────────────────────▼───────────────┐ │ │ │ NativeSigningService │ │ │ │ │ │ │ │ ┌──────────────┐ ┌───────────────────┐ │ │ │ │ │ Pkcs11Token │ │ Format Module │ │ │ │ │ │ (card I/O) │ │ (PAdES/CAdES/..) │ │ │ │ │ └──────┬───────┘ └────────┬──────────┘ │ │ │ │ │ raw sig │ signed doc │ │ │ │ ▼ ▼ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │ SigningResult { success, bytes, err } │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘ Key design decisions:\nNo Qt dependency — the entire signing engine is pure C++23 with no Qt types. LibreCelik converts between Qt types (QString, QByteArray) and standard types (std::string, std::vector\u0026lt;uint8_t\u0026gt;) at the boundary. No card protocol knowledge — the signing engine does not send APDU commands or know about card types. All card access goes through PKCS#11, which is a standard interface that any compliant token can satisfy. PIN is never stored — the PIN travels as a non-owning span\u0026lt;const uint8_t\u0026gt; from the GUI\u0026rsquo;s SecureBuffer through to the PKCS#11 C_Login call. No intermediate copy persists after the call returns. Format modules are stateless — each sign() call is self-contained. There is no session state between calls, making the engine safe for concurrent use from multiple threads. Adding a New Signature Format #To add a new format module:\nCreate include/libresign/native/newformat_module.h and src/native/newformat_module.cpp Implement a sign() function that accepts the document, Pkcs11Token\u0026amp;, and format-specific parameters Register the format in NativeSigningService::sign() by adding a case for the new SignatureFormat enum value Add the new enum value to SignatureFormat in types.h Add the source files to lib/libresign/CMakeLists.txt Write tests — use the DSS oracle (SIGNING_BACKEND=both) to validate format compliance ","date":null,"permalink":"https://librescrs.github.io/developer-guide/signing-architecture/","section":"Developer Guide","summary":"","title":"Signing Architecture"},{"content":"LibreSCRS — Libre Smart Card Reader System #Libre smart card tools for Linux \u0026amp; macOS.\nISO 7816 · PC/SC · PKCS#11 · PKCS#15 · ICAO 9303 · NIST PIV · BSI TR-03110\nGet Started For Developers Read Any Card #GUI smart card reader with plugin support. Passports, e-passports, eID, vehicle registration, PIV, and other PKI cards.\nExtend With Plugins #Build middleware and GUI plugins for new card types. Standards-based modern C++ architecture (C++23 middleware, C++20 GUI).\nSupport LibreSCRS #Help keep smart card tools free and open.\nSupport the Project Licence # LibreCelik: GPL-3.0-or-later LibreMiddleware: LGPL-2.1-or-later ","date":null,"permalink":"https://librescrs.github.io/","section":"","summary":"","title":""},{"content":"LibreSCRS 4.0.0 lands today. After two release candidates and extensive community testing across Linux, macOS, and Windows, both LibreMiddleware 4.0.0 and LibreCelik 4.0.0 carry GPG-signed git tags and cosign-signed release artifacts.\nWhat\u0026rsquo;s in 4.0 # C++23 core with std::expected-based fallible-factory API surface, LocalizedText i18n metadata, and CancelToken for cooperative cancellation. Native PAdES / XAdES / JAdES / CAdES / ASiC-E signing via LibreSCRS::Signing::SigningService — no external signing daemon. Auto-fit visual signature layout (layoutVisualSignature) — preview in the wizard renders pixel-exact against the embedded PAdES output. RFC 5280 trust chain validation with eIDAS qcStatements conformance. eMRTD PACE for contactless passport reading; CardEdge, PKCS#15, PIV providers; OpenSC fallback in the card-plugin path. Bundled CardEdge OpenSC external driver built against OpenSC 0.26.1 and 0.27.1, available as a sidecar tarball until upstream OpenSC ships our srbeid driver in a numbered release. Known issue — multi-card PIN routing #When more than one Serbian smart card is inserted simultaneously and the user initiates a signing flow, the PIN entry prompt may be associated with a different slot than the certificate selected. Single-card setups are unaffected. The fix is architectural — a Card+Slot model — and is the headline feature of 4.1.0, already implemented on the development branch.\nDownloads # LibreMiddleware 4.0.0 release LibreCelik 4.0.0 release All artifacts ship with .sigstore.json cosign signatures; verify with cosign verify-blob --bundle \u0026lt;name\u0026gt;.sigstore.json \u0026lt;name\u0026gt;.\n","date":null,"permalink":"https://librescrs.github.io/news/2026-05-librescrs-4-0-0/","section":"News","summary":"LibreMiddleware 4.0.0 + LibreCelik 4.0.0 released. C++23 core, ABI v6, native PAdES/XAdES/JAdES/CAdES/ASiC-E signing, RFC 5280 trust chain, eMRTD PACE, Serbian eID + AET SafeSign + GEO testna + PIV + generic PKCS#15 cards.","title":"LibreSCRS 4.0.0 — first stable of the 4.0 cycle"},{"content":"","date":null,"permalink":"https://librescrs.github.io/news/","section":"News","summary":"","title":"News"},{"content":"LibreSCRS started as a reverse-engineering effort to liberate Serbian government smart cards from Windows-only proprietary software. Through protocol analysis and AI-assisted development, we built open-source tools that now support any smart card — from national eIDs to vehicle registration documents and eMRTD e-passports.\nTimeline # Mid 2025 — Project begins: LibreCelik, GUI reader for Serbian eID and vehicle cards Early 2026 — LibreMiddleware extracted as standalone Qt-free library (no Qt dependency) Early 2026 — PKCS#11 module ships; OpenSC Serbian eID driver contributed upstream (PR #3595, merged) Early 2026 — Plugin architecture: both middleware and GUI become card-agnostic 2026 — eMRTD, PKCS#15, and PIV support: e-passports, any PKCS#15-based PKI card, PIV smart cards. PKCS#11 module generalized to support all card types. Universal toolkit. May 2026 — 4.0.0 release: C++23 core, native PAdES / XAdES / JAdES / CAdES / ASiC-E signing, RFC 5280 trust chain validation, eIDAS qcStatements conformance, auto-fit visual signature layout. The Story #There was no native Linux application that could read all types of Serbian government smart cards. Existing open-source projects — JFreesteel (Java) and Bas Celik (Go) — proved that reading citizen data from eID cards was possible without proprietary software, but they focused on demographic data. Nobody had tackled the cryptographic side: the CardEdge PKI applet that handles certificates, digital signatures, and PIN management. LibreSCRS set out to cover both — data reading and full PKI support — across all Serbian card types, on Linux, macOS, and Windows.\nSerbian eID cards have no public protocol documentation. The approach was straightforward: trace what the proprietary Windows software does over PC/SC, capture the APDU sequences, and work out the protocol from there. Vehicle registration cards follow the EU Directive 2003/127/EC standard, so their structure was well-defined from the start. Health insurance cards have their own layout. PKS qualified signature cards from the Chamber of Commerce use CardEdge for cryptographic operations but carry no demographic data at all. Each card type meant testing against real hardware until the protocol was understood.\nAs the codebase grew, it became clear that the card communication logic needed to stand on its own. LibreMiddleware was extracted as a pure modern C++ library — no Qt, no GUI dependencies — just smart card protocols, TLV parsing, and a clean API. This made it possible to build a PKCS#11 module that lets any application (Firefox, Chrome, OpenSSL CLI) use any LM-supported smart card (Serbian eID, PKS, PIV, generic PKCS#15) for authentication and digital signatures without the GUI.\nThe next step was a plugin architecture. Instead of hardcoding support for specific cards, both the middleware and the GUI became extensible. CardPluginRegistry discovers card handlers at runtime via dlopen; CardWidgetPluginRegistry loads GUI plugins via QPluginLoader. Adding a new card type means dropping in a shared library — no recompilation needed.\nMost recently, support was added for eMRTD e-passports and PKCS#15 generic cards. eMRTD required implementing BAC and PACE key agreement from the ICAO 9303 specification — Diffie-Hellman over elliptic curves, key derivation functions, and Secure Messaging with session keys. PKCS#15 adds the ability to discover and use certificates and keys on any compliant card.\nAI-Assisted Development #This project was built with AI as a development partner — from analyzing hex dumps of unknown APDU responses to generating TLV parsers and validating PACE cryptography implementations. Every line was verified against real hardware.\nProjects # Project Description License LibreCelik Qt6 desktop GUI smart card reader GPL-3.0 LibreMiddleware C++23 smart card middleware libraries LGPL-2.1 Related Projects #LibreSCRS is not the first open-source effort to liberate Serbian smart cards. We stand on the shoulders of those who came before:\nJFreesteel — the pioneering open-source Serbian eID library by Goran Rakic (Java, 2015). JFreesteel proved it was possible to read Serbian eID cards without proprietary software and inspired others to follow. Bas Celik — a actively maintained Go desktop reader for Serbian eID, vehicle, and health cards by Nikola Ubavic. A great alternative if you prefer a Go-based tool. Contributing #See the Contribute page for how to report bugs, submit pull requests, and add support for new card types.\nSupporting the Project #LibreSCRS is developed and maintained in spare time. If these tools are useful to you, please consider supporting the project.\nDonate via Open Source Collective\n","date":null,"permalink":"https://librescrs.github.io/about/","section":"About","summary":"","title":"About"},{"content":"LibreSCRS consists of two main projects that work together to read, process, and display smart card data.\nComponents #LibreMiddleware (LGPL-2.1) #A Qt-free C++23 static library collection for smart card communication. It handles everything from low-level APDU command/response exchanges to high-level card data extraction. All PC/SC communication lives exclusively in LibreMiddleware — LibreCelik has no direct dependency on PC/SC.\nPublic CMake targets #Downstream consumers link against the LibreSCRS::* alias surface — a stable public API that hides internal libraries behind PascalCase namespaces:\nTarget Namespace Purpose LibreSCRS::SmartCard LibreSCRS::SmartCard CardSession, MonitorService — PC/SC + monitor LibreSCRS::Plugin LibreSCRS::Plugin CardPlugin, CardPluginService, CardData, ReadResult LibreSCRS::Auth LibreSCRS::Auth CredentialProvider, AuthRequirement, CredentialResult LibreSCRS::Certificate LibreSCRS::Certificate X.509 certificate parsing and metadata LibreSCRS::Trust LibreSCRS::Trust TrustStoreService, TrustStore, TrustConfig LibreSCRS::Signing LibreSCRS::Signing SigningService, SigningRequest, SigningResult, VisualSignatureLayout LibreSCRS::Secure LibreSCRS::Secure Secure::Buffer, Secure::String (zero-on-destruct) The table below lists the internal bucket-B libraries that implement these public targets. The internal libraries are subject to change between releases and should not be linked directly by downstream code.\nLibrary Purpose smartcard PCSCConnection, Monitor (card event polling), APDU command/response, TLV and BER-TLV parsing (ISO 7816-4), TransmitFilter (transparent Secure Messaging layer) plugin CardPlugin interface (with streaming support), CardData model, CardPluginRegistry (dlopen), AutoReader (Monitor + plugin discovery bridge) pkcs15 Generic PKCS#15/ISO 7816-15 parser and card library — EF.ODF/EF.DIR discovery, certificate and key enumeration. Works with any PKCS#15-compliant card rs-eid Serbian eID card API with card reader implementations (Apollo 2008, Gemalto 2014+, Foreigner IF2020) eu-vrc EU Vehicle Registration Certificate (Directive 2003/127/EC) rs-health Serbian health insurance card (RFZO) cardedge CardEdge PKI applet for Serbian smart cards — certificates, PIN management, digital signing emrtd eMRTD e-passport communication — data group reading, MRZ parsing emrtd-crypto eMRTD cryptography — BAC, PACE (ECDH-GM), Secure Messaging piv PIV card communication (NIST SP 800-73) pkcs11 PKCS#11 shared library (librescrs-pkcs11) — supports all card types *-plugin Card plugins (.so): rs-eid, rs-health, eu-vrc, emrtd, piv, pkcs15, cardedge, opensc LibreCelik (GPL-3.0) #A Qt6 desktop GUI application that displays card data. It is a pure presentation layer — no PC/SC, no APDU, no card protocol knowledge. It receives CardData from middleware plugins and renders it through GUI plugins.\nModule Purpose smartcard SmartCardReaderListener — Qt adapter wrapping middleware smartcard::Monitor plugin CardWidgetPlugin interface and CardWidgetPluginRegistry (QPluginLoader) asynccardreader Generic async card reader using middleware CardPlugin fallback chain document Shared PKI UI (TokenSection), PIN change dialog, printing certificate X.509 certificate viewer (tree model + dialog) plugins/ GUI plugins (rs-eid, rs-health, eu-vrc, emrtd, piv, token) as Qt MODULE .so files LibreCelik fetches LibreMiddleware via CMake FetchContent. For local development, you can point it to a local checkout instead.\nPlugin Architecture #The entire system is built around plugins. There are two independent plugin layers — middleware plugins handle card communication, GUI plugins handle display. They connect through CardData, a universal data model that any middleware plugin produces and any GUI plugin consumes.\n┌─────────────────────────────────────────────────────────┐ │ LibreCelik (GUI) │ │ │ │ ┌──────────────────────┐ ┌────────────────────────┐ │ │ │SmartCardReaderListener│ │ CardWidgetPluginRegistry│ │ │ │(Qt adapter for Monitor│ │ (QPluginLoader) │ │ │ └──────────────────────┘ └──────────┬─────────────┘ │ │ ▲ │ loads │ │ │ wraps ┌──────▼──────────────┐ │ │ │ │ GUI Plugins (.so) │ │ │ │ │ ┌──────┐ ┌────────┐ │ │ │ │ │ │rs-eid│ │eu-vrc │ │ │ │ │ │ ├──────┤ ├────────┤ │ │ │ │ │ │rs- │ │ emrtd │ │ │ │ │ │ │health│ ├────────┤ │ │ │ │ │ ├──────┤ │ piv │ │ │ │ │ │ │token │ └────────┘ │ │ │ │ │ └──────┘ │ │ │ │ └─────────────────────┘ │ │ │ ▲ │ │ │ │ CardData │ ├───────────┼──────────────────────────┼──────────────────┤ │ │ LibreMiddleware│ │ │ │ │ │ │ ┌────────┴─────────┐ ┌─────────────┴──────────┐ │ │ │ smartcard::Monitor│ │ CardPluginRegistry │ │ │ │ (PC/SC polling) │ │ (dlopen) │ │ │ └──────────────────┘ └──────────┬─────────────┘ │ │ │ loads │ │ ┌───────────────────────────────▼─────────────────┐ │ │ │ Middleware Plugins (.so) │ │ │ │ ┌─────────┐ ┌──────────┐ ┌───────────────────┐ │ │ │ │ │ rs-eid │ │ emrtd │ │ opensc │ │ │ │ │ ├─────────┤ ├──────────┤ │ (PKI fallback) │ │ │ │ │ │ eu-vrc │ │ cardedge │ └───────────────────┘ │ │ │ │ ├─────────┤ ├──────────┤ ┌───────────────────┐ │ │ │ │ │rs-health│ │ piv │ │ pkcs15 │ │ │ │ │ └─────────┘ └──────────┘ └───────────────────┘ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ APDU (ISO 7816-4) │ │ ▼ │ │ ┌──────────────┐ │ │ │PCSCConnection│ │ │ │ (PC/SC) │ │ │ └──────┬──────┘ │ └──────────────────────────┼─────────────────────────────┘ │ ┌──────▼──────┐ │ Smart Card │ └─────────────┘ Card Detection: smartcard::Monitor #Card detection lives in LibreMiddleware as smartcard::Monitor — a pure C++23 class with no Qt dependency. It polls PC/SC for card insert/remove events on a background thread and notifies subscribers via callbacks:\nsubscribe(MonitorCallback) — register for MonitorEvent notifications (card inserted/removed, reader name, ATR) unsubscribe(id) — stop receiving events The monitor lazy-starts its polling thread on the first subscription and stops when the last subscriber unsubscribes. In LibreCelik, SmartCardReaderListener wraps the monitor and marshals events to the Qt main thread via signals.\nMiddleware Plugins (CardPlugin) #A middleware plugin is a shared library (.so / .dylib) loaded by CardPluginRegistry via dlopen at runtime. Each plugin implements:\ncanHandle(const std::vector\u0026lt;uint8_t\u0026gt;\u0026amp; atr) — fast ATR-only check. Returns bool — does this plugin recognize the ATR? No card communication needed. Plugins also expose probePriority() (an int) so the registry can rank candidates. canHandleConnection(PCSCConnection\u0026amp; conn) — live connection probe. Sends SELECT commands for known AIDs to confirm card support. Called only on plugins that did not match in canHandle() — giving them a second chance to claim the card via live communication. readCard(PCSCConnection\u0026amp; conn) — extract all data from the card. Sends APDU commands, parses TLV/BER-TLV responses, and returns a CardData object containing typed field groups (personal data, document data, photos, certificates). Two-phase probe: The registry first calls canHandle(atr) on all plugins — those that return true go into the candidate list immediately, ranked by probePriority(). Then canHandleConnection(conn) is called only on plugins that returned false in Phase 1, giving generic plugins (like OpenSC) a chance to claim the card by probing the live connection. This avoids unnecessary card communication for plugins that already matched by ATR.\nStreaming support: Plugins can implement readCardStreaming() in addition to readCard(). Streaming delivers CardData incrementally — fields appear in the GUI as they are read from the card, rather than waiting for the entire read to complete. This is especially useful for eMRTD where data groups are read sequentially through encrypted channels.\nFallback chain: If the top-ranked plugin\u0026rsquo;s readCard() fails, the next candidate is tried automatically. The OpenSC plugin serves as a generic fallback for any card it doesn\u0026rsquo;t natively support.\nPKI fallback: Data plugins (eID, vehicle, health) read demographic data but do not handle PKI operations. After a data plugin finishes, the system can trigger a separate PKI plugin (CardEdge, PKCS#15, or OpenSC) to provide certificate discovery, signing, and PIN management. This decoupling means data plugins don\u0026rsquo;t need to know about PKI.\nTo add a new card type: Write a class that inherits CardPlugin, implement canHandle(), canHandleConnection(), and readCard() (optionally readCardStreaming()), build as a shared library, and drop it into the plugin directory. The registry discovers it automatically at next startup.\nGUI Plugins (CardWidgetPlugin) #A GUI plugin is a Qt shared library loaded by CardWidgetPluginRegistry via QPluginLoader. Each plugin implements:\ncardType() — returns the card type string this plugin can display (must match what the middleware plugin sets in CardData). createWidget(const CardData\u0026amp;, QWidget* parent) — builds and returns a Qt widget that renders the card data. Full control over layout — text fields, photos, certificates, whatever the card contains. To add a new card display: Write a class that inherits CardWidgetPlugin and Q_PLUGIN_METADATA, implement cardType() and createWidget(), build as a Qt MODULE library.\nHow They Connect #Adding support for a completely new card type requires two plugins:\nMiddleware plugin — knows how to talk to the card (SELECT, READ BINARY, parse response) GUI plugin — knows how to display the data (layout, labels, formatting) The bridge is CardData — a map of field groups, where each group contains key-value pairs. The middleware plugin populates it, the GUI plugin reads it. Neither needs to know about the other. No recompilation of the core application is needed — just drop in the .so files.\neMRTD: Cryptographic Card Access #The eMRTD plugin demonstrates the most complex card communication in the system. E-passports require cryptographic key agreement before any data can be read:\nBAC (Basic Access Control) — derives session keys from the Machine Readable Zone (MRZ) printed on the passport PACE (Password Authenticated Connection Establishment) — elliptic curve Diffie-Hellman key agreement, the modern replacement for BAC. May require the user to enter the CAN (Card Access Number) printed on the passport. Secure Messaging — all subsequent APDU commands and responses are encrypted and MACed with session keys. Implemented as a TransmitFilter on PCSCConnection — once installed, encryption is transparent to all higher-level code. The emrtd-crypto library implements these protocols from the ICAO 9303 specification, while the emrtd library handles data group reading and MRZ parsing. The emrtd-plugin ties them together as a standard CardPlugin with streaming support — data groups are read progressively and delivered to the GUI as they become available. From the rest of the system\u0026rsquo;s perspective, it\u0026rsquo;s just another plugin that returns CardData.\nData Flow #The complete flow when a smart card is inserted:\n1. Card inserted into reader 2. smartcard::Monitor (LibreMiddleware, PC/SC polling thread) └─ SCardGetStatusChange detects card presence └─ creates MonitorEvent { CardInserted, readerName, atr } └─ notifies subscribers via callback 3. SmartCardReaderListener (LibreCelik, Qt adapter) └─ receives MonitorEvent on monitor thread └─ marshals to Qt main thread via QMetaObject::invokeMethod └─ emits signal with MonitorEvent 4. Main window receives signal, starts two-phase plugin discovery: Phase 1 — ATR filtering (no card communication): └─ CardPluginRegistry::findAllCandidates(atr, connection) ├─ rs-eid-plugin::canHandle(atr) → true (recognized Serbian eID ATR) ├─ eu-vrc-plugin::canHandle(atr) → false ├─ emrtd-plugin::canHandle(atr) → false └─ opensc-plugin::canHandle(atr) → false Candidates so far: [rs-eid-plugin (priority 100)] Phase 2 — connection probe (only on plugins that returned false): ├─ eu-vrc-plugin::canHandleConnection(conn) → false ├─ emrtd-plugin::canHandleConnection(conn) → false └─ opensc-plugin::canHandleConnection(conn) → true (found PKCS#15) Final candidates: [rs-eid-plugin (100), opensc-plugin (50)] 5. AsyncCardReader::requestData(topCandidate) └─ std::async → background thread └─ rs-eid-plugin::readCard(connection) ├─ SELECT AID, READ BINARY personal data ├─ parse BER-TLV response └─ return CardData { type: \u0026#34;rs.eid\u0026#34;, fields: {...} } 6. Result marshalled back to Qt main thread (QMetaObject::invokeMethod) 7. CardWidgetPluginRegistry::findByCardType(\u0026#34;rs.eid\u0026#34;) └─ rseid-gui-plugin matches 8. rseid-gui-plugin::createWidget(cardData, parent) └─ builds widget: photo, name, address, document number, certificates 9. Widget displayed in main window Design Patterns #Strategy #CardReaderBase defines the interface for communicating with a specific card reader chip. Concrete implementations — CardReaderApollo (Apollo 2008 chips) and CardReaderGemalto (Gemalto 2014+ chips) — encapsulate the differences in APDU sequences and file structures.\nAsync #AsyncCardReader wraps middleware plugin calls with std::async to keep the GUI responsive. Results are marshalled back to the Qt main thread via QMetaObject::invokeMethod and Qt signals. Supports both batch (readCard) and streaming (readCardStreaming) modes. In the middleware layer, AutoReader provides a convenience wrapper that bridges smartcard::Monitor events directly to CardPluginRegistry discovery and card reading — useful for applications that want automatic card handling without manual wiring.\nObserver #smartcard::Monitor uses a callback-based subscription model in the middleware layer. In the GUI layer, SmartCardReaderListener wraps the monitor and re-emits events as Qt signals, propagating card insert/remove events to the main window and any registered listeners.\nSingleton #SmartCardReaderListener::instance() provides a single dispatch point for card events across the GUI application.\nNamespaces #Public API (LibreSCRS::*) #The 4.0 public consumer surface lives under the LibreSCRS:: root in PascalCase namespaces. Every header under include/LibreSCRS/ belongs to this surface; all other namespaces are internal.\nNamespace Scope LibreSCRS::SmartCard CardSession, MonitorService — PC/SC + card-event monitor LibreSCRS::Plugin CardPlugin, CardPluginService, CardData, ReadResult, AutoReaderService LibreSCRS::Auth CredentialProvider, AuthRequirement, CredentialResult, FieldDescriptor LibreSCRS::Certificate X.509 certificate parsing and metadata LibreSCRS::Trust TrustStoreService, TrustStore, TrustConfig LibreSCRS::Signing SigningService, SigningRequest, SigningResult, VisualSignatureLayout LibreSCRS::Secure Secure::Buffer, Secure::String (zero-on-destruct) LibreSCRS (root) CancelToken, LocalizedText, SyncProvider Internal namespaces (subject to change) #The lowercase namespaces below live under lib/\u0026lt;library\u0026gt;/ and back the public API. They are not part of the supported consumer surface — names and signatures may change between releases.\nNamespace Scope smartcard:: Internal APDU / TLV / BER-TLV / PCSCConnection helpers plugin:: Internal plugin loading + registry helpers eidcard:: Serbian eID internal types euvrc:: EU Vehicle Registration internal types healthcard:: Serbian health insurance internal types cardedge:: CardEdge PKI applet internal types emrtd:: eMRTD internal data structures and MRZ parsing emrtd::crypto eMRTD cryptography — BAC, PACE, Secure Messaging piv:: PIV internal types pkcs15:: PKCS#15 internal parser types libresign:: Signing engine internals (PAdES / XAdES / JAdES / CAdES / ASiC-E) Standards #The following standards are relevant to the codebase:\nStandard Usage ISO 7816-4 Smart card communication — APDU command/response structure, TLV and BER-TLV encoding PC/SC Reader access layer — card detection, connection management, transaction control PKCS#11 Cryptographic token interface — browser authentication, digital signatures PKCS#15 Cryptographic information application — certificate and key discovery on smart cards ICAO 9303 eMRTD (e-passports) — BAC and PACE key agreement, Secure Messaging, data group structure NIST SP 800-73 PIV card interface — certificate discovery, authentication, digital signing BSI TR-03110 PACE protocol — password-authenticated key agreement for eMRTD ","date":null,"permalink":"https://librescrs.github.io/developer-guide/architecture/","section":"Developer Guide","summary":"","title":"Architecture Overview"},{"content":"Prerequisites # Dependency Version Notes CMake 3.24+ Build system C++ compiler GCC 13+ or Clang 17+ C++23 support required for LibreMiddleware; LibreCelik still targets C++20 (bump to C++23 is on the 4.x roadmap) Qt 6.6+ Widgets, PrintSupport, LinguistTools LibreCelik only PC/SC libpcsclite-dev (Linux) Built-in on macOS OpenSSL 3 — Bundled in LibreMiddleware thirdparty/ UUID uuid-dev (Linux) UUID generation Building LibreMiddleware #LibreMiddleware is a standalone C++23 library with no Qt dependency.\ngit clone https://github.com/LibreSCRS/LibreMiddleware.git cd LibreMiddleware cmake -B build cmake --build build Run the test suite:\ncd build \u0026amp;\u0026amp; ctest --output-on-failure Building LibreCelik #LibreCelik is the Qt6 GUI application. It fetches LibreMiddleware automatically via CMake FetchContent.\ngit clone https://github.com/LibreSCRS/LibreCelik.git cd LibreCelik cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build Local Development #When working on both projects simultaneously, point LibreCelik to your local LibreMiddleware checkout instead of fetching from GitHub:\ncmake -B build -DFETCHCONTENT_SOURCE_DIR_LIBREMIDDLEWARE=/path/to/LibreMiddleware cmake --build build This way changes to LibreMiddleware are picked up immediately without committing or pushing.\nRunning Tests #Both projects use Google Test (auto-fetched via CMake). Run all tests with:\ncd build \u0026amp;\u0026amp; ctest --output-on-failure To run a single test:\ncd build \u0026amp;\u0026amp; ctest -R \u0026lt;test_name\u0026gt; --output-on-failure Disable tests entirely with:\ncmake -B build -DBUILD_TESTING=OFF LibreCelik test runner gotcha #ctest may report \u0026ldquo;No tests found\u0026rdquo; due to a gtest_discover_tests timing issue. If this happens, run the test binaries directly:\ncd build ./test/LibreCelikTests ./test/CardWidgetPluginRegistryTests ./test/AsyncCardReaderTests ","date":null,"permalink":"https://librescrs.github.io/developer-guide/building-from-source/","section":"Developer Guide","summary":"","title":"Building From Source"},{"content":"","date":null,"permalink":"https://librescrs.github.io/categories/","section":"Categories","summary":"","title":"Categories"},{"content":"The project is open source. Here\u0026rsquo;s how to contribute.\nWays to Contribute # Report a bug — LibreCelik issues, LibreMiddleware issues Suggest a feature — open an issue on the relevant repository Submit a Pull Request — see below for setup and conventions Development Setup #Both projects build with CMake 3.24+. LibreMiddleware requires a C++23 compiler (GCC 13+ / Clang 17+); LibreCelik still targets C++20 (bump to C++23 is on the 4.x roadmap).\n# Clone both repositories git clone https://github.com/LibreSCRS/LibreCelik.git git clone https://github.com/LibreSCRS/LibreMiddleware.git # Build LibreMiddleware cd LibreMiddleware cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build # Build LibreCelik with local LibreMiddleware cd ../LibreCelik cmake -B build -DCMAKE_BUILD_TYPE=Release \\ -DFETCHCONTENT_SOURCE_DIR_LIBREMIDDLEWARE=../LibreMiddleware cmake --build build For details, see Building From Source.\nCoding Standards # LibreMiddleware: C++23. Use std::span, std::format, std::expected, smart pointers. LibreCelik: C++20. Compiler warnings: -Wall -Wextra -Wpedantic Naming: camelCase for variables, PascalCase for types. No trailing underscores on member variables. SPDX license headers on all source files. Every change must include tests. Pull Request Process # Fork the repository Create a feature branch Make your changes with tests Push and open a Pull Request Describe what the change does and why CI must pass Code review before merge Adding Support for a New Card #If you want to add support for a new smart card type:\nAnalyze the card — use the card_mapper CLI tool (part of LibreMiddleware) to explore the card\u0026rsquo;s file system and APDU responses. This is a useful first step to understand what the card contains.\nMiddleware plugin — implement the CardPlugin interface in LibreMiddleware. This handles card detection (ATR matching or connection probe) and data reading.\nGUI plugin — implement the CardWidgetPlugin interface in LibreCelik. This provides the Qt6 widget that displays the card data.\nSee the Architecture Overview for details on the plugin system and interfaces.\nDevelopment Process # AI-assisted development — we use AI tools as part of the development workflow Test-driven development Code review on every pull request CI pipeline runs tests on all supported platforms ","date":null,"permalink":"https://librescrs.github.io/contribute/","section":"Contribute","summary":"","title":"Contribute"},{"content":"This guide is for developers who want to understand the LibreSCRS internals, build from source, or extend the system with new card plugins.\nTopics # Architecture Overview — system components, data flow, plugin system, and design patterns Signing Architecture — native signing engine, format support, and certificate handling Signing Integration Guide — integrating digital signing into applications using LibreMiddleware Building From Source — prerequisites, build instructions, and running tests For plugin API details, see the CardPlugin and CardWidgetPlugin interfaces in the Architecture Overview.\n","date":null,"permalink":"https://librescrs.github.io/developer-guide/","section":"Developer Guide","summary":"","title":"Developer Guide"},{"content":"Smart cards with private keys can be used for digital signatures. The fastest way to sign a document is the built-in signing wizard in LibreCelik. This page also covers command-line signing and third-party PKCS#11 clients. The examples below use Serbian eID and PKS cards, but the same workflow applies to any card supported by LibreCelik, OpenSC, or the LibreSCRS PKCS#11 module.\nSigning in LibreCelik (recommended) #LibreCelik 4.0 ships a native signing wizard that produces EU eIDAS / ETSI baseline signatures directly from your smart card — no external signing service, no PDF tooling, no manual hash-and-sign dance. The wizard handles PAdES (PDF), CAdES (.p7s), XAdES (.xml), JAdES (.json), and ASiC-E (.asice) containers at the four conformance levels (B-B, B-T, B-LT, B-LTA).\nTo sign a document:\nInsert your signing card and open LibreCelik. Wait until the card appears in the main window. Open File → Sign document… (or the \u0026ldquo;Sign\u0026rdquo; action on the card view). Pick the document. Drag a file into the drop zone, or click Browse. Choose the signature format (PAdES for PDFs, ASiC-E for any other file type, CAdES / XAdES / JAdES if you have a specific requirement) and the conformance level (B-B for offline signing, B-T for a trusted timestamp, B-LT / B-LTA for long-term archive signatures — these additionally require an internet connection to fetch revocation data from CRL / OCSP). (PAdES only) Place the visual signature. Pick a page, drag the placement rectangle, and the wizard auto-fits the text. The preview is pixel-exact against the final embedded PDF. Select the signing certificate — the wizard lists all signing certificates discovered on the card. PIN status (good / blocked / retries-left) is shown next to each. Enter your PIN when prompted, then choose the output path. The wizard produces the signed file and shows the verification summary (signer, chain, timestamps). The wizard uses the LibreSCRS PKCS#11 module under the hood, so the same trust anchors and revocation behaviour apply whether you sign through the GUI or one of the command-line workflows below.\nPrerequisites #You need one of the following:\nOpenSC with built-in Serbian card support — just install OpenSC, nothing else needed. (The srbeid driver has been merged into OpenSC mainline but is not yet included in a release. If you build OpenSC from source, it works now.) OpenSC + LibreSCRS external driver — use this until the built-in driver is released. Gives you full OpenSC CLI tools (pkcs15-crypt, pkcs11-tool, etc.). See OpenSC integration. LibreSCRS PKCS#11 module — works without OpenSC. For browser authentication and signing via any PKCS#11 application. See PKCS#11 setup. Command-line signing with OpenSC #Sign a file #pkcs15-crypt --sha-256 expects a pre-computed binary hash as input, not the raw message.\n# Compute the hash openssl dgst -sha256 -binary /path/to/message.txt \u0026gt; /tmp/hash.bin # Sign with key 02 (Digital Signature) pkcs15-crypt --sign --pkcs1 --sha-256 --key 02 \\ --input /tmp/hash.bin --output /tmp/sig.bin Verify the signature ## Extract the public key from the certificate # Note: pkcs15-tool outputs PEM on Linux, DER on macOS pkcs15-tool --read-certificate 02 --output /tmp/cert.pem openssl x509 -in /tmp/cert.pem -pubkey -noout \u0026gt; /tmp/pubkey.pem # Verify openssl dgst -sha256 -verify /tmp/pubkey.pem \\ -signature /tmp/sig.bin /path/to/message.txt # Verified OK Using pkcs11-tool #You can also sign using pkcs11-tool with either the LibreSCRS PKCS#11 module or OpenSC\u0026rsquo;s module:\npkcs11-tool --module /usr/local/lib/librescrs-pkcs11.so \\ --sign --mechanism RSA-PKCS --id 02 \\ --input-file /tmp/hash.bin --output-file /tmp/sig.bin Browser authentication (eUprava) #For authenticating to government services like eUprava, set up the PKCS#11 module in Firefox. See the PKCS#11 Firefox setup guide.\nWhen you visit a site that requires client certificate authentication, Firefox will prompt you to select a certificate and enter your PIN.\nPDF signing in third-party readers #If you prefer a different PDF reader, most PKCS#11-aware readers can use your smart card via the LibreSCRS PKCS#11 module. Examples:\nOkular (KDE) — supports PKCS#11 via NSS Adobe Reader (if available on your platform) These paths are useful when you need a viewer-integrated signing flow that isn\u0026rsquo;t covered by LibreCelik\u0026rsquo;s wizard. If you test one of them with a specific card and have feedback, please share your findings.\nEmail signing (S/MIME) #Thunderbird and other email clients that support PKCS#11 security devices can use your smart card certificates for S/MIME email signing and encryption. Load the PKCS#11 module in Thunderbird the same way as in Firefox — see PKCS#11 setup.\nWhether the certificates on your card are suitable for S/MIME depends on their key usage extensions. If you have tested this, please share your results.\n","date":null,"permalink":"https://librescrs.github.io/user-guide/digital-signing/","section":"User Guide","summary":"","title":"Digital Signing"},{"content":"LibreCelik #GUI smart card reader for Linux \u0026amp; macOS. Reads passports, e-passports, eID, vehicle registration, PIV, and other PKI cards through plugins.\nDownload AppImage (Linux) Download DMG (macOS) PKCS#11 Module #Browser authentication and digital signing for smart cards with CardEdge PKI applet — Serbian eID, health insurance, and PKS (Chamber of Commerce) cards. Works with Firefox, Chrome, Thunderbird, and SSH.\nMerged into OpenSC mainline (PR #3595); not yet in a numbered OpenSC release. This module remains the recommended option until the next OpenSC release that includes the built-in driver.\nDownload Linux (tar.gz) Download macOS (zip) OpenSC External Driver #Serbian eID, health insurance, and PKS card driver for OpenSC. Enables PKCS#11 access through OpenSC\u0026rsquo;s infrastructure.\nTemporary — covers OpenSC 0.26.x / 0.27.x users until the next OpenSC release that bundles the built-in driver from PR #3595 (merged into mainline, not yet in a numbered release).\nDownload Linux Download macOS ","date":null,"permalink":"https://librescrs.github.io/downloads/","section":"Downloads","summary":"","title":"Downloads"},{"content":"Supported Cards #eMRTD / ePassport #Any passport or national ID card compliant with ICAO 9303. PACE and BAC authentication, Secure Messaging, biometric data (photo, MRZ), data groups. Passive, Chip, and Active Authentication for document authenticity verification.\nSerbian eID #Gemalto 2014+, IF2020 Foreigner. Personal data, address, document info, photo. Digital signature certificates and PIN management via CardEdge.\nSerbian Vehicle Registration (EU VRC) #EU Directive 2003/127/EC compliant. Owner, vehicle data, registration dates — all EU mandatory and optional fields. Print support.\nSerbian Health Insurance (RFZO) #Insured person, employer, insurance details.\nPIV (NIST SP 800-73) #US federal ID standard. Certificates, photo, fingerprints, PIN management.\nPKCS#15 Compatible Cards #Generic PKI standard for certificate discovery, PIN management, and digital signing. Covers Gemalto, CardEdge, and other compliant cards — including PKI on eMRTD-capable national ID cards.\nCapabilities # Automatic card detection — insert a card, LibreCelik identifies it and shows the data. No manual selection needed. Progressive reading — data appears as it is read from the card. No waiting for the full read to finish. Print — card data views support formatted printout. Multi-PIN management — cards with multiple PINs (e.g., separate authentication and signing PINs) show each PIN\u0026rsquo;s status and allow independent change. Plugin architecture — add support for new card types by dropping in a shared library. Both middleware (card communication) and GUI (data display) are extensible. Multilingual — English and Serbian (Cyrillic) interface. PKCS#11 module — universal cryptographic token interface supporting CardEdge, PKCS#15, and PIV cards. Use in Firefox, Chrome, SSH, and email signing. OpenSC integration — Serbian CardEdge driver merged into OpenSC mainline. External driver available for current OpenSC releases. For Developers #LibreMiddleware is a set of C++23 static libraries with no Qt dependency. Use them to build your own smart card application.\nPlugin API for adding new card types Streaming card read API SmartCard Monitor for event-driven card detection Secure Messaging, PACE, BAC implementations See the Developer Guide for architecture details and build instructions.\n","date":null,"permalink":"https://librescrs.github.io/features/","section":"Features","summary":"","title":"Features"},{"content":"LibreCelik is a desktop application for reading and displaying smart card data on Linux and macOS. It supports a growing range of card types through its plugin architecture.\nLinux #Download the .AppImage from the releases page, make it executable, and run it:\nchmod +x LibreCelik-*.AppImage ./LibreCelik-*.AppImage No installation required. The AppImage bundles all dependencies including Qt.\nPC/SC reader support #The pcscd daemon must be running for card access:\n# Debian/Ubuntu sudo apt install pcscd pcsc-tools sudo systemctl enable --now pcscd # Fedora/RHEL sudo dnf install pcsc-lite pcsc-tools sudo systemctl enable --now pcscd # Arch/Manjaro sudo pacman -S ccid pcsc-tools sudo systemctl enable --now pcscd Verify your card reader is detected:\npcsc_scan macOS #Download and open the .dmg from the releases page, drag LibreCelik to Applications.\nmacOS includes PC/SC support natively — no extra software needed.\nRequirements # A USB smart card reader (contact or contactless, depending on the card) Linux: pcscd daemon running macOS: no additional requirements Building from source #See the Developer Guide — Building From Source for full build instructions.\n","date":null,"permalink":"https://librescrs.github.io/user-guide/install-librecelik/","section":"User Guide","summary":"","title":"Install LibreCelik"},{"content":"Native OpenSC Support #The srbeid driver for Serbian smart cards has been merged into OpenSC mainline. Serbian eID (Gemalto 2014+, IF2020 Foreigner) and PKS Chamber of Commerce cards will be supported out of the box in the next OpenSC release.\nIf you build OpenSC from source (main branch), native support is already available — no external driver or configuration needed.\nExternal Driver #For users on current OpenSC release versions (0.26.x, 0.27.x) that do not yet include the native srbeid driver, LibreSCRS provides an external card driver module. Once installed, any PKCS#11-aware application can use Serbian eID and PKS cards transparently via OpenSC\u0026rsquo;s PKCS#11 bridge.\nSupported cards:\nSerbian eID Gemalto (2014+) — matched by ATR 3B:FF:94 Serbian eID IF2020 Foreigner — matched by AID PKS Chamber of Commerce card — matched by AID Not supported: Apollo 2008 eID (no CardEdge applet).\nDownload #Pre-built packages for OpenSC 0.26.x and 0.27.x are available on the releases page.\nExtract and copy:\n# Linux sudo cp librescrs-cardedge-opensc.so /usr/local/lib/ # macOS sudo cp librescrs-cardedge-opensc.dylib /usr/local/lib/ Build from source #Linux #sudo apt install libopensc-dev # Debian/Ubuntu # sudo dnf install opensc-devel # Fedora/RHEL cmake -S /path/to/LibreMiddleware -B build -DBUILD_CARDEDGE_OPENSC_DRIVER=ON cmake --build build --target librescrs-cardedge-opensc sudo cp build/lib/cardedge-opensc-driver/librescrs-cardedge-opensc.so /usr/local/lib/ macOS #Homebrew installs OpenSC but not its development headers. Clone the OpenSC source at the matching version tag:\nbrew install opensc opensc-tool --version # note the version, e.g. 0.26.1 git clone --branch 0.26.1 --depth 1 https://github.com/OpenSC/OpenSC /tmp/opensc-src cmake -S /path/to/LibreMiddleware -B build \\ -DBUILD_CARDEDGE_OPENSC_DRIVER=ON \\ -DOPENSC_INCLUDE_DIR=/tmp/opensc-src/src cmake --build build --target librescrs-cardedge-opensc sudo cp build/lib/cardedge-opensc-driver/librescrs-cardedge-opensc.dylib /usr/local/lib/ Configuration #Add the following to your opensc.conf:\nPlatform opensc.conf location Linux /etc/opensc/opensc.conf · /etc/opensc.conf · ~/.config/opensc/opensc.conf macOS /opt/homebrew/etc/opensc.conf · /Library/Application Support/OpenSC/opensc.conf app default { card_drivers = librescrs, internal; card_driver librescrs { module = /usr/local/lib/librescrs-cardedge-opensc.so; # Linux # module = /usr/local/lib/librescrs-cardedge-opensc.dylib; # macOS } framework pkcs15 { emulate librescrs { module = /usr/local/lib/librescrs-cardedge-opensc.so; # Linux # module = /usr/local/lib/librescrs-cardedge-opensc.dylib; # macOS } } } Verification #Card detection #opensc-tool --list-readers # Gemalto USB SmartCard Reader Slot 0 ATR: 3B FF ... PKCS#15 objects #pkcs15-tool --list-certificates pkcs15-tool --list-keys pkcs15-tool --list-pins # shows tries remaining For signing and verifying files, see the Digital Signing page.\nDebugging #Enable OpenSC debug logging in opensc.conf:\napp default { debug = 3; debug_file = /tmp/opensc-debug.txt; ... } Inspect /tmp/opensc-debug.txt after running any pkcs15-tool or pkcs11-tool command.\n","date":null,"permalink":"https://librescrs.github.io/user-guide/opensc-integration/","section":"User Guide","summary":"","title":"OpenSC Integration"},{"content":" Note: A built-in Serbian card driver (srbeid) has been merged into OpenSC mainline. It will be included in the next OpenSC release. Until then, this module remains the recommended option for PKCS#11 access to smart cards.\nThe LibreSCRS PKCS#11 module is a universal cryptographic token interface that automatically detects your card type and uses the appropriate provider. It works with any PKCS#11-aware application — Firefox, Chrome, SSH, email clients — without LibreCelik being open.\nCard types recognized automatically:\nCardEdge — Serbian eID Gemalto (2014+), IF2020 Foreigner, PKS Chamber of Commerce PKCS#15 — any PKCS#15-compliant smart card (generic PKI standard) PIV — US federal ID cards (NIST SP 800-73) Installation #Download the package for your platform from the releases page and extract it.\nLinux #tar -xzf librescrs-pkcs11-*-linux-*.tar.gz sudo cp lib/librescrs-pkcs11.so* /usr/local/lib/ sudo ldconfig Note: automatic system-wide p11-kit module discovery (so applications like Kleopatra, GnuPG-gpgsm, Firefox, Chromium, Thunderbird and Evolution pick up LibreSCRS cards without any per-application configuration) ships in the next release of LibreMiddleware. With 4.0.0 you still need to point each application at librescrs-pkcs11.so manually as shown below.\nmacOS #unzip librescrs-pkcs11-*-macos-universal.zip sudo cp librescrs-pkcs11.dylib /usr/local/lib/ Firefox # Open Settings \u0026gt; Privacy \u0026amp; Security \u0026gt; scroll to Security \u0026gt; Security Devices Click Load Enter a name (e.g. LibreSCRS) and the path to the module: Linux: /usr/local/lib/librescrs-pkcs11.so macOS: /usr/local/lib/librescrs-pkcs11.dylib Click OK Insert your smart card and refresh — Firefox will prompt for PIN when a certificate is needed. This is how you authenticate to services that require client certificate authentication, such as Serbian eUprava.\nChrome / Chromium #Chrome on Linux uses the NSS database. Register the module once:\n# Install modutil if not present sudo apt install libnss3-tools # Debian/Ubuntu sudo dnf install nss-tools # Fedora # Register for the current user modutil -dbdir sql:$HOME/.pki/nssdb -add \u0026#34;LibreSCRS\u0026#34; \\ -libfile /usr/local/lib/librescrs-pkcs11.so Restart Chrome. The card\u0026rsquo;s certificates will appear in Settings \u0026gt; Privacy and security \u0026gt; Manage certificates.\nThunderbird #Same as Firefox — Preferences \u0026gt; Privacy \u0026amp; Security \u0026gt; Security Devices \u0026gt; Load.\nOpenSSH #List the public keys on the card:\nssh-keygen -D /usr/local/lib/librescrs-pkcs11.so Use the module for SSH authentication:\nssh -I /usr/local/lib/librescrs-pkcs11.so user@host Or add to ~/.ssh/config:\nHost myserver PKCS11Provider /usr/local/lib/librescrs-pkcs11.so Building from source #See LibreMiddleware on GitHub for build instructions.\ncmake -S /path/to/LibreMiddleware -B build cmake --build build --target librescrs-pkcs11 ","date":null,"permalink":"https://librescrs.github.io/user-guide/pkcs11/","section":"User Guide","summary":"","title":"PKCS#11 Module"},{"content":"LibreCelik automatically detects the type of card you insert and displays the appropriate data. No manual configuration is required — just insert your card and the application handles the rest.\nSupported cards # Card Data shown Serbian eID (Apollo 2008, Gemalto 2014+, IF2020 Foreigner) Personal data, document info, photo. Gemalto and IF2020 also support digital signature certificates and PIN management (Apollo 2008 supports data reading only — no PKI) Vehicle registration (EU Directive 2003/127/EC) Owner, vehicle data, registration dates — all EU mandatory and optional fields Health insurance card (RFZO) Insured person, employer, insurance details eMRTD e-passports Personal data, photo, MRZ, data groups — requires BAC or PACE authentication (you may be prompted to enter the CAN from your passport). Passive, Chip, and Active Authentication for document verification PIV (NIST SP 800-73) Certificates, photo, fingerprints, PIN management PKCS#15 compatible cards (Gemalto, CardEdge, PKS, generic PKI) Certificate discovery, PIN verification and change, multi-PIN support The plugin architecture makes it straightforward to add support for new card types — both at the middleware level (card communication) and GUI level (data display).\nHow auto-detection works #When a card is inserted into the reader, LibreCelik:\nDetects the card event via smartcard::Monitor (PC/SC polling in LibreMiddleware) Queries all middleware plugins — first by ATR, then by live connection probe The highest-ranked plugin reads the card data Displays the data using the matching GUI plugin If no plugin recognizes the card, LibreCelik shows a message indicating the card type is not supported.\nProgressive card reading #Card data appears incrementally as it is read — you don\u0026rsquo;t have to wait for the entire card to be processed before seeing results. A spinner shows reading progress, and fields populate as they become available. This is especially noticeable with eMRTD e-passports where data groups are read sequentially through encrypted channels.\nPIN management #Cards that support PIN operations (eID, PKS, PKCS#15-compliant cards) show a PKI section with certificate details and PIN controls. Cards with multiple PINs (e.g., separate PINs for authentication and signing) display each PIN\u0026rsquo;s status individually and allow changing them independently.\nLanguage #LibreCelik supports English and Serbian (Cyrillic). Change the language via the menu in the top-right corner. Card data is displayed as stored on the card — typically in the language of the issuing country.\nPrinting #All card data views support printing. Use File \u0026gt; Print or the print button to generate a formatted printout of the displayed card data.\n","date":null,"permalink":"https://librescrs.github.io/user-guide/read-your-card/","section":"User Guide","summary":"","title":"Read Your Card"},{"content":" Sponsor on GitHub Donate via Open Collective LibreSCRS is developed and maintained in spare time. The project is independent. If these tools are useful to you, consider supporting the project.\nHow Funds Are Used # Testing hardware (card readers, smart cards from different countries) Development time CI/CD infrastructure ","date":null,"permalink":"https://librescrs.github.io/donate/","section":"Support the Project","summary":"","title":"Support the Project"},{"content":"","date":null,"permalink":"https://librescrs.github.io/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"This section covers everything you need to get started with LibreSCRS as an end user — from installing LibreCelik to reading smart cards, authenticating in your browser, and signing documents.\nInstall LibreCelik — download and set up the desktop application on Linux or macOS Read your card — supported cards, auto-detection, and language options Digital signing — sign documents and files using your smart card PKCS#11 module — browser authentication and digital signatures via Firefox, Chrome, Thunderbird, SSH OpenSC integration — native OpenSC support for Serbian cards and external driver for current releases ","date":null,"permalink":"https://librescrs.github.io/user-guide/","section":"User Guide","summary":"","title":"User Guide"}]