Skip to main content

pdfluent/
encrypt.rs

1//! Encryption, decryption, and permissions.
2
3/// Encryption algorithm.
4///
5/// # 1.0 behaviour
6///
7/// Both [`Aes128`](Self::Aes128) and [`Aes256`](Self::Aes256) currently
8/// produce AES-256 output at the crypto layer because
9/// `lopdf::aes256_encryption_state` is the only AES helper exposed by our
10/// underlying `lopdf` fork today. This means selecting `Aes128` yields
11/// **stronger** encryption than its name advertises, not weaker โ€” safe
12/// but misleading. A true AES-128 path (PDF 1.6 V=4, R=4) lands in a
13/// post-1.0 follow-up.
14///
15/// Recommended: use [`Aes256`](Self::Aes256) explicitly to match what
16/// actually happens.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum EncryptionAlgorithm {
20    /// AES-128 selector. **Currently routed to AES-256** โ€” see the enum
21    /// doc-comment.
22    Aes128,
23    /// AES with 256-bit key (PDF 1.7 ext / 2.0). **Recommended.**
24    Aes256,
25}
26
27/// Permissions granted on an encrypted PDF.
28///
29/// Construct via presets ([`Permissions::full_access`],
30/// [`Permissions::print_only`], [`Permissions::read_only`],
31/// [`Permissions::annotate`]) or the individual `with_*` methods for fine
32/// control.
33///
34/// # Accessibility
35///
36/// All presets enable `extract_accessibility`. Per **ISO 32000-2 ยง7.6.4.2**,
37/// a PDF consumer is required to honour accessibility-extraction regardless
38/// of the author's permission bits. Setting `extract_accessibility = false`
39/// via [`Permissions::with_extract_accessibility`] is therefore advisory; a
40/// spec-compliant reader will still allow screen-reader access.
41#[derive(Debug, Clone, Copy)]
42pub struct Permissions {
43    pub(crate) print: bool,
44    pub(crate) modify: bool,
45    pub(crate) copy: bool,
46    pub(crate) annotate: bool,
47    pub(crate) fill_forms: bool,
48    pub(crate) extract_accessibility: bool,
49    pub(crate) assemble: bool,
50    pub(crate) print_high_quality: bool,
51}
52
53impl Permissions {
54    /// All permissions allowed. Use this when you only want encryption for
55    /// confidentiality, not to restrict what authorised readers can do.
56    pub const fn full_access() -> Self {
57        Self {
58            print: true,
59            modify: true,
60            copy: true,
61            annotate: true,
62            fill_forms: true,
63            extract_accessibility: true,
64            assemble: true,
65            print_high_quality: true,
66        }
67    }
68
69    /// Printing allowed (including high-quality); all other operations
70    /// denied. Accessibility-extraction remains enabled per ISO 32000-2.
71    pub const fn print_only() -> Self {
72        Self {
73            print: true,
74            modify: false,
75            copy: false,
76            annotate: false,
77            fill_forms: false,
78            extract_accessibility: true,
79            assemble: false,
80            print_high_quality: true,
81        }
82    }
83
84    /// All operations denied. Accessibility-extraction remains enabled per
85    /// ISO 32000-2 โ€” assistive technologies retain access to the content.
86    pub const fn read_only() -> Self {
87        Self {
88            print: false,
89            modify: false,
90            copy: false,
91            annotate: false,
92            fill_forms: false,
93            extract_accessibility: true,
94            assemble: false,
95            print_high_quality: false,
96        }
97    }
98
99    /// Allow commenting, form filling, copying, and printing. Denies
100    /// document-structure modification and page assembly.
101    pub const fn annotate() -> Self {
102        Self {
103            print: true,
104            modify: false,
105            copy: true,
106            annotate: true,
107            fill_forms: true,
108            extract_accessibility: true,
109            assemble: false,
110            print_high_quality: true,
111        }
112    }
113
114    /// Allow / deny printing (low resolution).
115    pub const fn with_print(mut self, v: bool) -> Self {
116        self.print = v;
117        self
118    }
119
120    /// Allow / deny modification.
121    pub const fn with_modify(mut self, v: bool) -> Self {
122        self.modify = v;
123        self
124    }
125
126    /// Allow / deny copying text and graphics.
127    pub const fn with_copy(mut self, v: bool) -> Self {
128        self.copy = v;
129        self
130    }
131
132    /// Allow / deny adding/editing annotations.
133    pub const fn with_annotate(mut self, v: bool) -> Self {
134        self.annotate = v;
135        self
136    }
137
138    /// Allow / deny filling form fields.
139    pub const fn with_fill_forms(mut self, v: bool) -> Self {
140        self.fill_forms = v;
141        self
142    }
143
144    /// Allow / deny accessibility extraction (screen readers).
145    pub const fn with_extract_accessibility(mut self, v: bool) -> Self {
146        self.extract_accessibility = v;
147        self
148    }
149
150    /// Allow / deny assembling (insert/rotate/delete pages).
151    pub const fn with_assemble(mut self, v: bool) -> Self {
152        self.assemble = v;
153        self
154    }
155
156    /// Allow / deny high-resolution printing.
157    pub const fn with_print_high_quality(mut self, v: bool) -> Self {
158        self.print_high_quality = v;
159        self
160    }
161}
162
163/// Options for encrypting a document.
164///
165/// Used both to encrypt an un-encrypted document and to re-encrypt an
166/// already-encrypted one (requires [`crate::PdfDocument::decrypt`] first).
167#[derive(Debug, Clone)]
168#[non_exhaustive]
169pub struct EncryptOptions {
170    // Captured at construction by `aes128()` / `aes256()`; read by the
171    // `PdfDocument::encrypt` body when wired in Epic 2 #1244. Marked with
172    // an explicit allow so the scaffold phase does not fail `-D warnings`.
173    #[allow(dead_code)]
174    pub(crate) algorithm: EncryptionAlgorithm,
175    pub(crate) user_password: Option<String>,
176    pub(crate) owner_password: Option<String>,
177    pub(crate) permissions: Permissions,
178}
179
180impl Default for EncryptOptions {
181    fn default() -> Self {
182        Self::aes256()
183    }
184}
185
186impl EncryptOptions {
187    /// AES-256 with [`Permissions::full_access`]. Intended for the common
188    /// case: "encrypt to protect the bytes in transit, let authorised
189    /// readers do anything."
190    ///
191    /// Override with [`with_permissions`](Self::with_permissions) if you
192    /// want to restrict operations (e.g. [`Permissions::print_only`]).
193    pub fn aes256() -> Self {
194        Self {
195            algorithm: EncryptionAlgorithm::Aes256,
196            user_password: None,
197            owner_password: None,
198            permissions: Permissions::full_access(),
199        }
200    }
201
202    /// AES-128 with [`Permissions::full_access`]. Prefer
203    /// [`EncryptOptions::aes256`] unless targeting readers older than PDF
204    /// 1.7 ext.
205    pub fn aes128() -> Self {
206        Self {
207            algorithm: EncryptionAlgorithm::Aes128,
208            ..Self::aes256()
209        }
210    }
211
212    /// Set a user password (required to open).
213    pub fn with_user_password(mut self, pw: impl Into<String>) -> Self {
214        self.user_password = Some(pw.into());
215        self
216    }
217
218    /// Set an owner password (required to change permissions).
219    pub fn with_owner_password(mut self, pw: impl Into<String>) -> Self {
220        self.owner_password = Some(pw.into());
221        self
222    }
223
224    /// Restrict permissions.
225    pub fn with_permissions(mut self, p: Permissions) -> Self {
226        self.permissions = p;
227        self
228    }
229}