pub(crate) struct Rc4 {
s: [u8; 256],
i: u8,
j: u8,
}
impl Rc4 {
pub(crate) fn new(key: &[u8]) -> Self {
let mut s = [0u8; 256];
for (i, slot) in s.iter_mut().enumerate() {
#[allow(clippy::cast_possible_truncation)]
{
*slot = i as u8;
}
}
if !key.is_empty() {
let mut j: u8 = 0;
for i in 0..256usize {
let key_byte = match key.get(i.checked_rem(key.len()).unwrap_or(0)) {
Some(b) => *b,
None => 0,
};
let s_i = match s.get(i) {
Some(b) => *b,
None => 0,
};
j = j.wrapping_add(s_i).wrapping_add(key_byte);
let j_us = usize::from(j);
if let (Some(_), Some(_)) = (s.get(i), s.get(j_us)) {
s.swap(i, j_us);
}
}
}
Self { s, i: 0, j: 0 }
}
pub(crate) fn discard(&mut self, n: usize) {
for _ in 0..n {
self.i = self.i.wrapping_add(1);
let s_i = match self.s.get(usize::from(self.i)) {
Some(b) => *b,
None => 0,
};
self.j = self.j.wrapping_add(s_i);
let i_us = usize::from(self.i);
let j_us = usize::from(self.j);
if let (Some(_), Some(_)) = (self.s.get(i_us), self.s.get(j_us)) {
self.s.swap(i_us, j_us);
}
}
}
pub(crate) fn apply(&mut self, buf: &mut [u8]) {
for byte in buf.iter_mut() {
self.i = self.i.wrapping_add(1);
let s_i = match self.s.get(usize::from(self.i)) {
Some(b) => *b,
None => 0,
};
self.j = self.j.wrapping_add(s_i);
let i_us = usize::from(self.i);
let j_us = usize::from(self.j);
if let (Some(_), Some(_)) = (self.s.get(i_us), self.s.get(j_us)) {
self.s.swap(i_us, j_us);
}
let s_i_new = match self.s.get(i_us) {
Some(b) => *b,
None => 0,
};
let s_j_new = match self.s.get(j_us) {
Some(b) => *b,
None => 0,
};
let k = match self.s.get(usize::from(s_i_new.wrapping_add(s_j_new))) {
Some(b) => *b,
None => 0,
};
*byte ^= k;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rc4_canonical_key_plaintext_vector() {
let mut buf = b"Plaintext".to_vec();
let mut cipher = Rc4::new(b"Key");
cipher.apply(&mut buf);
assert_eq!(
buf,
vec![0xBB, 0xF3, 0x16, 0xE8, 0xD9, 0x40, 0xAF, 0x0A, 0xD3]
);
}
#[test]
fn rc4_canonical_wiki_vector() {
let mut buf = b"pedia".to_vec();
let mut cipher = Rc4::new(b"Wiki");
cipher.apply(&mut buf);
assert_eq!(buf, vec![0x10, 0x21, 0xBF, 0x04, 0x20]);
}
#[test]
fn rc4_canonical_secret_vector() {
let mut buf = b"Attack at dawn".to_vec();
let mut cipher = Rc4::new(b"Secret");
cipher.apply(&mut buf);
assert_eq!(
buf,
vec![
0x45, 0xA0, 0x1F, 0x64, 0x5F, 0xC3, 0x5B, 0x38, 0x35, 0x52, 0x54, 0x4B, 0x9B, 0xF5,
]
);
}
#[test]
fn rc4_round_trip() {
let original = b"Inno test payload v1\n";
let mut buf = original.to_vec();
Rc4::new(b"Key").apply(&mut buf);
assert_ne!(buf.as_slice(), original);
Rc4::new(b"Key").apply(&mut buf);
assert_eq!(buf.as_slice(), original);
}
#[test]
fn rc4_streaming_matches_one_shot() {
let one_shot = {
let mut buf = b"hello world hello world".to_vec();
Rc4::new(b"k").apply(&mut buf);
buf
};
let streamed = {
let mut buf = b"hello world hello world".to_vec();
let mut cipher = Rc4::new(b"k");
let mid = buf.len() / 2;
let (left, right) = buf.split_at_mut(mid);
cipher.apply(left);
cipher.apply(right);
buf
};
assert_eq!(one_shot, streamed);
}
}