A PDFium VCL RFC 3161 időbélyegkéréseket libcurron keresztül küldi nem Windows célokon, nyolc szimbólumhoz dinamikusan kötve, tükrözve annak a Windows backendnek az alakját, amely WinHTTP-hez köt. Két opcióbeállítás dönti el, hogy a szállítás megbízható-e terhelés alatt, és a teljes unitot egy gépen validálták, amely nem tudta lefordítani a célplatformjára
Az időbélyegzés az, ami egy aláírást olyanná tesz, amely túléli a tanúsítvány lejártát, és ez egy hálózati művelet egy aláírási művelet belsejében. Ez a kombináció a szállítási választást olyan mértékben teszi következményes, ahogy az általában nem: worker szálon fut, egy olyan szerverrel beszél, amelyet nem irányít, és egy ottani akadás aláírási folyamatsort állít meg, nem oldalletöltést
Miért libcurl, nem az FPC HTTP kliense?
Mert az alternatíva TLS stacket húz a repóba, aztán megkéri, hogy tartsa karban a verziófelismerését. A kézenfekvő út Free Pascalon a fphttpclient az OpenSSL socket réteggel, és a részleteken bukik el: az FPC 3.2.2 OpenSSL kötései a legtöbb aktuális disztribúción megbízhatatlanul ismerik fel az OpenSSL 3.x-et, a macOS pedig LibreSSL különbségeket tesz rá. Ami kis HTTP hívásként indul, az valaki más TLS ABI-jének folyamatos karbantartásává válik
A libcurl feloldja a saját TLS backendjét, és a platform megbízhatósági tárával validálja a láncokat, tehát a Pascal oldalnak egyetlen példányra sem szüksége ebből. A kötési réteg nyolc szimbólum. Az a darabszám maga az érv: kisebb felület az Ön kódja és egy mozgó függőség között kevesebb helyet hagy annak, hogy egy disztribúciófrissítés megtörje, és egyezik a meglévő Windows backenddel, amely hasonló módon köt egy maroknyi WinHTTP belépési pontot
uses
FPdfTsaFpc;
var
ReqDer, RespDer: TBytes;
begin
if not TsaHttpAvailable then
raise Exception.Create('no HTTP transport for timestamping');
Writeln('TSA transport: ', TsaHttpBackendName);
ReqDer := BuildTimeStampQuery(DocumentDigest);
if PostTimeStampQuery('https://tsa.example.org/tsr', ReqDer, RespDer) then
AttachTimeStampToken(RespDer)
else
raise Exception.Create('timestamp request failed');
end;
C variadikus függvény deklarálása Pascalban
A curl_easy_setopt és a curl_easy_getinfo variadikus a C oldalon, az Object Pascalnak pedig nincs módja kifejezni ezt. A működő megközelítés több rögzített prototípus deklarálása, argumentumosztályonként egy, mind ugyanarra az exportált szimbólumra mutatva: longot fogadó változat, pointert fogadó változat és így tovább, a hívási helyen úgy választva, amit ténylegesen átad
Ez bizonyos okból biztonságos, amelyet érdemes megérteni, nem lemásolni. E argumentumtípusok mindegyike egészregiszterben utazik az éppen érvényes platformhívási konvenciók alatt, és pontosan onnan olvassa a C implementáció va_arg-ja. A trükk ezért egészekre, pointerekre és handlekre érvényes, és nem érvényes lebegőpontos argumentumokra, amelyek más regiszterekben utaznak. Ne adjon duplát fogadó változatot annak feltevésével, hogy a minta általánosítható
// Egy exportált szimbólum, több rögzített prototípus. Minden változat
// az argumentumát egészregiszterben adja át, és a C oldal onnan olvassa.
// Lebegőpontos változat nem működne, és tilos hozzáadni
type
TCurlSetOptLong = function(Handle: Pointer; Option: Integer;
Value: NativeInt): Integer; cdecl;
TCurlSetOptPtr = function(Handle: Pointer; Option: Integer;
Value: Pointer): Integer; cdecl;
var
curl_easy_setopt_long: TCurlSetOptLong;
curl_easy_setopt_ptr: TCurlSetOptPtr;
Két beállítás, amely eldönti, befejeződik-e a kérés
Az első egy explicit üres Expect: fejléc. A libcurl bekapcsolja a HTTP 100-continue kézfogást nagyjából egy kilobájt feletti kéréstestekhez, és egy tanúsítványkéréssel járó időbélyegkérés általában átlépi azt a küszöböt. Néhány TSA szerver sosem válaszol a folytatásra, tehát a kliens kifut egy teljes timeoutot, mielőtt elküldene egy testet, amelyet a szerver azonnal elfogadott volna. Egy üres Expect: fejléc küldése elfojtja a kézfogást, és a kérés egy oda-vissza úton átjut
A második a CURLOPT_NOSIGNAL, amelyet állítani kell. Enélkül a libcurl a névfeloldási timeoutját SIGALRM-mal implementálja, és az a mechanizmus nem threadsafe. Az aláírás worker szálon fut, tehát az alapértelmezett viselkedés lappangó crash, amely párhuzamos futásnál jelenik meg, és egyszálas tesztben sosem. A zászló beállítása letiltja a szignál alapú utat, és csak a feloldó timeout szemcsefinomságába kerül
Mindkét hiba ugyanazt a profilt viseli, amely később drágává teszi megtalálásukat. Egyik sem mutatkozik meg funkcionális tesztben jól viselkedő szerverrel szemben egyetlen szálon. Mindkettő élesben jelenik meg, egy konkrét TSA-val szemben, terhelés alatt. Amikor hálózati könyvtárat köt, olvassa el, mit tételeznek fel az alapértelmezései a folyamatáról, mielőtt feltenné, hogy egyeznek
Hogyan verifikáljon kódot, amelyet a fordító sosem lát?
Úgy, hogy a fordító mégis látja, egy kontrollált másolaton keresztül. A fejlesztői gépnek itt nincs Linux vagy macOS keresztfordítója, tehát az időbélyeg-unit nem Windows ágai normál build során sosem érik el a kódgenerátort. A sosem lefordított kód csendben rothadó kód: átnevezés egy megosztott típusban, megváltozott paraméterlista, hozzáadott unitfüggőség, és hónapokig senki nem veszi észre
A technika mechanikus. Másolja a unitot ideiglenes könyvtárba, nevezze át, és cserélje le minden Windows feltételt, a {$IFDEF MSWINDOWS} formát és a {$IF DEFINED(MSWINDOWS) formát egyaránt, egy sosem definiált szimbólumra. Aztán fordítsa le a másolatot. Ha mind a 3 828 sor lefordul, bizonyította, hogy a nem Windows út létező unitokat használ, egyező aláírásokkal hívja a backendfüggvényeket, és hatókörön belüli típusokra hivatkozik. Ez nem bizonyíték arra, hogy a szállítás működik, és semmi kevesebb nem adja meg, mint maga a célplatform. Az, hogy az ág nem törött már most, az valójában az a hibamód, amely felgyülemlik
A kísérő szokás az, hogy maga a libcurl unit mentes marad platformőröktől, tehát részt vesz a szokásos Windows buildben, még akkor is, ha ott semmi nem hivatkozik rá. A napi build így ingyen őrzi a szintaxisát és típusait. Egy unit, amely csak olyan platformon fordul, amely nincs meg Önnek, olyan unit, amelyet egyáltalán nem ellenőriz fordító, és ugyanez az érvelés érvényes a Delphi és FPC keresztfordítói buktatók cikkben leírt keresztfordítói munka teljes spektrumán
Ami visszajön, annak keretezése
Egy időbélyegválasz kicsi DER struktúra, és a szállításból semmi sem kényszeríti ezt ki. Egy feltört, rosszul konfigurált vagy egyszerűen rossz URL-re irányított szerver tetszőleges streamet adhat vissza, és egy addig olvasó kliens, amíg a kapcsolat záródik, készségesen felhalmozza azt. Mindkét szállítás ezért plafont szab a válasznak, ami a helyes hely a korlátnak: a szállításnál való elutasítás megakadályozza, hogy túlméretezett test egyáltalán allokálódjon, míg egy elemzőszintű ellenőrzés csak azután sül el, hogy a memóriát lefoglalták
Ugyanez az érvelés érvényes az URL-re. A backend csak olyan sémákat fogad, amelyeket értelmesen beszélni tud, tehát egy konfigurációs hiba azonnal, tiszta üzenettel bukik el, ahelyett hogy a libcurlre kerülne, hogy az a maga módján, amint protokolltámogatása engedi, értelmezze
Hol ül a szállítás az aláírási történetben
Az időbélyegzés a hosszú távú validálás történetének első lépése, nem pedig az egésze. A tokent az aláíráshoz kell csatolni, a validálási anyagot a dokumentumbiztonsági tárba kell rögzíteni, és az archív időbélyegeket meg kell újítani, mielőtt az aktuális meggyengül. Ezt az egész ívet a hosszú távú PDF aláírások RFC 3161 időbélyegekkel és a DSS-sel fedi le
A szállítás egy darabja a tágabb hordozhatósági álláspontnak is: a natív könyvtár betöltése bármely célon cikkben leírt natív könyvtárbetöltő ugyanezt a problémakategóriát kezeli maga a PDFium bináris számára. Mindkét esetben a minta azonos: kössön dinamikusan kis számú szimbólumot, jelentse pontosan, mi nem kötődött, és soha ne engedje, hogy egy hiányzó függőség linkelésidejű hibává váljon, amely megakadályozza az alkalmazás indulását
A Windows és a nem Windows időbélyeg-backend egyaránt szállul a PDFium Delphi komponenssel, cél szerint kiválasztva, konfiguráció szerint nem, tehát egy Linuxon futó Lazarus alkalmazás és egy Windowson futó Delphi alkalmazás ugyanazt az időbélyegzett aláírást állítja elő különböző csővezetéken át