pub mod r5_r6;
use crate::error::PdfError;
use crate::objects::{Object, ObjectId};
const PAD: [u8; 32] = [
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
];
const AES_SALT: [u8; 4] = [0x73, 0x41, 0x6C, 0x54];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CryptMethod {
Rc4,
Aes128,
Aes256,
}
#[derive(Debug, Clone)]
pub struct StandardHandler {
pub key: Vec<u8>,
pub method: CryptMethod,
pub revision: u8,
}
impl StandardHandler {
pub fn decrypt_object(&self, id: ObjectId, data: &[u8]) -> Result<Vec<u8>, PdfError> {
match self.method {
CryptMethod::Rc4 => {
let obj_key = self.object_key(id);
Ok(rc4(&obj_key, data))
}
CryptMethod::Aes128 => {
let obj_key = self.object_key(id);
aes128_cbc_decrypt(&obj_key, data)
}
CryptMethod::Aes256 => aes256_cbc_decrypt(&self.key, data),
}
}
pub fn encrypt_object(
&self,
id: ObjectId,
data: &[u8],
iv: &[u8; 16],
) -> Result<Vec<u8>, PdfError> {
match self.method {
CryptMethod::Rc4 => {
let obj_key = self.object_key(id);
Ok(rc4(&obj_key, data))
}
CryptMethod::Aes128 => {
let obj_key = self.object_key(id);
aes128_cbc_encrypt(&obj_key, data, iv)
}
CryptMethod::Aes256 => aes256_cbc_encrypt(&self.key, data, iv),
}
}
fn object_key(&self, id: ObjectId) -> Vec<u8> {
let n = self.key.len();
let extra = if self.method == CryptMethod::Aes128 {
5 + 4
} else {
5
};
let mut buf = Vec::with_capacity(n + extra);
buf.extend_from_slice(&self.key);
buf.push((id.number & 0xFF) as u8);
buf.push(((id.number >> 8) & 0xFF) as u8);
buf.push(((id.number >> 16) & 0xFF) as u8);
buf.push((id.generation & 0xFF) as u8);
buf.push(((id.generation >> 8) & 0xFF) as u8);
if self.method == CryptMethod::Aes128 {
buf.extend_from_slice(&AES_SALT);
}
let h = md5(&buf);
let take = (n + 5).min(16);
h[..take].to_vec()
}
}
pub fn open_with_password(
encrypt: &crate::objects::Dict,
file_id: &[u8],
password: &[u8],
) -> Result<Option<StandardHandler>, PdfError> {
let params = parse_encrypt_dict(encrypt)?;
if params.revision == 5 || params.revision == 6 {
return r5_r6::open_with_password(¶ms, password);
}
if let Some(handler) = try_user_password(¶ms, file_id, password) {
return Ok(Some(handler));
}
if let Some(handler) = try_owner_password(¶ms, file_id, password) {
return Ok(Some(handler));
}
Ok(None)
}
#[derive(Debug, Clone)]
pub(crate) struct EncryptParams {
pub(crate) revision: u8,
pub(crate) length_bits: usize,
pub(crate) o: Vec<u8>,
pub(crate) u: Vec<u8>,
pub(crate) oe: Vec<u8>,
pub(crate) ue: Vec<u8>,
pub(crate) perms: Vec<u8>,
pub(crate) p: i32,
pub(crate) encrypt_metadata: bool,
pub(crate) cfm: CryptMethod,
}
fn parse_encrypt_dict(d: &crate::objects::Dict) -> Result<EncryptParams, PdfError> {
fn lookup<'a>(d: &'a crate::objects::Dict, key: &str) -> Option<&'a crate::objects::Object> {
d.entries().iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
match lookup(d, "Filter") {
Some(Object::Name(s)) if s == "Standard" => {}
Some(other) => {
return Err(PdfError::other(format!(
"PDF decrypt: only the Standard security handler is supported (got Filter={other:?})"
)))
}
None => return Err(PdfError::other("PDF decrypt: /Encrypt missing /Filter")),
}
let v = match lookup(d, "V") {
Some(Object::Integer(n)) => *n,
_ => return Err(PdfError::other("PDF decrypt: /Encrypt missing /V")),
};
let r = match lookup(d, "R") {
Some(Object::Integer(n)) => *n,
_ => return Err(PdfError::other("PDF decrypt: /Encrypt missing /R")),
};
if !(2..=6).contains(&r) {
return Err(PdfError::other(format!(
"PDF decrypt: revision R={r} not supported (R∈[2,6])"
)));
}
let length_bits_default = if v >= 5 { 256 } else { 40 };
let length_bits = match lookup(d, "Length") {
Some(Object::Integer(n)) => *n as usize,
_ => length_bits_default,
};
let length_ok = if v >= 5 {
length_bits == 256
} else {
(40..=128).contains(&length_bits) && length_bits % 8 == 0
};
if !length_ok {
return Err(PdfError::other(format!(
"PDF decrypt: /Length {length_bits} bits invalid for V={v} (V≤2: 40..=128 multiple of 8; V=5: must be 256)"
)));
}
let o = match lookup(d, "O") {
Some(Object::LiteralString(s)) | Some(Object::HexString(s)) => s.clone(),
_ => return Err(PdfError::other("PDF decrypt: /Encrypt missing /O")),
};
let u = match lookup(d, "U") {
Some(Object::LiteralString(s)) | Some(Object::HexString(s)) => s.clone(),
_ => return Err(PdfError::other("PDF decrypt: /Encrypt missing /U")),
};
let o_expected = if r >= 5 { 48 } else { 32 };
let u_expected = if r >= 5 { 48 } else { 32 };
if o.len() != o_expected {
return Err(PdfError::other(format!(
"PDF decrypt: /O must be {o_expected} bytes for R={r} (got {})",
o.len()
)));
}
if u.len() != u_expected {
return Err(PdfError::other(format!(
"PDF decrypt: /U must be {u_expected} bytes for R={r} (got {})",
u.len()
)));
}
let p = match lookup(d, "P") {
Some(Object::Integer(n)) => *n as i32,
_ => return Err(PdfError::other("PDF decrypt: /Encrypt missing /P")),
};
let encrypt_metadata = match lookup(d, "EncryptMetadata") {
Some(Object::Bool(b)) => *b,
_ => true,
};
let (oe, ue, perms) = if r >= 5 {
let oe = match lookup(d, "OE") {
Some(Object::LiteralString(s)) | Some(Object::HexString(s)) => s.clone(),
_ => return Err(PdfError::other("PDF decrypt: V=5 /Encrypt missing /OE")),
};
let ue = match lookup(d, "UE") {
Some(Object::LiteralString(s)) | Some(Object::HexString(s)) => s.clone(),
_ => return Err(PdfError::other("PDF decrypt: V=5 /Encrypt missing /UE")),
};
let perms = match lookup(d, "Perms") {
Some(Object::LiteralString(s)) | Some(Object::HexString(s)) => s.clone(),
_ => return Err(PdfError::other("PDF decrypt: V=5 /Encrypt missing /Perms")),
};
if oe.len() != 32 {
return Err(PdfError::other(format!(
"PDF decrypt: /OE must be 32 bytes (got {})",
oe.len()
)));
}
if ue.len() != 32 {
return Err(PdfError::other(format!(
"PDF decrypt: /UE must be 32 bytes (got {})",
ue.len()
)));
}
if perms.len() != 16 {
return Err(PdfError::other(format!(
"PDF decrypt: /Perms must be 16 bytes (got {})",
perms.len()
)));
}
(oe, ue, perms)
} else {
(Vec::new(), Vec::new(), Vec::new())
};
let cfm = match (v, r) {
(1, _) | (2, _) | (_, 2) | (_, 3) => CryptMethod::Rc4,
(4, _) | (5, _) => {
let stmf = match lookup(d, "StmF") {
Some(Object::Name(s)) => s.as_str(),
_ => "Identity",
};
if stmf == "Identity" {
if v >= 5 {
CryptMethod::Aes256
} else {
CryptMethod::Rc4
}
} else {
let cf = lookup(d, "CF").ok_or_else(|| {
PdfError::other("PDF decrypt: V=4/V=5 /Encrypt missing /CF dictionary")
})?;
let Object::Dict(cf_dict) = cf else {
return Err(PdfError::other("PDF decrypt: /CF must be a dictionary"));
};
let filter = lookup(cf_dict, stmf).ok_or_else(|| {
PdfError::other(format!("PDF decrypt: /CF missing crypt filter `{stmf}`"))
})?;
let Object::Dict(filter_dict) = filter else {
return Err(PdfError::other(format!(
"PDF decrypt: /CF/{stmf} must be a dict"
)));
};
match lookup(filter_dict, "CFM") {
Some(Object::Name(s)) if s == "V2" => CryptMethod::Rc4,
Some(Object::Name(s)) if s == "AESV2" => CryptMethod::Aes128,
Some(Object::Name(s)) if s == "AESV3" => CryptMethod::Aes256,
Some(Object::Name(s)) if s == "None" => {
return Err(PdfError::other(
"PDF decrypt: CFM=None requires a custom security handler",
))
}
Some(other) => {
return Err(PdfError::other(format!(
"PDF decrypt: unsupported CFM={other:?}"
)))
}
None => {
if v >= 5 {
CryptMethod::Aes256
} else {
CryptMethod::Rc4
}
}
}
}
}
_ => {
return Err(PdfError::other(format!(
"PDF decrypt: V={v} not supported (handler accepts V∈[1,2,4,5])"
)))
}
};
Ok(EncryptParams {
revision: r as u8,
length_bits,
o,
u,
oe,
ue,
perms,
p,
encrypt_metadata,
cfm,
})
}
fn compute_key(p: &EncryptParams, file_id: &[u8], password: &[u8]) -> Vec<u8> {
let n = p.length_bits / 8;
let pwd = pad_password(password);
let mut buf = Vec::with_capacity(32 + 32 + 4 + file_id.len() + 4);
buf.extend_from_slice(&pwd);
buf.extend_from_slice(&p.o);
let pbytes = (p.p as u32).to_le_bytes();
buf.extend_from_slice(&pbytes);
buf.extend_from_slice(file_id);
if p.revision >= 4 && !p.encrypt_metadata {
buf.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]);
}
let mut h = md5(&buf);
if p.revision >= 3 {
for _ in 0..50 {
h = md5(&h[..n]);
}
}
h[..n].to_vec()
}
fn pad_password(password: &[u8]) -> [u8; 32] {
let mut out = [0u8; 32];
let take = password.len().min(32);
out[..take].copy_from_slice(&password[..take]);
if take < 32 {
out[take..].copy_from_slice(&PAD[..32 - take]);
}
out
}
fn try_user_password(
p: &EncryptParams,
file_id: &[u8],
password: &[u8],
) -> Option<StandardHandler> {
let key = compute_key(p, file_id, password);
let derived_u = derive_u(p, file_id, &key);
let cmp_len = if p.revision >= 3 { 16 } else { 32 };
if constant_time_eq(&derived_u[..cmp_len], &p.u[..cmp_len]) {
Some(StandardHandler {
key,
method: p.cfm,
revision: p.revision,
})
} else {
None
}
}
fn derive_u(p: &EncryptParams, file_id: &[u8], key: &[u8]) -> [u8; 32] {
if p.revision == 2 {
let cipher = rc4(key, &PAD);
let mut out = [0u8; 32];
out.copy_from_slice(&cipher);
out
} else {
let mut hash_input = Vec::with_capacity(32 + file_id.len());
hash_input.extend_from_slice(&PAD);
hash_input.extend_from_slice(file_id);
let h = md5(&hash_input);
let mut data = rc4(key, &h);
for i in 1u8..=19 {
let xor_key: Vec<u8> = key.iter().map(|b| b ^ i).collect();
data = rc4(&xor_key, &data);
}
let mut out = [0u8; 32];
out[..16].copy_from_slice(&data[..16]);
out
}
}
fn try_owner_password(
p: &EncryptParams,
file_id: &[u8],
password: &[u8],
) -> Option<StandardHandler> {
let n = p.length_bits / 8;
let pwd = pad_password(password);
let mut h = md5(&pwd);
if p.revision >= 3 {
for _ in 0..50 {
h = md5(&h[..n]);
}
}
let owner_key = h[..n].to_vec();
let recovered_user_pwd = if p.revision == 2 {
rc4(&owner_key, &p.o)
} else {
let mut buf = p.o.clone();
for i in (0u8..=19).rev() {
let xor_key: Vec<u8> = owner_key.iter().map(|b| b ^ i).collect();
buf = rc4(&xor_key, &buf);
}
buf
};
let user_pwd = strip_pad(&recovered_user_pwd);
try_user_password(p, file_id, &user_pwd)
}
fn strip_pad(padded: &[u8]) -> Vec<u8> {
let n = padded.len();
for l in 0..=n {
if padded[l..] == PAD[..n - l] {
return padded[..l].to_vec();
}
}
padded.to_vec()
}
pub(crate) fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
diff |= x ^ y;
}
diff == 0
}
pub fn rc4(key: &[u8], data: &[u8]) -> Vec<u8> {
debug_assert!(!key.is_empty(), "RC4 key must not be empty");
let mut s: [u8; 256] = [0; 256];
for (i, b) in s.iter_mut().enumerate() {
*b = i as u8;
}
let mut j: u8 = 0;
for i in 0..256 {
j = j.wrapping_add(s[i]).wrapping_add(key[i % key.len()]);
s.swap(i, j as usize);
}
let mut i: u8 = 0;
j = 0;
let mut out = Vec::with_capacity(data.len());
for &byte in data {
i = i.wrapping_add(1);
j = j.wrapping_add(s[i as usize]);
s.swap(i as usize, j as usize);
let k = s[s[i as usize].wrapping_add(s[j as usize]) as usize];
out.push(byte ^ k);
}
out
}
pub(crate) fn aes256_ecb_decrypt_block(key: &[u8], block: &[u8; 16]) -> Result<[u8; 16], PdfError> {
use aes::cipher::{BlockDecrypt, KeyInit};
if key.len() != 32 {
return Err(PdfError::other(format!(
"PDF decrypt: AES-256 requires a 32-byte key (got {} bytes)",
key.len()
)));
}
let cipher = aes::Aes256::new(key.into());
let mut buf = aes::cipher::generic_array::GenericArray::clone_from_slice(block);
cipher.decrypt_block(&mut buf);
let mut out = [0u8; 16];
out.copy_from_slice(&buf);
Ok(out)
}
fn aes256_cbc_decrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, PdfError> {
use aes::cipher::{BlockDecryptMut, KeyIvInit};
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
if data.len() < 16 {
return Err(PdfError::other(
"PDF decrypt: AES-256 ciphertext shorter than IV",
));
}
if (data.len() - 16) % 16 != 0 {
return Err(PdfError::other(format!(
"PDF decrypt: AES-256 ciphertext length {} not aligned to 16-byte blocks (after IV)",
data.len() - 16
)));
}
if key.len() != 32 {
return Err(PdfError::other(format!(
"PDF decrypt: AES-256 expects a 32-byte key (got {} bytes)",
key.len()
)));
}
let iv = &data[..16];
let ct = &data[16..];
let dec = Aes256CbcDec::new(key.into(), iv.into());
let mut buf = ct.to_vec();
let pt = dec
.decrypt_padded_mut::<aes::cipher::block_padding::Pkcs7>(&mut buf)
.map_err(|e| PdfError::other(format!("PDF decrypt: AES-256 padding error: {e:?}")))?;
Ok(pt.to_vec())
}
fn aes256_cbc_encrypt(key: &[u8], data: &[u8], iv: &[u8; 16]) -> Result<Vec<u8>, PdfError> {
use aes::cipher::{BlockEncryptMut, KeyIvInit};
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
if key.len() != 32 {
return Err(PdfError::other(format!(
"PDF encrypt: AES-256 expects a 32-byte key (got {} bytes)",
key.len()
)));
}
let enc = Aes256CbcEnc::new(key.into(), iv.into());
let pad_block = (data.len() / 16) + 1;
let mut buf = vec![0u8; pad_block * 16];
let n = enc
.encrypt_padded_b2b_mut::<aes::cipher::block_padding::Pkcs7>(data, &mut buf)
.map_err(|e| PdfError::other(format!("PDF encrypt: AES-256 padding error: {e:?}")))?
.len();
buf.truncate(n);
let mut out = Vec::with_capacity(16 + n);
out.extend_from_slice(iv);
out.extend_from_slice(&buf);
Ok(out)
}
fn aes128_cbc_encrypt(key: &[u8], data: &[u8], iv: &[u8; 16]) -> Result<Vec<u8>, PdfError> {
use aes::cipher::{BlockEncryptMut, KeyIvInit};
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
if key.len() != 16 {
return Err(PdfError::other(format!(
"PDF encrypt: AES-128 expects a 16-byte key (got {} bytes)",
key.len()
)));
}
let enc = Aes128CbcEnc::new(key.into(), iv.into());
let pad_block = (data.len() / 16) + 1;
let mut buf = vec![0u8; pad_block * 16];
let n = enc
.encrypt_padded_b2b_mut::<aes::cipher::block_padding::Pkcs7>(data, &mut buf)
.map_err(|e| PdfError::other(format!("PDF encrypt: AES-128 padding error: {e:?}")))?
.len();
buf.truncate(n);
let mut out = Vec::with_capacity(16 + n);
out.extend_from_slice(iv);
out.extend_from_slice(&buf);
Ok(out)
}
fn aes128_cbc_decrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, PdfError> {
use aes::cipher::{BlockDecryptMut, KeyIvInit};
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
if data.len() < 16 {
return Err(PdfError::other(
"PDF decrypt: AES-128 ciphertext shorter than IV",
));
}
if (data.len() - 16) % 16 != 0 {
return Err(PdfError::other(format!(
"PDF decrypt: AES-128 ciphertext length {} not aligned to 16-byte blocks (after IV)",
data.len() - 16
)));
}
if key.len() != 16 {
return Err(PdfError::other(format!(
"PDF decrypt: AES-128 expects a 16-byte key (got {} bytes)",
key.len()
)));
}
let iv = &data[..16];
let ct = &data[16..];
let dec = Aes128CbcDec::new(key.into(), iv.into());
let mut buf = ct.to_vec();
let pt = dec
.decrypt_padded_mut::<aes::cipher::block_padding::Pkcs7>(&mut buf)
.map_err(|e| PdfError::other(format!("PDF decrypt: AES-128 padding error: {e:?}")))?;
Ok(pt.to_vec())
}
const MD5_S: [u32; 64] = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, ];
const MD5_K: [u32; 64] = [
0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391,
];
pub fn md5(input: &[u8]) -> [u8; 16] {
let mut state: [u32; 4] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476];
let mut buf = input.to_vec();
let bit_len = (input.len() as u64).wrapping_mul(8);
buf.push(0x80);
while buf.len() % 64 != 56 {
buf.push(0);
}
buf.extend_from_slice(&bit_len.to_le_bytes());
for chunk in buf.chunks_exact(64) {
let mut m = [0u32; 16];
for (i, w) in chunk.chunks_exact(4).enumerate() {
m[i] = u32::from_le_bytes([w[0], w[1], w[2], w[3]]);
}
let mut a = state[0];
let mut b = state[1];
let mut c = state[2];
let mut d = state[3];
for i in 0..64 {
let (f, g) = match i {
0..=15 => ((b & c) | (!b & d), i),
16..=31 => ((d & b) | (!d & c), (5 * i + 1) % 16),
32..=47 => (b ^ c ^ d, (3 * i + 5) % 16),
_ => (c ^ (b | !d), (7 * i) % 16),
};
let temp = d;
d = c;
c = b;
b = b.wrapping_add(
a.wrapping_add(f)
.wrapping_add(MD5_K[i])
.wrapping_add(m[g])
.rotate_left(MD5_S[i]),
);
a = temp;
}
state[0] = state[0].wrapping_add(a);
state[1] = state[1].wrapping_add(b);
state[2] = state[2].wrapping_add(c);
state[3] = state[3].wrapping_add(d);
}
let mut out = [0u8; 16];
for (i, s) in state.iter().enumerate() {
out[4 * i..4 * (i + 1)].copy_from_slice(&s.to_le_bytes());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn md5_empty_string() {
assert_eq!(
md5(b""),
[
0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8,
0x42, 0x7E
]
);
}
#[test]
fn md5_short_inputs() {
assert_eq!(
md5(b"a"),
[
0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77,
0x26, 0x61
]
);
assert_eq!(
md5(b"abc"),
[
0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1,
0x7F, 0x72
]
);
assert_eq!(
md5(b"message digest"),
[
0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1,
0x61, 0xD0
]
);
}
#[test]
fn md5_long_block() {
assert_eq!(
md5(b"abcdefghijklmnopqrstuvwxyz"),
[
0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67,
0xE1, 0x3B
]
);
}
#[test]
fn md5_multi_block() {
let s = b"12345678901234567890123456789012345678901234567890123456789012345678901234567890";
assert_eq!(
md5(s),
[
0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07,
0xB6, 0x7A
]
);
}
#[test]
fn rc4_rfc6229_key0102030405() {
let key = [0x01, 0x02, 0x03, 0x04, 0x05];
let pt = [0u8; 16];
let ct = rc4(&key, &pt);
assert_eq!(
ct,
vec![
0xB2, 0x39, 0x63, 0x05, 0xF0, 0x3D, 0xC0, 0x27, 0xCC, 0xC3, 0x52, 0x4A, 0x0A, 0x11,
0x18, 0xA8
]
);
}
#[test]
fn rc4_rfc6229_key0102030405060708() {
let key = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let pt = [0u8; 16];
let ct = rc4(&key, &pt);
assert_eq!(
ct,
vec![
0x97, 0xAB, 0x8A, 0x1B, 0xF0, 0xAF, 0xB9, 0x61, 0x32, 0xF2, 0xF6, 0x72, 0x58, 0xDA,
0x15, 0xA8
]
);
}
#[test]
fn rc4_self_inverse() {
let key = b"Key";
let plain = b"Plaintext";
let cipher = rc4(key, plain);
let recovered = rc4(key, &cipher);
assert_eq!(&recovered, plain);
}
#[test]
fn aes128_cbc_decrypt_round_trips_pkcs7() {
use aes::cipher::{BlockEncryptMut, KeyIvInit};
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
let key = [0x42u8; 16];
let iv = [0x17u8; 16];
let pt = b"hello world".to_vec();
let mut buf = vec![0u8; 16 + ((pt.len() / 16) + 1) * 16];
let enc = Aes128CbcEnc::new((&key).into(), (&iv).into());
let n = enc
.encrypt_padded_b2b_mut::<aes::cipher::block_padding::Pkcs7>(&pt, &mut buf[16..])
.unwrap()
.len();
buf[..16].copy_from_slice(&iv);
buf.truncate(16 + n);
let recovered = aes128_cbc_decrypt(&key, &buf).unwrap();
assert_eq!(recovered, pt);
}
#[test]
fn algorithm_2_known_answer_r3_empty_password() {
let p = EncryptParams {
revision: 3,
length_bits: 128,
o: vec![0xAA; 32],
u: vec![0; 32],
oe: Vec::new(),
ue: Vec::new(),
perms: Vec::new(),
p: -4,
encrypt_metadata: true,
cfm: CryptMethod::Rc4,
};
let id = vec![0xBB; 16];
let k = compute_key(&p, &id, b"");
let mut buf = Vec::new();
buf.extend_from_slice(&PAD);
buf.extend_from_slice(&p.o);
buf.extend_from_slice(&((-4i32) as u32).to_le_bytes());
buf.extend_from_slice(&id);
let mut h = md5(&buf);
for _ in 0..50 {
h = md5(&h[..16]);
}
assert_eq!(k, h[..16].to_vec());
}
#[test]
fn algorithm_1_object_key_rc4() {
let h = StandardHandler {
key: vec![0x01, 0x02, 0x03, 0x04, 0x05], method: CryptMethod::Rc4,
revision: 2,
};
let id = ObjectId {
number: 0x010203,
generation: 0x0405,
};
let k = h.object_key(id);
assert_eq!(k.len(), 10);
let mut buf = Vec::new();
buf.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05]);
buf.extend_from_slice(&[0x03, 0x02, 0x01]); buf.extend_from_slice(&[0x05, 0x04]); let h2 = md5(&buf);
assert_eq!(k, h2[..10].to_vec());
}
#[test]
fn algorithm_1_object_key_aes_appends_salt() {
let h = StandardHandler {
key: vec![0u8; 16],
method: CryptMethod::Aes128,
revision: 4,
};
let id = ObjectId {
number: 1,
generation: 0,
};
let k = h.object_key(id);
assert_eq!(k.len(), 16);
let mut buf = Vec::new();
buf.extend_from_slice(&h.key);
buf.extend_from_slice(&[0x01, 0x00, 0x00, 0x00, 0x00]);
buf.extend_from_slice(&AES_SALT);
let h2 = md5(&buf);
assert_eq!(k, h2[..16].to_vec());
}
#[test]
fn rc4_object_roundtrip_via_handler() {
let h = StandardHandler {
key: vec![0xDE, 0xAD, 0xBE, 0xEF, 0x42],
method: CryptMethod::Rc4,
revision: 2,
};
let id = ObjectId {
number: 7,
generation: 0,
};
let plain = b"Hello, encrypted world!".to_vec();
let cipher = h.decrypt_object(id, &plain).unwrap();
let recovered = h.decrypt_object(id, &cipher).unwrap();
assert_eq!(recovered, plain);
}
#[test]
fn strip_pad_recovers_short_passwords() {
let mut padded = Vec::from(b"hello".as_slice());
padded.extend_from_slice(&PAD[..27]);
assert_eq!(strip_pad(&padded), b"hello".to_vec());
}
#[test]
fn strip_pad_empty_password() {
assert_eq!(strip_pad(&PAD), Vec::<u8>::new());
}
}