View Source Pdf.Reader.Encryption.V4 (ExPDF v1.0.1)

Implements PDF Standard Security Handler algorithms for V4 (Crypt Filters + AES-128 CBC) — revision R=4.

Algorithms implemented

AlgorithmDescriptionFunction
Alg 6User password auth for V4/R=4authenticate_user/2
Alg 7Owner password auth for V4/R=4authenticate_owner/2
Crypt Filter dispatch (CF dict → cipher atom)select_crypt_filter/3
Stream decryption (AES-128-CBC or RC4, or passthrough)decrypt_stream/5
String decryption (same as stream, uses str_filter)decrypt_string/4

Algorithm 6 (V4 user password authentication, PDF 1.7 § 7.6.3.4)

Identical to Algorithm 5 for V1V2/R≥3 — the V4 extension only adds the EncryptMetadata byte to Algorithm 2 when /EncryptMetadata false. Both conditions are already handled by V1V2.authenticate_user/2, so Algorithm 6 is implemented by direct delegation.

Algorithm 7 (V4 owner password authentication)

Also delegated to V1V2.authenticate_owner/2 — same derivation path.

Crypt Filters (PDF 1.7 § 7.6.5)

V4 introduces per-stream encryption selection via the /CF dictionary. Each named entry in /CF carries a /CFM (Crypt Filter Method):

/CFM valueCipher atom returned
None:identity
V2:rc4
AESV2:aes_128
(unknown):identity

/StmF names the default filter for streams; /StrF for strings. A stream can override via its own /Filter entry (last array element when the value is a list, or the single name when it is a {:name, string}).

Stream and String Decryption

Per-object key derivation (ObjectKey.derive/4) is applied for both RC4 and AES-128 ciphers. For AES-128-CBC (AESV2):

  • First 16 bytes of the ciphertext blob are the IV.
  • Remaining bytes are the actual ciphertext.
  • PKCS7 padding is stripped and validated after decryption.
  • Invalid padding (last byte N is 0, > 16, or padding bytes don't all equal N) returns :error rather than raising (R-ENC14).

For :identity, the bytes are returned unchanged (R-ENC15, R-ENC20).

Spec references

Summary

Functions

Authenticates an owner password for a V4/R=4 handler (Algorithm 7).

Authenticates a user password for a V4/R=4 handler (Algorithm 6).

Decrypts a stream ciphertext blob using the effective crypt filter.

Decrypts a string ciphertext using the effective string crypt filter.

Selects the effective crypt filter cipher for a stream or string.

Functions

Link to this function

authenticate_owner(password, handler)

View Source
@spec authenticate_owner(binary(), Pdf.Reader.Encryption.StandardHandler.t()) ::
  {:ok, binary()} | :error | {:error, :encrypted_unsupported_handler}

Authenticates an owner password for a V4/R=4 handler (Algorithm 7).

Delegates directly to V1V2.authenticate_owner/2.

Returns

  • {:ok, file_key} — owner password authenticated.
  • :error — wrong password.
  • {:error, :encrypted_unsupported_handler} — RC4 unavailable at runtime.
Link to this function

authenticate_user(password, handler)

View Source
@spec authenticate_user(binary(), Pdf.Reader.Encryption.StandardHandler.t()) ::
  {:ok, binary()} | :error | {:error, :encrypted_unsupported_handler}

Authenticates a user password for a V4/R=4 handler (Algorithm 6).

Delegates directly to V1V2.authenticate_user/2 — Algorithm 2 already handles the R=4 EncryptMetadata extension.

Returns

  • {:ok, file_key} — password authenticated.
  • :error — wrong password.
  • {:error, :encrypted_unsupported_handler} — RC4 unavailable at runtime.
Link to this function

decrypt_stream(bytes, stream_dict, obj_num, gen_num, handler)

View Source
@spec decrypt_stream(
  binary(),
  map(),
  non_neg_integer(),
  non_neg_integer(),
  Pdf.Reader.Encryption.StandardHandler.t()
) :: {:ok, binary()} | :error

Decrypts a stream ciphertext blob using the effective crypt filter.

The security_handler must have :file_key populated (set after authentication).

Parameters

  • bytes — raw stream bytes (IV + ciphertext for AES, pure ciphertext for RC4).
  • stream_dict — the stream dictionary for per-stream filter override lookup.
  • obj_num — PDF object number.
  • gen_num — PDF generation number.
  • security_handler — a %StandardHandler{} with :file_key populated.

Returns

  • {:ok, plaintext} — successfully decrypted.
  • :error — invalid PKCS7 padding (AES only) or stream too short for IV.
Link to this function

decrypt_string(bytes, obj_num, gen_num, handler)

View Source
@spec decrypt_string(
  binary(),
  non_neg_integer(),
  non_neg_integer(),
  Pdf.Reader.Encryption.StandardHandler.t()
) :: {:ok, binary()} | :error

Decrypts a string ciphertext using the effective string crypt filter.

Uses str_filter for filter resolution (same decryption logic as streams).

Parameters

  • bytes — raw string bytes (IV + ciphertext for AES, pure ciphertext for RC4).
  • obj_num — PDF object number.
  • gen_num — PDF generation number.
  • security_handler — a %StandardHandler{} with :file_key populated.

Returns

  • {:ok, plaintext} — successfully decrypted.
  • :error — invalid PKCS7 padding (AES only) or insufficient bytes.
Link to this function

select_crypt_filter(stream_dict, handler, kind)

View Source
@spec select_crypt_filter(
  map(),
  Pdf.Reader.Encryption.StandardHandler.t(),
  :stream | :string
) ::
  :identity | :rc4 | :aes_128

Selects the effective crypt filter cipher for a stream or string.

Per PDF 1.7 § 7.6.5.4, the resolution order is:

  1. Check the stream's own /Filter entry for a per-stream crypt filter override (last element when it is a list; the name itself when it is a single {:name, string}).
  2. If no per-stream override is found, use handler.stm_filter (for :stream) or handler.str_filter (for :string).
  3. Look up the resolved filter name in handler.cf and map its /CFM to a cipher atom.

Filter name /Identity (or CFM None) always resolves to :identity.

Parameters

  • stream_dict — the stream or object dictionary (plain %{} map).
  • handler — a %StandardHandler{} with :cf, :stm_filter, :str_filter set.
  • kind:stream or :string.

Returns

:identity | :rc4 | :aes_128