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 | 43 | (http://delphi.about.com). .. use at your own risk and for whatever |
| 44 | 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 | 89 | 2.2 |
| 47 | 90 | === |
| 48 | 91 | |
| ... | ... | @@ -52,22 +95,6 @@ |
| 52 | 95 | Stefan Heinsen <stefan.heinsen@gmx.de> in August, 2009. He seems |
| 53 | 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 | 98 | * Look at page splitting. |
| 72 | 99 | |
| 73 | 100 | |
| ... | ... | @@ -109,9 +136,9 @@ General |
| 109 | 136 | of doing this seems very low since no viewer seems to care, so it's |
| 110 | 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 | 143 | * QPDFObjectHandle::getPageImages() doesn't notice images in |
| 117 | 144 | inherited resource dictionaries. See comments in that function. | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -141,7 +141,7 @@ class DLL_EXPORT QPDF |
| 141 | 141 | |
| 142 | 142 | static void compute_encryption_O_U( |
| 143 | 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 | 145 | std::string const& id1, |
| 146 | 146 | std::string& O, std::string& U); |
| 147 | 147 | // Return the full user password as stored in the PDF file. If |
| ... | ... | @@ -398,10 +398,12 @@ class DLL_EXPORT QPDF |
| 398 | 398 | |
| 399 | 399 | // methods to support encryption -- implemented in QPDF_encryption.cc |
| 400 | 400 | void initializeEncryption(); |
| 401 | - std::string getKeyForObject(int objid, int generation); | |
| 401 | + std::string getKeyForObject(int objid, int generation, bool use_aes); | |
| 402 | 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 | 408 | // Linearization Hint table structures. |
| 407 | 409 | // Naming conventions: |
| ... | ... | @@ -735,7 +737,9 @@ class DLL_EXPORT QPDF |
| 735 | 737 | bool ignore_xref_streams; |
| 736 | 738 | bool suppress_warnings; |
| 737 | 739 | bool attempt_recovery; |
| 738 | - bool encryption_use_aes; | |
| 740 | + int encryption_V; | |
| 741 | + bool encrypt_metadata; | |
| 742 | + QPDFObjectHandle encryption_dictionary; | |
| 739 | 743 | std::string provided_password; |
| 740 | 744 | std::string user_password; |
| 741 | 745 | std::string encryption_key; | ... | ... |
libqpdf/Pl_AES_PDF.cc
| 1 | 1 | #include <qpdf/Pl_AES_PDF.hh> |
| 2 | 2 | #include <qpdf/QUtil.hh> |
| 3 | +#include <qpdf/MD5.hh> | |
| 3 | 4 | #include <cstring> |
| 4 | 5 | #include <assert.h> |
| 5 | 6 | #include <stdexcept> |
| 6 | 7 | #include <qpdf/rijndael.h> |
| 7 | - | |
| 8 | -// XXX Still need CBC | |
| 8 | +#include <string> | |
| 9 | +#include <stdlib.h> | |
| 9 | 10 | |
| 10 | 11 | Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, |
| 11 | 12 | bool encrypt, unsigned char key[key_size]) : |
| 12 | 13 | Pipeline(identifier, next), |
| 13 | 14 | encrypt(encrypt), |
| 15 | + cbc_mode(true), | |
| 16 | + first(true), | |
| 14 | 17 | offset(0), |
| 15 | 18 | nrounds(0) |
| 16 | 19 | { |
| ... | ... | @@ -21,6 +24,7 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, |
| 21 | 24 | std::memset(this->rk, 0, sizeof(this->rk)); |
| 22 | 25 | std::memset(this->inbuf, 0, this->buf_size); |
| 23 | 26 | std::memset(this->outbuf, 0, this->buf_size); |
| 27 | + std::memset(this->cbc_block, 0, this->buf_size); | |
| 24 | 28 | if (encrypt) |
| 25 | 29 | { |
| 26 | 30 | this->nrounds = rijndaelSetupEncrypt(this->rk, this->key, keybits); |
| ... | ... | @@ -38,6 +42,12 @@ Pl_AES_PDF::~Pl_AES_PDF() |
| 38 | 42 | } |
| 39 | 43 | |
| 40 | 44 | void |
| 45 | +Pl_AES_PDF::disableCBC() | |
| 46 | +{ | |
| 47 | + this->cbc_mode = false; | |
| 48 | +} | |
| 49 | + | |
| 50 | +void | |
| 41 | 51 | Pl_AES_PDF::write(unsigned char* data, int len) |
| 42 | 52 | { |
| 43 | 53 | unsigned int bytes_left = len; |
| ... | ... | @@ -91,16 +101,79 @@ Pl_AES_PDF::finish() |
| 91 | 101 | } |
| 92 | 102 | |
| 93 | 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 | 124 | Pl_AES_PDF::flush(bool strip_padding) |
| 95 | 125 | { |
| 96 | 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 | 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 | 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 | 166 | else |
| 102 | 167 | { |
| 103 | 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 | 178 | unsigned int bytes = this->buf_size; |
| 106 | 179 | if (strip_padding) | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -253,7 +253,8 @@ QPDF::QPDF() : |
| 253 | 253 | ignore_xref_streams(false), |
| 254 | 254 | suppress_warnings(false), |
| 255 | 255 | attempt_recovery(true), |
| 256 | - encryption_use_aes(false), | |
| 256 | + encryption_V(0), | |
| 257 | + encrypt_metadata(true), | |
| 257 | 258 | cached_key_objid(0), |
| 258 | 259 | cached_key_generation(0), |
| 259 | 260 | first_xref_item_offset(0), |
| ... | ... | @@ -1813,17 +1814,7 @@ QPDF::pipeStreamData(int objid, int generation, |
| 1813 | 1814 | std::vector<PointerHolder<Pipeline> > to_delete; |
| 1814 | 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 | 1820 | try | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -281,7 +281,8 @@ QPDFWriter::setEncryptionParameters( |
| 281 | 281 | std::string O; |
| 282 | 282 | std::string U; |
| 283 | 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 | 286 | setEncryptionParametersInternal( |
| 286 | 287 | V, R, key_len, P, O, U, this->id1, user_password); |
| 287 | 288 | } | ... | ... |
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -5,11 +5,14 @@ |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/QPDFExc.hh> |
| 7 | 7 | |
| 8 | +#include <qpdf/QTC.hh> | |
| 8 | 9 | #include <qpdf/QUtil.hh> |
| 9 | 10 | #include <qpdf/Pl_RC4.hh> |
| 11 | +#include <qpdf/Pl_AES_PDF.hh> | |
| 10 | 12 | #include <qpdf/RC4.hh> |
| 11 | 13 | #include <qpdf/MD5.hh> |
| 12 | 14 | |
| 15 | +#include <assert.h> | |
| 13 | 16 | #include <string.h> |
| 14 | 17 | |
| 15 | 18 | static char const padding_string[] = { |
| ... | ... | @@ -123,9 +126,6 @@ QPDF::compute_data_key(std::string const& encryption_key, |
| 123 | 126 | md5.digest(digest); |
| 124 | 127 | return std::string((char*) digest, |
| 125 | 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 | 131 | std::string |
| ... | ... | @@ -322,7 +322,8 @@ QPDF::initializeEncryption() |
| 322 | 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 | 327 | if (! encryption_dict.isDictionary()) |
| 327 | 328 | { |
| 328 | 329 | throw QPDFExc(this->file.getName(), this->file.getLastOffset(), |
| ... | ... | @@ -360,12 +361,7 @@ QPDF::initializeEncryption() |
| 360 | 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 | 366 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) |
| 371 | 367 | { |
| ... | ... | @@ -385,19 +381,21 @@ QPDF::initializeEncryption() |
| 385 | 381 | } |
| 386 | 382 | } |
| 387 | 383 | |
| 388 | - bool encrypt_metadata = true; | |
| 384 | + this->encrypt_metadata = true; | |
| 389 | 385 | if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) |
| 390 | 386 | { |
| 391 | - encrypt_metadata = | |
| 387 | + this->encrypt_metadata = | |
| 392 | 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 | 400 | // password supplied was owner password; user_password has |
| 403 | 401 | // been initialized |
| ... | ... | @@ -415,7 +413,7 @@ QPDF::initializeEncryption() |
| 415 | 413 | } |
| 416 | 414 | |
| 417 | 415 | std::string |
| 418 | -QPDF::getKeyForObject(int objid, int generation) | |
| 416 | +QPDF::getKeyForObject(int objid, int generation, bool use_aes) | |
| 419 | 417 | { |
| 420 | 418 | if (! this->encrypted) |
| 421 | 419 | { |
| ... | ... | @@ -427,8 +425,7 @@ QPDF::getKeyForObject(int objid, int generation) |
| 427 | 425 | (generation == this->cached_key_generation))) |
| 428 | 426 | { |
| 429 | 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 | 429 | this->cached_key_objid = objid; |
| 433 | 430 | this->cached_key_generation = generation; |
| 434 | 431 | } |
| ... | ... | @@ -443,23 +440,62 @@ QPDF::decryptString(std::string& str, int objid, int generation) |
| 443 | 440 | { |
| 444 | 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 | 461 | void |
| 456 | 462 | QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 463 | + QPDFObjectHandle& stream_dict, | |
| 457 | 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 | 500 | else |
| 465 | 501 | { |
| ... | ... | @@ -472,11 +508,10 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 472 | 508 | void |
| 473 | 509 | QPDF::compute_encryption_O_U( |
| 474 | 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 | 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 | 515 | data.O = compute_O_value(user_password, owner_password, data); |
| 481 | 516 | O = data.O; |
| 482 | 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 | 18 | virtual void write(unsigned char* data, int len); |
| 19 | 19 | virtual void finish(); |
| 20 | 20 | |
| 21 | + // For testing only; PDF always uses CBC | |
| 22 | + void disableCBC(); | |
| 23 | + | |
| 21 | 24 | private: |
| 22 | 25 | void flush(bool discard_padding); |
| 26 | + void initializeVector(); | |
| 23 | 27 | |
| 24 | 28 | static unsigned int const buf_size = 16; |
| 25 | 29 | |
| 26 | 30 | bool encrypt; |
| 31 | + bool cbc_mode; | |
| 32 | + bool first; | |
| 27 | 33 | unsigned int offset; |
| 28 | 34 | unsigned char key[key_size]; |
| 29 | 35 | uint32_t rk[key_size + 28]; |
| 30 | 36 | unsigned char inbuf[buf_size]; |
| 31 | 37 | unsigned char outbuf[buf_size]; |
| 38 | + unsigned char cbc_block[buf_size]; | |
| 32 | 39 | unsigned int nrounds; |
| 33 | 40 | }; |
| 34 | 41 | ... | ... |