Skip to content

Commit

Permalink
Fix mulitplication overflow in ascii85 decode (#348)
Browse files Browse the repository at this point in the history
  • Loading branch information
Heinenen authored Nov 17, 2024
1 parent 668849e commit 2e4e977
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 22 deletions.
18 changes: 5 additions & 13 deletions src/encryption.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
use crate::rc4::Rc4;
use crate::{Document, Object, ObjectId};
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
use md5::{Digest as _, Md5};
use std::fmt;
use aes::cipher::{
block_padding::Pkcs7,
BlockDecryptMut,
KeyIvInit
};

#[derive(Debug)]
pub enum DecryptionError {
Expand Down Expand Up @@ -228,11 +224,7 @@ where
Key: AsRef<[u8]>,
{
let key = key.as_ref();
let len = if aes {
key.len() + 9
} else {
key.len() + 5
};
let len = if aes { key.len() + 9 } else { key.len() + 5 };
let mut builder = Vec::<u8>::with_capacity(len);
builder.extend_from_slice(key.as_ref());

Expand All @@ -242,10 +234,10 @@ where
builder.extend_from_slice(&obj_id.1.to_le_bytes()[..2]);

if aes {
builder.append(&mut vec![0x73, 0x41, 0x6C, 0x54]);
}
builder.append(&mut vec![0x73, 0x41, 0x6C, 0x54]);
}

// Now construct the rc4 key
// Now construct the rc4 key
let key_len = std::cmp::min(key.len() + 5, 16);
let rc4_key = &Md5::digest(builder)[..key_len];

Expand Down
53 changes: 44 additions & 9 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ impl Stream {
output = match filter.as_str() {
"FlateDecode" => Self::decompress_zlib(input, params)?,
"LZWDecode" => Self::decompress_lzw(input, params)?,
"ASCII85Decode" => Self::decode_ascii85(input),
"ASCII85Decode" => Self::decode_ascii85(input)?,
_ => return Err(Error::Type),
};
input = &output;
Expand Down Expand Up @@ -705,25 +705,31 @@ impl Stream {
Self::decompress_predictor(output, params)
}

fn decode_ascii85(input: &[u8]) -> Vec<u8> {
fn decode_ascii85(input: &[u8]) -> Result<Vec<u8>> {
let mut output = vec![];
let mut buffer: u32 = 0;
let mut count = 0;

for &ch in input {
if ch == b'z' && count == 0 {
// Check for EOD marker
assert_eq!(b"~>", &input[input.len() - 2..]);
for &ch in &input[..input.len() - 2] {
if ch == b'z' {
if count != 0 {
// z character is not allowed in middle of a group
return Err(Error::ContentDecode);
}
output.extend_from_slice(&[0, 0, 0, 0]);
continue;
}

if ch.is_ascii_whitespace() {
continue;
}

if !(b'!'..=b'u').contains(&ch) {
break;
}

buffer = buffer * 85 + ((ch - b'!') as u32);
buffer = buffer.checked_mul(85).ok_or(Error::ContentDecode)?;
buffer += (ch - b'!') as u32;
count += 1;

if count == 5 {
Expand All @@ -735,14 +741,15 @@ impl Stream {

if count > 0 {
for _ in count..5 {
buffer = buffer * 85 + 84;
buffer = buffer.checked_mul(85).ok_or(Error::ContentDecode)?;
buffer += 84;
}

let bytes = buffer.to_be_bytes();
output.extend_from_slice(&bytes[..count - 1]);
}

output
Ok(output)
}

fn decompress_predictor(mut data: Vec<u8>, params: Option<&Dictionary>) -> Result<Vec<u8>> {
Expand Down Expand Up @@ -775,3 +782,31 @@ impl Stream {
self.dict.get(b"Filter").is_ok()
}
}

#[cfg(test)]
mod test {
use crate::Error;

use super::Stream;

#[test]
fn test_decode_ascii85() {
let input = r#"9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF<GL>[email protected]$d7F!,L7@<6@)/0JDEF<G%<+EV:2F!,O<
DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF"AGXBPCsi+DGm>@3BB/F*&OCAfu2/AKYi(
DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF<G:8+EV:.+Cf>-FD5W8ARlolDIal(
DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G>u
D.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>"#;
let expected = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
let output = Stream::decode_ascii85(input.as_bytes()).unwrap();
println!("{}", String::from_utf8(output.clone()).unwrap());
assert_eq!(&output, expected.as_bytes());
}

#[test]
fn test_decode_ascii85_overflow() {
let input = b"uuuuu~>";
let output = Stream::decode_ascii85(input);
// let expected: Result<Vec<u8>, Error> = Err(Error::ContentDecode);
assert!(matches!(output, Err(Error::ContentDecode)));
}
}

0 comments on commit 2e4e977

Please sign in to comment.