#[cfg(feature = "legacy-crypto")]
struct Rc4Cipher {
s: [u8; 256],
i: u8,
j: u8,
}
#[cfg(feature = "legacy-crypto")]
impl Rc4Cipher {
fn new(key: &[u8]) -> Self {
let mut s = [0u8; 256];
for (i, val) in s.iter_mut().enumerate() {
*val = i as u8;
}
let mut j = 0u8;
for i in 0..256 {
j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]);
s.swap(i, j as usize);
}
Self { s, i: 0, j: 0 }
}
fn next_byte(&mut self) -> u8 {
self.i = self.i.wrapping_add(1);
self.j = self.j.wrapping_add(self.s[self.i as usize]);
self.s.swap(self.i as usize, self.j as usize);
let k = self.s[self.i as usize].wrapping_add(self.s[self.j as usize]);
self.s[k as usize]
}
fn apply_keystream(&mut self, data: &mut [u8]) {
for byte in data.iter_mut() {
*byte ^= self.next_byte();
}
}
}
#[cfg(feature = "legacy-crypto")]
pub(crate) fn rc4_crypt_impl(key: &[u8], data: &[u8]) -> Vec<u8> {
let mut cipher = Rc4Cipher::new(key);
let mut result = data.to_vec();
cipher.apply_keystream(&mut result);
result
}
pub fn rc4_crypt(key: &[u8], data: &[u8]) -> crate::Result<Vec<u8>> {
crate::crypto::active()
.symmetric()
.rc4(key, data)
.map_err(|e| match &e {
crate::crypto::Error::AlgorithmNotPermitted { .. } => {
crate::Error::InvalidPdf(format!(
"RC4 rejected by active CryptoProvider '{}': {}. \
RC4 is required for PDF Standard Security R≤4. \
Re-encrypt at R=6 (AES-256) or build pdf_oxide \
without the 'fips' feature so the default \
'rust-crypto' provider stays active.",
crate::crypto::active().name(),
e
))
},
crate::crypto::Error::InvalidInput(s) => {
crate::Error::InvalidPdf(format!("RC4 invalid input: {s}"))
},
crate::crypto::Error::Backend(s) => {
crate::Error::InvalidPdf(format!("RC4 backend error: {s}"))
},
crate::crypto::Error::Verification(s) => {
crate::Error::InvalidPdf(format!("RC4 verification error: {s}"))
},
})
}
#[cfg(all(test, feature = "legacy-crypto"))]
mod tests {
use super::*;
#[test]
fn test_rc4_symmetric() {
let key = b"testkey";
let plaintext = b"Hello, World!";
let ciphertext = rc4_crypt(key, plaintext).unwrap();
let decrypted = rc4_crypt(key, &ciphertext).unwrap();
assert_eq!(plaintext, &decrypted[..]);
assert_ne!(plaintext, &ciphertext[..]);
}
#[test]
fn test_rc4_empty() {
let result = rc4_crypt(b"testkey", b"").unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_rc4_different_keys() {
let plaintext = b"Secret message";
let encrypted1 = rc4_crypt(b"key1", plaintext).unwrap();
let encrypted2 = rc4_crypt(b"key2", plaintext).unwrap();
assert_ne!(encrypted1, encrypted2);
}
#[test]
fn test_rc4_known_vector() {
let key = b"Key";
let plaintext = b"Plaintext";
let ciphertext = rc4_crypt(key, plaintext).unwrap();
assert_ne!(plaintext, &ciphertext[..]);
let decrypted = rc4_crypt(key, &ciphertext).unwrap();
assert_eq!(plaintext, &decrypted[..]);
}
}