Optional module · pdftoolkit/pdf-reader 0.7.0

English | Français

Authentication is an explicit capability of one DocumentReader. Without it, an effective encrypted trailer raises EncryptedPdfException before the catalog is interpreted. The library never discovers passwords or opens external files.

use PDF\Reading\DocumentReader;
use PDF\Reading\FileBytes;
use PDF\Reading\Security\StandardPasswordAuthentication;
use PDF\Security\PreparedPassword;

// The application supplies these bytes and prepares the UTF-8 password.
$authentication = new StandardPasswordAuthentication(new PreparedPassword($password));
$reader = new DocumentReader(authentication: $authentication);
$document = $reader->read(new FileBytes($bytes));
$permissions = $document->permissions();

PreparedPassword represents already prepared UTF-8 input for R5–R7; choosing Unicode preparation is an explicit application step. For historical R2–R4 use new PdfDocEncodedPassword(new HexadecimalString($pdfDocEncodedBytes)) instead. Use PDF\Security\PdfDocEncodedPassword and PDF\Syntax\Object\StringObject\HexadecimalString. These bytes are not UTF-8. The default AES backends require OpenSSL. Alternative core AES backends can be injected in the authentication constructor; no executable is invoked at runtime.

Source mechanism Native operation
R2 / V1 RC4, 40-bit file key.
R3 / V2 RC4, variable historical key length.
R4 / V4 Named None, V2 and AESV2 filters; string, stream and embedded-file defaults.
R5 / V5 Historical AES-256 password authentication and encrypted permissions.
R6 / V5 Modern password hashing, AESV3 and authenticated permissions.
R7 / V6 AESV4 authenticated GCM payloads; a modified tag is rejected.

ReadPermissions retains the source mask, authentication access (User or Owner) and revision. effectiveMask() returns the source mask for a user and all document permissions for an owner. The application decides how to present and enforce usage rights. This result is not a digital-signature validation. AuthenticationFailed identifies an incorrect password without including it in the message; malformed dictionaries or encrypted data raise ParseException.

The authenticated document decrypts strings and streams once, using the owning object's key where required. An explicit first Crypt filter is consumed before the remaining codec chain. Xref streams, the encryption dictionary and signature Contents are exempt. Object-stream members are parsed after the container is decrypted and are not decrypted twice. Clear metadata and embedded-file filter selection follow their dictionary settings.

Metadata ownership and indirect stream types

Version 0.3.1 binds EncryptMetadata=false to the actual Metadata reference of the effective catalog. A page or Form's /Type /Metadata stream remains encrypted unless it explicitly selects Identity. Indirect Type names are resolved before choosing EFF versus StmF. Catalogs in encrypted object streams retain these rules without recursively loading their metadata while being decrypted.

Native authentication supplies the immutable document-root reference. A custom DocumentAuthentication constructing DocumentDecryption can supply that last optional documentRoot argument too; without it, no implicit catalog-metadata exemption is guessed. Explicit Crypt /Identity remains available independently. Source: ISO 32000-2:2020, §7.3.10 (physical page 49), EncryptMetadata (page 94) and distinct page metadata (page 121).

Table 20 (physical page 88) fixes the V4 file key at 128 bits; the optional global Length/default 40 belongs to V2/V3. A standard-name entry such as Identity inside CF is ignored before interpreting its value and cannot redefine that standard filter. Its actual identity behaviour remains available.

EncodedStream::encodedBytes() returns decrypted bytes ready for the remaining codecs; originalEncodedBytes() retains the original source ciphertext. Retained plaintext consumes the shared document budget. Import and writing use the explicit remapping bridge; authenticated source objects cannot be serialized directly into a new document.

Source PDF MAC verification (KDFSalt/AuthCode) is performed before protected content is interpreted, for Standard R5–R7. See the PDF MAC guide for both locations, exact byte coverage, required-integrity policy and boundaries. Adobe.PubSec has a separate certificate authentication capability. Unsupported image codecs remain separate from decryption. Successful authentication does not certify that an original PDF is safe to execute in another application.

Independent validation uses 24 actual R2–R7 PDFs: qpdf creates R2–R6 and Python cryptography independently creates AESV4 payloads using qpdf's R6 password entries. Both passwords, binary strings, object streams, metadata settings and signature exemptions are checked. These synthetic credentials are public test data. Seven additional cases verify catalog/page/Form metadata, explicit Identity, the fixed V4 key without global Length, ignored standard CF entries and indirect embedded-file types, with ordinary and compressed catalogs. qpdf independently decrypts the metadata files; cryptography constructs EFF payloads with qpdf credentials. qpdf 12.4.2 does not decrypt EFF when StmF is Identity, even with a direct Type, so its decryption is not used as an oracle for that particular selection. Its algorithms are never patched for the test.

Reader 0.4.1 preserves the terminal object number and generation through Encrypt, Root and catalog Metadata aliases. Reaccessing the encryption dictionary does not decrypt its O/U credential entries. Only the actual, correctly typed catalog metadata stream is implicitly clear; page and object metadata keep their cipher. Identity traversal uses the same bounded authenticated resolver and avoids recursively loading the stream currently being processed. MAC verification still precedes catalog interpretation.

Four additional alias files preserve the original encrypted payload bytes. qpdf 12.4.2, pypdf 6.20.1 and pyHanko 0.37 high-level decryptors do not follow Encrypt reference chains. Their previous source checks remain unchanged. The additional oracle uses pyHanko's unchanged strict low-level parser and actual Standard handler on the terminal dictionary, then cryptography AES-CBC/PKCS7 with each stream's real object number/generation. It compares catalog, page, object and Identity payloads and unchanged O/U bytes, without rewriting input or patching any authentication algorithm. CI pins checksum-verified qpdf 12.4.2; older qpdf versions can emit non-catalog metadata in cleartext under the document-only metadata setting. qpdf 12.4.2 also requires global Length for V4; the omitted-key case preserves all offsets and cipher bytes from the independently verified source, following table 20's fixed 128-bit contract. See security controls and components.

Guide source at the documented commit ↗