#![no_std]
#![cfg_attr(not(feature="std"), feature(error_in_core))]
extern crate alloc;
use alloc::vec::Vec;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("invalid ciphertext size. The length should be a multiple of {blocksize}, but the length is {found}")]
WrongSize { blocksize: usize, found: usize },
#[error(
"couldn't decrypt the data. Make sure your oracle is valid and that PKCS7 padding is used"
)]
InvalidPadding,
}
type Result<T> = core::result::Result<T, Error>;
pub fn decrypt(ciphertext: &[u8], blocksize: usize, oracle: fn(&[u8]) -> bool) -> Result<Vec<u8>> {
if ciphertext.len() % blocksize != 0 {
return Err(Error::WrongSize {
blocksize,
found: ciphertext.len(),
});
}
let mut plaintext = b"".to_vec();
let mut ciphertext = ciphertext.to_vec();
for _ in 0..ciphertext.len() / blocksize - 1 {
for i in 1..=blocksize {
let offset = ciphertext.len() - blocksize - i;
let initial_byte = ciphertext[offset];
let mut ciphertext = ciphertext.to_vec();
for j in 1..i {
ciphertext[offset + j] = i as u8 ^ plaintext[j - 1] ^ ciphertext[offset + j];
}
match (0..=255u8).find_map(|k| {
ciphertext[offset] = k;
if oracle(&ciphertext) {
if offset % blocksize == 0 || {
let mut ciphertext = ciphertext.clone();
ciphertext[offset - 1] = !ciphertext[offset - 1];
oracle(&ciphertext)
} {
return Some(k);
};
}
None
}) {
Some(k) => plaintext.insert(0, initial_byte ^ k ^ i as u8),
None => return Err(Error::InvalidPadding)
}
}
ciphertext.truncate(ciphertext.len() - blocksize);
}
Ok(plaintext)
}