mod algorithms;
pub mod crypt_filters;
mod pkcs5;
mod rc4;
use bitflags::bitflags;
use crate::{Dictionary, Document, Error, Object, ObjectId};
use crypt_filters::*;
use std::collections::BTreeMap;
use std::sync::Arc;
use thiserror::Error;
pub use algorithms::PasswordAlgorithm;
#[derive(Error, Debug)]
pub enum DecryptionError {
#[error("the /Encrypt dictionary is missing")]
MissingEncryptDictionary,
#[error("missing encryption version")]
MissingVersion,
#[error("missing encryption revision")]
MissingRevision,
#[error("missing the owner password (/O)")]
MissingOwnerPassword,
#[error("missing the user password (/U)")]
MissingUserPassword,
#[error("missing the permissions field (/P)")]
MissingPermissions,
#[error("missing the file /ID elements")]
MissingFileID,
#[error("invalid hash length")]
InvalidHashLength,
#[error("invalid key length")]
InvalidKeyLength,
#[error("invalid ciphertext length")]
InvalidCipherTextLength,
#[error("invalid permission length")]
InvalidPermissionLength,
#[error("invalid version")]
InvalidVersion,
#[error("invalid revision")]
InvalidRevision,
#[error("unexpected type; document does not comply with the spec")]
InvalidType,
#[error("the object is not capable of being decrypted")]
NotDecryptable,
#[error("the supplied password is incorrect")]
IncorrectPassword,
#[error("the document uses an encryption scheme that is not implemented in lopdf")]
UnsupportedEncryption,
#[error("the encryption version is not implemented in lopdf")]
UnsupportedVersion,
#[error("the encryption revision is not implemented in lopdf")]
UnsupportedRevision,
#[error(transparent)]
StringPrep(#[from] stringprep::Error),
#[error("invalid padding encountered when decrypting, key might be incorrect")]
Padding,
}
bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Permissions: u64 {
const PRINTABLE = 1 << 2;
const MODIFIABLE = 1 << 3;
const COPYABLE = 1 << 4;
const ANNOTABLE = 1 << 5;
const FILLABLE = 1 << 8;
const COPYABLE_FOR_ACCESSIBILITY = 1 << 9;
const ASSEMBLABLE = 1 << 10;
const PRINTABLE_IN_HIGH_QUALITY = 1 << 11;
}
}
impl Default for Permissions {
fn default() -> Self {
Self::all()
}
}
impl Permissions {
fn correct_bits(self) -> Self {
let mut bits = self.bits();
bits |= 0b11 << 6;
bits |= 0b1111 << 12 | 0xffff << 16;
bits |= 0xffffffff << 32;
Permissions::from_bits_retain(bits)
}
}
#[derive(Clone, Debug)]
pub enum EncryptionVersion<'a> {
V1 {
document: &'a Document,
owner_password: &'a str,
user_password: &'a str,
permissions: Permissions,
},
V2 {
document: &'a Document,
owner_password: &'a str,
user_password: &'a str,
key_length: usize,
permissions: Permissions,
},
V4 {
document: &'a Document,
encrypt_metadata: bool,
crypt_filters: BTreeMap<Vec<u8>, Arc<dyn CryptFilter>>,
stream_filter: Vec<u8>,
string_filter: Vec<u8>,
owner_password: &'a str,
user_password: &'a str,
permissions: Permissions,
},
#[deprecated(note="R5 is a proprietary Adobe extension and should not be used in newly produced documents other than for testing purposes.")]
R5 {
encrypt_metadata: bool,
crypt_filters: BTreeMap<Vec<u8>, Arc<dyn CryptFilter>>,
file_encryption_key: &'a [u8],
stream_filter: Vec<u8>,
string_filter: Vec<u8>,
owner_password: &'a str,
user_password: &'a str,
permissions: Permissions,
},
V5 {
encrypt_metadata: bool,
crypt_filters: BTreeMap<Vec<u8>, Arc<dyn CryptFilter>>,
file_encryption_key: &'a [u8],
stream_filter: Vec<u8>,
string_filter: Vec<u8>,
owner_password: &'a str,
user_password: &'a str,
permissions: Permissions,
},
}
#[derive(Clone, Debug, Default)]
pub struct EncryptionState {
pub(crate) version: i64,
pub(crate) revision: i64,
pub(crate) key_length: Option<usize>,
pub(crate) encrypt_metadata: bool,
pub(crate) crypt_filters: BTreeMap<Vec<u8>, Arc<dyn CryptFilter>>,
pub(crate) file_encryption_key: Vec<u8>,
pub(crate) stream_filter: Vec<u8>,
pub(crate) string_filter: Vec<u8>,
pub(crate) owner_value: Vec<u8>,
pub(crate) owner_encrypted: Vec<u8>,
pub(crate) user_value: Vec<u8>,
pub(crate) user_encrypted: Vec<u8>,
pub(crate) permissions: Permissions,
pub(crate) permission_encrypted: Vec<u8>,
}
impl TryFrom<EncryptionVersion<'_>> for EncryptionState {
type Error = Error;
fn try_from(version: EncryptionVersion) -> Result<EncryptionState, Self::Error> {
match version {
EncryptionVersion::V1 {
document,
owner_password,
user_password,
permissions,
} => {
let permissions = permissions.correct_bits();
let mut algorithm = PasswordAlgorithm {
encrypt_metadata: true,
length: None,
version: 1,
revision: 2,
permissions,
..Default::default()
};
let owner_password = algorithm.sanitize_password_r4(owner_password)?;
let user_password = algorithm.sanitize_password_r4(user_password)?;
algorithm.owner_value = algorithm.compute_hashed_owner_password_r4(
Some(&owner_password),
&user_password,
)?;
algorithm.user_value = algorithm.compute_hashed_user_password_r2(
document,
&user_password,
)?;
let file_encryption_key = algorithm.compute_file_encryption_key_r4(
document,
&user_password,
)?;
Ok(Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
file_encryption_key,
owner_value: algorithm.owner_value,
user_value: algorithm.user_value,
permissions: algorithm.permissions,
..Default::default()
})
}
EncryptionVersion::V2 {
document,
owner_password,
user_password,
key_length,
permissions,
} => {
let permissions = permissions.correct_bits();
let mut algorithm = PasswordAlgorithm {
encrypt_metadata: true,
length: Some(key_length),
version: 2,
revision: 3,
permissions,
..Default::default()
};
let owner_password = algorithm.sanitize_password_r4(owner_password)?;
let user_password = algorithm.sanitize_password_r4(user_password)?;
algorithm.owner_value = algorithm.compute_hashed_owner_password_r4(
Some(&owner_password),
&user_password,
)?;
algorithm.user_value = algorithm.compute_hashed_user_password_r3_r4(
document,
&user_password,
)?;
let file_encryption_key = algorithm.compute_file_encryption_key_r4(
document,
&user_password,
)?;
Ok(Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
file_encryption_key,
owner_value: algorithm.owner_value,
user_value: algorithm.user_value,
permissions,
..Default::default()
})
}
EncryptionVersion::V4 {
document,
encrypt_metadata,
crypt_filters,
stream_filter,
string_filter,
owner_password,
user_password,
permissions,
} => {
let permissions = permissions.correct_bits();
let mut algorithm = PasswordAlgorithm {
encrypt_metadata,
length: Some(128),
version: 4,
revision: 4,
permissions,
..Default::default()
};
let owner_password = algorithm.sanitize_password_r4(owner_password)?;
let user_password = algorithm.sanitize_password_r4(user_password)?;
algorithm.owner_value = algorithm.compute_hashed_owner_password_r4(
Some(&owner_password),
&user_password,
)?;
algorithm.user_value = algorithm.compute_hashed_user_password_r3_r4(
document,
&user_password,
)?;
let file_encryption_key = algorithm.compute_file_encryption_key_r4(
document,
&user_password,
)?;
Ok(Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
file_encryption_key,
crypt_filters,
stream_filter,
string_filter,
owner_value: algorithm.owner_value,
user_value: algorithm.user_value,
permissions: algorithm.permissions,
..Default::default()
})
}
#[allow(deprecated)]
EncryptionVersion::R5 {
encrypt_metadata,
crypt_filters,
file_encryption_key,
stream_filter,
string_filter,
owner_password,
user_password,
permissions,
} => {
if file_encryption_key.len() != 32 {
return Err(DecryptionError::InvalidKeyLength)?;
}
let permissions = permissions.correct_bits();
let mut algorithm = PasswordAlgorithm {
encrypt_metadata,
version: 5,
revision: 5,
permissions,
..Default::default()
};
let owner_password = algorithm.sanitize_password_r6(owner_password)?;
let user_password = algorithm.sanitize_password_r6(user_password)?;
let (user_value, user_encrypted) = algorithm.compute_hashed_user_password_r6(
file_encryption_key,
user_password,
)?;
algorithm.user_value = user_value;
algorithm.user_encrypted = user_encrypted;
let (owner_value, owner_encrypted) = algorithm.compute_hashed_owner_password_r6(
file_encryption_key,
owner_password,
)?;
algorithm.owner_value = owner_value;
algorithm.owner_encrypted = owner_encrypted;
algorithm.permission_encrypted = algorithm.compute_permissions(
file_encryption_key,
)?;
Ok(Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
crypt_filters,
file_encryption_key: file_encryption_key.to_vec(),
stream_filter,
string_filter,
owner_value: algorithm.owner_value,
owner_encrypted: algorithm.owner_encrypted,
user_value: algorithm.user_value,
user_encrypted: algorithm.user_encrypted,
permissions: algorithm.permissions,
permission_encrypted: algorithm.permission_encrypted,
})
}
EncryptionVersion::V5 {
encrypt_metadata,
crypt_filters,
file_encryption_key,
stream_filter,
string_filter,
owner_password,
user_password,
permissions,
} => {
if file_encryption_key.len() != 32 {
return Err(DecryptionError::InvalidKeyLength)?;
}
let permissions = permissions.correct_bits();
let mut algorithm = PasswordAlgorithm {
encrypt_metadata,
version: 5,
revision: 6,
permissions,
..Default::default()
};
let owner_password = algorithm.sanitize_password_r6(owner_password)?;
let user_password = algorithm.sanitize_password_r6(user_password)?;
let (user_value, user_encrypted) = algorithm.compute_hashed_user_password_r6(
file_encryption_key,
user_password,
)?;
algorithm.user_value = user_value;
algorithm.user_encrypted = user_encrypted;
let (owner_value, owner_encrypted) = algorithm.compute_hashed_owner_password_r6(
file_encryption_key,
owner_password,
)?;
algorithm.owner_value = owner_value;
algorithm.owner_encrypted = owner_encrypted;
algorithm.permission_encrypted = algorithm.compute_permissions(
file_encryption_key,
)?;
Ok(Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
crypt_filters,
file_encryption_key: file_encryption_key.to_vec(),
stream_filter,
string_filter,
owner_value: algorithm.owner_value,
owner_encrypted: algorithm.owner_encrypted,
user_value: algorithm.user_value,
user_encrypted: algorithm.user_encrypted,
permissions: algorithm.permissions,
permission_encrypted: algorithm.permission_encrypted,
})
}
}
}
}
impl EncryptionState {
pub fn version(&self) -> i64 {
self.version
}
pub fn revision(&self) -> i64 {
self.revision
}
pub fn key_length(&self) -> Option<usize> {
self.key_length
}
pub fn encrypt_metadata(&self) -> bool {
self.encrypt_metadata
}
pub fn crypt_filters(&self) -> &BTreeMap<Vec<u8>, Arc<dyn CryptFilter>> {
&self.crypt_filters
}
pub fn file_encryption_key(&self) -> &[u8] {
self.file_encryption_key.as_ref()
}
pub fn default_stream_filter(&self) -> &[u8] {
self.stream_filter.as_ref()
}
pub fn default_string_filter(&self) -> &[u8] {
self.string_filter.as_ref()
}
pub fn owner_value(&self) -> &[u8] {
self.owner_value.as_ref()
}
pub fn owner_encrypted(&self) -> &[u8] {
self.owner_encrypted.as_ref()
}
pub fn user_value(&self) -> &[u8] {
self.user_value.as_ref()
}
pub fn user_encrypted(&self) -> &[u8] {
self.user_encrypted.as_ref()
}
pub fn permissions(&self) -> Permissions {
self.permissions
}
pub fn permission_encrypted(&self) -> &[u8] {
self.permission_encrypted.as_ref()
}
pub fn decode<P>(
document: &Document,
password: P,
) -> Result<Self, Error>
where
P: AsRef<[u8]>,
{
if !document.is_encrypted() {
return Err(Error::NotEncrypted);
}
let filter = document.get_encrypted()
.and_then(|dict| dict.get(b"Filter"))
.and_then(|object| object.as_name())
.map_err(|_| Error::DictKey("Filter".to_string()))?;
if filter != b"Standard" {
return Err(Error::UnsupportedSecurityHandler(filter.to_vec()));
}
let algorithm = PasswordAlgorithm::try_from(document)?;
let file_encryption_key = algorithm.compute_file_encryption_key(document, password)?;
let mut crypt_filters = document.get_crypt_filters();
if algorithm.version < 4 {
crypt_filters.clear();
}
let mut state = Self {
version: algorithm.version,
revision: algorithm.revision,
key_length: algorithm.length,
encrypt_metadata: algorithm.encrypt_metadata,
crypt_filters,
file_encryption_key,
owner_value: algorithm.owner_value,
owner_encrypted: algorithm.owner_encrypted,
user_value: algorithm.user_value,
user_encrypted: algorithm.user_encrypted,
permissions: algorithm.permissions,
permission_encrypted: algorithm.permission_encrypted,
..Default::default()
};
if algorithm.version == 4 || algorithm.version == 5 {
if let Ok(stream_filter) = document.get_encrypted()
.and_then(|dict| dict.get(b"StmF"))
.and_then(|object| object.as_name()) {
state.stream_filter = stream_filter.to_vec();
}
if let Ok(string_filter) = document.get_encrypted()
.and_then(|dict| dict.get(b"StrF"))
.and_then(|object| object.as_name()) {
state.string_filter = string_filter.to_vec();
}
}
Ok(state)
}
pub fn encode(&self) -> Result<Dictionary, DecryptionError> {
let mut encrypted = Dictionary::new();
encrypted.set(b"Filter", Object::Name(b"Standard".to_vec()));
encrypted.set(b"V", Object::Integer(self.version));
encrypted.set(b"R", Object::Integer(self.revision));
if let Some(key_length) = self.key_length {
encrypted.set(b"Length", Object::Integer(key_length as i64));
}
if self.version >= 4 {
encrypted.set(b"EncryptMetadata", Object::Boolean(self.encrypt_metadata));
}
encrypted.set(b"O", Object::string_literal(self.owner_value.clone()));
encrypted.set(b"U", Object::string_literal(self.user_value.clone()));
encrypted.set(b"P", Object::Integer(self.permissions.bits() as i64));
if self.revision >= 4 {
let mut filters = Dictionary::new();
for (name, crypt_filter) in &self.crypt_filters {
let mut filter = Dictionary::new();
filter.set(b"Type", Object::Name(b"CryptFilter".to_vec()));
filter.set(b"CFM", Object::Name(crypt_filter.method().to_vec()));
filters.set(name.to_vec(), Object::Dictionary(filter));
}
encrypted.set(b"CF", Object::Dictionary(filters));
encrypted.set(b"StmF", Object::Name(self.stream_filter.clone()));
encrypted.set(b"StrF", Object::Name(self.string_filter.clone()));
}
if self.revision >= 5 {
encrypted.set(b"OE", Object::string_literal(self.owner_encrypted.clone()));
encrypted.set(b"UE", Object::string_literal(self.user_encrypted.clone()));
encrypted.set(b"Perms", Object::string_literal(self.permission_encrypted.clone()));
}
Ok(encrypted)
}
pub fn get_stream_filter(&self) -> Arc<dyn CryptFilter> {
self.crypt_filters.get(&self.stream_filter).cloned().unwrap_or(Arc::new(Rc4CryptFilter))
}
pub fn get_string_filter(&self) -> Arc<dyn CryptFilter> {
self.crypt_filters.get(&self.string_filter).cloned().unwrap_or(Arc::new(Rc4CryptFilter))
}
}
pub fn encrypt_object(state: &EncryptionState, obj_id: ObjectId, obj: &mut Object) -> Result<(), DecryptionError> {
let is_xref_stream = obj.as_stream()
.map(|stream| stream.dict.has_type(b"XRef"))
.unwrap_or(false);
if is_xref_stream {
return Ok(());
}
if obj.type_name().ok() == Some(b"Metadata") && !state.encrypt_metadata {
return Ok(());
}
let override_crypt_filter = obj.as_stream().ok()
.filter(|stream| stream.filters().map(|filters| filters.contains(&&b"Crypt"[..])).unwrap_or(false))
.and_then(|stream| stream.dict.get(b"DecodeParms").ok())
.and_then(|object| object.as_dict().ok())
.map(|dict| dict.get(b"Name")
.and_then(|object| object.as_name())
.ok()
.and_then(|name| state.crypt_filters.get(name).cloned())
.unwrap_or(Arc::new(IdentityCryptFilter))
);
let (mut crypt_filter, plaintext) = match obj {
Object::Array(objects) => {
for obj in objects {
encrypt_object(state, obj_id, obj)?;
}
return Ok(());
}
Object::Dictionary(objects) => {
for (_, obj) in objects.iter_mut() {
encrypt_object(state, obj_id, obj)?;
}
return Ok(());
}
Object::String(content, _) => (state.get_string_filter(), &*content),
Object::Stream(stream) => (state.get_stream_filter(), &stream.content),
_ => {
return Ok(());
}
};
if let Some(filter) = override_crypt_filter {
crypt_filter = filter;
}
let key = crypt_filter.compute_key(&state.file_encryption_key, obj_id)?;
let ciphertext = crypt_filter.encrypt(&key, plaintext)?;
match obj {
Object::Stream(stream) => stream.set_content(ciphertext),
Object::String(content, _) => *content = ciphertext,
_ => (),
}
Ok(())
}
pub fn decrypt_object(state: &EncryptionState, obj_id: ObjectId, obj: &mut Object) -> Result<(), DecryptionError> {
let is_xref_stream = obj.as_stream()
.map(|stream| stream.dict.has_type(b"XRef"))
.unwrap_or(false);
if is_xref_stream {
return Ok(());
}
if obj.type_name().ok() == Some(b"Metadata") && !state.encrypt_metadata {
return Ok(());
}
let override_crypt_filter = obj.as_stream().ok()
.filter(|stream| stream.filters().map(|filters| filters.contains(&&b"Crypt"[..])).unwrap_or(false))
.and_then(|stream| stream.dict.get(b"DecodeParms").ok())
.and_then(|object| object.as_dict().ok())
.map(|dict| dict.get(b"Name")
.and_then(|object| object.as_name())
.ok()
.and_then(|name| state.crypt_filters.get(name).cloned())
.unwrap_or(Arc::new(IdentityCryptFilter))
);
let (mut crypt_filter, ciphertext) = match obj {
Object::Array(objects) => {
for obj in objects {
decrypt_object(state, obj_id, obj)?;
}
return Ok(());
}
Object::Dictionary(objects) => {
for (_, obj) in objects.iter_mut() {
decrypt_object(state, obj_id, obj)?;
}
return Ok(());
}
Object::String(content, _) => (state.get_string_filter(), &*content),
Object::Stream(stream) => (state.get_stream_filter(), &stream.content),
_ => {
return Ok(());
}
};
if let Some(filter) = override_crypt_filter {
crypt_filter = filter;
}
let key = crypt_filter.compute_key(&state.file_encryption_key, obj_id)?;
let plaintext = crypt_filter.decrypt(&key, ciphertext)?;
match obj {
Object::Stream(stream) => stream.set_content(plaintext),
Object::String(content, _) => *content = plaintext,
_ => (),
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::{EncryptionState, EncryptionVersion, Permissions};
use crate::creator::tests::create_document;
use crate::encryption::{CryptFilter, Aes128CryptFilter, Aes256CryptFilter};
use rand::RngExt as _;
use super::rc4::Rc4;
use std::collections::BTreeMap;
use std::sync::Arc;
#[test]
fn rc4_works() {
let cases = [
(
String::from("Key"),
String::from("Plaintext"),
String::from("BBF316E8D940AF0AD3"),
),
(String::from("Wiki"), String::from("pedia"), String::from("1021BF0420")),
];
for (key, plain, cipher) in cases {
let cipher = cipher.as_bytes();
let mut cipher_bytes = Vec::with_capacity(cipher.len() / 2);
for hex_pair in cipher.chunks_exact(2) {
cipher_bytes.push(u8::from_str_radix(std::str::from_utf8(hex_pair).unwrap(), 16).unwrap());
}
let decryptor = Rc4::new(key);
let decrypted = decryptor.decrypt(&cipher_bytes);
assert_eq!(plain.as_bytes(), &decrypted[..]);
}
}
#[test]
fn encrypt_v1() {
let mut document = create_document();
let version = EncryptionVersion::V1 {
document: &document,
owner_password: "owner",
user_password: "user",
permissions: Permissions::all(),
};
let state = EncryptionState::try_from(version).unwrap();
assert!(document.encrypt(&state).is_ok());
assert!(document.decrypt("user").is_ok());
}
#[test]
fn encrypt_v2() {
let mut document = create_document();
let version = EncryptionVersion::V2 {
document: &document,
owner_password: "owner",
user_password: "user",
key_length: 40,
permissions: Permissions::all(),
};
let state = EncryptionState::try_from(version).unwrap();
assert!(document.encrypt(&state).is_ok());
assert!(document.decrypt("user").is_ok());
}
#[test]
fn encrypt_v4() {
let mut document = create_document();
let crypt_filter: Arc<dyn CryptFilter> = Arc::new(Aes128CryptFilter);
let version = EncryptionVersion::V4 {
document: &document,
encrypt_metadata: true,
crypt_filters: BTreeMap::from([(b"StdCF".to_vec(), crypt_filter)]),
stream_filter: b"StdCF".to_vec(),
string_filter: b"StdCF".to_vec(),
owner_password: "owner",
user_password: "user",
permissions: Permissions::all(),
};
let state = EncryptionState::try_from(version).unwrap();
assert!(document.encrypt(&state).is_ok());
assert!(document.decrypt("user").is_ok());
}
#[test]
fn encrypt_r5() {
let mut document = create_document();
let crypt_filter: Arc<dyn CryptFilter> = Arc::new(Aes256CryptFilter);
let mut file_encryption_key = [0u8; 32];
let mut rng = rand::rng();
rng.fill(&mut file_encryption_key);
#[allow(deprecated)]
let version = EncryptionVersion::R5 {
encrypt_metadata: true,
crypt_filters: BTreeMap::from([(b"StdCF".to_vec(), crypt_filter)]),
file_encryption_key: &file_encryption_key,
stream_filter: b"StdCF".to_vec(),
string_filter: b"StdCF".to_vec(),
owner_password: "owner",
user_password: "user",
permissions: Permissions::all(),
};
let state = EncryptionState::try_from(version).unwrap();
assert!(document.encrypt(&state).is_ok());
assert!(document.decrypt("user").is_ok());
}
#[test]
fn encrypt_v5() {
let mut document = create_document();
let crypt_filter: Arc<dyn CryptFilter> = Arc::new(Aes256CryptFilter);
let mut file_encryption_key = [0u8; 32];
let mut rng = rand::rng();
rng.fill(&mut file_encryption_key);
let version = EncryptionVersion::V5 {
encrypt_metadata: true,
crypt_filters: BTreeMap::from([(b"StdCF".to_vec(), crypt_filter)]),
file_encryption_key: &file_encryption_key,
stream_filter: b"StdCF".to_vec(),
string_filter: b"StdCF".to_vec(),
owner_password: "owner",
user_password: "user",
permissions: Permissions::all(),
};
let state = EncryptionState::try_from(version).unwrap();
assert!(document.encrypt(&state).is_ok());
assert!(document.decrypt("user").is_ok());
}
}