Teknik Makale

PDFium VCL için FPC'de libcurl Zaman Damgası Backend'i

PDFium VCL, RFC 3161 zaman damgası isteklerini Windows dışı hedeflerde libcurl üzerinden gönderir; sekiz sembole dinamik olarak bağlanır ve WinHTTP'ye bağlanan Windows backend'inin şeklini yansıtır. İki option ayarı, taşımanın yük altında güvenilir olup olmayacağına karar verir ve bütün unit, hedef platformu için onu derleyemeyen bir makinede doğrulandı

Zaman damgası, bir imzayı sertifika süresi dolsa da hayatta kalacak bir şeye çeviren şeydir ve bir imzalama işleminin içinde oturan bir ağ işlemidir. Bu bileşim, taşıma seçimini genellikle olmadığı bir şekilde kader belirleyici kılar: worker thread'de çalışır, denetlemediğiniz bir sunucuyla konuşur ve oradaki bir takılma sayfa yüklemesini değil imzalama hattını durdurur

FPC HTTP istemcisi yerine neden libcurl?

Çünkü alternatif, depoya bir TLS yığını çeker ve sonra sizin sürüm tespitini sürdürmenizi sağlar. Free Pascal'da bariz yol, OpenSSL socket katmanlı fphttpclient'tır ve ayrıntılarda kalır: FPC 3.2.2 OpenSSL bağlamaları, güncel dağıtımların çoğunda OpenSSL 3.x'i güvenilmez biçimde tespit eder ve macOS üstüne LibreSSL farklarını ekler. Küçük bir HTTP çağrısı olarak başlayan iş, başkasının TLS ABI'sinin sürekli bakımı olur

libcurl kendi TLS backend'ini kendisi seçer ve zincirleri platform trust store'una karşı doğrular; Pascal tarafı bunların hiçbirine ihtiyaç duymaz. Bağlama katmanı sekiz semboldür. O sayı argümanın kendisidir: kodunuz ile hareketli bir bağımlılık arasındaki daha küçük yüzey, bir dağıtım yükseltmesinin sizi kırabileceği daha az yer demektir; mevcut Windows backend'iyle de örtüşür, o da bir avuç WinHTTP giriş noktasını aynı şekilde bağlar

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;

Bir C variadic işlevini Pascal'da bildirmek

curl_easy_setopt ile curl_easy_getinfo, C tarafında variadic'tir ve Object Pascal bunu ifade etmenin bir yoluna sahip değildir. İşleyen yaklaşım, argüman sınıfı başına bir tane olmak üzere birkaç sabit prototip bildirmektir; hepsi aynı export sembolünü işaret eder: long alan bir varyant, pointer alan bir varyant ve devamı; çağrı noktasında gerçekte geçirdiğiniz şeye göre seçilir

Bu, ezberlenmekten çok anlaşılmasına değer özgül bir nedenle güvenlidir. O argüman türlerinin her biri, devredeki platform çağırma konvansiyonları altında bir tamsayı register'ından geçer; C tarafındaki uygulamanın va_arg'ı tam oradan okur. Hile bu yüzden tamsayılar, pointer'lar ve handle'lar için geçerlidir ve farklı register'lardan seyahat eden floating-point argümanları için geçerli değildir. Kalıbın genellediği varsayımıyla double alan bir varyant eklemeyin

// Tek export sembolü, birkaç sabit prototip. Her varyant argümanını
// bir tamsayı register'ında geçirir; C tarafı onu oradan okur. Bir
// floating-point varyant çalışmaz ve eklenmemelidir
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;

İsteğin tamamlanıp tamamlanmayacağına karar veren iki ayar

Birincisi, açıkça yazılmış boş bir Expect: başlığıdır. libcurl, kabaca bir kilobaytı aşan istek gövdeleri için HTTP 100-continue el sıkışmasını açar ve sertifika isteği taşıyan bir zaman damgası sorgusu genellikle o eşiği geçer. Bazı TSA sunucuları devamı hiç cevaplamaz; istemci, sunucunun anında kabul edeceği gövdeyi göndermeden önce tam bir zaman aşımını bekleyip durur. Boş bir Expect: başlığı göndermek el sıkışmayı bastırır ve istek tek gidiş-dönüşte geçer

İkincisi, kurulması zorunlu olan CURLOPT_NOSIGNAL'dır. Olmadığında libcurl, isim çözümleme zaman aşımını SIGALRM ile uygular ve o mekanizma thread-safe değildir. İmzalama bir worker thread'de koşar; varsayılan davranış, eşzamanlılık altında beliren ve tek thread'li testte hiç belirmeyen örtük bir çökmedir. Bayrağı kurmak sinyal tabanlı yolu devre dışı bırakır ve bedeli yalnızca çözümleyici zaman aşımı granülitesidir

İki kusur da, sonradan bulunmasını pahalı kılan bir profile sahiptir. Ne biri, iyi huylu bir sunucuya karşı tek thread'de koşan fonksiyonel bir testte görünür ne öteki. İkisi de üretimde, belirli bir TSA'ya karşı, yük altında belirir. Bir ağ kütüphanesini bağlarken, varsayılanlarının süreciniz hakkında ne varsaydığını, eşleştiklerini varsaymadan önce okuyun

PDFium VCL libcurl zaman damgası taşıma şeması: tamsayı registerlarında argüman geçiren sabit long ve pointer Pascal prototipleri olarak bildirilen curl_easy_setopt, HTTP 100-continue el sıkışmasını bastıran boş Expect başlığı, worker threadlerde SIGALRM yolunu kaldıran CURLOPT_NOSIGNAL ve taşıma düzeyindeki yanıt tavanı gösteriliyor
İki ayar isteğin tamamlanıp tamamlanmayacağına karar verir: boş bir Expect başlığı, devamı hiç cevaplamayan sunucuları devre dışı bırakır ve NOSIGNAL, imzalama bir worker thread'de çalışırken isim çözümleme zaman aşımlarını sinyal yolunun dışında tutar

Derleyicinizin asla görmeyeceği kodu nasıl doğrularsınız?

Derleyiciye yine de gösterek; kontrollü bir kopya üzerinden. Buradaki geliştirme makinesinde Linux ya da macOS cross-compiler yoktur; bu yüzden zaman damgası unit'inin Windows dışı dalları normal bir derlemede asla kod üreteciye ulaşmaz. Hiç derlenmeyen kod, sessizce çürüyen koddur: paylaşılan bir türde yeniden adlandırma, değişmiş bir parametre listesi, eklenmiş bir unit bağımlılığı ve aylarca kimsenin fark etmediği

Teknik mekaniktir. Unit'i geçici bir dizine kopyalayın, yeniden adlandırın ve her Windows koşullusunu, hem {$IFDEF MSWINDOWS} biçimini hem {$IF DEFINED(MSWINDOWS) biçimini, hiç tanımlanmayan bir sembolle değiştirin. Sonra kopyayı derleyin. 3.828 satırın tamamı derlendiğinde, Windows dışı yolun var olan unit'leri kullandığını, backend işlevlerini eşleşen imzalarla çağırdığını ve kapsam içindeki türlere referans verdiğini kanıtlamış olursunuz. Bu, taşımanın çalıştığının kanıtı değildir ve size onu hedef platformdan başka hiçbir şey vermez. Kanıtladığı şey, dalın hâlihazırda kırık olmadığıdır; birikerek ilerleyen hata modu da zaten budur

Refik alışkanlık, libcurl unit'inin kendisini platform guard'larından arındırılmış tutmaktır; orada ona referans veren hiçbir şey olmasa da sıradan Windows derlemesine katılır. Günlük derleme böylece sözdizimini ve türlerini bedavaya denetlemeye devam eder. Yalnızca elinizde olmayan bir platformda derlenen unit, hiçbir derleyicinin denetlemediği unit'tir; aynı akıl, Delphi ve FPC cross-compiler tuzakları makalesinde anlatılan çapraz derleyici işinin tamamına uygulanır

Geri döneni sınıra bağlamak

Bir zaman damgası yanıtı küçük bir DER yapısıdır ve taşımanın onu dayatan bir yanı yoktur. Ele geçirilmiş, yanlış yapılandırılmış ya da yalnızca yanlış URL'ye yönlendirilmiş bir sunucu keyfi bir akış döndürebilir ve bağlantı kapanana dek okuyan bir istemci onu gönül rahatlığıyla biriktirir. Her iki taşıma da bu yüzden yanıta tavan çeker; sınırın doğru yeri burasıdır: taşımada reddetmek, aşırı büyük bir gövdenin hiç tahsis edilmesini önler; çözümleyici düzeyindeki bir kontrol ise bellek taahhüt edildikten sonra ancak tetiklenir

Aynı akıl URL için de geçerlidir. Backend, anlamlı biçimde konuşabildiği şemaları kabul eder; yapılandırma hatası böylece libcurl'e protokol desteğinin izin verdiği herhangi bir şekilde yorumlatılmak yerine net bir mesajla anında kalır

Taşımanın imzalama hikayesindeki yeri

Zaman damgası, uzun vadeli doğrulama hikayesinin bütünü değil ilk adımıdır. Token imzaya iliştirilmek zorundadır, doğrulama malzemesi document security store'a kaydedilmek zorundadır ve arşiv zaman damgaları, güncel olan zayıflamadan önce yenilenmek zorundadır. O bütün yay, RFC 3161 zaman damgaları ve DSS ile uzun vadeli PDF imzaları makalesinde ele alınır

RFC 3161 zaman damgası isteğinin DocumentDigestten BuildTimeStampQuery ve PostTimeStampQuery üzerinden libcurl ile bir TSA sunucusuna aktığı PDFium VCL şeması; DER yanıtı taşımada sınırlanır, ardından AttachTimeStampToken uzun vadeli doğrulamada DSSyi ve arşiv zaman damgası yenilemesini besler
Zaman damgası, uzun vadeli doğrulama hikayesinin ilk adımıdır: token iliştirilmeli, doğrulama malzemesi document security store'a kaydedilmeli ve arşiv zaman damgaları, güncel olan zayıflamadan önce yenilenmelidir

Taşıma, daha geniş bir taşınabilirlik duruşunun da bir parçasıdır: native kütüphaneyi her hedefte yükleme makalesinde anlatılan yerli kütüphane yükleyicisi, aynı sorun sınıfını PDFium binary'sinin kendisi için ele alır. Her iki durumda da kalıp özdeştir: az sayıda sembolü dinamik bağla, neyin bağlanamadığını kesin bildir ve eksik bir bağımlılığın, uygulamayı başlatamayacak bir link hatasına dönüşmesine asla izin verme

Windows ve Windows dışı zaman damgası backend'lerinin ikisi de PDFium Delphi bileşeni ile gelir; hedefe göre seçilir, yapılandırmaya göre değil. Böylece Linux'ta bir Lazarus uygulaması ile Windows'ta bir Delphi uygulaması, farklı tesisat üzerinden aynı zaman damgalı imzayı üretir