Skip to content

Internals Encryption

Dragon edited this page Jun 3, 2026 · 1 revision

Internals: Encryption

This page describes the encryption internals of phppdf. The implementation is AES-256 per PDF 2.0 (R6, AESV3) - not the RC4 or AES-128 schemes documented in earlier PDF revisions.

Encryption

phppdf supports AES-256 encryption (PDF 2.0, R=6, V=4, /CFM /AESV3, /Length 256). The password-hashing algorithm is the recursive Algorithm 2.B defined in ISO 32000-2 (SHA-256/384/512 iterated rounds with AES-128-CBC inner rounds), which is substantially stronger than the MD5-based schemes used in older PDF versions.

How the encryption pass works

When $document->encryption() is configured, output() takes a different path via EncryptedPdfWriter:

  1. The file's master key is derived once via EncryptionKey::fileKey() from the user / owner passwords, the document /ID, and the desired permissions bitmask. This computation implements Algorithm 2.B: each password undergoes a recursive hash loop where the inner key evolves through rounds of AES-128-CBC encryption followed by a SHA-256, SHA-384, or SHA-512 step chosen by the sum of the first 16 bytes of the intermediate ciphertext modulo 3. The process terminates when the last AES-128-CBC round's first 16 output bytes have a sum that triggers the stop condition.
  2. For each indirect object being serialized, a per-object key is derived from the 32-byte file key using the object number and generation number.
  3. ObjectTransformer walks the object graph and rewrites every string ((...) or <...>) and every stream payload using that per-object key with AES-256-CBC (PKCS#7 padding, random 16-byte IV prefixed to the ciphertext).
  4. The trailer's /Encrypt entry references an encryption dictionary that declares the algorithm (/AESV3), key length (/Length 256), version (/V 4), revision (/R 6), permissions flags, and the encoded user / owner password hashes.

Permissions

Encryption::allowPrint(), allowCopy(), allowModify(), etc. set bits in the /P permissions integer. These are hints: a viewer that respects the spec will gray out the print button if allowPrint() is not set, but the underlying data is still readable to anyone who has the user password (or to anyone using a tool that ignores permission flags - they are not cryptographic, just advisory).

See also

Clone this wiki locally