Commit b873dc9c59f4445239f68d7138aebb53cc0df648

Authored by Jay Berkenbilt
1 parent 5c253d1c

implemented writing R4/V4 encryption except that the output files don't open in Adobe reader 9.1.3.

git-svn-id: svn+q:///qpdf/trunk@816 71b93d88-0707-0410-a8cf-f5a4172ac649
@@ -43,48 +43,53 @@ @@ -43,48 +43,53 @@
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. 46 + * R = 4, V = 4 encryption.
48 47
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. 48 + - Update C API for R4 encryption
52 49
53 - /Encrypt keys (if V == 4) 50 + - When we write encrypted files, we must remember to omit any
  51 + encryption filter settings from original streams.
54 52
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 53 + - test various combinations with and without cleartext-metadata
  54 + and aes in compression tests
59 55
60 - /CF - keys are crypt filter names, values are are crypt  
61 - dictionaries 56 + - figure out a way to test crypt filters defined on a stream
62 57
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. 58 + - would be nice to test strings and streams with different
  59 + encryption types, but without sample data, we'd have to write
  60 + them ourselves which is not that useful
69 61
70 - /Identity means not to encrypt. 62 + - figure out why xpdf can open my files but not acroread
71 63
72 - Crypt Dictionaries 64 + - figure out how to look at the metadata so I can tell whether
  65 + /EncryptMetadata is working the way it's supposed to
73 66
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. 67 + - Do something with embedded files, but what and how?
80 68
81 - We will ignore remaining fields and values. 69 + - General notes:
82 70
83 - Remember to honor /EncryptMetadata; applies to streams of /Type  
84 - /Metadata 71 + /CF - keys are crypt filter names, values are are crypt
  72 + dictionaries
85 73
86 - When we write encrypted files, we must remember to omit any  
87 - encryption filter settings from original streams. 74 + Individual streams may also have crypt filters. Filter type
  75 + /Crypt; /DecodeParms must contain a Crypt filter decode
  76 + parameters dictionary whose /Name entry specifies the particular
  77 + filter to be used. If /Name is missing, use /Identity.
  78 + /DecodeParms << /Crypt << /Name /XYZ >> >> where /XYZ is
  79 + /Identity or a key in /CF.
  80 +
  81 + /Identity means not to encrypt.
  82 +
  83 + Crypt Dictionaries
  84 +
  85 + /Type (optional) /CryptFilter
  86 + /CFM:
  87 + /V2 - use rc4
  88 + /AESV2 - use aes
  89 + /Length - supposed to be key length, but the one file I have
  90 + has a bogus value for it, so I'm ignoring it.
  91 +
  92 + We will ignore remaining fields and values.
88 93
89 2.2 94 2.2
90 === 95 ===
include/qpdf/QPDFWriter.hh
@@ -118,9 +118,10 @@ class DLL_EXPORT QPDFWriter @@ -118,9 +118,10 @@ class DLL_EXPORT QPDFWriter
118 118
119 // Set up for encrypted output. Disables stream prefiltering and 119 // Set up for encrypted output. Disables stream prefiltering and
120 // content normalization. Note that setting R2 encryption 120 // content normalization. Note that setting R2 encryption
121 - // parameters sets the PDF version to at least 1.3, and setting R3 121 + // parameters sets the PDF version to at least 1.3, setting R3
122 // encryption parameters pushes the PDF version number to at least 122 // encryption parameters pushes the PDF version number to at least
123 - // 1.4. 123 + // 1.4, and setting R4 parameters pushes the version to at least
  124 + // 1.5, or if AES is used, 1.6.
124 void setR2EncryptionParameters( 125 void setR2EncryptionParameters(
125 char const* user_password, char const* owner_password, 126 char const* user_password, char const* owner_password,
126 bool allow_print, bool allow_modify, 127 bool allow_print, bool allow_modify,
@@ -143,6 +144,11 @@ class DLL_EXPORT QPDFWriter @@ -143,6 +144,11 @@ class DLL_EXPORT QPDFWriter
143 char const* user_password, char const* owner_password, 144 char const* user_password, char const* owner_password,
144 bool allow_accessibility, bool allow_extract, 145 bool allow_accessibility, bool allow_extract,
145 r3_print_e print, r3_modify_e modify); 146 r3_print_e print, r3_modify_e modify);
  147 + void setR4EncryptionParameters(
  148 + char const* user_password, char const* owner_password,
  149 + bool allow_accessibility, bool allow_extract,
  150 + r3_print_e print, r3_modify_e modify,
  151 + bool encrypt_metadata, bool use_aes);
146 152
147 // Create linearized output. Disables qdf mode, content 153 // Create linearized output. Disables qdf mode, content
148 // normalization, and stream prefiltering. 154 // normalization, and stream prefiltering.
@@ -182,6 +188,11 @@ class DLL_EXPORT QPDFWriter @@ -182,6 +188,11 @@ class DLL_EXPORT QPDFWriter
182 void preserveObjectStreams(); 188 void preserveObjectStreams();
183 void generateObjectStreams(); 189 void generateObjectStreams();
184 void generateID(); 190 void generateID();
  191 + void interpretR3EncryptionParameters(
  192 + std::set<int>& bits_to_clear,
  193 + char const* user_password, char const* owner_password,
  194 + bool allow_accessibility, bool allow_extract,
  195 + r3_print_e print, r3_modify_e modify);
185 void setEncryptionParameters( 196 void setEncryptionParameters(
186 char const* user_password, char const* owner_password, 197 char const* user_password, char const* owner_password,
187 int V, int R, int key_len, std::set<int>& bits_to_clear); 198 int V, int R, int key_len, std::set<int>& bits_to_clear);
@@ -231,6 +242,7 @@ class DLL_EXPORT QPDFWriter @@ -231,6 +242,7 @@ class DLL_EXPORT QPDFWriter
231 // stack items are of type Pl_Buffer, the buffer is retrieved. 242 // stack items are of type Pl_Buffer, the buffer is retrieved.
232 void popPipelineStack(PointerHolder<Buffer>* bp = 0); 243 void popPipelineStack(PointerHolder<Buffer>* bp = 0);
233 244
  245 + void adjustAESStreamLength(unsigned long& length);
234 void pushEncryptionFilter(); 246 void pushEncryptionFilter();
235 void pushDiscardFilter(); 247 void pushDiscardFilter();
236 248
@@ -251,6 +263,8 @@ class DLL_EXPORT QPDFWriter @@ -251,6 +263,8 @@ class DLL_EXPORT QPDFWriter
251 bool linearized; 263 bool linearized;
252 object_stream_e object_stream_mode; 264 object_stream_e object_stream_mode;
253 std::string encryption_key; 265 std::string encryption_key;
  266 + bool encrypt_metadata;
  267 + bool encrypt_use_aes;
254 std::map<std::string, std::string> encryption_dictionary; 268 std::map<std::string, std::string> encryption_dictionary;
255 269
256 std::string id1; // for /ID key of 270 std::string id1; // for /ID key of
@@ -267,7 +281,7 @@ class DLL_EXPORT QPDFWriter @@ -267,7 +281,7 @@ class DLL_EXPORT QPDFWriter
267 std::map<int, size_t> lengths; 281 std::map<int, size_t> lengths;
268 int next_objid; 282 int next_objid;
269 int cur_stream_length_id; 283 int cur_stream_length_id;
270 - int cur_stream_length; 284 + unsigned long cur_stream_length;
271 bool added_newline; 285 bool added_newline;
272 int max_ostream_index; 286 int max_ostream_index;
273 std::set<int> normalized_streams; 287 std::set<int> normalized_streams;
libqpdf/QPDFWriter.cc
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 #include <qpdf/Pl_Discard.hh> 6 #include <qpdf/Pl_Discard.hh>
7 #include <qpdf/Pl_Buffer.hh> 7 #include <qpdf/Pl_Buffer.hh>
8 #include <qpdf/Pl_RC4.hh> 8 #include <qpdf/Pl_RC4.hh>
  9 +#include <qpdf/Pl_AES_PDF.hh>
9 #include <qpdf/Pl_Flate.hh> 10 #include <qpdf/Pl_Flate.hh>
10 #include <qpdf/Pl_PNGFilter.hh> 11 #include <qpdf/Pl_PNGFilter.hh>
11 #include <qpdf/QUtil.hh> 12 #include <qpdf/QUtil.hh>
@@ -37,6 +38,8 @@ QPDFWriter::QPDFWriter(QPDF&amp; pdf, char const* filename) : @@ -37,6 +38,8 @@ QPDFWriter::QPDFWriter(QPDF&amp; pdf, char const* filename) :
37 preserve_encryption(true), 38 preserve_encryption(true),
38 linearized(false), 39 linearized(false),
39 object_stream_mode(o_preserve), 40 object_stream_mode(o_preserve),
  41 + encrypt_metadata(true),
  42 + encrypt_use_aes(false),
40 encryption_dict_objid(0), 43 encryption_dict_objid(0),
41 next_objid(1), 44 next_objid(1),
42 cur_stream_length_id(0), 45 cur_stream_length_id(0),
@@ -188,6 +191,38 @@ QPDFWriter::setR3EncryptionParameters( @@ -188,6 +191,38 @@ QPDFWriter::setR3EncryptionParameters(
188 bool allow_accessibility, bool allow_extract, 191 bool allow_accessibility, bool allow_extract,
189 r3_print_e print, r3_modify_e modify) 192 r3_print_e print, r3_modify_e modify)
190 { 193 {
  194 + std::set<int> clear;
  195 + interpretR3EncryptionParameters(
  196 + clear, user_password, owner_password,
  197 + allow_accessibility, allow_extract, print, modify);
  198 + setMinimumPDFVersion("1.4");
  199 + setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear);
  200 +}
  201 +
  202 +void
  203 +QPDFWriter::setR4EncryptionParameters(
  204 + char const* user_password, char const* owner_password,
  205 + bool allow_accessibility, bool allow_extract,
  206 + r3_print_e print, r3_modify_e modify,
  207 + bool encrypt_metadata, bool use_aes)
  208 +{
  209 + std::set<int> clear;
  210 + interpretR3EncryptionParameters(
  211 + clear, user_password, owner_password,
  212 + allow_accessibility, allow_extract, print, modify);
  213 + this->encrypt_use_aes = use_aes;
  214 + this->encrypt_metadata = encrypt_metadata;
  215 + setMinimumPDFVersion(use_aes ? "1.6" : "1.5");
  216 + setEncryptionParameters(user_password, owner_password, 4, 4, 16, clear);
  217 +}
  218 +
  219 +void
  220 +QPDFWriter::interpretR3EncryptionParameters(
  221 + std::set<int>& clear,
  222 + char const* user_password, char const* owner_password,
  223 + bool allow_accessibility, bool allow_extract,
  224 + r3_print_e print, r3_modify_e modify)
  225 +{
191 // Acrobat 5 security options: 226 // Acrobat 5 security options:
192 227
193 // Checkboxes: 228 // Checkboxes:
@@ -206,7 +241,6 @@ QPDFWriter::setR3EncryptionParameters( @@ -206,7 +241,6 @@ QPDFWriter::setR3EncryptionParameters(
206 // Low Resolution 241 // Low Resolution
207 // Full printing 242 // Full printing
208 243
209 - std::set<int> clear;  
210 if (! allow_accessibility) 244 if (! allow_accessibility)
211 { 245 {
212 clear.insert(10); 246 clear.insert(10);
@@ -251,9 +285,6 @@ QPDFWriter::setR3EncryptionParameters( @@ -251,9 +285,6 @@ QPDFWriter::setR3EncryptionParameters(
251 285
252 // no default so gcc warns for missing cases 286 // no default so gcc warns for missing cases
253 } 287 }
254 -  
255 - setMinimumPDFVersion("1.4");  
256 - setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear);  
257 } 288 }
258 289
259 void 290 void
@@ -282,7 +313,7 @@ QPDFWriter::setEncryptionParameters( @@ -282,7 +313,7 @@ QPDFWriter::setEncryptionParameters(
282 std::string U; 313 std::string U;
283 QPDF::compute_encryption_O_U( 314 QPDF::compute_encryption_O_U(
284 user_password, owner_password, V, R, key_len, P, 315 user_password, owner_password, V, R, key_len, P,
285 - /*XXX encrypt_metadata*/true, this->id1, O, U); 316 + this->encrypt_metadata, this->id1, O, U);
286 setEncryptionParametersInternal( 317 setEncryptionParametersInternal(
287 V, R, key_len, P, O, U, this->id1, user_password); 318 V, R, key_len, P, O, U, this->id1, user_password);
288 } 319 }
@@ -326,9 +357,22 @@ QPDFWriter::setEncryptionParametersInternal( @@ -326,9 +357,22 @@ QPDFWriter::setEncryptionParametersInternal(
326 encryption_dictionary["/P"] = QUtil::int_to_string(P); 357 encryption_dictionary["/P"] = QUtil::int_to_string(P);
327 encryption_dictionary["/O"] = QPDF_String(O).unparse(true); 358 encryption_dictionary["/O"] = QPDF_String(O).unparse(true);
328 encryption_dictionary["/U"] = QPDF_String(U).unparse(true); 359 encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
  360 + if ((R >= 4) && (! encrypt_metadata))
  361 + {
  362 + encryption_dictionary["/EncryptMetadata"] = "false";
  363 + }
  364 + if (V == 4)
  365 + {
  366 + encryption_dictionary["/StmF"] = "/CF1";
  367 + encryption_dictionary["/StrF"] = "/CF1";
  368 + std::string method = (this->encrypt_use_aes ? "/AESV2" : "/V2");
  369 + encryption_dictionary["/CF"] =
  370 + "<< /CF1 << /AuthEvent /DocOpen /CFM " + method + " >> >>";
  371 + }
  372 +
329 this->encrypted = true; 373 this->encrypted = true;
330 - QPDF::EncryptionData encryption_data(V, R, key_len, P, O, U, this->id1,  
331 - /*XXX encrypt_metadata*/true); 374 + QPDF::EncryptionData encryption_data(
  375 + V, R, key_len, P, O, U, this->id1, this->encrypt_metadata);
332 this->encryption_key = QPDF::compute_encryption_key( 376 this->encryption_key = QPDF::compute_encryption_key(
333 user_password, encryption_data); 377 user_password, encryption_data);
334 } 378 }
@@ -337,7 +381,7 @@ void @@ -337,7 +381,7 @@ void
337 QPDFWriter::setDataKey(int objid) 381 QPDFWriter::setDataKey(int objid)
338 { 382 {
339 this->cur_data_key = QPDF::compute_data_key( 383 this->cur_data_key = QPDF::compute_data_key(
340 - this->encryption_key, objid, 0, /*XXX use_aes */false); 384 + this->encryption_key, objid, 0, this->encrypt_use_aes);
341 } 385 }
342 386
343 int 387 int
@@ -436,14 +480,36 @@ QPDFWriter::popPipelineStack(PointerHolder&lt;Buffer&gt;* bp) @@ -436,14 +480,36 @@ QPDFWriter::popPipelineStack(PointerHolder&lt;Buffer&gt;* bp)
436 } 480 }
437 481
438 void 482 void
  483 +QPDFWriter::adjustAESStreamLength(unsigned long& length)
  484 +{
  485 + if (this->encrypted && (! this->cur_data_key.empty()) &&
  486 + this->encrypt_use_aes)
  487 + {
  488 + // Stream length will be padded with 1 to 16 bytes to end up
  489 + // as a multiple of 16. It will also be prepended by 16 bits
  490 + // of random data.
  491 + length += 32 - (length & 0xf);
  492 + }
  493 +}
  494 +
  495 +void
439 QPDFWriter::pushEncryptionFilter() 496 QPDFWriter::pushEncryptionFilter()
440 { 497 {
441 if (this->encrypted && (! this->cur_data_key.empty())) 498 if (this->encrypted && (! this->cur_data_key.empty()))
442 { 499 {
443 - Pipeline* p =  
444 - new Pl_RC4("stream encryption", this->pipeline,  
445 - (unsigned char*) this->cur_data_key.c_str(),  
446 - this->cur_data_key.length()); 500 + Pipeline* p = 0;
  501 + if (this->encrypt_use_aes)
  502 + {
  503 + p = new Pl_AES_PDF(
  504 + "aes stream encryption", this->pipeline, true,
  505 + (unsigned char*) this->cur_data_key.c_str());
  506 + }
  507 + else
  508 + {
  509 + p = new Pl_RC4("rc4 stream encryption", this->pipeline,
  510 + (unsigned char*) this->cur_data_key.c_str(),
  511 + this->cur_data_key.length());
  512 + }
447 pushPipeline(p); 513 pushPipeline(p);
448 } 514 }
449 // Must call this unconditionally so we can call popPipelineStack 515 // Must call this unconditionally so we can call popPipelineStack
@@ -722,6 +788,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -722,6 +788,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
722 } 788 }
723 else if (object.isDictionary()) 789 else if (object.isDictionary())
724 { 790 {
  791 + // XXX Must not preserve Crypt filters from original stream
  792 + // dictionary
725 writeString("<<"); 793 writeString("<<");
726 writeStringQDF("\n"); 794 writeStringQDF("\n");
727 std::set<std::string> keys = object.getKeys(); 795 std::set<std::string> keys = object.getKeys();
@@ -836,6 +904,15 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -836,6 +904,15 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
836 } 904 }
837 905
838 this->cur_stream_length = stream_data.getPointer()->getSize(); 906 this->cur_stream_length = stream_data.getPointer()->getSize();
  907 + if (this->encrypted &&
  908 + stream_dict.getKey("/Type").isName() &&
  909 + (stream_dict.getKey("/Type").getName() == "/Metadata") &&
  910 + (! this->encrypt_metadata))
  911 + {
  912 + // Don't encrypt stream data for the metadata stream
  913 + this->cur_data_key.clear();
  914 + }
  915 + adjustAESStreamLength(this->cur_stream_length);
839 unparseObject(stream_dict, 0, flags, this->cur_stream_length, compress); 916 unparseObject(stream_dict, 0, flags, this->cur_stream_length, compress);
840 writeString("\nstream\n"); 917 writeString("\nstream\n");
841 pushEncryptionFilter(); 918 pushEncryptionFilter();
@@ -864,13 +941,29 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -864,13 +941,29 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
864 (! this->cur_data_key.empty())) 941 (! this->cur_data_key.empty()))
865 { 942 {
866 val = object.getStringValue(); 943 val = object.getStringValue();
867 - char* tmp = QUtil::copy_string(val);  
868 - unsigned int vlen = val.length();  
869 - RC4 rc4((unsigned char const*)this->cur_data_key.c_str(),  
870 - this->cur_data_key.length());  
871 - rc4.process((unsigned char*)tmp, vlen);  
872 - val = QPDF_String(std::string(tmp, vlen)).unparse();  
873 - delete [] tmp; 944 + if (this->encrypt_use_aes)
  945 + {
  946 + Pl_Buffer bufpl("encrypted string");
  947 + Pl_AES_PDF pl("aes encrypt string", &bufpl, true,
  948 + (unsigned char const*)this->cur_data_key.c_str());
  949 + pl.write((unsigned char*) val.c_str(), val.length());
  950 + pl.finish();
  951 + Buffer* buf = bufpl.getBuffer();
  952 + val = QPDF_String(
  953 + std::string((char*)buf->getBuffer(),
  954 + (size_t)buf->getSize())).unparse();
  955 + delete buf;
  956 + }
  957 + else
  958 + {
  959 + char* tmp = QUtil::copy_string(val);
  960 + unsigned int vlen = val.length();
  961 + RC4 rc4((unsigned char const*)this->cur_data_key.c_str(),
  962 + this->cur_data_key.length());
  963 + rc4.process((unsigned char*)tmp, vlen);
  964 + val = QPDF_String(std::string(tmp, vlen)).unparse();
  965 + delete [] tmp;
  966 + }
874 } 967 }
875 else 968 else
876 { 969 {
@@ -1000,8 +1093,9 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1000,8 +1093,9 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1000 writeStringQDF("\n "); 1093 writeStringQDF("\n ");
1001 writeString(" /Type /ObjStm"); 1094 writeString(" /Type /ObjStm");
1002 writeStringQDF("\n "); 1095 writeStringQDF("\n ");
1003 - writeString(" /Length " +  
1004 - QUtil::int_to_string(stream_buffer.getPointer()->getSize())); 1096 + unsigned long length = stream_buffer.getPointer()->getSize();
  1097 + adjustAESStreamLength(length);
  1098 + writeString(" /Length " + QUtil::int_to_string(length));
1005 writeStringQDF("\n "); 1099 writeStringQDF("\n ");
1006 if (compressed) 1100 if (compressed)
1007 { 1101 {
@@ -1489,6 +1583,7 @@ QPDFWriter::writeHintStream(int hint_id) @@ -1489,6 +1583,7 @@ QPDFWriter::writeHintStream(int hint_id)
1489 writeString(QUtil::int_to_string(O)); 1583 writeString(QUtil::int_to_string(O));
1490 } 1584 }
1491 writeString(" /Length "); 1585 writeString(" /Length ");
  1586 + adjustAESStreamLength(hlen);
1492 writeString(QUtil::int_to_string(hlen)); 1587 writeString(QUtil::int_to_string(hlen));
1493 writeString(" >>\nstream\n"); 1588 writeString(" >>\nstream\n");
1494 1589
qpdf/qpdf.cc
@@ -72,6 +72,9 @@ Additional flags are dependent upon key length.\n\ @@ -72,6 +72,9 @@ Additional flags are dependent upon key length.\n\
72 --extract=[yn] allow other text/graphic extraction\n\ 72 --extract=[yn] allow other text/graphic extraction\n\
73 --print=print-opt control printing access\n\ 73 --print=print-opt control printing access\n\
74 --modify=modify-opt control modify access\n\ 74 --modify=modify-opt control modify access\n\
  75 + --cleartext-metadata prevents encryption of metadata\n\
  76 + --use-aes=[yn] indicates whether to use AES encryption\n\
  77 + --force-V4 forces use of V=4 encryption handler\n\
75 \n\ 78 \n\
76 print-opt may be:\n\ 79 print-opt may be:\n\
77 \n\ 80 \n\
@@ -89,6 +92,14 @@ Additional flags are dependent upon key length.\n\ @@ -89,6 +92,14 @@ Additional flags are dependent upon key length.\n\
89 \n\ 92 \n\
90 The default for each permission option is to be fully permissive.\n\ 93 The default for each permission option is to be fully permissive.\n\
91 \n\ 94 \n\
  95 +Specifying cleartext-metadata forces the PDF version to at least 1.5.\n\
  96 +Specifying use of AES forces the PDF version to at least 1.6. These\n\
  97 +options are both off by default.\n\
  98 +\n\
  99 +The --force-V4 flag forces the V=4 encryption handler introduced in PDF 1.5\n\
  100 +to be used even if not otherwise needed. This option is primarily useful\n\
  101 +for testing qpdf and has no other practical use.\n\
  102 +\n\
92 \n\ 103 \n\
93 Advanced Transformation Options\n\ 104 Advanced Transformation Options\n\
94 -------------------------------\n\ 105 -------------------------------\n\
@@ -220,7 +231,8 @@ parse_encrypt_options( @@ -220,7 +231,8 @@ parse_encrypt_options(
220 std::string& user_password, std::string& owner_password, int& keylen, 231 std::string& user_password, std::string& owner_password, int& keylen,
221 bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate, 232 bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate,
222 bool& r3_accessibility, bool& r3_extract, 233 bool& r3_accessibility, bool& r3_extract,
223 - QPDFWriter::r3_print_e& r3_print, QPDFWriter::r3_modify_e& r3_modify) 234 + QPDFWriter::r3_print_e& r3_print, QPDFWriter::r3_modify_e& r3_modify,
  235 + bool& force_V4, bool& cleartext_metadata, bool& use_aes)
224 { 236 {
225 if (cur_arg + 3 >= argc) 237 if (cur_arg + 3 >= argc)
226 { 238 {
@@ -450,6 +462,65 @@ parse_encrypt_options( @@ -450,6 +462,65 @@ parse_encrypt_options(
450 usage("-accessibility invalid for 40-bit keys"); 462 usage("-accessibility invalid for 40-bit keys");
451 } 463 }
452 } 464 }
  465 + else if (strcmp(arg, "cleartext-metadata") == 0)
  466 + {
  467 + if (parameter)
  468 + {
  469 + usage("--cleartext-metadata does not take a parameter");
  470 + }
  471 + if (keylen == 40)
  472 + {
  473 + usage("--cleartext-metadata is invalid for 40-bit keys");
  474 + }
  475 + else
  476 + {
  477 + cleartext_metadata = true;
  478 + }
  479 + }
  480 + else if (strcmp(arg, "force-V4") == 0)
  481 + {
  482 + if (parameter)
  483 + {
  484 + usage("--force-V4 does not take a parameter");
  485 + }
  486 + if (keylen == 40)
  487 + {
  488 + usage("--force-V4 is invalid for 40-bit keys");
  489 + }
  490 + else
  491 + {
  492 + force_V4 = true;
  493 + }
  494 + }
  495 + else if (strcmp(arg, "use-aes") == 0)
  496 + {
  497 + if (parameter == 0)
  498 + {
  499 + usage("--use-aes must be given as --extract=option");
  500 + }
  501 + std::string val = parameter;
  502 + bool result = false;
  503 + if (val == "y")
  504 + {
  505 + result = true;
  506 + }
  507 + else if (val == "n")
  508 + {
  509 + result = false;
  510 + }
  511 + else
  512 + {
  513 + usage("invalid -use-aes parameter");
  514 + }
  515 + if (keylen == 40)
  516 + {
  517 + usage("use-aes is invalid for 40-bit keys");
  518 + }
  519 + else
  520 + {
  521 + use_aes = result;
  522 + }
  523 + }
453 else 524 else
454 { 525 {
455 usage(std::string("invalid encryption parameter --") + arg); 526 usage(std::string("invalid encryption parameter --") + arg);
@@ -516,6 +587,9 @@ int main(int argc, char* argv[]) @@ -516,6 +587,9 @@ int main(int argc, char* argv[])
516 bool r3_extract = true; 587 bool r3_extract = true;
517 QPDFWriter::r3_print_e r3_print = QPDFWriter::r3p_full; 588 QPDFWriter::r3_print_e r3_print = QPDFWriter::r3p_full;
518 QPDFWriter::r3_modify_e r3_modify = QPDFWriter::r3m_all; 589 QPDFWriter::r3_modify_e r3_modify = QPDFWriter::r3m_all;
  590 + bool force_V4 = false;
  591 + bool cleartext_metadata = false;
  592 + bool use_aes = false;
519 593
520 bool stream_data_set = false; 594 bool stream_data_set = false;
521 QPDFWriter::stream_data_e stream_data_mode = QPDFWriter::s_compress; 595 QPDFWriter::stream_data_e stream_data_mode = QPDFWriter::s_compress;
@@ -582,7 +656,8 @@ int main(int argc, char* argv[]) @@ -582,7 +656,8 @@ int main(int argc, char* argv[])
582 argc, argv, ++i, 656 argc, argv, ++i,
583 user_password, owner_password, keylen, 657 user_password, owner_password, keylen,
584 r2_print, r2_modify, r2_extract, r2_annotate, 658 r2_print, r2_modify, r2_extract, r2_annotate,
585 - r3_accessibility, r3_extract, r3_print, r3_modify); 659 + r3_accessibility, r3_extract, r3_print, r3_modify,
  660 + force_V4, cleartext_metadata, use_aes);
586 encrypt = true; 661 encrypt = true;
587 } 662 }
588 else if (strcmp(arg, "decrypt") == 0) 663 else if (strcmp(arg, "decrypt") == 0)
@@ -988,9 +1063,19 @@ int main(int argc, char* argv[]) @@ -988,9 +1063,19 @@ int main(int argc, char* argv[])
988 } 1063 }
989 else if (keylen == 128) 1064 else if (keylen == 128)
990 { 1065 {
991 - w.setR3EncryptionParameters(  
992 - user_password.c_str(), owner_password.c_str(),  
993 - r3_accessibility, r3_extract, r3_print, r3_modify); 1066 + if (force_V4 || cleartext_metadata || use_aes)
  1067 + {
  1068 + w.setR4EncryptionParameters(
  1069 + user_password.c_str(), owner_password.c_str(),
  1070 + r3_accessibility, r3_extract, r3_print, r3_modify,
  1071 + !cleartext_metadata, use_aes);
  1072 + }
  1073 + else
  1074 + {
  1075 + w.setR3EncryptionParameters(
  1076 + user_password.c_str(), owner_password.c_str(),
  1077 + r3_accessibility, r3_extract, r3_print, r3_modify);
  1078 + }
994 } 1079 }
995 else 1080 else
996 { 1081 {