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,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& pdf, char const* filename) : | @@ -37,6 +38,8 @@ QPDFWriter::QPDFWriter(QPDF& 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<Buffer>* bp) | @@ -436,14 +480,36 @@ QPDFWriter::popPipelineStack(PointerHolder<Buffer>* 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 | { |