Technický článek

Klasifikace změn v PDF po podpisu pomocí PDFlibPas

Podpis nad PDF nezakazuje pozdější změny. Fixuje bajtový rozsah a inkrementální aktualizace po něm připojuje nové bajty, takže podpis zůstává matematicky platný, zatímco dokument získává nový obsah. Zda je tento obsah přijatelný, je otázka politiky a DocMDP je místo, kde autor politiku stanovuje: žádné změny vůbec, pouze vyplňování formulářů a podepisování, nebo to plus anotace. Vynucení znamená klasifikovat, co se skutečně změnilo, což je úkol AnalyzeModifications. Namiřte ji na dřívější revizi, poté si přečtěte GetModificationLevel pro celkový verdikt a přístupové funkce pro jednotlivá zjištění nesoucí úroveň, číslo objektu a popis každého rozdílu

S tím na místě se vynucení DocMDP sbalí na porovnání: je vypočtená úroveň na úrovni povolené politikou nebo pod ní

Diagram žebříčku úrovní modifikací PDFlibPas od mlNone po mlUnclassified ukazující vynucení politiky DocMDP jako jediné porovnání v Delphi
Žebříček TPLModificationLevel běží od mlNone po mlUnclassified a vynucení DocMDP se redukuje na porovnání vypočtené úrovně s politikou

Proč se u podepsaného PDF očekává změna

Tři legitimní případy a pokrývají většinu toho, co uvidíte. Druhý podepisující přidává svůj podpis. Příjemce vyplňuje pole formulářů, které autor nechal otevřená. A materiál dlouhodobého ověřování se připojuje: odpovědi OCSP a CRL se zapisují do document security store dokumentu, takže podpis zůstává ověřitelný i po zmizení responderů. To poslední není jen povoleno, je to záměr dobře spravovaného archivu u podepsaných dokumentů

„Soubor po podpisu vyrostl“ tedy nenese žádnou informaci. Otázkou vždy je, co bylo přidáno, a odpověď musí přijít z porovnání stavů dokumentu, nikoli ze sledování bajtů. Mechanika připojování sama je předmětem článku o inkrementálních aktualizacích

Klasifikujte podle tvaru objektu, ne podle cesty, která jej vytvořila

Klasifikátor se dívá na to, čím objekt je po změně, nikoli na to, které volání knihovny jej vytvořilo. Je to záměrné, protože analýza běží nad soubory produkovanými jiným softwarem, kde žádná call path k prozkoumání není k dispozici

Rozpoznávají se čtyři tvary. Slovníky informací document security store a validace, objekty cross-reference stream, položka metadata katalogu a slovníky podpisů nesoucí bajtový rozsah jsou materiálem dlouhodobého archivu. Objekt nesoucí zároveň typ pole a hodnotu pole je vyplňování formulářů. Objekt, jehož typ je anotace, nebo jehož subtype je jedním z uvedených v Tabulce 168 normy ISO 32000-2, je změnou anotace. Všechno ostatní je neklasifikováno

Rozhodovací strom, který PDFlibPas aplikuje na každý změněný objekt PDF, třídí aktualizace do úrovní archiv, vyplňování formulářů, anotace nebo neklasifikováno
Každý změněný objekt je klasifikován tím, čím je — security store, xref stream, pole, anotace — nikdy voláním, které jej vytvořilo

Odstranění jsou posuzována přísněji než přídavky. Odstraněný objekt je whitelistován pouze tehdy, když objekt na staré straně byl sám archivním materiálem, což pokrývá běžný případ, kdy je security store nahrazen novějším. Každé jiné odstranění je neklasifikováno, protože mazání obsahu z podepsaného dokumentu není něco, co by úroveň oprávnění autorizovala. Rozdíly na úrovni dokumentu jsou ještě přísnější: změna počtu stránek jde rovnou do neklasifikováno bez zkoumání jednotlivých objektů, protože žádná úroveň DocMDP nepovoluje přidávání ani odebírání stránek

Whitelist se mýlí směrem k odmítání

Toto je návrhové pravidlo, které řídí každé hraniční rozhodnutí. Změna chybně klasifikovaná jako povolená je podpis validující se nad obsahem, který autor nikdy neautorizoval. Změna chybně klasifikovaná jako neklasifikovaná je dokument, který je označen a zkontrolován člověkem. Tyto dvě chyby nejsou symetrické, takže whitelist zůstává úzký a nerozpoznané tvary padají do neklasifikováno namísto dohadování

To má praktický důsledek, který stojí za předvídání: soubory od neobvyklých producentů budou občas hlásit neklasifikované změny, které jsou při prozkoumání neškodné. Správnou reakcí je podívat se na detail zjištění a číslo objektu, nikoli whitelist rozšiřovat, protože whitelist rostoucí, aby ztlumil jednotlivá hlášení, přestává být bezpečnostní kontrolou

uses
  PDFlibrary, PDFlibCompare;

var
  Pdf: TPDFlib;
  I, Level: Integer;
begin
  Pdf := TPDFlib.Create(nil);
  try
    Pdf.LoadFromFile('contract-countersigned.pdf', '');
    if Pdf.AnalyzeModifications('contract-as-signed.pdf', '') < 0 then
      raise Exception.Create('the earlier revision could not be loaded');

    // TPLModificationLevel v pořadí mlNone, mlLTAUpdates, mlFormFilling,
    // mlAnnotations, mlUnclassified; getter vrací svůj ordinál
    Level := Pdf.GetModificationLevel;
    // Vynucení DocMDP je nyní jediné porovnání s politikou
    if Level > Ord(mlFormFilling) then
      for I := 0 to Pdf.GetModificationFindingCount - 1 do
        Report.Add(Format('object %d, level %d: %s',
          [Pdf.GetModificationFindingObjNum(I),
           Pdf.GetModificationFindingLevel(I),
           Pdf.GetModificationFindingDetail(I)]));
  finally
    Pdf.Free;
  end;
end;

Celková úroveň je maximum přes všechna zjištění, což je jediné obhajitelné agregování: dokument obsahující devadesát devět archivních přídavků a jednu neklasifikovanou změnu je neklasifikovaná změna

Pod kapotou: otisky prstů, ne kryptografické hashe

Srovnávací engine, který vystavuje CompareWith a na němž je analýza modifikací postavena, identifikuje objekty otiskem jejich normalizovaného těla pomocí nekryptografického 64bitového hashe namísto SHA-256. Je to promyšlená volba. Strukturální srovnání potřebuje determinismus: stejné tělo objektu musí v rámci běhu vždy produkovat stejný otisk. Nepotřebuje odolnost proti kolizím, protože útočník, který ovládá obě strany srovnání, už vyhrál jinými prostředky a platit za plný kryptografický hash nad každým objektem v dokumentu s milionem objektů je skutečná cena bez přínosu

Dvě normalizační pravidla jsou důležitější než volba hashe. Nepřímé reference se skládají na zástupný token namísto rozbalení do odkazovaného obsahu: rozbalení by zkopírovalo tělo sdíleného objektu do každého referrera, takže jedna malá úprava sdíleného popisovače písma by zneplatnila otisk každého objektu, který k němu dochází, a hlášení by bylo nečitelné. A samotná čísla objektů jsou z otisku vyloučena, protože přepis může objekty přečíslovat bez změny čehokoli sémantického

Porovnávání pak běží ve dvou průchodech, nejprve se zarovná podle otisku a zbytek se spáruje podle čísla objektu, aby se identifikovaly změny, nikoli přídavek plus odstranění. Levné kontroly přicházejí první v celém průběhu: rozdíl počtu stránek je nahlášen dříve, než začne jakékoli procházení objektů

Dvouprůchodový diff revizí PDF v PDFlibPas: nejprve kontrola počtu stránek, 64bitové otisky, zarovnání otisků a párování podle čísel objektů
Srovnávací engine otiskuje normalizovaná těla objektů, hlásí nejdříve rozdíly počtu stránek a poté porovnává podle otisku a čísla objektu

Past: samo-srovnání není zaručeno být identické

Přirozený první test diff enginu je porovnat soubor se sebou samým a assertovat, že výsledek je identický. Tato aserce zde neplatí a důvod je poučný. Veřejná load cesta a cesta načítání dokumentu na nižší úrovni nekonfigurují dekódování identicky, takže tentýž soubor načtený oběma cestami může produkovat otisky, které se u některých objektů liší. Engine se nemýlí; obě načtení skutečně vytvořila různé stavy v paměti

Místo násilného spojení obou cest je sémantika srovnání stanovena úzce: analýza porovnává aktuální stav dokumentu s dřívější revizí a hlásí identické pouze tehdy, když se obě množiny otisků přesně kryjí. To je otázka, kterou si uživatelé skutečně kladou, a nevyžaduje, aby oba loadery byly zaměnitelné. Když navrhujete srovnávací funkci, definování významu „stejné“ je větší dílo než její výpočet

Kde to použít

Na dvou místech. V validačním hlášení, vedle kontroly podpisů, takže recenzent vidí nejen to, zda je podpis kryptograficky neporušený, ale i co se s dokumentem dělo poté; strana podpisů je předmětem podepisování a ověřování PAdES. A ve vstupní bráně, kde je dokument přicházející zvenčí kontrolován proti kopii, kterou jste odeslali, takže vrácená smlouva s přidanou anotací je posuzována jinak než ta s upravenou stránkou

Jedna výhrada k rozsahu. Tato analýza vám říká, co se změnilo mezi dvěma revizemi téže rodiny dokumentu. Neříká vám, zda je viditelný obsah klamavý, zda appearance stream pole formuláře odpovídá své hodnotě, nebo zda je text skrytý pod překryvem stále přítomen v content streamu. To vyžaduje samostatné zpracování a strana odstranění obsahu je předmětem článku o skutečné redakci. Vstupní body analýzy a srovnání jsou dokumentovány na stránce produktu losLab PDF Developer Library