///|
fn pdf_crypt_arc4_v4_length(
pdf : PdfDocument,
encrypt_dictionary : @syntax.PdfObject,
cf_dictionary : @syntax.PdfObject,
) -> Int? {
match pdf_crypt_lookup_int(pdf, pdf_crypt_length_name, encrypt_dictionary) {
Some(value) => Some(value)
None => pdf_crypt_lookup_int(pdf, pdf_crypt_length_name, cf_dictionary)
}
}
///|
fn pdf_crypt_type_from_encrypt_dictionary(
pdf : PdfDocument,
encrypt_dictionary : @syntax.PdfObject,
) -> @core.PdfCryptType? {
let standard = pdf_crypt_standard_name
let filter = pdf_crypt_lookup_name(
pdf, pdf_crypt_filter_name, encrypt_dictionary,
)
let version = pdf_crypt_lookup_int(pdf, pdf_crypt_v_name, encrypt_dictionary)
let length = pdf_crypt_lookup_int(
pdf, pdf_crypt_length_name, encrypt_dictionary,
)
let revision = pdf_crypt_lookup_int(pdf, pdf_crypt_r_name, encrypt_dictionary)
match (filter, version, length, revision) {
(Some(name), Some(1), _, Some(revision)) if name == standard =>
Some(PdfCryptARC4(40, revision))
(Some(name), Some(2), None, Some(revision)) if name == standard =>
Some(PdfCryptARC4(40, revision))
(Some(name), Some(2), Some(length), _) if name == standard &&
length % 8 == 0 &&
length >= 40 &&
length <= 128 => Some(PdfCryptARC4(length, 3))
(Some(name), Some(version), _, Some(revision)) if name == standard &&
(version == 4 || version == 5) =>
match pdf.lookup_direct(pdf_crypt_cf_name, encrypt_dictionary) {
Some(cf_dictionary) =>
match pdf.lookup_direct(pdf_crypt_std_cf_name, cf_dictionary) {
Some(std_cf_dictionary) =>
match
pdf_crypt_lookup_name(
pdf, pdf_crypt_cfm_name, std_cf_dictionary,
) {
Some(cfm) if cfm == pdf_crypt_v2_name =>
match
pdf_crypt_arc4_v4_length(
pdf, encrypt_dictionary, cf_dictionary,
) {
Some(length) => Some(PdfCryptARC4(length, 4))
None => None
}
Some(cfm) if cfm == pdf_crypt_aesv2_name => Some(PdfCryptAESV2)
Some(cfm) if cfm == pdf_crypt_aesv3_name =>
Some(PdfCryptAESV3(revision == 6))
_ => None
}
None => None
}
None => None
}
_ => None
}
}
///|
/// Parse the document trailer's `/Encrypt` dictionary.
///
/// The returned value contains the normalized security-handler parameters
/// needed for authentication and object crypt. Missing encryption dictionaries,
/// unsupported handlers, malformed `/O` or `/U` entries, and missing trailer
/// `/ID` values raise `@core.PdfError`.
fn PdfDocument::encryption_values(
self : PdfDocument,
) -> @crypt_core.PdfEncryptionValues raise @core.PdfError {
let encrypt_key = pdf_crypt_encrypt_name
let encrypt_dictionary = match
self.lookup_direct(encrypt_key, self.trailer_dict()) {
Some(value) => value
None => raise EncryptionExpected
}
let crypt_type = match
pdf_crypt_type_from_encrypt_dictionary(self, encrypt_dictionary) {
Some(value) => value
None => raise EncryptionMethodExpected
}
let user_owner_length = match crypt_type {
PdfCryptAESV3(_) => 48
_ => 32
}
let owner_key = pdf_crypt_o_name
let user_key = pdf_crypt_u_name
let id_key = pdf_crypt_id_name
let owner_entry = pdf_crypt_chop_string(
owner_key,
pdf_crypt_required_string(self, owner_key, encrypt_dictionary),
user_owner_length,
)
let user_entry = pdf_crypt_chop_string(
user_key,
pdf_crypt_required_string(self, user_key, encrypt_dictionary),
user_owner_length,
)
let permissions = pdf_crypt_required_int(
self, pdf_crypt_p_name, encrypt_dictionary,
)
let file_id = match self.lookup_direct(id_key, self.trailer_dict()) {
Some(PdfArray([PdfString(file_id), _])) => file_id
_ => raise EncryptionIDExpected
}
let encrypt_metadata = match
self.lookup_direct(pdf_crypt_encrypt_metadata_name, encrypt_dictionary) {
Some(PdfBoolean(false)) => false
_ => true
}
{
crypt_type,
user_entry,
owner_entry,
permissions,
file_id,
encrypt_metadata,
permissions_entry: pdf_crypt_lookup_string(
self, pdf_crypt_perms_name, encrypt_dictionary,
),
user_encryption_key: pdf_crypt_lookup_string(
self, pdf_crypt_ue_name, encrypt_dictionary,
),
owner_encryption_key: pdf_crypt_lookup_string(
self, pdf_crypt_oe_name, encrypt_dictionary,
),
}
}
///|
/// Return true when the trailer contains an `/Encrypt` entry.
pub fn PdfDocument::is_encrypted(self : PdfDocument) -> Bool {
self.lookup_direct(pdf_crypt_encrypt_name, self.trailer_dict()) != None
}
///|
/// Return the document encryption method, or `None` for unencrypted documents.
///
/// Unknown or unsupported encryption dictionaries may also return `None` after
/// the dictionary has been parsed.
pub fn PdfDocument::what_encryption(
self : PdfDocument,
) -> @crypt_core.PdfEncryptionMethod? raise @core.PdfError {
if self.is_encrypted() {
let values = self.encryption_values()
match values.crypt_type {
PdfCryptARC4(40, _) => Some(PdfEncryption40Bit)
PdfCryptARC4(128, _) => Some(PdfEncryption128Bit)
PdfCryptAESV2 => Some(PdfEncryptionAES128(values.encrypt_metadata))
PdfCryptAESV3(false) => Some(PdfEncryptionAES256(values.encrypt_metadata))
PdfCryptAESV3(true) =>
Some(PdfEncryptionAES256ISO(values.encrypt_metadata))
_ => None
}
} else {
None
}
}
///|
/// Decode the denied permissions from the document encryption dictionary.
///
/// Unencrypted documents return an empty array. Encrypted documents parse
/// `/Encrypt` and may raise if the encryption dictionary is malformed.
pub fn PdfDocument::permissions(
self : PdfDocument,
) -> Array[@crypt_core.PdfPermission] raise @core.PdfError {
if self.is_encrypted() {
@crypt_core.pdf_permissions_of_p(self.encryption_values().permissions)
} else {
[]
}
}