///|
pub(all) enum PdfPermission {
PdfNoEdit
PdfNoPrint
PdfNoCopy
PdfNoAnnot
PdfNoForms
PdfNoExtract
PdfNoAssemble
PdfNoHqPrint
} derive(Debug, Eq, ToJson)
///|
pub(all) enum PdfEncryptionMethod {
PdfEncryption40Bit
PdfEncryption128Bit
PdfEncryptionAES128(Bool)
PdfEncryptionAES256(Bool)
PdfEncryptionAES256ISO(Bool)
PdfEncryptionAlreadyEncrypted
} derive(Debug, Eq, ToJson)
///|
pub(all) enum PdfAesV3RandomField {
PdfAesV3FileKey
PdfAesV3UserValidationSalt
PdfAesV3UserKeySalt
PdfAesV3OwnerValidationSalt
PdfAesV3OwnerKeySalt
PdfAesV3PermissionsPadding
} derive(Debug, Eq, ToJson)
///|
pub(all) struct PdfEncryptionValues {
crypt_type : PdfCryptType
user_entry : PdfBytes
owner_entry : PdfBytes
permissions : Int
file_id : PdfBytes
encrypt_metadata : Bool
permissions_entry : PdfBytes?
user_encryption_key : PdfBytes?
owner_encryption_key : PdfBytes?
} derive(Debug, Eq, ToJson)
///|
pub(all) struct PdfSavedEncryption {
values : PdfEncryptionValues
} derive(Debug, Eq, ToJson)
///|
pub(all) struct PdfDecryptionResult {
document : PdfDocument
denied_permissions : Array[PdfPermission]
} derive(Debug, Eq, ToJson)
///|
fn pdf_crypt_name(text : String) -> PdfName {
pdf_name_of_bytes(@ascii.encode(text))
}
///|
let pdf_crypt_length_name : PdfName = pdf_crypt_name("/Length")
///|
let pdf_crypt_encrypt_name : PdfName = pdf_crypt_name("/Encrypt")
///|
let pdf_crypt_u_name : PdfName = pdf_crypt_name("/U")
///|
let pdf_crypt_o_name : PdfName = pdf_crypt_name("/O")
///|
let pdf_crypt_filter_name : PdfName = pdf_crypt_name("/Filter")
///|
let pdf_crypt_v_name : PdfName = pdf_crypt_name("/V")
///|
let pdf_crypt_standard_name : PdfName = pdf_crypt_name("/Standard")
///|
let pdf_crypt_r_name : PdfName = pdf_crypt_name("/R")
///|
let pdf_crypt_p_name : PdfName = pdf_crypt_name("/P")
///|
let pdf_crypt_std_cf_name : PdfName = pdf_crypt_name("/StdCF")
///|
let pdf_crypt_encrypt_metadata_name : PdfName = pdf_crypt_name(
"/EncryptMetadata",
)
///|
let pdf_crypt_cfm_name : PdfName = pdf_crypt_name("/CFM")
///|
let pdf_crypt_cf_name : PdfName = pdf_crypt_name("/CF")
///|
let pdf_crypt_strf_name : PdfName = pdf_crypt_name("/StrF")
///|
let pdf_crypt_stmf_name : PdfName = pdf_crypt_name("/StmF")
///|
let pdf_crypt_doc_open_name : PdfName = pdf_crypt_name("/DocOpen")
///|
let pdf_crypt_auth_event_name : PdfName = pdf_crypt_name("/AuthEvent")
///|
let pdf_crypt_ue_name : PdfName = pdf_crypt_name("/UE")
///|
let pdf_crypt_oe_name : PdfName = pdf_crypt_name("/OE")
///|
let pdf_crypt_v2_name : PdfName = pdf_crypt_name("/V2")
///|
let pdf_crypt_id_name : PdfName = pdf_crypt_name("/ID")
///|
let pdf_crypt_aesv2_name : PdfName = pdf_crypt_name("/AESV2")
///|
let pdf_crypt_perms_name : PdfName = pdf_crypt_name("/Perms")
///|
let pdf_crypt_crypt_name : PdfName = pdf_crypt_name("/Crypt")
///|
let pdf_crypt_aesv3_name : PdfName = pdf_crypt_name("/AESV3")
///|
let pdf_crypt_user_validation_salt_name : PdfName = pdf_crypt_name(
"/UserValidationSalt",
)
///|
let pdf_crypt_user_key_salt_name : PdfName = pdf_crypt_name("/UserKeySalt")
///|
let pdf_crypt_type_name : PdfName = pdf_crypt_name("/Type")
///|
let pdf_crypt_metadata_name : PdfName = pdf_crypt_name("/Metadata")
///|
let pdf_crypt_perms_padding_name : PdfName = pdf_crypt_name("/PermsPadding")
///|
let pdf_crypt_owner_validation_salt_name : PdfName = pdf_crypt_name(
"/OwnerValidationSalt",
)
///|
let pdf_crypt_owner_key_salt_name : PdfName = pdf_crypt_name("/OwnerKeySalt")
///|
let pdf_crypt_name_key_name : PdfName = pdf_crypt_name("/Name")
///|
let pdf_crypt_identity_name : PdfName = pdf_crypt_name("/Identity")
///|
let pdf_crypt_iv_name : PdfName = pdf_crypt_name("/IV")
///|
let pdf_crypt_file_key_name : PdfName = pdf_crypt_name("/FileKey")
///|
let pdf_crypt_decodeparms_name : PdfName = pdf_crypt_name("/DecodeParms")
///|
fn pdf_crypt_lookup_name(
pdf : PdfDocument,
key : PdfName,
dictionary : PdfObject,
) -> PdfName? {
match pdf.lookup_direct(key, dictionary) {
Some(PdfNameObject(name)) => Some(name)
_ => None
}
}
///|
fn pdf_crypt_lookup_int(
pdf : PdfDocument,
key : PdfName,
dictionary : PdfObject,
) -> Int? {
match pdf.lookup_direct(key, dictionary) {
Some(PdfInteger(value)) => Some(value)
_ => None
}
}
///|
fn pdf_crypt_lookup_string(
pdf : PdfDocument,
key : PdfName,
dictionary : PdfObject,
) -> PdfBytes? {
match pdf.lookup_direct(key, dictionary) {
Some(PdfString(value)) => Some(value)
_ => None
}
}
///|
fn pdf_crypt_required_string(
pdf : PdfDocument,
key : PdfName,
dictionary : PdfObject,
) -> PdfBytes raise PdfError {
match pdf_crypt_lookup_string(pdf, key, dictionary) {
Some(value) => value
None => raise EncryptionDictionaryEntryExpected(key)
}
}
///|
fn pdf_crypt_required_int(
pdf : PdfDocument,
key : PdfName,
dictionary : PdfObject,
) -> Int raise PdfError {
match pdf_crypt_lookup_int(pdf, key, dictionary) {
Some(value) => value
None => raise EncryptionDictionaryEntryExpected(key)
}
}
///|
fn pdf_crypt_chop_string(
key : PdfName,
value : PdfBytes,
need : Int,
) -> PdfBytes raise PdfError {
pdf_crypt_chop_view(key, value[:], need)
}
///|
fn pdf_crypt_chop_view(
key : PdfName,
value : BytesView,
need : Int,
) -> PdfBytes raise PdfError {
if value.length() < need {
raise EncryptionDictionaryEntryTooShort(key)
}
value[:need].to_owned()
}
///|
fn pdf_crypt_arc4_v4_length(
pdf : PdfDocument,
encrypt_dictionary : PdfObject,
cf_dictionary : 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 : PdfObject,
) -> 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
}
}
///|
pub fn PdfDocument::encryption_values(
self : PdfDocument,
) -> PdfEncryptionValues raise 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,
),
}
}
///|
pub fn PdfDocument::is_encrypted(self : PdfDocument) -> Bool {
self.lookup_direct(pdf_crypt_encrypt_name, self.trailer_dict()) != None
}
///|
pub fn PdfDocument::what_encryption(
self : PdfDocument,
) -> PdfEncryptionMethod? raise 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
}
}
///|
pub fn PdfDocument::permissions(
self : PdfDocument,
) -> Array[PdfPermission] raise PdfError {
if self.is_encrypted() {
pdf_permissions_of_p(self.encryption_values().permissions)
} else {
[]
}
}
///|
fn PdfEncryptionValues::effective_no_encrypt_metadata(
self : PdfEncryptionValues,
no_encrypt_metadata : Bool,
) -> Bool {
no_encrypt_metadata || !self.encrypt_metadata
}
///|
fn PdfEncryptionValues::password_settings(
self : PdfEncryptionValues,
) -> (Int, Int) raise PdfError {
match self.crypt_type {
PdfCryptARC4(key_length_bits, revision) => (revision, key_length_bits)
PdfCryptAESV2 => (4, 128)
PdfCryptAESV3(_) =>
raise EncryptionAuthenticationUnsupported(self.crypt_type)
}
}
///|
pub fn PdfEncryptionValues::authenticate_user_password(
self : PdfEncryptionValues,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> Bool raise PdfError {
let no_encrypt_metadata = self.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match self.crypt_type {
PdfCryptAESV3(iso) =>
pdf_authenticate_user_password_aesv3(iso, password, self.user_entry[:])
_ => {
let (revision, key_length_bits) = self.password_settings()
pdf_authenticate_user_password(
no_encrypt_metadata,
password,
revision,
self.user_entry[:],
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
)
}
}
}
///|
pub fn PdfEncryptionValues::file_key_for_user_password(
self : PdfEncryptionValues,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> PdfBytes? raise PdfError {
let no_encrypt_metadata = self.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match self.crypt_type {
PdfCryptAESV3(iso) =>
match
pdf_file_key_for_user_password_aesv3(
iso,
password,
self.user_entry[:],
self.user_encryption_key,
) {
Some(key) => {
self.validate_aesv3_permissions(key[:])
Some(key)
}
None => None
}
_ => {
let (revision, key_length_bits) = self.password_settings()
if pdf_authenticate_user_password(
no_encrypt_metadata,
password,
revision,
self.user_entry[:],
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
) {
Some(
pdf_encryption_file_key(
no_encrypt_metadata,
password,
revision,
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
),
)
} else {
None
}
}
}
}
///|
pub fn PdfEncryptionValues::authenticate_owner_password(
self : PdfEncryptionValues,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> Bool raise PdfError {
let no_encrypt_metadata = self.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match self.crypt_type {
PdfCryptAESV3(iso) =>
pdf_authenticate_owner_password_aesv3(
iso,
password,
self.user_entry[:],
self.owner_entry[:],
)
_ => {
let (revision, key_length_bits) = self.password_settings()
pdf_authenticate_owner_password(
no_encrypt_metadata,
password,
revision,
self.user_entry[:],
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
)
}
}
}
///|
pub fn PdfEncryptionValues::file_key_for_owner_password(
self : PdfEncryptionValues,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> PdfBytes? raise PdfError {
let no_encrypt_metadata = self.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match self.crypt_type {
PdfCryptAESV3(iso) =>
match
pdf_file_key_for_owner_password_aesv3(
iso,
password,
self.user_entry[:],
self.owner_entry[:],
self.owner_encryption_key,
) {
Some(key) => {
self.validate_aesv3_permissions(key[:])
Some(key)
}
None => None
}
_ => {
let (revision, key_length_bits) = self.password_settings()
let user_password = pdf_user_password_from_owner_password(
revision,
password,
self.owner_entry[:],
key_length_bits,
)
if pdf_authenticate_user_password(
no_encrypt_metadata,
user_password[:],
revision,
self.user_entry[:],
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
) {
Some(
pdf_encryption_file_key(
no_encrypt_metadata,
user_password[:],
revision,
self.owner_entry[:],
self.permissions,
self.file_id[:],
key_length_bits,
),
)
} else {
None
}
}
}
}
///|
fn PdfDocument::encryption_object_number(self : PdfDocument) -> Int {
match self.trailer_dict().lookup_immediate(pdf_crypt_encrypt_name) {
Some(PdfIndirect(number)) => number
_ => 0
}
}
///|
fn pdf_crypt_file_id_object(file_id : BytesView) -> PdfObject {
let owned = file_id.to_owned()
PdfArray([PdfString(owned), PdfString(owned)])
}
///|
fn PdfDocument::with_file_id(
self : PdfDocument,
file_id? : BytesView = [],
) -> (PdfDocument, PdfBytes) raise PdfError {
match self.lookup_direct(pdf_crypt_id_name, self.trailer_dict()) {
Some(PdfArray([PdfString(existing), _])) => (self.copy(), existing)
_ => {
let id = if file_id.length() > 0 {
file_id.to_owned()
} else {
pdf_generate_current_id_bytes("")
}
let output = self.copy()
output.set_trailer_dict(
output
.trailer_dict()
.add_dict_entry(pdf_crypt_id_name, pdf_crypt_file_id_object(id[:])),
)
(output, id)
}
}
}
///|
fn pdf_crypt_deterministic_seed32(
seed : BytesView,
purpose : BytesView,
) -> PdfBytes {
pdf_sha256_digest(
pdf_crypt_concat_bytes([
@ascii.encode("pdflite-deterministic-aes-provider")[:],
seed,
purpose,
]),
)
}
///|
fn pdf_crypt_deterministic_bytes(
seed : BytesView,
purpose : BytesView,
length : Int,
) -> PdfBytes {
if length <= 0 {
Bytes::new(0)
} else {
let rand = @random.Rand::chacha8(
seed=pdf_crypt_deterministic_seed32(seed, purpose),
)
Bytes::makei(length, _ => rand.uint(limit=256U).to_byte())
}
}
///|
#warnings("-unused_error_type")
fn pdf_deterministic_aes_iv_for_seed(
seed : BytesView,
object_number : Int,
generation : Int,
index : Int,
) -> PdfBytes raise PdfError {
let purpose = @ascii.encode(
"aes-iv:" +
object_number.to_string() +
":" +
generation.to_string() +
":" +
index.to_string(),
)
pdf_crypt_deterministic_bytes(seed, purpose[:], 16)
}
///|
pub fn pdf_deterministic_aes_iv_provider_from_seed(
seed : BytesView,
) -> (Int, Int, Int) -> PdfBytes raise PdfError {
let owned_seed = seed.to_owned()
fn(object_number, generation, index) raise PdfError {
pdf_deterministic_aes_iv_for_seed(
owned_seed[:],
object_number,
generation,
index,
)
}
}
///|
#warnings("-unused_error_type")
fn pdf_deterministic_aesv3_random_for_seed(
seed : BytesView,
field : PdfAesV3RandomField,
length : Int,
) -> PdfBytes raise PdfError {
pdf_crypt_deterministic_bytes(
seed,
pdf_aesv3_random_field_key(field).view(),
length,
)
}
///|
pub fn pdf_deterministic_aesv3_random_provider_from_seed(
seed : BytesView,
) -> (PdfAesV3RandomField, Int) -> PdfBytes raise PdfError {
let owned_seed = seed.to_owned()
fn(field, length) raise PdfError {
pdf_deterministic_aesv3_random_for_seed(owned_seed[:], field, length)
}
}
///|
fn PdfDocument::finish_decrypted_copy(
self : PdfDocument,
values : PdfEncryptionValues,
) -> Unit raise PdfError {
self.set_trailer_dict(
self.trailer_dict().remove_dict_entry(pdf_crypt_encrypt_name),
)
self.set_saved_encryption(Some({ values, }))
}
///|
fn PdfDocument::decrypt_with_file_key(
self : PdfDocument,
values : PdfEncryptionValues,
file_key : PdfBytes,
no_encrypt_metadata? : Bool = false,
) -> PdfDecryptionResult raise PdfError {
let no_encrypt_metadata = values.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match values.crypt_type {
PdfCryptARC4(key_length_bits, _) => {
let output = self.arc4_crypt_parsed_objects(
file_key[:],
key_length_bits,
no_encrypt_metadata~,
skip_object_number=self.encryption_object_number(),
)
output.finish_decrypted_copy(values)
{
document: output,
denied_permissions: pdf_permissions_of_p(values.permissions),
}
}
PdfCryptAESV2 => {
let output = self.aesv2_decrypt_parsed_objects(
file_key[:],
128,
no_encrypt_metadata~,
skip_object_number=self.encryption_object_number(),
)
output.finish_decrypted_copy(values)
{
document: output,
denied_permissions: pdf_permissions_of_p(values.permissions),
}
}
PdfCryptAESV3(_) => {
let output = self.aesv3_decrypt_parsed_objects(
file_key[:],
no_encrypt_metadata~,
skip_object_number=self.encryption_object_number(),
)
output.finish_decrypted_copy(values)
{
document: output,
denied_permissions: pdf_permissions_of_p(values.permissions),
}
}
}
}
///|
fn PdfEncryptionValues::file_key_for_available_password(
self : PdfEncryptionValues,
user_password : BytesView?,
owner_password : BytesView?,
no_encrypt_metadata? : Bool = false,
) -> PdfBytes? raise PdfError {
match user_password {
Some(password) =>
match self.file_key_for_user_password(password, no_encrypt_metadata~) {
Some(key) => return Some(key)
None =>
match owner_password {
Some(password) =>
self.file_key_for_owner_password(password, no_encrypt_metadata~)
None => None
}
}
None =>
match owner_password {
Some(password) =>
self.file_key_for_owner_password(password, no_encrypt_metadata~)
None => self.file_key_for_user_password([], no_encrypt_metadata~)
}
}
}
///|
fn PdfEncryptionValues::decrypt_single_stream_with_file_key(
self : PdfEncryptionValues,
object_number : Int,
generation : Int,
stream : PdfObject,
file_key : BytesView,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
let no_encrypt_metadata = self.effective_no_encrypt_metadata(
no_encrypt_metadata,
)
match self.crypt_type {
PdfCryptARC4(key_length_bits, _) =>
pdf_arc4_crypt_pdf_object(
object_number,
generation,
file_key,
key_length_bits,
stream,
no_encrypt_metadata~,
)
PdfCryptAESV2 =>
pdf_aesv2_decrypt_pdf_object(
object_number,
generation,
file_key,
128,
stream,
no_encrypt_metadata~,
)
PdfCryptAESV3(_) =>
pdf_aesv3_decrypt_pdf_object(file_key, stream, no_encrypt_metadata~)
}
}
///|
pub fn PdfDocument::decrypt_single_stream(
self : PdfDocument,
object_number : Int,
generation : Int,
stream : PdfObject,
user_password? : BytesView? = None,
owner_password? : BytesView? = None,
no_encrypt_metadata? : Bool = false,
) -> PdfObject? raise PdfError {
if !self.is_encrypted() {
Some(stream)
} else {
let values = self.encryption_values()
match
values.file_key_for_available_password(
user_password,
owner_password,
no_encrypt_metadata~,
) {
Some(file_key) =>
Some(
values.decrypt_single_stream_with_file_key(
object_number,
generation,
stream,
file_key[:],
no_encrypt_metadata~,
),
)
None => None
}
}
}
///|
pub fn PdfDocument::decrypt_with_passwords(
self : PdfDocument,
user_password? : BytesView? = None,
owner_password? : BytesView? = None,
no_encrypt_metadata? : Bool = false,
) -> PdfDecryptionResult? raise PdfError {
if !self.is_encrypted() {
Some({ document: self.copy(), denied_permissions: [] })
} else {
let values = self.encryption_values()
match
values.file_key_for_available_password(
user_password,
owner_password,
no_encrypt_metadata~,
) {
Some(file_key) =>
Some(self.decrypt_with_file_key(values, file_key, no_encrypt_metadata~))
None => None
}
}
}
///|
pub fn PdfDocument::encrypt_40bit(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
self.encrypt_arc4_standard(
user_password,
owner_password,
denied_permissions,
1,
2,
40,
false,
file_id~,
)
}
///|
pub fn PdfDocument::encrypt_128bit(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
self.encrypt_arc4_standard(
user_password,
owner_password,
denied_permissions,
2,
3,
128,
true,
file_id~,
)
}
///|
pub fn PdfDocument::encrypt_128bit_r4(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
encrypt_metadata? : Bool = true,
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
let no_encrypt_metadata = !encrypt_metadata
let (document, id) = self.with_file_id(file_id~)
let permissions = pdf_p_of_permissions(denied_permissions)
let owner_entry = pdf_make_owner_password_entry(
4, owner_password, user_password, 128,
)
let user_entry = pdf_make_user_password_entry(
no_encrypt_metadata,
user_password,
4,
owner_entry[:],
permissions,
id[:],
128,
)
let file_key = pdf_encryption_file_key(
no_encrypt_metadata,
user_password,
4,
owner_entry[:],
permissions,
id[:],
128,
)
let encrypted = document.arc4_crypt_parsed_objects(
file_key[:],
128,
no_encrypt_metadata~,
)
let std_cf = pdf_crypt_std_cf_name
let encrypt_dictionary : PdfObject = PdfDictionary([
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(4)),
(
pdf_crypt_cf_name,
PdfDictionary([
(
std_cf,
PdfDictionary([
(pdf_crypt_length_name, PdfInteger(16)),
(pdf_crypt_auth_event_name, PdfNameObject(pdf_crypt_doc_open_name)),
(pdf_crypt_cfm_name, PdfNameObject(pdf_crypt_v2_name)),
]),
),
]),
),
(pdf_crypt_encrypt_metadata_name, PdfBoolean(encrypt_metadata)),
(pdf_crypt_length_name, PdfInteger(128)),
(pdf_crypt_r_name, PdfInteger(4)),
(pdf_crypt_o_name, PdfString(owner_entry)),
(pdf_crypt_u_name, PdfString(user_entry)),
(pdf_crypt_p_name, PdfInteger(permissions)),
(pdf_crypt_strf_name, PdfNameObject(std_cf)),
(pdf_crypt_stmf_name, PdfNameObject(std_cf)),
])
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
pub fn PdfDocument::encrypt_128bit_aesv2_with_iv_provider(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
encrypt_metadata? : Bool = true,
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
let no_encrypt_metadata = !encrypt_metadata
let (document, id) = self.with_file_id(file_id~)
let permissions = pdf_p_of_permissions(denied_permissions)
let owner_entry = pdf_make_owner_password_entry(
4, owner_password, user_password, 128,
)
let user_entry = pdf_make_user_password_entry(
no_encrypt_metadata,
user_password,
4,
owner_entry[:],
permissions,
id[:],
128,
)
let file_key = pdf_encryption_file_key(
no_encrypt_metadata,
user_password,
4,
owner_entry[:],
permissions,
id[:],
128,
)
let encrypted = document.aesv2_encrypt_parsed_objects_with_iv_provider(
file_key[:],
128,
iv_provider,
no_encrypt_metadata~,
)
let std_cf = pdf_crypt_std_cf_name
let encrypt_dictionary : PdfObject = PdfDictionary([
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(4)),
(
pdf_crypt_cf_name,
PdfDictionary([
(
std_cf,
PdfDictionary([
(pdf_crypt_length_name, PdfInteger(16)),
(pdf_crypt_auth_event_name, PdfNameObject(pdf_crypt_doc_open_name)),
(pdf_crypt_cfm_name, PdfNameObject(pdf_crypt_aesv2_name)),
]),
),
]),
),
(pdf_crypt_encrypt_metadata_name, PdfBoolean(encrypt_metadata)),
(pdf_crypt_length_name, PdfInteger(128)),
(pdf_crypt_r_name, PdfInteger(4)),
(pdf_crypt_o_name, PdfString(owner_entry)),
(pdf_crypt_u_name, PdfString(user_entry)),
(pdf_crypt_p_name, PdfInteger(permissions)),
(pdf_crypt_strf_name, PdfNameObject(std_cf)),
(pdf_crypt_stmf_name, PdfNameObject(std_cf)),
])
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
pub fn PdfDocument::encrypt_256bit_aesv3_with_providers(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
random_provider : (PdfAesV3RandomField, Int) -> PdfBytes raise PdfError,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
iso? : Bool = false,
encrypt_metadata? : Bool = true,
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
let no_encrypt_metadata = !encrypt_metadata
let (document, id) = self.with_file_id(file_id~)
let permissions = pdf_p_of_permissions(denied_permissions)
let file_key = pdf_aesv3_random_bytes(random_provider, PdfAesV3FileKey, 32)
let user_validation_salt = pdf_aesv3_random_bytes(
random_provider,
PdfAesV3UserValidationSalt,
8,
)
let user_key_salt = pdf_aesv3_random_bytes(
random_provider,
PdfAesV3UserKeySalt,
8,
)
let owner_validation_salt = pdf_aesv3_random_bytes(
random_provider,
PdfAesV3OwnerValidationSalt,
8,
)
let owner_key_salt = pdf_aesv3_random_bytes(
random_provider,
PdfAesV3OwnerKeySalt,
8,
)
let permissions_padding = pdf_aesv3_random_bytes(
random_provider,
PdfAesV3PermissionsPadding,
4,
)
let user_entry = pdf_aesv3_entry(
iso,
user_password,
user_validation_salt[:],
user_key_salt[:],
)
let owner_entry = pdf_aesv3_entry(
iso,
owner_password,
owner_validation_salt[:],
owner_key_salt[:],
user_entry=user_entry[:],
)
let user_encryption_key = pdf_aesv3_wrap_file_key(
iso,
user_password,
user_key_salt[:],
file_key[:],
)
let owner_encryption_key = pdf_aesv3_wrap_file_key(
iso,
owner_password,
owner_key_salt[:],
file_key[:],
user_entry=user_entry[:],
)
let permissions_entry = pdf_aesv3_permissions_entry(
file_key[:],
permissions,
encrypt_metadata,
permissions_padding[:],
)
let encrypted = document.aesv3_encrypt_parsed_objects_with_iv_provider(
file_key[:],
iv_provider,
no_encrypt_metadata~,
)
let encrypt_dictionary = pdf_crypt_saved_aesv3_dictionary({
crypt_type: PdfCryptAESV3(iso),
user_entry,
owner_entry,
permissions,
file_id: id,
encrypt_metadata,
permissions_entry: Some(permissions_entry),
user_encryption_key: Some(user_encryption_key),
owner_encryption_key: Some(owner_encryption_key),
})
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
pub fn PdfDocument::encrypt_256bit_aesv3_iso_with_providers(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
random_provider : (PdfAesV3RandomField, Int) -> PdfBytes raise PdfError,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
encrypt_metadata? : Bool = true,
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
self.encrypt_256bit_aesv3_with_providers(
user_password,
owner_password,
denied_permissions,
random_provider,
iv_provider,
iso=true,
encrypt_metadata~,
file_id~,
)
}
///|
fn PdfDocument::encrypt_arc4_standard(
self : PdfDocument,
user_password : BytesView,
owner_password : BytesView,
denied_permissions : ArrayView[PdfPermission],
version : Int,
revision : Int,
key_length_bits : Int,
write_length : Bool,
file_id? : BytesView = [],
) -> PdfDocument raise PdfError {
let (document, id) = self.with_file_id(file_id~)
let permissions = pdf_p_of_permissions(denied_permissions)
let owner_entry = pdf_make_owner_password_entry(
revision, owner_password, user_password, key_length_bits,
)
let user_entry = pdf_make_user_password_entry(
false,
user_password,
revision,
owner_entry[:],
permissions,
id[:],
key_length_bits,
)
let file_key = pdf_encryption_file_key(
false,
user_password,
revision,
owner_entry[:],
permissions,
id[:],
key_length_bits,
)
let encrypted = document.arc4_crypt_parsed_objects(
file_key[:],
key_length_bits,
)
let entries : Array[(PdfName, PdfObject)] = [
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(version)),
(pdf_crypt_r_name, PdfInteger(revision)),
(pdf_crypt_o_name, PdfString(owner_entry)),
(pdf_crypt_u_name, PdfString(user_entry)),
(pdf_crypt_p_name, PdfInteger(permissions)),
]
if write_length {
entries.push((pdf_crypt_length_name, PdfInteger(key_length_bits)))
}
let encrypt_dictionary : PdfObject = PdfDictionary(entries)
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
fn pdf_crypt_saved_arc4_dictionary(
values : PdfEncryptionValues,
key_length_bits : Int,
revision : Int,
) -> PdfObject {
if revision == 4 {
let std_cf = pdf_crypt_std_cf_name
PdfDictionary([
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(4)),
(
pdf_crypt_cf_name,
PdfDictionary([
(
std_cf,
PdfDictionary([
(pdf_crypt_length_name, PdfInteger(key_length_bits / 8)),
(
pdf_crypt_auth_event_name,
PdfNameObject(pdf_crypt_doc_open_name),
),
(pdf_crypt_cfm_name, PdfNameObject(pdf_crypt_v2_name)),
]),
),
]),
),
(pdf_crypt_encrypt_metadata_name, PdfBoolean(values.encrypt_metadata)),
(pdf_crypt_length_name, PdfInteger(key_length_bits)),
(pdf_crypt_r_name, PdfInteger(4)),
(pdf_crypt_o_name, PdfString(values.owner_entry)),
(pdf_crypt_u_name, PdfString(values.user_entry)),
(pdf_crypt_p_name, PdfInteger(values.permissions)),
(pdf_crypt_strf_name, PdfNameObject(std_cf)),
(pdf_crypt_stmf_name, PdfNameObject(std_cf)),
])
} else {
let entries : Array[(PdfName, PdfObject)] = [
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(
pdf_crypt_v_name,
PdfInteger(if revision == 2 && key_length_bits == 40 { 1 } else { 2 }),
),
(pdf_crypt_r_name, PdfInteger(revision)),
(pdf_crypt_o_name, PdfString(values.owner_entry)),
(pdf_crypt_u_name, PdfString(values.user_entry)),
(pdf_crypt_p_name, PdfInteger(values.permissions)),
]
if key_length_bits != 40 {
entries.push((pdf_crypt_length_name, PdfInteger(key_length_bits)))
}
PdfDictionary(entries)
}
}
///|
fn pdf_crypt_saved_aesv2_dictionary(values : PdfEncryptionValues) -> PdfObject {
let std_cf = pdf_crypt_std_cf_name
PdfDictionary([
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(4)),
(
pdf_crypt_cf_name,
PdfDictionary([
(
std_cf,
PdfDictionary([
(pdf_crypt_length_name, PdfInteger(16)),
(pdf_crypt_auth_event_name, PdfNameObject(pdf_crypt_doc_open_name)),
(pdf_crypt_cfm_name, PdfNameObject(pdf_crypt_aesv2_name)),
]),
),
]),
),
(pdf_crypt_encrypt_metadata_name, PdfBoolean(values.encrypt_metadata)),
(pdf_crypt_length_name, PdfInteger(128)),
(pdf_crypt_r_name, PdfInteger(4)),
(pdf_crypt_o_name, PdfString(values.owner_entry)),
(pdf_crypt_u_name, PdfString(values.user_entry)),
(pdf_crypt_p_name, PdfInteger(values.permissions)),
(pdf_crypt_strf_name, PdfNameObject(std_cf)),
(pdf_crypt_stmf_name, PdfNameObject(std_cf)),
])
}
///|
fn pdf_crypt_saved_aesv3_dictionary(
values : PdfEncryptionValues,
) -> PdfObject raise PdfError {
let std_cf = pdf_crypt_std_cf_name
let revision = match values.crypt_type {
PdfCryptAESV3(true) => 6
_ => 5
}
PdfDictionary([
(pdf_crypt_filter_name, PdfNameObject(pdf_crypt_standard_name)),
(pdf_crypt_v_name, PdfInteger(5)),
(
pdf_crypt_cf_name,
PdfDictionary([
(
std_cf,
PdfDictionary([
(pdf_crypt_length_name, PdfInteger(32)),
(pdf_crypt_auth_event_name, PdfNameObject(pdf_crypt_doc_open_name)),
(pdf_crypt_cfm_name, PdfNameObject(pdf_crypt_aesv3_name)),
]),
),
]),
),
(pdf_crypt_encrypt_metadata_name, PdfBoolean(values.encrypt_metadata)),
(pdf_crypt_length_name, PdfInteger(256)),
(pdf_crypt_r_name, PdfInteger(revision)),
(pdf_crypt_o_name, PdfString(values.owner_entry)),
(pdf_crypt_u_name, PdfString(values.user_entry)),
(
pdf_crypt_oe_name,
PdfString(
pdf_aesv3_crypt_entry(pdf_crypt_oe_name, values.owner_encryption_key),
),
),
(
pdf_crypt_ue_name,
PdfString(
pdf_aesv3_crypt_entry(pdf_crypt_ue_name, values.user_encryption_key),
),
),
(
pdf_aesv3_permissions_key(),
PdfString(
pdf_crypt_chop_string(
pdf_aesv3_permissions_key(),
match values.permissions_entry {
Some(value) => value
None =>
raise EncryptionDictionaryEntryExpected(
pdf_aesv3_permissions_key(),
)
},
16,
),
),
),
(pdf_crypt_p_name, PdfInteger(values.permissions)),
(pdf_crypt_strf_name, PdfNameObject(std_cf)),
(pdf_crypt_stmf_name, PdfNameObject(std_cf)),
])
}
///|
fn PdfEncryptionValues::file_key_for_recrypt_password(
self : PdfEncryptionValues,
password : BytesView,
) -> PdfBytes raise PdfError {
match self.file_key_for_user_password(password) {
Some(key) => key
None =>
match self.file_key_for_owner_password(password) {
Some(key) => key
None => raise EncryptionRecryptFailed
}
}
}
///|
fn PdfDocument::pdf_crypt_prepare_plaintext_for_recrypt(
self : PdfDocument,
) -> Unit {
for number in self.object_numbers() {
match self.objects.get_entry(number) {
Some({ data: ObjectParsedAlreadyDecrypted(object), generation }) =>
self.objects.set_entry(number, {
data: ObjectParsed(object),
generation,
})
_ => ()
}
}
}
///|
fn PdfDocument::recrypt_arc4_with_file_key(
self : PdfDocument,
values : PdfEncryptionValues,
file_key : BytesView,
key_length_bits : Int,
revision : Int,
) -> PdfDocument raise PdfError {
let (document, _) = self.with_file_id(file_id=values.file_id[:])
document.pdf_crypt_prepare_plaintext_for_recrypt()
document.set_saved_encryption(None)
let encrypted = document.arc4_crypt_parsed_objects(
file_key,
key_length_bits,
no_encrypt_metadata=!values.encrypt_metadata,
)
encrypted.set_saved_encryption(None)
let encrypt_dictionary = pdf_crypt_saved_arc4_dictionary(
values, key_length_bits, revision,
)
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
fn PdfDocument::recrypt_aesv2_with_file_key(
self : PdfDocument,
values : PdfEncryptionValues,
file_key : BytesView,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
) -> PdfDocument raise PdfError {
let (document, _) = self.with_file_id(file_id=values.file_id[:])
document.pdf_crypt_prepare_plaintext_for_recrypt()
document.set_saved_encryption(None)
let encrypted = document.aesv2_encrypt_parsed_objects_with_iv_provider(
file_key,
128,
iv_provider,
no_encrypt_metadata=!values.encrypt_metadata,
)
encrypted.set_saved_encryption(None)
let encrypt_dictionary = pdf_crypt_saved_aesv2_dictionary(values)
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
fn PdfDocument::recrypt_aesv3_with_file_key(
self : PdfDocument,
values : PdfEncryptionValues,
file_key : BytesView,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
) -> PdfDocument raise PdfError {
let (document, _) = self.with_file_id(file_id=values.file_id[:])
document.pdf_crypt_prepare_plaintext_for_recrypt()
document.set_saved_encryption(None)
let encrypted = document.aesv3_encrypt_parsed_objects_with_iv_provider(
file_key,
iv_provider,
no_encrypt_metadata=!values.encrypt_metadata,
)
encrypted.set_saved_encryption(None)
let encrypt_dictionary = pdf_crypt_saved_aesv3_dictionary(values)
let encrypt_number = encrypted.add_object(encrypt_dictionary)
encrypted.set_trailer_dict(
encrypted
.trailer_dict()
.add_dict_entry(pdf_crypt_encrypt_name, PdfIndirect(encrypt_number)),
)
encrypted
}
///|
pub fn PdfDocument::recrypt_with_password(
self : PdfDocument,
password : BytesView,
) -> PdfDocument raise PdfError {
let saved = match self.saved_encryption() {
Some(saved) => saved
None => raise EncryptionSavedStateExpected
}
let values = saved.values
match values.crypt_type {
PdfCryptARC4(key_length_bits, revision) if (
key_length_bits == 40 || key_length_bits == 128
) &&
(revision == 2 || revision == 3 || revision == 4) => {
let file_key = values.file_key_for_recrypt_password(password)
self.recrypt_arc4_with_file_key(
values,
file_key[:],
key_length_bits,
revision,
)
}
_ => raise EncryptionRecryptUnsupported(values.crypt_type)
}
}
///|
pub fn PdfDocument::recrypt_with_password_aesv2_with_iv_provider(
self : PdfDocument,
password : BytesView,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
) -> PdfDocument raise PdfError {
let saved = match self.saved_encryption() {
Some(saved) => saved
None => raise EncryptionSavedStateExpected
}
let values = saved.values
match values.crypt_type {
PdfCryptAESV2 => {
let file_key = values.file_key_for_recrypt_password(password)
self.recrypt_aesv2_with_file_key(values, file_key[:], iv_provider)
}
_ => raise EncryptionRecryptUnsupported(values.crypt_type)
}
}
///|
pub fn PdfDocument::recrypt_with_password_aesv3_with_iv_provider(
self : PdfDocument,
password : BytesView,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
) -> PdfDocument raise PdfError {
let saved = match self.saved_encryption() {
Some(saved) => saved
None => raise EncryptionSavedStateExpected
}
let values = saved.values
match values.crypt_type {
PdfCryptAESV3(_) => {
let file_key = values.file_key_for_recrypt_password(password)
self.recrypt_aesv3_with_file_key(values, file_key[:], iv_provider)
}
_ => raise EncryptionRecryptUnsupported(values.crypt_type)
}
}
///|
pub fn PdfDocument::decrypt_with_user_password(
self : PdfDocument,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> PdfDecryptionResult? raise PdfError {
match self.trailer_dict().lookup_immediate(pdf_crypt_encrypt_name) {
None => Some({ document: self.copy(), denied_permissions: [] })
Some(_) => {
let values = self.encryption_values()
let file_key = match
values.file_key_for_user_password(password, no_encrypt_metadata~) {
Some(key) => key
None => return None
}
Some(self.decrypt_with_file_key(values, file_key, no_encrypt_metadata~))
}
}
}
///|
pub fn PdfDocument::decrypt_with_owner_password(
self : PdfDocument,
password : BytesView,
no_encrypt_metadata? : Bool = false,
) -> PdfDecryptionResult? raise PdfError {
match self.trailer_dict().lookup_immediate(pdf_crypt_encrypt_name) {
None => Some({ document: self.copy(), denied_permissions: [] })
Some(_) => {
let values = self.encryption_values()
let file_key = match
values.file_key_for_owner_password(password, no_encrypt_metadata~) {
Some(key) => key
None => return None
}
Some(self.decrypt_with_file_key(values, file_key, no_encrypt_metadata~))
}
}
}
///|
fn pdf_password_padding_fixed() -> FixedArray[Byte] {
[
0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff,
0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c,
0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a,
]
}
///|
pub fn pdf_password_padding() -> PdfBytes {
Bytes::from_array(pdf_password_padding_fixed())
}
///|
pub fn pdf_pad_password(password : BytesView) -> PdfBytes {
let padding = pdf_password_padding_fixed()
let output : Array[Byte] = []
let copied = if password.length() < 32 { password.length() } else { 32 }
for index in 0.. PdfBytes {
let output : Array[Byte] = []
for part in parts {
for byte in part {
output.push(byte)
}
}
Bytes::from_array(output)
}
///|
fn pdf_aesv3_cbc_encrypt_no_padding_with_iv(
key : BytesView,
iv : BytesView,
data : BytesView,
) -> PdfBytes raise PdfError {
guard data.length() % 16 == 0 else {
raise InvalidCryptoDataLength(data.length())
}
let output : Array[Byte] = []
let mut previous = pdf_crypt_chop_view(pdf_crypt_iv_name, iv, 16)
let mut position = 0
while position < data.length() {
let block : Array[Byte] = []
for index in 0..<16 {
block.push(
(data[position + index].to_int() ^ previous[index].to_int()).to_byte(),
)
}
previous = pdf_aes_encrypt_block(key, Bytes::from_array(block)[:])
for byte in previous {
output.push(byte)
}
position += 16
}
Bytes::from_array(output)
}
///|
fn pdf_aesv3_shamix_mod3(data : BytesView) -> Int {
let mut total = 0
for index in 0..<16 {
total += data[index].to_int()
}
total % 3
}
///|
fn pdf_aesv3_shamix_repeat(data : BytesView, count : Int) -> PdfBytes {
let output : Array[Byte] = Array::new(capacity=data.length() * count)
for _ in 0.. PdfBytes {
match pdf_aesv3_shamix_mod3(data) {
0 => pdf_sha256_digest(data)
1 => pdf_sha384_digest(data)
_ => pdf_sha512_digest(data)
}
}
///|
fn pdf_aesv3_shamix(
password : BytesView,
user_entry : BytesView?,
input : BytesView,
) -> PdfBytes raise PdfError {
let user_data = match user_entry {
Some(value) => value
None => []
}
let mut digest = pdf_sha256_digest(input)
let mut round = 0
let mut last_encrypted_byte = 0
while !(round >= 64 && last_encrypted_byte <= round - 32) {
round += 1
let repeated_input = pdf_crypt_concat_bytes([password, digest[:], user_data])
let repeated = pdf_aesv3_shamix_repeat(repeated_input[:], 64)
let encrypted = pdf_aesv3_cbc_encrypt_no_padding_with_iv(
digest[:16],
digest[16:32],
repeated[:],
)
last_encrypted_byte = encrypted[encrypted.length() - 1].to_int()
digest = pdf_aesv3_shamix_next_hash(encrypted[:])
}
digest[:32].to_owned()
}
///|
fn pdf_aesv3_password_hash(
iso : Bool,
password : BytesView,
salt : BytesView,
user_entry? : BytesView = [],
) -> PdfBytes raise PdfError {
let input = pdf_crypt_concat_bytes([password, salt, user_entry])
if iso {
let user_data = if user_entry.length() == 0 {
None
} else {
Some(user_entry)
}
pdf_aesv3_shamix(password, user_data, input[:])
} else {
pdf_sha256_digest(input[:])
}
}
///|
fn pdf_aesv3_random_field_key(field : PdfAesV3RandomField) -> PdfName {
match field {
PdfAesV3FileKey => pdf_crypt_file_key_name
PdfAesV3UserValidationSalt => pdf_crypt_user_validation_salt_name
PdfAesV3UserKeySalt => pdf_crypt_user_key_salt_name
PdfAesV3OwnerValidationSalt => pdf_crypt_owner_validation_salt_name
PdfAesV3OwnerKeySalt => pdf_crypt_owner_key_salt_name
PdfAesV3PermissionsPadding => pdf_crypt_perms_padding_name
}
}
///|
fn pdf_aesv3_random_bytes(
random_provider : (PdfAesV3RandomField, Int) -> PdfBytes raise PdfError,
field : PdfAesV3RandomField,
length : Int,
) -> PdfBytes raise PdfError {
pdf_crypt_chop_string(
pdf_aesv3_random_field_key(field),
random_provider(field, length),
length,
)
}
///|
fn pdf_aesv3_entry(
iso : Bool,
password : BytesView,
validation_salt : BytesView,
key_salt : BytesView,
user_entry? : BytesView = [],
) -> PdfBytes raise PdfError {
let digest = pdf_aesv3_password_hash(
iso,
password,
validation_salt,
user_entry~,
)
pdf_crypt_concat_bytes([digest[:32], validation_salt, key_salt])
}
///|
fn pdf_aesv3_cbc_encrypt_no_padding(
key : BytesView,
data : BytesView,
) -> PdfBytes raise PdfError {
pdf_aesv3_cbc_encrypt_no_padding_with_iv(key, Bytes::new(16)[:], data)
}
///|
fn pdf_aesv3_wrap_file_key(
iso : Bool,
password : BytesView,
key_salt : BytesView,
file_key : BytesView,
user_entry? : BytesView = [],
) -> PdfBytes raise PdfError {
let digest = pdf_aesv3_password_hash(iso, password, key_salt, user_entry~)
pdf_aesv3_cbc_encrypt_no_padding(digest[:], file_key)
}
///|
fn pdf_aesv3_permissions_entry(
file_key : BytesView,
permissions : Int,
encrypt_metadata : Bool,
padding : BytesView,
) -> PdfBytes raise PdfError {
let block : Array[Byte] = []
for shift in [0, 8, 16, 24] {
block.push(pdf_crypt_low_byte(permissions, shift))
}
for _ in 0..<4 {
block.push((255).to_byte())
}
block.push(if encrypt_metadata { b'T' } else { b'F' })
block.push(b'a')
block.push(b'd')
block.push(b'b')
for
byte in pdf_crypt_chop_view(
pdf_aesv3_random_field_key(PdfAesV3PermissionsPadding),
padding,
4,
) {
block.push(byte)
}
pdf_aes_encrypt_block(file_key, Bytes::from_array(block)[:])
}
///|
pub fn pdf_authenticate_user_password_aesv3(
iso : Bool,
password : BytesView,
user_entry : BytesView,
) -> Bool raise PdfError {
if user_entry.length() < 48 {
raise EncryptionDictionaryEntryTooShort(pdf_crypt_u_name)
}
let expected = pdf_aesv3_password_hash(iso, password, user_entry[32:40])
pdf_bytes_prefix_equal(user_entry, expected[:], 32)
}
///|
pub fn pdf_authenticate_owner_password_aesv3(
iso : Bool,
password : BytesView,
user_entry : BytesView,
owner_entry : BytesView,
) -> Bool raise PdfError {
if owner_entry.length() < 48 {
raise EncryptionDictionaryEntryTooShort(pdf_crypt_o_name)
}
if user_entry.length() < 48 {
raise EncryptionDictionaryEntryTooShort(pdf_crypt_u_name)
}
let expected = pdf_aesv3_password_hash(
iso,
password,
owner_entry[32:40],
user_entry=user_entry[:48],
)
pdf_bytes_prefix_equal(owner_entry, expected[:], 32)
}
///|
fn pdf_aesv3_crypt_entry(
key : PdfName,
entry : PdfBytes?,
) -> PdfBytes raise PdfError {
match entry {
Some(value) => pdf_crypt_chop_string(key, value, 32)
None => raise EncryptionDictionaryEntryExpected(key)
}
}
///|
fn pdf_aesv3_unwrap_file_key(
digest : PdfBytes,
encrypted_key : PdfBytes,
) -> PdfBytes raise PdfError {
let zero_iv = Bytes::new(16)
let data = pdf_crypt_concat_bytes([zero_iv[:], encrypted_key[:]])
pdf_aes_cbc_decrypt(digest[:], data[:], remove_padding=false)
}
///|
fn pdf_aesv3_permissions_key() -> PdfName {
pdf_crypt_perms_name
}
///|
fn pdf_aesv3_permissions_from_entry(
file_key : BytesView,
permissions_entry : BytesView,
) -> Int raise PdfError {
if permissions_entry.length() < 16 {
raise EncryptionDictionaryEntryTooShort(pdf_aesv3_permissions_key())
}
let decrypted = pdf_aes_ecb_decrypt(
file_key,
permissions_entry[:16],
remove_padding=false,
)
guard decrypted[9] == b'a' && decrypted[10] == b'd' && decrypted[11] == b'b' else {
raise EncryptionPermissionsCorrupt
}
decrypted[0].to_int() |
(decrypted[1].to_int() << 8) |
(decrypted[2].to_int() << 16) |
(decrypted[3].to_int() << 24)
}
///|
fn PdfEncryptionValues::validate_aesv3_permissions(
self : PdfEncryptionValues,
file_key : BytesView,
) -> Unit raise PdfError {
let permissions_entry = match self.permissions_entry {
Some(value) => value
None => raise EncryptionDictionaryEntryExpected(pdf_aesv3_permissions_key())
}
let permissions = pdf_aesv3_permissions_from_entry(
file_key,
permissions_entry[:],
)
if permissions != self.permissions {
raise EncryptionPermissionsMismatch(permissions, self.permissions)
}
}
///|
fn pdf_file_key_for_user_password_aesv3(
iso : Bool,
password : BytesView,
user_entry : BytesView,
user_encryption_key : PdfBytes?,
) -> PdfBytes? raise PdfError {
if pdf_authenticate_user_password_aesv3(iso, password, user_entry) {
let digest = pdf_aesv3_password_hash(iso, password, user_entry[40:48])
let encrypted_key = pdf_aesv3_crypt_entry(
pdf_crypt_ue_name, user_encryption_key,
)
Some(pdf_aesv3_unwrap_file_key(digest, encrypted_key))
} else {
None
}
}
///|
fn pdf_file_key_for_owner_password_aesv3(
iso : Bool,
password : BytesView,
user_entry : BytesView,
owner_entry : BytesView,
owner_encryption_key : PdfBytes?,
) -> PdfBytes? raise PdfError {
if pdf_authenticate_owner_password_aesv3(
iso, password, user_entry, owner_entry,
) {
let digest = pdf_aesv3_password_hash(
iso,
password,
owner_entry[40:48],
user_entry=user_entry[:48],
)
let encrypted_key = pdf_aesv3_crypt_entry(
pdf_crypt_oe_name, owner_encryption_key,
)
Some(pdf_aesv3_unwrap_file_key(digest, encrypted_key))
} else {
None
}
}
///|
fn pdf_crypt_low_byte(value : Int, shift : Int) -> Byte {
((value >> shift) & 0xff).to_byte()
}
///|
pub fn pdf_encryption_file_key(
no_encrypt_metadata : Bool,
password : BytesView,
revision : Int,
owner_entry : BytesView,
permissions : Int,
file_id : BytesView,
key_length_bits : Int,
) -> PdfBytes {
let md5 = @crypto.MD5::new()
let padded_password = pdf_pad_password(password)
md5.update(padded_password[:])
md5.update(owner_entry)
let permissions_bytes : FixedArray[Byte] = [
pdf_crypt_low_byte(permissions, 0),
pdf_crypt_low_byte(permissions, 8),
pdf_crypt_low_byte(permissions, 16),
pdf_crypt_low_byte(permissions, 24),
]
md5.update(permissions_bytes)
md5.update(file_id)
if revision >= 4 && no_encrypt_metadata {
let no_metadata_marker : FixedArray[Byte] = [0xff, 0xff, 0xff, 0xff]
md5.update(no_metadata_marker)
}
let key_length = key_length_bits / 8
let mut hashed = Bytes::from_array(md5.finalize())
if revision >= 3 {
for _ in 0..<50 {
hashed = pdf_md5_digest(hashed[:key_length])
}
}
hashed[:key_length].to_owned()
}
///|
fn pdf_key_xor_iteration(key : BytesView, iteration : Int) -> PdfBytes {
Bytes::makei(key.length(), index => {
(key[index].to_int() ^ iteration).to_byte()
})
}
///|
pub fn pdf_make_user_password_entry(
no_encrypt_metadata : Bool,
user_password : BytesView,
revision : Int,
owner_entry : BytesView,
permissions : Int,
file_id : BytesView,
key_length_bits : Int,
) -> PdfBytes raise PdfError {
let key = pdf_encryption_file_key(
no_encrypt_metadata, user_password, revision, owner_entry, permissions, file_id,
key_length_bits,
)
if revision == 2 {
pdf_arc4_crypt(key[:], pdf_password_padding()[:])
} else {
let md5 = @crypto.MD5::new()
md5.update(pdf_password_padding()[:])
md5.update(file_id)
let digest = Bytes::from_array(md5.finalize())
let mut encrypted = pdf_arc4_crypt(key[:], digest[:])
for iteration in 1..<20 {
let iteration_key = pdf_key_xor_iteration(key[:], iteration)
encrypted = pdf_arc4_crypt(iteration_key[:], encrypted[:])
}
let output : Array[Byte] = [ for byte in encrypted => byte ]
for _ in 0..<16 {
output.push(0)
}
Bytes::from_array(output)
}
}
///|
fn pdf_bytes_prefix_equal(
left : BytesView,
right : BytesView,
length : Int,
) -> Bool {
guard left.length() >= length && right.length() >= length else {
return false
}
for index in 0.. Bool raise PdfError {
let expected = pdf_make_user_password_entry(
no_encrypt_metadata, user_password, revision, owner_entry, permissions, file_id,
key_length_bits,
)
if revision >= 3 {
pdf_bytes_prefix_equal(user_entry, expected[:], 16)
} else {
pdf_bytes_prefix_equal(user_entry, expected[:], expected.length()) &&
user_entry.length() == expected.length()
}
}
///|
fn pdf_owner_password_key(
owner_password : BytesView,
revision : Int,
key_length_bits : Int,
) -> PdfBytes {
let padded_owner = pdf_pad_password(owner_password)
let mut digest = pdf_md5_digest(padded_owner[:])
if revision >= 3 {
for _ in 0..<50 {
digest = pdf_md5_digest(digest[:])
}
}
digest[:key_length_bits / 8].to_owned()
}
///|
pub fn pdf_make_owner_password_entry(
revision : Int,
owner_password : BytesView,
user_password : BytesView,
key_length_bits : Int,
) -> PdfBytes raise PdfError {
let source_password = if owner_password.length() == 0 {
user_password
} else {
owner_password
}
let key = pdf_owner_password_key(source_password, revision, key_length_bits)
let padded_user = pdf_pad_password(user_password)
if revision == 2 {
pdf_arc4_crypt(key[:], padded_user[:])
} else {
let mut encrypted = pdf_arc4_crypt(key[:], padded_user[:])
for iteration in 1..<20 {
let iteration_key = pdf_key_xor_iteration(key[:], iteration)
encrypted = pdf_arc4_crypt(iteration_key[:], encrypted[:])
}
encrypted
}
}
///|
pub fn pdf_user_password_from_owner_password(
revision : Int,
owner_password : BytesView,
owner_entry : BytesView,
key_length_bits : Int,
) -> PdfBytes raise PdfError {
let key = pdf_owner_password_key(owner_password, revision, key_length_bits)
if revision == 2 {
pdf_arc4_crypt(key[:], owner_entry)
} else {
let mut decrypted = owner_entry.to_owned()
let mut iteration = 19
while iteration >= 0 {
let iteration_key = pdf_key_xor_iteration(key[:], iteration)
decrypted = pdf_arc4_crypt(iteration_key[:], decrypted[:])
iteration -= 1
}
decrypted
}
}
///|
pub fn pdf_authenticate_owner_password(
no_encrypt_metadata : Bool,
owner_password : BytesView,
revision : Int,
user_entry : BytesView,
owner_entry : BytesView,
permissions : Int,
file_id : BytesView,
key_length_bits : Int,
) -> Bool raise PdfError {
let user_password = pdf_user_password_from_owner_password(
revision, owner_password, owner_entry, key_length_bits,
)
pdf_authenticate_user_password(
no_encrypt_metadata,
user_password[:],
revision,
user_entry,
owner_entry,
permissions,
file_id,
key_length_bits,
)
}
///|
fn pdf_crypt_stream_length_key() -> PdfName {
pdf_name_of_bytes(@ascii.encode("/Length"))
}
///|
fn pdf_crypt_stream_filter_key() -> PdfName {
pdf_crypt_filter_name
}
///|
fn pdf_crypt_stream_decodeparms_key() -> PdfName {
pdf_crypt_decodeparms_name
}
///|
fn pdf_crypt_stream_name_key() -> PdfName {
pdf_crypt_name_key_name
}
///|
fn pdf_crypt_stream_type_key() -> PdfName {
pdf_crypt_type_name
}
///|
fn pdf_crypt_object_is_name(object : PdfObject?, expected : PdfName) -> Bool {
match object {
Some(PdfNameObject(name)) => name == expected
_ => false
}
}
///|
fn pdf_crypt_first_filter_is_crypt(dictionary : PdfObject) -> Bool {
match dictionary.lookup_immediate(pdf_crypt_stream_filter_key()) {
Some(PdfNameObject(name)) => name == pdf_crypt_crypt_name
Some(PdfArray([PdfNameObject(name), ..])) => name == pdf_crypt_crypt_name
_ => false
}
}
///|
fn pdf_crypt_decodeparms_is_identity(dictionary : PdfObject) -> Bool {
match dictionary.lookup_immediate(pdf_crypt_stream_decodeparms_key()) {
Some(PdfDictionary(_) as decodeparms)
| Some(PdfArray([PdfDictionary(_) as decodeparms, ..])) =>
match decodeparms.lookup_immediate(pdf_crypt_stream_name_key()) {
Some(PdfNameObject(name)) => name == pdf_crypt_identity_name
None => true
_ => false
}
_ => true
}
}
///|
fn pdf_crypt_stream_is_identity(
no_encrypt_metadata : Bool,
dictionary : PdfObject,
) -> Bool {
if no_encrypt_metadata &&
pdf_crypt_object_is_name(
dictionary.lookup_immediate(pdf_crypt_stream_type_key()),
pdf_crypt_metadata_name,
) {
true
} else {
pdf_crypt_first_filter_is_crypt(dictionary) &&
pdf_crypt_decodeparms_is_identity(dictionary)
}
}
///|
fn pdf_arc4_crypt_pdf_object_with_stream_policy(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : PdfObject,
no_encrypt_metadata : Bool,
defer_stream_decryption : Bool,
) -> PdfObject raise PdfError {
match object {
PdfString(bytes) =>
PdfString(
pdf_arc4_crypt_object(
object_number,
generation,
file_key,
key_length_bits,
bytes[:],
),
)
PdfArray(values) => {
let output : Array[PdfObject] = []
for value in values {
output.push(
pdf_arc4_crypt_pdf_object_with_stream_policy(
object_number, generation, file_key, key_length_bits, value, no_encrypt_metadata,
defer_stream_decryption,
),
)
}
PdfArray(output)
}
PdfDictionary(entries) => {
let output : Array[(PdfName, PdfObject)] = []
for entry in entries {
let (name, value) = entry
output.push(
(
name,
pdf_arc4_crypt_pdf_object_with_stream_policy(
object_number, generation, file_key, key_length_bits, value, no_encrypt_metadata,
defer_stream_decryption,
),
),
)
}
PdfDictionary(output)
}
PdfStreamObject(stream) =>
if pdf_crypt_stream_is_identity(no_encrypt_metadata, stream.dictionary) {
object
} else {
let dictionary = pdf_arc4_crypt_pdf_object_with_stream_policy(
object_number,
generation,
file_key,
key_length_bits,
stream.dictionary,
no_encrypt_metadata,
defer_stream_decryption,
)
match stream.data {
StreamToGet(to_get) if defer_stream_decryption &&
to_get.crypt().is_identity() =>
PdfStreamObject({
dictionary,
data: StreamToGet({
input: to_get.input,
position: to_get.position,
length: to_get.length,
crypt: PdfStreamDataArc4Decrypt(
object_number,
generation,
file_key.to_owned(),
key_length_bits,
),
}),
})
_ => {
let data = pdf_stream_data_bytes(stream.data)
let encrypted_data = pdf_arc4_crypt_object(
object_number,
generation,
file_key,
key_length_bits,
data[:],
)
PdfStreamObject({
dictionary: dictionary.add_dict_entry(
pdf_crypt_stream_length_key(),
PdfInteger(encrypted_data.length()),
),
data: StreamGot(encrypted_data),
})
}
}
}
_ => object
}
}
///|
pub fn pdf_arc4_crypt_pdf_object(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : PdfObject,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
pdf_arc4_crypt_pdf_object_with_stream_policy(
object_number, generation, file_key, key_length_bits, object, no_encrypt_metadata,
false,
)
}
///|
pub fn pdf_aesv2_decrypt_object_data(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
data : BytesView,
) -> PdfBytes raise PdfError {
let object_key = pdf_encryption_object_key(
PdfCryptAESV2,
object_number,
generation,
file_key,
key_length_bits,
)
pdf_aes_cbc_decrypt(object_key[:], data)
}
///|
pub fn pdf_aesv2_encrypt_object_data(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
data : BytesView,
iv : BytesView,
) -> PdfBytes raise PdfError {
let object_key = pdf_encryption_object_key(
PdfCryptAESV2,
object_number,
generation,
file_key,
key_length_bits,
)
pdf_aes_cbc_encrypt_with_iv(object_key[:], iv, data)
}
///|
pub fn pdf_aesv3_decrypt_object_data(
file_key : BytesView,
data : BytesView,
) -> PdfBytes raise PdfError {
pdf_aes_cbc_decrypt(file_key, data)
}
///|
pub fn pdf_aesv3_encrypt_object_data(
file_key : BytesView,
data : BytesView,
iv : BytesView,
) -> PdfBytes raise PdfError {
pdf_aes_cbc_encrypt_with_iv(file_key, iv, data)
}
///|
fn pdf_aes_next_iv(
iv_provider : (Int) -> PdfBytes raise PdfError,
iv_index : Ref[Int],
) -> PdfBytes raise PdfError {
let iv = iv_provider(iv_index.val)
iv_index.val += 1
iv
}
///|
fn pdf_aes_encrypt_pdf_object_with_key(
key : BytesView,
object : PdfObject,
iv_provider : (Int) -> PdfBytes raise PdfError,
iv_index : Ref[Int],
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
match object {
PdfString(bytes) => {
let iv = pdf_aes_next_iv(iv_provider, iv_index)
PdfString(pdf_aes_cbc_encrypt_with_iv(key, iv[:], bytes[:]))
}
PdfArray(values) => {
let output : Array[PdfObject] = []
for value in values {
output.push(
pdf_aes_encrypt_pdf_object_with_key(
key,
value,
iv_provider,
iv_index,
no_encrypt_metadata~,
),
)
}
PdfArray(output)
}
PdfDictionary(entries) => {
let output : Array[(PdfName, PdfObject)] = []
for entry in entries {
let (name, value) = entry
output.push(
(
name,
pdf_aes_encrypt_pdf_object_with_key(
key,
value,
iv_provider,
iv_index,
no_encrypt_metadata~,
),
),
)
}
PdfDictionary(output)
}
PdfStreamObject(stream) =>
if pdf_crypt_stream_is_identity(no_encrypt_metadata, stream.dictionary) {
object
} else {
let dictionary = pdf_aes_encrypt_pdf_object_with_key(
key,
stream.dictionary,
iv_provider,
iv_index,
no_encrypt_metadata~,
)
let data = pdf_stream_data_bytes(stream.data)
let iv = pdf_aes_next_iv(iv_provider, iv_index)
let encrypted_data = pdf_aes_cbc_encrypt_with_iv(key, iv[:], data[:])
PdfStreamObject({
dictionary: dictionary.add_dict_entry(
pdf_crypt_stream_length_key(),
PdfInteger(encrypted_data.length()),
),
data: StreamGot(encrypted_data),
})
}
_ => object
}
}
///|
pub fn pdf_aesv2_encrypt_pdf_object_with_iv_provider(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : PdfObject,
iv_provider : (Int) -> PdfBytes raise PdfError,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
let object_key = pdf_encryption_object_key(
PdfCryptAESV2,
object_number,
generation,
file_key,
key_length_bits,
)
let iv_index : Ref[Int] = { val: 0 }
pdf_aes_encrypt_pdf_object_with_key(
object_key[:],
object,
iv_provider,
iv_index,
no_encrypt_metadata~,
)
}
///|
pub fn pdf_aesv3_encrypt_pdf_object_with_iv_provider(
file_key : BytesView,
object : PdfObject,
iv_provider : (Int) -> PdfBytes raise PdfError,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
let iv_index : Ref[Int] = { val: 0 }
pdf_aes_encrypt_pdf_object_with_key(
file_key,
object,
iv_provider,
iv_index,
no_encrypt_metadata~,
)
}
///|
fn pdf_aes_decrypt_pdf_object_with_key(
key : BytesView,
object : PdfObject,
no_encrypt_metadata? : Bool = false,
deferred_stream_crypt? : PdfStreamDataCrypt = PdfStreamDataNoCrypt,
) -> PdfObject raise PdfError {
match object {
PdfString(bytes) => PdfString(pdf_aes_cbc_decrypt(key, bytes[:]))
PdfArray(values) => {
let output : Array[PdfObject] = []
for value in values {
output.push(
pdf_aes_decrypt_pdf_object_with_key(
key,
value,
no_encrypt_metadata~,
deferred_stream_crypt~,
),
)
}
PdfArray(output)
}
PdfDictionary(entries) => {
let output : Array[(PdfName, PdfObject)] = []
for entry in entries {
let (name, value) = entry
output.push(
(
name,
pdf_aes_decrypt_pdf_object_with_key(
key,
value,
no_encrypt_metadata~,
deferred_stream_crypt~,
),
),
)
}
PdfDictionary(output)
}
PdfStreamObject(stream) =>
if pdf_crypt_stream_is_identity(no_encrypt_metadata, stream.dictionary) {
object
} else {
let dictionary = pdf_aes_decrypt_pdf_object_with_key(
key,
stream.dictionary,
no_encrypt_metadata~,
deferred_stream_crypt~,
)
match stream.data {
StreamToGet(to_get) if to_get.crypt().is_identity() &&
!deferred_stream_crypt.is_identity() =>
PdfStreamObject({
dictionary,
data: StreamToGet({
input: to_get.input,
position: to_get.position,
length: to_get.length,
crypt: deferred_stream_crypt.deep_copy(),
}),
})
_ => {
let data = pdf_stream_data_bytes(stream.data)
let decrypted_data = pdf_aes_cbc_decrypt(key, data[:])
PdfStreamObject({
dictionary: dictionary.add_dict_entry(
pdf_crypt_stream_length_key(),
PdfInteger(decrypted_data.length()),
),
data: StreamGot(decrypted_data),
})
}
}
}
_ => object
}
}
///|
pub fn pdf_aesv2_decrypt_pdf_object(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : PdfObject,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
let object_key = pdf_encryption_object_key(
PdfCryptAESV2,
object_number,
generation,
file_key,
key_length_bits,
)
pdf_aes_decrypt_pdf_object_with_key(
object_key[:],
object,
no_encrypt_metadata~,
deferred_stream_crypt=PdfStreamDataAesV2Decrypt(
object_number,
generation,
file_key.to_owned(),
key_length_bits,
),
)
}
///|
pub fn pdf_aesv3_decrypt_pdf_object(
file_key : BytesView,
object : PdfObject,
no_encrypt_metadata? : Bool = false,
) -> PdfObject raise PdfError {
pdf_aes_decrypt_pdf_object_with_key(
file_key,
object,
no_encrypt_metadata~,
deferred_stream_crypt=PdfStreamDataAesV3Decrypt(file_key.to_owned()),
)
}
///|
pub fn PdfDocument::arc4_crypt_parsed_objects(
self : PdfDocument,
file_key : BytesView,
key_length_bits : Int,
no_encrypt_metadata? : Bool = false,
skip_object_number? : Int = 0,
) -> PdfDocument raise PdfError {
let output = self.copy()
for number in output.object_numbers() {
if number != skip_object_number {
let entry = output.objects.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) =>
output.objects.set_entry(number, {
data: ObjectParsed(
pdf_arc4_crypt_pdf_object_with_stream_policy(
number,
entry.generation,
file_key,
key_length_bits,
object,
no_encrypt_metadata,
true,
),
),
generation: entry.generation,
})
ObjectParsedAlreadyDecrypted(_)
| ObjectToParse
| ObjectToParseFromObjectStream(_) => ()
}
}
}
output
}
///|
pub fn PdfDocument::aesv2_encrypt_parsed_objects_with_iv_provider(
self : PdfDocument,
file_key : BytesView,
key_length_bits : Int,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
no_encrypt_metadata? : Bool = false,
skip_object_number? : Int = 0,
) -> PdfDocument raise PdfError {
let output = self.copy()
for number in output.object_numbers() {
if number != skip_object_number {
let entry = output.objects.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) =>
output.objects.set_entry(number, {
data: ObjectParsed(
pdf_aesv2_encrypt_pdf_object_with_iv_provider(
number,
entry.generation,
file_key,
key_length_bits,
object,
fn(index) raise PdfError {
iv_provider(number, entry.generation, index)
},
no_encrypt_metadata~,
),
),
generation: entry.generation,
})
ObjectParsedAlreadyDecrypted(_)
| ObjectToParse
| ObjectToParseFromObjectStream(_) => ()
}
}
}
output
}
///|
pub fn PdfDocument::aesv2_decrypt_parsed_objects(
self : PdfDocument,
file_key : BytesView,
key_length_bits : Int,
no_encrypt_metadata? : Bool = false,
skip_object_number? : Int = 0,
) -> PdfDocument raise PdfError {
let output = self.copy()
for number in output.object_numbers() {
if number != skip_object_number {
let entry = output.objects.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) =>
output.objects.set_entry(number, {
data: ObjectParsed(
pdf_aesv2_decrypt_pdf_object(
number,
entry.generation,
file_key,
key_length_bits,
object,
no_encrypt_metadata~,
),
),
generation: entry.generation,
})
ObjectParsedAlreadyDecrypted(_)
| ObjectToParse
| ObjectToParseFromObjectStream(_) => ()
}
}
}
output
}
///|
pub fn PdfDocument::aesv3_encrypt_parsed_objects_with_iv_provider(
self : PdfDocument,
file_key : BytesView,
iv_provider : (Int, Int, Int) -> PdfBytes raise PdfError,
no_encrypt_metadata? : Bool = false,
skip_object_number? : Int = 0,
) -> PdfDocument raise PdfError {
let output = self.copy()
for number in output.object_numbers() {
if number != skip_object_number {
let entry = output.objects.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) =>
output.objects.set_entry(number, {
data: ObjectParsed(
pdf_aesv3_encrypt_pdf_object_with_iv_provider(
file_key,
object,
fn(index) raise PdfError {
iv_provider(number, entry.generation, index)
},
no_encrypt_metadata~,
),
),
generation: entry.generation,
})
ObjectParsedAlreadyDecrypted(_)
| ObjectToParse
| ObjectToParseFromObjectStream(_) => ()
}
}
}
output
}
///|
pub fn PdfDocument::aesv3_decrypt_parsed_objects(
self : PdfDocument,
file_key : BytesView,
no_encrypt_metadata? : Bool = false,
skip_object_number? : Int = 0,
) -> PdfDocument raise PdfError {
let output = self.copy()
for number in output.object_numbers() {
if number != skip_object_number {
let entry = output.objects.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) =>
output.objects.set_entry(number, {
data: ObjectParsed(
pdf_aesv3_decrypt_pdf_object(
file_key,
object,
no_encrypt_metadata~,
),
),
generation: entry.generation,
})
ObjectParsedAlreadyDecrypted(_)
| ObjectToParse
| ObjectToParseFromObjectStream(_) => ()
}
}
}
output
}
///|
fn pdf_permission_denied(
permissions : ArrayView[PdfPermission],
target : PdfPermission,
) -> Bool {
for permission in permissions {
if permission == target {
break true
}
} nobreak {
false
}
}
///|
fn pdf_permission_set_bit(mask : Int, bit : Int, value : Bool) -> Int {
if value {
mask | (1 << (bit - 1))
} else {
mask
}
}
///|
pub fn pdf_p_of_permissions(permissions : ArrayView[PdfPermission]) -> Int {
let mut mask = 0
mask = pdf_permission_set_bit(
mask,
3,
!pdf_permission_denied(permissions, PdfNoPrint),
)
mask = pdf_permission_set_bit(
mask,
4,
!pdf_permission_denied(permissions, PdfNoEdit),
)
mask = pdf_permission_set_bit(
mask,
5,
!pdf_permission_denied(permissions, PdfNoCopy),
)
mask = pdf_permission_set_bit(
mask,
6,
!pdf_permission_denied(permissions, PdfNoAnnot),
)
mask = pdf_permission_set_bit(mask, 7, true)
mask = pdf_permission_set_bit(mask, 8, true)
mask = pdf_permission_set_bit(
mask,
9,
!pdf_permission_denied(permissions, PdfNoForms),
)
mask = pdf_permission_set_bit(
mask,
10,
!pdf_permission_denied(permissions, PdfNoExtract),
)
mask = pdf_permission_set_bit(
mask,
11,
!pdf_permission_denied(permissions, PdfNoAssemble),
)
mask = pdf_permission_set_bit(
mask,
12,
!pdf_permission_denied(permissions, PdfNoHqPrint),
)
for bit in 13..<=32 {
mask = pdf_permission_set_bit(mask, bit, true)
}
mask
}
///|
fn pdf_permission_bit_denied(mask : Int, bit : Int) -> Bool {
((mask >> (bit - 1)) & 1) == 0
}
///|
pub fn pdf_permissions_of_p(mask : Int) -> Array[PdfPermission] {
let permissions : Array[PdfPermission] = []
if pdf_permission_bit_denied(mask, 12) {
permissions.push(PdfNoHqPrint)
}
if pdf_permission_bit_denied(mask, 11) {
permissions.push(PdfNoAssemble)
}
if pdf_permission_bit_denied(mask, 10) {
permissions.push(PdfNoExtract)
}
if pdf_permission_bit_denied(mask, 9) {
permissions.push(PdfNoForms)
}
if pdf_permission_bit_denied(mask, 6) {
permissions.push(PdfNoAnnot)
}
if pdf_permission_bit_denied(mask, 5) {
permissions.push(PdfNoCopy)
}
if pdf_permission_bit_denied(mask, 4) {
permissions.push(PdfNoEdit)
}
if pdf_permission_bit_denied(mask, 3) {
permissions.push(PdfNoPrint)
}
permissions
}