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}