Commit b873dc9c59f4445239f68d7138aebb53cc0df648
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
Showing
4 changed files
with
259 additions
and
60 deletions
TODO
| ... | ... | @@ -43,48 +43,53 @@ |
| 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. | |
| 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 | 94 | 2.2 |
| 90 | 95 | === | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -118,9 +118,10 @@ class DLL_EXPORT QPDFWriter |
| 118 | 118 | |
| 119 | 119 | // Set up for encrypted output. Disables stream prefiltering and |
| 120 | 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 | 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 | 125 | void setR2EncryptionParameters( |
| 125 | 126 | char const* user_password, char const* owner_password, |
| 126 | 127 | bool allow_print, bool allow_modify, |
| ... | ... | @@ -143,6 +144,11 @@ class DLL_EXPORT QPDFWriter |
| 143 | 144 | char const* user_password, char const* owner_password, |
| 144 | 145 | bool allow_accessibility, bool allow_extract, |
| 145 | 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 | 153 | // Create linearized output. Disables qdf mode, content |
| 148 | 154 | // normalization, and stream prefiltering. |
| ... | ... | @@ -182,6 +188,11 @@ class DLL_EXPORT QPDFWriter |
| 182 | 188 | void preserveObjectStreams(); |
| 183 | 189 | void generateObjectStreams(); |
| 184 | 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 | 196 | void setEncryptionParameters( |
| 186 | 197 | char const* user_password, char const* owner_password, |
| 187 | 198 | int V, int R, int key_len, std::set<int>& bits_to_clear); |
| ... | ... | @@ -231,6 +242,7 @@ class DLL_EXPORT QPDFWriter |
| 231 | 242 | // stack items are of type Pl_Buffer, the buffer is retrieved. |
| 232 | 243 | void popPipelineStack(PointerHolder<Buffer>* bp = 0); |
| 233 | 244 | |
| 245 | + void adjustAESStreamLength(unsigned long& length); | |
| 234 | 246 | void pushEncryptionFilter(); |
| 235 | 247 | void pushDiscardFilter(); |
| 236 | 248 | |
| ... | ... | @@ -251,6 +263,8 @@ class DLL_EXPORT QPDFWriter |
| 251 | 263 | bool linearized; |
| 252 | 264 | object_stream_e object_stream_mode; |
| 253 | 265 | std::string encryption_key; |
| 266 | + bool encrypt_metadata; | |
| 267 | + bool encrypt_use_aes; | |
| 254 | 268 | std::map<std::string, std::string> encryption_dictionary; |
| 255 | 269 | |
| 256 | 270 | std::string id1; // for /ID key of |
| ... | ... | @@ -267,7 +281,7 @@ class DLL_EXPORT QPDFWriter |
| 267 | 281 | std::map<int, size_t> lengths; |
| 268 | 282 | int next_objid; |
| 269 | 283 | int cur_stream_length_id; |
| 270 | - int cur_stream_length; | |
| 284 | + unsigned long cur_stream_length; | |
| 271 | 285 | bool added_newline; |
| 272 | 286 | int max_ostream_index; |
| 273 | 287 | std::set<int> normalized_streams; | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | #include <qpdf/Pl_Discard.hh> |
| 7 | 7 | #include <qpdf/Pl_Buffer.hh> |
| 8 | 8 | #include <qpdf/Pl_RC4.hh> |
| 9 | +#include <qpdf/Pl_AES_PDF.hh> | |
| 9 | 10 | #include <qpdf/Pl_Flate.hh> |
| 10 | 11 | #include <qpdf/Pl_PNGFilter.hh> |
| 11 | 12 | #include <qpdf/QUtil.hh> |
| ... | ... | @@ -37,6 +38,8 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : |
| 37 | 38 | preserve_encryption(true), |
| 38 | 39 | linearized(false), |
| 39 | 40 | object_stream_mode(o_preserve), |
| 41 | + encrypt_metadata(true), | |
| 42 | + encrypt_use_aes(false), | |
| 40 | 43 | encryption_dict_objid(0), |
| 41 | 44 | next_objid(1), |
| 42 | 45 | cur_stream_length_id(0), |
| ... | ... | @@ -188,6 +191,38 @@ QPDFWriter::setR3EncryptionParameters( |
| 188 | 191 | bool allow_accessibility, bool allow_extract, |
| 189 | 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 | 226 | // Acrobat 5 security options: |
| 192 | 227 | |
| 193 | 228 | // Checkboxes: |
| ... | ... | @@ -206,7 +241,6 @@ QPDFWriter::setR3EncryptionParameters( |
| 206 | 241 | // Low Resolution |
| 207 | 242 | // Full printing |
| 208 | 243 | |
| 209 | - std::set<int> clear; | |
| 210 | 244 | if (! allow_accessibility) |
| 211 | 245 | { |
| 212 | 246 | clear.insert(10); |
| ... | ... | @@ -251,9 +285,6 @@ QPDFWriter::setR3EncryptionParameters( |
| 251 | 285 | |
| 252 | 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 | 290 | void |
| ... | ... | @@ -282,7 +313,7 @@ QPDFWriter::setEncryptionParameters( |
| 282 | 313 | std::string U; |
| 283 | 314 | QPDF::compute_encryption_O_U( |
| 284 | 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 | 317 | setEncryptionParametersInternal( |
| 287 | 318 | V, R, key_len, P, O, U, this->id1, user_password); |
| 288 | 319 | } |
| ... | ... | @@ -326,9 +357,22 @@ QPDFWriter::setEncryptionParametersInternal( |
| 326 | 357 | encryption_dictionary["/P"] = QUtil::int_to_string(P); |
| 327 | 358 | encryption_dictionary["/O"] = QPDF_String(O).unparse(true); |
| 328 | 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 | 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 | 376 | this->encryption_key = QPDF::compute_encryption_key( |
| 333 | 377 | user_password, encryption_data); |
| 334 | 378 | } |
| ... | ... | @@ -337,7 +381,7 @@ void |
| 337 | 381 | QPDFWriter::setDataKey(int objid) |
| 338 | 382 | { |
| 339 | 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 | 387 | int |
| ... | ... | @@ -436,14 +480,36 @@ QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) |
| 436 | 480 | } |
| 437 | 481 | |
| 438 | 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 | 496 | QPDFWriter::pushEncryptionFilter() |
| 440 | 497 | { |
| 441 | 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 | 513 | pushPipeline(p); |
| 448 | 514 | } |
| 449 | 515 | // Must call this unconditionally so we can call popPipelineStack |
| ... | ... | @@ -722,6 +788,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 722 | 788 | } |
| 723 | 789 | else if (object.isDictionary()) |
| 724 | 790 | { |
| 791 | + // XXX Must not preserve Crypt filters from original stream | |
| 792 | + // dictionary | |
| 725 | 793 | writeString("<<"); |
| 726 | 794 | writeStringQDF("\n"); |
| 727 | 795 | std::set<std::string> keys = object.getKeys(); |
| ... | ... | @@ -836,6 +904,15 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 836 | 904 | } |
| 837 | 905 | |
| 838 | 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 | 916 | unparseObject(stream_dict, 0, flags, this->cur_stream_length, compress); |
| 840 | 917 | writeString("\nstream\n"); |
| 841 | 918 | pushEncryptionFilter(); |
| ... | ... | @@ -864,13 +941,29 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 864 | 941 | (! this->cur_data_key.empty())) |
| 865 | 942 | { |
| 866 | 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 | 968 | else |
| 876 | 969 | { |
| ... | ... | @@ -1000,8 +1093,9 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1000 | 1093 | writeStringQDF("\n "); |
| 1001 | 1094 | writeString(" /Type /ObjStm"); |
| 1002 | 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 | 1099 | writeStringQDF("\n "); |
| 1006 | 1100 | if (compressed) |
| 1007 | 1101 | { |
| ... | ... | @@ -1489,6 +1583,7 @@ QPDFWriter::writeHintStream(int hint_id) |
| 1489 | 1583 | writeString(QUtil::int_to_string(O)); |
| 1490 | 1584 | } |
| 1491 | 1585 | writeString(" /Length "); |
| 1586 | + adjustAESStreamLength(hlen); | |
| 1492 | 1587 | writeString(QUtil::int_to_string(hlen)); |
| 1493 | 1588 | writeString(" >>\nstream\n"); |
| 1494 | 1589 | ... | ... |
qpdf/qpdf.cc
| ... | ... | @@ -72,6 +72,9 @@ Additional flags are dependent upon key length.\n\ |
| 72 | 72 | --extract=[yn] allow other text/graphic extraction\n\ |
| 73 | 73 | --print=print-opt control printing access\n\ |
| 74 | 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 | 78 | \n\ |
| 76 | 79 | print-opt may be:\n\ |
| 77 | 80 | \n\ |
| ... | ... | @@ -89,6 +92,14 @@ Additional flags are dependent upon key length.\n\ |
| 89 | 92 | \n\ |
| 90 | 93 | The default for each permission option is to be fully permissive.\n\ |
| 91 | 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 | 103 | \n\ |
| 93 | 104 | Advanced Transformation Options\n\ |
| 94 | 105 | -------------------------------\n\ |
| ... | ... | @@ -220,7 +231,8 @@ parse_encrypt_options( |
| 220 | 231 | std::string& user_password, std::string& owner_password, int& keylen, |
| 221 | 232 | bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate, |
| 222 | 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 | 237 | if (cur_arg + 3 >= argc) |
| 226 | 238 | { |
| ... | ... | @@ -450,6 +462,65 @@ parse_encrypt_options( |
| 450 | 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 | 524 | else |
| 454 | 525 | { |
| 455 | 526 | usage(std::string("invalid encryption parameter --") + arg); |
| ... | ... | @@ -516,6 +587,9 @@ int main(int argc, char* argv[]) |
| 516 | 587 | bool r3_extract = true; |
| 517 | 588 | QPDFWriter::r3_print_e r3_print = QPDFWriter::r3p_full; |
| 518 | 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 | 594 | bool stream_data_set = false; |
| 521 | 595 | QPDFWriter::stream_data_e stream_data_mode = QPDFWriter::s_compress; |
| ... | ... | @@ -582,7 +656,8 @@ int main(int argc, char* argv[]) |
| 582 | 656 | argc, argv, ++i, |
| 583 | 657 | user_password, owner_password, keylen, |
| 584 | 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 | 661 | encrypt = true; |
| 587 | 662 | } |
| 588 | 663 | else if (strcmp(arg, "decrypt") == 0) |
| ... | ... | @@ -988,9 +1063,19 @@ int main(int argc, char* argv[]) |
| 988 | 1063 | } |
| 989 | 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 | 1080 | else |
| 996 | 1081 | { | ... | ... |