Skip to main content

SecurityHandler

Enum SecurityHandler 

Source
pub enum SecurityHandler {
    Rc4V2 {
        key: SmallKey,
        revision: u8,
        permissions: u32,
        owner_unlocked: bool,
        encrypt_metadata: bool,
        encoding: PasswordEncoding,
        embedded_cipher: Option<Cipher>,
        strings_identity: bool,
    },
    AesV4 {
        key: SmallKey,
        revision: u8,
        permissions: u32,
        owner_unlocked: bool,
        encrypt_metadata: bool,
        encoding: PasswordEncoding,
        embedded_cipher: Option<Cipher>,
        strings_identity: bool,
    },
    AesV5 {
        key: Box<[u8; 32]>,
        revision: u8,
        permissions: u32,
        owner_unlocked: bool,
        encrypt_metadata: bool,
        encoding: PasswordEncoding,
        embedded_cipher: Option<Cipher>,
        strings_identity: bool,
    },
    Identity,
}
Expand description

A document’s decryption state: which cipher, which key, and what the password unlocked.

A closed enum rather than a trait, because the set of standard security handlers is closed: adding a variant must break every match on it. Rc4V2 covers /V 1 to /V 4 without an AES crypt filter; AesV4 is AESV2, whose per-object key is an MD5 over the object number and the four bytes sAlT; AesV5 is AESV3, whose 32-byte key is used verbatim with no per-object derivation; Identity is both an unencrypted document and one naming /Identity as its crypt filter.

Variants§

§

Rc4V2

RC4, with a 5- to 16-byte file key.

Fields

§key: SmallKey

The file encryption key.

§revision: u8

/R, the handler revision.

§permissions: u32

/P, as the unsigned word permissions are compared as.

§owner_unlocked: bool

Whether the owner password was the one that opened the document.

§encrypt_metadata: bool

/EncryptMetadata.

§encoding: PasswordEncoding

Which password spelling worked.

§embedded_cipher: Option<Cipher>

The cipher /EFF names for embedded file streams, when it differs from this variant’s own (ISO 32000-1 §7.6.5 table 20). None is table 20’s default: the embedded class uses the stream cipher.

§strings_identity: bool

[oracle-bug] Whether /StrF resolved to /Identity while /StmF did not, so strings pass through undeciphered while streams are enciphered. §7.6.5 makes the two entries independent, so such a document is conformant and is opened.

§

AesV4

AESV2: a 16- or 24-byte file key with per-object sAlT derivation.

Fields

§key: SmallKey

The file encryption key.

§revision: u8

/R, the handler revision.

§permissions: u32

/P, as the unsigned word permissions are compared as.

§owner_unlocked: bool

Whether the owner password was the one that opened the document.

§encrypt_metadata: bool

/EncryptMetadata.

§encoding: PasswordEncoding

Which password spelling worked.

§embedded_cipher: Option<Cipher>

The cipher /EFF names for embedded file streams, when it differs from this variant’s own (ISO 32000-1 §7.6.5 table 20). None is table 20’s default: the embedded class uses the stream cipher.

§strings_identity: bool

[oracle-bug] Whether /StrF resolved to /Identity while /StmF did not, so strings pass through undeciphered while streams are enciphered. §7.6.5 makes the two entries independent, so such a document is conformant and is opened.

§

AesV5

AESV3 (/V 5, revision 5 or 6): the 32-byte key is used as-is.

Fields

§key: Box<[u8; 32]>

The file encryption key.

§revision: u8

/R, the handler revision.

§permissions: u32

/P, as the unsigned word permissions are compared as.

§owner_unlocked: bool

Whether the owner password was the one that opened the document.

§encrypt_metadata: bool

/EncryptMetadata.

§encoding: PasswordEncoding

Which password spelling worked.

§embedded_cipher: Option<Cipher>

The cipher /EFF names for embedded file streams, when it differs from this variant’s own (ISO 32000-1 §7.6.5 table 20). None is table 20’s default: the embedded class uses the stream cipher.

§strings_identity: bool

[oracle-bug] Whether /StrF resolved to /Identity while /StmF did not, so strings pass through undeciphered while streams are enciphered. §7.6.5 makes the two entries independent, so such a document is conformant and is opened.

§

Identity

No encryption, or /StrF /Identity.

Implementations§

Source§

impl SecurityHandler

Source

pub fn from_encrypt_dict( dict: &Dict, file_id: &[u8], password: &[u8], r: &impl Resolve, ) -> Result<Self, Error>

Build a handler from the trailer’s /Encrypt dictionary.

file_id is the first element of the trailer’s /ID array as raw bytes; pass &[] when /ID is absent, which contributes nothing to the key rather than an empty marker. password is raw bytes, uncapped — the specification’s 127-byte limit is not enforced. A non-empty password is tried as the owner password first and only then as the user password; an empty one is only ever a user password.

use pdfrum_crypt::{Error, SecurityHandler};
use pdfrum_object::{Dict, NoResolve, Object, PdfString, names};

// A public-key handler is not the standard one.
let dict = Dict::from_pairs([(
    names::FILTER.clone(),
    Object::Name(pdfrum_object::Name::from("Adobe.PubSec")),
)]);
assert!(matches!(
    SecurityHandler::from_encrypt_dict(&dict, &[], b"", &NoResolve),
    Err(Error::UnsupportedHandler(_))
));
§Errors

Error::WrongPassword when neither role accepts the password, and the parse errors of parse_encrypt_dict when the dictionary itself cannot be used.

Source

pub const fn strings_identity(&self) -> bool

[oracle-bug] Whether the string class resolved to /Identity while the stream class did not, so strings in this document are plaintext.

Always false for Self::Identity, which has nothing to contrast against — an unencrypted document’s strings are plaintext anyway.

Source

pub fn decrypt(&self, obj: ObjRef, class: CryptClass, data: &[u8]) -> Vec<u8>

Decrypt one string or stream payload belonging to indirect object obj.

Infallible by design: PDFium never fails a decrypt, it produces a best-effort result. An AES payload shorter than seventeen bytes, a trailing partial block, and a final block whose padding byte is out of range all yield less output than input rather than an error.

obj must be the enclosing indirect object, not a nested one: a direct string inside an indirect dictionary is keyed by the dictionary’s number and generation.

use pdfrum_crypt::{CryptClass, SecurityHandler};
use pdfrum_object::ObjRef;

// Fewer than seventeen bytes of AES ciphertext is all initialisation
// vector and no payload.
let handler = SecurityHandler::AesV5 {
    key: Box::new([0; 32]),
    revision: 6,
    permissions: 0xFFFF_FFFC,
    owner_unlocked: false,
    encrypt_metadata: true,
    encoding: pdfrum_crypt::PasswordEncoding::AsGiven,
    embedded_cipher: None,   // no /EFF: the stream cipher serves
    strings_identity: false,
};
assert!(handler.decrypt(ObjRef::new(4, 0), CryptClass::String, &[0; 16]).is_empty());
Source

pub fn embedded_cipher(&self) -> Option<Cipher>

The /EFF cipher override, or None when the embedded class uses the stream cipher — which ISO 32000-1 §7.6.5 table 20 makes the default.

Source

pub fn encrypt( &self, obj: ObjRef, class: CryptClass, iv: Iv, data: &[u8], ) -> Vec<u8>

Encipher one string or stream payload belonging to indirect object obj, the inverse of SecurityHandler::decrypt.

iv must be fresh per payload for the cipher to be sound; the RC4 handler and Self::Identity ignore it. Infallible, like its inverse.

RC4 preserves length exactly. AES grows a payload of n bytes to 32 + 16 * (n / 16): sixteen for the vector, and a PKCS#7 pad that is always present, so an already block-aligned payload gains a whole block. An empty payload is the exception and stays empty, so a save does not grow every empty string in a document by 32 bytes.

use pdfrum_crypt::{CryptClass, Iv, SecurityHandler};
use pdfrum_object::ObjRef;

let handler = SecurityHandler::AesV5 {
    key: Box::new([0; 32]),
    revision: 6,
    permissions: 0xFFFF_FFFC,
    owner_unlocked: false,
    encrypt_metadata: true,
    encoding: pdfrum_crypt::PasswordEncoding::AsGiven,
    embedded_cipher: None,   // no /EFF: the stream cipher serves
    strings_identity: false,
};
let obj = ObjRef::new(4, 0);
let sealed = handler.encrypt(obj, CryptClass::String, Iv([7; 16]), b"secret");
// Sixteen of vector, one block of ciphertext.
assert_eq!(sealed.len(), 32);
assert_eq!(handler.decrypt(obj, CryptClass::String, &sealed), b"secret");
Source

pub fn permissions(&self) -> Permissions

What the document permits, for the password that opened it.

A document opened with the owner password reports what /P allows, the same as a user reading of it; the owner’s own unrestricted view is SecurityHandler::owner_permissions. An unencrypted document has no restrictions at all.

The ISO table-22 decode lives in Permissions, next to the /P word, so no caller has to spell bits & 0x100.

assert_eq!(SecurityHandler::Identity.permissions(), Permissions::ALL);
Source

pub fn owner_permissions(&self) -> Permissions

What the document permits under the owner’s view.

A document the owner password opened reports every permission granted here, whatever /P says, because the owner may lift every restriction. For a document the user password opened — and for an unencrypted one — this is the same answer as SecurityHandler::permissions.

assert_eq!(SecurityHandler::Identity.owner_permissions(), Permissions::ALL);
Source

pub fn encrypt_metadata(&self) -> bool

Whether the document’s metadata stream is encrypted (/EncryptMetadata, default true).

The parser consults this to decide whether to skip decrypting the object /Root/Metadata points at.

Source

pub fn revision(&self) -> u8

/R, the handler revision. Zero for an unencrypted document.

Source

pub fn owner_unlocked(&self) -> bool

Whether the owner password opened this document.

Source

pub fn password_encoding(&self) -> PasswordEncoding

Which spelling of the password unlocked the document.

Trait Implementations§

Source§

impl Clone for SecurityHandler

Source§

fn clone(&self) -> SecurityHandler

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SecurityHandler

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for SecurityHandler

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.