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(key: &[u8], rng: &mut dyn FnMut(&mut [u8]), data: &[u8]) -> Vec<u8> {
998    let mut iv = [0u8; 16];
999    rng(&mut iv);
1000    let mut out = iv.to_vec();
1001    out.extend_from_slice(&aes_cbc_encrypt_blocks(key, &iv, &pkcs7_pad(data)));
1002    out
1003}
1004
1005// --- SHA-2 (FIPS 180-4): 256, 512 and 384 --------------------------------
1006
1007#[rustfmt::skip]
1008const SHA256_K: [u32; 64] = [
1009    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1010    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1011    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1012    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1013    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1014    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1015    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1016    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
1017];
1018
1019/// Streaming SHA-256 (FIPS 180-4): feed bytes with [`Sha256::update`],
1020/// close with [`Sha256::finalize`]. Public so the writer can hash a file's
1021/// body as it emits it, without holding the whole file in memory; the
1022/// one-shot [`sha256`] delegates here.
1023#[derive(Debug, Clone)]
1024pub struct Sha256 {
1025    h: [u32; 8],
1026    tail: [u8; 64],
1027    tail_len: usize,
1028    total: u64,
1029}
1030
1031impl Default for Sha256 {
1032    fn default() -> Sha256 {
1033        Sha256::new()
1034    }
1035}
1036
1037impl Sha256 {
1038    /// A hasher in the FIPS 180-4 initial state.
1039    pub fn new() -> Sha256 {
1040        Sha256 {
1041            h: [
1042                0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
1043                0x5be0cd19,
1044            ],
1045            tail: [0u8; 64],
1046            tail_len: 0,
1047            total: 0,
1048        }
1049    }
1050
1051    /// Absorbs `input`. Slice boundaries do not affect the digest: any
1052    /// sequence of updates whose concatenation is the same message yields
1053    /// the same [`Sha256::finalize`] result. Whole blocks are compressed
1054    /// straight from `input`; only a partial trailing block is buffered.
1055    pub fn update(&mut self, input: &[u8]) {
1056        self.total = self.total.wrapping_add(input.len() as u64);
1057        let mut input = input;
1058        if self.tail_len > 0 {
1059            let take = input.len().min(64 - self.tail_len);
1060            self.tail[self.tail_len..self.tail_len + take].copy_from_slice(&input[..take]);
1061            self.tail_len += take;
1062            input = &input[take..];
1063            if self.tail_len < 64 {
1064                return;
1065            }
1066            let block = self.tail;
1067            sha256_compress(&mut self.h, &block);
1068            self.tail_len = 0;
1069        }
1070        let (blocks, rest) = input.as_chunks::<64>();
1071        for block in blocks {
1072            sha256_compress(&mut self.h, block);
1073        }
1074        self.tail[..rest.len()].copy_from_slice(rest);
1075        self.tail_len = rest.len();
1076    }
1077
1078    /// Pads and returns the digest of everything absorbed so far.
1079    pub fn finalize(mut self) -> [u8; 32] {
1080        // Final padding touches at most two blocks: 0x80, zeros, and the
1081        // bit length in the last 8 bytes (FIPS 180-4).
1082        let mut pad = [0u8; 128];
1083        pad[..self.tail_len].copy_from_slice(&self.tail[..self.tail_len]);
1084        pad[self.tail_len] = 0x80;
1085        let padded = if self.tail_len < 56 { 64 } else { 128 };
1086        let bitlen = self.total.wrapping_mul(8);
1087        pad[padded - 8..padded].copy_from_slice(&bitlen.to_be_bytes());
1088        for block in pad[..padded].as_chunks::<64>().0 {
1089            sha256_compress(&mut self.h, block);
1090        }
1091        let mut out = [0u8; 32];
1092        for (i, word) in self.h.iter().enumerate() {
1093            out[4 * i..4 * i + 4].copy_from_slice(&word.to_be_bytes());
1094        }
1095        out
1096    }
1097}
1098
1099/// SHA-256 of `input` (FIPS 180-4); public so the writer can derive its `/ID`.
1100pub fn sha256(input: &[u8]) -> [u8; 32] {
1101    let mut hasher = Sha256::new();
1102    hasher.update(input);
1103    hasher.finalize()
1104}
1105
1106fn sha256_compress(h: &mut [u32; 8], block: &[u8; 64]) {
1107    #[cfg(target_arch = "aarch64")]
1108    if std::arch::is_aarch64_feature_detected!("sha2") {
1109        // SAFETY: the `sha2` target feature was just detected at runtime.
1110        unsafe { sha_hw::compress256(h, block) };
1111        return;
1112    }
1113    sha256_compress_soft(h, block);
1114}
1115
1116/// The portable compression, for CPUs without SHA-256 instructions.
1117fn sha256_compress_soft(h: &mut [u32; 8], block: &[u8; 64]) {
1118    let mut w = [0u32; 64];
1119    for (word, bytes) in w.iter_mut().zip(block.as_chunks::<4>().0) {
1120        *word = u32::from_be_bytes(*bytes);
1121    }
1122    for i in 16..64 {
1123        let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
1124        let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
1125        w[i] = w[i - 16]
1126            .wrapping_add(s0)
1127            .wrapping_add(w[i - 7])
1128            .wrapping_add(s1);
1129    }
1130    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = *h;
1131    for (k, wi) in SHA256_K.iter().zip(&w) {
1132        let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
1133        let ch = (e & f) ^ ((!e) & g);
1134        let t1 = hh
1135            .wrapping_add(s1)
1136            .wrapping_add(ch)
1137            .wrapping_add(*k)
1138            .wrapping_add(*wi);
1139        let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
1140        let maj = (a & b) ^ (a & c) ^ (b & c);
1141        let t2 = s0.wrapping_add(maj);
1142        hh = g;
1143        g = f;
1144        f = e;
1145        e = d.wrapping_add(t1);
1146        d = c;
1147        c = b;
1148        b = a;
1149        a = t1.wrapping_add(t2);
1150    }
1151    for (hv, v) in h.iter_mut().zip([a, b, c, d, e, f, g, hh]) {
1152        *hv = hv.wrapping_add(v);
1153    }
1154}
1155
1156#[rustfmt::skip]
1157const SHA512_K: [u64; 80] = [
1158    0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
1159    0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
1160    0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
1161    0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
1162    0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
1163    0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
1164    0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
1165    0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
1166    0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
1167    0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
1168    0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
1169    0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
1170    0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
1171    0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
1172    0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
1173    0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
1174    0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
1175    0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
1176    0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
1177    0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
1178];
1179
1180fn sha512_core(input: &[u8], mut h: [u64; 8]) -> [u64; 8] {
1181    let (blocks, tail) = input.as_chunks::<128>();
1182    for block in blocks {
1183        sha512_compress(&mut h, block);
1184    }
1185    // Final padding touches at most two blocks: 0x80, zeros, and the bit
1186    // length in the last 16 bytes (FIPS 180-4) — the message itself is
1187    // hashed in place above, never copied.
1188    let mut pad = [0u8; 256];
1189    pad[..tail.len()].copy_from_slice(tail);
1190    pad[tail.len()] = 0x80;
1191    let padded = if tail.len() < 112 { 128 } else { 256 };
1192    let bitlen = (input.len() as u128).wrapping_mul(8);
1193    pad[padded - 16..padded].copy_from_slice(&bitlen.to_be_bytes());
1194    for block in pad[..padded].as_chunks::<128>().0 {
1195        sha512_compress(&mut h, block);
1196    }
1197    h
1198}
1199
1200fn sha512_compress(h: &mut [u64; 8], block: &[u8; 128]) {
1201    #[cfg(target_arch = "aarch64")]
1202    if std::arch::is_aarch64_feature_detected!("sha3") {
1203        // SAFETY: the `sha3` target feature (SHA-512 instructions) was
1204        // just detected at runtime.
1205        unsafe { sha_hw::compress512(h, block) };
1206        return;
1207    }
1208    sha512_compress_soft(h, block);
1209}
1210
1211/// The portable compression, for CPUs without SHA-512 instructions.
1212fn sha512_compress_soft(h: &mut [u64; 8], block: &[u8; 128]) {
1213    let mut w = [0u64; 80];
1214    for (word, bytes) in w.iter_mut().zip(block.as_chunks::<8>().0) {
1215        *word = u64::from_be_bytes(*bytes);
1216    }
1217    for i in 16..80 {
1218        let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
1219        let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
1220        w[i] = w[i - 16]
1221            .wrapping_add(s0)
1222            .wrapping_add(w[i - 7])
1223            .wrapping_add(s1);
1224    }
1225    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = *h;
1226    for (k, wi) in SHA512_K.iter().zip(&w) {
1227        let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
1228        let ch = (e & f) ^ ((!e) & g);
1229        let t1 = hh
1230            .wrapping_add(s1)
1231            .wrapping_add(ch)
1232            .wrapping_add(*k)
1233            .wrapping_add(*wi);
1234        let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
1235        let maj = (a & b) ^ (a & c) ^ (b & c);
1236        let t2 = s0.wrapping_add(maj);
1237        hh = g;
1238        g = f;
1239        f = e;
1240        e = d.wrapping_add(t1);
1241        d = c;
1242        c = b;
1243        b = a;
1244        a = t1.wrapping_add(t2);
1245    }
1246    for (hv, v) in h.iter_mut().zip([a, b, c, d, e, f, g, hh]) {
1247        *hv = hv.wrapping_add(v);
1248    }
1249}
1250
1251/// SHA-2 compressions on the CPU's SHA instructions. Algorithm 2.B hashes
1252/// roughly a megabyte per key derivation across its rounds — after the AES
1253/// rounds moved to hardware this hashing was the remaining ~1-3ms of every
1254/// encrypted-document open (PMU counters put it 60x below the memory-bound
1255/// probe and at the ALU probe's branch signature: a pure serial dependency
1256/// chain, which is exactly what the SHA instructions collapse). Callers
1257/// detect `sha2`/`sha3` before entering; results are pinned to the FIPS
1258/// 180 vectors in the tests below.
1259#[cfg(target_arch = "aarch64")]
1260mod sha_hw {
1261    use core::arch::aarch64::{
1262        uint32x4_t, uint64x2_t, vaddq_u32, vaddq_u64, vextq_u64, vld1q_u32, vld1q_u64, vld1q_u8,
1263        vreinterpretq_u32_u8, vreinterpretq_u64_u8, vrev32q_u8, vrev64q_u8, vsha256h2q_u32,
1264        vsha256hq_u32, vsha256su0q_u32, vsha256su1q_u32, vsha512h2q_u64, vsha512hq_u64,
1265        vsha512su0q_u64, vsha512su1q_u64, vst1q_u32,
1266    };
1267
1268    use super::{SHA256_K, SHA512_K};
1269
1270    /// # Safety
1271    /// Requires the `sha2` target feature.
1272    #[target_feature(enable = "sha2")]
1273    pub unsafe fn compress256(h: &mut [u32; 8], block: &[u8; 64]) {
1274        let mut abcd = vld1q_u32(h.as_ptr());
1275        let mut efgh = vld1q_u32(h.as_ptr().add(4));
1276        let saved = (abcd, efgh);
1277        let mut w: [uint32x4_t; 4] = [
1278            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr()))),
1279            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(16)))),
1280            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(32)))),
1281            vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(48)))),
1282        ];
1283        for step in 0..16 {
1284            let wk = vaddq_u32(w[step % 4], vld1q_u32(SHA256_K.as_ptr().add(4 * step)));
1285            // The last three windows feed no further schedule.
1286            if step < 12 {
1287                w[step % 4] = vsha256su1q_u32(
1288                    vsha256su0q_u32(w[step % 4], w[(step + 1) % 4]),
1289                    w[(step + 2) % 4],
1290                    w[(step + 3) % 4],
1291                );
1292            }
1293            let prev = abcd;
1294            abcd = vsha256hq_u32(abcd, efgh, wk);
1295            efgh = vsha256h2q_u32(efgh, prev, wk);
1296        }
1297        vst1q_u32(h.as_mut_ptr(), vaddq_u32(abcd, saved.0));
1298        vst1q_u32(h.as_mut_ptr().add(4), vaddq_u32(efgh, saved.1));
1299    }
1300
1301    /// # Safety
1302    /// Requires the `sha3` target feature (the SHA-512 instructions).
1303    #[target_feature(enable = "sha2,sha3")]
1304    pub unsafe fn compress512(h: &mut [u64; 8], block: &[u8; 128]) {
1305        // State as register pairs s = [ab, cd, ef, gh]. Each step covers
1306        // two rounds; the register playing the "gh" role rotates
1307        // gh -> ef -> cd -> ab, which the r index tracks.
1308        let mut s: [uint64x2_t; 4] = [
1309            vld1q_u64(h.as_ptr()),
1310            vld1q_u64(h.as_ptr().add(2)),
1311            vld1q_u64(h.as_ptr().add(4)),
1312            vld1q_u64(h.as_ptr().add(6)),
1313        ];
1314        let saved = s;
1315        let mut w: [uint64x2_t; 8] = [core::mem::zeroed(); 8];
1316        for (pair, bytes) in w.iter_mut().zip(block.as_chunks::<16>().0) {
1317            *pair = vreinterpretq_u64_u8(vrev64q_u8(vld1q_u8(bytes.as_ptr())));
1318        }
1319        for step in 0..40 {
1320            let r = 3 - (step % 4);
1321            let wk = vaddq_u64(w[step % 8], vld1q_u64(SHA512_K.as_ptr().add(2 * step)));
1322            let sum = vaddq_u64(vextq_u64::<1>(wk, wk), s[r]);
1323            let im = vsha512hq_u64(
1324                sum,
1325                vextq_u64::<1>(s[(r + 3) % 4], s[r]),
1326                vextq_u64::<1>(s[(r + 2) % 4], s[(r + 3) % 4]),
1327            );
1328            let updated = vsha512h2q_u64(im, s[(r + 2) % 4], s[(r + 1) % 4]);
1329            s[(r + 2) % 4] = vaddq_u64(s[(r + 2) % 4], im);
1330            s[r] = updated;
1331            // The last four windows feed no further schedule.
1332            if step < 32 {
1333                let i = step % 8;
1334                w[i] = vsha512su1q_u64(
1335                    vsha512su0q_u64(w[i], w[(i + 1) % 8]),
1336                    w[(i + 7) % 8],
1337                    vextq_u64::<1>(w[(i + 4) % 8], w[(i + 5) % 8]),
1338                );
1339            }
1340        }
1341        for (i, (out, kept)) in s.iter().zip(saved).enumerate() {
1342            let summed = vaddq_u64(*out, kept);
1343            core::arch::aarch64::vst1q_u64(h.as_mut_ptr().add(2 * i), summed);
1344        }
1345    }
1346}
1347
1348fn sha512(input: &[u8]) -> Vec<u8> {
1349    let h = sha512_core(
1350        input,
1351        [
1352            0x6a09e667f3bcc908,
1353            0xbb67ae8584caa73b,
1354            0x3c6ef372fe94f82b,
1355            0xa54ff53a5f1d36f1,
1356            0x510e527fade682d1,
1357            0x9b05688c2b3e6c1f,
1358            0x1f83d9abfb41bd6b,
1359            0x5be0cd19137e2179,
1360        ],
1361    );
1362    h.iter().flat_map(|w| w.to_be_bytes()).collect()
1363}
1364
1365fn sha384(input: &[u8]) -> Vec<u8> {
1366    let h = sha512_core(
1367        input,
1368        [
1369            0xcbbb9d5dc1059ed8,
1370            0x629a292a367cd507,
1371            0x9159015a3070dd17,
1372            0x152fecd8f70e5939,
1373            0x67332667ffc00b31,
1374            0x8eb44a8768581511,
1375            0xdb0c2e0d64f98fa7,
1376            0x47b5481dbefa4fa4,
1377        ],
1378    );
1379    h.iter().take(6).flat_map(|w| w.to_be_bytes()).collect()
1380}
1381
1382/// Recovers the AES-256 file key `password` opens (ISO 32000-2 §7.6.4.3.3,
1383/// Algorithm 2.A) for revisions 5 and 6: as the user password against
1384/// `/U`+`/UE`, then as the owner password against `/O`+`/OE` (whose hashes
1385/// additionally salt in the first 48 bytes of `/U`). Passwords longer than
1386/// the 127 UTF-8 bytes the algorithm defines are truncated.
1387fn aesv3_key(enc: &Dict, r: i64, password: &[u8]) -> Option<Vec<u8>> {
1388    let pw = &password[..password.len().min(127)];
1389    let u = enc.get("U").and_then(Object::as_str_bytes)?;
1390    if u.len() < 48 {
1391        return None;
1392    }
1393    // Fast paths: derive each candidate file key straight from its key
1394    // salt and let the encrypted /Perms confirm it (ISO 32000-2 Algorithm
1395    // 2.A step g validates exactly this way) — one Algorithm 2.B hash per
1396    // path instead of two. Algorithm 2.B is the whole cost of opening an
1397    // encrypted document, so this halves it for every file whose /Perms
1398    // is intact; a wrong password decrypts /Perms to garbage and falls
1399    // through to the full U/O validation below, unchanged.
1400    let perms = enc.get("Perms").and_then(Object::as_str_bytes);
1401    let o = enc.get("O").and_then(Object::as_str_bytes);
1402    if let Some(perms) = perms.filter(|p| p.len() >= 16) {
1403        if let Some(ue) = enc.get("UE").and_then(Object::as_str_bytes) {
1404            if ue.len() >= 32 {
1405                let intermediate = hash_2b(r, pw, &u[40..48], &[]);
1406                let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &ue[..32]);
1407                if file_key.len() == 32 && perms_marker_valid(&file_key, perms) {
1408                    return Some(file_key);
1409                }
1410            }
1411        }
1412        if let (Some(o), Some(oe)) = (o, enc.get("OE").and_then(Object::as_str_bytes)) {
1413            if o.len() >= 48 && oe.len() >= 32 {
1414                let intermediate = hash_2b(r, pw, &o[40..48], &u[..48]);
1415                let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &oe[..32]);
1416                if file_key.len() == 32 && perms_marker_valid(&file_key, perms) {
1417                    return Some(file_key);
1418                }
1419            }
1420        }
1421    }
1422    if let Some(ue) = enc.get("UE").and_then(Object::as_str_bytes) {
1423        if ue.len() >= 32 && hash_2b(r, pw, &u[32..40], &[])[..32] == u[..32] {
1424            let intermediate = hash_2b(r, pw, &u[40..48], &[]);
1425            let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &ue[..32]);
1426            if file_key.len() == 32 {
1427                return Some(file_key);
1428            }
1429        }
1430    }
1431    let o = o?;
1432    let oe = enc.get("OE").and_then(Object::as_str_bytes)?;
1433    if o.len() < 48 || oe.len() < 32 {
1434        return None;
1435    }
1436    if hash_2b(r, pw, &o[32..40], &u[..48])[..32] != o[..32] {
1437        return None;
1438    }
1439    let intermediate = hash_2b(r, pw, &o[40..48], &u[..48]);
1440    let file_key = aes_cbc_decrypt_blocks(&intermediate, &[0u8; 16], &oe[..32]);
1441    (file_key.len() == 32).then_some(file_key)
1442}
1443
1444/// Whether `file_key` decrypts `/Perms` to its mandated marker: bytes 9-11
1445/// spell "adb" (ISO 32000-2 Algorithm 2.A step g; AES-256 ECB, no IV).
1446fn perms_marker_valid(file_key: &[u8], perms: &[u8]) -> bool {
1447    let rks = aes_expand_key(file_key);
1448    let inv_sbox = aes_inv_sbox();
1449    let mut block = [0u8; 16];
1450    block.copy_from_slice(&perms[..16]);
1451    aes_decrypt_block(&mut block, &rks, &inv_sbox);
1452    &block[9..12] == b"adb"
1453}
1454
1455/// The revision-6 password hash (ISO 32000-2, Algorithm 2.B); a plain SHA-256
1456/// for revision 5.
1457fn hash_2b(r: i64, password: &[u8], salt: &[u8], udata: &[u8]) -> Vec<u8> {
1458    let mut seed = Vec::with_capacity(password.len() + salt.len() + udata.len());
1459    seed.extend_from_slice(password);
1460    seed.extend_from_slice(salt);
1461    seed.extend_from_slice(udata);
1462    let mut k = sha256(&seed).to_vec();
1463    if r < 6 {
1464        return k; // revision 5: a single SHA-256
1465    }
1466    let mut round = 0usize;
1467    let mut k1 = Vec::with_capacity(64 * (password.len() + 64 + udata.len()));
1468    loop {
1469        k1.clear();
1470        for _ in 0..64 {
1471            k1.extend_from_slice(password);
1472            k1.extend_from_slice(&k);
1473            k1.extend_from_slice(udata);
1474        }
1475        let e = aes_cbc_encrypt_blocks(&k[..16], &k[16..32], &k1);
1476        let modulus = e[..16].iter().map(|&b| u32::from(b)).sum::<u32>() % 3;
1477        k = match modulus {
1478            0 => sha256(&e).to_vec(),
1479            1 => sha384(&e),
1480            _ => sha512(&e),
1481        };
1482        round += 1;
1483        if round >= 64 && usize::from(*e.last().unwrap()) <= round - 32 {
1484            break;
1485        }
1486    }
1487    k.truncate(32);
1488    k
1489}
1490
1491/// Freshly generated key material for a revision-6 `/Encrypt` dictionary:
1492/// the random file key plus `/U`, `/UE`, `/O`, `/OE` and `/Perms` derived
1493/// from it (ISO 32000-2 §7.6.4.3.4-3.6, Algorithms 8, 9 and 10).
1494pub(crate) struct KeyMaterial {
1495    file_key: [u8; 32],
1496    u: Vec<u8>,
1497    ue: Vec<u8>,
1498    o: Vec<u8>,
1499    oe: Vec<u8>,
1500    perms: Vec<u8>,
1501}
1502
1503/// Builds a [`KeyMaterial`] for `user_pw`/`owner_pw` at permission bits `p`
1504/// (ISO 32000-2 §7.6.4.2, Table 22), recording whether metadata is left
1505/// unencrypted. `rng` fills the random bytes Algorithms 8-10 call for: the
1506/// 32-byte file key, the user and owner validation/key salts, and the
1507/// 4-byte `/Perms` pad. Passwords longer than the 127 UTF-8 bytes the
1508/// algorithm defines are truncated, matching the validation side.
1509pub(crate) fn r6_key_material(
1510    user_pw: &[u8],
1511    owner_pw: &[u8],
1512    p: i32,
1513    encrypt_metadata: bool,
1514    rng: &mut dyn FnMut(&mut [u8]),
1515) -> KeyMaterial {
1516    let user_pw = &user_pw[..user_pw.len().min(127)];
1517    let owner_pw = &owner_pw[..owner_pw.len().min(127)];
1518
1519    let mut file_key = [0u8; 32];
1520    rng(&mut file_key);
1521
1522    // Algorithm 8: /U and /UE from a 16-byte random pad, the first 8
1523    // bytes the validation salt and the last 8 the key salt.
1524    let mut user_salt = [0u8; 16];
1525    rng(&mut user_salt);
1526    let mut u = hash_2b(6, user_pw, &user_salt[..8], &[]);
1527    u.extend_from_slice(&user_salt);
1528    let user_intermediate = hash_2b(6, user_pw, &user_salt[8..], &[]);
1529    let ue = aes_cbc_encrypt_blocks(&user_intermediate, &[0u8; 16], &file_key);
1530
1531    // Algorithm 9: /O and /OE, the same derivation but hashing in the
1532    // first 48 bytes of /U as well.
1533    let mut owner_salt = [0u8; 16];
1534    rng(&mut owner_salt);
1535    let mut o = hash_2b(6, owner_pw, &owner_salt[..8], &u[..48]);
1536    o.extend_from_slice(&owner_salt);
1537    let owner_intermediate = hash_2b(6, owner_pw, &owner_salt[8..], &u[..48]);
1538    let oe = aes_cbc_encrypt_blocks(&owner_intermediate, &[0u8; 16], &file_key);
1539
1540    // Algorithm 10: /Perms, the file key applied to a single AES-256 ECB
1541    // block (not CBC).
1542    let mut perms_block = [0u8; 16];
1543    perms_block[..4].copy_from_slice(&p.to_le_bytes());
1544    perms_block[4..8].copy_from_slice(&[0xff; 4]);
1545    perms_block[8] = if encrypt_metadata { b'T' } else { b'F' };
1546    perms_block[9..12].copy_from_slice(b"adb");
1547    rng(&mut perms_block[12..]);
1548    aes_encrypt_block(&mut perms_block, &aes_expand_key(&file_key));
1549
1550    KeyMaterial {
1551        file_key,
1552        u,
1553        ue,
1554        o,
1555        oe,
1556        perms: perms_block.to_vec(),
1557    }
1558}
1559
1560/// Fills `buf` with operating-system random bytes. Not available on
1561/// `wasm32-unknown-unknown`, where `getrandom` is not a dependency at all;
1562/// use [`Encryptor::aes256_with_rng`] there with a caller-supplied source.
1563#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1564pub(crate) fn fill_os_random(buf: &mut [u8]) {
1565    getrandom::fill(buf).expect("OS random number generator unavailable");
1566}
1567
1568/// The eight standard-handler permission bits a document opened under the
1569/// user password (rather than the owner password) is granted (ISO 32000-2
1570/// §7.6.4.2, Table 22). Bits the standard reserves are not represented
1571/// here; `p_value` fixes them to the values it mandates.
1572#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1573pub struct Permissions {
1574    /// Print the document, at full quality only when `print_hires` is also
1575    /// set (Table 22 bit 3).
1576    pub print: bool,
1577    /// Modify the document's contents, beyond what `annotate`,
1578    /// `fill_forms` and `assemble` separately cover (Table 22 bit 4).
1579    pub modify: bool,
1580    /// Copy or otherwise extract text and graphics (Table 22 bit 5).
1581    pub copy: bool,
1582    /// Add or modify text annotations and fill form fields (Table 22 bit
1583    /// 6).
1584    pub annotate: bool,
1585    /// Fill in existing form fields even without `annotate` (Table 22 bit
1586    /// 9).
1587    pub fill_forms: bool,
1588    /// Extract text and graphics for accessibility (Table 22 bit 10).
1589    pub accessibility: bool,
1590    /// Assemble the document: insert, rotate or delete pages, create
1591    /// bookmarks or thumbnails (Table 22 bit 11).
1592    pub assemble: bool,
1593    /// Print at full quality; without it, printing (if `print` allows it)
1594    /// may be limited to a low-resolution rasterization (Table 22 bit 12).
1595    pub print_hires: bool,
1596}
1597
1598/// The permission names [`Permissions::from_names`] accepts, in the order
1599/// named in `--allow`/`allow` help text and error messages: `print`,
1600/// `modify`, `copy`, `annotate`, `fill-forms`, `accessibility`, `assemble`,
1601/// `print-hires`.
1602pub const PERMISSION_NAMES: [&str; 8] = [
1603    "print",
1604    "modify",
1605    "copy",
1606    "annotate",
1607    "fill-forms",
1608    "accessibility",
1609    "assemble",
1610    "print-hires",
1611];
1612
1613impl Permissions {
1614    /// Every permission granted.
1615    pub fn all() -> Permissions {
1616        Permissions {
1617            print: true,
1618            modify: true,
1619            copy: true,
1620            annotate: true,
1621            fill_forms: true,
1622            accessibility: true,
1623            assemble: true,
1624            print_hires: true,
1625        }
1626    }
1627
1628    /// Every permission named in `names` granted, everything else denied.
1629    /// The first name not in [`PERMISSION_NAMES`] comes back as `Err`,
1630    /// carrying that name alone: the CLI and the Python bindings each wrap
1631    /// it in their own error type and message text.
1632    pub fn from_names<'a>(names: impl IntoIterator<Item = &'a str>) -> Result<Permissions, String> {
1633        let mut permissions = Permissions {
1634            print: false,
1635            modify: false,
1636            copy: false,
1637            annotate: false,
1638            fill_forms: false,
1639            accessibility: false,
1640            assemble: false,
1641            print_hires: false,
1642        };
1643        for name in names {
1644            match name {
1645                "print" => permissions.print = true,
1646                "modify" => permissions.modify = true,
1647                "copy" => permissions.copy = true,
1648                "annotate" => permissions.annotate = true,
1649                "fill-forms" => permissions.fill_forms = true,
1650                "accessibility" => permissions.accessibility = true,
1651                "assemble" => permissions.assemble = true,
1652                "print-hires" => permissions.print_hires = true,
1653                other => return Err(other.to_string()),
1654            }
1655        }
1656        Ok(permissions)
1657    }
1658
1659    /// The `/P` integer this set of permissions encodes (ISO 32000-2
1660    /// §7.6.4.2, Table 22): bits 1 and 2 are reserved and always cleared;
1661    /// bits 3, 4, 5, 6, 9, 10, 11 and 12 carry `print`, `modify`, `copy`,
1662    /// `annotate`, `fill_forms`, `accessibility`, `assemble` and
1663    /// `print_hires` in that order; every other bit is reserved and always
1664    /// set. Granting everything therefore yields `-4`.
1665    pub(crate) fn p_value(&self) -> i32 {
1666        let flags: [(u32, bool); 8] = [
1667            (3, self.print),
1668            (4, self.modify),
1669            (5, self.copy),
1670            (6, self.annotate),
1671            (9, self.fill_forms),
1672            (10, self.accessibility),
1673            (11, self.assemble),
1674            (12, self.print_hires),
1675        ];
1676        let mut bits: u32 = !0b11; // every bit set, then clear the two reserved low bits
1677        for (bit, granted) in flags {
1678            if !granted {
1679                bits &= !(1u32 << (bit - 1));
1680            }
1681        }
1682        bits as i32
1683    }
1684}
1685
1686impl Default for Permissions {
1687    /// Every permission granted, same as [`Permissions::all`].
1688    fn default() -> Permissions {
1689        Permissions::all()
1690    }
1691}
1692
1693/// Builds the complete AES-256, revision 6 `/Encrypt` dictionary
1694/// (ISO 32000-2 §7.6.4.2) from generated key material: `/Filter /Standard`,
1695/// `/V 5`, `/R 6`, `/Length 256`, a `/CF` naming the standard crypt filter
1696/// as `/AESV3` with a 32-byte key, `/StmF` and `/StrF` both pointing at it,
1697/// and the `/U`, `/UE`, `/O`, `/OE`, `/P` and `/Perms` entries.
1698fn aes256_encrypt_dict(material: &KeyMaterial, p: i32) -> Dict {
1699    let mut std_cf = Dict::new();
1700    std_cf.insert(
1701        Name("CFM".to_string()),
1702        Object::Name(Name("AESV3".to_string())),
1703    );
1704    std_cf.insert(Name("Length".to_string()), Object::Int(32));
1705    let mut cf = Dict::new();
1706    cf.insert(Name("StdCF".to_string()), Object::Dict(std_cf));
1707
1708    let mut dict = Dict::new();
1709    dict.insert(
1710        Name("Filter".to_string()),
1711        Object::Name(Name("Standard".to_string())),
1712    );
1713    dict.insert(Name("V".to_string()), Object::Int(5));
1714    dict.insert(Name("R".to_string()), Object::Int(6));
1715    dict.insert(Name("Length".to_string()), Object::Int(256));
1716    dict.insert(Name("CF".to_string()), Object::Dict(cf));
1717    dict.insert(
1718        Name("StmF".to_string()),
1719        Object::Name(Name("StdCF".to_string())),
1720    );
1721    dict.insert(
1722        Name("StrF".to_string()),
1723        Object::Name(Name("StdCF".to_string())),
1724    );
1725    dict.insert(Name("P".to_string()), Object::Int(i64::from(p)));
1726    dict.insert(Name("U".to_string()), Object::String(material.u.clone()));
1727    dict.insert(Name("UE".to_string()), Object::String(material.ue.clone()));
1728    dict.insert(Name("O".to_string()), Object::String(material.o.clone()));
1729    dict.insert(Name("OE".to_string()), Object::String(material.oe.clone()));
1730    dict.insert(
1731        Name("Perms".to_string()),
1732        Object::String(material.perms.clone()),
1733    );
1734    dict
1735}
1736
1737/// A configured Standard-handler encryptor: AES-256, revision 6, the write
1738/// side of [`Decryptor`]. Built by [`Encryptor::aes256`] or
1739/// [`Encryptor::aes256_with_rng`], which also return the complete
1740/// `/Encrypt` dictionary for the trailer.
1741#[allow(
1742    clippy::type_complexity,
1743    reason = "Box<dyn FnMut(&mut [u8]) + Send> names the one random-byte source the write path takes; a type alias would only rename it"
1744)]
1745pub struct Encryptor {
1746    file_key: [u8; 32],
1747    rng: Box<dyn FnMut(&mut [u8]) + Send>,
1748}
1749
1750impl Encryptor {
1751    /// AES-256 (`/V` 5, `/R` 6) key material from the operating system's
1752    /// random source (ISO 32000-2 §7.6.4.3). Returns the encryptor plus the
1753    /// complete `/Encrypt` dictionary to place in the trailer. Passwords
1754    /// encode as UTF-8 and are truncated to 127 bytes, matching the reader.
1755    ///
1756    /// Not available on `wasm32-unknown-unknown`: there is no operating
1757    /// system random source to draw from there. Use
1758    /// [`Encryptor::aes256_with_rng`] instead, with caller-supplied
1759    /// randomness (for example from the host's `crypto.getRandomValues`).
1760    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1761    pub fn aes256(
1762        user_password: &str,
1763        owner_password: &str,
1764        permissions: Permissions,
1765    ) -> (Encryptor, Dict) {
1766        Encryptor::aes256_with_rng(
1767            user_password,
1768            owner_password,
1769            permissions,
1770            Box::new(fill_os_random),
1771        )
1772    }
1773
1774    /// [`Encryptor::aes256`] with a caller-supplied source of random bytes
1775    /// in place of the operating system's, for reproducible tests.
1776    #[allow(clippy::type_complexity, reason = "see Encryptor's rng field")]
1777    pub fn aes256_with_rng(
1778        user_password: &str,
1779        owner_password: &str,
1780        permissions: Permissions,
1781        rng: Box<dyn FnMut(&mut [u8]) + Send>,
1782    ) -> (Encryptor, Dict) {
1783        let mut rng = rng;
1784        let p = permissions.p_value();
1785        let material = r6_key_material(
1786            user_password.as_bytes(),
1787            owner_password.as_bytes(),
1788            p,
1789            true,
1790            &mut *rng,
1791        );
1792        let dict = aes256_encrypt_dict(&material, p);
1793        let encryptor = Encryptor {
1794            file_key: material.file_key,
1795            rng,
1796        };
1797        (encryptor, dict)
1798    }
1799
1800    /// Encrypts one indirect object's strings and stream data in place, the
1801    /// exact inverse of [`Decryptor::decrypt_object`]: every string and
1802    /// stream body gets a fresh random IV and PKCS#7 padding before
1803    /// AES-256-CBC under the file key. AESV3 applies the same key to every
1804    /// object, so `num` and `gen` are accepted and ignored here, purely for
1805    /// signature symmetry with `decrypt_object`; a future per-object write
1806    /// cipher (an AESV2 write path, say) would need to start deriving its
1807    /// key from them instead of discarding them.
1808    pub fn encrypt_object(&mut self, obj: &mut Object, num: u32, gen: u16) {
1809        let _ = (num, gen);
1810        encrypt_in_place(obj, &self.file_key, &mut *self.rng);
1811    }
1812}
1813
1814/// Recursively encrypts every string and stream body reachable from `obj`
1815/// with the file `key`, mirroring [`decrypt_in_place`]'s walk exactly:
1816/// `String`, `Array`, `Dict` values and `Stream` (dict values plus data);
1817/// every other object type is left untouched.
1818fn encrypt_in_place(obj: &mut Object, key: &[u8], rng: &mut dyn FnMut(&mut [u8])) {
1819    match obj {
1820        Object::String(bytes) => *bytes = aes_cbc_encrypt(key, rng, bytes),
1821        Object::Array(items) => {
1822            for it in items {
1823                encrypt_in_place(it, key, &mut *rng);
1824            }
1825        }
1826        Object::Dict(dict) => {
1827            for v in dict.values_mut() {
1828                encrypt_in_place(v, key, &mut *rng);
1829            }
1830        }
1831        Object::Stream(stream) => {
1832            for v in stream.dict.values_mut() {
1833                encrypt_in_place(v, key, &mut *rng);
1834            }
1835            // A stream that names the Identity crypt filter says its data
1836            // is stored in the clear (ISO 32000-1 §7.4.10); keep it so, or
1837            // no reader could get it back.
1838            if stream_crypt_filter(&stream.dict).as_deref() == Some("Identity") {
1839                return;
1840            }
1841            stream.data = aes_cbc_encrypt(key, rng, &stream.data);
1842        }
1843        _ => {}
1844    }
1845}
1846
1847/// Per-round left-rotation amounts (RFC 1321).
1848#[rustfmt::skip]
1849const MD5_S: [u32; 64] = [
1850    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
1851    5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
1852    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
1853    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
1854];
1855
1856/// Per-round additive constants `floor(2^32 * abs(sin(i + 1)))` (RFC 1321).
1857#[rustfmt::skip]
1858const MD5_K: [u32; 64] = [
1859    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
1860    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
1861    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
1862    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
1863    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
1864    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
1865    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
1866    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
1867];
1868
1869/// One-shot MD5 (RFC 1321). Sufficient for the small key-derivation inputs; not
1870/// a streaming API.
1871fn md5(input: &[u8]) -> [u8; 16] {
1872    let (mut a0, mut b0, mut c0, mut d0) = (
1873        0x6745_2301u32,
1874        0xefcd_ab89u32,
1875        0x98ba_dcfeu32,
1876        0x1032_5476u32,
1877    );
1878
1879    let mut msg = input.to_vec();
1880    let bitlen = (input.len() as u64).wrapping_mul(8);
1881    msg.push(0x80);
1882    while msg.len() % 64 != 56 {
1883        msg.push(0);
1884    }
1885    msg.extend_from_slice(&bitlen.to_le_bytes());
1886
1887    for chunk in msg.as_chunks::<64>().0 {
1888        let mut m = [0u32; 16];
1889        for (word, bytes) in m.iter_mut().zip(chunk.as_chunks::<4>().0) {
1890            *word = u32::from_le_bytes(*bytes);
1891        }
1892        let (mut a, mut b, mut c, mut d) = (a0, b0, c0, d0);
1893        for i in 0..64 {
1894            let (f, g) = match i {
1895                0..=15 => ((b & c) | (!b & d), i),
1896                16..=31 => ((d & b) | (!d & c), (5 * i + 1) % 16),
1897                32..=47 => (b ^ c ^ d, (3 * i + 5) % 16),
1898                _ => (c ^ (b | !d), (7 * i) % 16),
1899            };
1900            let f = f.wrapping_add(a).wrapping_add(MD5_K[i]).wrapping_add(m[g]);
1901            a = d;
1902            d = c;
1903            c = b;
1904            b = b.wrapping_add(f.rotate_left(MD5_S[i]));
1905        }
1906        a0 = a0.wrapping_add(a);
1907        b0 = b0.wrapping_add(b);
1908        c0 = c0.wrapping_add(c);
1909        d0 = d0.wrapping_add(d);
1910    }
1911
1912    let mut out = [0u8; 16];
1913    out[0..4].copy_from_slice(&a0.to_le_bytes());
1914    out[4..8].copy_from_slice(&b0.to_le_bytes());
1915    out[8..12].copy_from_slice(&c0.to_le_bytes());
1916    out[12..16].copy_from_slice(&d0.to_le_bytes());
1917    out
1918}
1919
1920#[cfg(test)]
1921mod tests {
1922    use super::*;
1923    use crate::object::Name;
1924
1925    fn hex(bytes: &[u8]) -> String {
1926        bytes.iter().map(|b| format!("{b:02x}")).collect()
1927    }
1928
1929    #[test]
1930    fn md5_known_vectors() {
1931        assert_eq!(hex(&md5(b"")), "d41d8cd98f00b204e9800998ecf8427e");
1932        assert_eq!(hex(&md5(b"abc")), "900150983cd24fb0d6963f7d28e17f72");
1933        assert_eq!(
1934            hex(&md5(b"The quick brown fox jumps over the lazy dog")),
1935            "9e107d9d372bb6826bd81d3542a419d6"
1936        );
1937    }
1938
1939    #[test]
1940    fn md5_spans_block_boundary() {
1941        // 56 bytes forces a second padded block.
1942        let input = [b'a'; 56];
1943        assert_eq!(hex(&md5(&input)), "3b0c8ac703f828b04c6c197006d17218");
1944    }
1945
1946    /// FIPS-197 Appendix C block vectors, both key sizes and both
1947    /// directions, held against the dispatching entry (the hardware path
1948    /// wherever this test runs on a CPU with AES instructions) AND the
1949    /// portable rounds directly, so neither path can drift.
1950    #[test]
1951    fn aes_block_known_vectors() {
1952        let plain: [u8; 16] = core::array::from_fn(|i| (i as u8) * 0x11);
1953        let key128: [u8; 16] = core::array::from_fn(|i| i as u8);
1954        let key256: [u8; 32] = core::array::from_fn(|i| i as u8);
1955
1956        type Encrypt = fn(&mut [u8; 16], &[[u8; 16]]);
1957        type Decrypt = fn(&mut [u8; 16], &[[u8; 16]], &[u8; 256]);
1958        let paths: [(Encrypt, Decrypt); 2] = [
1959            (aes_encrypt_block, aes_decrypt_block),
1960            (aes_encrypt_block_soft, aes_decrypt_block_soft),
1961        ];
1962        for (encrypt, decrypt) in paths {
1963            let mut s = plain;
1964            encrypt(&mut s, &aes_expand_key(&key128));
1965            assert_eq!(hex(&s), "69c4e0d86a7b0430d8cdb78070b4c55a");
1966            decrypt(&mut s, &aes_expand_key(&key128), &aes_inv_sbox());
1967            assert_eq!(s, plain);
1968
1969            let mut s = plain;
1970            encrypt(&mut s, &aes_expand_key(&key256));
1971            assert_eq!(hex(&s), "8ea2b7ca516745bfeafc49904b496089");
1972            decrypt(&mut s, &aes_expand_key(&key256), &aes_inv_sbox());
1973            assert_eq!(s, plain);
1974        }
1975    }
1976
1977    /// Multi-block CBC round-trips through the block paths, chaining
1978    /// included: what the R6 hash encrypts, decryption must invert.
1979    #[test]
1980    fn aes_cbc_round_trips_multiple_blocks() {
1981        let key: [u8; 16] = core::array::from_fn(|i| (i as u8).wrapping_mul(7));
1982        let iv: [u8; 16] = core::array::from_fn(|i| 0xa5 ^ (i as u8));
1983        let data: Vec<u8> = (0..64u8).collect();
1984        let ct = aes_cbc_encrypt_blocks(&key, &iv, &data);
1985        assert_eq!(ct.len(), data.len());
1986        assert_ne!(ct, data);
1987        assert_eq!(aes_cbc_decrypt_blocks(&key, &iv, &ct), data);
1988    }
1989
1990    /// NIST FIPS 180 vectors for the SHA-2 family, spanning the empty
1991    /// input, one block, and inputs long enough to cross block
1992    /// boundaries — the contract the hardware compressions must match.
1993    #[test]
1994    fn sha2_known_vectors() {
1995        assert_eq!(
1996            hex(&sha256(b"")),
1997            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
1998        );
1999        assert_eq!(
2000            hex(&sha256(b"abc")),
2001            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
2002        );
2003        assert_eq!(
2004            hex(&sha256(&[b'a'; 200])),
2005            "c2a908d98f5df987ade41b5fce213067efbcc21ef2240212a41e54b5e7c28ae5"
2006        );
2007        assert_eq!(
2008            hex(&sha384(b"abc")),
2009            "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\
2010             8086072ba1e7cc2358baeca134c825a7"
2011        );
2012        assert_eq!(
2013            hex(&sha512(b"abc")),
2014            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a\
2015             2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
2016        );
2017        assert_eq!(
2018            hex(&sha512(&[b'a'; 300])),
2019            "a6a77010dd9696c23831e6549de51724df332c2075039b75fcfe6c2e6de42fbd\
2020             3c80ed4073267e00c8c320712c3cdd9d65a96f90a3fe4a58a6b70a103be08e83"
2021        );
2022    }
2023
2024    /// The portable compressions must evolve state identically to the
2025    /// dispatching entries (the hardware path wherever this runs on a CPU
2026    /// with SHA instructions), block by block.
2027    #[test]
2028    fn sha2_soft_compressions_match_the_dispatch() {
2029        let block64: [u8; 64] = core::array::from_fn(|i| (i as u8).wrapping_mul(31));
2030        let mut via_dispatch = [0x6a09e667u32; 8];
2031        let mut via_soft = via_dispatch;
2032        sha256_compress(&mut via_dispatch, &block64);
2033        sha256_compress_soft(&mut via_soft, &block64);
2034        assert_eq!(via_dispatch, via_soft);
2035
2036        let block128: [u8; 128] = core::array::from_fn(|i| (i as u8).wrapping_mul(29));
2037        let mut via_dispatch = [0x6a09e667f3bcc908u64; 8];
2038        let mut via_soft = via_dispatch;
2039        sha512_compress(&mut via_dispatch, &block128);
2040        sha512_compress_soft(&mut via_soft, &block128);
2041        assert_eq!(via_dispatch, via_soft);
2042    }
2043
2044    /// The incremental hasher must match the one-shot digest whatever the
2045    /// slice boundaries: odd sizes, block-straddling feeds, empty updates.
2046    #[test]
2047    fn incremental_sha256_matches_the_one_shot() {
2048        let input: Vec<u8> = (0..300u16).map(|i| (i % 251) as u8).collect();
2049        for splits in [
2050            vec![0, 1, 3, 7, 60, 63, 64, 65, 37],
2051            vec![300],
2052            vec![128, 128, 44],
2053            vec![55, 9, 236],
2054        ] {
2055            let mut hasher = Sha256::new();
2056            let mut fed = 0usize;
2057            for len in splits {
2058                hasher.update(&input[fed..fed + len]);
2059                fed += len;
2060            }
2061            hasher.update(&input[fed..]);
2062            assert_eq!(hasher.finalize(), sha256(&input));
2063        }
2064        assert_eq!(Sha256::new().finalize(), sha256(b""));
2065        assert_eq!(Sha256::default().finalize(), sha256(b""));
2066    }
2067
2068    #[test]
2069    fn rc4_known_vector() {
2070        // Classic RC4 test vector: key "Key", plaintext "Plaintext".
2071        let ct = rc4(b"Key", b"Plaintext");
2072        assert_eq!(hex(&ct), "bbf316e8d940af0ad3");
2073        // Symmetric: decrypting the ciphertext returns the plaintext.
2074        assert_eq!(rc4(b"Key", &ct), b"Plaintext");
2075    }
2076
2077    // --- End-to-end fixture: build a V2/R3 (128-bit RC4) file encrypted under
2078    // caller-chosen user and owner passwords, then confirm the loader
2079    // decrypts it — transparently for the empty user password, and through
2080    // the password APIs for real ones. ---
2081
2082    const N: usize = 16; // 128-bit key
2083    const P: i32 = -44;
2084    const ID0: &[u8] = b"0123456789abcdef";
2085
2086    /// `/O` for the given owner and user passwords (Algorithm 3, R3).
2087    fn owner_entry(owner_pw: &[u8], user_pw: &[u8]) -> Vec<u8> {
2088        owner_entry_with(N, owner_pw, user_pw)
2089    }
2090
2091    /// [`owner_entry`] for an `n`-byte key.
2092    fn owner_entry_with(n: usize, owner_pw: &[u8], user_pw: &[u8]) -> Vec<u8> {
2093        let mut d = md5(&pad_password(owner_pw));
2094        for _ in 0..50 {
2095            d = md5(&d[..n]);
2096        }
2097        let rc4key = d[..n].to_vec();
2098        let mut o = rc4(&rc4key, &pad_password(user_pw));
2099        for i in 1u8..=19 {
2100            let k: Vec<u8> = rc4key.iter().map(|b| b ^ i).collect();
2101            o = rc4(&k, &o);
2102        }
2103        o
2104    }
2105
2106    /// File key from `/O` for the given user password (Algorithm 2, R3).
2107    fn file_key(o: &[u8], user_pw: &[u8]) -> Vec<u8> {
2108        file_key_with(N, o, user_pw)
2109    }
2110
2111    /// [`file_key`] for an `n`-byte key.
2112    fn file_key_with(n: usize, o: &[u8], user_pw: &[u8]) -> Vec<u8> {
2113        let mut input = Vec::new();
2114        input.extend_from_slice(&pad_password(user_pw));
2115        input.extend_from_slice(o);
2116        input.extend_from_slice(&(P as u32).to_le_bytes());
2117        input.extend_from_slice(ID0);
2118        let mut d = md5(&input);
2119        for _ in 0..50 {
2120            d = md5(&d[..n]);
2121        }
2122        d[..n].to_vec()
2123    }
2124
2125    /// The `/Msg` string of dictionary object `num`, as loaded.
2126    fn msg_of(doc: &crate::Document, num: u32) -> Vec<u8> {
2127        use crate::object::ObjRef;
2128        let obj = doc.get(ObjRef { num, gen: 0 }).unwrap();
2129        obj.as_dict()
2130            .unwrap()
2131            .get("Msg")
2132            .unwrap()
2133            .as_str_bytes()
2134            .unwrap()
2135            .to_vec()
2136    }
2137
2138    /// The decoded data of stream object `num`.
2139    fn body_of(doc: &crate::Document, num: u32) -> Vec<u8> {
2140        use crate::object::ObjRef;
2141        let obj = doc.get(ObjRef { num, gen: 0 }).unwrap();
2142        doc.stream_data(obj.as_stream().unwrap()).unwrap()
2143    }
2144
2145    /// `/U` for the given file key (Algorithm 5, R3).
2146    fn user_entry(key: &[u8]) -> Vec<u8> {
2147        let mut input = Vec::new();
2148        input.extend_from_slice(&PAD);
2149        input.extend_from_slice(ID0);
2150        let mut x = md5(&input).to_vec();
2151        x = rc4(key, &x);
2152        for i in 1u8..=19 {
2153            let k: Vec<u8> = key.iter().map(|b| b ^ i).collect();
2154            x = rc4(&k, &x);
2155        }
2156        x.resize(32, 0); // trailing padding is arbitrary
2157        x
2158    }
2159
2160    fn obj_key(key: &[u8], num: u32, gen: u16) -> Vec<u8> {
2161        let mut input = key.to_vec();
2162        input.extend_from_slice(&num.to_le_bytes()[..3]);
2163        input.extend_from_slice(&gen.to_le_bytes()[..2]);
2164        md5(&input)[..(key.len() + 5).min(16)].to_vec()
2165    }
2166
2167    fn hexstr(b: &[u8]) -> String {
2168        let mut s = String::from("<");
2169        for x in b {
2170            s.push_str(&format!("{x:02x}"));
2171        }
2172        s.push('>');
2173        s
2174    }
2175
2176    fn encrypted_fixture(u_override: Option<Vec<u8>>) -> Vec<u8> {
2177        encrypted_fixture_with(b"", b"", u_override)
2178    }
2179
2180    fn encrypted_fixture_with(
2181        user_pw: &[u8],
2182        owner_pw: &[u8],
2183        u_override: Option<Vec<u8>>,
2184    ) -> Vec<u8> {
2185        use pdfboss_testkit::PdfBuilder;
2186        let o = owner_entry(owner_pw, user_pw);
2187        let key = file_key(&o, user_pw);
2188        let u = u_override.unwrap_or_else(|| user_entry(&key));
2189
2190        let msg = rc4(&obj_key(&key, 3, 0), b"Top secret message");
2191        let stream = rc4(&obj_key(&key, 4, 0), b"decrypted stream body");
2192
2193        let mut b = PdfBuilder::new().version(1, 4);
2194        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2195        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2196        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2197        b.stream(4, "", &stream);
2198        b.object(
2199            9,
2200            &format!(
2201                "<< /Filter /Standard /V 2 /R 3 /Length 128 /P {P} /O {} /U {} >>",
2202                hexstr(&o),
2203                hexstr(&u)
2204            ),
2205        );
2206        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2207        b.trailer_extra(&trailer).build(1)
2208    }
2209
2210    // Covers ISO 32000-1 §7.6.2, §7.6.3.2 and §7.6.3.3.
2211    #[test]
2212    fn document_load_decrypts_standard_rc4() {
2213        use crate::object::ObjRef;
2214        use crate::Document;
2215
2216        let doc = Document::load(encrypted_fixture(None)).expect("empty password opens the file");
2217
2218        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2219        let msg = obj3
2220            .as_dict()
2221            .unwrap()
2222            .get("Msg")
2223            .unwrap()
2224            .as_str_bytes()
2225            .unwrap();
2226        assert_eq!(msg, b"Top secret message", "string decrypted");
2227
2228        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2229        let data = doc.stream_data(obj4.as_stream().unwrap()).unwrap();
2230        assert_eq!(data, b"decrypted stream body", "stream decrypted");
2231    }
2232
2233    // Covers ISO 32000-1 §7.6.3.4.
2234    #[test]
2235    fn document_load_rejects_when_password_does_not_verify() {
2236        use crate::error::Error;
2237        use crate::Document;
2238
2239        // A `/U` that will not verify under the empty password stands in for a
2240        // real password-protected file: the loader must decline, not decrypt.
2241        let bad_u = vec![0u8; 32];
2242        let err = Document::load(encrypted_fixture(Some(bad_u)));
2243        assert!(matches!(err, Err(Error::Encrypted)));
2244    }
2245
2246    // Covers ISO 32000-1 §7.6.3, §7.6.3.3 and §7.6.3.4.
2247    #[test]
2248    fn real_user_password_opens_an_rc4_file() {
2249        use crate::error::Error;
2250        use crate::object::ObjRef;
2251        use crate::Document;
2252
2253        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2254        assert!(
2255            matches!(Document::load(bytes.clone()), Err(Error::Encrypted)),
2256            "without the password the file stays closed"
2257        );
2258        let doc = Document::load_with_password(bytes, "hunter2").expect("user password opens");
2259        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2260        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2261        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2262    }
2263
2264    // Covers ISO 32000-1 §7.6.3.4.
2265    #[test]
2266    fn owner_password_opens_an_rc4_file() {
2267        use crate::object::ObjRef;
2268        use crate::Document;
2269
2270        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2271        let doc =
2272            Document::load_with_password(bytes, "owner-secret").expect("owner password opens");
2273        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2274        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2275        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2276    }
2277
2278    // Covers ISO 32000-1 §7.6.3.4.
2279    #[test]
2280    fn wrong_password_stays_encrypted() {
2281        use crate::error::Error;
2282        use crate::Document;
2283
2284        let bytes = encrypted_fixture_with(b"hunter2", b"owner-secret", None);
2285        let err = Document::load_with_password(bytes, "letmein");
2286        assert!(matches!(err, Err(Error::Encrypted)));
2287    }
2288
2289    // Covers ISO 32000-1 §7.6.3 and §7.6.3.4.
2290    #[test]
2291    fn real_passwords_open_an_aes256_r6_file() {
2292        use crate::error::Error;
2293        use crate::object::ObjRef;
2294        use crate::Document;
2295
2296        let bytes = encrypted_fixture_aesv3_with(6, "pässword".as_bytes(), b"owner-secret");
2297        assert!(
2298            matches!(Document::load(bytes.clone()), Err(Error::Encrypted)),
2299            "without the password the file stays closed"
2300        );
2301        for pw in ["pässword", "owner-secret"] {
2302            let doc = Document::load_with_password(bytes.clone(), pw)
2303                .unwrap_or_else(|_| panic!("{pw:?} opens the file"));
2304            let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2305            let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2306            assert_eq!(msg.as_str_bytes().unwrap(), b"AES-256 secret");
2307        }
2308        assert!(matches!(
2309            Document::load_with_password(bytes, "letmein"),
2310            Err(Error::Encrypted)
2311        ));
2312    }
2313
2314    // Covers ISO 32000-1 §7.6.3.
2315    #[test]
2316    fn empty_password_files_still_open_through_the_password_api() {
2317        use crate::object::ObjRef;
2318        use crate::Document;
2319
2320        // Passing a password to an empty-password file must not break it:
2321        // the empty user password still verifies... only if the caller's
2322        // password IS empty; a random one is simply wrong for this file.
2323        let doc = Document::load_with_password(encrypted_fixture(None), "")
2324            .expect("the empty password opens through the password API too");
2325        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2326        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2327        assert_eq!(msg.as_str_bytes().unwrap(), b"Top secret message");
2328    }
2329
2330    // Covers ISO 32000-1 §7.6.3.2.
2331    #[test]
2332    fn unsupported_handler_is_declined() {
2333        // A future/unknown handler version is declined so the caller reports the
2334        // file as encrypted-and-unsupported.
2335        let mut enc = Dict::new();
2336        enc.insert(Name("Filter".into()), Object::Name(Name("Standard".into())));
2337        enc.insert(Name("V".into()), Object::Int(6));
2338        enc.insert(Name("R".into()), Object::Int(7));
2339        enc.insert(Name("O".into()), Object::String(vec![0; 48]));
2340        enc.insert(Name("U".into()), Object::String(vec![0; 48]));
2341        enc.insert(Name("P".into()), Object::Int(-4));
2342        assert!(Decryptor::from_standard(&enc, ID0).is_none());
2343    }
2344
2345    // --- AES / SHA-2 known-answer vectors ---
2346
2347    #[test]
2348    fn aes_fips197_block_vectors() {
2349        // FIPS-197 Appendix C.1 (AES-128) and C.3 (AES-256), same plaintext.
2350        let pt: [u8; 16] = [
2351            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
2352            0xee, 0xff,
2353        ];
2354        let key128: Vec<u8> = (0u8..16).collect();
2355        let rks = aes_expand_key(&key128);
2356        let mut b = pt;
2357        aes_encrypt_block(&mut b, &rks);
2358        assert_eq!(hex(&b), "69c4e0d86a7b0430d8cdb78070b4c55a");
2359        aes_decrypt_block(&mut b, &rks, &aes_inv_sbox());
2360        assert_eq!(b, pt, "AES-128 decrypt inverts encrypt");
2361
2362        let key256: Vec<u8> = (0u8..32).collect();
2363        let rks = aes_expand_key(&key256);
2364        let mut b = pt;
2365        aes_encrypt_block(&mut b, &rks);
2366        assert_eq!(hex(&b), "8ea2b7ca516745bfeafc49904b496089");
2367    }
2368
2369    #[test]
2370    fn sha2_vectors() {
2371        assert_eq!(
2372            hex(&sha256(b"abc")),
2373            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
2374        );
2375        assert_eq!(
2376            hex(&sha512(b"abc")),
2377            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a\
2378             2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
2379        );
2380        assert_eq!(
2381            hex(&sha384(b"abc")),
2382            "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\
2383             8086072ba1e7cc2358baeca134c825a7"
2384        );
2385    }
2386
2387    #[test]
2388    fn aes_cbc_roundtrip() {
2389        let key: Vec<u8> = (0u8..16).collect();
2390        let iv = [0x24u8; 16];
2391        let pt = b"a message spanning several AES blocks exactly?!!";
2392        let ct = aes_cbc_encrypt_blocks(&key, &iv, &pkcs7_pad(pt));
2393        let mut val = iv.to_vec(); // PDF format: IV followed by ciphertext
2394        val.extend_from_slice(&ct);
2395        assert_eq!(aes_cbc_decrypt(&key, &val), pt);
2396    }
2397
2398    // --- AESV2 (V4/R4) end-to-end fixture ---
2399
2400    fn obj_key_aes(key: &[u8], num: u32, gen: u16) -> Vec<u8> {
2401        let mut input = key.to_vec();
2402        input.extend_from_slice(&num.to_le_bytes()[..3]);
2403        input.extend_from_slice(&gen.to_le_bytes()[..2]);
2404        input.extend_from_slice(b"sAlT");
2405        md5(&input)[..(key.len() + 5).min(16)].to_vec()
2406    }
2407
2408    /// Encrypts as a PDF AES value: a 16-byte IV followed by CBC ciphertext of
2409    /// the PKCS#7-padded plaintext.
2410    fn aes_encrypt_pdf(key: &[u8], pt: &[u8], iv: &[u8; 16]) -> Vec<u8> {
2411        let mut out = iv.to_vec();
2412        out.extend_from_slice(&aes_cbc_encrypt_blocks(key, iv, &pkcs7_pad(pt)));
2413        out
2414    }
2415
2416    fn encrypted_fixture_aesv2() -> Vec<u8> {
2417        use pdfboss_testkit::PdfBuilder;
2418        let o = owner_entry(b"", b"");
2419        let key = file_key(&o, b""); // R4 derivation matches R3 (EncryptMetadata true)
2420        let u = user_entry(&key);
2421        let iv = [0x11u8; 16];
2422        let msg = aes_encrypt_pdf(&obj_key_aes(&key, 3, 0), b"Top secret message", &iv);
2423        let stream = aes_encrypt_pdf(&obj_key_aes(&key, 4, 0), b"decrypted stream body", &iv);
2424
2425        let mut b = PdfBuilder::new().version(1, 5);
2426        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2427        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2428        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2429        b.stream(4, "", &stream);
2430        b.object(
2431            9,
2432            &format!(
2433                "<< /Filter /Standard /V 4 /R 4 /Length 128 /P {P} /O {} /U {} \
2434                 /CF << /StdCF << /CFM /AESV2 /Length 16 >> >> /StmF /StdCF /StrF /StdCF >>",
2435                hexstr(&o),
2436                hexstr(&u)
2437            ),
2438        );
2439        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2440        b.trailer_extra(&trailer).build(1)
2441    }
2442
2443    // Covers ISO 32000-1 §7.6.2, §7.6.3.2, §7.6.3.3 and §7.6.5.
2444    #[test]
2445    fn document_load_decrypts_aesv2() {
2446        use crate::object::ObjRef;
2447        use crate::Document;
2448        let doc = Document::load(encrypted_fixture_aesv2()).expect("AESV2 empty password opens");
2449        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2450        let msg = obj3
2451            .as_dict()
2452            .unwrap()
2453            .get("Msg")
2454            .unwrap()
2455            .as_str_bytes()
2456            .unwrap();
2457        assert_eq!(msg, b"Top secret message");
2458        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2459        assert_eq!(
2460            doc.stream_data(obj4.as_stream().unwrap()).unwrap(),
2461            b"decrypted stream body"
2462        );
2463    }
2464
2465    // --- V4 crypt filters: /CF, /StmF, /StrF, /EFF and the filter /Length ---
2466
2467    /// A V4/R4 file under the empty user password. `encrypt_entries` is the
2468    /// crypt filter part of the `/Encrypt` dictionary (`/CF`, `/StmF`,
2469    /// `/StrF`, `/EFF`, `/Length`), `n` the key length in bytes, and `body`
2470    /// adds the objects under test, encrypting them with the file key it is
2471    /// handed. Object 9 is the encryption dictionary.
2472    fn v4_fixture(
2473        n: usize,
2474        encrypt_entries: &str,
2475        body: impl FnOnce(&mut pdfboss_testkit::PdfBuilder, &[u8]),
2476    ) -> Vec<u8> {
2477        use pdfboss_testkit::PdfBuilder;
2478        let o = owner_entry_with(n, b"", b"");
2479        let key = file_key_with(n, &o, b"");
2480        let u = user_entry(&key);
2481        let mut b = PdfBuilder::new().version(1, 5);
2482        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2483        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2484        body(&mut b, &key);
2485        b.object(
2486            9,
2487            &format!(
2488                "<< /Filter /Standard /V 4 /R 4 /P {P} /O {} /U {} {encrypt_entries} >>",
2489                hexstr(&o),
2490                hexstr(&u)
2491            ),
2492        );
2493        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2494        b.trailer_extra(&trailer).build(1)
2495    }
2496
2497    const AESV2_CF: &str = "/CF << /StdCF << /CFM /AESV2 /Length 16 >> >>";
2498    const TWO_CF: &str =
2499        "/CF << /StdCF << /CFM /AESV2 /Length 16 >> /RcCF << /CFM /V2 /Length 16 >> >>";
2500    const IV: [u8; 16] = [0x11; 16];
2501
2502    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /StmF may name the standard
2503    // Identity filter, which leaves every stream as stored while /StrF still
2504    // decrypts the strings.
2505    #[test]
2506    fn identity_stream_filter_leaves_streams_as_stored() {
2507        use crate::Document;
2508        let data = v4_fixture(
2509            16,
2510            &format!("/Length 128 {AESV2_CF} /StmF /Identity /StrF /StdCF"),
2511            |b, key| {
2512                let msg = aes_encrypt_pdf(&obj_key_aes(key, 3, 0), b"Top secret message", &IV);
2513                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2514                b.stream(4, "", b"stored in the clear");
2515            },
2516        );
2517        let doc = Document::load(data).expect("an Identity stream filter is a valid V4 file");
2518        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2519        assert_eq!(body_of(&doc, 4), b"stored in the clear");
2520    }
2521
2522    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /StrF picks the string cipher
2523    // on its own, so strings and streams may use different methods.
2524    #[test]
2525    fn string_filter_is_chosen_independently_of_the_stream_filter() {
2526        use crate::Document;
2527        let data = v4_fixture(
2528            16,
2529            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /RcCF"),
2530            |b, key| {
2531                let msg = rc4(&obj_key(key, 3, 0), b"Top secret message");
2532                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2533                let stream =
2534                    aes_encrypt_pdf(&obj_key_aes(key, 4, 0), b"decrypted stream body", &IV);
2535                b.stream(4, "", &stream);
2536            },
2537        );
2538        let doc = Document::load(data).expect("two crypt filters open");
2539        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2540        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2541    }
2542
2543    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: /EFF names the filter for
2544    // embedded file streams; other streams keep /StmF.
2545    #[test]
2546    fn embedded_file_filter_applies_to_embedded_file_streams() {
2547        use crate::Document;
2548        let data = v4_fixture(
2549            16,
2550            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /StdCF /EFF /RcCF"),
2551            |b, key| {
2552                let attached = rc4(&obj_key(key, 4, 0), b"attached file bytes");
2553                b.stream(4, "/Type /EmbeddedFile", &attached);
2554                let stream =
2555                    aes_encrypt_pdf(&obj_key_aes(key, 5, 0), b"decrypted stream body", &IV);
2556                b.stream(5, "", &stream);
2557            },
2558        );
2559        let doc = Document::load(data).expect("an /EFF entry opens");
2560        assert_eq!(body_of(&doc, 4), b"attached file bytes");
2561        assert_eq!(body_of(&doc, 5), b"decrypted stream body");
2562    }
2563
2564    // Covers ISO 32000-1 §7.6.5: the Standard handler writes the crypt
2565    // filter's /Length in bytes, and it sets the key size when the
2566    // encryption dictionary itself has no /Length.
2567    #[test]
2568    fn crypt_filter_length_sets_the_key_size() {
2569        use crate::Document;
2570        let data = v4_fixture(
2571            5,
2572            "/CF << /StdCF << /CFM /V2 /Length 5 >> >> /StmF /StdCF /StrF /StdCF",
2573            |b, key| {
2574                let msg = rc4(&obj_key(key, 3, 0), b"Top secret message");
2575                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2576                b.stream(4, "", &rc4(&obj_key(key, 4, 0), b"decrypted stream body"));
2577            },
2578        );
2579        let doc = Document::load(data).expect("a 40-bit V4 file opens");
2580        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2581        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2582    }
2583
2584    // Covers ISO 32000-1 §7.6.2 and §7.6.5: AESV2 is AES-128, so its object
2585    // key is the whole 16-byte MD5 digest whatever the file key's length,
2586    // and a 40-bit file key must not reach the block cipher with 10 bytes.
2587    #[test]
2588    fn aesv2_always_derives_a_16_byte_object_key() {
2589        use crate::Document;
2590        let object_key = |key: &[u8], num: u32| {
2591            let mut input = key.to_vec();
2592            input.extend_from_slice(&num.to_le_bytes()[..3]);
2593            input.extend_from_slice(&[0, 0]);
2594            input.extend_from_slice(b"sAlT");
2595            md5(&input).to_vec()
2596        };
2597        let data = v4_fixture(
2598            5,
2599            "/Length 40 /CF << /StdCF << /CFM /AESV2 /Length 5 >> >> /StmF /StdCF /StrF /StdCF",
2600            |b, key| {
2601                let msg = aes_encrypt_pdf(&object_key(key, 3), b"Top secret message", &IV);
2602                b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2603                let stream = aes_encrypt_pdf(&object_key(key, 4), b"decrypted stream body", &IV);
2604                b.stream(4, "", &stream);
2605            },
2606        );
2607        let doc = Document::load(data).expect("a 40-bit AESV2 file opens");
2608        assert_eq!(msg_of(&doc, 3), b"Top secret message");
2609        assert_eq!(body_of(&doc, 4), b"decrypted stream body");
2610    }
2611
2612    // --- Per-stream /Crypt filters ---
2613
2614    // Covers ISO 32000-1 §7.4.10 and §7.6.5: a stream naming the Identity
2615    // crypt filter is stored in the clear inside an encrypted document.
2616    #[test]
2617    fn a_stream_crypt_filter_naming_identity_is_left_as_stored() {
2618        use crate::Document;
2619        let data = v4_fixture(
2620            16,
2621            &format!("/Length 128 {AESV2_CF} /StmF /StdCF /StrF /StdCF"),
2622            |b, key| {
2623                b.stream(
2624                    4,
2625                    "/Filter /Crypt /DecodeParms << /Type /CryptFilterDecodeParms /Name /Identity >>",
2626                    b"stored in the clear",
2627                );
2628                let stream =
2629                    aes_encrypt_pdf(&obj_key_aes(key, 5, 0), b"decrypted stream body", &IV);
2630                b.stream(5, "", &stream);
2631            },
2632        );
2633        let doc = Document::load(data).expect("opens");
2634        assert_eq!(body_of(&doc, 4), b"stored in the clear");
2635        assert_eq!(body_of(&doc, 5), b"decrypted stream body");
2636    }
2637
2638    // Covers ISO 32000-1 §7.4.10: without a /Name the Crypt filter means
2639    // Identity, and the filters after it still run.
2640    #[test]
2641    fn a_stream_crypt_filter_without_a_name_means_identity() {
2642        use crate::Document;
2643        let data = v4_fixture(
2644            16,
2645            &format!("/Length 128 {AESV2_CF} /StmF /StdCF /StrF /StdCF"),
2646            |b, _| {
2647                b.stream(
2648                    4,
2649                    "/Filter [/Crypt /ASCIIHexDecode]",
2650                    b"73746F72656420617320686578>",
2651                );
2652            },
2653        );
2654        let doc = Document::load(data).expect("opens");
2655        assert_eq!(body_of(&doc, 4), b"stored as hex");
2656    }
2657
2658    // Covers ISO 32000-1 §7.4.10 and §7.6.5: a stream naming one of the /CF
2659    // filters is decrypted with that filter's method and the file key as
2660    // is, without the per-object key of Algorithm 1.
2661    #[test]
2662    fn a_stream_crypt_filter_naming_a_cf_entry_uses_the_file_key_as_is() {
2663        use crate::Document;
2664        let data = v4_fixture(
2665            16,
2666            &format!("/Length 128 {TWO_CF} /StmF /StdCF /StrF /StdCF"),
2667            |b, key| {
2668                b.stream(
2669                    4,
2670                    "/Filter /Crypt /DecodeParms << /Name /RcCF >>",
2671                    &rc4(key, b"under the file key"),
2672                );
2673            },
2674        );
2675        let doc = Document::load(data).expect("opens");
2676        assert_eq!(body_of(&doc, 4), b"under the file key");
2677    }
2678
2679    // --- AESV3 (V5/R5 and R6) end-to-end fixture ---
2680
2681    fn encrypted_fixture_aesv3(r: i64) -> Vec<u8> {
2682        encrypted_fixture_aesv3_with(r, b"", b"")
2683    }
2684
2685    fn encrypted_fixture_aesv3_with(r: i64, user_pw: &[u8], owner_pw: &[u8]) -> Vec<u8> {
2686        encrypted_fixture_aesv3_with_metadata(r, user_pw, owner_pw, true, None)
2687    }
2688
2689    /// [`encrypted_fixture_aesv3_with`], plus an `/EncryptMetadata` entry
2690    /// and, when `metadata` is given, object 5 as a `/Type /Metadata`
2691    /// stream holding its bytes verbatim (never encrypted here, since a
2692    /// real writer with `encrypt_metadata` false stores it in plaintext
2693    /// too): the fixture a reader must not corrupt.
2694    fn encrypted_fixture_aesv3_with_metadata(
2695        r: i64,
2696        user_pw: &[u8],
2697        owner_pw: &[u8],
2698        encrypt_metadata: bool,
2699        metadata: Option<&[u8]>,
2700    ) -> Vec<u8> {
2701        use pdfboss_testkit::PdfBuilder;
2702        let key = aesv3_file_key();
2703        let iv = [0x22u8; 16];
2704        let msg = aes_encrypt_pdf(&key, b"AES-256 secret", &iv);
2705        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &iv);
2706
2707        let mut b = PdfBuilder::new().version(1, 7);
2708        let catalog = match metadata {
2709            Some(_) => "<< /Type /Catalog /Pages 2 0 R /Metadata 5 0 R >>",
2710            None => "<< /Type /Catalog /Pages 2 0 R >>",
2711        };
2712        b.object(1, catalog);
2713        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2714        b.object(3, &format!("<< /Msg {} >>", hexstr(&msg)));
2715        b.stream(4, "", &stream);
2716        if let Some(metadata) = metadata {
2717            b.stream(5, "<< /Type /Metadata /Subtype /XML >>", metadata);
2718        }
2719        let encrypt_metadata_entry = if encrypt_metadata {
2720            ""
2721        } else {
2722            " /EncryptMetadata false"
2723        };
2724        b.object(
2725            9,
2726            &format!(
2727                "<< /Filter /Standard /V 5 /R {r} /Length 256 /P {P} {}{encrypt_metadata_entry} \
2728                 /CF << /StdCF << /CFM /AESV3 /Length 32 >> >> /StmF /StdCF /StrF /StdCF >>",
2729                aesv3_password_entries(r, user_pw, owner_pw, &key),
2730            ),
2731        );
2732        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2733        b.trailer_extra(&trailer).build(1)
2734    }
2735
2736    /// The arbitrary 256-bit file key every AESV3 fixture is encrypted with.
2737    fn aesv3_file_key() -> Vec<u8> {
2738        (0u8..32).map(|i| i ^ 0x5a).collect()
2739    }
2740
2741    /// The `/U`, `/UE`, `/O` and `/OE` entries of a revision `r` AES-256
2742    /// dictionary that open `key` under the given passwords.
2743    fn aesv3_password_entries(r: i64, user_pw: &[u8], owner_pw: &[u8], key: &[u8]) -> String {
2744        let vsalt: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
2745        let ksalt: [u8; 8] = [9, 10, 11, 12, 13, 14, 15, 16];
2746        let mut u = hash_2b(r, user_pw, &vsalt, &[]); // 32-byte validation hash
2747        u.extend_from_slice(&vsalt);
2748        u.extend_from_slice(&ksalt);
2749        let intermediate = hash_2b(r, user_pw, &ksalt, &[]);
2750        let ue = aes_cbc_encrypt_blocks(&intermediate, &[0u8; 16], key);
2751        // The owner hashes additionally salt in the first 48 bytes of /U.
2752        let ovsalt: [u8; 8] = [21, 22, 23, 24, 25, 26, 27, 28];
2753        let oksalt: [u8; 8] = [31, 32, 33, 34, 35, 36, 37, 38];
2754        let mut o = hash_2b(r, owner_pw, &ovsalt, &u[..48]);
2755        o.extend_from_slice(&ovsalt);
2756        o.extend_from_slice(&oksalt);
2757        let ointermediate = hash_2b(r, owner_pw, &oksalt, &u[..48]);
2758        let oe = aes_cbc_encrypt_blocks(&ointermediate, &[0u8; 16], key);
2759        format!(
2760            "/U {} /UE {} /O {} /OE {}",
2761            hexstr(&u),
2762            hexstr(&ue),
2763            hexstr(&o),
2764            hexstr(&oe)
2765        )
2766    }
2767
2768    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: only the strings of an
2769    // encryption dictionary must be direct, so /CF may be an indirect
2770    // reference (pdf.js issue7665 is such a file) and still be read.
2771    #[test]
2772    fn an_indirect_cf_dictionary_is_followed() {
2773        use crate::Document;
2774        use pdfboss_testkit::PdfBuilder;
2775        let key = aesv3_file_key();
2776        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &[0x22u8; 16]);
2777        let mut b = PdfBuilder::new().version(1, 7);
2778        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2779        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2780        b.stream(4, "", &stream);
2781        b.object(8, "<< /StdCF << /CFM /AESV3 /Length 32 >> >>");
2782        b.object(
2783            9,
2784            &format!(
2785                "<< /Filter /Standard /V 5 /R 6 /Length 256 /P {P} {} \
2786                 /CF 8 0 R /StmF /StdCF /StrF /StdCF >>",
2787                aesv3_password_entries(6, b"", b"", &key),
2788            ),
2789        );
2790        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2791        let doc = Document::load(b.trailer_extra(&trailer).build(1))
2792            .expect("an indirect /CF still names the filters");
2793        assert_eq!(body_of(&doc, 4), b"AES-256 stream body");
2794    }
2795
2796    // Covers ISO 32000-1 §7.6.5 and §7.6.3.2: a V5 file picks its filters
2797    // from /CF the way a V4 file does, the Identity filter included.
2798    #[test]
2799    fn aesv3_identity_string_filter_leaves_strings_as_stored() {
2800        use crate::Document;
2801        use pdfboss_testkit::PdfBuilder;
2802        let key = aesv3_file_key();
2803        let stream = aes_encrypt_pdf(&key, b"AES-256 stream body", &[0x22u8; 16]);
2804        let mut b = PdfBuilder::new().version(1, 7);
2805        b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
2806        b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
2807        b.object(3, "<< /Msg (stored in the clear) >>");
2808        b.stream(4, "", &stream);
2809        b.object(
2810            9,
2811            &format!(
2812                "<< /Filter /Standard /V 5 /R 6 /Length 256 /P {P} {} \
2813                 /CF << /StdCF << /CFM /AESV3 /Length 32 >> >> /StmF /StdCF /StrF /Identity >>",
2814                aesv3_password_entries(6, b"", b"", &key),
2815            ),
2816        );
2817        let trailer = format!("/Encrypt 9 0 R /ID [{}{}]", hexstr(ID0), hexstr(ID0));
2818        let doc = Document::load(b.trailer_extra(&trailer).build(1))
2819            .expect("an Identity string filter is a valid V5 file");
2820        assert_eq!(msg_of(&doc, 3), b"stored in the clear");
2821        assert_eq!(body_of(&doc, 4), b"AES-256 stream body");
2822    }
2823
2824    fn assert_aesv3_decrypts(r: i64) {
2825        use crate::object::ObjRef;
2826        use crate::Document;
2827        let doc = Document::load(encrypted_fixture_aesv3(r)).expect("AESV3 empty password opens");
2828        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2829        let msg = obj3
2830            .as_dict()
2831            .unwrap()
2832            .get("Msg")
2833            .unwrap()
2834            .as_str_bytes()
2835            .unwrap();
2836        assert_eq!(msg, b"AES-256 secret", "R{r} string");
2837        let obj4 = doc.get(ObjRef { num: 4, gen: 0 }).unwrap();
2838        assert_eq!(
2839            doc.stream_data(obj4.as_stream().unwrap()).unwrap(),
2840            b"AES-256 stream body",
2841            "R{r} stream"
2842        );
2843    }
2844
2845    // Covers ISO 32000-1 §7.6.2.
2846    #[test]
2847    fn document_load_decrypts_aesv3_r5() {
2848        assert_aesv3_decrypts(5);
2849    }
2850
2851    // Covers ISO 32000-1 §7.6.3.3 and §7.6.5.
2852    #[test]
2853    fn document_load_decrypts_aesv3_r6() {
2854        assert_aesv3_decrypts(6); // exercises the iterated Algorithm 2.B hash
2855    }
2856
2857    // Covers ISO 32000-1 §14.3.2, §7.6.3.2 and §7.6.5.
2858    #[test]
2859    fn encrypt_metadata_false_leaves_the_metadata_stream_plaintext() {
2860        use crate::object::ObjRef;
2861        use crate::Document;
2862
2863        const XMP: &[u8] = b"<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>\
2864            <x:xmpmeta xmlns:x='adobe:ns:meta/'><rdf:RDF \
2865            xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'><rdf:Description \
2866            xmlns:dc='http://purl.org/dc/elements/1.1/'><dc:title>XMP Secret \
2867            Title</dc:title></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end='w'?>";
2868
2869        let bytes =
2870            encrypted_fixture_aesv3_with_metadata(6, b"user-pw", b"owner-pw", false, Some(XMP));
2871        let doc = Document::load_with_password(bytes, "user-pw").expect("user password opens");
2872
2873        let obj3 = doc.get(ObjRef { num: 3, gen: 0 }).unwrap();
2874        let msg = obj3.as_dict().unwrap().get("Msg").unwrap();
2875        assert_eq!(
2876            msg.as_str_bytes().unwrap(),
2877            b"AES-256 secret",
2878            "an ordinary string still decrypts under EncryptMetadata false"
2879        );
2880
2881        let obj5 = doc.get(ObjRef { num: 5, gen: 0 }).unwrap();
2882        let metadata = doc.stream_data(obj5.as_stream().unwrap()).unwrap();
2883        assert_eq!(
2884            metadata, XMP,
2885            "the metadata stream was stored in plaintext and must not be decrypted"
2886        );
2887    }
2888
2889    // --- R6 key material (write side): the existing validation code above
2890    // is the oracle everything generated here must open under. ---
2891
2892    /// Generated key material must open through the same `aesv3_key`
2893    /// validation a real reader runs, for both passwords, and reject a
2894    /// wrong one; the embedded `/Perms` must decrypt to its marker under
2895    /// the generated file key. The dict comes from `aes256_encrypt_dict`,
2896    /// the same builder `Encryptor::aes256_with_rng` ships, so this test
2897    /// exercises the real production layout rather than a hand-built one.
2898    // Covers ISO 32000-1 §7.6.3.3.
2899    #[test]
2900    fn r6_key_material_opens_under_aesv3_key() {
2901        let mut c = 0u8;
2902        let mut rng = |b: &mut [u8]| {
2903            for x in b {
2904                c = c.wrapping_add(1);
2905                *x = c;
2906            }
2907        };
2908        let material = r6_key_material(b"user-pw", b"owner-pw", P, true, &mut rng);
2909        let dict = aes256_encrypt_dict(&material, P);
2910
2911        let user_key = aesv3_key(&dict, 6, b"user-pw").expect("user password opens");
2912        assert_eq!(user_key, material.file_key.to_vec());
2913
2914        let owner_key = aesv3_key(&dict, 6, b"owner-pw").expect("owner password opens");
2915        assert_eq!(owner_key, material.file_key.to_vec());
2916
2917        assert!(aesv3_key(&dict, 6, b"wrong-pw").is_none());
2918
2919        assert!(perms_marker_valid(&material.file_key, &material.perms));
2920
2921        // With /Perms absent, aesv3_key skips the fast path and falls
2922        // through to comparing the /U and /O validation hashes directly,
2923        // proving those 32 bytes (not just the key-salt derivation the
2924        // fast path above exercised) are correct too.
2925        let mut without_perms = dict.clone();
2926        without_perms.remove("Perms");
2927        assert_eq!(
2928            aesv3_key(&without_perms, 6, b"user-pw"),
2929            Some(material.file_key.to_vec())
2930        );
2931        assert_eq!(
2932            aesv3_key(&without_perms, 6, b"owner-pw"),
2933            Some(material.file_key.to_vec())
2934        );
2935    }
2936
2937    // --- Permissions: the /P bit layout (ISO 32000-2 Table 22) ---
2938
2939    #[test]
2940    fn permissions_all_grants_every_bit() {
2941        // Bits 1-2 reserved and cleared, every other bit set: -4.
2942        assert_eq!(Permissions::all().p_value(), -4);
2943        assert_eq!(Permissions::default().p_value(), -4);
2944    }
2945
2946    #[test]
2947    fn permissions_p_value_clears_exactly_its_own_bit() {
2948        let base = Permissions::all().p_value();
2949        assert_eq!(
2950            Permissions {
2951                print: false,
2952                ..Permissions::all()
2953            }
2954            .p_value(),
2955            base & !(1 << 2), // bit 3
2956        );
2957        assert_eq!(
2958            Permissions {
2959                modify: false,
2960                ..Permissions::all()
2961            }
2962            .p_value(),
2963            base & !(1 << 3), // bit 4
2964        );
2965        assert_eq!(
2966            Permissions {
2967                copy: false,
2968                ..Permissions::all()
2969            }
2970            .p_value(),
2971            base & !(1 << 4), // bit 5
2972        );
2973        assert_eq!(
2974            Permissions {
2975                annotate: false,
2976                ..Permissions::all()
2977            }
2978            .p_value(),
2979            base & !(1 << 5), // bit 6
2980        );
2981        assert_eq!(
2982            Permissions {
2983                fill_forms: false,
2984                ..Permissions::all()
2985            }
2986            .p_value(),
2987            base & !(1 << 8), // bit 9
2988        );
2989        assert_eq!(
2990            Permissions {
2991                accessibility: false,
2992                ..Permissions::all()
2993            }
2994            .p_value(),
2995            base & !(1 << 9), // bit 10
2996        );
2997        assert_eq!(
2998            Permissions {
2999                assemble: false,
3000                ..Permissions::all()
3001            }
3002            .p_value(),
3003            base & !(1 << 10), // bit 11
3004        );
3005        assert_eq!(
3006            Permissions {
3007                print_hires: false,
3008                ..Permissions::all()
3009            }
3010            .p_value(),
3011            base & !(1 << 11), // bit 12
3012        );
3013    }
3014
3015    // --- Encryptor (write side): the round-trip proof for Task 2 ---
3016
3017    /// A `FnMut(&mut [u8])` filling buffers from an incrementing counter,
3018    /// so key material and IVs are reproducible without touching the OS
3019    /// random source.
3020    #[allow(clippy::type_complexity, reason = "see Encryptor's rng field")]
3021    fn counter_rng() -> Box<dyn FnMut(&mut [u8]) + Send> {
3022        let mut c = 0u8;
3023        Box::new(move |buf: &mut [u8]| {
3024            for b in buf {
3025                c = c.wrapping_add(1);
3026                *b = c;
3027            }
3028        })
3029    }
3030
3031    /// Recovers the file key from a produced `/Encrypt` dict through the
3032    /// same `aesv3_key` validation a real reader runs, and builds the
3033    /// matching `Decryptor`: the oracle every encrypted object must open
3034    /// under.
3035    fn decryptor_for(dict: &Dict, password: &[u8]) -> Decryptor {
3036        let key = aesv3_key(dict, 6, password).expect("password opens the produced dict");
3037        Decryptor {
3038            key,
3039            filters: CryptFilters::uniform(Cipher::Aesv3),
3040            encrypt_metadata: true,
3041        }
3042    }
3043
3044    #[test]
3045    fn encrypt_object_round_trips_dict_array_and_leaves_other_types_untouched() {
3046        use crate::object::ObjRef;
3047
3048        let (mut enc, dict) =
3049            Encryptor::aes256_with_rng("user-pw", "owner-pw", Permissions::all(), counter_rng());
3050
3051        let mut d = Dict::new();
3052        d.insert(
3053            Name("Msg".to_string()),
3054            Object::String(b"Top secret message".to_vec()),
3055        );
3056        d.insert(
3057            Name("List".to_string()),
3058            Object::Array(vec![
3059                Object::String(b"first".to_vec()),
3060                Object::String(b"second".to_vec()),
3061            ]),
3062        );
3063        d.insert(
3064            Name("Ref".to_string()),
3065            Object::Ref(ObjRef { num: 7, gen: 0 }),
3066        );
3067        d.insert(
3068            Name("Kind".to_string()),
3069            Object::Name(Name("Example".to_string())),
3070        );
3071        d.insert(Name("Count".to_string()), Object::Int(42));
3072        d.insert(Name("Scale".to_string()), Object::Real(1.5));
3073        d.insert(Name("On".to_string()), Object::Bool(true));
3074        d.insert(Name("Missing".to_string()), Object::Null);
3075        let original = Object::Dict(d);
3076
3077        let mut obj = original.clone();
3078        enc.encrypt_object(&mut obj, 3, 0);
3079        assert_ne!(obj, original, "encryption must change the strings");
3080
3081        // Ref, Name, numbers, bools and null pass through untouched, even
3082        // in the still-encrypted object.
3083        let (enc_dict, orig_dict) = (obj.as_dict().unwrap(), original.as_dict().unwrap());
3084        for key in ["Ref", "Kind", "Count", "Scale", "On", "Missing"] {
3085            assert_eq!(
3086                enc_dict.get(key),
3087                orig_dict.get(key),
3088                "{key} passes through untouched"
3089            );
3090        }
3091
3092        let decryptor = decryptor_for(&dict, b"user-pw");
3093        let mut roundtripped = obj.clone();
3094        decryptor.decrypt_object(&mut roundtripped, 3, 0);
3095        assert_eq!(roundtripped, original);
3096
3097        // The owner password recovers the same file key.
3098        let owner_decryptor = decryptor_for(&dict, b"owner-pw");
3099        let mut via_owner = obj;
3100        owner_decryptor.decrypt_object(&mut via_owner, 3, 0);
3101        assert_eq!(via_owner, original);
3102    }
3103
3104    #[test]
3105    fn encrypt_object_round_trips_a_stream_and_its_dict() {
3106        use crate::object::Stream;
3107
3108        let (mut enc, dict) =
3109            Encryptor::aes256_with_rng("", "owner-pw", Permissions::all(), counter_rng());
3110
3111        let mut stream_dict = Dict::new();
3112        stream_dict.insert(
3113            Name("Producer".to_string()),
3114            Object::String(b"pdfboss".to_vec()),
3115        );
3116        let original = Object::Stream(Stream {
3117            dict: stream_dict,
3118            data: b"decrypted stream body".to_vec(),
3119        });
3120        let mut obj = original.clone();
3121        enc.encrypt_object(&mut obj, 4, 0);
3122        let encrypted = obj.as_stream().unwrap();
3123        assert_ne!(encrypted.data, original.as_stream().unwrap().data);
3124        assert_ne!(
3125            encrypted.dict.get("Producer"),
3126            original.as_stream().unwrap().dict.get("Producer"),
3127            "the stream dict's own strings are encrypted too"
3128        );
3129
3130        let decryptor = decryptor_for(&dict, b"");
3131        let mut roundtripped = obj;
3132        decryptor.decrypt_object(&mut roundtripped, 4, 0);
3133        assert_eq!(roundtripped, original);
3134    }
3135
3136    // Covers ISO 32000-1 §7.4.10: a stream that names the Identity crypt
3137    // filter stays in the clear when the document is encrypted, so the
3138    // writer and the reader agree on what the dictionary says.
3139    #[test]
3140    fn encrypt_object_leaves_an_identity_crypt_stream_as_stored() {
3141        use crate::object::Stream;
3142
3143        let (mut enc, dict) =
3144            Encryptor::aes256_with_rng("", "owner-pw", Permissions::all(), counter_rng());
3145        let mut stream_dict = Dict::new();
3146        stream_dict.insert(
3147            Name("Filter".to_string()),
3148            Object::Name(Name("Crypt".to_string())),
3149        );
3150        let mut parms = Dict::new();
3151        parms.insert(
3152            Name("Name".to_string()),
3153            Object::Name(Name("Identity".to_string())),
3154        );
3155        stream_dict.insert(Name("DecodeParms".to_string()), Object::Dict(parms));
3156        let original = Object::Stream(Stream {
3157            dict: stream_dict,
3158            data: b"<?xpacket?> metadata in the clear".to_vec(),
3159        });
3160
3161        let mut obj = original.clone();
3162        enc.encrypt_object(&mut obj, 6, 0);
3163        assert_eq!(obj, original, "an Identity stream is written as stored");
3164
3165        let decryptor = decryptor_for(&dict, b"");
3166        decryptor.decrypt_object(&mut obj, 6, 0);
3167        assert_eq!(obj, original, "and read back as stored");
3168    }
3169
3170    #[test]
3171    fn encrypt_object_round_trips_padding_edge_lengths() {
3172        let (mut enc, dict) =
3173            Encryptor::aes256_with_rng("pw", "owner", Permissions::all(), counter_rng());
3174        let decryptor = decryptor_for(&dict, b"pw");
3175
3176        for plaintext in [Vec::new(), vec![0x41], vec![0x42; 16]] {
3177            let original = Object::String(plaintext.clone());
3178            let mut obj = original.clone();
3179            enc.encrypt_object(&mut obj, 1, 0);
3180            let mut roundtripped = obj;
3181            decryptor.decrypt_object(&mut roundtripped, 1, 0);
3182            assert_eq!(roundtripped, original, "{} bytes", plaintext.len());
3183        }
3184    }
3185}