///|
fn pdf_aes_next_iv(
iv_provider : (Int) -> @core.PdfBytes raise @core.PdfError,
iv_index : Ref[Int],
) -> @core.PdfBytes raise @core.PdfError {
let iv = iv_provider(iv_index.val)
iv_index.val += 1
iv
}
///|
fn pdf_aes_encrypt_pdf_object_with_data_encryptor(
object : @syntax.PdfObject,
iv_provider : (Int) -> @core.PdfBytes raise @core.PdfError,
iv_index : Ref[Int],
encrypt_data : (BytesView, BytesView) -> @core.PdfBytes raise @core.PdfError,
no_encrypt_metadata : Bool,
) -> @syntax.PdfObject raise @core.PdfError {
match object {
PdfString(bytes) => {
let iv = pdf_aes_next_iv(iv_provider, iv_index)
PdfString(encrypt_data(bytes, iv))
}
PdfArray(values) => {
let output : Array[@syntax.PdfObject] = []
for value in values {
output.push(
pdf_aes_encrypt_pdf_object_with_data_encryptor(
value, iv_provider, iv_index, encrypt_data, no_encrypt_metadata,
),
)
}
PdfArray(output)
}
PdfDictionary(entries) => {
let output : Array[(@core.PdfName, @syntax.PdfObject)] = []
for entry in entries {
let (name, value) = entry
output.push(
(
name,
pdf_aes_encrypt_pdf_object_with_data_encryptor(
value, iv_provider, iv_index, encrypt_data, 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_data_encryptor(
stream.dictionary,
iv_provider,
iv_index,
encrypt_data,
no_encrypt_metadata,
)
let data = @syntax.pdf_stream_data_bytes(stream.data)
let iv = pdf_aes_next_iv(iv_provider, iv_index)
let encrypted_data = encrypt_data(data, iv)
PdfStreamObject({
dictionary: dictionary.add_dict_entry(
pdf_crypt_stream_length_key(),
PdfInteger(encrypted_data.length()),
),
data: StreamGot(encrypted_data),
})
}
_ => object
}
}
///|
/// Encrypt a PDF object using AESV2 with per-object derived keys.
///
/// The IV provider is called with a zero-based index for each encrypted string
/// or stream inside the object. Identity crypt-filter streams are preserved.
fn pdf_aesv2_encrypt_pdf_object_with_iv_provider(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : @syntax.PdfObject,
iv_provider : (Int) -> @core.PdfBytes raise @core.PdfError,
no_encrypt_metadata : Bool,
) -> @syntax.PdfObject raise @core.PdfError {
let iv_index = Ref::{ val: 0, }
pdf_aes_encrypt_pdf_object_with_data_encryptor(
object,
iv_provider,
iv_index,
fn(data, iv) raise @core.PdfError {
@crypt_core.pdf_aesv2_encrypt_object_data(
object_number, generation, file_key, key_length_bits, data, iv,
)
},
no_encrypt_metadata,
)
}
///|
/// Encrypt a PDF object using AESV3 with the file key directly.
///
/// The IV provider is called with a zero-based index for each encrypted string
/// or stream inside the object. Identity crypt-filter streams are preserved.
fn pdf_aesv3_encrypt_pdf_object_with_iv_provider(
file_key : BytesView,
object : @syntax.PdfObject,
iv_provider : (Int) -> @core.PdfBytes raise @core.PdfError,
no_encrypt_metadata : Bool,
) -> @syntax.PdfObject raise @core.PdfError {
let iv_index = Ref::{ val: 0, }
pdf_aes_encrypt_pdf_object_with_data_encryptor(
object,
iv_provider,
iv_index,
fn(data, iv) raise @core.PdfError {
@crypt_core.pdf_aesv3_encrypt_object_data(file_key, data, iv)
},
no_encrypt_metadata,
)
}
///|
fn pdf_aes_decrypt_pdf_object_with_key(
key : BytesView,
object : @syntax.PdfObject,
no_encrypt_metadata : Bool,
deferred_stream_crypt : @syntax.PdfStreamDataCrypt,
) -> @syntax.PdfObject raise @core.PdfError {
match object {
PdfString(bytes) => PdfString(@crypt_core.pdf_aes_cbc_decrypt(key, bytes))
PdfArray(values) => {
let output : Array[@syntax.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[(@core.PdfName, @syntax.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 = @syntax.pdf_stream_data_bytes(stream.data)
let decrypted_data = @crypt_core.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
}
}
///|
/// Decrypt a PDF object using AESV2 with per-object derived keys.
///
/// Deferred stream data is annotated with AESV2 crypt state so it can be
/// decrypted lazily when the stream bytes are loaded.
fn pdf_aesv2_decrypt_pdf_object(
object_number : Int,
generation : Int,
file_key : BytesView,
key_length_bits : Int,
object : @syntax.PdfObject,
no_encrypt_metadata : Bool,
) -> @syntax.PdfObject raise @core.PdfError {
let object_key = @crypt_core.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,
PdfStreamDataAesV2Decrypt(
object_number,
generation,
file_key.to_owned(),
key_length_bits,
),
)
}
///|
/// Decrypt a PDF object using AESV3 with the file key directly.
///
/// Deferred stream data is annotated with AESV3 crypt state so it can be
/// decrypted lazily when the stream bytes are loaded.
fn pdf_aesv3_decrypt_pdf_object(
file_key : BytesView,
object : @syntax.PdfObject,
no_encrypt_metadata : Bool,
) -> @syntax.PdfObject raise @core.PdfError {
pdf_aes_decrypt_pdf_object_with_key(
file_key,
object,
no_encrypt_metadata,
PdfStreamDataAesV3Decrypt(file_key.to_owned()),
)
}