Commit e57c25814e49b82863753894ee7d97c18e4c4525

Authored by Jay Berkenbilt
1 parent 93ac1695

Support for encryption with /V=5 and /R=5 and /R=6

Read and write support is implemented for /V=5 with /R=5 as well as
/R=6.  /R=5 is the deprecated encryption method used by Acrobat IX.
/R=6 is the encryption method used by PDF 2.0 from ISO 32000-2.
1 General 1 General
2 ======= 2 =======
3 3
4 - * See if I can support the encryption format used with /R 5 /V 5  
5 - (AESV3), even though a qpdf-announce subscriber with an adobe.com  
6 - email address mentioned that this is deprecated. There is also a  
7 - new encryption format coming in a future release (PDF 2.0), which  
8 - may be better to support. As of the qpdf 3.0 release, the  
9 - specification was not publicly available yet.  
10 -  
11 - AESV3 encryption is supported with PDF 1.7 extension level 3 and is  
12 - being deprecated, but there are plenty of files out there. The  
13 - encryption format is decribed in adobe_supplement_iso32000.pdf.  
14 - Such a file must specify that it uses these extensions in its  
15 - document catalog:  
16 -  
17 - <<  
18 - /Type /Catalog  
19 - /Extensions <<  
20 - /ADBE <<  
21 - /BaseVersion /1.7  
22 - /ExtensionLevel 3  
23 - >>  
24 - >>  
25 - >>  
26 -  
27 - Possible sha256 implementations: http://sol-biotech.com/code/sha2/,  
28 - http://hashlib2plus.sourceforge.net/  
29 -  
30 * Improve the random number seed to make it more secure so that we 4 * Improve the random number seed to make it more secure so that we
31 have stronger random numbers, particularly when multiple files are 5 have stronger random numbers, particularly when multiple files are
32 generated in the same second. This code may need to be 6 generated in the same second. This code may need to be
include/qpdf/QPDF.hh
@@ -224,7 +224,7 @@ class QPDF @@ -224,7 +224,7 @@ class QPDF
224 224
225 // Encryption support 225 // Encryption support
226 226
227 - enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes }; 227 + enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes, e_aesv3 };
228 class EncryptionData 228 class EncryptionData
229 { 229 {
230 public: 230 public:
@@ -326,7 +326,7 @@ class QPDF @@ -326,7 +326,7 @@ class QPDF
326 QPDF_DLL 326 QPDF_DLL
327 static std::string compute_data_key( 327 static std::string compute_data_key(
328 std::string const& encryption_key, int objid, int generation, 328 std::string const& encryption_key, int objid, int generation,
329 - bool use_aes); 329 + bool use_aes, int encryption_V, int encryption_R);
330 QPDF_DLL 330 QPDF_DLL
331 static std::string compute_encryption_key( 331 static std::string compute_encryption_key(
332 std::string const& password, EncryptionData const& data); 332 std::string const& password, EncryptionData const& data);
@@ -337,6 +337,14 @@ class QPDF @@ -337,6 +337,14 @@ class QPDF
337 int V, int R, int key_len, int P, bool encrypt_metadata, 337 int V, int R, int key_len, int P, bool encrypt_metadata,
338 std::string const& id1, 338 std::string const& id1,
339 std::string& O, std::string& U); 339 std::string& O, std::string& U);
  340 + QPDF_DLL
  341 + static void compute_encryption_parameters_V5(
  342 + char const* user_password, char const* owner_password,
  343 + int V, int R, int key_len, int P, bool encrypt_metadata,
  344 + std::string const& id1,
  345 + std::string& encryption_key,
  346 + std::string& O, std::string& U,
  347 + std::string& OE, std::string& UE, std::string& Perms);
340 // Return the full user password as stored in the PDF file. If 348 // Return the full user password as stored in the PDF file. If
341 // you are attempting to recover the user password in a 349 // you are attempting to recover the user password in a
342 // user-presentable form, call getTrimmedUserPassword() instead. 350 // user-presentable form, call getTrimmedUserPassword() instead.
@@ -345,6 +353,10 @@ class QPDF @@ -345,6 +353,10 @@ class QPDF
345 // Return human-readable form of user password. 353 // Return human-readable form of user password.
346 QPDF_DLL 354 QPDF_DLL
347 std::string getTrimmedUserPassword() const; 355 std::string getTrimmedUserPassword() const;
  356 + // Return the previously computed or retrieved encryption key for
  357 + // this file
  358 + QPDF_DLL
  359 + std::string getEncryptionKey() const;
348 360
349 // Linearization support 361 // Linearization support
350 362
@@ -628,6 +640,13 @@ class QPDF @@ -628,6 +640,13 @@ class QPDF
628 void initializeEncryption(); 640 void initializeEncryption();
629 std::string getKeyForObject(int objid, int generation, bool use_aes); 641 std::string getKeyForObject(int objid, int generation, bool use_aes);
630 void decryptString(std::string&, int objid, int generation); 642 void decryptString(std::string&, int objid, int generation);
  643 + static std::string compute_encryption_key_from_password(
  644 + std::string const& password, EncryptionData const& data);
  645 + static std::string recover_encryption_key_with_password(
  646 + std::string const& password, EncryptionData const& data);
  647 + static std::string recover_encryption_key_with_password(
  648 + std::string const& password, EncryptionData const& data,
  649 + bool& perms_valid);
631 void decryptStream( 650 void decryptStream(
632 Pipeline*& pipeline, int objid, int generation, 651 Pipeline*& pipeline, int objid, int generation,
633 QPDFObjectHandle& stream_dict, 652 QPDFObjectHandle& stream_dict,
@@ -981,6 +1000,7 @@ class QPDF @@ -981,6 +1000,7 @@ class QPDF
981 std::ostream* err_stream; 1000 std::ostream* err_stream;
982 bool attempt_recovery; 1001 bool attempt_recovery;
983 int encryption_V; 1002 int encryption_V;
  1003 + int encryption_R;
984 bool encrypt_metadata; 1004 bool encrypt_metadata;
985 std::map<std::string, encryption_method_e> crypt_filters; 1005 std::map<std::string, encryption_method_e> crypt_filters;
986 encryption_method_e cf_stream; 1006 encryption_method_e cf_stream;
include/qpdf/QPDFWriter.hh
@@ -223,8 +223,9 @@ class QPDFWriter @@ -223,8 +223,9 @@ class QPDFWriter
223 // content normalization. Note that setting R2 encryption 223 // content normalization. Note that setting R2 encryption
224 // parameters sets the PDF version to at least 1.3, setting R3 224 // parameters sets the PDF version to at least 1.3, setting R3
225 // encryption parameters pushes the PDF version number to at least 225 // encryption parameters pushes the PDF version number to at least
226 - // 1.4, and setting R4 parameters pushes the version to at least  
227 - // 1.5, or if AES is used, 1.6. 226 + // 1.4, setting R4 parameters pushes the version to at least 1.5,
  227 + // or if AES is used, 1.6, and setting R5 or R6 parameters pushes
  228 + // the version to at least 1.7 with extension level 3.
228 QPDF_DLL 229 QPDF_DLL
229 void setR2EncryptionParameters( 230 void setR2EncryptionParameters(
230 char const* user_password, char const* owner_password, 231 char const* user_password, char const* owner_password,
@@ -241,6 +242,21 @@ class QPDFWriter @@ -241,6 +242,21 @@ class QPDFWriter
241 bool allow_accessibility, bool allow_extract, 242 bool allow_accessibility, bool allow_extract,
242 qpdf_r3_print_e print, qpdf_r3_modify_e modify, 243 qpdf_r3_print_e print, qpdf_r3_modify_e modify,
243 bool encrypt_metadata, bool use_aes); 244 bool encrypt_metadata, bool use_aes);
  245 + // R5 is deprecated. Do not use it for production use. Writing
  246 + // R5 is supported by qpdf primarily to generate test files for
  247 + // applications that may need to test R5 support.
  248 + QPDF_DLL
  249 + void setR5EncryptionParameters(
  250 + char const* user_password, char const* owner_password,
  251 + bool allow_accessibility, bool allow_extract,
  252 + qpdf_r3_print_e print, qpdf_r3_modify_e modify,
  253 + bool encrypt_metadata);
  254 + QPDF_DLL
  255 + void setR6EncryptionParameters(
  256 + char const* user_password, char const* owner_password,
  257 + bool allow_accessibility, bool allow_extract,
  258 + qpdf_r3_print_e print, qpdf_r3_modify_e modify,
  259 + bool encrypt_metadata_aes);
244 260
245 // Create linearized output. Disables qdf mode, content 261 // Create linearized output. Disables qdf mode, content
246 // normalization, and stream prefiltering. 262 // normalization, and stream prefiltering.
@@ -302,7 +318,8 @@ class QPDFWriter @@ -302,7 +318,8 @@ class QPDFWriter
302 int V, int R, int key_len, long P, 318 int V, int R, int key_len, long P,
303 std::string const& O, std::string const& U, 319 std::string const& O, std::string const& U,
304 std::string const& OE, std::string const& UE, std::string const& Perms, 320 std::string const& OE, std::string const& UE, std::string const& Perms,
305 - std::string const& id1, std::string const& user_password); 321 + std::string const& id1, std::string const& user_password,
  322 + std::string const& encryption_key);
306 void setDataKey(int objid); 323 void setDataKey(int objid);
307 int openObject(int objid = 0); 324 int openObject(int objid = 0);
308 void closeObject(int objid); 325 void closeObject(int objid);
@@ -378,6 +395,8 @@ class QPDFWriter @@ -378,6 +395,8 @@ class QPDFWriter
378 bool encrypt_metadata; 395 bool encrypt_metadata;
379 bool encrypt_use_aes; 396 bool encrypt_use_aes;
380 std::map<std::string, std::string> encryption_dictionary; 397 std::map<std::string, std::string> encryption_dictionary;
  398 + int encryption_V;
  399 + int encryption_R;
381 400
382 std::string id1; // for /ID key of 401 std::string id1; // for /ID key of
383 std::string id2; // trailer dictionary 402 std::string id2; // trailer dictionary
libqpdf/QPDF.cc
@@ -97,6 +97,7 @@ QPDF::QPDF() : @@ -97,6 +97,7 @@ QPDF::QPDF() :
97 err_stream(&std::cerr), 97 err_stream(&std::cerr),
98 attempt_recovery(true), 98 attempt_recovery(true),
99 encryption_V(0), 99 encryption_V(0),
  100 + encryption_R(0),
100 encrypt_metadata(true), 101 encrypt_metadata(true),
101 cf_stream(e_none), 102 cf_stream(e_none),
102 cf_string(e_none), 103 cf_string(e_none),
libqpdf/QPDFWriter.cc
@@ -67,6 +67,8 @@ QPDFWriter::init() @@ -67,6 +67,8 @@ QPDFWriter::init()
67 min_extension_level = 0; 67 min_extension_level = 0;
68 final_extension_level = 0; 68 final_extension_level = 0;
69 forced_extension_level = 0; 69 forced_extension_level = 0;
  70 + encryption_V = 0;
  71 + encryption_R = 0;
70 encryption_dict_objid = 0; 72 encryption_dict_objid = 0;
71 next_objid = 1; 73 next_objid = 1;
72 cur_stream_length_id = 0; 74 cur_stream_length_id = 0;
@@ -344,6 +346,38 @@ QPDFWriter::setR4EncryptionParameters( @@ -344,6 +346,38 @@ QPDFWriter::setR4EncryptionParameters(
344 } 346 }
345 347
346 void 348 void
  349 +QPDFWriter::setR5EncryptionParameters(
  350 + char const* user_password, char const* owner_password,
  351 + bool allow_accessibility, bool allow_extract,
  352 + qpdf_r3_print_e print, qpdf_r3_modify_e modify,
  353 + bool encrypt_metadata)
  354 +{
  355 + std::set<int> clear;
  356 + interpretR3EncryptionParameters(
  357 + clear, user_password, owner_password,
  358 + allow_accessibility, allow_extract, print, modify);
  359 + this->encrypt_use_aes = true;
  360 + this->encrypt_metadata = encrypt_metadata;
  361 + setEncryptionParameters(user_password, owner_password, 5, 5, 32, clear);
  362 +}
  363 +
  364 +void
  365 +QPDFWriter::setR6EncryptionParameters(
  366 + char const* user_password, char const* owner_password,
  367 + bool allow_accessibility, bool allow_extract,
  368 + qpdf_r3_print_e print, qpdf_r3_modify_e modify,
  369 + bool encrypt_metadata)
  370 +{
  371 + std::set<int> clear;
  372 + interpretR3EncryptionParameters(
  373 + clear, user_password, owner_password,
  374 + allow_accessibility, allow_extract, print, modify);
  375 + this->encrypt_use_aes = true;
  376 + this->encrypt_metadata = encrypt_metadata;
  377 + setEncryptionParameters(user_password, owner_password, 5, 6, 32, clear);
  378 +}
  379 +
  380 +void
347 QPDFWriter::interpretR3EncryptionParameters( 381 QPDFWriter::interpretR3EncryptionParameters(
348 std::set<int>& clear, 382 std::set<int>& clear,
349 char const* user_password, char const* owner_password, 383 char const* user_password, char const* owner_password,
@@ -426,6 +460,12 @@ QPDFWriter::setEncryptionParameters( @@ -426,6 +460,12 @@ QPDFWriter::setEncryptionParameters(
426 bits_to_clear.insert(1); 460 bits_to_clear.insert(1);
427 bits_to_clear.insert(2); 461 bits_to_clear.insert(2);
428 462
  463 + if (R > 3)
  464 + {
  465 + // Bit 10 is deprecated and should always be set.
  466 + bits_to_clear.erase(10);
  467 + }
  468 +
429 int P = 0; 469 int P = 0;
430 // Create the complement of P, then invert. 470 // Create the complement of P, then invert.
431 for (std::set<int>::iterator iter = bits_to_clear.begin(); 471 for (std::set<int>::iterator iter = bits_to_clear.begin();
@@ -438,11 +478,26 @@ QPDFWriter::setEncryptionParameters( @@ -438,11 +478,26 @@ QPDFWriter::setEncryptionParameters(
438 generateID(); 478 generateID();
439 std::string O; 479 std::string O;
440 std::string U; 480 std::string U;
441 - QPDF::compute_encryption_O_U(  
442 - user_password, owner_password, V, R, key_len, P,  
443 - this->encrypt_metadata, this->id1, O, U); 481 + std::string OE;
  482 + std::string UE;
  483 + std::string Perms;
  484 + std::string encryption_key;
  485 + if (V < 5)
  486 + {
  487 + QPDF::compute_encryption_O_U(
  488 + user_password, owner_password, V, R, key_len, P,
  489 + this->encrypt_metadata, this->id1, O, U);
  490 + }
  491 + else
  492 + {
  493 + QPDF::compute_encryption_parameters_V5(
  494 + user_password, owner_password, V, R, key_len, P,
  495 + this->encrypt_metadata, this->id1,
  496 + encryption_key, O, U, OE, UE, Perms);
  497 + }
444 setEncryptionParametersInternal( 498 setEncryptionParametersInternal(
445 - V, R, key_len, P, O, U, "", "", "", this->id1, user_password); 499 + V, R, key_len, P, O, U, OE, UE, Perms,
  500 + this->id1, user_password, encryption_key);
446 } 501 }
447 502
448 void 503 void
@@ -482,6 +537,19 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -482,6 +537,19 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
482 this->encrypt_metadata ? 0 : 1); 537 this->encrypt_metadata ? 0 : 1);
483 QTC::TC("qpdf", "QPDFWriter copy use_aes", 538 QTC::TC("qpdf", "QPDFWriter copy use_aes",
484 this->encrypt_use_aes ? 0 : 1); 539 this->encrypt_use_aes ? 0 : 1);
  540 + std::string OE;
  541 + std::string UE;
  542 + std::string Perms;
  543 + std::string encryption_key;
  544 + if (V >= 5)
  545 + {
  546 + QTC::TC("qpdf", "QPDFWriter copy V5");
  547 + OE = encrypt.getKey("/OE").getStringValue();
  548 + UE = encrypt.getKey("/UE").getStringValue();
  549 + Perms = encrypt.getKey("/Perms").getStringValue();
  550 + encryption_key = qpdf.getEncryptionKey();
  551 + }
  552 +
485 setEncryptionParametersInternal( 553 setEncryptionParametersInternal(
486 V, 554 V,
487 encrypt.getKey("/R").getIntValue(), 555 encrypt.getKey("/R").getIntValue(),
@@ -489,11 +557,12 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -489,11 +557,12 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
489 encrypt.getKey("/P").getIntValue(), 557 encrypt.getKey("/P").getIntValue(),
490 encrypt.getKey("/O").getStringValue(), 558 encrypt.getKey("/O").getStringValue(),
491 encrypt.getKey("/U").getStringValue(), 559 encrypt.getKey("/U").getStringValue(),
492 - "", // XXXX OE  
493 - "", // XXXX UE  
494 - "", // XXXX Perms 560 + OE,
  561 + UE,
  562 + Perms,
495 this->id1, // this->id1 == the other file's id1 563 this->id1, // this->id1 == the other file's id1
496 - qpdf.getPaddedUserPassword()); 564 + qpdf.getPaddedUserPassword(),
  565 + encryption_key);
497 } 566 }
498 } 567 }
499 568
@@ -605,10 +674,11 @@ QPDFWriter::setEncryptionParametersInternal( @@ -605,10 +674,11 @@ QPDFWriter::setEncryptionParametersInternal(
605 int V, int R, int key_len, long P, 674 int V, int R, int key_len, long P,
606 std::string const& O, std::string const& U, 675 std::string const& O, std::string const& U,
607 std::string const& OE, std::string const& UE, std::string const& Perms, 676 std::string const& OE, std::string const& UE, std::string const& Perms,
608 - std::string const& id1, std::string const& user_password) 677 + std::string const& id1, std::string const& user_password,
  678 + std::string const& encryption_key)
609 { 679 {
610 - // XXXX OE, UE, Perms, V=5  
611 - 680 + this->encryption_V = V;
  681 + this->encryption_R = R;
612 encryption_dictionary["/Filter"] = "/Standard"; 682 encryption_dictionary["/Filter"] = "/Standard";
613 encryption_dictionary["/V"] = QUtil::int_to_string(V); 683 encryption_dictionary["/V"] = QUtil::int_to_string(V);
614 encryption_dictionary["/Length"] = QUtil::int_to_string(key_len * 8); 684 encryption_dictionary["/Length"] = QUtil::int_to_string(key_len * 8);
@@ -618,9 +688,15 @@ QPDFWriter::setEncryptionParametersInternal( @@ -618,9 +688,15 @@ QPDFWriter::setEncryptionParametersInternal(
618 encryption_dictionary["/U"] = QPDF_String(U).unparse(true); 688 encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
619 if (V >= 5) 689 if (V >= 5)
620 { 690 {
621 - setMinimumPDFVersion("1.4"); 691 + encryption_dictionary["/OE"] = QPDF_String(OE).unparse(true);
  692 + encryption_dictionary["/UE"] = QPDF_String(UE).unparse(true);
  693 + encryption_dictionary["/Perms"] = QPDF_String(Perms).unparse(true);
  694 + }
  695 + if (R >= 6)
  696 + {
  697 + setMinimumPDFVersion("1.7", 8);
622 } 698 }
623 - if (R >= 5) 699 + else if (R == 5)
624 { 700 {
625 setMinimumPDFVersion("1.7", 3); 701 setMinimumPDFVersion("1.7", 3);
626 } 702 }
@@ -641,14 +717,16 @@ QPDFWriter::setEncryptionParametersInternal( @@ -641,14 +717,16 @@ QPDFWriter::setEncryptionParametersInternal(
641 { 717 {
642 encryption_dictionary["/EncryptMetadata"] = "false"; 718 encryption_dictionary["/EncryptMetadata"] = "false";
643 } 719 }
644 - if (V == 4) 720 + if ((V == 4) || (V == 5))
645 { 721 {
646 // The spec says the value for the crypt filter key can be 722 // The spec says the value for the crypt filter key can be
647 // anything, and xpdf seems to agree. However, Adobe Reader 723 // anything, and xpdf seems to agree. However, Adobe Reader
648 // won't open our files unless we use /StdCF. 724 // won't open our files unless we use /StdCF.
649 encryption_dictionary["/StmF"] = "/StdCF"; 725 encryption_dictionary["/StmF"] = "/StdCF";
650 encryption_dictionary["/StrF"] = "/StdCF"; 726 encryption_dictionary["/StrF"] = "/StdCF";
651 - std::string method = (this->encrypt_use_aes ? "/AESV2" : "/V2"); 727 + std::string method = (this->encrypt_use_aes
  728 + ? ((V < 5) ? "/AESV2" : "/AESV3")
  729 + : "/V2");
652 encryption_dictionary["/CF"] = 730 encryption_dictionary["/CF"] =
653 "<< /StdCF << /AuthEvent /DocOpen /CFM " + method + " >> >>"; 731 "<< /StdCF << /AuthEvent /DocOpen /CFM " + method + " >> >>";
654 } 732 }
@@ -656,15 +734,23 @@ QPDFWriter::setEncryptionParametersInternal( @@ -656,15 +734,23 @@ QPDFWriter::setEncryptionParametersInternal(
656 this->encrypted = true; 734 this->encrypted = true;
657 QPDF::EncryptionData encryption_data( 735 QPDF::EncryptionData encryption_data(
658 V, R, key_len, P, O, U, OE, UE, Perms, id1, this->encrypt_metadata); 736 V, R, key_len, P, O, U, OE, UE, Perms, id1, this->encrypt_metadata);
659 - this->encryption_key = QPDF::compute_encryption_key(  
660 - user_password, encryption_data); 737 + if (V < 5)
  738 + {
  739 + this->encryption_key = QPDF::compute_encryption_key(
  740 + user_password, encryption_data);
  741 + }
  742 + else
  743 + {
  744 + this->encryption_key = encryption_key;
  745 + }
661 } 746 }
662 747
663 void 748 void
664 QPDFWriter::setDataKey(int objid) 749 QPDFWriter::setDataKey(int objid)
665 { 750 {
666 this->cur_data_key = QPDF::compute_data_key( 751 this->cur_data_key = QPDF::compute_data_key(
667 - this->encryption_key, objid, 0, this->encrypt_use_aes); 752 + this->encryption_key, objid, 0,
  753 + this->encrypt_use_aes, this->encryption_V, this->encryption_R);
668 } 754 }
669 755
670 int 756 int
libqpdf/QPDF_encryption.cc
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 #include <qpdf/Pl_RC4.hh> 10 #include <qpdf/Pl_RC4.hh>
11 #include <qpdf/Pl_AES_PDF.hh> 11 #include <qpdf/Pl_AES_PDF.hh>
12 #include <qpdf/Pl_Buffer.hh> 12 #include <qpdf/Pl_Buffer.hh>
  13 +#include <qpdf/Pl_SHA2.hh>
13 #include <qpdf/RC4.hh> 14 #include <qpdf/RC4.hh>
14 #include <qpdf/MD5.hh> 15 #include <qpdf/MD5.hh>
15 16
@@ -23,9 +24,15 @@ static unsigned char const padding_string[] = { @@ -23,9 +24,15 @@ static unsigned char const padding_string[] = {
23 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a 24 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
24 }; 25 };
25 26
26 -static unsigned int const O_key_bytes = sizeof(MD5::Digest);  
27 static unsigned int const key_bytes = 32; 27 static unsigned int const key_bytes = 32;
28 28
  29 +// V4 key lengths apply to V <= 4
  30 +static unsigned int const OU_key_bytes_V4 = sizeof(MD5::Digest);
  31 +
  32 +static unsigned int const OU_key_bytes_V5 = 48;
  33 +static unsigned int const OUE_key_bytes_V5 = 32;
  34 +static unsigned int const Perms_key_bytes_V5 = 16;
  35 +
29 int 36 int
30 QPDF::EncryptionData::getV() const 37 QPDF::EncryptionData::getV() const
31 { 38 {
@@ -120,7 +127,7 @@ QPDF::EncryptionData::setV5EncryptionParameters( @@ -120,7 +127,7 @@ QPDF::EncryptionData::setV5EncryptionParameters(
120 } 127 }
121 128
122 static void 129 static void
123 -pad_or_truncate_password(std::string const& password, char k1[key_bytes]) 130 +pad_or_truncate_password_V4(std::string const& password, char k1[key_bytes])
124 { 131 {
125 int password_bytes = std::min(key_bytes, (unsigned int)password.length()); 132 int password_bytes = std::min(key_bytes, (unsigned int)password.length());
126 int pad_bytes = key_bytes - password_bytes; 133 int pad_bytes = key_bytes - password_bytes;
@@ -159,13 +166,19 @@ QPDF::trim_user_password(std::string&amp; user_password) @@ -159,13 +166,19 @@ QPDF::trim_user_password(std::string&amp; user_password)
159 } 166 }
160 167
161 static std::string 168 static std::string
162 -pad_or_truncate_password(std::string const& password) 169 +pad_or_truncate_password_V4(std::string const& password)
163 { 170 {
164 char k1[key_bytes]; 171 char k1[key_bytes];
165 - pad_or_truncate_password(password, k1); 172 + pad_or_truncate_password_V4(password, k1);
166 return std::string(k1, key_bytes); 173 return std::string(k1, key_bytes);
167 } 174 }
168 175
  176 +static std::string
  177 +truncate_password_V5(std::string const& password)
  178 +{
  179 + return password.substr(0, std::min((size_t)127, password.length()));
  180 +}
  181 +
169 static void 182 static void
170 iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) 183 iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations)
171 { 184 {
@@ -199,15 +212,146 @@ iterate_rc4(unsigned char* data, int data_len, @@ -199,15 +212,146 @@ iterate_rc4(unsigned char* data, int data_len,
199 delete [] key; 212 delete [] key;
200 } 213 }
201 214
  215 +static std::string
  216 +process_with_aes(std::string const& key,
  217 + bool encrypt,
  218 + std::string const& data,
  219 + size_t outlength = 0,
  220 + unsigned int repetitions = 1,
  221 + unsigned char const* iv = 0,
  222 + size_t iv_length = 0)
  223 +{
  224 + Pl_Buffer buffer("buffer");
  225 + Pl_AES_PDF aes("aes", &buffer, encrypt,
  226 + (unsigned char const*)key.c_str(),
  227 + (unsigned int)key.length());
  228 + if (iv)
  229 + {
  230 + aes.setIV(iv, iv_length);
  231 + }
  232 + else
  233 + {
  234 + aes.useZeroIV();
  235 + }
  236 + aes.disablePadding();
  237 + for (unsigned int i = 0; i < repetitions; ++i)
  238 + {
  239 + aes.write((unsigned char*)data.c_str(), data.length());
  240 + }
  241 + aes.finish();
  242 + PointerHolder<Buffer> bufp = buffer.getBuffer();
  243 + if (outlength == 0)
  244 + {
  245 + outlength = bufp->getSize();
  246 + }
  247 + else
  248 + {
  249 + outlength = std::min(outlength, bufp->getSize());
  250 + }
  251 + return std::string((char const*)bufp->getBuffer(), outlength);
  252 +}
  253 +
  254 +static std::string
  255 +hash_V5(std::string const& password,
  256 + std::string const& salt,
  257 + std::string const& udata,
  258 + QPDF::EncryptionData const& data)
  259 +{
  260 + Pl_SHA2 hash(256);
  261 + hash.write((unsigned char*)password.c_str(), password.length());
  262 + hash.write((unsigned char*)salt.c_str(), salt.length());
  263 + hash.write((unsigned char*)udata.c_str(), udata.length());
  264 + hash.finish();
  265 + std::string K = hash.getRawDigest();
  266 +
  267 + std::string result;
  268 + if (data.getR() < 6)
  269 + {
  270 + result = K;
  271 + }
  272 + else
  273 + {
  274 + // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash
  275 +
  276 + int round_number = 0;
  277 + bool done = false;
  278 + while (! done)
  279 + {
  280 + // The hash algorithm has us setting K initially to the R5
  281 + // value and then repeating a series of steps 64 times
  282 + // before starting with the termination case testing. The
  283 + // wording of the specification is very unclear as to the
  284 + // exact number of times it should be run since the
  285 + // wording about whether the initial setup counts as round
  286 + // 0 or not is ambiguous. This code counts the initial
  287 + // setup (R5) value as round 0, which appears to be
  288 + // correct. This was determined to be correct by
  289 + // increasing or decreasing the number of rounds by 1 or 2
  290 + // from this value and generating 20 test files. In this
  291 + // interpretation, all the test files worked with Adobe
  292 + // Reader X. In the other configurations, many of the
  293 + // files did not work, and we were accurately able to
  294 + // predict which files didn't work by looking at the
  295 + // conditions under which we terminated repetition.
  296 +
  297 + ++round_number;
  298 + std::string K1 = password + K + udata;
  299 + assert(K.length() >= 32);
  300 + std::string E = process_with_aes(
  301 + K.substr(0, 16), true, K1, 0, 64,
  302 + (unsigned char*)K.substr(16, 16).c_str(), 16);
  303 +
  304 + // E_mod_3 is supposed to be mod 3 of the first 16 bytes
  305 + // of E taken as as a (128-bit) big-endian number. Since
  306 + // (xy mod n) is equal to ((x mod n) + (y mod n)) mod n
  307 + // and since 256 mod n is 1, we can just take the sums of
  308 + // the the mod 3s of each byte to get the same result.
  309 + int E_mod_3 = 0;
  310 + for (unsigned int i = 0; i < 16; ++i)
  311 + {
  312 + E_mod_3 += (unsigned char)E[i];
  313 + }
  314 + E_mod_3 %= 3;
  315 + int next_hash = ((E_mod_3 == 0) ? 256 :
  316 + (E_mod_3 == 1) ? 384 :
  317 + 512);
  318 + Pl_SHA2 hash(next_hash);
  319 + hash.write((unsigned char*)E.c_str(), E.length());
  320 + hash.finish();
  321 + K = hash.getRawDigest();
  322 +
  323 + if (round_number >= 64)
  324 + {
  325 + unsigned int ch = (unsigned int)((unsigned char) *(E.rbegin()));
  326 +
  327 + if (ch <= (unsigned int)(round_number - 32))
  328 + {
  329 + done = true;
  330 + }
  331 + }
  332 + }
  333 + result = K.substr(0, 32);
  334 + }
  335 +
  336 + return result;
  337 +}
  338 +
202 std::string 339 std::string
203 QPDF::compute_data_key(std::string const& encryption_key, 340 QPDF::compute_data_key(std::string const& encryption_key,
204 - int objid, int generation,  
205 - bool use_aes) 341 + int objid, int generation, bool use_aes,
  342 + int encryption_V, int encryption_R)
206 { 343 {
207 // Algorithm 3.1 from the PDF 1.7 Reference Manual 344 // Algorithm 3.1 from the PDF 1.7 Reference Manual
208 345
209 std::string result = encryption_key; 346 std::string result = encryption_key;
210 347
  348 + if (encryption_V >= 5)
  349 + {
  350 + // Algorithm 3.1a (PDF 1.7 extension level 3): just use
  351 + // encryption key straight.
  352 + return result;
  353 + }
  354 +
211 // Append low three bytes of object ID and low two bytes of generation 355 // Append low three bytes of object ID and low two bytes of generation
212 result += (char) (objid & 0xff); 356 result += (char) (objid & 0xff);
213 result += (char) ((objid >> 8) & 0xff); 357 result += (char) ((objid >> 8) & 0xff);
@@ -231,11 +375,37 @@ std::string @@ -231,11 +375,37 @@ std::string
231 QPDF::compute_encryption_key( 375 QPDF::compute_encryption_key(
232 std::string const& password, EncryptionData const& data) 376 std::string const& password, EncryptionData const& data)
233 { 377 {
  378 + if (data.getV() >= 5)
  379 + {
  380 + // For V >= 5, the encryption key is generated and stored in
  381 + // the file, encrypted separately with both user and owner
  382 + // passwords.
  383 + return recover_encryption_key_with_password(password, data);
  384 + }
  385 + else
  386 + {
  387 + // For V < 5, the encryption key is derived from the user
  388 + // password.
  389 + return compute_encryption_key_from_password(password, data);
  390 + }
  391 +}
  392 +
  393 +std::string
  394 +QPDF::compute_encryption_key_from_password(
  395 + std::string const& password, EncryptionData const& data)
  396 +{
234 // Algorithm 3.2 from the PDF 1.7 Reference Manual 397 // Algorithm 3.2 from the PDF 1.7 Reference Manual
235 398
  399 + // This code does not properly handle Unicode passwords.
  400 + // Passwords are supposed to be converted from OS codepage
  401 + // characters to PDFDocEncoding. Unicode passwords are supposed
  402 + // to be converted to OS codepage before converting to
  403 + // PDFDocEncoding. We instead require the password to be
  404 + // presented in its final form.
  405 +
236 MD5 md5; 406 MD5 md5;
237 md5.encodeDataIncrementally( 407 md5.encodeDataIncrementally(
238 - pad_or_truncate_password(password).c_str(), key_bytes); 408 + pad_or_truncate_password_V4(password).c_str(), key_bytes);
239 md5.encodeDataIncrementally(data.getO().c_str(), key_bytes); 409 md5.encodeDataIncrementally(data.getO().c_str(), key_bytes);
240 char pbytes[4]; 410 char pbytes[4];
241 int P = data.getP(); 411 int P = data.getP();
@@ -261,8 +431,13 @@ static void @@ -261,8 +431,13 @@ static void
261 compute_O_rc4_key(std::string const& user_password, 431 compute_O_rc4_key(std::string const& user_password,
262 std::string const& owner_password, 432 std::string const& owner_password,
263 QPDF::EncryptionData const& data, 433 QPDF::EncryptionData const& data,
264 - unsigned char key[O_key_bytes]) 434 + unsigned char key[OU_key_bytes_V4])
265 { 435 {
  436 + if (data.getV() >= 5)
  437 + {
  438 + throw std::logic_error(
  439 + "compute_O_rc4_key called for file with V >= 5");
  440 + }
266 std::string password = owner_password; 441 std::string password = owner_password;
267 if (password.empty()) 442 if (password.empty())
268 { 443 {
@@ -270,10 +445,10 @@ compute_O_rc4_key(std::string const&amp; user_password, @@ -270,10 +445,10 @@ compute_O_rc4_key(std::string const&amp; user_password,
270 } 445 }
271 MD5 md5; 446 MD5 md5;
272 md5.encodeDataIncrementally( 447 md5.encodeDataIncrementally(
273 - pad_or_truncate_password(password).c_str(), key_bytes); 448 + pad_or_truncate_password_V4(password).c_str(), key_bytes);
274 MD5::Digest digest; 449 MD5::Digest digest;
275 iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0)); 450 iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0));
276 - memcpy(key, digest, O_key_bytes); 451 + memcpy(key, digest, OU_key_bytes_V4);
277 } 452 }
278 453
279 static std::string 454 static std::string
@@ -283,11 +458,11 @@ compute_O_value(std::string const&amp; user_password, @@ -283,11 +458,11 @@ compute_O_value(std::string const&amp; user_password,
283 { 458 {
284 // Algorithm 3.3 from the PDF 1.7 Reference Manual 459 // Algorithm 3.3 from the PDF 1.7 Reference Manual
285 460
286 - unsigned char O_key[O_key_bytes]; 461 + unsigned char O_key[OU_key_bytes_V4];
287 compute_O_rc4_key(user_password, owner_password, data, O_key); 462 compute_O_rc4_key(user_password, owner_password, data, O_key);
288 463
289 char upass[key_bytes]; 464 char upass[key_bytes];
290 - pad_or_truncate_password(user_password, upass); 465 + pad_or_truncate_password_V4(user_password, upass);
291 iterate_rc4((unsigned char*) upass, key_bytes, 466 iterate_rc4((unsigned char*) upass, key_bytes,
292 O_key, data.getLengthBytes(), 467 O_key, data.getLengthBytes(),
293 (data.getR() >= 3) ? 20 : 1, false); 468 (data.getR() >= 3) ? 20 : 1, false);
@@ -303,7 +478,7 @@ compute_U_value_R2(std::string const&amp; user_password, @@ -303,7 +478,7 @@ compute_U_value_R2(std::string const&amp; user_password,
303 478
304 std::string k1 = QPDF::compute_encryption_key(user_password, data); 479 std::string k1 = QPDF::compute_encryption_key(user_password, data);
305 char udata[key_bytes]; 480 char udata[key_bytes];
306 - pad_or_truncate_password("", udata); 481 + pad_or_truncate_password_V4("", udata);
307 iterate_rc4((unsigned char*) udata, key_bytes, 482 iterate_rc4((unsigned char*) udata, key_bytes,
308 (unsigned char*)k1.c_str(), data.getLengthBytes(), 1, false); 483 (unsigned char*)k1.c_str(), data.getLengthBytes(), 1, false);
309 return std::string(udata, key_bytes); 484 return std::string(udata, key_bytes);
@@ -319,7 +494,7 @@ compute_U_value_R3(std::string const&amp; user_password, @@ -319,7 +494,7 @@ compute_U_value_R3(std::string const&amp; user_password,
319 std::string k1 = QPDF::compute_encryption_key(user_password, data); 494 std::string k1 = QPDF::compute_encryption_key(user_password, data);
320 MD5 md5; 495 MD5 md5;
321 md5.encodeDataIncrementally( 496 md5.encodeDataIncrementally(
322 - pad_or_truncate_password("").c_str(), key_bytes); 497 + pad_or_truncate_password_V4("").c_str(), key_bytes);
323 md5.encodeDataIncrementally(data.getId1().c_str(), 498 md5.encodeDataIncrementally(data.getId1().c_str(),
324 (int)data.getId1().length()); 499 (int)data.getId1().length());
325 MD5::Digest digest; 500 MD5::Digest digest;
@@ -350,40 +525,214 @@ compute_U_value(std::string const&amp; user_password, @@ -350,40 +525,214 @@ compute_U_value(std::string const&amp; user_password,
350 } 525 }
351 526
352 static bool 527 static bool
353 -check_user_password(std::string const& user_password,  
354 - QPDF::EncryptionData const& data) 528 +check_user_password_V4(std::string const& user_password,
  529 + QPDF::EncryptionData const& data)
355 { 530 {
356 // Algorithm 3.6 from the PDF 1.7 Reference Manual 531 // Algorithm 3.6 from the PDF 1.7 Reference Manual
357 532
358 std::string u_value = compute_U_value(user_password, data); 533 std::string u_value = compute_U_value(user_password, data);
359 - int to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest) : key_bytes); 534 + int to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest)
  535 + : key_bytes);
360 return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0); 536 return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0);
361 } 537 }
362 538
363 static bool 539 static bool
364 -check_owner_password(std::string& user_password,  
365 - std::string const& owner_password,  
366 - QPDF::EncryptionData const& data) 540 +check_user_password_V5(std::string const& user_password,
  541 + QPDF::EncryptionData const& data)
  542 +{
  543 + // Algorithm 3.11 from the PDF 1.7 extension level 3
  544 +
  545 + std::string user_data = data.getU().substr(0, 32);
  546 + std::string validation_salt = data.getU().substr(32, 8);
  547 + std::string password = truncate_password_V5(user_password);
  548 + return (hash_V5(password, validation_salt, "", data) == user_data);
  549 +}
  550 +
  551 +static bool
  552 +check_user_password(std::string const& user_password,
  553 + QPDF::EncryptionData const& data)
  554 +{
  555 + if (data.getV() < 5)
  556 + {
  557 + return check_user_password_V4(user_password, data);
  558 + }
  559 + else
  560 + {
  561 + return check_user_password_V5(user_password, data);
  562 + }
  563 +}
  564 +
  565 +static bool
  566 +check_owner_password_V4(std::string& user_password,
  567 + std::string const& owner_password,
  568 + QPDF::EncryptionData const& data)
367 { 569 {
368 // Algorithm 3.7 from the PDF 1.7 Reference Manual 570 // Algorithm 3.7 from the PDF 1.7 Reference Manual
369 571
370 - unsigned char key[O_key_bytes]; 572 + unsigned char key[OU_key_bytes_V4];
371 compute_O_rc4_key(user_password, owner_password, data, key); 573 compute_O_rc4_key(user_password, owner_password, data, key);
372 unsigned char O_data[key_bytes]; 574 unsigned char O_data[key_bytes];
373 memcpy(O_data, (unsigned char*) data.getO().c_str(), key_bytes); 575 memcpy(O_data, (unsigned char*) data.getO().c_str(), key_bytes);
374 iterate_rc4(O_data, key_bytes, key, data.getLengthBytes(), 576 iterate_rc4(O_data, key_bytes, key, data.getLengthBytes(),
375 - (data.getR() >= 3) ? 20 : 1, true); 577 + (data.getR() >= 3) ? 20 : 1, true);
376 std::string new_user_password = 578 std::string new_user_password =
377 - std::string((char*)O_data, key_bytes); 579 + std::string((char*)O_data, key_bytes);
378 bool result = false; 580 bool result = false;
379 if (check_user_password(new_user_password, data)) 581 if (check_user_password(new_user_password, data))
380 { 582 {
381 - result = true;  
382 - user_password = new_user_password; 583 + result = true;
  584 + user_password = new_user_password;
383 } 585 }
384 return result; 586 return result;
385 } 587 }
386 588
  589 +static bool
  590 +check_owner_password_V5(std::string const& owner_password,
  591 + QPDF::EncryptionData const& data)
  592 +{
  593 + // Algorithm 3.12 from the PDF 1.7 extension level 3
  594 +
  595 + std::string user_data = data.getU().substr(0, 48);
  596 + std::string owner_data = data.getO().substr(0, 32);
  597 + std::string validation_salt = data.getO().substr(32, 8);
  598 + std::string password = truncate_password_V5(owner_password);
  599 + return (hash_V5(password, validation_salt, user_data,
  600 + data) == owner_data);
  601 +}
  602 +
  603 +static bool
  604 +check_owner_password(std::string& user_password,
  605 + std::string const& owner_password,
  606 + QPDF::EncryptionData const& data)
  607 +{
  608 + if (data.getV() < 5)
  609 + {
  610 + return check_owner_password_V4(user_password, owner_password, data);
  611 + }
  612 + else
  613 + {
  614 + return check_owner_password_V5(owner_password, data);
  615 + }
  616 +}
  617 +
  618 +std::string
  619 +QPDF::recover_encryption_key_with_password(
  620 + std::string const& password, EncryptionData const& data)
  621 +{
  622 + // Disregard whether Perms is valid.
  623 + bool disregard;
  624 + return recover_encryption_key_with_password(password, data, disregard);
  625 +}
  626 +
  627 +static void
  628 +compute_U_UE_value_V5(std::string const& user_password,
  629 + std::string const& encryption_key,
  630 + QPDF::EncryptionData const& data,
  631 + std::string& U, std::string& UE)
  632 +{
  633 + // Algorithm 3.8 from the PDF 1.7 extension level 3
  634 + char k[16];
  635 + QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k));
  636 + std::string validation_salt(k, 8);
  637 + std::string key_salt(k + 8, 8);
  638 + U = hash_V5(user_password, validation_salt, "", data) +
  639 + validation_salt + key_salt;
  640 + std::string intermediate_key = hash_V5(user_password, key_salt, "", data);
  641 + UE = process_with_aes(intermediate_key, true, encryption_key);
  642 +}
  643 +
  644 +static void
  645 +compute_O_OE_value_V5(std::string const& owner_password,
  646 + std::string const& encryption_key,
  647 + QPDF::EncryptionData const& data,
  648 + std::string const& U,
  649 + std::string& O, std::string& OE)
  650 +{
  651 + // Algorithm 3.9 from the PDF 1.7 extension level 3
  652 + char k[16];
  653 + QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k));
  654 + std::string validation_salt(k, 8);
  655 + std::string key_salt(k + 8, 8);
  656 + O = hash_V5(owner_password, validation_salt, U, data) +
  657 + validation_salt + key_salt;
  658 + std::string intermediate_key = hash_V5(owner_password, key_salt, U, data);
  659 + OE = process_with_aes(intermediate_key, true, encryption_key);
  660 +}
  661 +
  662 +void
  663 +compute_Perms_value_V5_clear(std::string const& encryption_key,
  664 + QPDF::EncryptionData const& data,
  665 + unsigned char k[16])
  666 +{
  667 + // From algorithm 3.10 from the PDF 1.7 extension level 3
  668 + unsigned long long extended_perms = 0xffffffff00000000LL | data.getP();
  669 + for (int i = 0; i < 8; ++i)
  670 + {
  671 + k[i] = (unsigned char) (extended_perms & 0xff);
  672 + extended_perms >>= 8;
  673 + }
  674 + k[8] = data.getEncryptMetadata() ? 'T' : 'F';
  675 + k[9] = 'a';
  676 + k[10] = 'd';
  677 + k[11] = 'b';
  678 + QUtil::initializeWithRandomBytes(k + 12, 4);
  679 +}
  680 +
  681 +static std::string
  682 +compute_Perms_value_V5(std::string const& encryption_key,
  683 + QPDF::EncryptionData const& data)
  684 +{
  685 + // Algorithm 3.10 from the PDF 1.7 extension level 3
  686 + unsigned char k[16];
  687 + compute_Perms_value_V5_clear(encryption_key, data, k);
  688 + return process_with_aes(encryption_key, true,
  689 + std::string((char const*) k, sizeof(k)));
  690 +}
  691 +
  692 +std::string
  693 +QPDF::recover_encryption_key_with_password(
  694 + std::string const& password, EncryptionData const& data,
  695 + bool& perms_valid)
  696 +{
  697 + // Algorithm 3.2a from the PDF 1.7 extension level 3
  698 +
  699 + // This code does not handle Unicode passwords correctly.
  700 + // Empirical evidence suggests that most viewers don't. We are
  701 + // supposed to process the input string with the SASLprep (RFC
  702 + // 4013) profile of stringprep (RFC 3454) and then convert the
  703 + // result to UTF-8.
  704 +
  705 + perms_valid = false;
  706 + std::string key_password = truncate_password_V5(password);
  707 + std::string key_salt;
  708 + std::string user_data;
  709 + std::string encrypted_file_key;
  710 + if (check_owner_password_V5(key_password, data))
  711 + {
  712 + key_salt = data.getO().substr(40, 8);
  713 + user_data = data.getU().substr(0, 48);
  714 + encrypted_file_key = data.getOE().substr(0, 32);
  715 + }
  716 + else if (check_user_password_V5(key_password, data))
  717 + {
  718 + key_salt = data.getU().substr(40, 8);
  719 + encrypted_file_key = data.getUE().substr(0, 32);
  720 + }
  721 + std::string intermediate_key =
  722 + hash_V5(key_password, key_salt, user_data, data);
  723 + std::string file_key =
  724 + process_with_aes(intermediate_key, false, encrypted_file_key);
  725 +
  726 + // Decrypt Perms and check against expected value
  727 + std::string perms_check =
  728 + process_with_aes(file_key, false, data.getPerms(), 12);
  729 + unsigned char k[16];
  730 + compute_Perms_value_V5_clear(file_key, data, k);
  731 + perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0);
  732 +
  733 + return file_key;
  734 +}
  735 +
387 QPDF::encryption_method_e 736 QPDF::encryption_method_e
388 QPDF::interpretCF(QPDFObjectHandle cf) 737 QPDF::interpretCF(QPDFObjectHandle cf)
389 { 738 {
@@ -487,29 +836,70 @@ QPDF::initializeEncryption() @@ -487,29 +836,70 @@ QPDF::initializeEncryption()
487 std::string U = encryption_dict.getKey("/U").getStringValue(); 836 std::string U = encryption_dict.getKey("/U").getStringValue();
488 unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue(); 837 unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue();
489 838
490 - if (! (((R == 2) || (R == 3) || (R == 4)) &&  
491 - ((V == 1) || (V == 2) || (V == 4)))) 839 + // If supporting new encryption R/V values, remember to update
  840 + // error message inside this if statement.
  841 + if (! (((R >= 2) && (R <= 6)) &&
  842 + ((V == 1) || (V == 2) || (V == 4) || (V == 5))))
492 { 843 {
493 throw QPDFExc(qpdf_e_unsupported, this->file->getName(), 844 throw QPDFExc(qpdf_e_unsupported, this->file->getName(),
494 "encryption dictionary", this->file->getLastOffset(), 845 "encryption dictionary", this->file->getLastOffset(),
495 - "Unsupported /R or /V in encryption dictionary"); 846 + "Unsupported /R or /V in encryption dictionary; R = " +
  847 + QUtil::int_to_string(R) + " (max 6), V = " +
  848 + QUtil::int_to_string(V) + " (max 5)");
496 } 849 }
497 850
498 this->encryption_V = V; 851 this->encryption_V = V;
  852 + this->encryption_R = R;
  853 +
  854 + // OE, UE, and Perms are only present if V >= 5.
  855 + std::string OE;
  856 + std::string UE;
  857 + std::string Perms;
499 858
500 - if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) 859 + if (V < 5)
501 { 860 {
502 - throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),  
503 - "encryption dictionary", this->file->getLastOffset(),  
504 - "incorrect length for /O and/or /P in "  
505 - "encryption dictionary"); 861 + if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
  862 + {
  863 + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
  864 + "encryption dictionary", this->file->getLastOffset(),
  865 + "incorrect length for /O and/or /U in "
  866 + "encryption dictionary");
  867 + }
  868 + }
  869 + else
  870 + {
  871 + if (! (encryption_dict.getKey("/OE").isString() &&
  872 + encryption_dict.getKey("/UE").isString() &&
  873 + encryption_dict.getKey("/Perms").isString()))
  874 + {
  875 + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
  876 + "encryption dictionary", this->file->getLastOffset(),
  877 + "some V=5 encryption dictionary parameters are "
  878 + "missing or the wrong type");
  879 + }
  880 + OE = encryption_dict.getKey("/OE").getStringValue();
  881 + UE = encryption_dict.getKey("/UE").getStringValue();
  882 + Perms = encryption_dict.getKey("/Perms").getStringValue();
  883 +
  884 + if ((O.length() < OU_key_bytes_V5) ||
  885 + (U.length() < OU_key_bytes_V5) ||
  886 + (OE.length() < OUE_key_bytes_V5) ||
  887 + (UE.length() < OUE_key_bytes_V5) ||
  888 + (Perms.length() < Perms_key_bytes_V5))
  889 + {
  890 + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
  891 + "encryption dictionary", this->file->getLastOffset(),
  892 + "incorrect length for some of"
  893 + " /O, /U, /OE, /UE, or /Perms in"
  894 + " encryption dictionary");
  895 + }
506 } 896 }
507 897
508 int Length = 40; 898 int Length = 40;
509 if (encryption_dict.getKey("/Length").isInteger()) 899 if (encryption_dict.getKey("/Length").isInteger())
510 { 900 {
511 Length = encryption_dict.getKey("/Length").getIntValue(); 901 Length = encryption_dict.getKey("/Length").getIntValue();
512 - if ((Length % 8) || (Length < 40) || (Length > 128)) 902 + if ((Length % 8) || (Length < 40) || (Length > 256))
513 { 903 {
514 throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), 904 throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
515 "encryption dictionary", this->file->getLastOffset(), 905 "encryption dictionary", this->file->getLastOffset(),
@@ -524,7 +914,7 @@ QPDF::initializeEncryption() @@ -524,7 +914,7 @@ QPDF::initializeEncryption()
524 encryption_dict.getKey("/EncryptMetadata").getBoolValue(); 914 encryption_dict.getKey("/EncryptMetadata").getBoolValue();
525 } 915 }
526 916
527 - if (V == 4) 917 + if ((V == 4) || (V == 5))
528 { 918 {
529 QPDFObjectHandle CF = encryption_dict.getKey("/CF"); 919 QPDFObjectHandle CF = encryption_dict.getKey("/CF");
530 std::set<std::string> keys = CF.getKeys(); 920 std::set<std::string> keys = CF.getKeys();
@@ -549,6 +939,11 @@ QPDF::initializeEncryption() @@ -549,6 +939,11 @@ QPDF::initializeEncryption()
549 QTC::TC("qpdf", "QPDF_encryption CFM AESV2"); 939 QTC::TC("qpdf", "QPDF_encryption CFM AESV2");
550 method = e_aes; 940 method = e_aes;
551 } 941 }
  942 + else if (method_name == "/AESV3")
  943 + {
  944 + QTC::TC("qpdf", "QPDF_encryption CFM AESV3");
  945 + method = e_aesv3;
  946 + }
552 else 947 else
553 { 948 {
554 // Don't complain now -- maybe we won't need 949 // Don't complain now -- maybe we won't need
@@ -574,13 +969,14 @@ QPDF::initializeEncryption() @@ -574,13 +969,14 @@ QPDF::initializeEncryption()
574 this->cf_file = this->cf_stream; 969 this->cf_file = this->cf_stream;
575 } 970 }
576 } 971 }
577 - EncryptionData data(V, R, Length / 8, P, O, U, "", "", "", 972 +
  973 + EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms,
578 id1, this->encrypt_metadata); 974 id1, this->encrypt_metadata);
579 if (check_owner_password( 975 if (check_owner_password(
580 this->user_password, this->provided_password, data)) 976 this->user_password, this->provided_password, data))
581 { 977 {
582 // password supplied was owner password; user_password has 978 // password supplied was owner password; user_password has
583 - // been initialized 979 + // been initialized for V < 5
584 } 980 }
585 else if (check_user_password(this->provided_password, data)) 981 else if (check_user_password(this->provided_password, data))
586 { 982 {
@@ -592,7 +988,30 @@ QPDF::initializeEncryption() @@ -592,7 +988,30 @@ QPDF::initializeEncryption()
592 "", 0, "invalid password"); 988 "", 0, "invalid password");
593 } 989 }
594 990
595 - this->encryption_key = compute_encryption_key(this->user_password, data); 991 + if (V < 5)
  992 + {
  993 + // For V < 5, the user password is encrypted with the owner
  994 + // password, and the user password is always used for
  995 + // computing the encryption key.
  996 + this->encryption_key = compute_encryption_key(
  997 + this->user_password, data);
  998 + }
  999 + else
  1000 + {
  1001 + // For V >= 5, either password can be used independently to
  1002 + // compute the encryption key, and neither password can be
  1003 + // used to recover the other.
  1004 + bool perms_valid;
  1005 + this->encryption_key = recover_encryption_key_with_password(
  1006 + this->provided_password, data, perms_valid);
  1007 + if (! perms_valid)
  1008 + {
  1009 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
  1010 + "encryption dictionary", this->file->getLastOffset(),
  1011 + "/Perms field in encryption dictionary"
  1012 + " doesn't match expected value"));
  1013 + }
  1014 + }
596 } 1015 }
597 1016
598 std::string 1017 std::string
@@ -608,7 +1027,8 @@ QPDF::getKeyForObject(int objid, int generation, bool use_aes) @@ -608,7 +1027,8 @@ QPDF::getKeyForObject(int objid, int generation, bool use_aes)
608 (generation == this->cached_key_generation))) 1027 (generation == this->cached_key_generation)))
609 { 1028 {
610 this->cached_object_encryption_key = 1029 this->cached_object_encryption_key =
611 - compute_data_key(this->encryption_key, objid, generation, use_aes); 1030 + compute_data_key(this->encryption_key, objid, generation,
  1031 + use_aes, this->encryption_V, this->encryption_R);
612 this->cached_key_objid = objid; 1032 this->cached_key_objid = objid;
613 this->cached_key_generation = generation; 1033 this->cached_key_generation = generation;
614 } 1034 }
@@ -624,7 +1044,7 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -624,7 +1044,7 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
624 return; 1044 return;
625 } 1045 }
626 bool use_aes = false; 1046 bool use_aes = false;
627 - if (this->encryption_V == 4) 1047 + if (this->encryption_V >= 4)
628 { 1048 {
629 switch (this->cf_string) 1049 switch (this->cf_string)
630 { 1050 {
@@ -635,6 +1055,10 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -635,6 +1055,10 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
635 use_aes = true; 1055 use_aes = true;
636 break; 1056 break;
637 1057
  1058 + case e_aesv3:
  1059 + use_aes = true;
  1060 + break;
  1061 +
638 case e_rc4: 1062 case e_rc4:
639 break; 1063 break;
640 1064
@@ -710,7 +1134,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -710,7 +1134,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
710 return; 1134 return;
711 } 1135 }
712 bool use_aes = false; 1136 bool use_aes = false;
713 - if (this->encryption_V == 4) 1137 + if (this->encryption_V >= 4)
714 { 1138 {
715 encryption_method_e method = e_unknown; 1139 encryption_method_e method = e_unknown;
716 std::string method_source = "/StmF from /Encrypt dictionary"; 1140 std::string method_source = "/StmF from /Encrypt dictionary";
@@ -747,7 +1171,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -747,7 +1171,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
747 if (crypt_params.isDictionary() && 1171 if (crypt_params.isDictionary() &&
748 crypt_params.getKey("/Name").isName()) 1172 crypt_params.getKey("/Name").isName())
749 { 1173 {
750 -// XXX QTC::TC("qpdf", "QPDF_encrypt crypt array"); 1174 + QTC::TC("qpdf", "QPDF_encrypt crypt array");
751 method = interpretCF( 1175 method = interpretCF(
752 crypt_params.getKey("/Name")); 1176 crypt_params.getKey("/Name"));
753 method_source = "stream's Crypt " 1177 method_source = "stream's Crypt "
@@ -790,6 +1214,10 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -790,6 +1214,10 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
790 use_aes = true; 1214 use_aes = true;
791 break; 1215 break;
792 1216
  1217 + case e_aesv3:
  1218 + use_aes = true;
  1219 + break;
  1220 +
793 case e_rc4: 1221 case e_rc4:
794 break; 1222 break;
795 1223
@@ -831,6 +1259,11 @@ QPDF::compute_encryption_O_U( @@ -831,6 +1259,11 @@ QPDF::compute_encryption_O_U(
831 int V, int R, int key_len, int P, bool encrypt_metadata, 1259 int V, int R, int key_len, int P, bool encrypt_metadata,
832 std::string const& id1, std::string& O, std::string& U) 1260 std::string const& id1, std::string& O, std::string& U)
833 { 1261 {
  1262 + if (V >= 5)
  1263 + {
  1264 + throw std::logic_error(
  1265 + "compute_encryption_O_U called for file with V >= 5");
  1266 + }
834 EncryptionData data(V, R, key_len, P, "", "", "", "", "", 1267 EncryptionData data(V, R, key_len, P, "", "", "", "", "",
835 id1, encrypt_metadata); 1268 id1, encrypt_metadata);
836 data.setO(compute_O_value(user_password, owner_password, data)); 1269 data.setO(compute_O_value(user_password, owner_password, data));
@@ -839,6 +1272,26 @@ QPDF::compute_encryption_O_U( @@ -839,6 +1272,26 @@ QPDF::compute_encryption_O_U(
839 U = data.getU(); 1272 U = data.getU();
840 } 1273 }
841 1274
  1275 +void
  1276 +QPDF::compute_encryption_parameters_V5(
  1277 + char const* user_password, char const* owner_password,
  1278 + int V, int R, int key_len, int P, bool encrypt_metadata,
  1279 + std::string const& id1,
  1280 + std::string& encryption_key,
  1281 + std::string& O, std::string& U,
  1282 + std::string& OE, std::string& UE, std::string& Perms)
  1283 +{
  1284 + EncryptionData data(V, R, key_len, P, "", "", "", "", "",
  1285 + id1, encrypt_metadata);
  1286 + unsigned char k[key_bytes];
  1287 + QUtil::initializeWithRandomBytes(k, key_bytes);
  1288 + encryption_key = std::string((char const*)k, key_bytes);
  1289 + compute_U_UE_value_V5(user_password, encryption_key, data, U, UE);
  1290 + compute_O_OE_value_V5(owner_password, encryption_key, data, U, O, OE);
  1291 + Perms = compute_Perms_value_V5(encryption_key, data);
  1292 + data.setV5EncryptionParameters(O, OE, U, UE, Perms);
  1293 +}
  1294 +
842 std::string const& 1295 std::string const&
843 QPDF::getPaddedUserPassword() const 1296 QPDF::getPaddedUserPassword() const
844 { 1297 {
@@ -853,6 +1306,12 @@ QPDF::getTrimmedUserPassword() const @@ -853,6 +1306,12 @@ QPDF::getTrimmedUserPassword() const
853 return result; 1306 return result;
854 } 1307 }
855 1308
  1309 +std::string
  1310 +QPDF::getEncryptionKey() const
  1311 +{
  1312 + return this->encryption_key;
  1313 +}
  1314 +
856 bool 1315 bool
857 QPDF::isEncrypted() const 1316 QPDF::isEncrypted() const
858 { 1317 {
qpdf/qpdf.cc
@@ -97,7 +97,7 @@ Note that -- terminates parsing of encryption flags.\n\ @@ -97,7 +97,7 @@ Note that -- terminates parsing of encryption flags.\n\
97 Either or both of the user password and the owner password may be\n\ 97 Either or both of the user password and the owner password may be\n\
98 empty strings.\n\ 98 empty strings.\n\
99 \n\ 99 \n\
100 -key-length may be 40 or 128\n\ 100 +key-length may be 40, 128, or 256\n\
101 \n\ 101 \n\
102 Additional flags are dependent upon key length.\n\ 102 Additional flags are dependent upon key length.\n\
103 \n\ 103 \n\
@@ -118,6 +118,11 @@ Additional flags are dependent upon key length.\n\ @@ -118,6 +118,11 @@ Additional flags are dependent upon key length.\n\
118 --use-aes=[yn] indicates whether to use AES encryption\n\ 118 --use-aes=[yn] indicates whether to use AES encryption\n\
119 --force-V4 forces use of V=4 encryption handler\n\ 119 --force-V4 forces use of V=4 encryption handler\n\
120 \n\ 120 \n\
  121 + If 256, options are the same as 128 with these exceptions:\n\
  122 + --force-V4 this option is not available with 256-bit keys\n\
  123 + --use-aes this option is always on with 256-bit keys\n\
  124 + --force-R5 forces use of deprecated R=5 encryption\n\
  125 +\n\
121 print-opt may be:\n\ 126 print-opt may be:\n\
122 \n\ 127 \n\
123 full allow full printing\n\ 128 full allow full printing\n\
@@ -283,6 +288,9 @@ static std::string show_encryption_method(QPDF::encryption_method_e method) @@ -283,6 +288,9 @@ static std::string show_encryption_method(QPDF::encryption_method_e method)
283 case QPDF::e_aes: 288 case QPDF::e_aes:
284 result = "AESv2"; 289 result = "AESv2";
285 break; 290 break;
  291 + case QPDF::e_aesv3:
  292 + result = "AESv3";
  293 + break;
286 // no default so gcc will warn for missing case 294 // no default so gcc will warn for missing case
287 } 295 }
288 return result; 296 return result;
@@ -485,7 +493,8 @@ parse_encrypt_options( @@ -485,7 +493,8 @@ parse_encrypt_options(
485 bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate, 493 bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate,
486 bool& r3_accessibility, bool& r3_extract, 494 bool& r3_accessibility, bool& r3_extract,
487 qpdf_r3_print_e& r3_print, qpdf_r3_modify_e& r3_modify, 495 qpdf_r3_print_e& r3_print, qpdf_r3_modify_e& r3_modify,
488 - bool& force_V4, bool& cleartext_metadata, bool& use_aes) 496 + bool& force_V4, bool& cleartext_metadata, bool& use_aes,
  497 + bool& force_R5)
489 { 498 {
490 if (cur_arg + 3 >= argc) 499 if (cur_arg + 3 >= argc)
491 { 500 {
@@ -502,9 +511,14 @@ parse_encrypt_options( @@ -502,9 +511,14 @@ parse_encrypt_options(
502 { 511 {
503 keylen = 128; 512 keylen = 128;
504 } 513 }
  514 + else if (len_str == "256")
  515 + {
  516 + keylen = 256;
  517 + use_aes = true;
  518 + }
505 else 519 else
506 { 520 {
507 - usage("encryption key length must be 40 or 128"); 521 + usage("encryption key length must be 40, 128, or 256");
508 } 522 }
509 while (1) 523 while (1)
510 { 524 {
@@ -736,15 +750,30 @@ parse_encrypt_options( @@ -736,15 +750,30 @@ parse_encrypt_options(
736 { 750 {
737 usage("--force-V4 does not take a parameter"); 751 usage("--force-V4 does not take a parameter");
738 } 752 }
739 - if (keylen == 40) 753 + if (keylen != 128)
740 { 754 {
741 - usage("--force-V4 is invalid for 40-bit keys"); 755 + usage("--force-V4 is invalid only for 128-bit keys");
742 } 756 }
743 else 757 else
744 { 758 {
745 force_V4 = true; 759 force_V4 = true;
746 } 760 }
747 } 761 }
  762 + else if (strcmp(arg, "force-R5") == 0)
  763 + {
  764 + if (parameter)
  765 + {
  766 + usage("--force-R5 does not take a parameter");
  767 + }
  768 + if (keylen != 256)
  769 + {
  770 + usage("--force-R5 is invalid only for 256-bit keys");
  771 + }
  772 + else
  773 + {
  774 + force_R5 = true;
  775 + }
  776 + }
748 else if (strcmp(arg, "use-aes") == 0) 777 else if (strcmp(arg, "use-aes") == 0)
749 { 778 {
750 if (parameter == 0) 779 if (parameter == 0)
@@ -765,10 +794,16 @@ parse_encrypt_options( @@ -765,10 +794,16 @@ parse_encrypt_options(
765 { 794 {
766 usage("invalid -use-aes parameter"); 795 usage("invalid -use-aes parameter");
767 } 796 }
768 - if (keylen == 40) 797 + if ((keylen == 40) && result)
769 { 798 {
770 usage("use-aes is invalid for 40-bit keys"); 799 usage("use-aes is invalid for 40-bit keys");
771 } 800 }
  801 + else if ((keylen == 256) && (! result))
  802 + {
  803 + // qpdf would happily create files encrypted with RC4
  804 + // using /V=5, but Adobe reader can't read them.
  805 + usage("use-aes can't be disabled with 256-bit keys");
  806 + }
772 else 807 else
773 { 808 {
774 use_aes = result; 809 use_aes = result;
@@ -921,6 +956,7 @@ int main(int argc, char* argv[]) @@ -921,6 +956,7 @@ int main(int argc, char* argv[])
921 qpdf_r3_print_e r3_print = qpdf_r3p_full; 956 qpdf_r3_print_e r3_print = qpdf_r3p_full;
922 qpdf_r3_modify_e r3_modify = qpdf_r3m_all; 957 qpdf_r3_modify_e r3_modify = qpdf_r3m_all;
923 bool force_V4 = false; 958 bool force_V4 = false;
  959 + bool force_R5 = false;
924 bool cleartext_metadata = false; 960 bool cleartext_metadata = false;
925 bool use_aes = false; 961 bool use_aes = false;
926 962
@@ -1004,7 +1040,7 @@ int main(int argc, char* argv[]) @@ -1004,7 +1040,7 @@ int main(int argc, char* argv[])
1004 user_password, owner_password, keylen, 1040 user_password, owner_password, keylen,
1005 r2_print, r2_modify, r2_extract, r2_annotate, 1041 r2_print, r2_modify, r2_extract, r2_annotate,
1006 r3_accessibility, r3_extract, r3_print, r3_modify, 1042 r3_accessibility, r3_extract, r3_print, r3_modify,
1007 - force_V4, cleartext_metadata, use_aes); 1043 + force_V4, cleartext_metadata, use_aes, force_R5);
1008 encrypt = true; 1044 encrypt = true;
1009 decrypt = false; 1045 decrypt = false;
1010 copy_encryption = false; 1046 copy_encryption = false;
@@ -1612,6 +1648,23 @@ int main(int argc, char* argv[]) @@ -1612,6 +1648,23 @@ int main(int argc, char* argv[])
1612 r3_accessibility, r3_extract, r3_print, r3_modify); 1648 r3_accessibility, r3_extract, r3_print, r3_modify);
1613 } 1649 }
1614 } 1650 }
  1651 + else if (keylen == 256)
  1652 + {
  1653 + if (force_R5)
  1654 + {
  1655 + w.setR5EncryptionParameters(
  1656 + user_password.c_str(), owner_password.c_str(),
  1657 + r3_accessibility, r3_extract, r3_print, r3_modify,
  1658 + !cleartext_metadata);
  1659 + }
  1660 + else
  1661 + {
  1662 + w.setR6EncryptionParameters(
  1663 + user_password.c_str(), owner_password.c_str(),
  1664 + r3_accessibility, r3_extract, r3_print, r3_modify,
  1665 + !cleartext_metadata);
  1666 + }
  1667 + }
1615 else 1668 else
1616 { 1669 {
1617 throw std::logic_error("bad encryption keylen"); 1670 throw std::logic_error("bad encryption keylen");
qpdf/qpdf.testcov
@@ -243,6 +243,7 @@ QPDFWriter extra header text add newline 0 @@ -243,6 +243,7 @@ QPDFWriter extra header text add newline 0
243 QPDF bogus 0 offset 0 243 QPDF bogus 0 offset 0
244 QPDF global offset 0 244 QPDF global offset 0
245 QPDFWriter make stream key direct 0 245 QPDFWriter make stream key direct 0
  246 +QPDFWriter copy V5 0
246 QPDFWriter increasing extension level 0 247 QPDFWriter increasing extension level 0
247 QPDFWriter make Extensions direct 0 248 QPDFWriter make Extensions direct 0
248 QPDFWriter make ADBE direct 1 249 QPDFWriter make ADBE direct 1
@@ -253,3 +254,5 @@ QPDFWriter remove existing Extensions 0 @@ -253,3 +254,5 @@ QPDFWriter remove existing Extensions 0
253 QPDFWriter skip Extensions 0 254 QPDFWriter skip Extensions 0
254 QPDFWriter preserve ADBE 0 255 QPDFWriter preserve ADBE 0
255 QPDF_encryption skip 0x28 0 256 QPDF_encryption skip 0x28 0
  257 +QPDF_encrypt crypt array 0
  258 +QPDF_encryption CFM AESV3 0
qpdf/qtest/qpdf.test
@@ -1250,6 +1250,10 @@ $td-&gt;notify(&quot;--- Encryption Tests ---&quot;); @@ -1250,6 +1250,10 @@ $td-&gt;notify(&quot;--- Encryption Tests ---&quot;);
1250 # resulting files were saved and manually checked with Acrobat 5.0 to 1250 # resulting files were saved and manually checked with Acrobat 5.0 to
1251 # ensure that the security settings were as intended. 1251 # ensure that the security settings were as intended.
1252 1252
  1253 +# The enc-XI-file.pdf files were treated the same way but with Acrobat
  1254 +# XI instead of Acrobat 5.0. They were used to create test files with
  1255 +# newer encryption formats.
  1256 +
1253 # Values: basename, password, encryption flags, /P Encrypt key, 1257 # Values: basename, password, encryption flags, /P Encrypt key,
1254 # extract-for-accessibility, extract-for-any-purpose, 1258 # extract-for-accessibility, extract-for-any-purpose,
1255 # print-low-res, print-high-res, modify-assembly, modify-forms, 1259 # print-low-res, print-high-res, modify-assembly, modify-forms,
@@ -1293,9 +1297,25 @@ my @encrypted_files = @@ -1293,9 +1297,25 @@ my @encrypted_files =
1293 '', -4, 1297 '', -4,
1294 1, 1, 1, 1, 1, 1, 1, 1, 1], 1298 1, 1, 1, 1, 1, 1, 1, 1, 1],
1295 ['long-password', 'asdf asdf asdf asdf asdf asdf qwer'], 1299 ['long-password', 'asdf asdf asdf asdf asdf asdf qwer'],
1296 - ['long-password', 'asdf asdf asdf asdf asdf asdf qw']); 1300 + ['long-password', 'asdf asdf asdf asdf asdf asdf qw'],
  1301 + ['XI-base', ''],
  1302 + ['XI-R6,V5,O=master', '',
  1303 + '-extract=n -print=none -modify=assembly', -2368,
  1304 + 1, 0, 0, 0, 1, 0, 0, 0, 0],
  1305 + ['XI-R6,V5,O=master', 'master',
  1306 + '-extract=n -print=none -modify=assembly', -2368,
  1307 + 1, 0, 0, 0, 1, 0, 0, 0, 0],
  1308 + ['XI-R6,V5,U=view,O=master', 'view',
  1309 + '-print=low', -2052,
  1310 + 1, 1, 1, 0, 1, 1, 1, 1, 1],
  1311 + ['XI-R6,V5,U=view,O=master', 'master',
  1312 + '-print=low', -2052,
  1313 + 1, 1, 1, 0, 1, 1, 1, 1, 1],
  1314 + ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm'],
  1315 + ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcv'],
  1316 + );
1297 1317
1298 -$n_tests += 3 + (2 * (@encrypted_files)) + (6 * (@encrypted_files - 3)) + 9; 1318 +$n_tests += 5 + (2 * (@encrypted_files)) + (6 * (@encrypted_files - 6)) + 9;
1299 1319
1300 $td->runtest("encrypted file", 1320 $td->runtest("encrypted file",
1301 {$td->COMMAND => "test_driver 2 U25A0.pdf"}, 1321 {$td->COMMAND => "test_driver 2 U25A0.pdf"},
@@ -1312,6 +1332,19 @@ $td-&gt;runtest(&quot;recheck encrypted file&quot;, @@ -1312,6 +1332,19 @@ $td-&gt;runtest(&quot;recheck encrypted file&quot;,
1312 $td->EXIT_STATUS => 0}, 1332 $td->EXIT_STATUS => 0},
1313 $td->NORMALIZE_NEWLINES); 1333 $td->NORMALIZE_NEWLINES);
1314 1334
  1335 +# Test that long passwords that are one character too short fail. We
  1336 +# test the truncation cases in the loop below by using passwords
  1337 +# longer than the supported length.
  1338 +$td->runtest("significant password characters (V < 5)",
  1339 + {$td->COMMAND => "qpdf --check enc-long-password.pdf" .
  1340 + " --password='asdf asdf asdf asdf asdf asdf q'"},
  1341 + {$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2});
  1342 +$td->runtest("significant password characters (V = 5)",
  1343 + {$td->COMMAND => "qpdf --check enc-XI-long-password.pdf" .
  1344 + " --password=qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxc"},
  1345 + {$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2});
  1346 +
  1347 +my $enc_base = undef;
1315 foreach my $d (@encrypted_files) 1348 foreach my $d (@encrypted_files)
1316 { 1349 {
1317 my ($file, $pass, $xeflags, $P, 1350 my ($file, $pass, $xeflags, $P,
@@ -1330,17 +1363,26 @@ foreach my $d (@encrypted_files) @@ -1330,17 +1363,26 @@ foreach my $d (@encrypted_files)
1330 "modify annotations: " . &$f($modifyannot) . "\n" . 1363 "modify annotations: " . &$f($modifyannot) . "\n" .
1331 "modify other: " . &$f($modifyother) . "\n" . 1364 "modify other: " . &$f($modifyother) . "\n" .
1332 "modify anything: " . &$f($modifyall) . "\n"; 1365 "modify anything: " . &$f($modifyall) . "\n";
  1366 + if ($file =~ m/XI-/)
  1367 + {
  1368 + $enc_details .=
  1369 + "stream encryption method: AESv3\n" .
  1370 + "string encryption method: AESv3\n" .
  1371 + "file encryption method: AESv3\n";
  1372 + }
1333 1373
1334 # Test writing to stdout 1374 # Test writing to stdout
1335 $td->runtest("decrypt $file", 1375 $td->runtest("decrypt $file",
1336 {$td->COMMAND => 1376 {$td->COMMAND =>
1337 - "qpdf --static-id -qdf --no-original-object-ids" . 1377 + "qpdf --static-id -qdf --object-streams=disable" .
  1378 + " --no-original-object-ids" .
1338 " --password=\"$pass\" enc-$file.pdf -" . 1379 " --password=\"$pass\" enc-$file.pdf -" .
1339 " > $file.enc"}, 1380 " > $file.enc"},
1340 {$td->STRING => "", 1381 {$td->STRING => "",
1341 $td->EXIT_STATUS => 0}); 1382 $td->EXIT_STATUS => 0});
1342 - if ($file eq 'base') 1383 + if ($file =~ m/base$/)
1343 { 1384 {
  1385 + $enc_base = $file;
1344 $td->runtest("check ID", 1386 $td->runtest("check ID",
1345 {$td->COMMAND => "perl check-ID.pl $file.enc"}, 1387 {$td->COMMAND => "perl check-ID.pl $file.enc"},
1346 {$td->STRING => "ID okay\n", 1388 {$td->STRING => "ID okay\n",
@@ -1350,20 +1392,27 @@ foreach my $d (@encrypted_files) @@ -1350,20 +1392,27 @@ foreach my $d (@encrypted_files)
1350 else 1392 else
1351 { 1393 {
1352 $td->runtest("check against base", 1394 $td->runtest("check against base",
1353 - {$td->COMMAND => "./diff-encrypted base.enc $file.enc"}, 1395 + {$td->COMMAND =>
  1396 + "./diff-encrypted $enc_base.enc $file.enc"},
1354 {$td->STRING => "okay\n", 1397 {$td->STRING => "okay\n",
1355 $td->EXIT_STATUS => 0}, 1398 $td->EXIT_STATUS => 0},
1356 $td->NORMALIZE_NEWLINES); 1399 $td->NORMALIZE_NEWLINES);
1357 } 1400 }
1358 - if ($file =~ m/^R(\d),V(\d)(?:,U=(\w+))?(?:,O=(\w+))?$/) 1401 + if ($file =~ m/^(?:XI-)?R(\d),V(\d)(?:,U=(\w+))?(?:,O=(\w+))?$/)
1359 { 1402 {
1360 my $R = $1; 1403 my $R = $1;
1361 my $V = $2; 1404 my $V = $2;
1362 my $upass = $3 || ""; 1405 my $upass = $3 || "";
1363 my $opass = $4 || ""; 1406 my $opass = $4 || "";
1364 - my $bits = (($V == 2) ? 128 : 40); 1407 + my $bits = (($V == 5) ? 256 : ($V == 2) ? 128 : 40);
1365 1408
1366 my $eflags = "-encrypt \"$upass\" \"$opass\" $bits $xeflags --"; 1409 my $eflags = "-encrypt \"$upass\" \"$opass\" $bits $xeflags --";
  1410 + if (($pass ne $upass) && ($V >= 5))
  1411 + {
  1412 + # V >= 5 can no longer recover user password with owner
  1413 + # password.
  1414 + $upass = "";
  1415 + }
1367 $td->runtest("encrypt $file", 1416 $td->runtest("encrypt $file",
1368 {$td->COMMAND => 1417 {$td->COMMAND =>
1369 "qpdf --static-id --no-original-object-ids -qdf" . 1418 "qpdf --static-id --no-original-object-ids -qdf" .
@@ -1488,7 +1537,7 @@ $td-&gt;runtest(&quot;check linearization&quot;, @@ -1488,7 +1537,7 @@ $td-&gt;runtest(&quot;check linearization&quot;,
1488 $td->NORMALIZE_NEWLINES); 1537 $td->NORMALIZE_NEWLINES);
1489 1538
1490 # Test AES encryption in various ways. 1539 # Test AES encryption in various ways.
1491 -$n_tests += 14; 1540 +$n_tests += 18;
1492 $td->runtest("encrypt with AES", 1541 $td->runtest("encrypt with AES",
1493 {$td->COMMAND => "qpdf --encrypt '' '' 128 --use-aes=y --" . 1542 {$td->COMMAND => "qpdf --encrypt '' '' 128 --use-aes=y --" .
1494 " enc-base.pdf a.pdf"}, 1543 " enc-base.pdf a.pdf"},
@@ -1548,6 +1597,24 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;, @@ -1548,6 +1597,24 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;,
1548 {$td->COMMAND => "grep /ObjStm b.pdf | wc -l"}, 1597 {$td->COMMAND => "grep /ObjStm b.pdf | wc -l"},
1549 {$td->REGEXP => "\\s*0\\s*", $td->EXIT_STATUS => 0}, 1598 {$td->REGEXP => "\\s*0\\s*", $td->EXIT_STATUS => 0},
1550 $td->NORMALIZE_NEWLINES); 1599 $td->NORMALIZE_NEWLINES);
  1600 +$td->runtest("encrypt with V=5,R=5",
  1601 + {$td->COMMAND =>
  1602 + "qpdf --encrypt user owner 256 --force-R5 -- " .
  1603 + "minimal.pdf a.pdf"},
  1604 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1605 +$td->runtest("check encryption",
  1606 + {$td->COMMAND => "qpdf --check a.pdf --password=owner"},
  1607 + {$td->FILE => "V5R5.out", $td->EXIT_STATUS => 0},
  1608 + $td->NORMALIZE_NEWLINES);
  1609 +$td->runtest("encrypt with V=5,R=6",
  1610 + {$td->COMMAND =>
  1611 + "qpdf --encrypt user owner 256 -- " .
  1612 + "minimal.pdf a.pdf"},
  1613 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1614 +$td->runtest("check encryption",
  1615 + {$td->COMMAND => "qpdf --check a.pdf --password=user"},
  1616 + {$td->FILE => "V5R6.out", $td->EXIT_STATUS => 0},
  1617 + $td->NORMALIZE_NEWLINES);
1551 1618
1552 # Look at some actual V4 files 1619 # Look at some actual V4 files
1553 $n_tests += 14; 1620 $n_tests += 14;
@@ -1629,6 +1696,36 @@ $td-&gt;runtest(&quot;compare qdf&quot;, @@ -1629,6 +1696,36 @@ $td-&gt;runtest(&quot;compare qdf&quot;,
1629 {$td->STRING => "okay\n", $td->EXIT_STATUS => 0}, 1696 {$td->STRING => "okay\n", $td->EXIT_STATUS => 0},
1630 $td->NORMALIZE_NEWLINES); 1697 $td->NORMALIZE_NEWLINES);
1631 1698
  1699 +# Files with attachments
  1700 +my @attachments = (
  1701 + 'enc-XI-attachments-base.pdf',
  1702 + 'enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf',
  1703 + 'enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf');
  1704 +$n_tests += 4 * @attachments;
  1705 +foreach my $f (@attachments)
  1706 +{
  1707 + my $pass = '';
  1708 + my $tpass = '';
  1709 + if ($f =~ m/U=([^,]+)/)
  1710 + {
  1711 + $pass = "--password=$1";
  1712 + $tpass = $1;
  1713 + }
  1714 + $td->runtest("decrypt $f",
  1715 + {$td->COMMAND => "qpdf --decrypt $pass $f a.pdf"},
  1716 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1717 + $td->runtest("extract attachments",
  1718 + {$td->COMMAND => "test_driver 35 a.pdf"},
  1719 + {$td->FILE => "attachments.out", $td->EXIT_STATUS => 0},
  1720 + $td->NORMALIZE_NEWLINES);
  1721 + $td->runtest("copy $f",
  1722 + {$td->COMMAND => "qpdf $pass $f a.pdf"},
  1723 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1724 + $td->runtest("extract attachments",
  1725 + {$td->COMMAND => "test_driver 35 a.pdf $tpass"},
  1726 + {$td->FILE => "attachments.out", $td->EXIT_STATUS => 0},
  1727 + $td->NORMALIZE_NEWLINES);
  1728 +}
1632 1729
1633 show_ntests(); 1730 show_ntests();
1634 # ---------- 1731 # ----------
qpdf/qtest/qpdf/V5R5.out 0 โ†’ 100644
  1 +checking a.pdf
  2 +PDF Version: 1.7 extension level 3
  3 +R = 5
  4 +P = -4
  5 +User password =
  6 +extract for accessibility: allowed
  7 +extract for any purpose: allowed
  8 +print low resolution: allowed
  9 +print high resolution: allowed
  10 +modify document assembly: allowed
  11 +modify forms: allowed
  12 +modify annotations: allowed
  13 +modify other: allowed
  14 +modify anything: allowed
  15 +stream encryption method: AESv3
  16 +string encryption method: AESv3
  17 +file encryption method: AESv3
  18 +File is not linearized
  19 +No syntax or stream encoding errors found; the file may still contain
  20 +errors that qpdf cannot detect
qpdf/qtest/qpdf/V5R6.out 0 โ†’ 100644
  1 +checking a.pdf
  2 +PDF Version: 1.7 extension level 8
  3 +R = 6
  4 +P = -4
  5 +User password = user
  6 +extract for accessibility: allowed
  7 +extract for any purpose: allowed
  8 +print low resolution: allowed
  9 +print high resolution: allowed
  10 +modify document assembly: allowed
  11 +modify forms: allowed
  12 +modify annotations: allowed
  13 +modify other: allowed
  14 +modify anything: allowed
  15 +stream encryption method: AESv3
  16 +string encryption method: AESv3
  17 +file encryption method: AESv3
  18 +File is not linearized
  19 +No syntax or stream encoding errors found; the file may still contain
  20 +errors that qpdf cannot detect
qpdf/qtest/qpdf/attachments.out 0 โ†’ 100644
  1 +attachment1.txt:
  2 +This is the first attachment.
  3 +--END--
  4 +attachment2.png:
  5 +.PNG........IHDR...1 (2620 bytes)--END--
  6 +test 35 done
qpdf/qtest/qpdf/diff-encrypted
1 #!/bin/sh 1 #!/bin/sh
2 -lines=$(expr + $(diff $1 $2 | egrep '^[<>]' | egrep -v 'Date' | wc -l)) 2 +lines=$(expr + $(diff $1 $2 | egrep '^[<>]' | egrep -v '(Date|InstanceID)' | wc -l))
3 if [ "$lines" = "0" ]; then 3 if [ "$lines" = "0" ]; then
4 echo okay 4 echo okay
5 else 5 else
qpdf/qtest/qpdf/enc-XI-R6,V5,O=master.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,O=master.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-attachments-base.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-base.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/enc-XI-long-password.pdf 0 โ†’ 100644
No preview for this file type
qpdf/test_driver.cc
@@ -112,7 +112,12 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -112,7 +112,12 @@ void runtest(int n, char const* filename1, char const* arg2)
112 { 112 {
113 pdf.setAttemptRecovery(false); 113 pdf.setAttemptRecovery(false);
114 } 114 }
115 - if (n % 2 == 0) 115 + if ((n == 35) && (arg2 != 0))
  116 + {
  117 + // arg2 is password
  118 + pdf.processFile(filename1, arg2);
  119 + }
  120 + else if (n % 2 == 0)
116 { 121 {
117 if (n % 4 == 0) 122 if (n % 4 == 0)
118 { 123 {
@@ -1150,6 +1155,65 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1150,6 +1155,65 @@ void runtest(int n, char const* filename1, char const* arg2)
1150 << "extension level: " << pdf.getExtensionLevel() << std::endl 1155 << "extension level: " << pdf.getExtensionLevel() << std::endl
1151 << pdf.getRoot().getKey("/Extensions").unparse() << std::endl; 1156 << pdf.getRoot().getKey("/Extensions").unparse() << std::endl;
1152 } 1157 }
  1158 + else if (n == 35)
  1159 + {
  1160 + // Extract attachments
  1161 +
  1162 + std::map<std::string, PointerHolder<Buffer> > attachments;
  1163 + QPDFObjectHandle root = pdf.getRoot();
  1164 + QPDFObjectHandle names = root.getKey("/Names");
  1165 + QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles");
  1166 + names = embeddedFiles.getKey("/Names");
  1167 + for (int i = 0; i < names.getArrayNItems(); ++i)
  1168 + {
  1169 + QPDFObjectHandle item = names.getArrayItem(i);
  1170 + if (item.isDictionary() &&
  1171 + item.getKey("/Type").isName() &&
  1172 + (item.getKey("/Type").getName() == "/Filespec") &&
  1173 + item.getKey("/EF").isDictionary() &&
  1174 + item.getKey("/EF").getKey("/F").isStream())
  1175 + {
  1176 + std::string filename = item.getKey("/F").getStringValue();
  1177 + QPDFObjectHandle stream = item.getKey("/EF").getKey("/F");
  1178 + attachments[filename] = stream.getStreamData();
  1179 + }
  1180 + }
  1181 + for (std::map<std::string, PointerHolder<Buffer> >::iterator iter =
  1182 + attachments.begin(); iter != attachments.end(); ++iter)
  1183 + {
  1184 + std::string const& filename = (*iter).first;
  1185 + std::string data = std::string(
  1186 + (char const*)(*iter).second->getBuffer(),
  1187 + (*iter).second->getSize());
  1188 + bool is_binary = false;
  1189 + for (size_t i = 0; i < data.size(); ++i)
  1190 + {
  1191 + if (data[i] < 0)
  1192 + {
  1193 + is_binary = true;
  1194 + break;
  1195 + }
  1196 + }
  1197 + if (is_binary)
  1198 + {
  1199 + std::string t;
  1200 + for (size_t i = 0; i < std::min(data.size(), (size_t)20); ++i)
  1201 + {
  1202 + if ((data[i] >= 32) && (data[i] <= 126))
  1203 + {
  1204 + t += data[i];
  1205 + }
  1206 + else
  1207 + {
  1208 + t += ".";
  1209 + }
  1210 + }
  1211 + t += " (" + QUtil::int_to_string(data.size()) + " bytes)";
  1212 + data = t;
  1213 + }
  1214 + std::cout << filename << ":\n" << data << "--END--\n";
  1215 + }
  1216 + }
1153 else 1217 else
1154 { 1218 {
1155 throw std::runtime_error(std::string("invalid test ") + 1219 throw std::runtime_error(std::string("invalid test ") +