Module optionnel · pdftoolkit/pdf-reader 0.7.0

English | Français

PublicKeyAuthentication implémente Adobe.PubSec avec un RecipientDecrypter du core fourni explicitement. Le lecteur reçoit les octets PDF de l’application, récupère le seed d’une enveloppe CMS correspondante et authentifie les filtres du document avant d’interpréter le catalogue. Il ne recherche aucune clé, ne télécharge aucun certificat et n’établit aucune confiance PKI.

use PDF\Reading\DocumentReader;
use PDF\Reading\FileBytes;
use PDF\Reading\Security\PublicKeyAuthentication;
use PDF\Reading\Security\PublicKeyAuthenticationLimits;
use PDF\Security\PublicKeyPermissionByteOrder;
use PDF\Security\RecipientDecrypter;

/** @var RecipientDecrypter $recipientDecrypter Identité choisie par l’application. */
$authentication = new PublicKeyAuthentication(
    $recipientDecrypter,
    PublicKeyPermissionByteOrder::MostSignificantFirst,
    new PublicKeyAuthenticationLimits(maxCredentialAttempts: 64),
);
$document = (new DocumentReader(authentication: $authentication))->read(new FileBytes($bytes));
$permissions = $document->permissions();
$effectiveMask = $permissions?->effectiveMask();

Pour des credentials PEM locaux, construire OpenSslIdentity du core avec une CertificateChain explicite, les octets PEM de la clé privée et une phrase secrète facultative, puis la transmettre à OpenSslRecipientDecrypter. Ce backend demande ext-openssl et des fichiers temporaires dans un dossier choisi par l’application ; il n’invoque aucun exécutable. Les autres capacités CMS restent des choix applicatifs. Une liste non vide de déchiffreurs peut fournir des identités différentes pour le document et ses pièces jointes. Les destinataires sont essayés dans l’ordre source, puis les identités dans l’ordre applicatif. Un payload correspondant mais mal formé échoue sans repli vers un destinataire plus permissif.

Lancer l’exemple applicatif avec un PDF explicite, un certificat DER et une clé privée PEM non chiffrée (ou l’adapter pour fournir une phrase secrète de façon sûre) :

php examples/certificate.php document.pdf certificate.der private-key.pem

Il affiche le nombre de pages, les permissions effectives et l’emplacement du MAC vérifié.

Mécanisme source Opération prise en charge
s3/s4, V1/V2 RC4 historique ; s3 et V1 utilisent 40 bits.
s5, V4 Filtres nommés V2/RC4 et AESV2/AES-128.
s5, V5/V6 Respectivement AESV3/AES-256 CBC et AESV4/GCM, avec PDF MAC source.
Identity Passage inchangé standard, non redéfinissable par la source.

Le Length d’un CF public-key est exprimé en bits, contrairement aux octets des CF Standard. Une longueur absente utilise les défauts historiques : AESV2 128, AESV3/AESV4 256, RC4 Length global ou 40. Cette lecture compatible n’atteste pas la conformité ISO2. Les handlers personnalisés, méthodes inconnues et CFM None restent refusés. None délègue le déchiffrement au handler ; il ne devient pas Identity.

Faits des destinataires et permissions

Un payload CMS de portée document contient exactement 20 octets de seed puis quatre octets de permissions. Une enveloppe de flux individuel contient exactement les 20 octets de seed. La dérivation utilise ce seed récupéré et tous les paquets CMS originaux dans leur ordre original, y compris ceux d’autres destinataires. L’ancien champ de compatibilité Encrypt.P n’accorde aucun droit.

L’ordre des octets de permissions est un argument obligatoire : MostSignificantFirst suit la sémantique déployée/corrigée ISO2 ; IsoLeastSignificantFirst sélectionne le texte littéral ISO2008. Aucune heuristique ne passe de l’un à l’autre. Le bit 2 public-key accorde tous les droits d’usage sans représenter une authentification propriétaire Standard par mot de passe.

ReadDocument::permissions() retourne désormais ?DocumentPermissions, contrat commun effectiveMask(). Standard retourne toujours ReadPermissions, avec mask, access et revision. Les certificats produisent PublicKeyReadPermissions ; sa map filters expose des PublicKeyRecipientAccess avec le mot de permissions authentifié et le premier recipientIndex correspondant, sans matériau de clé. Lorsque les filtres par défaut ont des permissions différentes, effectiveMask() retient prudemment leur intersection. L’accès à un flux individuel n’accorde pas de droits supplémentaires sur le document. Pour les champs propres aux mots de passe, l’application doit vérifier instanceof ReadPermissions. Le subFilter typé conserve l’identité s3/s4/s5. Pour s3, seuls les bits d’usage R2 historiques 3–6 sont pertinents ; les bits réservés ne représentent pas les permissions granulaires ultérieures. Depuis 0.5.2, les effectiveMask() du document et du destinataire traduisent ces quatre droits R2 dans le masque commun : le remplissage dépend du bit 6, l’extraction d’accessibilité du bit 5, l’assemblage du bit 4 et l’impression sans restriction de qualité du bit 3. Les bits source 9–12 n’accordent jamais ces droits pour s3. Le mot authentifié original reste disponible dans PublicKeyRecipientAccess::permissions, avec son subFilter explicite. Le bit 2 accorde toujours tous les droits PubSec. Pour s4/s5, les bits granulaires conservent leur sens habituel. L’application doit appliquer le masque effectif, sans interpréter un mot s3 brut comme un mot s4/s5.

Filtres distincts et accès différé

Chaque filtre nommé possède sa propre clé récupérée et ses faits de destinataire. DocOpen s’authentifie à l’ouverture. EFOpen ne s’authentifie qu’à l’accès au flux protégé : l’absence de credentials d’une pièce jointe n’empêche pas d’ouvrir un document par ailleurs accessible. Un EFOpen sélectionné par StrF ou StmF est traité comme DocOpen, conformément à la table 25. Aucun repli vers une autre clé ou Identity n’est permis. Crypt explicite utilise directement la clé du filtre ; le chiffrement historique ordinaire utilise le numéro et la génération de l’objet propriétaire.

L’appartenance des métadonnées au catalogue, les identités terminales, les exemptions signature/XRef et le déchiffrement unique des membres ObjStm suivent la même chaîne bornée que Standard. Le flag metadata vient du filtre des flux ; seule la véritable métadonnée du catalogue bénéficie d’une exemption implicite.

Pour PubSec moderne, tout PDF MAC signalé est vérifié avant le catalogue avec la clé authentifiée de DefaultCryptFilter. Un DefaultCryptFilter de portée document s’authentifie dès l’ouverture même déclaré EFOpen, pour que son bit MAC obligatoire ne soit pas masqué par un accès différé. Les autres sélecteurs ne substituent pas leurs clés. Le bit 13 authentifié, KDFSalt direct, AuthCode du dernier trailer physique et la politique d’intégrité obligatoire conservent leurs contrôles. Choisir SourceIntegrityPolicy::RequirePdfMac lorsqu’une intégrité est attendue ; voir le guide MAC source. Ni le déchiffrement CMS ni un MAC valide n’établit la confiance d’une signature, la révocation ou l’autorisation de modifier des révisions protégées.

Limites et validation

PublicKeyAuthenticationLimits permet par défaut 64 filtres, 1 024 paquets de destinataires au total, 1 Mio par paquet, 16 Mio cumulés, 4 096 tentatives de credentials et 32 identités applicatives. Les comptes, octets et tentatives sont cumulatifs entre filtres, accès différés compris, et repartent à zéro pour chaque lecture. Les limites générales de syntaxe, source, rétention et décodage s’appliquent aussi. PublicKeyAuthenticationFailed signale l’absence de credential correspondant sans exposer certificat, clé ou données source. Dictionnaires/payloads mal formés et budgets épuisés produisent ParseException.

L’oracle indépendant tools/validate_public_key_authentication.py génère des sources réelles avec pyHanko non modifié et compare les résultats PHP à des opérations CMS/clé/chiffrement indépendantes. Ses 47 sources comprennent 14 cas s3 ; dix combinent chaque droit R2 avec les bits source 9–12 nuls ou actifs. pyHanko fournit les vrais CMS et octets chiffrés ; l’oracle applique séparément l’interprétation normative s3, sans reprendre l’énumération commune des droits de pyHanko. Les tests adverses natifs couvrent séparément l’ordre, la portée, les premiers droits correspondants, les filtres inconnus, les échecs différés et les limites. Les oracles Standard, metadata, hybrides et MAC existants restent requis.

Sources normatives conservées sans modification dans le core : ISO 32000-2:2020 §§7.6.5–7.6.6 (pages physiques 101–108), ISO 32000-1:2008 §7.6.4 (pages 72–77), supplément Adobe AESV3 (page 21), ISO/TS 32004:2024 §6.4 (page 15). La carte des sources consigne leurs SHA-256 ; l’implémentation réutilise les primitives de dérivation et de chiffrement documentées du core au lieu d’ajouter une implémentation cryptographique.

Source de ce guide au commit documenté ↗