Commit c13bc66de8d6ef553c4ed05247774476a859a5f3

Authored by Jay Berkenbilt
1 parent 27e8d4bb

checkpoint -- partially implemented /V=4 encryption

git-svn-id: svn+q:///qpdf/trunk@811 71b93d88-0707-0410-a8cf-f5a4172ac649
@@ -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&amp; encryption_key, @@ -123,9 +126,6 @@ QPDF::compute_data_key(std::string const&amp; 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&amp; str, int objid, int generation) @@ -443,23 +440,62 @@ QPDF::decryptString(std::string&amp; 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*&amp; pipeline, int objid, int generation, @@ -472,11 +508,10 @@ QPDF::decryptStream(Pipeline*&amp; 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