Skip to main content

pdfrum_crypt/
rc4.rs

1//! RC4, the stream cipher behind `/V 1..4` (ISO 32000 §7.6.2).
2//!
3//! Textbook key-scheduling and pseudo-random generation over a 256-byte
4//! permutation. The one non-textbook detail is the empty key: PDFium's key
5//! schedule substitutes a zero key byte rather than dividing by zero, so an
6//! empty key names a real, reproducible permutation instead of an error. A
7//! recovered user password can legitimately be empty, so that path is
8//! reachable from ordinary files.
9//!
10//! The cipher is an involution — encryption and decryption are the same
11//! operation — which the revision 3+ password checks lean on to undo twenty
12//! rounds of encryption by running twenty rounds more.
13
14/// The permutation state of one RC4 keystream.
15///
16/// A value of this type is a keystream position, not a cipher: consuming it
17/// with [`Rc4::apply`] is the whole API.
18#[derive(Clone)]
19struct Rc4 {
20    perm: [u8; 256],
21    x: u8,
22    y: u8,
23}
24
25impl Rc4 {
26    /// Schedule the permutation from `key`.
27    ///
28    /// An empty key contributes zero at every step, matching PDFium rather
29    /// than rejecting the input.
30    fn new(key: &[u8]) -> Self {
31        let mut perm = [0u8; 256];
32        for (i, slot) in perm.iter_mut().enumerate() {
33            *slot = u8::try_from(i).unwrap_or(0);
34        }
35        let mut j = 0u8;
36        for i in 0..=u8::MAX {
37            let k = key
38                .get(usize::from(i) % key.len().max(1))
39                .copied()
40                .unwrap_or(0);
41            j = j.wrapping_add(at(&perm, i)).wrapping_add(k);
42            perm.swap(usize::from(i), usize::from(j));
43        }
44        Self { perm, x: 0, y: 0 }
45    }
46
47    /// Exclusive-or `data` with the keystream, in place.
48    fn apply(&mut self, data: &mut [u8]) {
49        for byte in data {
50            self.x = self.x.wrapping_add(1);
51            self.y = self.y.wrapping_add(at(&self.perm, self.x));
52            self.perm.swap(usize::from(self.x), usize::from(self.y));
53            let s = at(&self.perm, self.x).wrapping_add(at(&self.perm, self.y));
54            *byte ^= at(&self.perm, s);
55        }
56    }
57}
58
59/// Read the permutation at a byte-wide position.
60///
61/// A `u8` cannot address past the end of a 256-element array, which is the
62/// whole reason the permutation is that width; stating the lookup once keeps
63/// that argument in one place instead of at every use.
64fn at(perm: &[u8; 256], index: u8) -> u8 {
65    perm.get(usize::from(index)).copied().unwrap_or(0)
66}
67
68/// Crypt `data` under `key`, returning a fresh buffer.
69///
70/// RC4 is symmetric, so this both encrypts and decrypts.
71///
72/// ```
73/// # use pdfrum_crypt::rc4;
74/// let ct = rc4(b"foobar", b"secret");
75/// assert_eq!(rc4(b"foobar", &ct), b"secret");
76/// ```
77#[must_use]
78pub fn rc4(key: &[u8], data: &[u8]) -> Vec<u8> {
79    let mut out = data.to_vec();
80    rc4_in_place(key, &mut out);
81    out
82}
83
84/// Crypt `data` under `key` in place.
85pub(crate) fn rc4_in_place(key: &[u8], data: &mut [u8]) {
86    Rc4::new(key).apply(data);
87}
88
89#[cfg(test)]
90mod tests {
91    use super::rc4;
92
93    /// The plaintext of `fx_crypt_unittest.cpp`'s RC4 vectors, which iterates
94    /// a `uint8_t[]` string literal and so includes the NUL terminator.
95    fn short_plaintext() -> Vec<u8> {
96        let mut v = b"The Quick Fox Jumped Over The Lazy Brown Dog.".to_vec();
97        v.push(0);
98        v
99    }
100
101    fn long_plaintext() -> Vec<u8> {
102        let mut v = concat!(
103            "The Quick Fox Jumped Over The Lazy Brown Dog.\n",
104            "1234567890123456789012345678901234567890123456789012345678901234567890\n",
105            "1234567890123456789012345678901234567890123456789012345678901234567890\n",
106            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n",
107            "!@#$%^&*()[]{};':\",.<>/?\\|\r\t\n"
108        )
109        .as_bytes()
110        .to_vec();
111        v.push(0);
112        v
113    }
114
115    // From fx_crypt_unittest.cpp:328-341 — the empty key is a defined
116    // permutation, not an error.
117    #[test]
118    fn empty_key_short_data() {
119        let expected: [u8; 46] = [
120            138, 112, 236, 97, 242, 66, 52, 89, 225, 38, 88, 8, 47, 78, 216, 24, 170, 106, 26, 199,
121            208, 131, 157, 242, 55, 11, 25, 90, 66, 182, 19, 255, 210, 181, 85, 69, 31, 240, 206,
122            171, 97, 62, 202, 172, 30, 252,
123        ];
124        assert_eq!(rc4(&[], &short_plaintext()), expected);
125    }
126
127    // From fx_crypt_unittest.cpp:363-391 — over 256 bytes, so the
128    // permutation wraps.
129    #[test]
130    fn empty_key_long_data() {
131        let expected: [u8; 271] = [
132            138, 112, 236, 97, 242, 66, 52, 89, 225, 38, 88, 8, 47, 78, 216, 24, 170, 106, 26, 199,
133            208, 131, 157, 242, 55, 11, 25, 90, 66, 182, 19, 255, 210, 181, 85, 69, 31, 240, 206,
134            171, 97, 62, 202, 172, 30, 246, 19, 43, 184, 0, 173, 27, 140, 90, 167, 240, 122, 125,
135            184, 49, 149, 71, 63, 104, 171, 144, 242, 106, 121, 124, 209, 149, 61, 1, 66, 186, 252,
136            47, 51, 170, 253, 75, 95, 41, 203, 28, 197, 174, 144, 209, 166, 98, 142, 125, 44, 5,
137            147, 42, 73, 178, 119, 90, 253, 69, 103, 178, 15, 136, 51, 112, 39, 81, 37, 111, 129,
138            232, 106, 159, 126, 142, 120, 124, 48, 140, 253, 12, 223, 208, 106, 76, 60, 238, 5,
139            162, 100, 226, 251, 156, 169, 35, 193, 10, 242, 210, 20, 96, 37, 84, 99, 183, 179, 203,
140            62, 122, 54, 6, 51, 239, 142, 250, 238, 41, 223, 58, 48, 101, 29, 187, 43, 235, 3, 5,
141            176, 33, 14, 171, 36, 26, 234, 207, 105, 79, 69, 126, 82, 183, 105, 228, 31, 173, 8,
142            240, 99, 5, 147, 206, 215, 140, 48, 190, 165, 50, 41, 232, 29, 105, 156, 64, 229, 165,
143            12, 64, 163, 255, 146, 108, 212, 125, 142, 101, 13, 99, 174, 10, 160, 68, 196, 120,
144            110, 201, 254, 158, 97, 215, 0, 207, 90, 23, 208, 161, 105, 226, 164, 114, 80, 137, 58,
145            107, 109, 42, 110, 100, 202, 170, 224, 89, 28, 5, 138, 19, 253, 105, 220, 105, 24, 187,
146            109, 89, 205, 89, 202,
147        ];
148        assert_eq!(rc4(&[], &long_plaintext()), expected);
149    }
150
151    // From fx_crypt_unittest.cpp:421-426.
152    #[test]
153    fn foobar_key_short_data() {
154        let expected: [u8; 46] = [
155            59, 193, 117, 206, 167, 54, 218, 7, 229, 214, 188, 55, 90, 205, 196, 25, 36, 114, 199,
156            218, 161, 107, 122, 119, 106, 167, 44, 175, 240, 123, 192, 102, 174, 167, 105, 187,
157            202, 70, 121, 81, 17, 30, 5, 138, 116, 166,
158        ];
159        assert_eq!(rc4(b"foobar", &short_plaintext()), expected);
160    }
161
162    // From fx_crypt_unittest.cpp:447-477.
163    #[test]
164    fn foobar_key_long_data() {
165        let expected: [u8; 271] = [
166            59, 193, 117, 206, 167, 54, 218, 7, 229, 214, 188, 55, 90, 205, 196, 25, 36, 114, 199,
167            218, 161, 107, 122, 119, 106, 167, 44, 175, 240, 123, 192, 102, 174, 167, 105, 187,
168            202, 70, 121, 81, 17, 30, 5, 138, 116, 172, 169, 50, 160, 116, 237, 117, 108, 241, 127,
169            61, 83, 45, 77, 176, 0, 106, 191, 221, 132, 143, 219, 94, 2, 235, 204, 166, 201, 139,
170            140, 163, 104, 115, 48, 37, 18, 114, 168, 49, 235, 163, 179, 131, 182, 218, 120, 200,
171            9, 90, 60, 47, 55, 235, 135, 37, 21, 170, 48, 112, 185, 169, 43, 233, 88, 134, 117,
172            126, 248, 40, 176, 248, 30, 131, 108, 43, 139, 68, 232, 219, 7, 39, 223, 45, 199, 243,
173            54, 171, 31, 37, 161, 24, 38, 251, 13, 144, 106, 215, 179, 203, 5, 253, 25, 32, 25,
174            146, 109, 193, 143, 141, 177, 226, 134, 222, 95, 79, 156, 202, 240, 34, 153, 145, 169,
175            150, 231, 63, 113, 242, 156, 39, 136, 249, 108, 50, 181, 22, 22, 180, 57, 76, 69, 62,
176            254, 47, 141, 249, 235, 90, 25, 34, 40, 194, 66, 86, 110, 192, 235, 191, 205, 133, 91,
177            32, 104, 65, 43, 36, 140, 36, 228, 156, 105, 251, 169, 168, 203, 189, 238, 221, 64,
178            200, 68, 137, 153, 9, 183, 84, 153, 140, 239, 0, 15, 50, 126, 145, 22, 110, 43, 56, 94,
179            127, 48, 96, 47, 172, 3, 31, 130, 249, 243, 73, 206, 89, 9, 93, 156, 167, 205, 166, 75,
180            227, 36, 34, 81, 124, 195, 246, 152,
181        ];
182        assert_eq!(rc4(b"foobar", &long_plaintext()), expected);
183    }
184
185    #[test]
186    fn empty_input_yields_empty_output() {
187        assert!(rc4(b"key", &[]).is_empty());
188        assert!(rc4(&[], &[]).is_empty());
189    }
190
191    #[test]
192    fn crypting_twice_restores_the_input() {
193        let data: Vec<u8> = (0..300u32).map(|i| (i * 7 % 251) as u8).collect();
194        for key in [&b""[..], b"k", b"a longer key than one block would need"] {
195            assert_eq!(rc4(key, &rc4(key, &data)), data, "key {key:?}");
196        }
197    }
198}