Skip to main content

pdfboss_core/
crypt.rs

1//! Decryption for the Standard security handler (ISO 32000 §7.6), opened
2//! with the user or the owner password (the empty user password opens
3//! transparently).
4//!
5//! Handles RC4 (`/V` 1–2, `/R` 2–3, 40–128-bit), AESV2 (`/V` 4, 128-bit
6//! AES-CBC) and AESV3 (`/V` 5, `/R` 5–6, 256-bit AES-CBC). V4 and V5 files
7//! pick the cipher per crypt filter: `/StmF` for streams, `/StrF` for
8//! strings and `/EFF` for embedded files, the standard `Identity` filter
9//! included. Documents whose
10//! password the caller does not supply are reported as encrypted. The
11//! primitives — MD5, RC4, AES and the SHA-2 family — are implemented here from
12//! their published specifications so the crate needs no cryptographic
13//! dependency.
14//!
15//! This is low-level API: [`crate::Document`] applies it automatically when
16//! opening an encrypted file, and most callers never name [`Decryptor`]
17//! directly. It is public for code that drives object reads itself, as the
18//! asynchronous API does.
19//!
20//! [`Encryptor`] is the write side, AES-256 (`/V` 5, `/R` 6) only: it builds
21//! the complete `/Encrypt` dictionary and encrypts an object's strings and
22//! stream data in place, the exact inverse of [`Decryptor::decrypt_object`].
23
24use crate::object::{Dict, Name, ObjRef, Object};
25
26/// Password padding string (ISO 32000 §7.6.3.3, Algorithm 2, step (a)).
27const PAD: [u8; 32] = [
28    0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
29    0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
30];
31
32/// The method a crypt filter applies to strings and streams.
33#[derive(Clone, Copy, PartialEq, Debug)]
34enum Cipher {
35    /// The standard `Identity` crypt filter: data passes through unchanged
36    /// (ISO 32000-1 Table 26).
37    Identity,
38    /// RC4 stream cipher, per-object key (V1/V2, and V4 with `/CFM /V2`).
39    Rc4,
40    /// AES-128-CBC, per-object key with the `sAlT` suffix (V4, `/CFM /AESV2`).
41    Aesv2,
42    /// AES-256-CBC, the file key applied directly (V5, `/CFM /AESV3`).
43    Aesv3,
44}
45
46/// A configured Standard-handler decryptor for an opened document.
47#[derive(Clone)]
48pub struct Decryptor {
49    /// The file key (`n` bytes for RC4/AESV2, 32 for AESV3).
50    key: Vec<u8>,
51    /// The ciphers for streams, strings and embedded files.
52    filters: CryptFilters,
53    /// The `/Encrypt` dictionary's `/EncryptMetadata` value (default true
54    /// when absent): when false, a stream whose own dictionary says
55    /// `/Type /Metadata` was stored in plaintext and must not be decrypted.
56    encrypt_metadata: bool,
57}
58
59/// The ciphers an encryption dictionary assigns: V4 and V5 files declare
60/// crypt filters in `/CF` and pick among them with `/StmF` for streams,
61/// `/StrF` for strings and `/EFF` for embedded file streams (ISO 32000-1
62/// Table 20 and Table 25); V1 and V2 files apply RC4 to everything.
63#[derive(Clone)]
64struct CryptFilters {
65    /// The `/CF` entries with a supported `/CFM`, by name, for streams that
66    /// pick one through their own `/Crypt` filter. `Identity` is never
67    /// listed: the standard filter always passes data through.
68    named: Vec<(String, Cipher)>,
69    /// `/StmF`: the cipher for streams (Identity when absent).
70    stream: Cipher,
71    /// `/StrF`: the cipher for strings (Identity when absent).
72    string: Cipher,
73    /// `/EFF`: the cipher for `/Type /EmbeddedFile` streams (`/StmF` when absent).
74    embedded_file: Cipher,
75}
76
77impl CryptFilters {
78    /// One cipher for strings, streams and embedded files alike.
79    fn uniform(cipher: Cipher) -> CryptFilters {
80        CryptFilters {
81            named: Vec::new(),
82            stream: cipher,
83            string: cipher,
84            embedded_file: cipher,
85        }
86    }
87
88    /// Reads `/CF`, `/StmF`, `/StrF` and `/EFF`. The standard `Identity`
89    /// filter needs no `/CF` entry and any entry of that name is ignored
90    /// (Table 20). `None` when a filter one of the three defaults names is
91    /// missing from `/CF` or has a `/CFM` other than `V2`, `AESV2` or
92    /// `AESV3`, which Table 25 says a reader shall report as an unsupported
93    /// algorithm; a `/CF` entry nothing names may say what it likes.
94    ///
95    /// Covers ISO 32000-1 §7.6.5 and §7.6.3.2.
96    fn parse(enc: &Dict) -> Option<CryptFilters> {
97        let named: Vec<(String, Cipher)> = enc
98            .get_dict("CF")
99            .into_iter()
100            .flat_map(Dict::iter)
101            .filter(|(name, _)| name.0 != "Identity")
102            .filter_map(|(name, filter)| Some((name.0.clone(), cipher_method(filter.as_dict()?)?)))
103            .collect();
104        let filters = CryptFilters {
105            named,
106            stream: Cipher::Identity,
107            string: Cipher::Identity,
108            embedded_file: Cipher::Identity,
109        };
110        let pick = |key: &str, default: Cipher| match enc.get_name(key) {
111            None => Some(default),
112            Some(n) => filters.named(&n.0),
113        };
114        let stream = pick("StmF", Cipher::Identity)?;
115        let string = pick("StrF", Cipher::Identity)?;
116        let embedded_file = pick("EFF", stream)?;
117        Some(CryptFilters {
118            stream,
119            string,
120            embedded_file,
121            ..filters
122        })
123    }
124
125    /// The cipher of the crypt filter called `name`: the standard
126    /// `Identity`, or the `/CF` entry of that name if its method is
127    /// supported.
128    fn named(&self, name: &str) -> Option<Cipher> {
129        if name == "Identity" {
130            return Some(Cipher::Identity);
131        }
132        self.named
133            .iter()
134            .find(|(n, _)| n == name)
135            .map(|(_, cipher)| *cipher)
136    }
137}
138
139/// The crypt filter a stream names for itself: a `/Crypt` entry first in
140/// its `/Filter` (ISO 32000-1 §7.4.10) selects the `/Name` of the matching
141/// `/DecodeParms` dictionary, `Identity` when that is absent. `None` when
142/// the stream has no `/Crypt` filter. Indirect `/Filter` or `/DecodeParms`
143/// values cannot be followed here (the decryptor has no resolver) and read
144/// as absent.
145///
146/// Covers ISO 32000-1 §7.4.10.
147fn stream_crypt_filter(dict: &Dict) -> Option<String> {
148    let first = match dict.get("Filter")? {
149        Object::Name(n) => n,
150        Object::Array(items) => items.first()?.as_name()?,
151        _ => return None,
152    };
153    if first.0 != "Crypt" {
154        return None;
155    }
156    let parms = match dict.get("DecodeParms") {
157        Some(Object::Dict(d)) => Some(d),
158        Some(Object::Array(items)) => items.first().and_then(Object::as_dict),
159        _ => None,
160    };
161    let name = parms
162        .and_then(|p| p.get_name("Name"))
163        .map_or("Identity", |n| n.0.as_str());
164    Some(name.to_string())
165}
166
167/// The indirect references among an encryption dictionary's crypt filters:
168/// `/CF` itself when it is one, otherwise the entries of `/CF` that are.
169/// Only the strings of an encryption dictionary must be direct (ISO 32000-1
170/// §7.6.1), so a writer may store the crypt filters as separate objects; a
171/// caller fetches these and hands them to [`direct_crypt_filters`] before
172/// building a [`Decryptor`]. Call the pair twice when `/CF` is itself a
173/// reference, since its entries only become visible once it is fetched.
174///
175/// ```
176/// use pdfboss_core::{crypt_filter_refs, Dict, Name, ObjRef, Object};
177///
178/// let mut enc = Dict::new();
179/// enc.insert(Name("CF".into()), Object::Ref(ObjRef { num: 7, gen: 0 }));
180/// assert_eq!(crypt_filter_refs(&enc), vec![ObjRef { num: 7, gen: 0 }]);
181/// ```
182///
183/// Covers ISO 32000-1 §7.6.3.2.
184pub fn crypt_filter_refs(enc: &Dict) -> Vec<ObjRef> {
185    match enc.get("CF") {
186        Some(Object::Ref(r)) => vec![*r],
187        Some(Object::Dict(cf)) => cf.iter().filter_map(|(_, v)| v.as_ref()).collect(),
188        _ => Vec::new(),
189    }
190}
191
192/// `enc` with `/CF`, and each entry of it, replaced by `lookup`'s value
193/// when it is an indirect reference; a reference `lookup` cannot supply
194/// stays as it is, and the filter it names then counts as missing.
195///
196/// ```
197/// use pdfboss_core::{direct_crypt_filters, Dict, Name, ObjRef, Object};
198///
199/// let mut enc = Dict::new();
200/// enc.insert(Name("CF".into()), Object::Ref(ObjRef { num: 7, gen: 0 }));
201/// let direct = direct_crypt_filters(&enc, |_| Some(Object::Dict(Dict::new())));
202/// assert!(direct.get_dict("CF").is_some());
203/// ```
204///
205/// Covers ISO 32000-1 §7.6.3.2.
206pub fn direct_crypt_filters(enc: &Dict, mut lookup: impl FnMut(ObjRef) -> Option<Object>) -> Dict {
207    let mut out = enc.clone();
208    let cf = match out.get("CF") {
209        Some(Object::Ref(r)) => lookup(*r),
210        Some(Object::Dict(cf)) => Some(Object::Dict(cf.clone())),
211        _ => None,
212    };
213    let Some(Object::Dict(mut cf)) = cf else {
214        return out;
215    };
216    for value in cf.values_mut() {
217        if let Object::Ref(r) = *value {
218            if let Some(direct) = lookup(r) {
219                *value = direct;
220            }
221        }
222    }
223    out.insert(Name("CF".to_string()), Object::Dict(cf));
224    out
225}
226
227/// The cipher a crypt filter dictionary's `/CFM` selects; `None` for the
228/// value `None` (the security handler would decrypt by itself, which the
229/// Standard handler never does), for an absent method and for unknown ones.
230///
231/// Covers ISO 32000-1 §7.6.5.
232fn cipher_method(filter: &Dict) -> Option<Cipher> {
233    match filter.get_name("CFM")?.0.as_str() {
234        "V2" => Some(Cipher::Rc4),
235        "AESV2" => Some(Cipher::Aesv2),
236        "AESV3" => Some(Cipher::Aesv3),
237        _ => None,
238    }
239}
240
241/// The V4 file key length in bytes: the encryption dictionary's `/Length`
242/// in bits when present, else the `/Length` of the stream or the string
243/// crypt filter, which the Standard handler writes in bytes (Table 25: "16
244/// means 128"; a value of 40 or more is taken as bits), else 16.
245///
246/// Covers ISO 32000-1 §7.6.5.
247fn v4_key_length(enc: &Dict) -> usize {
248    let filter_length = ["StmF", "StrF"].iter().find_map(|key| {
249        let name = enc.get_name(key)?;
250        enc.get_dict("CF")?.get_dict(&name.0)?.get_int("Length")
251    });
252    let bytes = match (enc.get_int("Length"), filter_length) {
253        (Some(bits), _) => bits / 8,
254        (None, Some(len)) if len >= 40 => len / 8,
255        (None, Some(len)) => len,
256        (None, None) => 16,
257    };
258    bytes.clamp(5, 16) as usize
259}
260
261/// The `/Encrypt` dictionary's `/EncryptMetadata` flag, true when absent
262/// (ISO 32000-2 §7.6.4.2, Table 20).
263///
264/// Covers ISO 32000-1 §7.6.3.2.
265fn encrypt_metadata_flag(enc: &Dict) -> bool {
266    enc.get("EncryptMetadata")
267        .and_then(Object::as_bool)
268        .unwrap_or(true)
269}
270
271impl Decryptor {
272    /// Builds a decryptor from the resolved `/Encrypt` dictionary and the first
273    /// `/ID` element, assuming the empty user password. Returns `None` when the
274    /// handler or its parameters are unsupported, or the empty password does
275    /// not open the file (so the caller can report it as unsupported).
276    ///
277    /// This is low-level: [`crate::Document`] configures decryption itself, and
278    /// a caller only reaches for this when driving object reads directly — as
279    /// the asynchronous API does.
280    ///
281    /// ```
282    /// use pdfboss_core::{Decryptor, Dict};
283    ///
284    /// // An empty dictionary names no handler, so there is nothing to build.
285    /// assert!(Decryptor::from_standard(&Dict::default(), &[]).is_none());
286    /// ```
287    ///
288    /// Covers ISO 32000-1 §7.6.3.
289    pub fn from_standard(enc: &Dict, id0: &[u8]) -> Option<Decryptor> {
290        Decryptor::from_standard_with_password(enc, id0, b"")
291    }
292
293    /// [`Decryptor::from_standard_with_password`] for a text password: the
294    /// UTF-8 bytes are tried first and, when they differ and every char
295    /// fits, the Latin-1 bytes as well — the legacy revisions hash raw
296    /// bytes without naming an encoding, and real files use both.
297    pub fn from_standard_with_password_str(
298        enc: &Dict,
299        id0: &[u8],
300        password: &str,
301    ) -> Option<Decryptor> {
302        let dec = Decryptor::from_standard_with_password(enc, id0, password.as_bytes());
303        if dec.is_some() || password.is_ascii() || password.chars().any(|c| (c as u32) > 255) {
304            return dec;
305        }
306        let latin1: Vec<u8> = password.chars().map(|c| c as u8).collect();
307        Decryptor::from_standard_with_password(enc, id0, &latin1)
308    }
309
310    /// [`Decryptor::from_standard`] with a caller-supplied password, tried
311    /// first as the user password and then as the owner password (which
312    /// recovers the user-level key, ISO 32000 §7.6.3.4 Algorithm 7 for the
313    /// RC4/AES-128 revisions, §7.6.4.3.3 for AES-256). `None` when the
314    /// password opens nothing.
315    ///
316    /// Covers ISO 32000-1 §7.6.3, §7.6.3.2 and §7.6.5.
317    pub fn from_standard_with_password(
318        enc: &Dict,
319        id0: &[u8],
320        password: &[u8],
321    ) -> Option<Decryptor> {
322        if enc.get_name("Filter").map(|n| n.0.as_str()) != Some("Standard") {
323            return None;
324        }
325        let v = enc.get_int("V").unwrap_or(0);
326        let r = enc.get_int("R").unwrap_or(0);
327        let encrypt_metadata = encrypt_metadata_flag(enc);
328        let (key, filters) = match (v, r) {
329            // RC4: V1 (40-bit) and V2 (up to 128-bit).
330            (1 | 2, 2 | 3) => {
331                let n = if v == 1 {
332                    5
333                } else {
334                    (enc.get_int("Length").unwrap_or(40) / 8).clamp(5, 16) as usize
335                };
336                let key = rc4_family_key(enc, id0, r, n, password)?;
337                (key, CryptFilters::uniform(Cipher::Rc4))
338            }
339            // V4: RC4 or AES-128 under the legacy key algorithm, the ciphers
340            // chosen by the crypt filters.
341            (4, 4) => {
342                let filters = CryptFilters::parse(enc)?;
343                let key = rc4_family_key(enc, id0, r, v4_key_length(enc), password)?;
344                (key, filters)
345            }
346            // V5: AES-256 with SHA-2-based key derivation, the ciphers
347            // chosen by the crypt filters.
348            (5, 5 | 6) => {
349                let filters = CryptFilters::parse(enc)?;
350                let key = aesv3_key(enc, r, password)?;
351                (key, filters)
352            }
353            _ => return None,
354        };
355        Some(Decryptor {
356            key,
357            filters,
358            encrypt_metadata,
359        })
360    }
361
362    /// Decrypts one indirect object's strings and stream data in place. Objects
363    /// extracted from object streams are already plaintext and must not be
364    /// passed here.
365    ///
366    /// Low-level, like [`Decryptor::from_standard`]: [`crate::Document`] applies
367    /// this itself as it loads objects, and a caller only reaches for it when
368    /// driving object reads directly — as the asynchronous API does.
369    ///
370    /// ```
371    /// use pdfboss_core::{Decryptor, Object};
372    ///
373    /// // Reachable from outside the crate; exercising it needs an encrypted
374    /// // document, so this only pins the signature and the visibility.
375    /// let apply: fn(&Decryptor, &mut Object, u32, u16) = Decryptor::decrypt_object;
376    /// let _ = apply;
377    /// ```
378    pub fn decrypt_object(&self, obj: &mut Object, num: u32, gen: u16) {
379        self.decrypt_in_place(obj, num, gen);
380    }
381
382    /// Recursively decrypts every string and stream body reachable from
383    /// `obj`: strings under the string filter, stream data under the stream
384    /// filter, or the embedded file filter for a `/Type /EmbeddedFile`
385    /// stream. A stream with a `/Crypt` filter of its own names the crypt
386    /// filter instead and is decrypted with the file key as is, Algorithm 1
387    /// not applied (ISO 32000-1 §7.4.10); a name that matches no `/CF`
388    /// entry leaves the data as stored. When `encrypt_metadata` is false,
389    /// a stream whose own dictionary says `/Type /Metadata` was stored in
390    /// plaintext (ISO 32000-2 §7.6.4.2, Table 20) and its data is left
391    /// alone; the dictionary's own values still walk normally.
392    ///
393    /// Covers ISO 32000-1 §7.6.2, §7.6.5 and §7.4.10.
394    fn decrypt_in_place(&self, obj: &mut Object, num: u32, gen: u16) {
395        match obj {
396            Object::String(bytes) => self.apply(self.filters.string, num, gen, bytes),
397            Object::Array(items) => items
398                .iter_mut()
399                .for_each(|it| self.decrypt_in_place(it, num, gen)),
400            Object::Dict(dict) => dict
401                .values_mut()
402                .for_each(|v| self.decrypt_in_place(v, num, gen)),
403            Object::Stream(stream) => {
404                stream
405                    .dict
406                    .values_mut()
407                    .for_each(|v| self.decrypt_in_place(v, num, gen));
408                if !self.encrypt_metadata && is_metadata_stream(&stream.dict) {
409                    return;
410                }
411                if let Some(name) = stream_crypt_filter(&stream.dict) {
412                    match self.filters.named(&name) {
413                        Some(Cipher::Identity) | None => {}
414                        Some(cipher) => {
415                            stream.data = decrypt_bytes(cipher, &self.key, &stream.data)
416                        }
417                    }
418                    return;
419                }
420                let cipher = if is_embedded_file_stream(&stream.dict) {
421                    self.filters.embedded_file
422                } else {
423                    self.filters.stream
424                };
425                self.apply(cipher, num, gen, &mut stream.data);
426            }
427            _ => {}
428        }
429    }
430
431    /// Decrypts one string or stream body in place under `cipher` with the
432    /// key Algorithm 1 derives for object `num gen`; the Identity filter
433    /// leaves it as stored.
434    fn apply(&self, cipher: Cipher, num: u32, gen: u16, data: &mut Vec<u8>) {
435        let key = match cipher {
436            Cipher::Identity => return,
437            Cipher::Aesv3 => self.key.clone(), // one file key for every object
438            Cipher::Rc4 | Cipher::Aesv2 => self.object_key(cipher, num, gen),
439        };
440        *data = decrypt_bytes(cipher, &key, data);
441    }
442
443    /// Per-object key: `MD5(filekey ++ num[0..3] ++ gen[0..2] [++ "sAlT"])`
444    /// truncated to `min(n + 5, 16)` bytes for RC4 (ISO 32000 §7.6.2,
445    /// Algorithm 1). AESV2 is AES-128, so it adds the `sAlT` suffix and
446    /// keeps the whole 16-byte digest whatever the file key's length.
447    ///
448    /// Covers ISO 32000-1 §7.6.2.
449    fn object_key(&self, cipher: Cipher, num: u32, gen: u16) -> Vec<u8> {
450        let mut input = Vec::with_capacity(self.key.len() + 9);
451        input.extend_from_slice(&self.key);
452        input.extend_from_slice(&num.to_le_bytes()[..3]);
453        input.extend_from_slice(&gen.to_le_bytes()[..2]);
454        if cipher == Cipher::Aesv2 {
455            input.extend_from_slice(b"sAlT");
456        }
457        let digest = md5(&input);
458        let n = match cipher {
459            Cipher::Aesv2 => 16,
460            _ => (self.key.len() + 5).min(16),
461        };
462        digest[..n].to_vec()
463    }
464}
465
466/// Whether `dict` names `/Type /Metadata`.
467fn is_metadata_stream(dict: &Dict) -> bool {
468    dict.get_name("Type").map(|n| n.0.as_str()) == Some("Metadata")
469}
470
471/// Whether `dict` names `/Type /EmbeddedFile`.
472fn is_embedded_file_stream(dict: &Dict) -> bool {
473    dict.get_name("Type").map(|n| n.0.as_str()) == Some("EmbeddedFile")
474}
475
476/// Applies `cipher` to one string or stream body with the given `key`.
477fn decrypt_bytes(cipher: Cipher, key: &[u8], data: &[u8]) -> Vec<u8> {
478    match cipher {
479        Cipher::Identity => data.to_vec(),
480        Cipher::Rc4 => rc4(key, data),
481        Cipher::Aesv2 | Cipher::Aesv3 => aes_cbc_decrypt(key, data),
482    }
483}
484
485/// Pads or truncates a password to the 32 bytes every legacy algorithm
486/// hashes (ISO 32000 §7.6.3.3, Algorithm 2 step (a)). The empty password
487/// pads to [`PAD`] itself.
488///
489/// Covers ISO 32000-1 §7.6.3.3.
490fn pad_password(password: &[u8]) -> [u8; 32] {
491    let mut out = [0u8; 32];
492    let n = password.len().min(32);
493    out[..n].copy_from_slice(&password[..n]);
494    out[n..].copy_from_slice(&PAD[..32 - n]);
495    out
496}
497
498/// The RC4/AESV2 file key `password` actually opens: tried as the user
499/// password (Algorithm 2 + the `/U` check), then as the owner password
500/// (Algorithm 7: the owner key decrypts `/O` back into the padded user
501/// password, which must then verify like any user password).
502///
503/// Covers ISO 32000-1 §7.6.3.4.
504fn rc4_family_key(enc: &Dict, id0: &[u8], r: i64, n: usize, password: &[u8]) -> Option<Vec<u8>> {
505    let u = enc.get("U").and_then(Object::as_str_bytes)?;
506    if let Some(key) = md5_file_key(enc, id0, r, n, &pad_password(password)) {
507        if verify_user_password(&key, r, id0, u) {
508            return Some(key);
509        }
510    }
511    // Owner attempt. The owner key comes from the owner password alone
512    // (Algorithm 3 steps (a)-(d)); what it decrypts out of `/O` is the
513    // padded user password, ready for Algorithm 2 verbatim.
514    let o = enc.get("O").and_then(Object::as_str_bytes)?;
515    if o.len() < 32 {
516        return None;
517    }
518    let mut d = md5(&pad_password(password));
519    if r >= 3 {
520        for _ in 0..50 {
521            d = md5(&d[..n]);
522        }
523    }
524    let okey = &d[..n];
525    let recovered = if r == 2 {
526        rc4(okey, &o[..32])
527    } else {
528        let mut x = o[..32].to_vec();
529        for i in (1u8..=19).rev() {
530            let keyed: Vec<u8> = okey.iter().map(|b| b ^ i).collect();
531            x = rc4(&keyed, &x);
532        }
533        rc4(okey, &x)
534    };
535    let padded: [u8; 32] = recovered.get(..32)?.try_into().ok()?;
536    let key = md5_file_key(enc, id0, r, n, &padded)?;
537    verify_user_password(&key, r, id0, u).then_some(key)
538}
539
540/// Algorithm 2: derive the RC4/AESV2 file key from a padded user password.
541///
542/// Covers ISO 32000-1 §7.6.3.3.
543fn md5_file_key(enc: &Dict, id0: &[u8], r: i64, n: usize, padded: &[u8; 32]) -> Option<Vec<u8>> {
544    let o = enc.get("O").and_then(Object::as_str_bytes)?;
545    if o.len() < 32 {
546        return None;
547    }
548    let p = enc.get_int("P")?;
549    let mut input = Vec::with_capacity(32 + 32 + 4 + id0.len() + 4);
550    input.extend_from_slice(padded);
551    input.extend_from_slice(&o[..32]);
552    input.extend_from_slice(&(p as i32 as u32).to_le_bytes()); // /P low 32 bits, LE
553    input.extend_from_slice(id0);
554    // Revision 4 with /EncryptMetadata false hashes an extra 0xFFFFFFFF.
555    if r >= 4 && !encrypt_metadata_flag(enc) {
556        input.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]);
557    }
558    let mut digest = md5(&input);
559    if r >= 3 {
560        for _ in 0..50 {
561            digest = md5(&digest[..n]);
562        }
563    }
564    Some(digest[..n].to_vec())
565}
566
567/// Checks the empty user password by recomputing `/U` and comparing.
568///
569/// Covers ISO 32000-1 §7.6.3.4.
570fn verify_user_password(key: &[u8], r: i64, id0: &[u8], u: &[u8]) -> bool {
571    if r == 2 {
572        // Algorithm 4: U = RC4(key, PAD).
573        let computed = rc4(key, &PAD);
574        u.len() >= 32 && computed == u[..32]
575    } else {
576        // Algorithm 5: U = MD5(PAD ++ ID[0]) encrypted with 20 keyed RC4 passes.
577        let mut input = Vec::with_capacity(32 + id0.len());
578        input.extend_from_slice(&PAD);
579        input.extend_from_slice(id0);
580        let mut x = md5(&input).to_vec();
581        x = rc4(key, &x);
582        for i in 1u8..=19 {
583            let keyed: Vec<u8> = key.iter().map(|b| b ^ i).collect();
584            x = rc4(&keyed, &x);
585        }
586        // Only the first 16 bytes are defined; the rest of /U is arbitrary padding.
587        u.len() >= 16 && x[..16] == u[..16]
588    }
589}
590
591/// RC4 stream cipher (symmetric: the same call encrypts and decrypts).
592fn rc4(key: &[u8], data: &[u8]) -> Vec<u8> {
593    debug_assert!(!key.is_empty());
594    let mut s: [u8; 256] = core::array::from_fn(|i| i as u8);
595    let mut j = 0u8;
596    for i in 0..256 {
597        j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]);
598        s.swap(i, j as usize);
599    }
600    let mut out = Vec::with_capacity(data.len());
601    let (mut i, mut j) = (0u8, 0u8);
602    for &byte in data {
603        i = i.wrapping_add(1);
604        j = j.wrapping_add(s[i as usize]);
605        s.swap(i as usize, j as usize);
606        let k = s[s[i as usize].wrapping_add(s[j as usize]) as usize];
607        out.push(byte ^ k);
608    }
609    out
610}
611
612// --- AES (FIPS-197) and CBC mode -----------------------------------------
613
614/// AES substitution box.
615#[rustfmt::skip]
616const AES_SBOX: [u8; 256] = [
617    0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
618    0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
619    0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
620    0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
621    0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
622    0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
623    0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
624    0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
625    0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
626    0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
627    0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
628    0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
629    0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
630    0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
631    0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
632    0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
633];
634
635/// Round constants for key expansion (`RCON[j]` used when `i % Nk == 0`).
636const AES_RCON: [u8; 11] = [
637    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
638];
639
640/// The inverse S-box, derived once from `AES_SBOX`.
641fn aes_inv_sbox() -> [u8; 256] {
642    let mut inv = [0u8; 256];
643    for (i, &s) in AES_SBOX.iter().enumerate() {
644        inv[s as usize] = i as u8;
645    }
646    inv
647}
648
649/// Multiplies two elements of GF(2^8) with the AES reduction polynomial.
650fn gmul(mut a: u8, mut b: u8) -> u8 {
651    let mut p = 0u8;
652    for _ in 0..8 {
653        if b & 1 != 0 {
654            p ^= a;
655        }
656        let hi = a & 0x80;
657        a <<= 1;
658        if hi != 0 {
659            a ^= 0x1b;
660        }
661        b >>= 1;
662    }
663    p
664}
665
666/// Expands a 16- or 32-byte key into `Nr + 1` round keys (state is stored
667/// column-major, so byte `r + 4c` is row `r`, column `c`).
668fn aes_expand_key(key: &[u8]) -> Vec<[u8; 16]> {
669    let nk = key.len() / 4; // 4 (AES-128) or 8 (AES-256)
670    let nr = nk + 6;
671    let total = 4 * (nr + 1);
672    let mut w: Vec<[u8; 4]> = Vec::with_capacity(total);
673    for i in 0..nk {
674        w.push([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
675    }
676    for i in nk..total {
677        let mut t = w[i - 1];
678        if i.is_multiple_of(nk) {
679            t = [t[1], t[2], t[3], t[0]]; // RotWord
680            for b in &mut t {
681                *b = AES_SBOX[*b as usize]; // SubWord
682            }
683            t[0] ^= AES_RCON[i / nk];
684        } else if nk > 6 && i % nk == 4 {
685            for b in &mut t {
686                *b = AES_SBOX[*b as usize];
687            }
688        }
689        let prev = w[i - nk];
690        w.push([
691            prev[0] ^ t[0],
692            prev[1] ^ t[1],
693            prev[2] ^ t[2],
694            prev[3] ^ t[3],
695        ]);
696    }
697    (0..=nr)
698        .map(|round| {
699            let mut rk = [0u8; 16];
700            for c in 0..4 {
701                rk[4 * c..4 * c + 4].copy_from_slice(&w[4 * round + c]);
702            }
703            rk
704        })
705        .collect()
706}
707
708fn add_round_key(s: &mut [u8; 16], rk: &[u8; 16]) {
709    for (b, k) in s.iter_mut().zip(rk) {
710        *b ^= k;
711    }
712}
713
714fn shift_rows(s: &mut [u8; 16]) {
715    let o = *s;
716    for r in 1..4 {
717        for c in 0..4 {
718            s[r + 4 * c] = o[r + 4 * ((c + r) % 4)];
719        }
720    }
721}
722
723fn inv_shift_rows(s: &mut [u8; 16]) {
724    let o = *s;
725    for r in 1..4 {
726        for c in 0..4 {
727            s[r + 4 * c] = o[r + 4 * ((c + 4 - r) % 4)];
728        }
729    }
730}
731
732fn mix_columns(s: &mut [u8; 16]) {
733    for c in 0..4 {
734        let i = 4 * c;
735        let (a0, a1, a2, a3) = (s[i], s[i + 1], s[i + 2], s[i + 3]);
736        s[i] = gmul(a0, 2) ^ gmul(a1, 3) ^ a2 ^ a3;
737        s[i + 1] = a0 ^ gmul(a1, 2) ^ gmul(a2, 3) ^ a3;
738        s[i + 2] = a0 ^ a1 ^ gmul(a2, 2) ^ gmul(a3, 3);
739        s[i + 3] = gmul(a0, 3) ^ a1 ^ a2 ^ gmul(a3, 2);
740    }
741}
742
743fn inv_mix_columns(s: &mut [u8; 16]) {
744    for c in 0..4 {
745        let i = 4 * c;
746        let (a0, a1, a2, a3) = (s[i], s[i + 1], s[i + 2], s[i + 3]);
747        s[i] = gmul(a0, 14) ^ gmul(a1, 11) ^ gmul(a2, 13) ^ gmul(a3, 9);
748        s[i + 1] = gmul(a0, 9) ^ gmul(a1, 14) ^ gmul(a2, 11) ^ gmul(a3, 13);
749        s[i + 2] = gmul(a0, 13) ^ gmul(a1, 9) ^ gmul(a2, 14) ^ gmul(a3, 11);
750        s[i + 3] = gmul(a0, 11) ^ gmul(a1, 13) ^ gmul(a2, 9) ^ gmul(a3, 14);
751    }
752}
753
754fn aes_encrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]]) {
755    #[cfg(target_arch = "aarch64")]
756    if std::arch::is_aarch64_feature_detected!("aes") {
757        // SAFETY: the `aes` target feature was just detected at runtime.
758        unsafe { aes_hw::encrypt_block(s, rks) };
759        return;
760    }
761    #[cfg(target_arch = "x86_64")]
762    if std::arch::is_x86_feature_detected!("aes") {
763        // SAFETY: the `aes` target feature was just detected at runtime.
764        unsafe { aes_hw::encrypt_block(s, rks) };
765        return;
766    }
767    aes_encrypt_block_soft(s, rks);
768}
769
770/// The portable byte-oriented rounds, for CPUs without AES instructions.
771fn aes_encrypt_block_soft(s: &mut [u8; 16], rks: &[[u8; 16]]) {
772    let nr = rks.len() - 1;
773    add_round_key(s, &rks[0]);
774    for rk in &rks[1..nr] {
775        s.iter_mut().for_each(|b| *b = AES_SBOX[*b as usize]);
776        shift_rows(s);
777        mix_columns(s);
778        add_round_key(s, rk);
779    }
780    s.iter_mut().for_each(|b| *b = AES_SBOX[*b as usize]);
781    shift_rows(s);
782    add_round_key(s, &rks[nr]);
783}
784
785fn aes_decrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]], inv_sbox: &[u8; 256]) {
786    #[cfg(target_arch = "aarch64")]
787    if std::arch::is_aarch64_feature_detected!("aes") {
788        // SAFETY: the `aes` target feature was just detected at runtime.
789        unsafe { aes_hw::decrypt_block(s, rks) };
790        return;
791    }
792    #[cfg(target_arch = "x86_64")]
793    if std::arch::is_x86_feature_detected!("aes") {
794        // SAFETY: the `aes` target feature was just detected at runtime.
795        unsafe { aes_hw::decrypt_block(s, rks) };
796        return;
797    }
798    aes_decrypt_block_soft(s, rks, inv_sbox);
799}
800
801/// The portable byte-oriented rounds, for CPUs without AES instructions.
802fn aes_decrypt_block_soft(s: &mut [u8; 16], rks: &[[u8; 16]], inv_sbox: &[u8; 256]) {
803    let nr = rks.len() - 1;
804    add_round_key(s, &rks[nr]);
805    for rk in rks[1..nr].iter().rev() {
806        inv_shift_rows(s);
807        s.iter_mut().for_each(|b| *b = inv_sbox[*b as usize]);
808        add_round_key(s, rk);
809        inv_mix_columns(s);
810    }
811    inv_shift_rows(s);
812    s.iter_mut().for_each(|b| *b = inv_sbox[*b as usize]);
813    add_round_key(s, &rks[0]);
814}
815
816/// AES block rounds on the CPU's AES instructions. The R6 password hash
817/// CBC-encrypts about a megabyte per key derivation (Algorithm 2.B runs
818/// 64+ rounds over a 64-fold repeated block), which the byte-oriented
819/// software rounds above turn into ~500ms per encrypted document; these
820/// paths bring that under a millisecond. Callers detect the `aes` feature
821/// before entering; every function must produce bytes identical to the
822/// software rounds (pinned by the FIPS-197 vectors in the tests below).
823#[cfg(target_arch = "aarch64")]
824mod aes_hw {
825    use core::arch::aarch64::{
826        vaesdq_u8, vaeseq_u8, vaesimcq_u8, vaesmcq_u8, veorq_u8, vld1q_u8, vmovq_n_u8, vst1q_u8,
827    };
828
829    /// # Safety
830    /// Requires the `aes` target feature.
831    #[target_feature(enable = "aes")]
832    pub unsafe fn encrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]]) {
833        let nr = rks.len() - 1;
834        // AESE folds AddRoundKey into SubBytes+ShiftRows, so the loop
835        // consumes rks[0..nr-1] and the last round's key lands as a plain
836        // XOR after the final AESE.
837        let mut x = vld1q_u8(s.as_ptr());
838        for rk in &rks[..nr - 1] {
839            x = vaesmcq_u8(vaeseq_u8(x, vld1q_u8(rk.as_ptr())));
840        }
841        x = vaeseq_u8(x, vld1q_u8(rks[nr - 1].as_ptr()));
842        x = veorq_u8(x, vld1q_u8(rks[nr].as_ptr()));
843        vst1q_u8(s.as_mut_ptr(), x);
844    }
845
846    /// # Safety
847    /// Requires the `aes` target feature.
848    #[target_feature(enable = "aes")]
849    pub unsafe fn decrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]]) {
850        let nr = rks.len() - 1;
851        // AESD with a zero key is exactly InvSubBytes(InvShiftRows(x)),
852        // which lets the loop mirror the software inverse cipher's
853        // operation order with the round keys untransformed.
854        let zero = vmovq_n_u8(0);
855        let mut x = vld1q_u8(s.as_ptr());
856        x = veorq_u8(x, vld1q_u8(rks[nr].as_ptr()));
857        for rk in rks[1..nr].iter().rev() {
858            x = vaesdq_u8(x, zero);
859            x = veorq_u8(x, vld1q_u8(rk.as_ptr()));
860            x = vaesimcq_u8(x);
861        }
862        x = vaesdq_u8(x, zero);
863        x = veorq_u8(x, vld1q_u8(rks[0].as_ptr()));
864        vst1q_u8(s.as_mut_ptr(), x);
865    }
866}
867
868/// See the aarch64 twin above; same contract, AES-NI instructions.
869#[cfg(target_arch = "x86_64")]
870mod aes_hw {
871    use core::arch::x86_64::{
872        __m128i, _mm_aesdec_si128, _mm_aesdeclast_si128, _mm_aesenc_si128, _mm_aesenclast_si128,
873        _mm_aesimc_si128, _mm_loadu_si128, _mm_storeu_si128, _mm_xor_si128,
874    };
875
876    /// # Safety
877    /// Requires the `aes` target feature.
878    #[target_feature(enable = "aes")]
879    pub unsafe fn encrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]]) {
880        let nr = rks.len() - 1;
881        let mut x = _mm_loadu_si128(s.as_ptr().cast::<__m128i>());
882        x = _mm_xor_si128(x, _mm_loadu_si128(rks[0].as_ptr().cast::<__m128i>()));
883        for rk in &rks[1..nr] {
884            x = _mm_aesenc_si128(x, _mm_loadu_si128(rk.as_ptr().cast::<__m128i>()));
885        }
886        x = _mm_aesenclast_si128(x, _mm_loadu_si128(rks[nr].as_ptr().cast::<__m128i>()));
887        _mm_storeu_si128(s.as_mut_ptr().cast::<__m128i>(), x);
888    }
889
890    /// # Safety
891    /// Requires the `aes` target feature.
892    #[target_feature(enable = "aes")]
893    pub unsafe fn decrypt_block(s: &mut [u8; 16], rks: &[[u8; 16]]) {
894        let nr = rks.len() - 1;
895        // AESDEC applies InvMixColumns before its key XOR, so the middle
896        // round keys go through AESIMC (the equivalent inverse cipher).
897        let mut x = _mm_loadu_si128(s.as_ptr().cast::<__m128i>());
898        x = _mm_xor_si128(x, _mm_loadu_si128(rks[nr].as_ptr().cast::<__m128i>()));
899        for rk in rks[1..nr].iter().rev() {
900            let dk = _mm_aesimc_si128(_mm_loadu_si128(rk.as_ptr().cast::<__m128i>()));
901            x = _mm_aesdec_si128(x, dk);
902        }
903        x = _mm_aesdeclast_si128(x, _mm_loadu_si128(rks[0].as_ptr().cast::<__m128i>()));
904        _mm_storeu_si128(s.as_mut_ptr().cast::<__m128i>(), x);
905    }
906}
907
908/// AES-CBC decryption of whole blocks (no IV prefix, no padding removal).
909/// Returns an empty vector when the input is not a positive multiple of 16.
910fn aes_cbc_decrypt_blocks(key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> {
911    // Only AES-128 and AES-256 keys expand; anything else comes from a
912    // dictionary that lies about its method and yields nothing.
913    if !matches!(key.len(), 16 | 32) {
914        return Vec::new();
915    }
916    if data.is_empty() || !data.len().is_multiple_of(16) || iv.len() < 16 {
917        return Vec::new();
918    }
919    let rks = aes_expand_key(key);
920    let inv_sbox = aes_inv_sbox();
921    let mut prev = [0u8; 16];
922    prev.copy_from_slice(&iv[..16]);
923    let mut out = Vec::with_capacity(data.len());
924    for chunk in data.as_chunks::<16>().0 {
925        let mut block = [0u8; 16];
926        block.copy_from_slice(chunk);
927        let cipher = block;
928        aes_decrypt_block(&mut block, &rks, &inv_sbox);
929        for (b, p) in block.iter_mut().zip(&prev) {
930            *b ^= p;
931        }
932        out.extend_from_slice(&block);
933        prev = cipher;
934    }
935    out
936}
937
938/// AES-CBC encryption of whole blocks (no IV prefix, no padding). Used only by
939/// the R6 key-derivation hash.
940fn aes_cbc_encrypt_blocks(key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> {
941    let rks = aes_expand_key(key);
942    let mut prev = [0u8; 16];
943    prev.copy_from_slice(&iv[..16]);
944    let mut out = Vec::with_capacity(data.len());
945    for chunk in data.as_chunks::<16>().0 {
946        let mut block = [0u8; 16];
947        for ((b, c), p) in block.iter_mut().zip(chunk).zip(&prev) {
948            *b = c ^ p;
949        }
950        aes_encrypt_block(&mut block, &rks);
951        out.extend_from_slice(&block);
952        prev = block;
953    }
954    out
955}
956
957/// Decrypts a PDF AES value: the first 16 bytes are the IV, the rest is
958/// CBC-encrypted with PKCS#7 padding. Malformed input yields empty output
959/// rather than garbage.
960fn aes_cbc_decrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
961    if data.len() < 16 {
962        return Vec::new();
963    }
964    let (iv, ct) = data.split_at(16);
965    let mut out = aes_cbc_decrypt_blocks(key, iv, ct);
966    strip_pkcs7(&mut out);
967    out
968}
969
970/// Removes PKCS#7 padding in place if present and well-formed.
971fn strip_pkcs7(data: &mut Vec<u8>) {
972    let Some(&pad) = data.last() else {
973        return;
974    };
975    let pad = pad as usize;
976    if (1..=16).contains(&pad) && pad <= data.len() {
977        let start = data.len() - pad;
978        if data[start..].iter().all(|&b| b as usize == pad) {
979            data.truncate(start);
980        }
981    }
982}
983
984/// Pads `data` to a whole number of 16-byte blocks with PKCS#7: a full
985/// extra block of `0x10` bytes when `data` is already block-aligned, so
986/// [`strip_pkcs7`] always has a marker to remove.
987fn pkcs7_pad(data: &[u8]) -> Vec<u8> {
988    let pad = 16 - data.len() % 16;
989    let mut out = data.to_vec();
990    out.resize(data.len() + pad, pad as u8);
991    out
992}
993
994/// Encrypts a PDF AES value: a fresh 16-byte IV from `rng` followed by the
995/// PKCS#7-padded plaintext under AES-256-CBC, the exact inverse of
996/// [`aes_cbc_decrypt`].
997fn aes_cbc_encrypt(
998    key: &[u8],
999    rng: &mut dyn FnMut(&mut [u8]) -> crate::error::Result<()>,
1000    data: &[u8],
1001) -> crate::error::Result<Vec<u8>> {
1002    let mut iv = [0u8; 16];
1003    rng(&mut iv)?;
1004    let mut out = iv.to_vec();
1005    out.extend_from_slice(&aes_cbc_encrypt_blocks(key, &iv, &pkcs7_pad(data)));
1006    Ok(out)
1007}
1008
1009// --- SHA-2 (FIPS 180-4): 256, 512 and 384 --------------------------------
1010
1011#[rustfmt::skip]
1012const SHA256_K: [u32; 64] = [
1013    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1014    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1015    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1016    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1017    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1018    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1019    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1020    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
1021];
1022
1023/// Streaming SHA-256 (FIPS 180-4): feed bytes with [`Sha256::update`],
1024/// close with [`Sha256::finalize`]. Public so the writer can hash a file's
1025/// body as it emits it, without holding the whole file in memory; the
1026/// one-shot [`sha256`] delegates here.
1027#[derive(Debug, Clone)]
1028pub struct Sha256 {
1029    h: [u32; 8],
1030    tail: [u8; 64],
1031    tail_len: usize,
1032    total: u64,
1033}
1034
1035impl Default for Sha256 {
1036    fn default() -> Sha256 {
1037        Sha256::new()
1038    }
1039}
1040
1041impl Sha256 {
1042    /// A hasher in the FIPS 180-4 initial state.
1043    pub fn new() -> Sha256 {
1044        Sha256 {
1045            h: [
1046                0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
1047                0x5be0cd19,
1048            ],
1049            tail: [0u8; 64],
1050            tail_len: 0,
1051            total: 0,
1052        }
1053    }
1054
1055    /// Absorbs `input`. Slice boundaries do not affect the digest: any
1056    /// sequence of updates whose concatenation is the same message yields
1057    /// the same [`Sha256::finalize`] result. Whole blocks are compressed
1058    /// straight from `input`; only a partial trailing block is buffered.
1059    pub fn update(&mut self, input: &[u8]) {
1060        self.total = self.total.wrapping_add(input.len() as u64);
1061        let mut input = input;
1062        if self.tail_len > 0 {
1063            let take = input.len().min(64 - self.tail_len);
1064            self.tail[self.tail_len..self.tail_len + take].copy_from_slice(&input[..take]);
1065            self.tail_len += take;
1066            input = &input[take..];
1067            if self.tail_len < 64 {
1068                return;
1069            }
1070            let block = self.tail;
1071            sha256_compress(&mut self.h, &block);
1072            self.tail_len = 0;
1073        }
1074        let (blocks, rest) = input.as_chunks::<64>();
1075        for block in blocks {
1076            sha256_compress(&mut self.h, block);
1077        }
1078        self.tail[..rest.len()].copy_from_slice(rest);
1079        self.tail_len = rest.len();
1080    }
1081
1082    /// Pads and returns the digest of everything absorbed so far.
1083    pub fn finalize(mut self) -> [u8; 32] {
1084        // Final padding touches at most two blocks: 0x80, zeros, and the
1085        // bit length in the last 8 bytes (FIPS 180-4).
1086        let mut pad = [0u8; 128];
1087        pad[..self.tail_len].copy_from_slice(&self.tail[..self.tail_len]);
1088        pad[self.tail_len] = 0x80;
1089        let padded = if self.tail_len < 56 { 64 } else { 128 };
1090        let bitlen = self.total.wrapping_mul(8);
1091        pad[padded - 8..padded].copy_from_slice(&bitlen.to_be_bytes());
1092        for block in pad[..padded].as_chunks::<64>().0 {
1093            sha256_compress(&mut self.h, block);
1094        }
1095        let mut out = [0u8; 32];
1096        for (i, word) in self.h.iter().enumerate() {
1097            out[4 * i..4 * i + 4].copy_from_slice(&word.to_be_bytes());
1098        }
1099        out
1100    }
1101}
1102
1103/// SHA-256 of `input` (FIPS 180-4); public so the writer can derive its `/ID`.
1104pub fn sha256(input: &[u8]) -> [u8; 32] {
1105    let mut hasher = Sha256::new();
1106    hasher.update(input);
1107    hasher.finalize()
1108}
1109
1110fn sha256_compress(h: &mut [u32; 8], block: &[u8; 64]) {
1111    #[cfg(target_arch = "aarch64")]
1112    if std::arch::is_aarch64_feature_detected!("sha2") {
1113        // SAFETY: the `sha2` target feature was just detected at runtime.
1114        unsafe { sha_hw::compress256(h, block) };
1115        return;
1116    }
1117    sha256_compress_soft(h, block);
1118}
1119
1120/// The portable compression, for CPUs without SHA-256 instructions.
1121fn sha256_compress_soft(h: &mut [u32; 8], block: &[u8; 64]) {
1122    let mut w = [0u32; 64];
1123    for (word, bytes) in w.iter_mut().zip(block.as_chunks::<4>().0) {
1124        *word = u32::from_be_bytes(*bytes);
1125    }
1126    for i in 16..64 {
1127        let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
1128        let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
1129        w[i] = w[i - 16]
1130            .wrapping_add(s0)
1131            .wrapping_add(w[i - 7])
1132            .wrapping_add(s1);
1133    }
1134    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = *h;
1135    for (k, wi) in SHA256_K.iter().zip(&w) {
1136        let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
1137        let ch = (e & f) ^ ((!e) & g);
1138        let t1 = hh
1139            .wrapping_add(s1)
1140            .wrapping_add(ch)
1141            .wrapping_add(*k)
1142            .wrapping_add(*wi);
1143        let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
1144        let maj = (a & b) ^ (a & c) ^ (b & c);
1145        let t2 = s0.wrapping_add(maj);
1146        hh = g;
1147        g = f;
1148        f = e;
1149        e = d.wrapping_add(t1);
1150        d = c;
1151        c = b;
1152        b = a;
1153        a = t1.wrapping_add(t2);
1154    }
1155    for (hv, v) in h.iter_mut().zip([a, b, c, d, e, f, g, hh]) {
1156        *hv = hv.wrapping_add(v);
1157    }
1158}
1159
1160#[rustfmt::skip]
1161const SHA512_K: [u64; 80] = [
1162    0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
1163    0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
1164    0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
1165    0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
1166    0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
1167    0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
1168    0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
1169    0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
1170    0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
1171    0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
1172    0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
1173    0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
1174    0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
1175    0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
1176    0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
1177    0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
1178    0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
1179    0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
1180    0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
1181    0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
1182];
1183
1184fn sha512_core(input: &[u8], mut h: [u64; 8]) -> [u64; 8] {
1185    let (blocks, tail) = input.as_chunks::<128>();
1186    for block in blocks {
1187        sha512_compress(&mut h, block);
1188    }
1189    // Final padding touches at most two blocks: 0x80, zeros, and the bit
1190    // length in the last 16 bytes (FIPS 180-4) — the message itself is
1191    // hashed in place above, never copied.
1192    let mut pad = [0u8; 256];
1193    pad[..tail.len()].copy_from_slice(tail);
1194    pad[tail.len()] = 0x80;
1195    let padded = if tail.len() < 112 { 128 } else { 256 };
1196    let bitlen = (input.len() as u128).wrapping_mul(8);
1197    pad[padded - 16..padded].copy_from_slice(&bitlen.to_be_bytes());
1198    for block in pad[..padded].as_chunks::<128>().0 {
1199        sha512_compress(&mut h, block);
1200    }
1201    h
1202}
1203
1204fn sha512_compress(h: &mut [u64; 8], block: &[u8; 128]) {
1205    #[cfg(target_arch = "aarch64")]
1206    if std::arch::is_aarch64_feature_detected!("sha3") {
1207        // SAFETY: the `sha3` target feature (SHA-512 instructions) was
1208        // just detected at runtime.
1209        unsafe { sha_hw::compress512(h, block) };
1210        return;
1211    }
1212    sha512_compress_soft(h, block);
1213}
1214
1215/// The portable compression, for CPUs without SHA-512 instructions.
1216fn sha512_compress_soft(h: &mut [u64; 8], block: &[u8; 128]) {
1217    let mut w = [0u64; 80];
1218    for (word, bytes) in w.iter_mut().zip(block.as_chunks::<8>().0) {
1219        *word = u64::from_be_bytes(*bytes);
1220    }
1221    for i in 16..80 {
1222        let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
1223        let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
1224        w[i] = w[i - 16]
1225            .wrapping_add(s0)
1226            .wrapping_add(w[i - 7])
1227            .wrapping_add(s1);
1228    }
1229    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = *h;
1230    for (k, wi) in SHA512_K.iter().zip(&w) {
1231        let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
1232        let ch = (e & f) ^ ((!e) & g);
1233        let t1 = hh
1234            .wrapping_add(s1)
1235            .wrapping_add(ch)
1236            .wrapping_add(*k)
1237            .wrapping_add(*wi);
1238        let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
1239        let maj = (a & b) ^ (a & c) ^ (b & c);
1240        let t2 = s0.wrapping_add(maj);
1241        hh = g;
1242        g = f;
1243        f = e;
1244        e = d.wrapping_add(t1);
1245        d = c;
1246        c = b;
1247        b = a;
1248        a = t1.wrapping_add(t2);
1249    }
1250    for (hv, v) in h.iter_mut().zip([a, b, c, d, e, f, g, hh]) {
1251        *hv = hv.wrapping_add(v);
1252    }
1253}
1254
1255/// SHA-2 compressions on the CPU's SHA instructions. Algorithm 2.B hashes
1256/// roughly a megabyte per key derivation across its rounds — after the AES
1257/// rounds moved to hardware this hashing was the remaining ~1-3ms of every
1258/// encrypted-document open (PMU counters put it 60x below the memory-bound
1259/// probe and at the ALU probe's branch signature: a pure serial dependency
1260/// chain, which is exactly what the SHA instructions collapse). Callers
1261/// detect `sha2`/`sha3` before entering; results are pinned to the FIPS
1262/// 180 vectors in the tests below.
1263#[cfg(target_arch = "aarch64")]
1264mod sha_hw {
1265    use core::arch::aarch64::{
1266        uint32x4_t, uint64x2_t, vaddq_u32, vaddq_u64, vextq_u64, vld1q_u32, vld1q_u64, vld1q_u8,
1267        vreinterpretq_u32_u8, vreinterpretq_u64_u8, vrev32q_u8, vrev64q_u8, vsha256h2q_u32,
1268        vsha256hq_u32, vsha256su0q_u32, vsha256su1q_u32, vsha512h2q_u64, vsha512hq_u64,
1269        vsha512su0q_u64, vsha512su1q_u64, vst1q_u32,
1270    };
1271
1272    use super::{SHA256_K, SHA512_K};
1273
1274    /// # Safety
1275    /// Requires the `sha2` target feature.
1276    #[target_feature(enable = "sha2")]
1277    pub unsafe fn compress256(h: &mut [u32; 8], block: &[u8; 64]) {
1278        let mut abcd = vld1q_u32(h.as_ptr());
1279        let mut efgh = vld1q_u32(h.as_ptr().add(4));
1280        let saved = (abcd, efgh);
1281        let mut w: [uint32x4_t; 4] = [
1282            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr()))),
1283            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(16)))),
1284            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(32)))),
1285            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(48)))),
1286        ];
1287        for step in 0..16 {
1288            let wk = vaddq_u32(w[step % 4], vld1q_u32(SHA256_K.as_ptr().add(4 * step)));
1289            // The last three windows feed no further schedule.
1290            if step < 12 {
1291                w[step % 4] = vsha256su1q_u32(
1292                    vsha256su0q_u32(w[step % 4], w[(step + 1) % 4]),
1293                    w[(step + 2) % 4],
1294                    w[(step + 3) % 4],
1295                );
1296            }
1297            let prev = abcd;
1298            abcd = vsha256hq_u32(abcd, efgh, wk);
1299            efgh = vsha256h2q_u32(efgh, prev, wk);
1300        }
1301        vst1q_u32(h.as_mut_ptr(), vaddq_u32(abcd, saved.0));
1302        vst1q_u32(h.as_mut_ptr().add(4), vaddq_u32(efgh, saved.1));
1303    }
1304
1305    /// # Safety
1306    /// Requires the `sha3` target feature (the SHA-512 instructions).
1307    #[target_feature(enable = "sha2,sha3")]
1308    pub unsafe fn compress512(h: &mut [u64; 8], block: &[u8; 128]) {
1309        // State as register pairs s = [ab, cd, ef, gh]. Each step covers
1310        // two rounds; the register playing the "gh" role rotates
1311        // gh -> ef -> cd -> ab, which the r index tracks.
1312        let mut s: [uint64x2_t; 4] = [
1313            vld1q_u64(h.as_ptr()),
1314            vld1q_u64(h.as_ptr().add(2)),
1315            vld1q_u64(h.as_ptr().add(4)),
1316            vld1q_u64(h.as_ptr().add(6)),
1317        ];
1318        let saved = s;
1319        let mut w: [uint64x2_t; 8] = [core::mem::zeroed(); 8];
1320        for (pair, bytes) in w.iter_mut().zip(block.as_chunks::<16>().0) {
1321            *pair = vreinterpretq_u64_u8(vrev64q_u8(vld1q_u8(bytes.as_ptr())));
1322        }
1323        for step in 0..40 {
1324            let r = 3 - (step % 4);
1325            let wk = vaddq_u64(w[step % 8], vld1q_u64(SHA512_K.as_ptr().add(2 * step)));
1326            let sum = vaddq_u64(vextq_u64::<1>(wk, wk), s[r]);
1327            let im = vsha512hq_u64(
1328                sum,
1329                vextq_u64::<1>(s[(r + 3) % 4], s[r]),
1330                vextq_u64::<1>(s[(r + 2) % 4], s[(r + 3) % 4]),
1331            );
1332            let updated = vsha512h2q_u64(im, s[(r + 2) % 4], s[(r + 1) % 4]);
1333            s[(r + 2) % 4] = vaddq_u64(s[(r + 2) % 4], im);
1334            s[r] = updated;
1335            // The last four windows feed no further schedule.
1336            if step < 32 {
1337                let i = step % 8;
1338                w[i] = vsha512su1q_u64(
1339                    vsha512su0q_u64(w[i], w[(i + 1) % 8]),
1340                    w[(i + 7) % 8],
1341                    vextq_u64::<1>(w[(i + 4) % 8], w[(i + 5) % 8]),
1342                );
1343            }
1344        }
1345        for (i, (out, kept)) in s.iter().zip(saved).enumerate() {
1346            let summed = vaddq_u64(*out, kept);
1347            core::arch::aarch64::vst1q_u64(h.as_mut_ptr().add(2 * i), summed);
1348        }
1349    }
1350}
1351
1352fn sha512(input: &[u8]) -> Vec<u8> {
1353    let h = sha512_core(
1354        input,
1355        [
1356            0x6a09e667f3bcc908,
1357            0xbb67ae8584caa73b,
1358            0x3c6ef372fe94f82b,
1359            0xa54ff53a5f1d36f1,
1360            0x510e527fade682d1,
1361            0x9b05688c2b3e6c1f,
1362            0x1f83d9abfb41bd6b,
1363            0x5be0cd19137e2179,
1364        ],
1365    );
1366    h.iter().flat_map(|w| w.to_be_bytes()).collect()
1367}
1368
1369fn sha384(input: &[u8]) -> Vec<u8> {
1370    let h = sha512_core(
1371        input,
1372        [
1373            0xcbbb9d5dc1059ed8,
1374            0x629a292a367cd507,
1375            0x9159015a3070dd17,
1376            0x152fecd8f70e5939,
1377            0x67332667ffc00b31,
1378            0x8eb44a8768581511,
1379            0xdb0c2e0d64f98fa7,
1380            0x47b5481dbefa4fa4,
1381        ],
1382    );
1383    h.iter().take(6).flat_map(|w| w.to_be_bytes()).collect()
1384}
1385
1386/// Recovers the AES-256 file key `password` opens (ISO 32000-2 §7.6.4.3.3,
1387/// Algorithm 2.A) for revisions 5 and 6: as the user password against
1388/// `/U`+`/UE`, then as the owner password against `/O`+`/OE` (whose hashes
1389/// additionally salt in the first 48 bytes of `/U`). Passwords longer than
1390/// the 127 UTF-8 bytes the algorithm defines are truncated.
1391fn aesv3_key(enc: &Dict, r: i64, password: &[u8]) -> Option<Vec<u8>> {
1392    let pw = &password[..password.len().min(127)];
1393    let u = enc.get("U").and_then(Object::as_str_bytes)?;
1394    if u.len() < 48 {
1395        return None;
1396    }
1397    // Fast paths: derive each candidate file key straight from its key
1398    // salt and let the encrypted /Perms confirm it (ISO 32000-2 Algorithm
1399    // 2.A step g validates exactly this way) — one Algorithm 2.B hash per
1400    // path instead of two. Algorithm 2.B is the whole cost of opening an
1401    // encrypted document, so this halves it for every file whose /Perms
1402    // is intact; a wrong password decrypts /Perms to garbage and falls
1403    // through to the full U/O validation below, unchanged.
1404    let perms = enc.get("Perms").and_then(Object::as_str_bytes);
1405    let o = enc.get("O").and_then(Object::as_str_bytes);
1406    if let Some(perms) = perms.filter(|p| p.len() >= 16) {
1407        if let Some(ue) = enc.get("UE").and_then(Object::as_str_bytes) {
1408            if ue.len() >= 32 {
1409                let intermediate = hash_2b(r, pw, &u[40..48], &[]);
1410                let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &ue[..32]);
1411                if file_key.len() == 32 && perms_marker_valid(&file_key, perms) {
1412                    return Some(file_key);
1413                }
1414            }
1415        }
1416        if let (Some(o), Some(oe)) = (o, enc.get("OE").and_then(Object::as_str_bytes)) {
1417            if o.len() >= 48 && oe.len() >= 32 {
1418                let intermediate = hash_2b(r, pw, &o[40..48], &u[..48]);
1419                let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &oe[..32]);
1420                if file_key.len() == 32 && perms_marker_valid(&file_key, perms) {
1421                    return Some(file_key);
1422                }
1423            }
1424        }
1425    }
1426    if let Some(ue) = enc.get("UE").and_then(Object::as_str_bytes) {
1427        if ue.len() >= 32 && hash_2b(r, pw, &u[32..40], &[])[..32] == u[..32] {
1428            let intermediate = hash_2b(r, pw, &u[40..48], &[]);
1429            let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &ue[..32]);
1430            if file_key.len() == 32 {
1431                return Some(file_key);
1432            }
1433        }
1434    }
1435    let o = o?;
1436    let oe = enc.get("OE").and_then(Object::as_str_bytes)?;
1437    if o.len() < 48 || oe.len() < 32 {
1438        return None;
1439    }
1440    if hash_2b(r, pw, &o[32..40], &u[..48])[..32] != o[..32] {
1441        return None;
1442    }
1443    let intermediate = hash_2b(r, pw, &o[40..48], &u[..48]);
1444    let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &oe[..32]);
1445    (file_key.len() == 32).then_some(file_key)
1446}
1447
1448/// Whether `file_key` decrypts `/Perms` to its mandated marker: bytes 9-11
1449/// spell "adb" (ISO 32000-2 Algorithm 2.A step g; AES-256 ECB, no IV).
1450fn perms_marker_valid(file_key: &[u8], perms: &[u8]) -> bool {
1451    let rks = aes_expand_key(file_key);
1452    let inv_sbox = aes_inv_sbox();
1453    let mut block = [0u8; 16];
1454    block.copy_from_slice(&perms[..16]);
1455    aes_decrypt_block(&mut block, &rks, &inv_sbox);
1456    &block[9..12] == b"adb"
1457}
1458
1459/// The revision-6 password hash (ISO 32000-2, Algorithm 2.B); a plain SHA-256
1460/// for revision 5.
1461fn hash_2b(r: i64, password: &[u8], salt: &[u8], udata: &[u8]) -> Vec<u8> {
1462    let mut seed = Vec::with_capacity(password.len() + salt.len() + udata.len());
1463    seed.extend_from_slice(password);
1464    seed.extend_from_slice(salt);
1465    seed.extend_from_slice(udata);
1466    let mut k = sha256(&seed).to_vec();
1467    if r < 6 {
1468        return k; // revision 5: a single SHA-256
1469    }
1470    let mut round = 0usize;
1471    let mut k1 = Vec::with_capacity(64 * (password.len() + 64 + udata.len()));
1472    loop {
1473        k1.clear();
1474        for _ in 0..64 {
1475            k1.extend_from_slice(password);
1476            k1.extend_from_slice(&k);
1477            k1.extend_from_slice(udata);
1478        }
1479        let e = aes_cbc_encrypt_blocks(&k[..16], &k[16..32], &k1);
1480        let modulus = e[..16].iter().map(|&b| u32::from(b)).sum::<u32>() % 3;
1481        k = match modulus {
1482            0 => sha256(&e).to_vec(),
1483            1 => sha384(&e),
1484            _ => sha512(&e),
1485        };
1486        round += 1;
1487        if round >= 64
1488            && e.last()
1489                .is_some_and(|&last| usize::from(last) <= round - 32)
1490        {
1491            break;
1492        }
1493    }
1494    k.truncate(32);
1495    k
1496}
1497
1498/// Freshly generated key material for a revision-6 `/Encrypt` dictionary:
1499/// the random file key plus `/U`, `/UE`, `/O`, `/OE` and `/Perms` derived
1500/// from it (ISO 32000-2 §7.6.4.3.4-3.6, Algorithms 8, 9 and 10).
1501pub(crate) struct KeyMaterial {
1502    file_key: [u8; 32],
1503    u: Vec<u8>,
1504    ue: Vec<u8>,
1505    o: Vec<u8>,
1506    oe: Vec<u8>,
1507    perms: Vec<u8>,
1508}
1509
1510/// Builds a [`KeyMaterial`] for `user_pw`/`owner_pw` at permission bits `p`
1511/// (ISO 32000-2 §7.6.4.2, Table 22), recording whether metadata is left
1512/// unencrypted. `rng` fills the random bytes Algorithms 8-10 call for: the
1513/// 32-byte file key, the user and owner validation/key salts, and the
1514/// 4-byte `/Perms` pad. Passwords longer than the 127 UTF-8 bytes the
1515/// algorithm defines are truncated, matching the validation side.
1516pub(crate) fn r6_key_material<E>(
1517    user_pw: &[u8],
1518    owner_pw: &[u8],
1519    p: i32,
1520    encrypt_metadata: bool,
1521    rng: &mut dyn FnMut(&mut [u8]) -> std::result::Result<(), E>,
1522) -> std::result::Result<KeyMaterial, E> {
1523    let user_pw = &user_pw[..user_pw.len().min(127)];
1524    let owner_pw = &owner_pw[..owner_pw.len().min(127)];
1525
1526    let mut file_key = [0u8; 32];
1527    rng(&mut file_key)?;
1528
1529    // Algorithm 8: /U and /UE from a 16-byte random pad, the first 8
1530    // bytes the validation salt and the last 8 the key salt.
1531    let mut user_salt = [0u8; 16];
1532    rng(&mut user_salt)?;
1533    let mut u = hash_2b(6, user_pw, &user_salt[..8], &[]);
1534    u.extend_from_slice(&user_salt);
1535    let user_intermediate = hash_2b(6, user_pw, &user_salt[8..], &[]);
1536    let ue = aes_cbc_encrypt_blocks(&user_intermediate, &[0u8; 16], &file_key);
1537
1538    // Algorithm 9: /O and /OE, the same derivation but hashing in the
1539    // first 48 bytes of /U as well.
1540    let mut owner_salt = [0u8; 16];
1541    rng(&mut owner_salt)?;
1542    let mut o = hash_2b(6, owner_pw, &owner_salt[..8], &u[..48]);
1543    o.extend_from_slice(&owner_salt);
1544    let owner_intermediate = hash_2b(6, owner_pw, &owner_salt[8..], &u[..48]);
1545    let oe = aes_cbc_encrypt_blocks(&owner_intermediate, &[0u8; 16], &file_key);
1546
1547    // Algorithm 10: /Perms, the file key applied to a single AES-256 ECB
1548    // block (not CBC).
1549    let mut perms_block = [0u8; 16];
1550    perms_block[..4].copy_from_slice(&p.to_le_bytes());
1551    perms_block[4..8].copy_from_slice(&[0xff; 4]);
1552    perms_block[8] = if encrypt_metadata { b'T' } else { b'F' };
1553    perms_block[9..12].copy_from_slice(b"adb");
1554    rng(&mut perms_block[12..])?;
1555    aes_encrypt_block(&mut perms_block, &aes_expand_key(&file_key));
1556
1557    Ok(KeyMaterial {
1558        file_key,
1559        u,
1560        ue,
1561        o,
1562        oe,
1563        perms: perms_block.to_vec(),
1564    })
1565}
1566
1567/// Fills `buf` with operating-system random bytes, or reports the source as
1568/// unavailable rather than panicking. Not available on
1569/// `wasm32-unknown-unknown`, where `getrandom` is not a dependency at all;
1570/// use [`Encryptor::aes256_with_rng`] there with a caller-supplied source.
1571#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1572pub(crate) fn fill_os_random(buf: &mut [u8]) -> crate::error::Result<()> {
1573    getrandom::fill(buf).map_err(|e| crate::error::Error::RandomUnavailable(e.to_string()))
1574}
1575
1576/// The eight standard-handler permission bits a document opened under the
1577/// user password (rather than the owner password) is granted (ISO 32000-2
1578/// §7.6.4.2, Table 22). Bits the standard reserves are not represented
1579/// here; `p_value` fixes them to the values it mandates.
1580#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1581pub struct Permissions {
1582    /// Print the document, at full quality only when `print_hires` is also
1583    /// set (Table 22 bit 3).
1584    pub print: bool,
1585    /// Modify the document's contents, beyond what `annotate`,
1586    /// `fill_forms` and `assemble` separately cover (Table 22 bit 4).
1587    pub modify: bool,
1588    /// Copy or otherwise extract text and graphics (Table 22 bit 5).
1589    pub copy: bool,
1590    /// Add or modify text annotations and fill form fields (Table 22 bit
1591    /// 6).
1592    pub annotate: bool,
1593    /// Fill in existing form fields even without `annotate` (Table 22 bit
1594    /// 9).
1595    pub fill_forms: bool,
1596    /// Extract text and graphics for accessibility (Table 22 bit 10).
1597    pub accessibility: bool,
1598    /// Assemble the document: insert, rotate or delete pages, create
1599    /// bookmarks or thumbnails (Table 22 bit 11).
1600    pub assemble: bool,
1601    /// Print at full quality; without it, printing (if `print` allows it)
1602    /// may be limited to a low-resolution rasterization (Table 22 bit 12).
1603    pub print_hires: bool,
1604}
1605
1606/// The permission names [`Permissions::from_names`] accepts, in the order
1607/// named in `--allow`/`allow` help text and error messages: `print`,
1608/// `modify`, `copy`, `annotate`, `fill-forms`, `accessibility`, `assemble`,
1609/// `print-hires`.
1610pub const PERMISSION_NAMES: [&str; 8] = [
1611    "print",
1612    "modify",
1613    "copy",
1614    "annotate",
1615    "fill-forms",
1616    "accessibility",
1617    "assemble",
1618    "print-hires",
1619];
1620
1621impl Permissions {
1622    /// Every permission granted.
1623    pub fn all() -> Permissions {
1624        Permissions {
1625            print: true,
1626            modify: true,
1627            copy: true,
1628            annotate: true,
1629            fill_forms: true,
1630            accessibility: true,
1631            assemble: true,
1632            print_hires: true,
1633        }
1634    }
1635
1636    /// Every permission named in `names` granted, everything else denied.
1637    /// The first name not in [`PERMISSION_NAMES`] comes back as `Err`,
1638    /// carrying that name alone: the CLI and the Python bindings each wrap
1639    /// it in their own error type and message text.
1640    pub fn from_names<'a>(names: impl IntoIterator<Item = &'a str>) -> Result<Permissions, String> {
1641        let mut permissions = Permissions {
1642            print: false,
1643            modify: false,
1644            copy: false,
1645            annotate: false,
1646            fill_forms: false,
1647            accessibility: false,
1648            assemble: false,
1649            print_hires: false,
1650        };
1651        for name in names {
1652            match name {
1653                "print" => permissions.print = true,
1654                "modify" => permissions.modify = true,
1655                "copy" => permissions.copy = true,
1656                "annotate" => permissions.annotate = true,
1657                "fill-forms" => permissions.fill_forms = true,
1658                "accessibility" => permissions.accessibility = true,
1659                "assemble" => permissions.assemble = true,
1660                "print-hires" => permissions.print_hires = true,
1661                other => return Err(other.to_string()),
1662            }
1663        }
1664        Ok(permissions)
1665    }
1666
1667    /// The `/P` integer this set of permissions encodes (ISO 32000-2
1668    /// §7.6.4.2, Table 22): bits 1 and 2 are reserved and always cleared;
1669    /// bits 3, 4, 5, 6, 9, 10, 11 and 12 carry `print`, `modify`, `copy`,
1670    /// `annotate`, `fill_forms`, `accessibility`, `assemble` and
1671    /// `print_hires` in that order; every other bit is reserved and always
1672    /// set. Granting everything therefore yields `-4`.
1673    pub(crate) fn p_value(&self) -> i32 {
1674        let flags: [(u32, bool); 8] = [
1675            (3, self.print),
1676            (4, self.modify),
1677            (5, self.copy),
1678            (6, self.annotate),
1679            (9, self.fill_forms),
1680            (10, self.accessibility),
1681            (11, self.assemble),
1682            (12, self.print_hires),
1683        ];
1684        let mut bits: u32 = !0b11; // every bit set, then clear the two reserved low bits
1685        for (bit, granted) in flags {
1686            if !granted {
1687                bits &= !(1u32 << (bit - 1));
1688            }
1689        }
1690        bits as i32
1691    }
1692}
1693
1694impl Default for Permissions {
1695    /// Every permission granted, same as [`Permissions::all`].
1696    fn default() -> Permissions {
1697        Permissions::all()
1698    }
1699}
1700
1701/// Builds the complete AES-256, revision 6 `/Encrypt` dictionary
1702/// (ISO 32000-2 §7.6.4.2) from generated key material: `/Filter /Standard`,
1703/// `/V 5`, `/R 6`, `/Length 256`, a `/CF` naming the standard crypt filter
1704/// as `/AESV3` with a 32-byte key, `/StmF` and `/StrF` both pointing at it,
1705/// and the `/U`, `/UE`, `/O`, `/OE`, `/P` and `/Perms` entries.
1706fn aes256_encrypt_dict(material: &KeyMaterial, p: i32) -> Dict {
1707    let mut std_cf = Dict::new();
1708    std_cf.insert(
1709        Name("CFM".to_string()),
1710        Object::Name(Name("AESV3".to_string())),
1711    );
1712    std_cf.insert(Name("Length".to_string()), Object::Int(32));
1713    let mut cf = Dict::new();
1714    cf.insert(Name("StdCF".to_string()), Object::Dict(std_cf));
1715
1716    let mut dict = Dict::new();
1717    dict.insert(
1718        Name("Filter".to_string()),
1719        Object::Name(Name("Standard".to_string())),
1720    );
1721    dict.insert(Name("V".to_string()), Object::Int(5));
1722    dict.insert(Name("R".to_string()), Object::Int(6));
1723    dict.insert(Name("Length".to_string()), Object::Int(256));
1724    dict.insert(Name("CF".to_string()), Object::Dict(cf));
1725    dict.insert(
1726        Name("StmF".to_string()),
1727        Object::Name(Name("StdCF".to_string())),
1728    );
1729    dict.insert(
1730        Name("StrF".to_string()),
1731        Object::Name(Name("StdCF".to_string())),
1732    );
1733    dict.insert(Name("P".to_string()), Object::Int(i64::from(p)));
1734    dict.insert(Name("U".to_string()), Object::String(material.u.clone()));
1735    dict.insert(Name("UE".to_string()), Object::String(material.ue.clone()));
1736    dict.insert(Name("O".to_string()), Object::String(material.o.clone()));
1737    dict.insert(Name("OE".to_string()), Object::String(material.oe.clone()));
1738    dict.insert(
1739        Name("Perms".to_string()),
1740        Object::String(material.perms.clone()),
1741    );
1742    dict
1743}
1744
1745/// A configured Standard-handler encryptor: AES-256, revision 6, the write
1746/// side of [`Decryptor`]. Built by [`Encryptor::aes256`] or
1747/// [`Encryptor::aes256_with_rng`], which also return the complete
1748/// `/Encrypt` dictionary for the trailer.
1749#[allow(
1750    clippy::type_complexity,
1751    reason = "Box<dyn FnMut(&mut [u8]) -> Result<()> + Send> names the one random-byte source the write path takes; a type alias would only rename it"
1752)]
1753pub struct Encryptor {
1754    file_key: [u8; 32],
1755    rng: Box<dyn FnMut(&mut [u8]) -> crate::error::Result<()> + Send>,
1756}
1757
1758impl Encryptor {
1759    /// AES-256 (`/V` 5, `/R` 6) key material from the operating system's
1760    /// random source (ISO 32000-2 §7.6.4.3). Returns the encryptor plus the
1761    /// complete `/Encrypt` dictionary to place in the trailer. Passwords
1762    /// encode as UTF-8 and are truncated to 127 bytes, matching the reader.
1763    /// Fails with [`crate::Error::RandomUnavailable`] when the operating
1764    /// system's random source cannot be read, rather than panicking.
1765    ///
1766    /// Not available on `wasm32-unknown-unknown`: there is no operating
1767    /// system random source to draw from there. Use
1768    /// [`Encryptor::aes256_with_rng`] instead, with caller-supplied
1769    /// randomness (for example from the host's `crypto.getRandomValues`).
1770    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1771    pub fn aes256(
1772        user_password: &str,
1773        owner_password: &str,
1774        permissions: Permissions,
1775    ) -> crate::error::Result<(Encryptor, Dict)> {
1776        let mut rng = |buf: &mut [u8]| fill_os_random(buf);
1777        let p = permissions.p_value();
1778        let material = r6_key_material(
1779            user_password.as_bytes(),
1780            owner_password.as_bytes(),
1781            p,
1782            true,
1783            &mut rng,
1784        )?;
1785        let dict = aes256_encrypt_dict(&material, p);
1786        let encryptor = Encryptor {
1787            file_key: material.file_key,
1788            rng: Box::new(|buf: &mut [u8]| fill_os_random(buf)),
1789        };
1790        Ok((encryptor, dict))
1791    }
1792
1793    /// [`Encryptor::aes256`] with a caller-supplied source of random bytes
1794    /// in place of the operating system's, for reproducible tests.
1795    #[allow(clippy::type_complexity, reason = "see Encryptor's rng field")]
1796    pub fn aes256_with_rng(
1797        user_password: &str,
1798        owner_password: &str,
1799        permissions: Permissions,
1800        rng: Box<dyn FnMut(&mut [u8]) + Send>,
1801    ) -> (Encryptor, Dict) {
1802        let mut rng = rng;
1803        let p = permissions.p_value();
1804        let material = match r6_key_material(
1805            user_password.as_bytes(),
1806            owner_password.as_bytes(),
1807            p,
1808            true,
1809            &mut |buf: &mut [u8]| -> std::result::Result<(), std::convert::Infallible> {
1810                rng(buf);
1811                Ok(())
1812            },
1813        ) {
1814            Ok(material) => material,
1815            Err(never) => match never {},
1816        };
1817        let dict = aes256_encrypt_dict(&material, p);
1818        let encryptor = Encryptor {
1819            file_key: material.file_key,
1820            rng: Box::new(move |buf: &mut [u8]| {
1821                rng(buf);
1822                Ok(())
1823            }),
1824        };
1825        (encryptor, dict)
1826    }
1827
1828    /// Encrypts one indirect object's strings and stream data in place, the
1829    /// exact inverse of [`Decryptor::decrypt_object`]: every string and
1830    /// stream body gets a fresh random IV and PKCS#7 padding before
1831    /// AES-256-CBC under the file key. AESV3 applies the same key to every
1832    /// object, so `num` and `gen` are accepted and ignored here, purely for
1833    /// signature symmetry with `decrypt_object`; a future per-object write
1834    /// cipher (an AESV2 write path, say) would need to start deriving its
1835    /// key from them instead of discarding them.
1836    pub fn encrypt_object(
1837        &mut self,
1838        obj: &mut Object,
1839        num: u32,
1840        gen: u16,
1841    ) -> crate::error::Result<()> {
1842        let _ = (num, gen);
1843        encrypt_in_place(obj, &self.file_key, &mut *self.rng)
1844    }
1845}
1846
1847/// Recursively encrypts every string and stream body reachable from `obj`
1848/// with the file `key`, mirroring [`decrypt_in_place`]'s walk exactly:
1849/// `String`, `Array`, `Dict` values and `Stream` (dict values plus data);
1850/// every other object type is left untouched.
1851fn encrypt_in_place(
1852    obj: &mut Object,
1853    key: &[u8],
1854    rng: &mut dyn FnMut(&mut [u8]) -> crate::error::Result<()>,
1855) -> crate::error::Result<()> {
1856    match obj {
1857        Object::String(bytes) => *bytes = aes_cbc_encrypt(key, rng, bytes)?,
1858        Object::Array(items) => {
1859            for it in items {
1860                encrypt_in_place(it, key, &mut *rng)?;
1861            }
1862        }
1863        Object::Dict(dict) => {
1864            for v in dict.values_mut() {
1865                encrypt_in_place(v, key, &mut *rng)?;
1866            }
1867        }
1868        Object::Stream(stream) => {
1869            for v in stream.dict.values_mut() {
1870                encrypt_in_place(v, key, &mut *rng)?;
1871            }
1872            // A stream that names the Identity crypt filter says its data
1873            // is stored in the clear (ISO 32000-1 §7.4.10); keep it so, or
1874            // no reader could get it back.
1875            if stream_crypt_filter(&stream.dict).as_deref() == Some("Identity") {
1876                return Ok(());
1877            }
1878            stream.data = aes_cbc_encrypt(key, rng, &stream.data)?;
1879        }
1880        _ => {}
1881    }
1882    Ok(())
1883}
1884
1885/// Per-round left-rotation amounts (RFC 1321).
1886#[rustfmt::skip]
1887const MD5_S: [u32; 64] = [
1888    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
1889    5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
1890    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
1891    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
1892];
1893
1894/// Per-round additive constants `floor(2^32 * abs(sin(i + 1)))` (RFC 1321).
1895#[rustfmt::skip]
1896const MD5_K: [u32; 64] = [
1897    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
1898    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
1899    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
1900    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
1901    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
1902    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
1903    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
1904    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
1905];
1906
1907/// One-shot MD5 (RFC 1321). Sufficient for the small key-derivation inputs; not
1908/// a streaming API.
1909fn md5(input: &[u8]) -> [u8; 16] {
1910    let (mut a0, mut b0, mut c0, mut d0) = (
1911        0x6745_2301u32,
1912        0xefcd_ab89u32,
1913        0x98ba_dcfeu32,
1914        0x1032_5476u32,
1915    );
1916
1917    let mut msg = input.to_vec();
1918    let bitlen = (input.len() as u64).wrapping_mul(8);
1919    msg.push(0x80);
1920    while msg.len() % 64 != 56 {
1921        msg.push(0);
1922    }
1923    msg.extend_from_slice(&bitlen.to_le_bytes());
1924
1925    for chunk in msg.as_chunks::<64>().0 {
1926        let mut m = [0u32; 16];
1927        for (word, bytes) in m.iter_mut().zip(chunk.as_chunks::<4>().0) {
1928            *word = u32::from_le_bytes(*bytes);
1929        }
1930        let (mut a, mut b, mut c, mut d) = (a0, b0, c0, d0);
1931        for i in 0..64 {
1932            let (f, g) = match i {
1933                0..=15 => ((b & c) | (!b & d), i),
1934                16..=31 => ((d & b) | (!d & c), (5 * i + 1) % 16),
1935                32..=47 => (b ^ c ^ d, (3 * i + 5) % 16),
1936                _ => (c ^ (b | !d), (7 * i) % 16),
1937            };
1938            let f = f.wrapping_add(a).wrapping_add(MD5_K[i]).wrapping_add(m[g]);
1939            a = d;
1940            d = c;
1941            c = b;
1942            b = b.wrapping_add(f.rotate_left(MD5_S[i]));
1943        }
1944        a0 = a0.wrapping_add(a);
1945        b0 = b0.wrapping_add(b);
1946        c0 = c0.wrapping_add(c);
1947        d0 = d0.wrapping_add(d);
1948    }
1949
1950    let mut out = [0u8; 16];
1951    out[0..4].copy_from_slice(&a0.to_le_bytes());
1952    out[4..8].copy_from_slice(&b0.to_le_bytes());
1953    out[8..12].copy_from_slice(&c0.to_le_bytes());
1954    out[12..16].copy_from_slice(&d0.to_le_bytes());
1955    out
1956}
1957
1958#[cfg(test)]
1959mod tests {
1960    use super::*;
1961    use crate::object::Name;
1962
1963    fn hex(bytes: &[u8]) -> String {
1964        bytes.iter().map(|b| format!("{b:02x}")).collect()
1965    }
1966
1967    #[test]
1968    fn md5_known_vectors() {
1969        assert_eq!(hex(&md5(b"")), "d41d8cd98f00b204e9800998ecf8427e");
1970        assert_eq!(hex(&md5(b"abc")), "900150983cd24fb0d6963f7d28e17f72");
1971        assert_eq!(
1972            hex(&md5(b"The quick brown fox jumps over the lazy dog")),
1973            "9e107d9d372bb6826bd81d3542a419d6"
1974        );
1975    }
1976
1977    #[test]
1978    fn md5_spans_block_boundary() {
1979        // 56 bytes forces a second padded block.
1980        let input = [b'a'; 56];
1981        assert_eq!(hex(&md5(&input)), "3b0c8ac703f828b04c6c197006d17218");
1982    }
1983
1984    /// FIPS-197 Appendix C block vectors, both key sizes and both
1985    /// directions, held against the dispatching entry (the hardware path
1986    /// wherever this test runs on a CPU with AES instructions) AND the
1987    /// portable rounds directly, so neither path can drift.
1988    #[test]
1989    fn aes_block_known_vectors() {
1990        let plain: [u8; 16] = core::array::from_fn(|i| (i as u8) * 0x11);
1991        let key128: [u8; 16] = core::array::from_fn(|i| i as u8);
1992        let key256: [u8; 32] = core::array::from_fn(|i| i as u8);
1993
1994        type Encrypt = fn(&mut [u8; 16], &[[u8; 16]]);
1995        type Decrypt = fn(&mut [u8; 16], &[[u8; 16]], &[u8; 256]);
1996        let paths: [(Encrypt, Decrypt); 2] = [
1997            (aes_encrypt_block, aes_decrypt_block),
1998            (aes_encrypt_block_soft, aes_decrypt_block_soft),
1999        ];
2000        for (encrypt, decrypt) in paths {
2001            let mut s = plain;
2002            encrypt(&mut s, &aes_expand_key(&key128));
2003            assert_eq!(hex(&s), "69c4e0d86a7b0430d8cdb78070b4c55a");
2004            decrypt(&mut s, &aes_expand_key(&key128), &aes_inv_sbox());
2005            assert_eq!(s, plain);
2006
2007            let mut s = plain;
2008            encrypt(&mut s, &aes_expand_key(&key256));
2009            assert_eq!(hex(&s), "8ea2b7ca516745bfeafc49904b496089");
2010            decrypt(&mut s, &aes_expand_key(&key256), &aes_inv_sbox());
2011            assert_eq!(s, plain);
2012        }
2013    }
2014
2015    /// Multi-block CBC round-trips through the block paths, chaining
2016    /// included: what the R6 hash encrypts, decryption must invert.
2017    #[test]
2018    fn aes_cbc_round_trips_multiple_blocks() {
2019        let key: [u8; 16] = core::array::from_fn(|i| (i as u8).wrapping_mul(7));
2020        let iv: [u8; 16] = core::array::from_fn(|i| 0xa5 ^ (i as u8));
2021        let data: Vec<u8> = (0..64u8).collect();
2022        let ct = aes_cbc_encrypt_blocks(&key, &iv, &data);
2023        assert_eq!(ct.len(), data.len());
2024        assert_ne!(ct, data);
2025        assert_eq!(aes_cbc_decrypt_blocks(&key, &iv, &ct), data);
2026    }
2027
2028    /// NIST FIPS 180 vectors for the SHA-2 family, spanning the empty
2029    /// input, one block, and inputs long enough to cross block
2030    /// boundaries — the contract the hardware compressions must match.
2031    #[test]
2032    fn sha2_known_vectors() {
2033        assert_eq!(
2034            hex(&sha256(b"")),
2035            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
2036        );
2037        assert_eq!(
2038            hex(&sha256(b"abc")),
2039            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
2040        );
2041        assert_eq!(
2042            hex(&sha256(&[b'a'; 200])),
2043            "c2a908d98f5df987ade41b5fce213067efbcc21ef2240212a41e54b5e7c28ae5"
2044        );
2045        assert_eq!(
2046            hex(&sha384(b"abc")),
2047            "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\
2048             8086072ba1e7cc2358baeca134c825a7"
2049        );
2050        assert_eq!(
2051            hex(&sha512(b"abc")),
2052            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a\
2053             2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
2054        );
2055        assert_eq!(
2056            hex(&sha512(&[b'a'; 300])),
2057            "a6a77010dd9696c23831e6549de51724df332c2075039b75fcfe6c2e6de42fbd\
2058             3c80ed4073267e00c8c320712c3cdd9d65a96f90a3fe4a58a6b70a103be08e83"
2059        );
2060    }
2061
2062    /// The portable compressions must evolve state identically to the
2063    /// dispatching entries (the hardware path wherever this runs on a CPU
2064    /// with SHA instructions), block by block.
2065    #[test]
2066    fn sha2_soft_compressions_match_the_dispatch() {
2067        let block64: [u8; 64] = core::array::from_fn(|i| (i as u8).wrapping_mul(31));
2068        let mut via_dispatch = [0x6a09e667u32; 8];
2069        let mut via_soft = via_dispatch;
2070        sha256_compress(&mut via_dispatch, &block64);
2071        sha256_compress_soft(&mut via_soft, &block64);
2072        assert_eq!(via_dispatch, via_soft);
2073
2074        let block128: [u8; 128] = core::array::from_fn(|i| (i as u8).wrapping_mul(29));
2075        let mut via_dispatch = [0x6a09e667f3bcc908u64; 8];
2076        let mut via_soft = via_dispatch;
2077        sha512_compress(&mut via_dispatch, &block128);
2078        sha512_compress_soft(&mut via_soft, &block128);
2079        assert_eq!(via_dispatch, via_soft);
2080    }
2081
2082    /// The incremental hasher must match the one-shot digest whatever the
2083    /// slice boundaries: odd sizes, block-straddling feeds, empty updates.
2084    #[test]
2085    fn incremental_sha256_matches_the_one_shot() {
2086        let input: Vec<u8> = (0..300u16).map(|i| (i % 251) as u8).collect();
2087        for splits in [
2088            vec![0, 1, 3, 7, 60, 63, 64, 65, 37],
2089            vec![300],
2090            vec![128, 128, 44],
2091            vec![55, 9, 236],
2092        ] {
2093            let mut hasher = Sha256::new();
2094            let mut fed = 0usize;
2095            for len in splits {
2096                hasher.update(&input[fed..fed + len]);
2097                fed += len;
2098            }
2099            hasher.update(&input[fed..]);
2100            assert_eq!(hasher.finalize(), sha256(&input));
2101        }
2102        assert_eq!(Sha256::new().finalize(), sha256(b""));
2103        assert_eq!(Sha256::default().finalize(), sha256(b""));
2104    }
2105
2106    #[test]
2107    fn rc4_known_vector() {
2108        // Classic RC4 test vector: key "Key", plaintext "Plaintext".
2109        let ct = rc4(b"Key", b"Plaintext");
2110        assert_eq!(hex(&ct), "bbf316e8d940af0ad3");
2111        // Symmetric: decrypting the ciphertext returns the plaintext.
2112        assert_eq!(rc4(b"Key", &ct), b"Plaintext");
2113    }
2114
2115    // --- End-to-end fixture: build a V2/R3 (128-bit RC4) file encrypted under
2116    // caller-chosen user and owner passwords, then confirm the loader
2117    // decrypts it — transparently for the empty user password, and through
2118    // the password APIs for real ones. ---
2119
2120    const N: usize = 16; // 128-bit key
2121    const P: i32 = -44;
2122    const ID0: &[u8] = b"0123456789abcdef";
2123
2124    /// `/O` for the given owner and user passwords (Algorithm 3, R3).
2125    fn owner_entry(owner_pw: &[u8], user_pw: &[u8]) -> Vec<u8> {
2126        owner_entry_with(N, owner_pw, user_pw)
2127    }
2128
2129    /// [`owner_entry`] for an `n`-byte key.
2130    fn owner_entry_with(n: usize, owner_pw: &[u8], user_pw: &[u8]) -> Vec<u8> {
2131        let mut d = md5(&pad_password(owner_pw));
2132        for _ in 0..50 {
2133            d = md5(&d[..n]);
2134        }
2135        let rc4key = d[..n].to_vec();
2136        let mut o = rc4(&rc4key, &pad_password(user_pw));
2137        for i in 1u8..=19 {
2138            let k: Vec<u8> = rc4key.iter().map(|b| b ^ i).collect();
2139            o = rc4(&k, &o);
2140        }
2141        o
2142    }
2143
2144    /// File key from `/O` for the given user password (Algorithm 2, R3).
2145    fn file_key(o: &[u8], user_pw: &[u8]) -> Vec<u8> {
2146        file_key_with(N, o, user_pw)
2147    }
2148
2149    /// [`file_key`] for an `n`-byte key.
2150    fn file_key_with(n: usize, o: &[u8], user_pw: &[u8]) -> Vec<u8> {
2151        let mut input = Vec::new();
2152        input.extend_from_slice(&pad_password(user_pw));
2153        input.extend_from_slice(o);
2154        input.extend_from_slice(&(P as u32).to_le_bytes());
2155        input.extend_from_slice(ID0);
2156        let mut d = md5(&input);
2157        for _ in 0..50 {
2158            d = md5(&d[..n]);
2159        }
2160        d[..n].to_vec()
2161    }
2162
2163    /// The `/Msg` string of dictionary object `num`, as loaded.
2164    fn msg_of(doc: &crate::Document, num: u32) -> Vec<u8> {
2165        use crate::object::ObjRef;
2166        let obj = doc.get(ObjRef { num, gen: 0 }).unwrap();
2167        obj.as_dict()
2168            .unwrap()
2169            .get("Msg")
2170            .unwrap()
2171            .as_str_bytes()
2172            .unwrap()
2173            .to_vec()
2174    }
2175
2176    /// The decoded data of stream object `num`.
2177    fn body_of(doc: &crate::Document, num: u32) -> Vec<u8> {
2178        use crate::object::ObjRef;
2179        let obj = doc.get(ObjRef { num, gen: 0 }).unwrap();
2180        doc.stream_data(obj.as_stream().unwrap()).unwrap()
2181    }
2182
2183    /// `/U` for the given file key (Algorithm 5, R3).
2184    fn user_entry(key: &[u8]) -> Vec<u8> {
2185        let mut input = Vec::new();
2186        input.extend_from_slice(&PAD);
2187        input.extend_from_slice(ID0);
2188        let mut x = md5(&input).to_vec();
2189        x = rc4(key, &x);
2190        for i in 1u8..=19 {
2191            let k: Vec<u8> = key.iter().map(|b| b ^ i).collect();
2192            x = rc4(&k, &x);
2193        }
2194        x.resize(32, 0); // trailing padding is arbitrary
2195        x
2196    }
2197
2198    fn obj_key(key: &[u8], num: u32, gen: u16) -> Vec<u8> {
2199        let mut input = key.to_vec();
2200        input.extend_from_slice(&num.to_le_bytes()[..3]);
2201        input.extend_from_slice(&gen.to_le_bytes()[..2]);
2202        md5(&input)[..(key.len() + 5).min(16)].to_vec()
2203    }
2204
2205    fn hexstr(b: &[u8]) -> String {
2206        let mut s = String::from("<");
2207        for x in b {
2208            s.push_str(&format!("{x:02x}"));
2209        }
2210        s.push('>');
2211        s
2212    }
2213
2214    fn encrypted_fixture(u_override: Option<Vec<u8>>) -> Vec<u8> {
2215        encrypted_fixture_with(b"", b"", u_override)
2216    }
2217
2218    fn encrypted_fixture_with(
2219        user_pw: &[u8],
2220        owner_pw: &[u8],
2221        u_override: Option<Vec<u8>>,
2222    ) -> Vec<u8> {
2223        use pdfboss_testkit::PdfBuilder;
2224        let o = owner_entry(owner_pw, user_pw);
2225        let key = file_key(&o, user_pw);
2226        let u = u_override.unwrap_or_else(|| user_entry(&key));
2227
2228        let msg = rc4(&obj_key(&key, 3, 0), b"Top secret message");
2229        let stream = rc4(&obj_key(&key, 4, 0), b"decrypted stream body");
2230
2231        let mut b = PdfBuilder::new().version(1, 4);
2232        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2233        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2234        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2235        b.stream(4, "", &stream);
2236        b.object(
2237            9,
2238            &format!(
2239                "<< /Filter /Standard /V 2 /R 3 /Length 128 /P {P} /O {} /U {} >>",
2240                hexstr(&o),
2241                hexstr(&u)
2242            ),
2243        );
2244        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2245        b.trailer_extra(&trailer).build(1)
2246    }
2247
2248    // Covers ISO 32000-1 §7.6.2, §7.6.3.2 and §7.6.3.3.
2249    #[test]
2250    fn document_load_decrypts_standard_rc4() {
2251        use crate::object::ObjRef;
2252        use crate::Document;
2253
2254        let doc = Document::load(encrypted_fixture(None)).expect("empty password opens the file");
2255
2256        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2257        let msg = obj3
2258            .as_dict()
2259            .unwrap()
2260            .get("Msg")
2261            .unwrap()
2262            .as_str_bytes()
2263            .unwrap();
2264        assert_eq!(msg, b"Top secret message", "string decrypted");
2265
2266        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2267        let data = doc.stream_data(obj4.as_stream().unwrap()).unwrap();
2268        assert_eq!(data, b"decrypted stream body", "stream decrypted");
2269    }
2270
2271    // Covers ISO 32000-1 §7.6.3.4.
2272    #[test]
2273    fn document_load_rejects_when_password_does_not_verify() {
2274        use crate::error::Error;
2275        use crate::Document;
2276
2277        // A `/U` that will not verify under the empty password stands in for a
2278        // real password-protected file: the loader must decline, not decrypt.
2279        let bad_u = vec![0u8; 32];
2280        let err = Document::load(encrypted_fixture(Some(bad_u)));
2281        assert!(matches!(err, Err(Error::Encrypted)));
2282    }
2283
2284    // Covers ISO 32000-1 §7.6.3, §7.6.3.3 and §7.6.3.4.
2285    #[test]
2286    fn real_user_password_opens_an_rc4_file() {
2287        use crate::error::Error;
2288        use crate::object::ObjRef;
2289        use crate::Document;
2290
2291        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2292        assert!(
2293            matches!(Document::load(bytes.clone()), Err(Error::Encrypted)),
2294            "without the password the file stays closed"
2295        );
2296        let doc = Document::load_with_password(bytes, "hunter2").expect("user password opens");
2297        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2298        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2299        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2300    }
2301
2302    // Covers ISO 32000-1 §7.6.3.4.
2303    #[test]
2304    fn owner_password_opens_an_rc4_file() {
2305        use crate::object::ObjRef;
2306        use crate::Document;
2307
2308        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2309        let doc =
2310            Document::load_with_password(bytes, "owner-secret").expect("owner password opens");
2311        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2312        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2313        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2314    }
2315
2316    // Covers ISO 32000-1 §7.6.3.4.
2317    #[test]
2318    fn wrong_password_stays_encrypted() {
2319        use crate::error::Error;
2320        use crate::Document;
2321
2322        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2323        let err = Document::load_with_password(bytes, "letmein");
2324        assert!(matches!(err, Err(Error::Encrypted)));
2325    }
2326
2327    // Covers ISO 32000-1 §7.6.3 and §7.6.3.4.
2328    #[test]
2329    fn real_passwords_open_an_aes256_r6_file() {
2330        use crate::error::Error;
2331        use crate::object::ObjRef;
2332        use crate::Document;
2333
2334        let bytes = encrypted_fixture_aesv3_with(6, "pässword".as_bytes(), b"owner-secret");
2335        assert!(
2336            matches!(Document::load(bytes.clone()), Err(Error::Encrypted)),
2337            "without the password the file stays closed"
2338        );
2339        for pw in ["pässword", "owner-secret"] {
2340            let doc = Document::load_with_password(bytes.clone(), pw)
2341                .unwrap_or_else(|_| panic!("{pw:?} opens the file"));
2342            let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2343            let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2344            assert_eq!(msg.as_str_bytes().unwrap(), b"AES-256 secret");
2345        }
2346        assert!(matches!(
2347            Document::load_with_password(bytes, "letmein"),
2348            Err(Error::Encrypted)
2349        ));
2350    }
2351
2352    // Covers ISO 32000-1 §7.6.3.
2353    #[test]
2354    fn empty_password_files_still_open_through_the_password_api() {
2355        use crate::object::ObjRef;
2356        use crate::Document;
2357
2358        // Passing a password to an empty-password file must not break it:
2359        // the empty user password still verifies... only if the caller's
2360        // password IS empty; a random one is simply wrong for this file.
2361        let doc = Document::load_with_password(encrypted_fixture(None), "")
2362            .expect("the empty password opens through the password API too");
2363        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2364        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2365        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2366    }
2367
2368    // Covers ISO 32000-1 §7.6.3.2.
2369    #[test]
2370    fn unsupported_handler_is_declined() {
2371        // A future/unknown handler version is declined so the caller reports the
2372        // file as encrypted-and-unsupported.
2373        let mut enc = Dict::new();
2374        enc.insert(Name("Filter".into()), Object::Name(Name("Standard".into())));
2375        enc.insert(Name("V".into()), Object::Int(6));
2376        enc.insert(Name("R".into()), Object::Int(7));
2377        enc.insert(Name("O".into()), Object::String(vec![0; 48]));
2378        enc.insert(Name("U".into()), Object::String(vec![0; 48]));
2379        enc.insert(Name("P".into()), Object::Int(-4));
2380        assert!(Decryptor::from_standard(&enc, ID0).is_none());
2381    }
2382
2383    // --- AES / SHA-2 known-answer vectors ---
2384
2385    #[test]
2386    fn aes_fips197_block_vectors() {
2387        // FIPS-197 Appendix C.1 (AES-128) and C.3 (AES-256), same plaintext.
2388        let pt: [u8; 16] = [
2389            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
2390            0xee, 0xff,
2391        ];
2392        let key128: Vec<u8> = (0u8..16).collect();
2393        let rks = aes_expand_key(&key128);
2394        let mut b = pt;
2395        aes_encrypt_block(&mut b, &rks);
2396        assert_eq!(hex(&b), "69c4e0d86a7b0430d8cdb78070b4c55a");
2397        aes_decrypt_block(&mut b, &rks, &aes_inv_sbox());
2398        assert_eq!(b, pt, "AES-128 decrypt inverts encrypt");
2399
2400        let key256: Vec<u8> = (0u8..32).collect();
2401        let rks = aes_expand_key(&key256);
2402        let mut b = pt;
2403        aes_encrypt_block(&mut b, &rks);
2404        assert_eq!(hex(&b), "8ea2b7ca516745bfeafc49904b496089");
2405    }
2406
2407    #[test]
2408    fn sha2_vectors() {
2409        assert_eq!(
2410            hex(&sha256(b"abc")),
2411            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
2412        );
2413        assert_eq!(
2414            hex(&sha512(b"abc")),
2415            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a\
2416             2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
2417        );
2418        assert_eq!(
2419            hex(&sha384(b"abc")),
2420            "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\
2421             8086072ba1e7cc2358baeca134c825a7"
2422        );
2423    }
2424
2425    #[test]
2426    fn aes_cbc_roundtrip() {
2427        let key: Vec<u8> = (0u8..16).collect();
2428        let iv = [0x24u8; 16];
2429        let pt = b"a message spanning several AES blocks exactly?!!";
2430        let ct = aes_cbc_encrypt_blocks(&key, &iv, &pkcs7_pad(pt));
2431        let mut val = iv.to_vec(); // PDF format: IV followed by ciphertext
2432        val.extend_from_slice(&ct);
2433        assert_eq!(aes_cbc_decrypt(&key, &val), pt);
2434    }
2435
2436    // --- AESV2 (V4/R4) end-to-end fixture ---
2437
2438    fn obj_key_aes(key: &[u8], num: u32, gen: u16) -> Vec<u8> {
2439        let mut input = key.to_vec();
2440        input.extend_from_slice(&num.to_le_bytes()[..3]);
2441        input.extend_from_slice(&gen.to_le_bytes()[..2]);
2442        input.extend_from_slice(b"sAlT");
2443        md5(&input)[..(key.len() + 5).min(16)].to_vec()
2444    }
2445
2446    /// Encrypts as a PDF AES value: a 16-byte IV followed by CBC ciphertext of
2447    /// the PKCS#7-padded plaintext.
2448    fn aes_encrypt_pdf(key: &[u8], pt: &[u8], iv: &[u8; 16]) -> Vec<u8> {
2449        let mut out = iv.to_vec();
2450        out.extend_from_slice(&aes_cbc_encrypt_blocks(key, iv, &pkcs7_pad(pt)));
2451        out
2452    }
2453
2454    fn encrypted_fixture_aesv2() -> Vec<u8> {
2455        use pdfboss_testkit::PdfBuilder;
2456        let o = owner_entry(b"", b"");
2457        let key = file_key(&o, b""); // R4 derivation matches R3 (EncryptMetadata true)
2458        let u = user_entry(&key);
2459        let iv = [0x11u8; 16];
2460        let msg = aes_encrypt_pdf(&obj_key_aes(&key, 3, 0), b"Top secret message", &iv);
2461        let stream = aes_encrypt_pdf(&obj_key_aes(&key, 4, 0), b"decrypted stream body", &iv);
2462
2463        let mut b = PdfBuilder::new().version(1, 5);
2464        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2465        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2466        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2467        b.stream(4, "", &stream);
2468        b.object(
2469            9,
2470            &format!(
2471                "<< /Filter /Standard /V 4 /R 4 /Length 128 /P {P} /O {} /U {} \
2472                 /CF << /StdCF << /CFM /AESV2 /Length 16 >> >> /StmF /StdCF /StrF /StdCF >>",
2473                hexstr(&o),
2474                hexstr(&u)
2475            ),
2476        );
2477        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2478        b.trailer_extra(&trailer).build(1)
2479    }
2480
2481    // Covers ISO 32000-1 §7.6.2, §7.6.3.2, §7.6.3.3 and §7.6.5.
2482    #[test]
2483    fn document_load_decrypts_aesv2() {
2484        use crate::object::ObjRef;
2485        use crate::Document;
2486        let doc = Document::load(encrypted_fixture_aesv2()).expect("AESV2 empty password opens");
2487        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2488        let msg = obj3
2489            .as_dict()
2490            .unwrap()
2491            .get("Msg")
2492            .unwrap()
2493            .as_str_bytes()
2494            .unwrap();
2495        assert_eq!(msg, b"Top secret message");
2496        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2497        assert_eq!(
2498            doc.stream_data(obj4.as_stream().unwrap()).unwrap(),
2499            b"decrypted stream body"
2500        );
2501    }
2502
2503    // --- V4 crypt filters: /CF, /StmF, /StrF, /EFF and the filter /Length ---
2504
2505    /// A V4/R4 file under the empty user password. `encrypt_entries` is the
2506    /// crypt filter part of the `/Encrypt` dictionary (`/CF`, `/StmF`,
2507    /// `/StrF`, `/EFF`, `/Length`), `n` the key length in bytes, and `body`
2508    /// adds the objects under test, encrypting them with the file key it is
2509    /// handed. Object 9 is the encryption dictionary.
2510    fn v4_fixture(
2511        n: usize,
2512        encrypt_entries: &str,
2513        body: impl FnOnce(&mut pdfboss_testkit::PdfBuilder, &[u8]),
2514    ) -> Vec<u8> {
2515        use pdfboss_testkit::PdfBuilder;
2516        let o = owner_entry_with(n, b"", b"");
2517        let key = file_key_with(n, &o, b"");
2518        let u = user_entry(&key);
2519        let mut b = PdfBuilder::new().version(1, 5);
2520        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2521        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2522        body(&mut b, &key);
2523        b.object(
2524            9,
2525            &format!(
2526                "<< /Filter /Standard /V 4 /R 4 /P {P} /O {} /U {} {encrypt_entries} >>",
2527                hexstr(&o),
2528                hexstr(&u)
2529            ),
2530        );
2531        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2532        b.trailer_extra(&trailer).build(1)
2533    }
2534
2535    const AESV2_CF: &str = "/CF << /StdCF << /CFM /AESV2 /Length 16 >> >>";
2536    const TWO_CF: &str =
2537        "/CF << /StdCF << /CFM /AESV2 /Length 16 >> /RcCF << /CFM /V2 /Length 16 >> >>";
2538    const IV: [u8; 16] = [0x11; 16];
2539
2540    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /StmF may name the standard
2541    // Identity filter, which leaves every stream as stored while /StrF still
2542    // decrypts the strings.
2543    #[test]
2544    fn identity_stream_filter_leaves_streams_as_stored() {
2545        use crate::Document;
2546        let data = v4_fixture(
2547            16,
2548            &format!("/Length 128 {AESV2_CF} /StmF /Identity /StrF /StdCF"),
2549            |b, key| {
2550                let msg = aes_encrypt_pdf(&obj_key_aes(key, 3, 0), b"Top secret message", &IV);
2551                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2552                b.stream(4, "", b"stored in the clear");
2553            },
2554        );
2555        let doc = Document::load(data).expect("an Identity stream filter is a valid V4 file");
2556        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2557        assert_eq!(body_of(&doc, 4), b"stored in the clear");
2558    }
2559
2560    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /StrF picks the string cipher
2561    // on its own, so strings and streams may use different methods.
2562    #[test]
2563    fn string_filter_is_chosen_independently_of_the_stream_filter() {
2564        use crate::Document;
2565        let data = v4_fixture(
2566            16,
2567            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /RcCF"),
2568            |b, key| {
2569                let msg = rc4(&obj_key(key, 3, 0), b"Top secret message");
2570                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2571                let stream =
2572                    aes_encrypt_pdf(&obj_key_aes(key, 4, 0), b"decrypted stream body", &IV);
2573                b.stream(4, "", &stream);
2574            },
2575        );
2576        let doc = Document::load(data).expect("two crypt filters open");
2577        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2578        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2579    }
2580
2581    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /EFF names the filter for
2582    // embedded file streams; other streams keep /StmF.
2583    #[test]
2584    fn embedded_file_filter_applies_to_embedded_file_streams() {
2585        use crate::Document;
2586        let data = v4_fixture(
2587            16,
2588            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /StdCF /EFF /RcCF"),
2589            |b, key| {
2590                let attached = rc4(&obj_key(key, 4, 0), b"attached file bytes");
2591                b.stream(4, "/Type /EmbeddedFile", &attached);
2592                let stream =
2593                    aes_encrypt_pdf(&obj_key_aes(key, 5, 0), b"decrypted stream body", &IV);
2594                b.stream(5, "", &stream);
2595            },
2596        );
2597        let doc = Document::load(data).expect("an /EFF entry opens");
2598        assert_eq!(body_of(&doc, 4), b"attached file bytes");
2599        assert_eq!(body_of(&doc, 5), b"decrypted stream body");
2600    }
2601
2602    // Covers ISO 32000-1 §7.6.5: the Standard handler writes the crypt
2603    // filter's /Length in bytes, and it sets the key size when the
2604    // encryption dictionary itself has no /Length.
2605    #[test]
2606    fn crypt_filter_length_sets_the_key_size() {
2607        use crate::Document;
2608        let data = v4_fixture(
2609            5,
2610            "/CF << /StdCF << /CFM /V2 /Length 5 >> >> /StmF /StdCF /StrF /StdCF",
2611            |b, key| {
2612                let msg = rc4(&obj_key(key, 3, 0), b"Top secret message");
2613                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2614                b.stream(4, "", &rc4(&obj_key(key, 4, 0), b"decrypted stream body"));
2615            },
2616        );
2617        let doc = Document::load(data).expect("a 40-bit V4 file opens");
2618        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2619        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2620    }
2621
2622    // Covers ISO 32000-1 §7.6.2 and §7.6.5: AESV2 is AES-128, so its object
2623    // key is the whole 16-byte MD5 digest whatever the file key's length,
2624    // and a 40-bit file key must not reach the block cipher with 10 bytes.
2625    #[test]
2626    fn aesv2_always_derives_a_16_byte_object_key() {
2627        use crate::Document;
2628        let object_key = |key: &[u8], num: u32| {
2629            let mut input = key.to_vec();
2630            input.extend_from_slice(&num.to_le_bytes()[..3]);
2631            input.extend_from_slice(&[0, 0]);
2632            input.extend_from_slice(b"sAlT");
2633            md5(&input).to_vec()
2634        };
2635        let data = v4_fixture(
2636            5,
2637            "/Length 40 /CF << /StdCF << /CFM /AESV2 /Length 5 >> >> /StmF /StdCF /StrF /StdCF",
2638            |b, key| {
2639                let msg = aes_encrypt_pdf(&object_key(key, 3), b"Top secret message", &IV);
2640                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2641                let stream = aes_encrypt_pdf(&object_key(key, 4), b"decrypted stream body", &IV);
2642                b.stream(4, "", &stream);
2643            },
2644        );
2645        let doc = Document::load(data).expect("a 40-bit AESV2 file opens");
2646        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2647        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2648    }
2649
2650    // --- Per-stream /Crypt filters ---
2651
2652    // Covers ISO 32000-1 §7.4.10 and §7.6.5: a stream naming the Identity
2653    // crypt filter is stored in the clear inside an encrypted document.
2654    #[test]
2655    fn a_stream_crypt_filter_naming_identity_is_left_as_stored() {
2656        use crate::Document;
2657        let data = v4_fixture(
2658            16,
2659            &format!("/Length 128 {AESV2_CF} /StmF /StdCF /StrF /StdCF"),
2660            |b, key| {
2661                b.stream(
2662                    4,
2663                    "/Filter /Crypt /DecodeParms << /Type /CryptFilterDecodeParms /Name /Identity >>",
2664                    b"stored in the clear",
2665                );
2666                let stream =
2667                    aes_encrypt_pdf(&obj_key_aes(key, 5, 0), b"decrypted stream body", &IV);
2668                b.stream(5, "", &stream);
2669            },
2670        );
2671        let doc = Document::load(data).expect("opens");
2672        assert_eq!(body_of(&doc, 4), b"stored in the clear");
2673        assert_eq!(body_of(&doc, 5), b"decrypted stream body");
2674    }
2675
2676    // Covers ISO 32000-1 §7.4.10: without a /Name the Crypt filter means
2677    // Identity, and the filters after it still run.
2678    #[test]
2679    fn a_stream_crypt_filter_without_a_name_means_identity() {
2680        use crate::Document;
2681        let data = v4_fixture(
2682            16,
2683            &format!("/Length 128 {AESV2_CF} /StmF /StdCF /StrF /StdCF"),
2684            |b, _| {
2685                b.stream(
2686                    4,
2687                    "/Filter [/Crypt /ASCIIHexDecode]",
2688                    b"73746F72656420617320686578>",
2689                );
2690            },
2691        );
2692        let doc = Document::load(data).expect("opens");
2693        assert_eq!(body_of(&doc, 4), b"stored as hex");
2694    }
2695
2696    // Covers ISO 32000-1 §7.4.10 and §7.6.5: a stream naming one of the /CF
2697    // filters is decrypted with that filter's method and the file key as
2698    // is, without the per-object key of Algorithm 1.
2699    #[test]
2700    fn a_stream_crypt_filter_naming_a_cf_entry_uses_the_file_key_as_is() {
2701        use crate::Document;
2702        let data = v4_fixture(
2703            16,
2704            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /StdCF"),
2705            |b, key| {
2706                b.stream(
2707                    4,
2708                    "/Filter /Crypt /DecodeParms << /Name /RcCF >>",
2709                    &rc4(key, b"under the file key"),
2710                );
2711            },
2712        );
2713        let doc = Document::load(data).expect("opens");
2714        assert_eq!(body_of(&doc, 4), b"under the file key");
2715    }
2716
2717    // --- AESV3 (V5/R5 and R6) end-to-end fixture ---
2718
2719    fn encrypted_fixture_aesv3(r: i64) -> Vec<u8> {
2720        encrypted_fixture_aesv3_with(r, b"", b"")
2721    }
2722
2723    fn encrypted_fixture_aesv3_with(r: i64, user_pw: &[u8], owner_pw: &[u8]) -> Vec<u8> {
2724        encrypted_fixture_aesv3_with_metadata(r, user_pw, owner_pw, true, None)
2725    }
2726
2727    /// [`encrypted_fixture_aesv3_with`], plus an `/EncryptMetadata` entry
2728    /// and, when `metadata` is given, object 5 as a `/Type /Metadata`
2729    /// stream holding its bytes verbatim (never encrypted here, since a
2730    /// real writer with `encrypt_metadata` false stores it in plaintext
2731    /// too): the fixture a reader must not corrupt.
2732    fn encrypted_fixture_aesv3_with_metadata(
2733        r: i64,
2734        user_pw: &[u8],
2735        owner_pw: &[u8],
2736        encrypt_metadata: bool,
2737        metadata: Option<&[u8]>,
2738    ) -> Vec<u8> {
2739        use pdfboss_testkit::PdfBuilder;
2740        let key = aesv3_file_key();
2741        let iv = [0x22u8; 16];
2742        let msg = aes_encrypt_pdf(&key, b"AES-256 secret", &iv);
2743        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &iv);
2744
2745        let mut b = PdfBuilder::new().version(1, 7);
2746        let catalog = match metadata {
2747            Some(_) => "<< /Type /Catalog /Pages 2 0 R /Metadata 5 0 R >>",
2748            None => "<< /Type /Catalog /Pages 2 0 R >>",
2749        };
2750        b.object(1, catalog);
2751        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2752        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2753        b.stream(4, "", &stream);
2754        if let Some(metadata) = metadata {
2755            b.stream(5, "<< /Type /Metadata /Subtype /XML >>", metadata);
2756        }
2757        let encrypt_metadata_entry = if encrypt_metadata {
2758            ""
2759        } else {
2760            " /EncryptMetadata false"
2761        };
2762        b.object(
2763            9,
2764            &format!(
2765                "<< /Filter /Standard /V 5 /R {r} /Length 256 /P {P} {}{encrypt_metadata_entry} \
2766                 /CF << /StdCF << /CFM /AESV3 /Length 32 >> >> /StmF /StdCF /StrF /StdCF >>",
2767                aesv3_password_entries(r, user_pw, owner_pw, &key),
2768            ),
2769        );
2770        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2771        b.trailer_extra(&trailer).build(1)
2772    }
2773
2774    /// The arbitrary 256-bit file key every AESV3 fixture is encrypted with.
2775    fn aesv3_file_key() -> Vec<u8> {
2776        (0u8..32).map(|i| i ^ 0x5a).collect()
2777    }
2778
2779    /// The `/U`, `/UE`, `/O` and `/OE` entries of a revision `r` AES-256
2780    /// dictionary that open `key` under the given passwords.
2781    fn aesv3_password_entries(r: i64, user_pw: &[u8], owner_pw: &[u8], key: &[u8]) -> String {
2782        let vsalt: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
2783        let ksalt: [u8; 8] = [9, 10, 11, 12, 13, 14, 15, 16];
2784        let mut u = hash_2b(r, user_pw, &vsalt, &[]); // 32-byte validation hash
2785        u.extend_from_slice(&vsalt);
2786        u.extend_from_slice(&ksalt);
2787        let intermediate = hash_2b(r, user_pw, &ksalt, &[]);
2788        let ue = aes_cbc_encrypt_blocks(&intermediate, &[0u8; 16], key);
2789        // The owner hashes additionally salt in the first 48 bytes of /U.
2790        let ovsalt: [u8; 8] = [21, 22, 23, 24, 25, 26, 27, 28];
2791        let oksalt: [u8; 8] = [31, 32, 33, 34, 35, 36, 37, 38];
2792        let mut o = hash_2b(r, owner_pw, &ovsalt, &u[..48]);
2793        o.extend_from_slice(&ovsalt);
2794        o.extend_from_slice(&oksalt);
2795        let ointermediate = hash_2b(r, owner_pw, &oksalt, &u[..48]);
2796        let oe = aes_cbc_encrypt_blocks(&ointermediate, &[0u8; 16], key);
2797        format!(
2798            "/U {} /UE {} /O {} /OE {}",
2799            hexstr(&u),
2800            hexstr(&ue),
2801            hexstr(&o),
2802            hexstr(&oe)
2803        )
2804    }
2805
2806    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: only the strings of an
2807    // encryption dictionary must be direct, so /CF may be an indirect
2808    // reference (pdf.js issue7665 is such a file) and still be read.
2809    #[test]
2810    fn an_indirect_cf_dictionary_is_followed() {
2811        use crate::Document;
2812        use pdfboss_testkit::PdfBuilder;
2813        let key = aesv3_file_key();
2814        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &[0x22u8; 16]);
2815        let mut b = PdfBuilder::new().version(1, 7);
2816        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2817        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2818        b.stream(4, "", &stream);
2819        b.object(8, "<< /StdCF << /CFM /AESV3 /Length 32 >> >>");
2820        b.object(
2821            9,
2822            &format!(
2823                "<< /Filter /Standard /V 5 /R 6 /Length 256 /P {P} {} \
2824                 /CF 8 0 R /StmF /StdCF /StrF /StdCF >>",
2825                aesv3_password_entries(6, b"", b"", &key),
2826            ),
2827        );
2828        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2829        let doc = Document::load(b.trailer_extra(&trailer).build(1))
2830            .expect("an indirect /CF still names the filters");
2831        assert_eq!(body_of(&doc, 4), b"AES-256 stream body");
2832    }
2833
2834    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: a V5 file picks its filters
2835    // from /CF the way a V4 file does, the Identity filter included.
2836    #[test]
2837    fn aesv3_identity_string_filter_leaves_strings_as_stored() {
2838        use crate::Document;
2839        use pdfboss_testkit::PdfBuilder;
2840        let key = aesv3_file_key();
2841        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &[0x22u8; 16]);
2842        let mut b = PdfBuilder::new().version(1, 7);
2843        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2844        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2845        b.object(3, "<< /Msg (stored in the clear) >>");
2846        b.stream(4, "", &stream);
2847        b.object(
2848            9,
2849            &format!(
2850                "<< /Filter /Standard /V 5 /R 6 /Length 256 /P {P} {} \
2851                 /CF << /StdCF << /CFM /AESV3 /Length 32 >> >> /StmF /StdCF /StrF /Identity >>",
2852                aesv3_password_entries(6, b"", b"", &key),
2853            ),
2854        );
2855        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2856        let doc = Document::load(b.trailer_extra(&trailer).build(1))
2857            .expect("an Identity string filter is a valid V5 file");
2858        assert_eq!(msg_of(&doc, 3), b"stored in the clear");
2859        assert_eq!(body_of(&doc, 4), b"AES-256 stream body");
2860    }
2861
2862    fn assert_aesv3_decrypts(r: i64) {
2863        use crate::object::ObjRef;
2864        use crate::Document;
2865        let doc = Document::load(encrypted_fixture_aesv3(r)).expect("AESV3 empty password opens");
2866        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2867        let msg = obj3
2868            .as_dict()
2869            .unwrap()
2870            .get("Msg")
2871            .unwrap()
2872            .as_str_bytes()
2873            .unwrap();
2874        assert_eq!(msg, b"AES-256 secret", "R{r} string");
2875        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2876        assert_eq!(
2877            doc.stream_data(obj4.as_stream().unwrap()).unwrap(),
2878            b"AES-256 stream body",
2879            "R{r} stream"
2880        );
2881    }
2882
2883    // Covers ISO 32000-1 §7.6.2.
2884    #[test]
2885    fn document_load_decrypts_aesv3_r5() {
2886        assert_aesv3_decrypts(5);
2887    }
2888
2889    // Covers ISO 32000-1 §7.6.3.3 and §7.6.5.
2890    #[test]
2891    fn document_load_decrypts_aesv3_r6() {
2892        assert_aesv3_decrypts(6); // exercises the iterated Algorithm 2.B hash
2893    }
2894
2895    // Covers ISO 32000-1 §14.3.2, §7.6.3.2 and §7.6.5.
2896    #[test]
2897    fn encrypt_metadata_false_leaves_the_metadata_stream_plaintext() {
2898        use crate::object::ObjRef;
2899        use crate::Document;
2900
2901        const XMP: &[u8] = b"<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>\
2902            <x:xmpmeta xmlns:x='adobe:ns:meta/'><rdf:RDF \
2903            xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'><rdf:Description \
2904            xmlns:dc='http://purl.org/dc/elements/1.1/'><dc:title>XMP Secret \
2905            Title</dc:title></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end='w'?>";
2906
2907        let bytes =
2908            encrypted_fixture_aesv3_with_metadata(6, b"user-pw", b"owner-pw", false, Some(XMP));
2909        let doc = Document::load_with_password(bytes, "user-pw").expect("user password opens");
2910
2911        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2912        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2913        assert_eq!(
2914            msg.as_str_bytes().unwrap(),
2915            b"AES-256 secret",
2916            "an ordinary string still decrypts under EncryptMetadata false"
2917        );
2918
2919        let obj5 = doc.get(ObjRef { num: 5, gen: 0 }).unwrap();
2920        let metadata = doc.stream_data(obj5.as_stream().unwrap()).unwrap();
2921        assert_eq!(
2922            metadata, XMP,
2923            "the metadata stream was stored in plaintext and must not be decrypted"
2924        );
2925    }
2926
2927    // --- R6 key material (write side): the existing validation code above
2928    // is the oracle everything generated here must open under. ---
2929
2930    #[test]
2931    fn encrypt_in_place_reports_an_unavailable_random_source() {
2932        let mut obj = Object::String(b"secret".to_vec());
2933        let mut failing = |_: &mut [u8]| -> crate::error::Result<()> {
2934            Err(crate::error::Error::RandomUnavailable(
2935                "exhausted".to_owned(),
2936            ))
2937        };
2938        let result = encrypt_in_place(&mut obj, &[0u8; 32], &mut failing);
2939        assert!(matches!(
2940            result,
2941            Err(crate::error::Error::RandomUnavailable(_))
2942        ));
2943    }
2944
2945    /// Generated key material must open through the same `aesv3_key`
2946    /// validation a real reader runs, for both passwords, and reject a
2947    /// wrong one; the embedded `/Perms` must decrypt to its marker under
2948    /// the generated file key. The dict comes from `aes256_encrypt_dict`,
2949    /// the same builder `Encryptor::aes256_with_rng` ships, so this test
2950    /// exercises the real production layout rather than a hand-built one.
2951    // Covers ISO 32000-1 §7.6.3.3.
2952    #[test]
2953    fn r6_key_material_opens_under_aesv3_key() {
2954        let mut c = 0u8;
2955        let mut rng = |b: &mut [u8]| -> std::result::Result<(), std::convert::Infallible> {
2956            for x in b {
2957                c = c.wrapping_add(1);
2958                *x = c;
2959            }
2960            Ok(())
2961        };
2962        let material = match r6_key_material(b"user-pw", b"owner-pw", P, true, &mut rng) {
2963            Ok(material) => material,
2964            Err(never) => match never {},
2965        };
2966        let dict = aes256_encrypt_dict(&material, P);
2967
2968        let user_key = aesv3_key(&dict, 6, b"user-pw").expect("user password opens");
2969        assert_eq!(user_key, material.file_key.to_vec());
2970
2971        let owner_key = aesv3_key(&dict, 6, b"owner-pw").expect("owner password opens");
2972        assert_eq!(owner_key, material.file_key.to_vec());
2973
2974        assert!(aesv3_key(&dict, 6, b"wrong-pw").is_none());
2975
2976        assert!(perms_marker_valid(&material.file_key, &material.perms));
2977
2978        // With /Perms absent, aesv3_key skips the fast path and falls
2979        // through to comparing the /U and /O validation hashes directly,
2980        // proving those 32 bytes (not just the key-salt derivation the
2981        // fast path above exercised) are correct too.
2982        let mut without_perms = dict.clone();
2983        without_perms.remove("Perms");
2984        assert_eq!(
2985            aesv3_key(&without_perms, 6, b"user-pw"),
2986            Some(material.file_key.to_vec())
2987        );
2988        assert_eq!(
2989            aesv3_key(&without_perms, 6, b"owner-pw"),
2990            Some(material.file_key.to_vec())
2991        );
2992    }
2993
2994    // --- Permissions: the /P bit layout (ISO 32000-2 Table 22) ---
2995
2996    #[test]
2997    fn permissions_all_grants_every_bit() {
2998        // Bits 1-2 reserved and cleared, every other bit set: -4.
2999        assert_eq!(Permissions::all().p_value(), -4);
3000        assert_eq!(Permissions::default().p_value(), -4);
3001    }
3002
3003    #[test]
3004    fn permissions_p_value_clears_exactly_its_own_bit() {
3005        let base = Permissions::all().p_value();
3006        assert_eq!(
3007            Permissions {
3008                print: false,
3009                ..Permissions::all()
3010            }
3011            .p_value(),
3012            base & !(1 << 2), // bit 3
3013        );
3014        assert_eq!(
3015            Permissions {
3016                modify: false,
3017                ..Permissions::all()
3018            }
3019            .p_value(),
3020            base & !(1 << 3), // bit 4
3021        );
3022        assert_eq!(
3023            Permissions {
3024                copy: false,
3025                ..Permissions::all()
3026            }
3027            .p_value(),
3028            base & !(1 << 4), // bit 5
3029        );
3030        assert_eq!(
3031            Permissions {
3032                annotate: false,
3033                ..Permissions::all()
3034            }
3035            .p_value(),
3036            base & !(1 << 5), // bit 6
3037        );
3038        assert_eq!(
3039            Permissions {
3040                fill_forms: false,
3041                ..Permissions::all()
3042            }
3043            .p_value(),
3044            base & !(1 << 8), // bit 9
3045        );
3046        assert_eq!(
3047            Permissions {
3048                accessibility: false,
3049                ..Permissions::all()
3050            }
3051            .p_value(),
3052            base & !(1 << 9), // bit 10
3053        );
3054        assert_eq!(
3055            Permissions {
3056                assemble: false,
3057                ..Permissions::all()
3058            }
3059            .p_value(),
3060            base & !(1 << 10), // bit 11
3061        );
3062        assert_eq!(
3063            Permissions {
3064                print_hires: false,
3065                ..Permissions::all()
3066            }
3067            .p_value(),
3068            base & !(1 << 11), // bit 12
3069        );
3070    }
3071
3072    // --- Encryptor (write side): the round-trip proof for Task 2 ---
3073
3074    /// A `FnMut(&mut [u8])` filling buffers from an incrementing counter,
3075    /// so key material and IVs are reproducible without touching the OS
3076    /// random source.
3077    #[allow(clippy::type_complexity, reason = "see Encryptor's rng field")]
3078    fn counter_rng() -> Box<dyn FnMut(&mut [u8]) + Send> {
3079        let mut c = 0u8;
3080        Box::new(move |buf: &mut [u8]| {
3081            for b in buf {
3082                c = c.wrapping_add(1);
3083                *b = c;
3084            }
3085        })
3086    }
3087
3088    /// Recovers the file key from a produced `/Encrypt` dict through the
3089    /// same `aesv3_key` validation a real reader runs, and builds the
3090    /// matching `Decryptor`: the oracle every encrypted object must open
3091    /// under.
3092    fn decryptor_for(dict: &Dict, password: &[u8]) -> Decryptor {
3093        let key = aesv3_key(dict, 6, password).expect("password opens the produced dict");
3094        Decryptor {
3095            key,
3096            filters: CryptFilters::uniform(Cipher::Aesv3),
3097            encrypt_metadata: true,
3098        }
3099    }
3100
3101    #[test]
3102    fn encrypt_object_round_trips_dict_array_and_leaves_other_types_untouched() {
3103        use crate::object::ObjRef;
3104
3105        let (mut enc, dict) =
3106            Encryptor::aes256_with_rng("user-pw", "owner-pw", Permissions::all(), counter_rng());
3107
3108        let mut d = Dict::new();
3109        d.insert(
3110            Name("Msg".to_string()),
3111            Object::String(b"Top secret message".to_vec()),
3112        );
3113        d.insert(
3114            Name("List".to_string()),
3115            Object::Array(vec![
3116                Object::String(b"first".to_vec()),
3117                Object::String(b"second".to_vec()),
3118            ]),
3119        );
3120        d.insert(
3121            Name("Ref".to_string()),
3122            Object::Ref(ObjRef { num: 7, gen: 0 }),
3123        );
3124        d.insert(
3125            Name("Kind".to_string()),
3126            Object::Name(Name("Example".to_string())),
3127        );
3128        d.insert(Name("Count".to_string()), Object::Int(42));
3129        d.insert(Name("Scale".to_string()), Object::Real(1.5));
3130        d.insert(Name("On".to_string()), Object::Bool(true));
3131        d.insert(Name("Missing".to_string()), Object::Null);
3132        let original = Object::Dict(d);
3133
3134        let mut obj = original.clone();
3135        enc.encrypt_object(&mut obj, 3, 0).unwrap();
3136        assert_ne!(obj, original, "encryption must change the strings");
3137
3138        // Ref, Name, numbers, bools and null pass through untouched, even
3139        // in the still-encrypted object.
3140        let (enc_dict, orig_dict) = (obj.as_dict().unwrap(), original.as_dict().unwrap());
3141        for key in ["Ref", "Kind", "Count", "Scale", "On", "Missing"] {
3142            assert_eq!(
3143                enc_dict.get(key),
3144                orig_dict.get(key),
3145                "{key} passes through untouched"
3146            );
3147        }
3148
3149        let decryptor = decryptor_for(&dict, b"user-pw");
3150        let mut roundtripped = obj.clone();
3151        decryptor.decrypt_object(&mut roundtripped, 3, 0);
3152        assert_eq!(roundtripped, original);
3153
3154        // The owner password recovers the same file key.
3155        let owner_decryptor = decryptor_for(&dict, b"owner-pw");
3156        let mut via_owner = obj;
3157        owner_decryptor.decrypt_object(&mut via_owner, 3, 0);
3158        assert_eq!(via_owner, original);
3159    }
3160
3161    #[test]
3162    fn encrypt_object_round_trips_a_stream_and_its_dict() {
3163        use crate::object::Stream;
3164
3165        let (mut enc, dict) =
3166            Encryptor::aes256_with_rng("", "owner-pw", Permissions::all(), counter_rng());
3167
3168        let mut stream_dict = Dict::new();
3169        stream_dict.insert(
3170            Name("Producer".to_string()),
3171            Object::String(b"pdfboss".to_vec()),
3172        );
3173        let original = Object::Stream(Stream {
3174            dict: stream_dict,
3175            data: b"decrypted stream body".to_vec(),
3176        });
3177        let mut obj = original.clone();
3178        enc.encrypt_object(&mut obj, 4, 0).unwrap();
3179        let encrypted = obj.as_stream().unwrap();
3180        assert_ne!(encrypted.data, original.as_stream().unwrap().data);
3181        assert_ne!(
3182            encrypted.dict.get("Producer"),
3183            original.as_stream().unwrap().dict.get("Producer"),
3184            "the stream dict's own strings are encrypted too"
3185        );
3186
3187        let decryptor = decryptor_for(&dict, b"");
3188        let mut roundtripped = obj;
3189        decryptor.decrypt_object(&mut roundtripped, 4, 0);
3190        assert_eq!(roundtripped, original);
3191    }
3192
3193    // Covers ISO 32000-1 §7.4.10: a stream that names the Identity crypt
3194    // filter stays in the clear when the document is encrypted, so the
3195    // writer and the reader agree on what the dictionary says.
3196    #[test]
3197    fn encrypt_object_leaves_an_identity_crypt_stream_as_stored() {
3198        use crate::object::Stream;
3199
3200        let (mut enc, dict) =
3201            Encryptor::aes256_with_rng("", "owner-pw", Permissions::all(), counter_rng());
3202        let mut stream_dict = Dict::new();
3203        stream_dict.insert(
3204            Name("Filter".to_string()),
3205            Object::Name(Name("Crypt".to_string())),
3206        );
3207        let mut parms = Dict::new();
3208        parms.insert(
3209            Name("Name".to_string()),
3210            Object::Name(Name("Identity".to_string())),
3211        );
3212        stream_dict.insert(Name("DecodeParms".to_string()), Object::Dict(parms));
3213        let original = Object::Stream(Stream {
3214            dict: stream_dict,
3215            data: b"<?xpacket?> metadata in the clear".to_vec(),
3216        });
3217
3218        let mut obj = original.clone();
3219        enc.encrypt_object(&mut obj, 6, 0).unwrap();
3220        assert_eq!(obj, original, "an Identity stream is written as stored");
3221
3222        let decryptor = decryptor_for(&dict, b"");
3223        decryptor.decrypt_object(&mut obj, 6, 0);
3224        assert_eq!(obj, original, "and read back as stored");
3225    }
3226
3227    #[test]
3228    fn encrypt_object_round_trips_padding_edge_lengths() {
3229        let (mut enc, dict) =
3230            Encryptor::aes256_with_rng("pw", "owner", Permissions::all(), counter_rng());
3231        let decryptor = decryptor_for(&dict, b"pw");
3232
3233        for plaintext in [Vec::new(), vec![0x41], vec![0x42; 16]] {
3234            let original = Object::String(plaintext.clone());
3235            let mut obj = original.clone();
3236            enc.encrypt_object(&mut obj, 1, 0).unwrap();
3237            let mut roundtripped = obj;
3238            decryptor.decrypt_object(&mut roundtripped, 1, 0);
3239            assert_eq!(roundtripped, original, "{} bytes", plaintext.len());
3240        }
3241    }
3242}