Une signature sur un PDF n'interdit pas les changements ultérieurs. Elle fixe une plage d'octets, et une mise à jour incrémentale ajoute de nouveaux octets après elle, si bien que la signature reste mathématiquement valide pendant que le document acquiert du nouveau contenu. Que ce contenu soit acceptable est une question de politique, et DocMDP est l'endroit où l'auteur énonce la politique : aucun changement du tout, remplissage de formulaires et signature seulement, ou cela plus les annotations. L'appliquer signifie classer ce qui a réellement changé, et c'est ce que fait AnalyzeModifications. Pointez-la vers une révision antérieure, puis lisez GetModificationLevel pour le verdict global et les accesseurs par constat pour le niveau, le numéro d'objet et la description de chaque différence
Cela en place, l'application de DocMDP se réduit à une comparaison : le niveau calculé est-il au niveau ou en dessous du niveau que la politique autorise
Pourquoi un PDF signé est censé changer
Trois cas légitimes, et ils couvrent la majeure partie de ce que vous verrez. Un second signataire ajoute sa signature. Un destinataire remplit les champs de formulaire que l'auteur a laissés ouverts. Et du matériel de validation à long terme est ajouté : des réponses OCSP et des CRL écrites dans le magasin de sécurité du document pour que la signature reste vérifiable après la disparition des répondeurs. Ce dernier cas n'est pas seulement permis, c'est ce qu'une archive bien gérée fait exprès aux documents signés
« Le fichier a grossi après signature » ne porte donc aucune information. La question est toujours ce qui a été ajouté, et la réponse doit venir d'une comparaison d'états de document plutôt que de l'observation des octets. La mécanique d'ajout elle-même est couverte dans l'article sur la mise à jour incrémentale
Classer par la forme de l'objet, pas par le chemin qui l'a produit
Le classifieur regarde ce qu'un objet est après le changement, pas quel appel de bibliothèque l'a créé. C'est délibéré, car l'analyse s'exécute sur des fichiers produits par d'autres logiciels, où aucun chemin d'appel n'est disponible à inspecter
Quatre formes sont reconnues. Les dictionnaires d'informations du magasin de sécurité du document et liés à la validation, les objets de flux de table de références croisées, l'entrée metadata du catalogue, et les dictionnaires de signature portant une plage d'octets sont du matériel d'archive à long terme. Un objet portant à la fois un type de champ et une valeur de champ est du remplissage de formulaire. Un objet dont le type est annotation, ou dont le sous-type est l'un de ceux listés dans le tableau 168 de l'ISO 32000-2, est un changement d'annotation. Tout le reste est non classé
Les suppressions sont traitées plus strictement que les ajouts. Un objet supprimé est mis en liste blanche seulement lorsque l'objet du côté ancien était lui-même du matériel d'archive, ce qui couvre le cas normal d'un magasin de sécurité remplacé par un plus récent. Toute autre suppression est non classée, car supprimer du contenu d'un document signé n'est pas quelque chose qu'un niveau d'autorisation autorise. Les différences au niveau du document sont plus strictes encore : un changement de nombre de pages va directement en non classé sans examiner les objets individuels, puisque aucun niveau DocMDP ne permet d'ajouter ou de retirer des pages
La liste blanche penche vers le refus
C'est la règle de conception qui gouverne chaque décision à la limite. Un changement faussement classé comme permis est une signature qui valide sur du contenu que l'auteur n'a jamais autorisé. Un changement faussement classé comme non classé est un document qui se fait signaler et examiner par une personne. Ces deux erreurs ne sont pas symétriques, donc la liste blanche reste étroite et les formes non reconnues tombent en non classé plutôt que d'être devinées
Cela a une conséquence pratique qu'il vaut la peine d'anticiper : les fichiers de producteurs inhabituels signaleront parfois des changements non classés qui sont, à l'examen, bénins. La bonne réponse est de regarder le détail du constat et le numéro d'objet plutôt que d'élargir la liste blanche, car une liste blanche qui grossit pour faire taire des rapports individuels cesse d'être un contrôle de sécurité
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 ordonné mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified ; le getter renvoie son ordinal
Level := Pdf.GetModificationLevel;
// L'application de DocMDP est maintenant une comparaison contre la politique
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;
Le niveau global est le maximum sur tous les constats, qui est la seule agrégation défendable : un document contenant quatre-vingt-dix-neuf ajouts d'archive et un changement non classé est un changement non classé
En dessous : empreintes, pas hachages cryptographiques
Le moteur de comparaison qu'expose CompareWith, et sur lequel l'analyse de modifications est construite, identifie les objets par une empreinte de leur corps normalisé utilisant un hachage 64 bits non cryptographique plutôt que SHA-256. C'est un choix réfléchi. Ce dont la comparaison structurelle a besoin, c'est de déterminisme : le même corps d'objet doit toujours produire la même empreinte au sein d'une exécution. Il n'a pas besoin de résistance aux collisions, car un attaquant qui contrôle les deux côtés de la comparaison a déjà gagné par d'autres moyens, et payer un hachage cryptographique complet sur chaque objet d'un document à un million d'objets est un coût réel sans bénéfice
Deux règles de normalisation comptent plus que le choix du hachage. Les références indirectes se replient en un jeton d'espace réservé au lieu d'être étendues en contenu référencé : étendre copierait le corps d'un objet partagé dans chaque référent, donc une petite modification d'un descripteur de police partagé invaliderait l'empreinte de chaque objet qui y parvient, et le rapport serait illisible. Et les numéros d'objets eux-mêmes sont exclus de l'empreinte, car une réécriture peut renuméroter les objets sans rien changer de sémantique
Le rapprochement s'exécute ensuite en deux passes, en alignant d'abord par empreinte et en appariant le reste par numéro d'objet pour identifier des changements plutôt qu'un ajout plus une suppression. Les vérifications bon marché passent d'abord partout : une différence de nombre de pages est signalée avant que tout parcours d'objets ne commence
Un piège : l'auto-comparaison n'est pas garantie d'être identique
Le premier test naturel pour un moteur de diff est de comparer un fichier à lui-même et d'affirmer que le résultat est identique. Cette assertion ne tient pas ici, et la raison est instructive. La voie de chargement publique et la voie de chargement de document de plus bas niveau ne configurent pas le décodage de manière identique, donc le même fichier chargé par les deux routes peut produire des empreintes qui diffèrent pour certains objets. Le moteur n'a pas tort ; les deux chargements ont réellement produit des états en mémoire différents
Plutôt que de forcer les deux voies ensemble, la sémantique de comparaison est énoncée étroitement : l'analyse compare l'état de document courant à une révision antérieure, et rapporte identique seulement lorsque les deux ensembles d'empreintes coïncident exactement. C'est la question que les utilisateurs posent réellement, et elle n'exige pas que les deux chargeurs soient interchangeables. Quand vous concevez une fonctionnalité de comparaison, définir ce que signifie « le même » est plus du travail que le calculer
Où l'utiliser
Deux endroits. Dans un rapport de validation, à côté de la vérification de signature, pour qu'un réviseur voie non seulement si la signature est cryptographiquement intacte mais ce qui est arrivé au document ensuite ; le côté signature est couvert dans la signature et la validation PAdES. Et dans un point d'entrée, où un document arrivant de l'extérieur est confronté à la copie que vous avez envoyée, pour qu'un contrat renvoyé avec une annotation ajoutée soit traité différemment d'un avec une page modifiée
Une réserve sur le périmètre. Cette analyse vous dit ce qui a changé entre deux révisions de la même lignée de document. Elle ne vous dit pas si le contenu visible est trompeur, si le flux d'apparence d'un champ de formulaire correspond à sa valeur, ou si du texte caché sous une surcouche est toujours présent dans le flux de contenu. Cela exige un traitement séparé, et le côté suppression de contenu est couvert dans l'article sur la censure réelle. Les points d'entrée d'analyse et de comparaison sont documentés sur la page produit de la losLab PDF Developer Library