Пређи на главни садржај
  1. Водич за програмере/

Преглед архитектуре

LibreSCRS се састоји од два главна пројекта који заједно читају, обрађују и приказују податке са смарт картица.

Компоненте #

LibreMiddleware (LGPL-2.1) #

Колекција статичких C++23 библиотека за комуникацију са смарт картицама, без зависности од Qt-а. Обрађује све од APDU команди ниског нивоа до екстракције података са картице. Сва PC/SC комуникација живи искључиво у LibreMiddleware-у — LibreCelik нема директну зависност од PC/SC-а.

Јавни CMake циљеви #

Спољни корисници линкују против LibreSCRS::* алијас површине — стабилног јавног API-ја који скрива интерне библиотеке иза PascalCase именских простора:

ЦиљИменски просторНамена
LibreSCRS::SmartCardLibreSCRS::SmartCardCardSession, MonitorService — PC/SC + монитор
LibreSCRS::PluginLibreSCRS::PluginCardPlugin, CardPluginService, CardData, ReadResult
LibreSCRS::AuthLibreSCRS::AuthCredentialProvider, AuthRequirement, CredentialResult
LibreSCRS::CertificateLibreSCRS::CertificateПарсирање и метаподаци X.509 сертификата
LibreSCRS::TrustLibreSCRS::TrustTrustStoreService, TrustStore, TrustConfig
LibreSCRS::SigningLibreSCRS::SigningSigningService, SigningRequest, SigningResult, VisualSignatureLayout
LibreSCRS::SecureLibreSCRS::SecureSecure::Buffer, Secure::String (нулирање при уништавању)

Табела испод приказује интерне bucket-B библиотеке које имплементирају ове јавне циљеве. Интерне библиотеке могу да се мењају између издања и спољни код не треба директно да линкује против њих.

БиблиотекаНамена
smartcardPCSCConnection, Monitor (детекција картица), APDU команда/одговор, TLV и BER-TLV парсирање (ISO 7816-4), TransmitFilter (транспарентни Secure Messaging слој)
pluginCardPlugin интерфејс (са подршком за streaming), CardData модел, CardPluginRegistry (dlopen), AutoReader (мост између Monitor-а и откривања додатака)
pkcs15Генерички PKCS#15/ISO 7816-15 парсер и библиотека — EF.ODF/EF.DIR откривање, енумерација сертификата и кључева. Ради са било којом PKCS#15-компатибилном картицом
rs-eidAPI за српску eID картицу са имплементацијама читача (Apollo 2008, Gemalto 2014+, Foreigner IF2020)
eu-vrcЕУ саобраћајна дозвола (Директива 2003/127/EC)
rs-healthКартица српског здравственог осигурања (РФЗО)
cardedgeCardEdge PKI аплет за српске смарт картице — сертификати, управљање PIN-ом, дигитално потписивање
emrtdeMRTD комуникација са е-пасошем — читање група података, парсирање MRZ-а
emrtd-cryptoeMRTD криптографија — BAC, PACE (ECDH-GM), Secure Messaging
pivPIV комуникација са картицом (NIST SP 800-73)
pkcs11PKCS#11 дељена библиотека (librescrs-pkcs11) — подржава све типове картица
*-pluginДодаци за картице (.so): rs-eid, rs-health, eu-vrc, emrtd, piv, pkcs15, cardedge, opensc

LibreCelik (GPL-3.0) #

Qt6 десктоп GUI апликација за приказ података са картица. Чист презентацијски слој — без PC/SC-а, без APDU-а, без знања о протоколима картица. Прима CardData од middleware додатака и приказује га кроз GUI додатке.

МодулНамена
smartcardSmartCardReaderListener — Qt адаптер који обавија middleware smartcard::Monitor
pluginCardWidgetPlugin интерфејс и CardWidgetPluginRegistry (QPluginLoader)
asynccardreaderГенерички асинхрони читач картица кроз middleware CardPlugin ланац
documentДељени PKI кориснички интерфејс (TokenSection), дијалог за промену PIN-а, штампање
certificateПрегледач X.509 сертификата (модел стабла + дијалог)
plugins/GUI додаци (rs-eid, rs-health, eu-vrc, emrtd, piv, token) као Qt MODULE .so датотеке

LibreCelik преузима LibreMiddleware преко CMake FetchContent. За локални развој можете усмерити на локалну копију.


Архитектура додатака #

Цео систем је изграђен око додатака. Постоје два независна слоја — middleware додаци за комуникацију са картицом, GUI додаци за приказ. Повезани су кроз CardData, универзални модел података који сваки middleware додатак производи, а сваки GUI додатак конзумира.

┌─────────────────────────────────────────────────────────┐
│                      LibreCelik (GUI)                   │
│                                                         │
│  ┌──────────────────────┐ ┌────────────────────────┐    │
│  │SmartCardReaderListener│ │ CardWidgetPluginRegistry│    │
│  │(Qt адаптер за Monitor)│ │ (QPluginLoader)        │    │
│  └──────────────────────┘ └──────────┬─────────────┘    │
│           ▲                          │ учитава           │
│           │ обавија          ┌──────▼──────────────┐   │
│           │                   │  GUI додаци (.so)    │   │
│           │                   │ ┌──────┐ ┌────────┐ │   │
│           │                   │ │rs-eid│ │eu-vrc  │ │   │
│           │                   │ ├──────┤ ├────────┤ │   │
│           │                   │ │rs-   │ │ emrtd  │ │   │
│           │                   │ │health│ ├────────┤ │   │
│           │                   │ ├──────┤ │  piv   │ │   │
│           │                   │ │token │ └────────┘ │   │
│           │                   │ └──────┘            │   │
│           │                   └─────────────────────┘   │
│           │                          ▲                  │
│           │                          │ CardData         │
├───────────┼──────────────────────────┼──────────────────┤
│           │           LibreMiddleware│                   │
│           │                          │                  │
│  ┌────────┴─────────┐ ┌─────────────┴──────────┐       │
│  │ smartcard::Monitor│ │ CardPluginRegistry     │       │
│  │ (PC/SC polling)  │ │ (dlopen)               │       │
│  └──────────────────┘ └──────────┬─────────────┘       │
│                                  │ учитава              │
│  ┌───────────────────────────────▼─────────────────┐   │
│  │         Middleware додаци (.so)                   │   │
│  │ ┌─────────┐ ┌──────────┐ ┌───────────────────┐ │   │
│  │ │ rs-eid  │ │  emrtd   │ │     opensc         │ │   │
│  │ ├─────────┤ ├──────────┤ │ (PKI резервна)     │ │   │
│  │ │ eu-vrc  │ │ cardedge │ └───────────────────┘ │   │
│  │ ├─────────┤ ├──────────┤ ┌───────────────────┐ │   │
│  │ │rs-health│ │   piv    │ │     pkcs15         │ │   │
│  │ └─────────┘ └──────────┘ └───────────────────┘ │   │
│  └─────────────────────────────────────────────────┘   │
│                          │                             │
│                          │ APDU (ISO 7816-4)           │
│                          ▼                             │
│                   ┌──────────────┐                     │
│                   │PCSCConnection│                     │
│                   │  (PC/SC)     │                     │
│                   └──────┬──────┘                      │
└──────────────────────────┼─────────────────────────────┘
                           │
                    ┌──────▼──────┐
                    │ Смарт картица│
                    └─────────────┘

Детекција картице: smartcard::Monitor #

Детекција картице живи у LibreMiddleware-у као smartcard::Monitor — чиста C++23 класа без зависности од Qt-а. На позадинској нити прозива PC/SC за догађаје убацивања/вађења картице и обавештава претплатнике кроз повратне позиве:

  • subscribe(MonitorCallback) — регистрација за MonitorEvent обавештења (картица убачена/извађена, име читача, ATR)
  • unsubscribe(id) — престанак примања догађаја

Монитор лењо покреће нит за прозивање при првој претплати и зауставља је када се последњи претплатник одјави. У LibreCelik-у, SmartCardReaderListener обавија монитор и маршалира догађаје на Qt главну нит кроз сигнале.

Middleware додаци (CardPlugin) #

Middleware додатак је дељена библиотека (.so / .dylib) коју CardPluginRegistry учитава преко dlopen у време извршавања. Сваки додатак имплементира:

  • canHandle(const std::vector<uint8_t>& atr) — брза провера само по ATR-у. Враћа bool — да ли овај додатак препознаје ATR? Без комуникације са картицом. Додаци такође излажу probePriority() (int) тако да регистар може рангирати кандидате.
  • canHandleConnection(PCSCConnection& conn) — провера на живој конекцији. Шаље SELECT команде за познате AID-ове ради потврде подршке. Позива се само на додацима који нису прошли canHandle() — даје им другу шансу да преузму картицу кроз живу комуникацију.
  • readCard(PCSCConnection& conn) — екстрахује све податке са картице. Шаље APDU команде, парсира TLV/BER-TLV одговоре и враћа CardData објекат са типизираним групама поља (лични подаци, подаци документа, фотографије, сертификати).

Двофазна провера: Регистар прво позива canHandle(atr) на свим додацима — они који врате true иду одмах у листу кандидата, рангирани по probePriority(). Затим се canHandleConnection(conn) позива само на додацима који су вратили false у Фази 1, дајући генеричким додацима (попут OpenSC-а) шансу да преузму картицу провером живе конекције. Ово избегава непотребну комуникацију за додатке који су већ препознали картицу по ATR-у.

Streaming подршка: Додаци могу имплементирати readCardStreaming() поред readCard(). Streaming испоручује CardData инкрементално — поља се појављују у GUI-ју док се читају са картице, уместо чекања да се цело читање заврши. Ово је посебно корисно за eMRTD где се групе података читају секвенцијално кроз шифроване канале.

Ланац резервних опција: Ако readCard() најбоље рангираног додатка не успе, аутоматски се покушава следећи кандидат. OpenSC додатак служи као генеричка резервна опција за сваку картицу коју нема нативно подржану.

PKI резервна опција: Додаци за податке (еИД, саобраћајна, здравствена) читају демографске податке, али не обрађују PKI операције. Након што додатак за податке заврши, систем може покренути засебан PKI додатак (CardEdge, PKCS#15 или OpenSC) за откривање сертификата, потписивање и управљање PIN-ом. Ово раздвајање значи да додаци за податке не морају знати за PKI.

Додавање новог типа картице: Напишите класу која наслеђује CardPlugin, имплементирајте canHandle(), canHandleConnection() и readCard() (опционо readCardStreaming()), компајлирајте као дељену библиотеку и поставите је у директоријум за додатке. Регистар је аутоматски открива при следећем покретању.

GUI додаци (CardWidgetPlugin) #

GUI додатак је Qt дељена библиотека коју CardWidgetPluginRegistry учитава преко QPluginLoader. Сваки додатак имплементира:

  • cardType() — враћа тип картице који овај додатак може да прикаже (мора да одговара ономе што middleware додатак поставља у CardData).
  • createWidget(const CardData&, QWidget* parent) — гради и враћа Qt виџет који приказује податке са картице. Пуна контрола над распоредом — текстуална поља, фотографије, сертификати, шта год картица садржи.

Додавање новог приказа картице: Напишите класу која наслеђује CardWidgetPlugin и Q_PLUGIN_METADATA, имплементирајте cardType() и createWidget(), компајлирајте као Qt MODULE библиотеку.

Како се повезују #

Додавање подршке за потпуно нови тип картице захтева два додатка:

  1. Middleware додатак — зна како да комуницира са картицом (SELECT, READ BINARY, парсирање одговора)
  2. GUI додатак — зна како да прикаже податке (распоред, ознаке, форматирање)

Мост између њих је CardData — мапа група поља, где свака група садржи парове кључ-вредност. Middleware додатак га попуњава, GUI додатак га чита. Ниједан не мора да зна за други. Поновна компилација основне апликације није потребна — само поставите .so датотеке.

eMRTD: Криптографски приступ картици #

eMRTD додатак демонстрира најсложенију комуникацију са картицом у систему. Е-пасоши захтевају криптографско договарање кључева пре него што се било који податак може прочитати:

  • BAC (Basic Access Control) — изводи сесијске кључеве из машински читљиве зоне (MRZ) штампане на пасошу
  • PACE (Password Authenticated Connection Establishment) — Diffie-Hellman договарање кључева на елиптичним кривама, модерна замена за BAC. Може захтевати од корисника унос CAN броја (Card Access Number) штампаног на пасошу.
  • Secure Messaging — све наредне APDU команде и одговори су шифровани и МАЦ-овани сесијским кључевима. Имплементирано као TransmitFilter на PCSCConnection — једном инсталиран, шифровање је транспарентно за сав код вишег нивоа.

Библиотека emrtd-crypto имплементира ове протоколе из ICAO 9303 спецификације, док библиотека emrtd обрађује читање група података и парсирање MRZ-а. emrtd-plugin их повезује као стандардни CardPlugin са streaming подршком — групе података се читају прогресивно и испоручују GUI-ју како постану доступне. Из перспективе остатка система, то је само још један додатак који враћа CardData.


Ток података #

Комплетан ток када се смарт картица убаци:

1. Картица убачена у читач

2. smartcard::Monitor (LibreMiddleware, PC/SC нит за прозивање)
   └─ SCardGetStatusChange детектује присуство картице
   └─ креира MonitorEvent { CardInserted, readerName, atr }
   └─ обавештава претплатнике кроз повратни позив

3. SmartCardReaderListener (LibreCelik, Qt адаптер)
   └─ прима MonitorEvent на нити монитора
   └─ маршалира на Qt главну нит преко QMetaObject::invokeMethod
   └─ емитује сигнал са MonitorEvent

4. Главни прозор прима сигнал, покреће двофазно откривање додатака:
   Фаза 1 — ATR филтрирање (без комуникације са картицом):
   └─ CardPluginRegistry::findAllCandidates(atr, connection)
      ├─ rs-eid-plugin::canHandle(atr)      → true  (препознат ATR српске еИД)
      ├─ eu-vrc-plugin::canHandle(atr)      → false
      ├─ emrtd-plugin::canHandle(atr)       → false
      └─ opensc-plugin::canHandle(atr)      → false
      Кандидати до сад: [rs-eid-plugin (приоритет 100)]
   Фаза 2 — провера на конекцији (само додаци који су вратили false):
      ├─ eu-vrc-plugin::canHandleConnection(conn)   → false
      ├─ emrtd-plugin::canHandleConnection(conn)    → false
      └─ opensc-plugin::canHandleConnection(conn)   → true (нађен PKCS#15)
      Коначни кандидати: [rs-eid-plugin (100), opensc-plugin (50)]

5. AsyncCardReader::requestData(topCandidate)
   └─ std::async → позадинска нит
   └─ rs-eid-plugin::readCard(connection)
      ├─ SELECT AID, READ BINARY лични подаци
      ├─ парсирање BER-TLV одговора
      └─ враћа CardData { type: "rs.eid", fields: {...} }

6. Резултат маршалиран назад на Qt главну нит (QMetaObject::invokeMethod)

7. CardWidgetPluginRegistry::findByCardType("rs.eid")
   └─ rseid-gui-plugin одговара

8. rseid-gui-plugin::createWidget(cardData, parent)
   └─ гради виџет: фотографија, име, адреса, број документа, сертификати

9. Виџет приказан у главном прозору

Пројектни обрасци #

Strategy #

CardReaderBase дефинише интерфејс за комуникацију са одређеним чипом читача. Конкретне имплементације — CardReaderApollo (Apollo 2008 чипови) и CardReaderGemalto (Gemalto 2014+ чипови) — енкапсулирају разлике у APDU секвенцама и структурама датотека.

Async #

AsyncCardReader обавија позиве middleware додатака са std::async да би GUI остао одзиван. Резултати се маршалирају назад на Qt главну нит преко QMetaObject::invokeMethod и Qt сигнала. Подржава и batch (readCard) и streaming (readCardStreaming) режиме. У middleware слоју, AutoReader пружа удобан омотач који повезује smartcard::Monitor догађаје директно са CardPluginRegistry откривањем и читањем картице — корисно за апликације које желе аутоматско руковање картицама без ручног повезивања.

Observer #

smartcard::Monitor користи модел претплате заснован на повратним позивима у middleware слоју. У GUI слоју, SmartCardReaderListener обавија монитор и поново емитује догађаје као Qt сигнале, пропагирајући догађаје убацивања/вађења картице до главног прозора и свих регистрованих слушалаца.

Singleton #

SmartCardReaderListener::instance() пружа јединствену тачку за диспечовање догађаја картице кроз GUI апликацију.


Простори имена #

Јавни API (LibreSCRS::*) #

Јавна корисничка површина 4.0 живи испод LibreSCRS:: корена у PascalCase именским просторима. Свако заглавље испод include/LibreSCRS/ припада овој површини; сви други простори су интерни.

Простор именаОпсег
LibreSCRS::SmartCardCardSession, MonitorService — PC/SC + монитор догађаја картице
LibreSCRS::PluginCardPlugin, CardPluginService, CardData, ReadResult, AutoReaderService
LibreSCRS::AuthCredentialProvider, AuthRequirement, CredentialResult, FieldDescriptor
LibreSCRS::CertificateПарсирање и метаподаци X.509 сертификата
LibreSCRS::TrustTrustStoreService, TrustStore, TrustConfig
LibreSCRS::SigningSigningService, SigningRequest, SigningResult, VisualSignatureLayout
LibreSCRS::SecureSecure::Buffer, Secure::String (нулирање при уништавању)
LibreSCRS (корен)CancelToken, LocalizedText, SyncProvider

Интерни простори имена (подложни промени) #

Простори имена малим словима испод живе у lib/<библиотека>/ и подржавају јавни API. Нису део подржане корисничке површине — имена и потписи могу да се мењају између издања.

Простор именаОпсег
smartcard::Интерни APDU / TLV / BER-TLV / PCSCConnection помоћници
plugin::Интерни помоћници за учитавање додатака и регистар
eidcard::Интерни типови за српску еИД картицу
euvrc::Интерни типови за ЕУ саобраћајну дозволу
healthcard::Интерни типови за здравствену картицу
cardedge::Интерни типови CardEdge PKI аплета
emrtd::eMRTD интерне структуре података и парсирање MRZ-а
emrtd::cryptoeMRTD криптографија — BAC, PACE, Secure Messaging
piv::PIV интерни типови
pkcs15::PKCS#15 интерни типови парсера
libresign::Унутрашњи engine за потписивање (PAdES / XAdES / JAdES / CAdES / ASiC-E)

Стандарди #

Следећи стандарди су релевантни за базу кода:

СтандардПримена
ISO 7816-4Комуникација са смарт картицом — структура APDU команде/одговора, TLV и BER-TLV кодирање
PC/SCСлој за приступ читачу — детекција картице, управљање конекцијом, контрола трансакција
PKCS#11Интерфејс криптографског токена — аутентификација у прегледачу, дигитални потписи
PKCS#15Апликација криптографских информација — откривање сертификата и кључева на смарт картицама
ICAO 9303eMRTD (е-пасоши) — BAC и PACE договарање кључева, Secure Messaging, структура група података
NIST SP 800-73PIV интерфејс картице — откривање сертификата, аутентификација, дигитално потписивање
BSI TR-03110PACE протокол — договарање кључева аутентификованих лозинком за eMRTD