Een handtekening over een PDF verbiedt latere wijzigingen niet. Die fixeert een bytebereik, en een incrementele update plakt nieuwe bytes erachter, zodat de handtekening wiskundig geldig blijft terwijl het document nieuwe inhoud krijgt. Of die inhoud acceptabel is, is een beleidsvraag, en DocMDP is de plek waar de auteur dat beleid uitspreekt: helemaal geen wijzigingen, alleen formulieren invullen en ondertekenen, of dat plus annotaties. Handhaving betekent classificeren wat er werkelijk is gewijzigd, en dat is precies wat AnalyzeModifications doet. Wijs die naar een eerdere revisie en lees daarna GetModificationLevel voor het eindoordeel en de per-finding-accessors voor het niveau, het objectnummer en de omschrijving van elk verschil
Met dat op zijn plek krimpt DocMDP-handhaving samen tot één vergelijking: ligt het berekende niveau op of onder het niveau dat het beleid toestaat
Waarom van een ondertekende PDF wordt verwacht dat die verandert
Drie legitieme gevallen, en ze dekken het grootste deel van wat u zult zien. Een tweede ondertekenaar voegt zijn handtekening toe. Een ontvanger vult formuliervelden in die de auteur openliet. En materiaal voor langetermijnvalidatie wordt toegevoegd: OCSP-antwoorden en CRLs die in de document security store worden weggeschreven zodat de handtekening verifieerbaar blijft nadat de responders zijn verdwenen. Dat laatste is niet alleen toegestaan, het is wat een goed beheerd archief bewust doet met ondertekende documenten
Dus "het bestand groeide na ondertekening" draagt geen informatie. De vraag is altijd wat er is toegevoegd, en het antwoord moet komen uit het vergelijken van documentstatussen in plaats van uit het bekijken van bytes. De append-mechaniek zelf wordt behandeld in het artikel over incrementele updates
Classificeer naar de vorm van het object, niet naar het pad dat die produceerde
De classificator kijkt naar wat een object is na de wijziging, niet naar welke bibliotheekaanroep die heeft gemaakt. Dat is bewust, want de analyse draait tegen bestanden die door andere software zijn geproduceerd, waar geen aanroeppad beschikbaar is om te inspecteren
Vier vormen worden herkend. Document security store en informatiewoordenboeken rond validatie, cross-reference stream-objecten, de metadata-ingang van de catalog en signatuurwoordenboeken met een bytebereik zijn langetermijnarchiefmateriaal. Een object met zowel een veldtype als een veldwaarde is formulierinvulling. Een object waarvan het type annotation is, of waarvan het subtype een van die in ISO 32000-2 Tabel 168 genoemde is, is een annotatiewijziging. Alles daarbuiten is unclassified
Verwijderingen worden strenger behandeld dan toevoegingen. Een verwijderd object wordt alleen op de whitelist gezet wanneer het object aan de oude kant zelf archiefmateriaal was, wat het normale geval dekt van een security store die door een nieuwere wordt vervangen. Elke andere verwijdering is unclassified, want inhoud uit een ondertekend document verwijderen is niets waar een permissieniveau toestemming voor geeft. Documentniveauverschillen zijn nog strenger: een wijziging in het aantal pagina's gaat rechtstreeks naar unclassified zonder individuele objecten te onderzoeken, want geen enkel DocMDP-niveau staat het toevoegen of verwijderen van pagina's toe
De whitelist maakt fouten in de richting van weigeren
Dit is de ontwerpregel die elke grensbeslissing stuurt. Een wijziging die ten onrechte als toegestaan is geclassificeerd, is een handtekening die valideert over inhoud die de auteur nooit heeft geautoriseerd. Een wijziging die ten onrechte als unclassified is geclassificeerd, is een document dat wordt gemarkeerd en door een mens bekeken. Die twee fouten zijn niet symmetrisch, dus de whitelist blijft smal en onherkende vormen vallen door naar unclassified in plaats van gegokt te worden
Dat heeft een praktisch gevolg om op vooruit te kijken: bestanden van ongebruikelijke producenten zullen soms unclassified-wijzigingen rapporteren die bij inspectie onschuldig zijn. De juiste reactie is naar de detailinformatie van de bevinding en het objectnummer te kijken in plaats van de whitelist te verbreden, want een whitelist die groeit om individuele rapporten tot zwijgen te brengen houdt op een beveiligingscontrole te zijn
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 geordend mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified; de getter geeft zijn ordinale waarde
Level := Pdf.GetModificationLevel;
// DocMDP-handhaving is nu één vergelijking tegen het beleid
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;
Het eindniveau is het maximum over alle bevindingen, en dat is de enige verdedigbare aggregatie: een document met negenennegentig archieftoevoegingen en één unclassified-wijziging is een unclassified-wijziging
Daaronder: vingerafdrukken, geen cryptografische hashes
De vergelijkingsengine die CompareWith blootgeeft en waarop de wijzigingsanalyse is gebouwd, identificeert objecten aan de hand van een vingerafdruk van hun genormaliseerde body met een niet-cryptografische 64-bit hash in plaats van SHA-256. Dat is een overwogen keuze. Wat structurele vergelijking nodig heeft, is determinisme: dezelfde objectbody moet binnen één run altijd dezelfde vingerafdruk opleveren. Collision resistance is niet nodig, want een aanvaller die beide kanten van de vergelijking beheerst heeft al op andere wijzen gewonnen, en het betalen van een volledige cryptografische hash over elk object in een document met een miljoen objecten is een reële kost zonder voordeel
Twee normalisatieregels doen er meer toe dan de keuze van de hash. Indirecte verwijzingen worden samengevouwen naar een placeholder-token in plaats van uitgevouwen tot de gerefereerde inhoud: uitvouwen zou de body van een gedeeld object in elke verwijzer kopiëren, dus één kleine bewerking van een gedeelde font-descriptor zou de vingerafdruk ongeldig maken van elk object dat die bereikt, en het rapport zou onleesbaar zijn. En objectnummers zelf zijn uitgesloten van de vingerafdruk, want een herschrijving kan objecten hernummeren zonder iets semantisch te veranderen
De matching verloopt daarna in twee passen, eerst uitgelijnd op vingerafdruk en de rest gepaard op objectnummer om wijzigingen te herkennen in plaats van een toevoeging plus een verwijdering. Goedkope controles gaan doorlopend vooraf: een verschil in paginatelling wordt gerapporteerd voordat enige objecttraversatie begint
Een valkuil: zelfvergelijking is niet gegarandeerd identiek
De natuurlijke eerste test voor een diff-engine is een bestand met zichzelf te vergelijken en te asserten dat het resultaat identiek is. Die assertie geldt hier niet, en de reden is leerzaam. Het publieke laadpad en het laadpad op lager niveau configureren de decoding niet identiek, dus hetzelfde bestand via de twee routes geladen kan vingerafdrukken opleveren die voor sommige objecten verschillen. De engine is niet fout; de twee loads hebben echt verschillende in-memory statussen opgeleverd
In plaats van de twee paden met geweld samen te voegen, zijn de vergelijkingssemantiek smal geformuleerd: de analyse vergelijkt de huidige documentstatus met een eerdere revisie en rapporteert alleen identiek wanneer de twee vingerafdrukverzamelingen exact samenvallen. Dat is de vraag die gebruikers werkelijk stellen, en die vraagt niet dat de twee loaders uitwisselbaar zijn. Wanneer u een vergelijkingsfunctie ontwerpt, is definiëren wat "hetzelfde" betekent meer werk dan het berekenen ervan
Waar u het kunt gebruiken
Twee plekken. In een validatierapport, naast handtekeningcontrole, zodat een reviewer niet alleen ziet of de handtekening cryptografisch intact is, maar ook wat er daarna met het document gebeurde; de handtekeningkant wordt behandeld in PAdES-ondertekening en -validatie. En in een intake-poort, waar een document dat van buiten arriveert wordt gecontroleerd tegen het exemplaar dat u hebt verzonden, zodat een teruggestuurd contract met toegevoegde annotatie anders wordt behandeld dan een met een bewerkte pagina
Eén kanttekening bij de reikwijdte. Deze analyse vertelt u wat er is veranderd tussen twee revisies van dezelfde documentlijn. Ze vertelt u niet of zichtbare inhoud misleidend is, of de appearance stream van een formulierveld bij zijn waarde past, of tekst die onder een overlay verborgen is nog in de content stream aanwezig is. Die vragen vragen aparte behandeling, en de kant van inhoudsverwijdering wordt behandeld in het artikel over echte redactie. De toegangspunten voor analyse en vergelijking zijn gedocumenteerd op de productpagina van de losLab PDF Developer Library