Commit 846c9f6bcc9aa86067850088808ff8d724a0d18f
1 parent
ad19b03f
checkpoint -- started doing some R4 encryption support
git-svn-id: svn+q:///qpdf/trunk@807 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
11 changed files
with
352 additions
and
31 deletions
README
| @@ -8,6 +8,32 @@ Artistic License which may be found in the source distribution as | @@ -8,6 +8,32 @@ Artistic License which may be found in the source distribution as | ||
| 8 | "Artistic-2.0". It is provided "as is" without express or implied | 8 | "Artistic-2.0". It is provided "as is" without express or implied |
| 9 | warranty. | 9 | warranty. |
| 10 | 10 | ||
| 11 | + | ||
| 12 | +Licensing terms of embedded software | ||
| 13 | +==================================== | ||
| 14 | + | ||
| 15 | +Some additional software with additional licensing terms is embedded | ||
| 16 | +within the qpdf source distribution in "external-libs". This software | ||
| 17 | +is not actually used by the qpdf build unless the | ||
| 18 | +--enable-build-external-libs option is passed to ./configure. These | ||
| 19 | +packages have their own licensing terms, both of which are compatible | ||
| 20 | +with qpdf's license. | ||
| 21 | + | ||
| 22 | +Zlib's license can be read in external-libs/zlib/zlib.h | ||
| 23 | + | ||
| 24 | +PCRE's licensing terms can be found in external-libs/pcre/LICENSE. | ||
| 25 | +PCRE's licensing terms require that we include the following | ||
| 26 | +information: | ||
| 27 | + | ||
| 28 | + Regular expression support is provided by the PCRE library package, | ||
| 29 | + which is open source software, written by Philip Hazel, and | ||
| 30 | + copyright by the University of Cambridge, England. | ||
| 31 | + | ||
| 32 | +The sources to PCRE can be independently obtained from | ||
| 33 | + | ||
| 34 | + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ | ||
| 35 | + | ||
| 36 | + | ||
| 11 | Building on UNIX/Linux | 37 | Building on UNIX/Linux |
| 12 | ====================== | 38 | ====================== |
| 13 | 39 | ||
| @@ -24,11 +50,11 @@ Building on Windows | @@ -24,11 +50,11 @@ Building on Windows | ||
| 24 | =================== | 50 | =================== |
| 25 | 51 | ||
| 26 | QPDF is known to build and pass its test suite with mingw (gcc 4.4.0) | 52 | QPDF is known to build and pass its test suite with mingw (gcc 4.4.0) |
| 27 | -and Microsoft Visual C++ .NET 2008 Express. In both cases, cygwin is | ||
| 28 | -required to run the test suite. Either cygwin or MSYS is required to | ||
| 29 | -build as well in order to get make and other related tools. The MSVC | ||
| 30 | -build has only been tested under cygwin. The mingw build requires | ||
| 31 | -MSYS and will probably not work with cygwin. | 53 | +and Microsoft Visual C++ .NET 2008 Express. Either cygwin or MSYS |
| 54 | +plus ActivateState Perl is required to build as well in order to get | ||
| 55 | +make and other related tools. The MSVC works with either cygwin or | ||
| 56 | +MSYS. The mingw build requires MSYS and will probably not work with | ||
| 57 | +cygwin. | ||
| 32 | 58 | ||
| 33 | For details on how to build under Windows, see README.windows. | 59 | For details on how to build under Windows, see README.windows. |
| 34 | 60 |
TODO
| @@ -56,6 +56,18 @@ | @@ -56,6 +56,18 @@ | ||
| 56 | dictionary. (V = 4 is Crypt Filters.) See | 56 | dictionary. (V = 4 is Crypt Filters.) See |
| 57 | ~/Q/pdf-collection/R4-encrypt-PDF_Inside_and_Out.pdf | 57 | ~/Q/pdf-collection/R4-encrypt-PDF_Inside_and_Out.pdf |
| 58 | 58 | ||
| 59 | + Search for XXX in the code. Implementation has been started. | ||
| 60 | + | ||
| 61 | + Algorithms from PDF Spec in QPDF_encrypt.cc have been updated. We | ||
| 62 | + can at least properly verify the user password with an R4 file. In | ||
| 63 | + order to finish the job, we need an aes-128-cbc implementation. | ||
| 64 | + Then we can fill in the gaps for the aes pipeline and actually run | ||
| 65 | + the test suite. The pipeline may be able to hard-code the | ||
| 66 | + initialization vector stuff by taking the first block of input and | ||
| 67 | + by writing a random block for output. The padding is already in | ||
| 68 | + the code, but the initialization vector is not since I accidentally | ||
| 69 | + started using an aes256 implementation instead of aes128-cbc. | ||
| 70 | + | ||
| 59 | * Look at page splitting. | 71 | * Look at page splitting. |
| 60 | 72 | ||
| 61 | 73 |
include/qpdf/QPDF.hh
| @@ -92,14 +92,15 @@ class DLL_EXPORT QPDF | @@ -92,14 +92,15 @@ class DLL_EXPORT QPDF | ||
| 92 | // This class holds data read from the encryption dictionary. | 92 | // This class holds data read from the encryption dictionary. |
| 93 | EncryptionData(int V, int R, int Length_bytes, int P, | 93 | EncryptionData(int V, int R, int Length_bytes, int P, |
| 94 | std::string const& O, std::string const& U, | 94 | std::string const& O, std::string const& U, |
| 95 | - std::string const& id1) : | 95 | + std::string const& id1, bool encrypt_metadata) : |
| 96 | V(V), | 96 | V(V), |
| 97 | R(R), | 97 | R(R), |
| 98 | Length_bytes(Length_bytes), | 98 | Length_bytes(Length_bytes), |
| 99 | P(P), | 99 | P(P), |
| 100 | O(O), | 100 | O(O), |
| 101 | U(U), | 101 | U(U), |
| 102 | - id1(id1) | 102 | + id1(id1), |
| 103 | + encrypt_metadata(encrypt_metadata) | ||
| 103 | { | 104 | { |
| 104 | } | 105 | } |
| 105 | 106 | ||
| @@ -110,6 +111,7 @@ class DLL_EXPORT QPDF | @@ -110,6 +111,7 @@ class DLL_EXPORT QPDF | ||
| 110 | std::string O; | 111 | std::string O; |
| 111 | std::string U; | 112 | std::string U; |
| 112 | std::string id1; | 113 | std::string id1; |
| 114 | + bool encrypt_metadata; | ||
| 113 | }; | 115 | }; |
| 114 | 116 | ||
| 115 | bool isEncrypted() const; | 117 | bool isEncrypted() const; |
| @@ -132,7 +134,8 @@ class DLL_EXPORT QPDF | @@ -132,7 +134,8 @@ class DLL_EXPORT QPDF | ||
| 132 | // getTrimmedUserPassword's result. | 134 | // getTrimmedUserPassword's result. |
| 133 | static void trim_user_password(std::string& user_password); | 135 | static void trim_user_password(std::string& user_password); |
| 134 | static std::string compute_data_key( | 136 | static std::string compute_data_key( |
| 135 | - std::string const& encryption_key, int objid, int generation); | 137 | + std::string const& encryption_key, int objid, int generation, |
| 138 | + bool use_aes); | ||
| 136 | static std::string compute_encryption_key( | 139 | static std::string compute_encryption_key( |
| 137 | std::string const& password, EncryptionData const& data); | 140 | std::string const& password, EncryptionData const& data); |
| 138 | 141 | ||
| @@ -732,6 +735,7 @@ class DLL_EXPORT QPDF | @@ -732,6 +735,7 @@ class DLL_EXPORT QPDF | ||
| 732 | bool ignore_xref_streams; | 735 | bool ignore_xref_streams; |
| 733 | bool suppress_warnings; | 736 | bool suppress_warnings; |
| 734 | bool attempt_recovery; | 737 | bool attempt_recovery; |
| 738 | + bool encryption_use_aes; | ||
| 735 | std::string provided_password; | 739 | std::string provided_password; |
| 736 | std::string user_password; | 740 | std::string user_password; |
| 737 | std::string encryption_key; | 741 | std::string encryption_key; |
libqpdf/Pl_AES_PDF.cc
0 โ 100644
| 1 | +#include <qpdf/Pl_AES_PDF.hh> | ||
| 2 | +#include <qpdf/QUtil.hh> | ||
| 3 | +#include <cstring> | ||
| 4 | +#include <assert.h> | ||
| 5 | +#include <stdexcept> | ||
| 6 | + | ||
| 7 | +Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, | ||
| 8 | + bool encrypt, unsigned char* key_data) : | ||
| 9 | + Pipeline(identifier, next), | ||
| 10 | + encrypt(encrypt), | ||
| 11 | + offset(0) | ||
| 12 | +{ | ||
| 13 | + std::memset(this->buf, 0, this->buf_size); | ||
| 14 | + // XXX init | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +Pl_AES_PDF::~Pl_AES_PDF() | ||
| 18 | +{ | ||
| 19 | + // XXX finalize | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +void | ||
| 23 | +Pl_AES_PDF::write(unsigned char* data, int len) | ||
| 24 | +{ | ||
| 25 | + unsigned int bytes_left = len; | ||
| 26 | + unsigned char* p = data; | ||
| 27 | + | ||
| 28 | + while (bytes_left > 0) | ||
| 29 | + { | ||
| 30 | + if (this->offset == this->buf_size) | ||
| 31 | + { | ||
| 32 | + flush(false); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + unsigned int available = this->buf_size - this->offset; | ||
| 36 | + int bytes = (bytes_left < available ? bytes_left : available); | ||
| 37 | + bytes_left -= bytes; | ||
| 38 | + std::memcpy(this->buf + this->offset, p, bytes); | ||
| 39 | + this->offset += bytes; | ||
| 40 | + p += bytes; | ||
| 41 | + } | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +void | ||
| 45 | +Pl_AES_PDF::finish() | ||
| 46 | +{ | ||
| 47 | + if (this->encrypt) | ||
| 48 | + { | ||
| 49 | + if (this->offset == this->buf_size) | ||
| 50 | + { | ||
| 51 | + flush(false); | ||
| 52 | + } | ||
| 53 | + // Pad as described in section 3.5.1 of version 1.7 of the PDF | ||
| 54 | + // specification, including providing an entire block of padding | ||
| 55 | + // if the input was a multiple of 16 bytes. | ||
| 56 | + unsigned char pad = this->buf_size - this->offset; | ||
| 57 | + memset(this->buf + this->offset, pad, pad); | ||
| 58 | + this->offset = this->buf_size; | ||
| 59 | + flush(false); | ||
| 60 | + } | ||
| 61 | + else | ||
| 62 | + { | ||
| 63 | + if (this->offset != this->buf_size) | ||
| 64 | + { | ||
| 65 | + throw std::runtime_error( | ||
| 66 | + "aes encrypted stream length was not a multiple of " + | ||
| 67 | + QUtil::int_to_string(this->buf_size) + " bytes (offset = " + | ||
| 68 | + QUtil::int_to_string(this->offset) + ")"); | ||
| 69 | + } | ||
| 70 | + flush(true); | ||
| 71 | + } | ||
| 72 | + getNext()->finish(); | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +void | ||
| 76 | +Pl_AES_PDF::flush(bool strip_padding) | ||
| 77 | +{ | ||
| 78 | + assert(this->offset == this->buf_size); | ||
| 79 | + if (this->encrypt) | ||
| 80 | + { | ||
| 81 | + // XXX encrypt this->buf | ||
| 82 | + } | ||
| 83 | + else | ||
| 84 | + { | ||
| 85 | + // XXX decrypt this->buf | ||
| 86 | + } | ||
| 87 | + unsigned int bytes = this->buf_size; | ||
| 88 | + if (strip_padding) | ||
| 89 | + { | ||
| 90 | + unsigned char last = this->buf[this->buf_size - 1]; | ||
| 91 | + if (last <= this->buf_size) | ||
| 92 | + { | ||
| 93 | + bool strip = true; | ||
| 94 | + for (unsigned int i = 1; i <= last; ++i) | ||
| 95 | + { | ||
| 96 | + if (this->buf[this->buf_size - i] != last) | ||
| 97 | + { | ||
| 98 | + strip = false; | ||
| 99 | + break; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + if (strip) | ||
| 103 | + { | ||
| 104 | + bytes -= last; | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + getNext()->write(this->buf, bytes); | ||
| 109 | + this->offset = 0; | ||
| 110 | +} |
libqpdf/QPDF.cc
| @@ -253,6 +253,7 @@ QPDF::QPDF() : | @@ -253,6 +253,7 @@ QPDF::QPDF() : | ||
| 253 | ignore_xref_streams(false), | 253 | ignore_xref_streams(false), |
| 254 | suppress_warnings(false), | 254 | suppress_warnings(false), |
| 255 | attempt_recovery(true), | 255 | attempt_recovery(true), |
| 256 | + encryption_use_aes(false), | ||
| 256 | cached_key_objid(0), | 257 | cached_key_objid(0), |
| 257 | cached_key_generation(0), | 258 | cached_key_generation(0), |
| 258 | first_xref_item_offset(0), | 259 | first_xref_item_offset(0), |
libqpdf/QPDFWriter.cc
| @@ -326,7 +326,8 @@ QPDFWriter::setEncryptionParametersInternal( | @@ -326,7 +326,8 @@ QPDFWriter::setEncryptionParametersInternal( | ||
| 326 | encryption_dictionary["/O"] = QPDF_String(O).unparse(true); | 326 | encryption_dictionary["/O"] = QPDF_String(O).unparse(true); |
| 327 | encryption_dictionary["/U"] = QPDF_String(U).unparse(true); | 327 | encryption_dictionary["/U"] = QPDF_String(U).unparse(true); |
| 328 | this->encrypted = true; | 328 | this->encrypted = true; |
| 329 | - QPDF::EncryptionData encryption_data(V, R, key_len, P, O, U, this->id1); | 329 | + QPDF::EncryptionData encryption_data(V, R, key_len, P, O, U, this->id1, |
| 330 | + /*XXX encrypt_metadata*/true); | ||
| 330 | this->encryption_key = QPDF::compute_encryption_key( | 331 | this->encryption_key = QPDF::compute_encryption_key( |
| 331 | user_password, encryption_data); | 332 | user_password, encryption_data); |
| 332 | } | 333 | } |
| @@ -335,7 +336,7 @@ void | @@ -335,7 +336,7 @@ void | ||
| 335 | QPDFWriter::setDataKey(int objid) | 336 | QPDFWriter::setDataKey(int objid) |
| 336 | { | 337 | { |
| 337 | this->cur_data_key = QPDF::compute_data_key( | 338 | this->cur_data_key = QPDF::compute_data_key( |
| 338 | - this->encryption_key, objid, 0); | 339 | + this->encryption_key, objid, 0, /*XXX use_aes */false); |
| 339 | } | 340 | } |
| 340 | 341 | ||
| 341 | int | 342 | int |
libqpdf/QPDF_encryption.cc
| @@ -99,9 +99,10 @@ iterate_rc4(unsigned char* data, int data_len, | @@ -99,9 +99,10 @@ iterate_rc4(unsigned char* data, int data_len, | ||
| 99 | 99 | ||
| 100 | std::string | 100 | std::string |
| 101 | QPDF::compute_data_key(std::string const& encryption_key, | 101 | QPDF::compute_data_key(std::string const& encryption_key, |
| 102 | - int objid, int generation) | 102 | + int objid, int generation, |
| 103 | + bool use_aes) | ||
| 103 | { | 104 | { |
| 104 | - // Algorithm 3.1 from the PDF 1.4 Reference Manual | 105 | + // Algorithm 3.1 from the PDF 1.7 Reference Manual |
| 105 | 106 | ||
| 106 | std::string result = encryption_key; | 107 | std::string result = encryption_key; |
| 107 | 108 | ||
| @@ -111,6 +112,10 @@ QPDF::compute_data_key(std::string const& encryption_key, | @@ -111,6 +112,10 @@ QPDF::compute_data_key(std::string const& encryption_key, | ||
| 111 | result += (char) ((objid >> 16) & 0xff); | 112 | result += (char) ((objid >> 16) & 0xff); |
| 112 | result += (char) (generation & 0xff); | 113 | result += (char) (generation & 0xff); |
| 113 | result += (char) ((generation >> 8) & 0xff); | 114 | result += (char) ((generation >> 8) & 0xff); |
| 115 | + if (use_aes) | ||
| 116 | + { | ||
| 117 | + result += "sAlT"; | ||
| 118 | + } | ||
| 114 | 119 | ||
| 115 | MD5 md5; | 120 | MD5 md5; |
| 116 | md5.encodeDataIncrementally(result.c_str(), result.length()); | 121 | md5.encodeDataIncrementally(result.c_str(), result.length()); |
| @@ -118,13 +123,16 @@ QPDF::compute_data_key(std::string const& encryption_key, | @@ -118,13 +123,16 @@ QPDF::compute_data_key(std::string const& encryption_key, | ||
| 118 | md5.digest(digest); | 123 | md5.digest(digest); |
| 119 | return std::string((char*) digest, | 124 | return std::string((char*) digest, |
| 120 | std::min(result.length(), (size_t) 16)); | 125 | std::min(result.length(), (size_t) 16)); |
| 126 | + | ||
| 127 | + // XXX Item 4 in Algorithm 3.1 mentions CBC and a random number. | ||
| 128 | + // We still have to incorporate that. | ||
| 121 | } | 129 | } |
| 122 | 130 | ||
| 123 | std::string | 131 | std::string |
| 124 | QPDF::compute_encryption_key( | 132 | QPDF::compute_encryption_key( |
| 125 | std::string const& password, EncryptionData const& data) | 133 | std::string const& password, EncryptionData const& data) |
| 126 | { | 134 | { |
| 127 | - // Algorithm 3.2 from the PDF 1.4 Reference Manual | 135 | + // Algorithm 3.2 from the PDF 1.7 Reference Manual |
| 128 | 136 | ||
| 129 | MD5 md5; | 137 | MD5 md5; |
| 130 | md5.encodeDataIncrementally( | 138 | md5.encodeDataIncrementally( |
| @@ -137,8 +145,14 @@ QPDF::compute_encryption_key( | @@ -137,8 +145,14 @@ QPDF::compute_encryption_key( | ||
| 137 | pbytes[3] = (char) ((data.P >> 24) & 0xff); | 145 | pbytes[3] = (char) ((data.P >> 24) & 0xff); |
| 138 | md5.encodeDataIncrementally(pbytes, 4); | 146 | md5.encodeDataIncrementally(pbytes, 4); |
| 139 | md5.encodeDataIncrementally(data.id1.c_str(), id_bytes); | 147 | md5.encodeDataIncrementally(data.id1.c_str(), id_bytes); |
| 148 | + if ((data.R >= 4) && (! data.encrypt_metadata)) | ||
| 149 | + { | ||
| 150 | + char bytes[4]; | ||
| 151 | + memset(bytes, 0xff, 4); | ||
| 152 | + md5.encodeDataIncrementally(bytes, 4); | ||
| 153 | + } | ||
| 140 | MD5::Digest digest; | 154 | MD5::Digest digest; |
| 141 | - iterate_md5_digest(md5, digest, ((data.R == 3) ? 50 : 0)); | 155 | + iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0)); |
| 142 | return std::string((char*)digest, data.Length_bytes); | 156 | return std::string((char*)digest, data.Length_bytes); |
| 143 | } | 157 | } |
| 144 | 158 | ||
| @@ -157,7 +171,7 @@ compute_O_rc4_key(std::string const& user_password, | @@ -157,7 +171,7 @@ compute_O_rc4_key(std::string const& user_password, | ||
| 157 | md5.encodeDataIncrementally( | 171 | md5.encodeDataIncrementally( |
| 158 | pad_or_truncate_password(password).c_str(), key_bytes); | 172 | pad_or_truncate_password(password).c_str(), key_bytes); |
| 159 | MD5::Digest digest; | 173 | MD5::Digest digest; |
| 160 | - iterate_md5_digest(md5, digest, ((data.R == 3) ? 50 : 0)); | 174 | + iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0)); |
| 161 | memcpy(key, digest, O_key_bytes); | 175 | memcpy(key, digest, O_key_bytes); |
| 162 | } | 176 | } |
| 163 | 177 | ||
| @@ -166,7 +180,7 @@ compute_O_value(std::string const& user_password, | @@ -166,7 +180,7 @@ compute_O_value(std::string const& user_password, | ||
| 166 | std::string const& owner_password, | 180 | std::string const& owner_password, |
| 167 | QPDF::EncryptionData const& data) | 181 | QPDF::EncryptionData const& data) |
| 168 | { | 182 | { |
| 169 | - // Algorithm 3.3 from the PDF 1.4 Reference Manual | 183 | + // Algorithm 3.3 from the PDF 1.7 Reference Manual |
| 170 | 184 | ||
| 171 | unsigned char O_key[O_key_bytes]; | 185 | unsigned char O_key[O_key_bytes]; |
| 172 | compute_O_rc4_key(user_password, owner_password, data, O_key); | 186 | compute_O_rc4_key(user_password, owner_password, data, O_key); |
| @@ -174,7 +188,7 @@ compute_O_value(std::string const& user_password, | @@ -174,7 +188,7 @@ compute_O_value(std::string const& user_password, | ||
| 174 | char upass[key_bytes]; | 188 | char upass[key_bytes]; |
| 175 | pad_or_truncate_password(user_password, upass); | 189 | pad_or_truncate_password(user_password, upass); |
| 176 | iterate_rc4((unsigned char*) upass, key_bytes, | 190 | iterate_rc4((unsigned char*) upass, key_bytes, |
| 177 | - O_key, data.Length_bytes, (data.R == 3) ? 20 : 1, false); | 191 | + O_key, data.Length_bytes, (data.R >= 3) ? 20 : 1, false); |
| 178 | return std::string(upass, key_bytes); | 192 | return std::string(upass, key_bytes); |
| 179 | } | 193 | } |
| 180 | 194 | ||
| @@ -183,7 +197,7 @@ std::string | @@ -183,7 +197,7 @@ std::string | ||
| 183 | compute_U_value_R2(std::string const& user_password, | 197 | compute_U_value_R2(std::string const& user_password, |
| 184 | QPDF::EncryptionData const& data) | 198 | QPDF::EncryptionData const& data) |
| 185 | { | 199 | { |
| 186 | - // Algorithm 3.4 from the PDF 1.4 Reference Manual | 200 | + // Algorithm 3.4 from the PDF 1.7 Reference Manual |
| 187 | 201 | ||
| 188 | std::string k1 = QPDF::compute_encryption_key(user_password, data); | 202 | std::string k1 = QPDF::compute_encryption_key(user_password, data); |
| 189 | char udata[key_bytes]; | 203 | char udata[key_bytes]; |
| @@ -198,7 +212,7 @@ std::string | @@ -198,7 +212,7 @@ std::string | ||
| 198 | compute_U_value_R3(std::string const& user_password, | 212 | compute_U_value_R3(std::string const& user_password, |
| 199 | QPDF::EncryptionData const& data) | 213 | QPDF::EncryptionData const& data) |
| 200 | { | 214 | { |
| 201 | - // Algorithm 3.5 from the PDF 1.4 Reference Manual | 215 | + // Algorithm 3.5 from the PDF 1.7 Reference Manual |
| 202 | 216 | ||
| 203 | std::string k1 = QPDF::compute_encryption_key(user_password, data); | 217 | std::string k1 = QPDF::compute_encryption_key(user_password, data); |
| 204 | MD5 md5; | 218 | MD5 md5; |
| @@ -224,7 +238,7 @@ static std::string | @@ -224,7 +238,7 @@ static std::string | ||
| 224 | compute_U_value(std::string const& user_password, | 238 | compute_U_value(std::string const& user_password, |
| 225 | QPDF::EncryptionData const& data) | 239 | QPDF::EncryptionData const& data) |
| 226 | { | 240 | { |
| 227 | - if (data.R == 3) | 241 | + if (data.R >= 3) |
| 228 | { | 242 | { |
| 229 | return compute_U_value_R3(user_password, data); | 243 | return compute_U_value_R3(user_password, data); |
| 230 | } | 244 | } |
| @@ -236,10 +250,10 @@ static bool | @@ -236,10 +250,10 @@ static bool | ||
| 236 | check_user_password(std::string const& user_password, | 250 | check_user_password(std::string const& user_password, |
| 237 | QPDF::EncryptionData const& data) | 251 | QPDF::EncryptionData const& data) |
| 238 | { | 252 | { |
| 239 | - // Algorithm 3.6 from the PDF 1.4 Reference Manual | 253 | + // Algorithm 3.6 from the PDF 1.7 Reference Manual |
| 240 | 254 | ||
| 241 | std::string u_value = compute_U_value(user_password, data); | 255 | std::string u_value = compute_U_value(user_password, data); |
| 242 | - int to_compare = ((data.R == 3) ? sizeof(MD5::Digest) : key_bytes); | 256 | + int to_compare = ((data.R >= 3) ? sizeof(MD5::Digest) : key_bytes); |
| 243 | return (memcmp(data.U.c_str(), u_value.c_str(), to_compare) == 0); | 257 | return (memcmp(data.U.c_str(), u_value.c_str(), to_compare) == 0); |
| 244 | } | 258 | } |
| 245 | 259 | ||
| @@ -248,14 +262,14 @@ check_owner_password(std::string& user_password, | @@ -248,14 +262,14 @@ check_owner_password(std::string& user_password, | ||
| 248 | std::string const& owner_password, | 262 | std::string const& owner_password, |
| 249 | QPDF::EncryptionData const& data) | 263 | QPDF::EncryptionData const& data) |
| 250 | { | 264 | { |
| 251 | - // Algorithm 3.7 from the PDF 1.4 Reference Manual | 265 | + // Algorithm 3.7 from the PDF 1.7 Reference Manual |
| 252 | 266 | ||
| 253 | unsigned char key[O_key_bytes]; | 267 | unsigned char key[O_key_bytes]; |
| 254 | compute_O_rc4_key(user_password, owner_password, data, key); | 268 | compute_O_rc4_key(user_password, owner_password, data, key); |
| 255 | unsigned char O_data[key_bytes]; | 269 | unsigned char O_data[key_bytes]; |
| 256 | memcpy(O_data, (unsigned char*) data.O.c_str(), key_bytes); | 270 | memcpy(O_data, (unsigned char*) data.O.c_str(), key_bytes); |
| 257 | iterate_rc4(O_data, key_bytes, key, data.Length_bytes, | 271 | iterate_rc4(O_data, key_bytes, key, data.Length_bytes, |
| 258 | - (data.R == 3) ? 20 : 1, true); | 272 | + (data.R >= 3) ? 20 : 1, true); |
| 259 | std::string new_user_password = | 273 | std::string new_user_password = |
| 260 | std::string((char*)O_data, key_bytes); | 274 | std::string((char*)O_data, key_bytes); |
| 261 | bool result = false; | 275 | bool result = false; |
| @@ -339,13 +353,20 @@ QPDF::initializeEncryption() | @@ -339,13 +353,20 @@ QPDF::initializeEncryption() | ||
| 339 | std::string U = encryption_dict.getKey("/U").getStringValue(); | 353 | std::string U = encryption_dict.getKey("/U").getStringValue(); |
| 340 | unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue(); | 354 | unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue(); |
| 341 | 355 | ||
| 342 | - if (! (((R == 2) || (R == 3)) && | ||
| 343 | - ((V == 1) || (V == 2)))) | 356 | + if (! (((R == 2) || (R == 3) || (R == 4)) && |
| 357 | + ((V == 1) || (V == 2) || (V == 4)))) | ||
| 344 | { | 358 | { |
| 345 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | 359 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), |
| 346 | "Unsupported /R or /V in encryption dictionary"); | 360 | "Unsupported /R or /V in encryption dictionary"); |
| 347 | } | 361 | } |
| 348 | 362 | ||
| 363 | + // XXX remove this check to continue implementing R4. | ||
| 364 | + if ((R == 4) || (V == 4)) | ||
| 365 | + { | ||
| 366 | + throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | ||
| 367 | + "PDF >= 1.5 encryption support is not fully implemented"); | ||
| 368 | + } | ||
| 369 | + | ||
| 349 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) | 370 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) |
| 350 | { | 371 | { |
| 351 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | 372 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), |
| @@ -364,7 +385,18 @@ QPDF::initializeEncryption() | @@ -364,7 +385,18 @@ QPDF::initializeEncryption() | ||
| 364 | } | 385 | } |
| 365 | } | 386 | } |
| 366 | 387 | ||
| 367 | - EncryptionData data(V, R, Length / 8, P, O, U, id1); | 388 | + bool encrypt_metadata = true; |
| 389 | + if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) | ||
| 390 | + { | ||
| 391 | + encrypt_metadata = | ||
| 392 | + encryption_dict.getKey("/EncryptMetadata").getBoolValue(); | ||
| 393 | + } | ||
| 394 | + // XXX not really... | ||
| 395 | + if (R >= 4) | ||
| 396 | + { | ||
| 397 | + this->encryption_use_aes = true; | ||
| 398 | + } | ||
| 399 | + EncryptionData data(V, R, Length / 8, P, O, U, id1, encrypt_metadata); | ||
| 368 | if (check_owner_password(this->user_password, this->provided_password, data)) | 400 | if (check_owner_password(this->user_password, this->provided_password, data)) |
| 369 | { | 401 | { |
| 370 | // password supplied was owner password; user_password has | 402 | // password supplied was owner password; user_password has |
| @@ -395,7 +427,8 @@ QPDF::getKeyForObject(int objid, int generation) | @@ -395,7 +427,8 @@ QPDF::getKeyForObject(int objid, int generation) | ||
| 395 | (generation == this->cached_key_generation))) | 427 | (generation == this->cached_key_generation))) |
| 396 | { | 428 | { |
| 397 | this->cached_object_encryption_key = | 429 | this->cached_object_encryption_key = |
| 398 | - compute_data_key(this->encryption_key, objid, generation); | 430 | + compute_data_key(this->encryption_key, objid, generation, |
| 431 | + this->encryption_use_aes); | ||
| 399 | this->cached_key_objid = objid; | 432 | this->cached_key_objid = objid; |
| 400 | this->cached_key_generation = generation; | 433 | this->cached_key_generation = generation; |
| 401 | } | 434 | } |
| @@ -424,8 +457,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | @@ -424,8 +457,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | ||
| 424 | std::vector<PointerHolder<Pipeline> >& heap) | 457 | std::vector<PointerHolder<Pipeline> >& heap) |
| 425 | { | 458 | { |
| 426 | std::string key = getKeyForObject(objid, generation); | 459 | std::string key = getKeyForObject(objid, generation); |
| 427 | - pipeline = new Pl_RC4("stream decryption", pipeline, | ||
| 428 | - (unsigned char*) key.c_str(), key.length()); | 460 | + if (this->encryption_use_aes) |
| 461 | + { | ||
| 462 | + throw std::logic_error("aes not yet implemented"); // XXX | ||
| 463 | + } | ||
| 464 | + else | ||
| 465 | + { | ||
| 466 | + pipeline = new Pl_RC4("RC4 stream decryption", pipeline, | ||
| 467 | + (unsigned char*) key.c_str(), key.length()); | ||
| 468 | + } | ||
| 429 | heap.push_back(pipeline); | 469 | heap.push_back(pipeline); |
| 430 | } | 470 | } |
| 431 | 471 | ||
| @@ -435,7 +475,8 @@ QPDF::compute_encryption_O_U( | @@ -435,7 +475,8 @@ QPDF::compute_encryption_O_U( | ||
| 435 | int V, int R, int key_len, int P, | 475 | int V, int R, int key_len, int P, |
| 436 | std::string const& id1, std::string& O, std::string& U) | 476 | std::string const& id1, std::string& O, std::string& U) |
| 437 | { | 477 | { |
| 438 | - EncryptionData data(V, R, key_len, P, "", "", id1); | 478 | + EncryptionData data(V, R, key_len, P, "", "", id1, |
| 479 | + /*XXX encrypt_metadata*/true); | ||
| 439 | data.O = compute_O_value(user_password, owner_password, data); | 480 | data.O = compute_O_value(user_password, owner_password, data); |
| 440 | O = data.O; | 481 | O = data.O; |
| 441 | U = compute_U_value(user_password, data); | 482 | U = compute_U_value(user_password, data); |
libqpdf/build.mk
| @@ -13,6 +13,7 @@ SRCS_libqpdf = \ | @@ -13,6 +13,7 @@ SRCS_libqpdf = \ | ||
| 13 | libqpdf/MD5.cc \ | 13 | libqpdf/MD5.cc \ |
| 14 | libqpdf/PCRE.cc \ | 14 | libqpdf/PCRE.cc \ |
| 15 | libqpdf/Pipeline.cc \ | 15 | libqpdf/Pipeline.cc \ |
| 16 | + libqpdf/Pl_AES_PDF.cc \ | ||
| 16 | libqpdf/Pl_ASCII85Decoder.cc \ | 17 | libqpdf/Pl_ASCII85Decoder.cc \ |
| 17 | libqpdf/Pl_ASCIIHexDecoder.cc \ | 18 | libqpdf/Pl_ASCIIHexDecoder.cc \ |
| 18 | libqpdf/Pl_Buffer.cc \ | 19 | libqpdf/Pl_Buffer.cc \ |
libqpdf/qpdf/Pl_AES_PDF.hh
0 โ 100644
| 1 | +#ifndef __PL_AES_PDF_HH__ | ||
| 2 | +#define __PL_AES_PDF_HH__ | ||
| 3 | + | ||
| 4 | +#include <qpdf/Pipeline.hh> | ||
| 5 | + | ||
| 6 | +class DLL_EXPORT Pl_AES_PDF: public Pipeline | ||
| 7 | +{ | ||
| 8 | + public: | ||
| 9 | + // key_data should be a pointer to key_size bytes of data | ||
| 10 | + static unsigned int const key_size = 16; | ||
| 11 | + Pl_AES_PDF(char const* identifier, Pipeline* next, | ||
| 12 | + bool encrypt, unsigned char* key_data); | ||
| 13 | + virtual ~Pl_AES_PDF(); | ||
| 14 | + | ||
| 15 | + virtual void write(unsigned char* data, int len); | ||
| 16 | + virtual void finish(); | ||
| 17 | + | ||
| 18 | + private: | ||
| 19 | + void flush(bool discard_padding); | ||
| 20 | + | ||
| 21 | + bool encrypt; | ||
| 22 | + unsigned int offset; | ||
| 23 | + static unsigned int const buf_size = 16; | ||
| 24 | + unsigned char buf[buf_size]; | ||
| 25 | +}; | ||
| 26 | + | ||
| 27 | +#endif // __PL_AES_PDF_HH__ |
libtests/aes.cc
0 โ 100644
| 1 | +#include <qpdf/Pl_AES_PDF.hh> | ||
| 2 | +#include <qpdf/Pl_StdioFile.hh> | ||
| 3 | + | ||
| 4 | +#include <stdio.h> | ||
| 5 | +#include <string.h> | ||
| 6 | +#include <iostream> | ||
| 7 | +#include <stdlib.h> | ||
| 8 | + | ||
| 9 | +static void usage() | ||
| 10 | +{ | ||
| 11 | + std::cerr << "Usage: aes { -encrypt | -decrypt }" | ||
| 12 | + << " hex-key infile outfile" << std::endl; | ||
| 13 | + exit(2); | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +int main(int argc, char* argv[]) | ||
| 17 | +{ | ||
| 18 | + if (argc != 5) | ||
| 19 | + { | ||
| 20 | + usage(); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + char* action = argv[1]; | ||
| 24 | + char* hexkey = argv[2]; | ||
| 25 | + char* infilename = argv[3]; | ||
| 26 | + char* outfilename = argv[4]; | ||
| 27 | + | ||
| 28 | + bool encrypt = true; | ||
| 29 | + if (strcmp(action, "-decrypt") == 0) | ||
| 30 | + { | ||
| 31 | + encrypt = false; | ||
| 32 | + } | ||
| 33 | + else if (strcmp(action, "-encrypt") != 0) | ||
| 34 | + { | ||
| 35 | + usage(); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + unsigned int hexkeylen = strlen(hexkey); | ||
| 39 | + unsigned int keylen = hexkeylen / 2; | ||
| 40 | + if (keylen != Pl_AES_PDF::key_size) | ||
| 41 | + { | ||
| 42 | + std::cerr << "key length must be " << Pl_AES_PDF::key_size | ||
| 43 | + << " bytes" << std::endl; | ||
| 44 | + exit(2); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + FILE* infile = fopen(infilename, "rb"); | ||
| 48 | + if (infile == 0) | ||
| 49 | + { | ||
| 50 | + std::cerr << "can't open " << infilename << std::endl; | ||
| 51 | + exit(2); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + FILE* outfile = fopen(outfilename, "wb"); | ||
| 55 | + if (outfile == 0) | ||
| 56 | + { | ||
| 57 | + std::cerr << "can't open " << outfilename << std::endl; | ||
| 58 | + exit(2); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + unsigned char key[Pl_AES_PDF::key_size]; | ||
| 62 | + for (unsigned int i = 0; i < strlen(hexkey); i += 2) | ||
| 63 | + { | ||
| 64 | + char t[3]; | ||
| 65 | + t[0] = hexkey[i]; | ||
| 66 | + t[1] = hexkey[i + 1]; | ||
| 67 | + t[2] = '\0'; | ||
| 68 | + | ||
| 69 | + long val = strtol(t, 0, 16); | ||
| 70 | + key[i/2] = (unsigned char) val; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + Pl_StdioFile* out = new Pl_StdioFile("stdout", outfile); | ||
| 74 | + Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key); | ||
| 75 | + | ||
| 76 | + // 16 < buffer size, buffer_size is not a multiple of 8 for testing | ||
| 77 | + unsigned char buf[83]; | ||
| 78 | + bool done = false; | ||
| 79 | + while (! done) | ||
| 80 | + { | ||
| 81 | + int len = fread(buf, 1, sizeof(buf), infile); | ||
| 82 | + if (len <= 0) | ||
| 83 | + { | ||
| 84 | + done = true; | ||
| 85 | + } | ||
| 86 | + else | ||
| 87 | + { | ||
| 88 | + aes->write(buf, len); | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + aes->finish(); | ||
| 92 | + delete aes; | ||
| 93 | + delete out; | ||
| 94 | + fclose(infile); | ||
| 95 | + fclose(outfile); | ||
| 96 | + return 0; | ||
| 97 | +} |