From e57c25814e49b82863753894ee7d97c18e4c4525 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 29 Dec 2012 21:31:21 -0500 Subject: Support for encryption with /V=5 and /R=5 and /R=6 Read and write support is implemented for /V=5 with /R=5 as well as /R=6. /R=5 is the deprecated encryption method used by Acrobat IX. /R=6 is the encryption method used by PDF 2.0 from ISO 32000-2. --- libqpdf/QPDFWriter.cc | 122 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 104 insertions(+), 18 deletions(-) (limited to 'libqpdf/QPDFWriter.cc') diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 8bfb6ff9..a02239c4 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -67,6 +67,8 @@ QPDFWriter::init() min_extension_level = 0; final_extension_level = 0; forced_extension_level = 0; + encryption_V = 0; + encryption_R = 0; encryption_dict_objid = 0; next_objid = 1; cur_stream_length_id = 0; @@ -343,6 +345,38 @@ QPDFWriter::setR4EncryptionParameters( setEncryptionParameters(user_password, owner_password, 4, 4, 16, clear); } +void +QPDFWriter::setR5EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata) +{ + std::set clear; + interpretR3EncryptionParameters( + clear, user_password, owner_password, + allow_accessibility, allow_extract, print, modify); + this->encrypt_use_aes = true; + this->encrypt_metadata = encrypt_metadata; + setEncryptionParameters(user_password, owner_password, 5, 5, 32, clear); +} + +void +QPDFWriter::setR6EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata) +{ + std::set clear; + interpretR3EncryptionParameters( + clear, user_password, owner_password, + allow_accessibility, allow_extract, print, modify); + this->encrypt_use_aes = true; + this->encrypt_metadata = encrypt_metadata; + setEncryptionParameters(user_password, owner_password, 5, 6, 32, clear); +} + void QPDFWriter::interpretR3EncryptionParameters( std::set& clear, @@ -426,6 +460,12 @@ QPDFWriter::setEncryptionParameters( bits_to_clear.insert(1); bits_to_clear.insert(2); + if (R > 3) + { + // Bit 10 is deprecated and should always be set. + bits_to_clear.erase(10); + } + int P = 0; // Create the complement of P, then invert. for (std::set::iterator iter = bits_to_clear.begin(); @@ -438,11 +478,26 @@ QPDFWriter::setEncryptionParameters( generateID(); std::string O; std::string U; - QPDF::compute_encryption_O_U( - user_password, owner_password, V, R, key_len, P, - this->encrypt_metadata, this->id1, O, U); + std::string OE; + std::string UE; + std::string Perms; + std::string encryption_key; + if (V < 5) + { + QPDF::compute_encryption_O_U( + user_password, owner_password, V, R, key_len, P, + this->encrypt_metadata, this->id1, O, U); + } + else + { + QPDF::compute_encryption_parameters_V5( + user_password, owner_password, V, R, key_len, P, + this->encrypt_metadata, this->id1, + encryption_key, O, U, OE, UE, Perms); + } setEncryptionParametersInternal( - V, R, key_len, P, O, U, "", "", "", this->id1, user_password); + V, R, key_len, P, O, U, OE, UE, Perms, + this->id1, user_password, encryption_key); } void @@ -482,6 +537,19 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf) this->encrypt_metadata ? 0 : 1); QTC::TC("qpdf", "QPDFWriter copy use_aes", this->encrypt_use_aes ? 0 : 1); + std::string OE; + std::string UE; + std::string Perms; + std::string encryption_key; + if (V >= 5) + { + QTC::TC("qpdf", "QPDFWriter copy V5"); + OE = encrypt.getKey("/OE").getStringValue(); + UE = encrypt.getKey("/UE").getStringValue(); + Perms = encrypt.getKey("/Perms").getStringValue(); + encryption_key = qpdf.getEncryptionKey(); + } + setEncryptionParametersInternal( V, encrypt.getKey("/R").getIntValue(), @@ -489,11 +557,12 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf) encrypt.getKey("/P").getIntValue(), encrypt.getKey("/O").getStringValue(), encrypt.getKey("/U").getStringValue(), - "", // XXXX OE - "", // XXXX UE - "", // XXXX Perms + OE, + UE, + Perms, this->id1, // this->id1 == the other file's id1 - qpdf.getPaddedUserPassword()); + qpdf.getPaddedUserPassword(), + encryption_key); } } @@ -605,10 +674,11 @@ QPDFWriter::setEncryptionParametersInternal( int V, int R, int key_len, long P, std::string const& O, std::string const& U, std::string const& OE, std::string const& UE, std::string const& Perms, - std::string const& id1, std::string const& user_password) + std::string const& id1, std::string const& user_password, + std::string const& encryption_key) { - // XXXX OE, UE, Perms, V=5 - + this->encryption_V = V; + this->encryption_R = R; encryption_dictionary["/Filter"] = "/Standard"; encryption_dictionary["/V"] = QUtil::int_to_string(V); encryption_dictionary["/Length"] = QUtil::int_to_string(key_len * 8); @@ -618,9 +688,15 @@ QPDFWriter::setEncryptionParametersInternal( encryption_dictionary["/U"] = QPDF_String(U).unparse(true); if (V >= 5) { - setMinimumPDFVersion("1.4"); + encryption_dictionary["/OE"] = QPDF_String(OE).unparse(true); + encryption_dictionary["/UE"] = QPDF_String(UE).unparse(true); + encryption_dictionary["/Perms"] = QPDF_String(Perms).unparse(true); + } + if (R >= 6) + { + setMinimumPDFVersion("1.7", 8); } - if (R >= 5) + else if (R == 5) { setMinimumPDFVersion("1.7", 3); } @@ -641,14 +717,16 @@ QPDFWriter::setEncryptionParametersInternal( { encryption_dictionary["/EncryptMetadata"] = "false"; } - if (V == 4) + if ((V == 4) || (V == 5)) { // The spec says the value for the crypt filter key can be // anything, and xpdf seems to agree. However, Adobe Reader // won't open our files unless we use /StdCF. encryption_dictionary["/StmF"] = "/StdCF"; encryption_dictionary["/StrF"] = "/StdCF"; - std::string method = (this->encrypt_use_aes ? "/AESV2" : "/V2"); + std::string method = (this->encrypt_use_aes + ? ((V < 5) ? "/AESV2" : "/AESV3") + : "/V2"); encryption_dictionary["/CF"] = "<< /StdCF << /AuthEvent /DocOpen /CFM " + method + " >> >>"; } @@ -656,15 +734,23 @@ QPDFWriter::setEncryptionParametersInternal( this->encrypted = true; QPDF::EncryptionData encryption_data( V, R, key_len, P, O, U, OE, UE, Perms, id1, this->encrypt_metadata); - this->encryption_key = QPDF::compute_encryption_key( - user_password, encryption_data); + if (V < 5) + { + this->encryption_key = QPDF::compute_encryption_key( + user_password, encryption_data); + } + else + { + this->encryption_key = encryption_key; + } } void QPDFWriter::setDataKey(int objid) { this->cur_data_key = QPDF::compute_data_key( - this->encryption_key, objid, 0, this->encrypt_use_aes); + this->encryption_key, objid, 0, + this->encrypt_use_aes, this->encryption_V, this->encryption_R); } int -- cgit v1.2.3-54-g00ecf