Skip to content

Commit

Permalink
Merge pull request #143 from agourlay/expose-aes-info
Browse files Browse the repository at this point in the history
feat: Expose AES information
  • Loading branch information
Pr0methean committed May 23, 2024
2 parents da8be86 + 492a6b9 commit 6d1b5f7
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
21 changes: 20 additions & 1 deletion src/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::io::{self, Error, ErrorKind, Read, Write};
use zeroize::{Zeroize, Zeroizing};

/// The length of the password verifcation value in bytes
const PWD_VERIFY_LENGTH: usize = 2;
pub const PWD_VERIFY_LENGTH: usize = 2;
/// The length of the authentication code in bytes
const AUTH_CODE_LENGTH: usize = 10;
/// The number of iterations used with PBKDF2
Expand Down Expand Up @@ -124,6 +124,25 @@ impl<R: Read> AesReader<R> {
finalized: false,
})
}

/// Read the AES header bytes and returns the verification value and salt.
///
/// # Returns
///
/// the verification value and the salt
pub fn get_verification_value_and_salt(
mut self,
) -> io::Result<([u8; PWD_VERIFY_LENGTH], Vec<u8>)> {
let salt_length = self.aes_mode.salt_length();

let mut salt = vec![0; salt_length];
self.reader.read_exact(&mut salt)?;

// next are 2 bytes used for password verification
let mut pwd_verification_value = [0; PWD_VERIFY_LENGTH];
self.reader.read_exact(&mut pwd_verification_value)?;
Ok((pwd_verification_value, salt))
}
}

/// A reader for aes encrypted files, which has already passed the first password check.
Expand Down
49 changes: 49 additions & 0 deletions src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub(crate) mod zip_archive {
}
}

#[cfg(feature = "aes-crypto")]
use crate::aes::PWD_VERIFY_LENGTH;
#[cfg(feature = "lzma")]
use crate::read::lzma::LzmaDecoder;
use crate::result::ZipError::{InvalidPassword, UnsupportedArchive};
Expand Down Expand Up @@ -648,6 +650,41 @@ impl<R: Read + Seek> ZipArchive<R> {
Ok(shared)
}

/// Returns the verification value and salt for the AES encryption of the file
///
/// It fails if the file number is invalid.
///
/// # Returns
///
/// - None if the file is not encrypted with AES
#[cfg(feature = "aes-crypto")]
pub fn get_aes_verification_key_and_salt(
&mut self,
file_number: usize,
) -> ZipResult<Option<AesInfo>> {
let (_, data) = self
.shared
.files
.get_index(file_number)
.ok_or(ZipError::FileNotFound)?;

let limit_reader = find_content(data, &mut self.reader)?;
match data.aes_mode {
None => Ok(None),
Some((aes_mode, _, _)) => {
let (verification_value, salt) =
AesReader::new(limit_reader, aes_mode, data.compressed_size)
.get_verification_value_and_salt()?;
let aes_info = AesInfo {
aes_mode,
verification_value,
salt,
};
Ok(Some(aes_info))
}
}
}

/// Read a ZIP archive, collecting the files it contains
///
/// This uses the central directory record of the ZIP file, and ignores local file headers
Expand Down Expand Up @@ -940,6 +977,18 @@ impl<R: Read + Seek> ZipArchive<R> {
}
}

/// Holds the AES information of a file in the zip archive
#[derive(Debug)]
#[cfg(feature = "aes-crypto")]
pub struct AesInfo {
/// The AES encryption mode
pub aes_mode: AesMode,
/// The verification key
pub verification_value: [u8; PWD_VERIFY_LENGTH],
/// The salt
pub salt: Vec<u8>,
}

const fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T> {
Err(ZipError::UnsupportedArchive(detail))
}
Expand Down

0 comments on commit 6d1b5f7

Please sign in to comment.