Commit c13bc66de8d6ef553c4ed05247774476a859a5f3
1 parent
27e8d4bb
checkpoint -- partially implemented /V=4 encryption
git-svn-id: svn+q:///qpdf/trunk@811 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
7 changed files
with
210 additions
and
72 deletions
TODO
| @@ -43,6 +43,49 @@ | @@ -43,6 +43,49 @@ | ||
| 43 | (http://delphi.about.com). .. use at your own risk and for whatever | 43 | (http://delphi.about.com). .. use at your own risk and for whatever |
| 44 | the purpose you want .. no support provided. Sample code provided." | 44 | the purpose you want .. no support provided. Sample code provided." |
| 45 | 45 | ||
| 46 | + * Implement as much of R = 4 encryption as possible. Already able to | ||
| 47 | + decode AES-128-CBC and check passwords. | ||
| 48 | + | ||
| 49 | + aes test suite: use fips-197 test vector with cbc disabled; encrypt | ||
| 50 | + and decrypt some other files including multiples of 16 and not to | ||
| 51 | + test cbc mode. | ||
| 52 | + | ||
| 53 | + /Encrypt keys (if V == 4) | ||
| 54 | + | ||
| 55 | + /StmF - name of crypt filter for streams; default /Identity | ||
| 56 | + /StrF - name of crypt filter for strings; default /Identity | ||
| 57 | + /EFF - crypt filter for embedded files without their own crypt | ||
| 58 | + filters; default is to use /StmF | ||
| 59 | + | ||
| 60 | + /CF - keys are crypt filter names, values are are crypt | ||
| 61 | + dictionaries | ||
| 62 | + | ||
| 63 | + Individual streams may also have crypt filters. Filter type | ||
| 64 | + /Crypt; /DecodeParms must contain a Crypt filter decode | ||
| 65 | + parameters dictionary whose /Name entry specifies the particular | ||
| 66 | + filter to be used. If /Name is missing, use /Identity. | ||
| 67 | + /DecodeParms << /Crypt << /Name /XYZ >> >> where /XYZ is | ||
| 68 | + /Identity or a key in /CF. | ||
| 69 | + | ||
| 70 | + /Identity means not to encrypt. | ||
| 71 | + | ||
| 72 | + Crypt Dictionaries | ||
| 73 | + | ||
| 74 | + /Type (optional) /CryptFilter | ||
| 75 | + /CFM: | ||
| 76 | + /V2 - use rc4 | ||
| 77 | + /AESV2 - use aes | ||
| 78 | + /Length - supposed to be key length, but the one file I have | ||
| 79 | + has a bogus value for it, so I'm ignoring it. | ||
| 80 | + | ||
| 81 | + We will ignore remaining fields and values. | ||
| 82 | + | ||
| 83 | + Remember to honor /EncryptMetadata; applies to streams of /Type | ||
| 84 | + /Metadata | ||
| 85 | + | ||
| 86 | + When we write encrypted files, we must remember to omit any | ||
| 87 | + encryption filter settings from original streams. | ||
| 88 | + | ||
| 46 | 2.2 | 89 | 2.2 |
| 47 | === | 90 | === |
| 48 | 91 | ||
| @@ -52,22 +95,6 @@ | @@ -52,22 +95,6 @@ | ||
| 52 | Stefan Heinsen <stefan.heinsen@gmx.de> in August, 2009. He seems | 95 | Stefan Heinsen <stefan.heinsen@gmx.de> in August, 2009. He seems |
| 53 | to like to send encrypted mail. (key 01FCC336) | 96 | to like to send encrypted mail. (key 01FCC336) |
| 54 | 97 | ||
| 55 | - * See whether we can do anything with /V > 3 in the encryption | ||
| 56 | - dictionary. (V = 4 is Crypt Filters.) See | ||
| 57 | - ~/Q/pdf-collection/R4-encrypt-PDF_Inside_and_Out.pdf | ||
| 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 | - | ||
| 71 | * Look at page splitting. | 98 | * Look at page splitting. |
| 72 | 99 | ||
| 73 | 100 | ||
| @@ -109,9 +136,9 @@ General | @@ -109,9 +136,9 @@ General | ||
| 109 | of doing this seems very low since no viewer seems to care, so it's | 136 | of doing this seems very low since no viewer seems to care, so it's |
| 110 | probably not worth it. | 137 | probably not worth it. |
| 111 | 138 | ||
| 112 | - * Embedded files streams: figure out why running qpdf over the pdf | ||
| 113 | - 1.7 spec results in a file that crashes acrobat reader when you try | ||
| 114 | - to save nested documents. | 139 | + * Embedded file streams: figure out why running qpdf over the pdf 1.7 |
| 140 | + spec results in a file that crashes acrobat reader when you try to | ||
| 141 | + save nested documents. | ||
| 115 | 142 | ||
| 116 | * QPDFObjectHandle::getPageImages() doesn't notice images in | 143 | * QPDFObjectHandle::getPageImages() doesn't notice images in |
| 117 | inherited resource dictionaries. See comments in that function. | 144 | inherited resource dictionaries. See comments in that function. |
include/qpdf/QPDF.hh
| @@ -141,7 +141,7 @@ class DLL_EXPORT QPDF | @@ -141,7 +141,7 @@ class DLL_EXPORT QPDF | ||
| 141 | 141 | ||
| 142 | static void compute_encryption_O_U( | 142 | static void compute_encryption_O_U( |
| 143 | char const* user_password, char const* owner_password, | 143 | char const* user_password, char const* owner_password, |
| 144 | - int V, int R, int key_len, int P, | 144 | + int V, int R, int key_len, int P, bool encrypt_metadata, |
| 145 | std::string const& id1, | 145 | std::string const& id1, |
| 146 | std::string& O, std::string& U); | 146 | std::string& O, std::string& U); |
| 147 | // Return the full user password as stored in the PDF file. If | 147 | // Return the full user password as stored in the PDF file. If |
| @@ -398,10 +398,12 @@ class DLL_EXPORT QPDF | @@ -398,10 +398,12 @@ class DLL_EXPORT QPDF | ||
| 398 | 398 | ||
| 399 | // methods to support encryption -- implemented in QPDF_encryption.cc | 399 | // methods to support encryption -- implemented in QPDF_encryption.cc |
| 400 | void initializeEncryption(); | 400 | void initializeEncryption(); |
| 401 | - std::string getKeyForObject(int objid, int generation); | 401 | + std::string getKeyForObject(int objid, int generation, bool use_aes); |
| 402 | void decryptString(std::string&, int objid, int generation); | 402 | void decryptString(std::string&, int objid, int generation); |
| 403 | - void decryptStream(Pipeline*& pipeline, int objid, int generation, | ||
| 404 | - std::vector<PointerHolder<Pipeline> >& heap); | 403 | + void decryptStream( |
| 404 | + Pipeline*& pipeline, int objid, int generation, | ||
| 405 | + QPDFObjectHandle& stream_dict, | ||
| 406 | + std::vector<PointerHolder<Pipeline> >& heap); | ||
| 405 | 407 | ||
| 406 | // Linearization Hint table structures. | 408 | // Linearization Hint table structures. |
| 407 | // Naming conventions: | 409 | // Naming conventions: |
| @@ -735,7 +737,9 @@ class DLL_EXPORT QPDF | @@ -735,7 +737,9 @@ class DLL_EXPORT QPDF | ||
| 735 | bool ignore_xref_streams; | 737 | bool ignore_xref_streams; |
| 736 | bool suppress_warnings; | 738 | bool suppress_warnings; |
| 737 | bool attempt_recovery; | 739 | bool attempt_recovery; |
| 738 | - bool encryption_use_aes; | 740 | + int encryption_V; |
| 741 | + bool encrypt_metadata; | ||
| 742 | + QPDFObjectHandle encryption_dictionary; | ||
| 739 | std::string provided_password; | 743 | std::string provided_password; |
| 740 | std::string user_password; | 744 | std::string user_password; |
| 741 | std::string encryption_key; | 745 | std::string encryption_key; |
libqpdf/Pl_AES_PDF.cc
| 1 | #include <qpdf/Pl_AES_PDF.hh> | 1 | #include <qpdf/Pl_AES_PDF.hh> |
| 2 | #include <qpdf/QUtil.hh> | 2 | #include <qpdf/QUtil.hh> |
| 3 | +#include <qpdf/MD5.hh> | ||
| 3 | #include <cstring> | 4 | #include <cstring> |
| 4 | #include <assert.h> | 5 | #include <assert.h> |
| 5 | #include <stdexcept> | 6 | #include <stdexcept> |
| 6 | #include <qpdf/rijndael.h> | 7 | #include <qpdf/rijndael.h> |
| 7 | - | ||
| 8 | -// XXX Still need CBC | 8 | +#include <string> |
| 9 | +#include <stdlib.h> | ||
| 9 | 10 | ||
| 10 | Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, | 11 | Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, |
| 11 | bool encrypt, unsigned char key[key_size]) : | 12 | bool encrypt, unsigned char key[key_size]) : |
| 12 | Pipeline(identifier, next), | 13 | Pipeline(identifier, next), |
| 13 | encrypt(encrypt), | 14 | encrypt(encrypt), |
| 15 | + cbc_mode(true), | ||
| 16 | + first(true), | ||
| 14 | offset(0), | 17 | offset(0), |
| 15 | nrounds(0) | 18 | nrounds(0) |
| 16 | { | 19 | { |
| @@ -21,6 +24,7 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, | @@ -21,6 +24,7 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, | ||
| 21 | std::memset(this->rk, 0, sizeof(this->rk)); | 24 | std::memset(this->rk, 0, sizeof(this->rk)); |
| 22 | std::memset(this->inbuf, 0, this->buf_size); | 25 | std::memset(this->inbuf, 0, this->buf_size); |
| 23 | std::memset(this->outbuf, 0, this->buf_size); | 26 | std::memset(this->outbuf, 0, this->buf_size); |
| 27 | + std::memset(this->cbc_block, 0, this->buf_size); | ||
| 24 | if (encrypt) | 28 | if (encrypt) |
| 25 | { | 29 | { |
| 26 | this->nrounds = rijndaelSetupEncrypt(this->rk, this->key, keybits); | 30 | this->nrounds = rijndaelSetupEncrypt(this->rk, this->key, keybits); |
| @@ -38,6 +42,12 @@ Pl_AES_PDF::~Pl_AES_PDF() | @@ -38,6 +42,12 @@ Pl_AES_PDF::~Pl_AES_PDF() | ||
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | void | 44 | void |
| 45 | +Pl_AES_PDF::disableCBC() | ||
| 46 | +{ | ||
| 47 | + this->cbc_mode = false; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +void | ||
| 41 | Pl_AES_PDF::write(unsigned char* data, int len) | 51 | Pl_AES_PDF::write(unsigned char* data, int len) |
| 42 | { | 52 | { |
| 43 | unsigned int bytes_left = len; | 53 | unsigned int bytes_left = len; |
| @@ -91,16 +101,79 @@ Pl_AES_PDF::finish() | @@ -91,16 +101,79 @@ Pl_AES_PDF::finish() | ||
| 91 | } | 101 | } |
| 92 | 102 | ||
| 93 | void | 103 | void |
| 104 | +Pl_AES_PDF::initializeVector() | ||
| 105 | +{ | ||
| 106 | + std::string seed_str; | ||
| 107 | + seed_str += QUtil::int_to_string((int)QUtil::get_current_time()); | ||
| 108 | + seed_str += " QPDF aes random"; | ||
| 109 | + MD5 m; | ||
| 110 | + m.encodeString(seed_str.c_str()); | ||
| 111 | + MD5::Digest digest; | ||
| 112 | + m.digest(digest); | ||
| 113 | + assert(sizeof(digest) >= sizeof(unsigned int)); | ||
| 114 | + unsigned int seed; | ||
| 115 | + memcpy((void*)(&seed), digest, sizeof(unsigned int)); | ||
| 116 | + srandom(seed); | ||
| 117 | + for (unsigned int i = 0; i < this->buf_size; ++i) | ||
| 118 | + { | ||
| 119 | + this->cbc_block[i] = (unsigned char)(random() & 0xff); | ||
| 120 | + } | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +void | ||
| 94 | Pl_AES_PDF::flush(bool strip_padding) | 124 | Pl_AES_PDF::flush(bool strip_padding) |
| 95 | { | 125 | { |
| 96 | assert(this->offset == this->buf_size); | 126 | assert(this->offset == this->buf_size); |
| 127 | + | ||
| 128 | + if (first) | ||
| 129 | + { | ||
| 130 | + first = false; | ||
| 131 | + if (this->cbc_mode) | ||
| 132 | + { | ||
| 133 | + if (encrypt) | ||
| 134 | + { | ||
| 135 | + // Set cbc_block to a random initialization vector and | ||
| 136 | + // write it to the output stream | ||
| 137 | + initializeVector(); | ||
| 138 | + getNext()->write(this->cbc_block, this->buf_size); | ||
| 139 | + } | ||
| 140 | + else | ||
| 141 | + { | ||
| 142 | + // Take the first block of input as the initialization | ||
| 143 | + // vector. There's nothing to write at this time. | ||
| 144 | + memcpy(this->cbc_block, this->inbuf, this->buf_size); | ||
| 145 | + this->offset = 0; | ||
| 146 | + return; | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 97 | if (this->encrypt) | 151 | if (this->encrypt) |
| 98 | { | 152 | { |
| 153 | + if (this->cbc_mode) | ||
| 154 | + { | ||
| 155 | + for (unsigned int i = 0; i < this->buf_size; ++i) | ||
| 156 | + { | ||
| 157 | + this->inbuf[i] ^= this->cbc_block[i]; | ||
| 158 | + } | ||
| 159 | + } | ||
| 99 | rijndaelEncrypt(this->rk, this->nrounds, this->inbuf, this->outbuf); | 160 | rijndaelEncrypt(this->rk, this->nrounds, this->inbuf, this->outbuf); |
| 161 | + if (this->cbc_mode) | ||
| 162 | + { | ||
| 163 | + memcpy(this->cbc_block, this->outbuf, this->buf_size); | ||
| 164 | + } | ||
| 100 | } | 165 | } |
| 101 | else | 166 | else |
| 102 | { | 167 | { |
| 103 | rijndaelDecrypt(this->rk, this->nrounds, this->inbuf, this->outbuf); | 168 | rijndaelDecrypt(this->rk, this->nrounds, this->inbuf, this->outbuf); |
| 169 | + if (this->cbc_mode) | ||
| 170 | + { | ||
| 171 | + for (unsigned int i = 0; i < this->buf_size; ++i) | ||
| 172 | + { | ||
| 173 | + this->outbuf[i] ^= this->cbc_block[i]; | ||
| 174 | + } | ||
| 175 | + memcpy(this->cbc_block, this->inbuf, this->buf_size); | ||
| 176 | + } | ||
| 104 | } | 177 | } |
| 105 | unsigned int bytes = this->buf_size; | 178 | unsigned int bytes = this->buf_size; |
| 106 | if (strip_padding) | 179 | if (strip_padding) |
libqpdf/QPDF.cc
| @@ -253,7 +253,8 @@ QPDF::QPDF() : | @@ -253,7 +253,8 @@ 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 | + encryption_V(0), |
| 257 | + encrypt_metadata(true), | ||
| 257 | cached_key_objid(0), | 258 | cached_key_objid(0), |
| 258 | cached_key_generation(0), | 259 | cached_key_generation(0), |
| 259 | first_xref_item_offset(0), | 260 | first_xref_item_offset(0), |
| @@ -1813,17 +1814,7 @@ QPDF::pipeStreamData(int objid, int generation, | @@ -1813,17 +1814,7 @@ QPDF::pipeStreamData(int objid, int generation, | ||
| 1813 | std::vector<PointerHolder<Pipeline> > to_delete; | 1814 | std::vector<PointerHolder<Pipeline> > to_delete; |
| 1814 | if (this->encrypted) | 1815 | if (this->encrypted) |
| 1815 | { | 1816 | { |
| 1816 | - bool xref_stream = false; | ||
| 1817 | - if (stream_dict.getKey("/Type").isName() && | ||
| 1818 | - (stream_dict.getKey("/Type").getName() == "/XRef")) | ||
| 1819 | - { | ||
| 1820 | - QTC::TC("qpdf", "QPDF piping xref stream from encrypted file"); | ||
| 1821 | - xref_stream = true; | ||
| 1822 | - } | ||
| 1823 | - if (! xref_stream) | ||
| 1824 | - { | ||
| 1825 | - decryptStream(pipeline, objid, generation, to_delete); | ||
| 1826 | - } | 1817 | + decryptStream(pipeline, objid, generation, stream_dict, to_delete); |
| 1827 | } | 1818 | } |
| 1828 | 1819 | ||
| 1829 | try | 1820 | try |
libqpdf/QPDFWriter.cc
| @@ -281,7 +281,8 @@ QPDFWriter::setEncryptionParameters( | @@ -281,7 +281,8 @@ QPDFWriter::setEncryptionParameters( | ||
| 281 | std::string O; | 281 | std::string O; |
| 282 | std::string U; | 282 | std::string U; |
| 283 | QPDF::compute_encryption_O_U( | 283 | QPDF::compute_encryption_O_U( |
| 284 | - user_password, owner_password, V, R, key_len, P, this->id1, O, U); | 284 | + user_password, owner_password, V, R, key_len, P, |
| 285 | + /*XXX encrypt_metadata*/true, this->id1, O, U); | ||
| 285 | setEncryptionParametersInternal( | 286 | setEncryptionParametersInternal( |
| 286 | V, R, key_len, P, O, U, this->id1, user_password); | 287 | V, R, key_len, P, O, U, this->id1, user_password); |
| 287 | } | 288 | } |
libqpdf/QPDF_encryption.cc
| @@ -5,11 +5,14 @@ | @@ -5,11 +5,14 @@ | ||
| 5 | 5 | ||
| 6 | #include <qpdf/QPDFExc.hh> | 6 | #include <qpdf/QPDFExc.hh> |
| 7 | 7 | ||
| 8 | +#include <qpdf/QTC.hh> | ||
| 8 | #include <qpdf/QUtil.hh> | 9 | #include <qpdf/QUtil.hh> |
| 9 | #include <qpdf/Pl_RC4.hh> | 10 | #include <qpdf/Pl_RC4.hh> |
| 11 | +#include <qpdf/Pl_AES_PDF.hh> | ||
| 10 | #include <qpdf/RC4.hh> | 12 | #include <qpdf/RC4.hh> |
| 11 | #include <qpdf/MD5.hh> | 13 | #include <qpdf/MD5.hh> |
| 12 | 14 | ||
| 15 | +#include <assert.h> | ||
| 13 | #include <string.h> | 16 | #include <string.h> |
| 14 | 17 | ||
| 15 | static char const padding_string[] = { | 18 | static char const padding_string[] = { |
| @@ -123,9 +126,6 @@ QPDF::compute_data_key(std::string const& encryption_key, | @@ -123,9 +126,6 @@ QPDF::compute_data_key(std::string const& encryption_key, | ||
| 123 | md5.digest(digest); | 126 | md5.digest(digest); |
| 124 | return std::string((char*) digest, | 127 | return std::string((char*) digest, |
| 125 | std::min(result.length(), (size_t) 16)); | 128 | 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. | ||
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | std::string | 131 | std::string |
| @@ -322,7 +322,8 @@ QPDF::initializeEncryption() | @@ -322,7 +322,8 @@ QPDF::initializeEncryption() | ||
| 322 | "incorrect length"); | 322 | "incorrect length"); |
| 323 | } | 323 | } |
| 324 | 324 | ||
| 325 | - QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); | 325 | + this->encryption_dictionary = this->trailer.getKey("/Encrypt"); |
| 326 | + QPDFObjectHandle& encryption_dict = this->encryption_dictionary; | ||
| 326 | if (! encryption_dict.isDictionary()) | 327 | if (! encryption_dict.isDictionary()) |
| 327 | { | 328 | { |
| 328 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | 329 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), |
| @@ -360,12 +361,7 @@ QPDF::initializeEncryption() | @@ -360,12 +361,7 @@ QPDF::initializeEncryption() | ||
| 360 | "Unsupported /R or /V in encryption dictionary"); | 361 | "Unsupported /R or /V in encryption dictionary"); |
| 361 | } | 362 | } |
| 362 | 363 | ||
| 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 | - } | 364 | + this->encryption_V = V; |
| 369 | 365 | ||
| 370 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) | 366 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) |
| 371 | { | 367 | { |
| @@ -385,19 +381,21 @@ QPDF::initializeEncryption() | @@ -385,19 +381,21 @@ QPDF::initializeEncryption() | ||
| 385 | } | 381 | } |
| 386 | } | 382 | } |
| 387 | 383 | ||
| 388 | - bool encrypt_metadata = true; | 384 | + this->encrypt_metadata = true; |
| 389 | if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) | 385 | if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) |
| 390 | { | 386 | { |
| 391 | - encrypt_metadata = | 387 | + this->encrypt_metadata = |
| 392 | encryption_dict.getKey("/EncryptMetadata").getBoolValue(); | 388 | encryption_dict.getKey("/EncryptMetadata").getBoolValue(); |
| 393 | } | 389 | } |
| 394 | - // XXX not really... | ||
| 395 | - if (R >= 4) | 390 | + |
| 391 | + // XXX warn if /SubFilter is present | ||
| 392 | + if (V == 4) | ||
| 396 | { | 393 | { |
| 397 | - this->encryption_use_aes = true; | 394 | + // XXX get CF |
| 398 | } | 395 | } |
| 399 | - EncryptionData data(V, R, Length / 8, P, O, U, id1, encrypt_metadata); | ||
| 400 | - if (check_owner_password(this->user_password, this->provided_password, data)) | 396 | + EncryptionData data(V, R, Length / 8, P, O, U, id1, this->encrypt_metadata); |
| 397 | + if (check_owner_password( | ||
| 398 | + this->user_password, this->provided_password, data)) | ||
| 401 | { | 399 | { |
| 402 | // password supplied was owner password; user_password has | 400 | // password supplied was owner password; user_password has |
| 403 | // been initialized | 401 | // been initialized |
| @@ -415,7 +413,7 @@ QPDF::initializeEncryption() | @@ -415,7 +413,7 @@ QPDF::initializeEncryption() | ||
| 415 | } | 413 | } |
| 416 | 414 | ||
| 417 | std::string | 415 | std::string |
| 418 | -QPDF::getKeyForObject(int objid, int generation) | 416 | +QPDF::getKeyForObject(int objid, int generation, bool use_aes) |
| 419 | { | 417 | { |
| 420 | if (! this->encrypted) | 418 | if (! this->encrypted) |
| 421 | { | 419 | { |
| @@ -427,8 +425,7 @@ QPDF::getKeyForObject(int objid, int generation) | @@ -427,8 +425,7 @@ QPDF::getKeyForObject(int objid, int generation) | ||
| 427 | (generation == this->cached_key_generation))) | 425 | (generation == this->cached_key_generation))) |
| 428 | { | 426 | { |
| 429 | this->cached_object_encryption_key = | 427 | this->cached_object_encryption_key = |
| 430 | - compute_data_key(this->encryption_key, objid, generation, | ||
| 431 | - this->encryption_use_aes); | 428 | + compute_data_key(this->encryption_key, objid, generation, use_aes); |
| 432 | this->cached_key_objid = objid; | 429 | this->cached_key_objid = objid; |
| 433 | this->cached_key_generation = generation; | 430 | this->cached_key_generation = generation; |
| 434 | } | 431 | } |
| @@ -443,23 +440,62 @@ QPDF::decryptString(std::string& str, int objid, int generation) | @@ -443,23 +440,62 @@ QPDF::decryptString(std::string& str, int objid, int generation) | ||
| 443 | { | 440 | { |
| 444 | return; | 441 | return; |
| 445 | } | 442 | } |
| 446 | - std::string key = getKeyForObject(objid, generation); | ||
| 447 | - char* tmp = QUtil::copy_string(str); | ||
| 448 | - unsigned int vlen = str.length(); | ||
| 449 | - RC4 rc4((unsigned char const*)key.c_str(), key.length()); | ||
| 450 | - rc4.process((unsigned char*)tmp, vlen); | ||
| 451 | - str = std::string(tmp, vlen); | ||
| 452 | - delete [] tmp; | 443 | + bool use_aes = false; // XXX |
| 444 | + std::string key = getKeyForObject(objid, generation, use_aes); | ||
| 445 | + if (use_aes) | ||
| 446 | + { | ||
| 447 | + // XXX | ||
| 448 | + throw std::logic_error("XXX"); | ||
| 449 | + } | ||
| 450 | + else | ||
| 451 | + { | ||
| 452 | + unsigned int vlen = str.length(); | ||
| 453 | + char* tmp = QUtil::copy_string(str); | ||
| 454 | + RC4 rc4((unsigned char const*)key.c_str(), key.length()); | ||
| 455 | + rc4.process((unsigned char*)tmp, vlen); | ||
| 456 | + str = std::string(tmp, vlen); | ||
| 457 | + delete [] tmp; | ||
| 458 | + } | ||
| 453 | } | 459 | } |
| 454 | 460 | ||
| 455 | void | 461 | void |
| 456 | QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | 462 | QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 463 | + QPDFObjectHandle& stream_dict, | ||
| 457 | std::vector<PointerHolder<Pipeline> >& heap) | 464 | std::vector<PointerHolder<Pipeline> >& heap) |
| 458 | { | 465 | { |
| 459 | - std::string key = getKeyForObject(objid, generation); | ||
| 460 | - if (this->encryption_use_aes) | 466 | + bool decrypt = true; |
| 467 | + std::string type; | ||
| 468 | + if (stream_dict.getKey("/Type").isName()) | ||
| 469 | + { | ||
| 470 | + type = stream_dict.getKey("/Type").getName(); | ||
| 471 | + } | ||
| 472 | + if (type == "/XRef") | ||
| 473 | + { | ||
| 474 | + QTC::TC("qpdf", "QPDF piping xref stream from encrypted file"); | ||
| 475 | + decrypt = false; | ||
| 476 | + } | ||
| 477 | + bool use_aes = false; | ||
| 478 | + if (this->encryption_V == 4) | ||
| 479 | + { | ||
| 480 | + if ((! this->encrypt_metadata) && (type == "/Metadata")) | ||
| 481 | + { | ||
| 482 | + // XXX no test case for this | ||
| 483 | + decrypt = false; | ||
| 484 | + } | ||
| 485 | + // XXX check crypt filter; if not found, use StmF; see TODO | ||
| 486 | + use_aes = true; // XXX | ||
| 487 | + } | ||
| 488 | + if (! decrypt) | ||
| 489 | + { | ||
| 490 | + return; | ||
| 491 | + } | ||
| 492 | + | ||
| 493 | + std::string key = getKeyForObject(objid, generation, use_aes); | ||
| 494 | + if (use_aes) | ||
| 461 | { | 495 | { |
| 462 | - throw std::logic_error("aes not yet implemented"); // XXX | 496 | + assert(key.length() == Pl_AES_PDF::key_size); |
| 497 | + pipeline = new Pl_AES_PDF("AES stream decryption", pipeline, | ||
| 498 | + false, (unsigned char*) key.c_str()); | ||
| 463 | } | 499 | } |
| 464 | else | 500 | else |
| 465 | { | 501 | { |
| @@ -472,11 +508,10 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | @@ -472,11 +508,10 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | ||
| 472 | void | 508 | void |
| 473 | QPDF::compute_encryption_O_U( | 509 | QPDF::compute_encryption_O_U( |
| 474 | char const* user_password, char const* owner_password, | 510 | char const* user_password, char const* owner_password, |
| 475 | - int V, int R, int key_len, int P, | 511 | + int V, int R, int key_len, int P, bool encrypt_metadata, |
| 476 | std::string const& id1, std::string& O, std::string& U) | 512 | std::string const& id1, std::string& O, std::string& U) |
| 477 | { | 513 | { |
| 478 | - EncryptionData data(V, R, key_len, P, "", "", id1, | ||
| 479 | - /*XXX encrypt_metadata*/true); | 514 | + EncryptionData data(V, R, key_len, P, "", "", id1, encrypt_metadata); |
| 480 | data.O = compute_O_value(user_password, owner_password, data); | 515 | data.O = compute_O_value(user_password, owner_password, data); |
| 481 | O = data.O; | 516 | O = data.O; |
| 482 | U = compute_U_value(user_password, data); | 517 | U = compute_U_value(user_password, data); |
libqpdf/qpdf/Pl_AES_PDF.hh
| @@ -18,17 +18,24 @@ class DLL_EXPORT Pl_AES_PDF: public Pipeline | @@ -18,17 +18,24 @@ class DLL_EXPORT Pl_AES_PDF: public Pipeline | ||
| 18 | virtual void write(unsigned char* data, int len); | 18 | virtual void write(unsigned char* data, int len); |
| 19 | virtual void finish(); | 19 | virtual void finish(); |
| 20 | 20 | ||
| 21 | + // For testing only; PDF always uses CBC | ||
| 22 | + void disableCBC(); | ||
| 23 | + | ||
| 21 | private: | 24 | private: |
| 22 | void flush(bool discard_padding); | 25 | void flush(bool discard_padding); |
| 26 | + void initializeVector(); | ||
| 23 | 27 | ||
| 24 | static unsigned int const buf_size = 16; | 28 | static unsigned int const buf_size = 16; |
| 25 | 29 | ||
| 26 | bool encrypt; | 30 | bool encrypt; |
| 31 | + bool cbc_mode; | ||
| 32 | + bool first; | ||
| 27 | unsigned int offset; | 33 | unsigned int offset; |
| 28 | unsigned char key[key_size]; | 34 | unsigned char key[key_size]; |
| 29 | uint32_t rk[key_size + 28]; | 35 | uint32_t rk[key_size + 28]; |
| 30 | unsigned char inbuf[buf_size]; | 36 | unsigned char inbuf[buf_size]; |
| 31 | unsigned char outbuf[buf_size]; | 37 | unsigned char outbuf[buf_size]; |
| 38 | + unsigned char cbc_block[buf_size]; | ||
| 32 | unsigned int nrounds; | 39 | unsigned int nrounds; |
| 33 | }; | 40 | }; |
| 34 | 41 |