Skip to main content

otf_pdf_syntax/
crypt.rs

1//! The standard security handler, read side (ISO 32000-2 §7.6).
2//!
3//! Reads revisions 2 to 6: RC4 with 40- to 128-bit keys, AES-128 and
4//! AES-256, including the deprecated revision 5. Writes revision 6 (AES-256)
5//! only, never RC4 or AES-128 (docs/PLAN.md §2).
6
7use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::NoPadding};
8use md5::{Digest, Md5};
9use rc4::{KeyInit, Rc4, StreamCipher, consts};
10use sha2::{Sha256, Sha384, Sha512};
11
12use crate::error::{Error, Result};
13use crate::object::{Dict, Name, ObjId, Object, PdfString};
14
15/// Padding for passwords shorter than 32 bytes (Algorithm 2, step a).
16const PAD: [u8; 32] = [
17    0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
18    0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a,
19];
20
21/// How one class of data (strings, streams, embedded files) is encrypted.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum CryptMethod {
24    /// Not encrypted (the `Identity` crypt filter).
25    Identity,
26    /// RC4 with a per-object key.
27    Rc4,
28    /// AES-128-CBC with a per-object key.
29    AesV2,
30    /// AES-256-CBC with the file key.
31    AesV3,
32}
33
34/// Which password opened the file.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Authentication {
37    /// No password matched, but only embedded files are encrypted, so the
38    /// document itself is readable. Data that needs the key is returned
39    /// undecrypted; see [`SecurityHandler::is_authenticated`].
40    None,
41    /// The user password (possibly the empty one).
42    User,
43    /// The owner password: all permissions apply.
44    Owner,
45}
46
47/// A security handler, authenticated and ready to decrypt.
48#[derive(Debug, Clone)]
49pub struct SecurityHandler {
50    revision: i64,
51    key: Vec<u8>,
52    strings: CryptMethod,
53    streams: CryptMethod,
54    embedded_files: CryptMethod,
55    encrypt_metadata: bool,
56    permissions: u32,
57    authentication: Authentication,
58}
59
60impl SecurityHandler {
61    /// Authenticates against `/Encrypt` with `password`, trying it as the
62    /// user password and then as the owner password. `id0` is the first
63    /// element of the trailer's `/ID`.
64    pub fn new(encrypt: &Dict, id0: &[u8], password: &[u8]) -> Result<Self> {
65        let filter = encrypt.get_name(b"Filter").unwrap_or(b"Standard");
66        if filter != b"Standard" {
67            return Err(Error::UnsupportedEncryption(format!(
68                "security handler /{}",
69                String::from_utf8_lossy(filter)
70            )));
71        }
72        let v = encrypt.get_int(b"V").unwrap_or(0);
73        let r = encrypt.get_int(b"R").unwrap_or(0);
74        if !(2..=6).contains(&r) {
75            return Err(Error::UnsupportedEncryption(format!("revision {r}")));
76        }
77        let o = encrypt
78            .get(b"O")
79            .and_then(Object::as_string)
80            .unwrap_or_default();
81        let u = encrypt
82            .get(b"U")
83            .and_then(Object::as_string)
84            .unwrap_or_default();
85        // /P is a signed 32-bit integer; only its bit pattern matters.
86        let p = encrypt.get_int(b"P").unwrap_or(0) as i32;
87        let encrypt_metadata = encrypt
88            .get(b"EncryptMetadata")
89            .and_then(Object::as_bool)
90            .unwrap_or(true);
91
92        let (strings, streams, embedded_files) = crypt_methods(encrypt, v)?;
93        let mut handler = Self {
94            revision: r,
95            key: Vec::new(),
96            strings,
97            streams,
98            embedded_files,
99            encrypt_metadata,
100            permissions: p as u32,
101            authentication: Authentication::User,
102        };
103
104        if let Some((key, auth)) =
105            authenticate(encrypt, id0, password, v, r, o, u, p, encrypt_metadata)
106        {
107            handler.key = key;
108            handler.authentication = auth;
109            return Ok(handler);
110        }
111        // Only embedded files are encrypted (`/AuthEvent /EFOpen`): the
112        // document reads without a password; attachments need one.
113        if strings == CryptMethod::Identity && streams == CryptMethod::Identity {
114            handler.authentication = Authentication::None;
115            return Ok(handler);
116        }
117        Err(Error::InvalidPassword)
118    }
119
120    /// The `/P` permission bits.
121    pub fn permissions(&self) -> u32 {
122        self.permissions
123    }
124
125    /// Whether a password matched, so every class of data can be decrypted.
126    pub fn is_authenticated(&self) -> bool {
127        self.authentication != Authentication::None
128    }
129
130    /// Which password authenticated.
131    pub fn authentication(&self) -> Authentication {
132        self.authentication
133    }
134
135    /// The security handler revision (`/R`).
136    pub fn revision(&self) -> i64 {
137        self.revision
138    }
139
140    /// Whether metadata streams are encrypted.
141    pub fn encrypt_metadata(&self) -> bool {
142        self.encrypt_metadata
143    }
144
145    /// The method for strings.
146    pub fn string_method(&self) -> CryptMethod {
147        self.strings
148    }
149
150    /// The method for streams.
151    pub fn stream_method(&self) -> CryptMethod {
152        self.streams
153    }
154
155    /// The method for embedded file streams.
156    pub fn embedded_file_method(&self) -> CryptMethod {
157        self.embedded_files
158    }
159
160    /// Decrypts `data` belonging to object `id` with `method`.
161    pub fn decrypt(&self, id: ObjId, method: CryptMethod, data: &[u8]) -> Vec<u8> {
162        if !self.is_authenticated() {
163            return data.to_vec();
164        }
165        match method {
166            CryptMethod::Identity => data.to_vec(),
167            CryptMethod::Rc4 => {
168                let mut out = data.to_vec();
169                rc4(&self.object_key(id, false), &mut out);
170                out
171            }
172            CryptMethod::AesV2 => aes_cbc_decrypt(&self.object_key(id, true), data),
173            CryptMethod::AesV3 => aes_cbc_decrypt(&self.key, data),
174        }
175    }
176
177    /// Decrypts every string inside `object` (recursively), for object `id`.
178    /// Streams' data is left alone; see [`SecurityHandler::decrypt`].
179    pub fn decrypt_strings(&self, id: ObjId, object: &mut Object) {
180        match object {
181            Object::String(s) => s.bytes = self.decrypt(id, self.strings, &s.bytes),
182            Object::Array(items) => {
183                for item in items {
184                    self.decrypt_strings(id, item);
185                }
186            }
187            Object::Dict(d) => self.decrypt_dict_strings(id, d),
188            Object::Stream(s) => self.decrypt_dict_strings(id, &mut s.dict),
189            _ => {}
190        }
191    }
192
193    fn decrypt_dict_strings(&self, id: ObjId, dict: &mut Dict) {
194        // A signature's /Contents is not encrypted (§7.6.2).
195        let is_signature = dict.is_type(b"Sig")
196            || dict.is_type(b"DocTimeStamp")
197            || dict.contains_key(b"ByteRange");
198        let keys: Vec<_> = dict.iter().map(|(k, _)| k.clone()).collect();
199        for key in keys {
200            if is_signature && key.as_bytes() == b"Contents" {
201                continue;
202            }
203            if let Some(mut value) = dict.remove(key.as_bytes()) {
204                self.decrypt_strings(id, &mut value);
205                dict.insert(key, value);
206            }
207        }
208    }
209
210    /// Algorithm 1: the per-object key for RC4 and AES-128.
211    fn object_key(&self, id: ObjId, aes: bool) -> Vec<u8> {
212        let mut h = Md5::new();
213        h.update(&self.key);
214        h.update(&id.num.to_le_bytes()[..3]);
215        h.update(id.generation.to_le_bytes());
216        if aes {
217            h.update(b"sAlT");
218        }
219        let digest = h.finalize();
220        let n = (self.key.len() + 5).min(16);
221        digest.get(..n).unwrap_or_default().to_vec()
222    }
223}
224
225/// Tries `password` and its encoding variants as user and owner password.
226#[allow(clippy::too_many_arguments)]
227fn authenticate(
228    encrypt: &Dict,
229    id0: &[u8],
230    password: &[u8],
231    v: i64,
232    r: i64,
233    o: &[u8],
234    u: &[u8],
235    p: i32,
236    encrypt_metadata: bool,
237) -> Option<(Vec<u8>, Authentication)> {
238    let candidates = password_candidates(password, r);
239    if r >= 5 {
240        let oe = encrypt
241            .get(b"OE")
242            .and_then(Object::as_string)
243            .unwrap_or_default();
244        let ue = encrypt
245            .get(b"UE")
246            .and_then(Object::as_string)
247            .unwrap_or_default();
248        return candidates.iter().find_map(|pw| {
249            let pw = pw.get(..pw.len().min(127)).unwrap_or_default();
250            aes256_key(r, pw, o, u, oe, ue)
251        });
252    }
253    let length_bits = if v == 1 {
254        40
255    } else {
256        encrypt.get_int(b"Length").unwrap_or(40)
257    };
258    // Some writers give /Length in bytes.
259    let length_bits = if length_bits <= 16 {
260        length_bits * 8
261    } else {
262        length_bits
263    };
264    let n = if r == 2 {
265        5
266    } else {
267        (length_bits / 8).clamp(5, 16) as usize
268    };
269    let user_key = |pw: &[u8]| compute_key(pw, o, p, id0, r, n, encrypt_metadata);
270    candidates.iter().find_map(|pw| {
271        let key = user_key(pw);
272        if check_user_key(&key, u, id0, r) {
273            return Some((key, Authentication::User));
274        }
275        // Owner password: recover the user password from /O (Algorithm 7).
276        let key = user_key(&owner_to_user_password(pw, o, r, n));
277        check_user_key(&key, u, id0, r).then_some((key, Authentication::Owner))
278    })
279}
280
281/// The byte strings to try for a password given as UTF-8 (or raw bytes).
282///
283/// R2–R4 passwords are PDFDocEncoding, so non-ASCII text is also tried as
284/// Latin-1. R5/R6 passwords are SASLprep-ed UTF-8; the cheap part of that
285/// profile (dropping characters mapped to nothing, mapping non-ASCII spaces
286/// to U+0020) is applied, but not NFKC normalization, which needs Unicode
287/// tables this crate does not carry.
288fn password_candidates(password: &[u8], r: i64) -> Vec<Vec<u8>> {
289    let mut out = vec![password.to_vec()];
290    let Ok(text) = std::str::from_utf8(password) else {
291        return out;
292    };
293    let variant: Option<Vec<u8>> = if r <= 4 {
294        text.chars()
295            .map(|c| u8::try_from(u32::from(c)).ok())
296            .collect::<Option<Vec<u8>>>()
297    } else {
298        let prepped: String = text
299            .chars()
300            .filter(|c| !is_mapped_to_nothing(*c))
301            .map(|c| {
302                if c != ' ' && c.is_whitespace() {
303                    ' '
304                } else {
305                    c
306                }
307            })
308            .collect();
309        Some(prepped.into_bytes())
310    };
311    if let Some(v) = variant
312        && v != password
313    {
314        out.push(v);
315    }
316    out
317}
318
319/// RFC 3454 table B.1, "commonly mapped to nothing".
320fn is_mapped_to_nothing(c: char) -> bool {
321    matches!(
322        c,
323        '\u{ad}'
324            | '\u{34f}'
325            | '\u{1806}'
326            | '\u{180b}'..='\u{180d}'
327            | '\u{200b}'..='\u{200d}'
328            | '\u{2060}'
329            | '\u{fe00}'..='\u{fe0f}'
330            | '\u{feff}'
331    )
332}
333
334/// Reads `/StrF`, `/StmF` and `/EFF` (V4/V5 crypt filters) or the implied
335/// method for V1/V2.
336fn crypt_methods(encrypt: &Dict, v: i64) -> Result<(CryptMethod, CryptMethod, CryptMethod)> {
337    match v {
338        1..=3 => Ok((CryptMethod::Rc4, CryptMethod::Rc4, CryptMethod::Rc4)),
339        4 | 5 => {
340            let cf = encrypt.get_dict(b"CF");
341            let method = |key: &[u8]| -> Result<CryptMethod> {
342                let name = encrypt.get_name(key).unwrap_or(b"Identity");
343                if name == b"Identity" {
344                    return Ok(CryptMethod::Identity);
345                }
346                let filter = cf.and_then(|cf| cf.get_dict(name));
347                match filter.and_then(|f| f.get_name(b"CFM")) {
348                    Some(b"V2") => Ok(CryptMethod::Rc4),
349                    Some(b"AESV2") => Ok(CryptMethod::AesV2),
350                    Some(b"AESV3") => Ok(CryptMethod::AesV3),
351                    Some(b"None") => Ok(CryptMethod::Identity),
352                    // A missing filter or CFM: V5 implies AES-256, V4 AES-128
353                    // is what readers assume.
354                    None if v == 5 => Ok(CryptMethod::AesV3),
355                    None => Ok(CryptMethod::AesV2),
356                    Some(other) => Err(Error::UnsupportedEncryption(format!(
357                        "crypt filter method /{}",
358                        String::from_utf8_lossy(other)
359                    ))),
360                }
361            };
362            let strings = method(b"StrF")?;
363            let streams = method(b"StmF")?;
364            let embedded = if encrypt.contains_key(b"EFF") {
365                method(b"EFF")?
366            } else {
367                streams
368            };
369            Ok((strings, streams, embedded))
370        }
371        other => Err(Error::UnsupportedEncryption(format!("algorithm V {other}"))),
372    }
373}
374
375fn padded(password: &[u8]) -> [u8; 32] {
376    let mut out = PAD;
377    let n = password.len().min(32);
378    if let (Some(dst), Some(src)) = (out.get_mut(..n), password.get(..n)) {
379        dst.copy_from_slice(src);
380    }
381    if let (Some(dst), Some(src)) = (out.get_mut(n..), PAD.get(..32 - n)) {
382        dst.copy_from_slice(src);
383    }
384    out
385}
386
387/// Algorithm 2: the file key from a user password (R2–R4).
388fn compute_key(
389    password: &[u8],
390    o: &[u8],
391    p: i32,
392    id0: &[u8],
393    r: i64,
394    n: usize,
395    encrypt_metadata: bool,
396) -> Vec<u8> {
397    let mut h = Md5::new();
398    h.update(padded(password));
399    h.update(o.get(..32).unwrap_or(o));
400    h.update(p.to_le_bytes());
401    h.update(id0);
402    if r >= 4 && !encrypt_metadata {
403        h.update([0xff; 4]);
404    }
405    let mut digest = h.finalize().to_vec();
406    if r >= 3 {
407        for _ in 0..50 {
408            digest = Md5::digest(digest.get(..n).unwrap_or_default()).to_vec();
409        }
410    }
411    digest.truncate(n);
412    digest
413}
414
415/// Algorithms 4 and 5 (the comparison half of 6).
416fn check_user_key(key: &[u8], u: &[u8], id0: &[u8], r: i64) -> bool {
417    if r == 2 {
418        let mut out = PAD.to_vec();
419        rc4(key, &mut out);
420        return u.get(..32) == Some(&out[..]);
421    }
422    let mut h = Md5::new();
423    h.update(PAD);
424    h.update(id0);
425    let mut out = h.finalize().to_vec();
426    for i in 0..20u8 {
427        let k: Vec<u8> = key.iter().map(|b| b ^ i).collect();
428        rc4(&k, &mut out);
429    }
430    u.get(..16) == out.get(..16)
431}
432
433/// Algorithm 7, first half: recovers the user password from `/O`.
434fn owner_to_user_password(owner: &[u8], o: &[u8], r: i64, n: usize) -> Vec<u8> {
435    let mut digest = Md5::digest(padded(owner)).to_vec();
436    if r >= 3 {
437        for _ in 0..50 {
438            digest = Md5::digest(&digest).to_vec();
439        }
440    }
441    digest.truncate(n);
442    let mut out = o.get(..32).unwrap_or(o).to_vec();
443    if r == 2 {
444        rc4(&digest, &mut out);
445    } else {
446        for i in (0..20u8).rev() {
447            let k: Vec<u8> = digest.iter().map(|b| b ^ i).collect();
448            rc4(&k, &mut out);
449        }
450    }
451    out
452}
453
454/// R5/R6: validates the password against `/U` then `/O` and unwraps the file
455/// key from `/UE` or `/OE`.
456fn aes256_key(
457    r: i64,
458    pw: &[u8],
459    o: &[u8],
460    u: &[u8],
461    oe: &[u8],
462    ue: &[u8],
463) -> Option<(Vec<u8>, Authentication)> {
464    let (o, u) = (o.get(..48)?, u.get(..48)?);
465    let hash = |input: &[u8], salt: &[u8], udata: &[u8]| -> Vec<u8> {
466        if r == 5 {
467            let mut h = Sha256::new();
468            h.update(input);
469            h.update(salt);
470            h.update(udata);
471            h.finalize().to_vec()
472        } else {
473            hash_2b(input, salt, udata)
474        }
475    };
476    let unwrap = |intermediate: &[u8], wrapped: &[u8]| -> Option<Vec<u8>> {
477        let mut buf = wrapped.get(..32)?.to_vec();
478        cbc::Decryptor::<aes::Aes256>::new_from_slices(intermediate, &[0u8; 16])
479            .ok()?
480            .decrypt_padded_mut::<NoPadding>(&mut buf)
481            .ok()?;
482        Some(buf)
483    };
484    if hash(pw, u.get(32..40)?, &[]) == u.get(..32)? {
485        let k = hash(pw, u.get(40..48)?, &[]);
486        return Some((unwrap(&k, ue)?, Authentication::User));
487    }
488    if hash(pw, o.get(32..40)?, u) == o.get(..32)? {
489        let k = hash(pw, o.get(40..48)?, u);
490        return Some((unwrap(&k, oe)?, Authentication::Owner));
491    }
492    None
493}
494
495/// Algorithm 2.B: the R6 hardened hash.
496fn hash_2b(password: &[u8], salt: &[u8], udata: &[u8]) -> Vec<u8> {
497    let mut h = Sha256::new();
498    h.update(password);
499    h.update(salt);
500    h.update(udata);
501    let mut k = h.finalize().to_vec();
502    let mut round = 0u32;
503    loop {
504        let mut k1 = Vec::with_capacity(64 * (password.len() + k.len() + udata.len()));
505        for _ in 0..64 {
506            k1.extend_from_slice(password);
507            k1.extend_from_slice(&k);
508            k1.extend_from_slice(udata);
509        }
510        let (Some(key), Some(iv)) = (k.get(..16), k.get(16..32)) else {
511            return k;
512        };
513        let len = k1.len();
514        let Ok(enc) = cbc::Encryptor::<aes::Aes128>::new_from_slices(key, iv) else {
515            return k;
516        };
517        // k1's length is a multiple of 64, so no padding is needed.
518        let Ok(e) = enc.encrypt_padded_mut::<NoPadding>(&mut k1, len) else {
519            return k;
520        };
521        let e = e.to_vec();
522        let sum: u32 = e.iter().take(16).map(|&b| u32::from(b)).sum();
523        k = match sum % 3 {
524            0 => Sha256::digest(&e).to_vec(),
525            1 => Sha384::digest(&e).to_vec(),
526            _ => Sha512::digest(&e).to_vec(),
527        };
528        let last = u32::from(e.last().copied().unwrap_or(0));
529        round += 1;
530        if round >= 64 && last <= round - 32 {
531            break;
532        }
533    }
534    k.truncate(32);
535    k
536}
537
538/// RC4 with a 5- to 16-byte key; other lengths leave `data` unchanged
539/// (they cannot arise from a valid `/Length`).
540fn rc4(key: &[u8], data: &mut [u8]) {
541    macro_rules! apply {
542        ($($n:literal => $size:ty),*) => {
543            match key.len() {
544                $($n => {
545                    if let Ok(mut c) = Rc4::<$size>::new_from_slice(key) {
546                        c.apply_keystream(data);
547                    }
548                })*
549                _ => {}
550            }
551        };
552    }
553    apply!(5 => consts::U5, 6 => consts::U6, 7 => consts::U7, 8 => consts::U8,
554        9 => consts::U9, 10 => consts::U10, 11 => consts::U11, 12 => consts::U12,
555        13 => consts::U13, 14 => consts::U14, 15 => consts::U15, 16 => consts::U16);
556}
557
558/// AES-CBC with the IV in the first 16 bytes. Lenient: a trailing partial
559/// block is dropped and invalid padding is left in place.
560fn aes_cbc_decrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
561    let (Some(iv), Some(body)) = (data.get(..16), data.get(16..)) else {
562        return Vec::new();
563    };
564    let mut buf = body
565        .get(..body.len() / 16 * 16)
566        .unwrap_or_default()
567        .to_vec();
568    let ok = match key.len() {
569        16 => cbc::Decryptor::<aes::Aes128>::new_from_slices(key, iv)
570            .ok()
571            .and_then(|d| d.decrypt_padded_mut::<NoPadding>(&mut buf).ok().map(|_| ())),
572        32 => cbc::Decryptor::<aes::Aes256>::new_from_slices(key, iv)
573            .ok()
574            .and_then(|d| d.decrypt_padded_mut::<NoPadding>(&mut buf).ok().map(|_| ())),
575        _ => None,
576    };
577    if ok.is_none() {
578        return Vec::new();
579    }
580    if let Some(&pad) = buf.last() {
581        let n = usize::from(pad);
582        if (1..=16).contains(&n) && n <= buf.len() && buf.iter().rev().take(n).all(|&b| b == pad) {
583            buf.truncate(buf.len() - n);
584        }
585    }
586    buf
587}
588
589/// Randomness, injected by the host (docs/PLAN.md §2: no ambient I/O).
590/// Encryption takes keys, salts and IVs from it; a deterministic source gives
591/// byte-identical output.
592pub trait Entropy: Send + Sync {
593    /// Fills `buf` with random bytes.
594    fn fill(&self, buf: &mut [u8]);
595}
596
597/// What an encrypted document allows a user-password reader to do
598/// (ISO 32000-2 Table 22). The owner password allows everything.
599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
600pub struct Permissions {
601    /// Print (bit 3).
602    pub print: bool,
603    /// Modify contents (bit 4).
604    pub modify: bool,
605    /// Copy or extract text and graphics (bit 5).
606    pub copy: bool,
607    /// Add or modify annotations and fill forms (bit 6).
608    pub annotate: bool,
609    /// Fill in form fields (bit 9).
610    pub fill_forms: bool,
611    /// Extract for accessibility (bit 10).
612    pub accessibility: bool,
613    /// Assemble: insert, rotate, delete pages (bit 11).
614    pub assemble: bool,
615    /// Print at full quality (bit 12).
616    pub print_high_quality: bool,
617}
618
619impl Permissions {
620    /// Everything allowed.
621    pub const ALL: Permissions = Permissions {
622        print: true,
623        modify: true,
624        copy: true,
625        annotate: true,
626        fill_forms: true,
627        accessibility: true,
628        assemble: true,
629        print_high_quality: true,
630    };
631
632    /// The `/P` value: reserved bits 7, 8 and 13–32 set, 1–2 clear.
633    pub fn bits(&self) -> u32 {
634        let mut p: u32 = 0xffff_f0c0;
635        for (on, bit) in [
636            (self.print, 3),
637            (self.modify, 4),
638            (self.copy, 5),
639            (self.annotate, 6),
640            (self.fill_forms, 9),
641            (self.accessibility, 10),
642            (self.assemble, 11),
643            (self.print_high_quality, 12),
644        ] {
645            if on {
646                p |= 1 << (bit - 1);
647            }
648        }
649        p
650    }
651
652    /// The permissions encoded in a `/P` value.
653    pub fn from_bits(p: u32) -> Self {
654        let bit = |n: u32| p & (1 << (n - 1)) != 0;
655        Self {
656            print: bit(3),
657            modify: bit(4),
658            copy: bit(5),
659            annotate: bit(6),
660            fill_forms: bit(9),
661            accessibility: bit(10),
662            assemble: bit(11),
663            print_high_quality: bit(12),
664        }
665    }
666}
667
668impl Default for Permissions {
669    fn default() -> Self {
670        Self::ALL
671    }
672}
673
674impl SecurityHandler {
675    /// Creates a revision 6 (AES-256) handler for writing, and the
676    /// `/Encrypt` dictionary that describes it (Algorithms 8, 9 and 10).
677    pub fn new_aes256(
678        user_password: &[u8],
679        owner_password: &[u8],
680        permissions: Permissions,
681        encrypt_metadata: bool,
682        entropy: &dyn Entropy,
683    ) -> (Self, Dict) {
684        let truncate = |pw: &[u8]| pw.get(..pw.len().min(127)).unwrap_or_default().to_vec();
685        let (user, owner) = (truncate(user_password), truncate(owner_password));
686        let mut key = [0u8; 32];
687        entropy.fill(&mut key);
688        let mut salts = [0u8; 32];
689        entropy.fill(&mut salts);
690        let (uvs, rest) = salts.split_at(8);
691        let (uks, rest) = rest.split_at(8);
692        let (ovs, oks) = rest.split_at(8);
693
694        let wrap = |intermediate: &[u8]| -> Vec<u8> {
695            let mut buf = key.to_vec();
696            if let Ok(enc) =
697                cbc::Encryptor::<aes::Aes256>::new_from_slices(intermediate, &[0u8; 16])
698            {
699                let _ = enc.encrypt_padded_mut::<NoPadding>(&mut buf, 32);
700            }
701            buf
702        };
703        let mut u = hash_2b(&user, uvs, &[]);
704        u.extend_from_slice(uvs);
705        u.extend_from_slice(uks);
706        let ue = wrap(&hash_2b(&user, uks, &[]));
707        let mut o = hash_2b(&owner, ovs, &u);
708        o.extend_from_slice(ovs);
709        o.extend_from_slice(oks);
710        let oe = wrap(&hash_2b(&owner, oks, &u));
711
712        let p = permissions.bits();
713        let mut perms = [0u8; 16];
714        perms[..4].copy_from_slice(&p.to_le_bytes());
715        perms[4..8].copy_from_slice(&[0xff; 4]);
716        perms[8] = if encrypt_metadata { b'T' } else { b'F' };
717        perms[9..12].copy_from_slice(b"adb");
718        entropy.fill(&mut perms[12..]);
719        if let Ok(enc) = cbc::Encryptor::<aes::Aes256>::new_from_slices(&key, &[0u8; 16]) {
720            // One block with a zero IV is ECB, which Algorithm 10 specifies.
721            let _ = enc.encrypt_padded_mut::<NoPadding>(&mut perms, 16);
722        }
723
724        let mut std_cf = Dict::new();
725        std_cf.insert("AuthEvent", Name::from("DocOpen"));
726        std_cf.insert("CFM", Name::from("AESV3"));
727        std_cf.insert("Length", 32i64);
728        let mut cf = Dict::new();
729        cf.insert("StdCF", std_cf);
730        let mut dict = Dict::new();
731        dict.insert("Filter", Name::from("Standard"));
732        dict.insert("V", 5i64);
733        dict.insert("R", 6i64);
734        dict.insert("Length", 256i64);
735        dict.insert("CF", cf);
736        dict.insert("StmF", Name::from("StdCF"));
737        dict.insert("StrF", Name::from("StdCF"));
738        dict.insert("O", PdfString::hex(o));
739        dict.insert("U", PdfString::hex(u));
740        dict.insert("OE", PdfString::hex(oe));
741        dict.insert("UE", PdfString::hex(ue));
742        dict.insert("Perms", PdfString::hex(perms.to_vec()));
743        dict.insert("P", i64::from(p as i32));
744        if !encrypt_metadata {
745            dict.insert("EncryptMetadata", false);
746        }
747        let handler = Self {
748            revision: 6,
749            key: key.to_vec(),
750            strings: CryptMethod::AesV3,
751            streams: CryptMethod::AesV3,
752            embedded_files: CryptMethod::AesV3,
753            encrypt_metadata,
754            permissions: p,
755            authentication: Authentication::Owner,
756        };
757        (handler, dict)
758    }
759
760    /// Whether this handler can encrypt new data: only AES-256 is ever
761    /// written (D4); a file using RC4 or AES-128 must be re-encrypted by a
762    /// full save instead.
763    pub fn can_encrypt(&self) -> bool {
764        self.is_authenticated()
765            && [self.strings, self.streams]
766                .iter()
767                .all(|m| matches!(m, CryptMethod::AesV3 | CryptMethod::Identity))
768    }
769
770    /// Encrypts `data` for object `id` with `method` (AES-256 or Identity).
771    pub fn encrypt(
772        &self,
773        method: CryptMethod,
774        data: &[u8],
775        entropy: &dyn Entropy,
776    ) -> Result<Vec<u8>> {
777        match method {
778            CryptMethod::Identity => Ok(data.to_vec()),
779            CryptMethod::AesV3 => {
780                let mut iv = [0u8; 16];
781                entropy.fill(&mut iv);
782                let pad = 16 - data.len() % 16;
783                let mut buf = data.to_vec();
784                buf.resize(data.len() + pad, pad as u8);
785                let len = buf.len();
786                cbc::Encryptor::<aes::Aes256>::new_from_slices(&self.key, &iv)
787                    .map_err(|_| Error::UnsupportedEncryption("bad AES-256 key".into()))?
788                    .encrypt_padded_mut::<NoPadding>(&mut buf, len)
789                    .map_err(|_| Error::UnsupportedEncryption("AES padding".into()))?;
790                let mut out = iv.to_vec();
791                out.extend_from_slice(&buf);
792                Ok(out)
793            }
794            _ => Err(Error::UnsupportedEncryption(
795                "only AES-256 is written; save in full to re-encrypt".into(),
796            )),
797        }
798    }
799
800    /// Encrypts every string inside `object` in place (streams' data is
801    /// left to [`SecurityHandler::encrypt`]).
802    pub fn encrypt_strings(&self, object: &mut Object, entropy: &dyn Entropy) -> Result<()> {
803        match object {
804            Object::String(s) => {
805                s.bytes = self.encrypt(self.strings, &s.bytes, entropy)?;
806                Ok(())
807            }
808            Object::Array(items) => items
809                .iter_mut()
810                .try_for_each(|i| self.encrypt_strings(i, entropy)),
811            Object::Dict(d) => self.encrypt_dict_strings(d, entropy),
812            Object::Stream(st) => self.encrypt_dict_strings(&mut st.dict, entropy),
813            _ => Ok(()),
814        }
815    }
816
817    fn encrypt_dict_strings(&self, dict: &mut Dict, entropy: &dyn Entropy) -> Result<()> {
818        let is_signature = dict.is_type(b"Sig")
819            || dict.is_type(b"DocTimeStamp")
820            || dict.contains_key(b"ByteRange");
821        let keys: Vec<_> = dict.iter().map(|(k, _)| k.clone()).collect();
822        for key in keys {
823            if is_signature && key.as_bytes() == b"Contents" {
824                continue;
825            }
826            if let Some(mut value) = dict.remove(key.as_bytes()) {
827                self.encrypt_strings(&mut value, entropy)?;
828                dict.insert(key, value);
829            }
830        }
831        Ok(())
832    }
833}
834
835#[cfg(test)]
836mod tests {
837    use super::*;
838
839    #[test]
840    fn rc4_known_vector() {
841        // RFC 6229 is for longer keys; this is the classic "Key"/"Plaintext".
842        let mut data = b"Plaintext".to_vec();
843        rc4(b"Key\0\0", &mut data);
844        let mut back = data.clone();
845        rc4(b"Key\0\0", &mut back);
846        assert_eq!(back, b"Plaintext");
847        let mut untouched = b"x".to_vec();
848        rc4(b"abc", &mut untouched);
849        assert_eq!(untouched, b"x");
850    }
851
852    #[test]
853    fn padding() {
854        assert_eq!(padded(b""), PAD);
855        let p = padded(b"ab");
856        assert_eq!(&p[..2], b"ab");
857        assert_eq!(&p[2..], &PAD[..30]);
858        assert_eq!(padded(&[7; 40]), [7; 32]);
859    }
860
861    #[test]
862    fn aes_lenient_decrypt() {
863        assert!(aes_cbc_decrypt(&[0; 16], b"short").is_empty());
864        assert!(aes_cbc_decrypt(&[0; 3], &[0; 32]).is_empty());
865        // Round trip one block with PKCS#7 padding.
866        let key = [1u8; 16];
867        let iv = [2u8; 16];
868        let mut block = *b"hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
869        cbc::Encryptor::<aes::Aes128>::new_from_slices(&key, &iv)
870            .unwrap()
871            .encrypt_padded_mut::<NoPadding>(&mut block, 16)
872            .unwrap();
873        let mut data = iv.to_vec();
874        data.extend_from_slice(&block);
875        assert_eq!(aes_cbc_decrypt(&key, &data), b"hello");
876    }
877
878    #[test]
879    fn unsupported_handlers() {
880        let mut d = Dict::new();
881        d.insert("Filter", crate::object::Name::from("Adobe.PubSec"));
882        assert!(matches!(
883            SecurityHandler::new(&d, b"", b""),
884            Err(Error::UnsupportedEncryption(_))
885        ));
886        let mut d = Dict::new();
887        d.insert("Filter", crate::object::Name::from("Standard"));
888        d.insert("R", 7i64);
889        assert!(matches!(
890            SecurityHandler::new(&d, b"", b""),
891            Err(Error::UnsupportedEncryption(_))
892        ));
893    }
894
895    #[test]
896    fn password_variants() {
897        assert_eq!(password_candidates(b"abc", 4), vec![b"abc".to_vec()]);
898        assert_eq!(
899            password_candidates("æøå".as_bytes(), 4),
900            vec!["æøå".as_bytes().to_vec(), vec![0xe6, 0xf8, 0xe5]]
901        );
902        // Not representable in Latin-1: only the raw bytes.
903        assert_eq!(password_candidates("€".as_bytes(), 3).len(), 1);
904        assert_eq!(
905            password_candidates("a\u{ad}b\u{a0}c".as_bytes(), 6)[1],
906            b"ab c".to_vec()
907        );
908        assert_eq!(password_candidates(&[0xff], 6), vec![vec![0xff]]);
909    }
910
911    #[test]
912    fn identity_only_opens_without_password() {
913        let mut cf = Dict::new();
914        let mut std_cf = Dict::new();
915        std_cf.insert("CFM", crate::object::Name::from("AESV3"));
916        cf.insert("StdCF", std_cf);
917        let mut d = Dict::new();
918        d.insert("Filter", crate::object::Name::from("Standard"));
919        d.insert("V", 5i64);
920        d.insert("R", 6i64);
921        d.insert("CF", cf);
922        d.insert("StmF", crate::object::Name::from("Identity"));
923        d.insert("StrF", crate::object::Name::from("Identity"));
924        d.insert("EFF", crate::object::Name::from("StdCF"));
925        let h = SecurityHandler::new(&d, b"", b"").unwrap();
926        assert_eq!(h.authentication(), Authentication::None);
927        assert!(!h.is_authenticated());
928        assert_eq!(h.embedded_file_method(), CryptMethod::AesV3);
929        assert_eq!(
930            h.decrypt(ObjId::new(1, 0), CryptMethod::AesV3, b"raw"),
931            b"raw"
932        );
933        // With AES streams the same failure is a wrong password.
934        d.insert("StmF", crate::object::Name::from("StdCF"));
935        assert_eq!(
936            SecurityHandler::new(&d, b"", b"").err(),
937            Some(Error::InvalidPassword)
938        );
939    }
940
941    /// A deterministic test entropy source: a counter.
942    struct Counter(std::sync::atomic::AtomicU8);
943
944    impl Entropy for Counter {
945        fn fill(&self, buf: &mut [u8]) {
946            for b in buf {
947                *b = self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
948            }
949        }
950    }
951
952    #[test]
953    fn permissions_bits() {
954        assert_eq!(
955            Permissions::ALL.bits(),
956            0xffff_ffc0 | 0b1111 << 2 | 0xf << 8
957        );
958        let none = Permissions::from_bits(0xffff_f0c0);
959        assert!(!none.print && !none.copy && !none.assemble);
960        assert_eq!(
961            Permissions::from_bits(Permissions::ALL.bits()),
962            Permissions::ALL
963        );
964        let print_only = Permissions {
965            print: true,
966            ..Permissions::from_bits(0)
967        };
968        assert_eq!(print_only.bits(), 0xffff_f0c4);
969    }
970
971    #[test]
972    fn aes256_write_then_read() {
973        let entropy = Counter(Default::default());
974        let perms = Permissions {
975            copy: false,
976            ..Permissions::ALL
977        };
978        let (writer, dict) = SecurityHandler::new_aes256(b"user", b"owner", perms, true, &entropy);
979        assert!(writer.can_encrypt());
980        let id = ObjId::new(7, 0);
981        let secret = b"attack at dawn".to_vec();
982        let encrypted = writer
983            .encrypt(CryptMethod::AesV3, &secret, &entropy)
984            .unwrap();
985        assert_eq!(encrypted.len(), 16 + 16);
986
987        for (pw, auth) in [
988            (&b"user"[..], Authentication::User),
989            (b"owner", Authentication::Owner),
990        ] {
991            let reader = SecurityHandler::new(&dict, b"", pw).unwrap();
992            assert_eq!(reader.authentication(), auth);
993            assert_eq!(reader.revision(), 6);
994            assert_eq!(Permissions::from_bits(reader.permissions()), perms);
995            assert_eq!(reader.decrypt(id, CryptMethod::AesV3, &encrypted), secret);
996        }
997        assert_eq!(
998            SecurityHandler::new(&dict, b"", b"nope").err(),
999            Some(Error::InvalidPassword)
1000        );
1001
1002        let mut obj = Object::Array(vec![Object::String(PdfString::literal("hi"))]);
1003        writer.encrypt_strings(&mut obj, &entropy).unwrap();
1004        let reader = SecurityHandler::new(&dict, b"", b"user").unwrap();
1005        reader.decrypt_strings(id, &mut obj);
1006        assert_eq!(obj.as_array().unwrap()[0].as_string(), Some(&b"hi"[..]));
1007    }
1008
1009    #[test]
1010    fn same_entropy_same_output() {
1011        let a = SecurityHandler::new_aes256(
1012            b"u",
1013            b"o",
1014            Permissions::ALL,
1015            false,
1016            &Counter(Default::default()),
1017        )
1018        .1;
1019        let b = SecurityHandler::new_aes256(
1020            b"u",
1021            b"o",
1022            Permissions::ALL,
1023            false,
1024            &Counter(Default::default()),
1025        )
1026        .1;
1027        assert_eq!(a, b);
1028        assert_eq!(a.get(b"EncryptMetadata"), Some(&Object::Bool(false)));
1029    }
1030
1031    #[test]
1032    fn legacy_methods_are_never_written() {
1033        let entropy = Counter(Default::default());
1034        let (h, _) = SecurityHandler::new_aes256(b"", b"", Permissions::ALL, true, &entropy);
1035        assert!(h.encrypt(CryptMethod::Rc4, b"x", &entropy).is_err());
1036        assert!(h.encrypt(CryptMethod::AesV2, b"x", &entropy).is_err());
1037        assert_eq!(
1038            h.encrypt(CryptMethod::Identity, b"x", &entropy).unwrap(),
1039            b"x"
1040        );
1041    }
1042
1043    #[test]
1044    fn crypt_filter_methods() {
1045        let mut std_cf = Dict::new();
1046        std_cf.insert("CFM", crate::object::Name::from("AESV2"));
1047        let mut cf = Dict::new();
1048        cf.insert("StdCF", std_cf);
1049        let mut d = Dict::new();
1050        d.insert("CF", cf);
1051        d.insert("StmF", crate::object::Name::from("StdCF"));
1052        d.insert("StrF", crate::object::Name::from("Identity"));
1053        assert_eq!(
1054            crypt_methods(&d, 4).unwrap(),
1055            (
1056                CryptMethod::Identity,
1057                CryptMethod::AesV2,
1058                CryptMethod::AesV2
1059            )
1060        );
1061        assert_eq!(
1062            crypt_methods(&Dict::new(), 2).unwrap(),
1063            (CryptMethod::Rc4, CryptMethod::Rc4, CryptMethod::Rc4)
1064        );
1065        assert!(crypt_methods(&Dict::new(), 9).is_err());
1066    }
1067}