use std::io;
use aes::{
Aes256,
cipher::{
BlockDecryptMut,
BlockEncryptMut,
KeyIvInit,
block_padding::NoPadding,
},
};
use base64::{
Engine as _,
engine::general_purpose::STANDARD,
};
use byteorder::{
ByteOrder,
LittleEndian,
};
use cbc::{
Decryptor,
Encryptor,
};
use quick_xml::{
Writer,
events::{
BytesDecl,
Event,
},
};
type Aes256CbcEnc = Encryptor<Aes256>;
type Aes256CbcDec = Decryptor<Aes256>;
use super::{
super::const_str::{
CERTIFICATE_NS,
ENCRYPTION_NS,
PASSWORD_NS,
},
constants,
key::create_iv,
};
use crate::writer::driver::{
write_end_tag,
write_new_line,
write_start_tag,
};
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn crypt_package(
encrypt: bool,
block_size: usize,
salt: &[u8],
key: &[u8],
input: &[u8],
) -> Vec<u8> {
let mut output_chunks: Vec<Vec<u8>> = Vec::new();
let offset = if encrypt {
0
} else {
constants::PACKAGE_OFFSET
};
let mut i: usize = 0;
let mut end = 0;
while end < input.len() {
let start = end;
end = (start + constants::PACKAGE_ENCRYPTION_CHUNK_SIZE).min(input.len());
let mut input_chunk = input[start + offset..end + offset].to_vec();
let remainder = input_chunk.len() % block_size;
if remainder > 0 {
input_chunk.extend(vec![0u8; block_size - remainder]);
}
let block_key_buffer = create_uint32_le_buffer(i as u32, None);
let iv = create_iv(salt, block_size, &block_key_buffer);
let output_chunk = crypt(encrypt, key, &iv, &input_chunk).unwrap();
output_chunks.push(output_chunk);
i += 1;
}
let mut output = output_chunks.concat();
if encrypt {
let input_len = input.len() as u32;
let length_buffer = create_uint32_le_buffer(input_len, Some(constants::PACKAGE_OFFSET));
output = [length_buffer, output].concat();
} else {
let length = LittleEndian::read_u32(&input[0..4]) as usize;
output.truncate(length);
}
output
}
pub(crate) fn crypt(encrypt: bool, key: &[u8], iv: &[u8], input: &[u8]) -> Result<Vec<u8>, String> {
match key.len() * 8 {
256 => {
if encrypt {
let cipher = Aes256CbcEnc::new_from_slices(key, iv)
.map_err(|e| format!("Error creating cipher: {e}"))?;
let mut buffer = input.to_vec();
cipher
.encrypt_padded_mut::<NoPadding>(&mut buffer, input.len())
.map_err(|e| format!("Encryption error: {e}"))?;
Ok(buffer)
} else {
let cipher = Aes256CbcDec::new_from_slices(key, iv)
.map_err(|e| format!("Error creating cipher: {e}"))?;
let mut buffer = input.to_vec();
cipher
.decrypt_padded_mut::<NoPadding>(&mut buffer)
.map_err(|e| format!("Decryption error: {e}"))?;
Ok(buffer)
}
}
_ => Err("Key size not supported!".to_string()),
}
}
pub(crate) fn create_uint32_le_buffer(value: u32, buffer_size: Option<usize>) -> Vec<u8> {
let mut buffer = value.to_le_bytes().to_vec();
if let Some(size) = buffer_size.filter(|&s| s > 4) {
buffer.resize(size, 0);
}
buffer
}
pub(crate) fn build_encryption_info(
package_salt: &[u8],
data_integrity_encrypted_hmac_key: &[u8],
data_integrity_encrypted_hmac_value: &[u8],
key_salt: &[u8],
key_encrypted_verifier_hash_input: &[u8],
key_encrypted_verifier_hash_value: &[u8],
key_encrypted_key_value: &[u8],
) -> Vec<u8> {
let mut writer = Writer::new(io::Cursor::new(Vec::new()));
writer
.write_event(Event::Decl(BytesDecl::new(
"1.0",
Some("UTF-8"),
Some("yes"),
)))
.unwrap();
write_new_line(&mut writer);
write_start_tag(
&mut writer,
"encryption",
vec![
("xmlns", ENCRYPTION_NS).into(),
("xmlns:p", PASSWORD_NS).into(),
("xmlns:c", CERTIFICATE_NS).into(),
],
false,
);
write_start_tag(
&mut writer,
"keyData",
vec![
("saltSize", &package_salt.len().to_string()).into(),
("blockSize", &constants::PACKAGE_BLOCK_SIZE.to_string()).into(),
("keyBits", &constants::PACKAGE_KEY_BITS.to_string()).into(),
("hashSize", &constants::PACKAGE_HASH_SIZE.to_string()).into(),
("cipherAlgorithm", constants::PACKAGE_CIPHER_ALGORITHM).into(),
("cipherChaining", constants::PACKAGE_CIPHER_CHAINING).into(),
("hashAlgorithm", constants::PACKAGE_HASH_ALGORITHM).into(),
("saltValue", &STANDARD.encode(package_salt)).into(),
],
true,
);
write_start_tag(
&mut writer,
"dataIntegrity",
vec![
(
"encryptedHmacKey",
&STANDARD.encode(data_integrity_encrypted_hmac_key),
)
.into(),
(
"encryptedHmacValue",
&STANDARD.encode(data_integrity_encrypted_hmac_value),
)
.into(),
],
true,
);
write_start_tag(&mut writer, "keyEncryptors", vec![], false);
write_start_tag(
&mut writer,
"keyEncryptor",
vec![("uri", PASSWORD_NS).into()],
false,
);
write_start_tag(
&mut writer,
"p:encryptedKey",
vec![
("spinCount", &constants::KEY_SPIN_COUNT.to_string()).into(),
("saltSize", &key_salt.len().to_string()).into(),
("blockSize", &constants::KEY_BLOCK_SIZE.to_string()).into(),
("keyBits", &constants::KEY_BITLENGTH.to_string()).into(),
("hashSize", &constants::KEY_HASH_SIZE.to_string()).into(),
("cipherAlgorithm", constants::KEY_CIPHER_ALGORITHM).into(),
("cipherChaining", constants::KEY_CIPHER_CHAINING).into(),
("hashAlgorithm", constants::KEY_HASH_ALGORITHM).into(),
("saltValue", &STANDARD.encode(key_salt)).into(),
(
"encryptedVerifierHashInput",
&STANDARD.encode(key_encrypted_verifier_hash_input),
)
.into(),
(
"encryptedVerifierHashValue",
&STANDARD.encode(key_encrypted_verifier_hash_value),
)
.into(),
(
"encryptedKeyValue",
&STANDARD.encode(key_encrypted_key_value),
)
.into(),
],
true,
);
write_end_tag(&mut writer, "keyEncryptor");
write_end_tag(&mut writer, "keyEncryptors");
write_end_tag(&mut writer, "encryption");
let data = writer.into_inner().into_inner();
let mut result = Vec::with_capacity(constants::ENCRYPTION_INFO_PREFIX.len() + data.len());
result.extend_from_slice(&constants::ENCRYPTION_INFO_PREFIX);
result.extend(data);
result
}