#![forbid(unsafe_code)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
const RA: [u32; 4] = [9, 0, 13, 0];
const RB: [u32; 4] = [0, 10, 0, 25];
fn to_words(sector: &[u8]) -> Vec<u32> {
sector
.chunks_exact(4)
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect()
}
fn from_words(words: &[u32], out: &mut [u8]) {
for (i, w) in words.iter().enumerate() {
if let Some(slot) = out.get_mut(i * 4..i * 4 + 4) {
slot.copy_from_slice(&w.to_le_bytes());
}
}
}
#[inline]
fn back(i: usize, k: usize, n: usize) -> usize {
(i + n - (k % n)) % n
}
fn diffuser_a_decrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..5 {
for i in 0..n {
let a = d[back(i, 2, n)];
let b = d[back(i, 5, n)].rotate_left(RA[i % 4]);
d[i] = d[i].wrapping_add(a ^ b);
}
}
from_words(&d, sector);
}
fn diffuser_b_decrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..3 {
for i in 0..n {
let a = d[(i + 2) % n];
let b = d[(i + 5) % n].rotate_left(RB[i % 4]);
d[i] = d[i].wrapping_add(a ^ b);
}
}
from_words(&d, sector);
}
fn diffuser_a_encrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..5 {
for i in (0..n).rev() {
let a = d[back(i, 2, n)];
let b = d[back(i, 5, n)].rotate_left(RA[i % 4]);
d[i] = d[i].wrapping_sub(a ^ b);
}
}
from_words(&d, sector);
}
fn diffuser_b_encrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..3 {
for i in (0..n).rev() {
let a = d[(i + 2) % n];
let b = d[(i + 5) % n].rotate_left(RB[i % 4]);
d[i] = d[i].wrapping_sub(a ^ b);
}
}
from_words(&d, sector);
}
pub fn decrypt(sector: &mut [u8], sector_key: &[u8; 32]) {
diffuser_b_decrypt(sector);
diffuser_a_decrypt(sector);
for (i, b) in sector.iter_mut().enumerate() {
*b ^= sector_key[i % 32];
}
}
pub fn encrypt(sector: &mut [u8], sector_key: &[u8; 32]) {
for (i, b) in sector.iter_mut().enumerate() {
*b ^= sector_key[i % 32];
}
diffuser_a_encrypt(sector);
diffuser_b_encrypt(sector);
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(bytes: &[u8]) -> String {
use std::fmt::Write;
bytes.iter().fold(String::new(), |mut s, b| {
let _ = write!(s, "{b:02x}");
s
})
}
fn sample_sector() -> Vec<u8> {
(0..512u32)
.map(|i| (i.wrapping_mul(31) ^ 0xA5) as u8)
.collect()
}
fn sample_key() -> [u8; 32] {
let mut k = [0u8; 32];
for (i, b) in k.iter_mut().enumerate() {
*b = i as u8;
}
k
}
#[test]
fn decrypt_matches_captured_regression_vector() {
let mut buf = sample_sector();
decrypt(&mut buf, &sample_key());
assert_eq!(
hex(&buf[..32]),
"9649e3f15c8ecdb6fceb5a864f24e97596052689bf414d5c3137edb27dc43c6e"
);
assert_eq!(hex(&buf[496..512]), "21eafdd00ad4826068a2d7a8f28fcf97");
}
#[test]
fn encrypt_decrypt_roundtrip_is_identity() {
let key = sample_key();
let orig = sample_sector();
let mut buf = orig.clone();
encrypt(&mut buf, &key);
assert_ne!(buf, orig);
decrypt(&mut buf, &key);
assert_eq!(buf, orig);
}
#[test]
fn empty_and_subword_inputs_do_not_panic() {
let key = sample_key();
let mut empty: [u8; 0] = [];
decrypt(&mut empty, &key);
encrypt(&mut empty, &key);
let mut three = [1u8, 2, 3]; decrypt(&mut three, &key);
encrypt(&mut three, &key);
}
#[test]
fn tiny_sector_word_counts_do_not_panic() {
let key = sample_key();
for words in 1..=6usize {
let mut buf = vec![0xABu8; words * 4];
decrypt(&mut buf, &key);
let mut buf2 = vec![0xABu8; words * 4];
encrypt(&mut buf2, &key);
}
}
}