Commit 2055837e386be29c3eebf4ee012e6cebd7e2c773

Authored by m-holger
1 parent 80093a05

Refactor encryption permissions handling to replace `std::set` with `std::bitset…

…`, simplify method signatures, and centralize logic in `QPDF::EncryptionData` for improved maintainability.
include/qpdf/QPDF.hh
... ... @@ -476,6 +476,13 @@ class QPDF
476 476 encrypt_metadata(encrypt_metadata)
477 477 {
478 478 }
  479 + EncryptionData(int V, int R, int Length_bytes, bool encrypt_metadata) :
  480 + V(V),
  481 + R(R),
  482 + Length_bytes(Length_bytes),
  483 + encrypt_metadata(encrypt_metadata)
  484 + {
  485 + }
479 486  
480 487 int getV() const;
481 488 int getR() const;
... ... @@ -492,8 +499,10 @@ class QPDF
492 499 bool getEncryptMetadata() const;
493 500 // Bits in P are numbered from 1 as in the PDF spec.
494 501 void setP(size_t bit, bool val);
  502 + void setP(unsigned long val);
495 503 void setO(std::string const&);
496 504 void setU(std::string const&);
  505 + void setId1(std::string const& val);
497 506 void setV5EncryptionParameters(
498 507 std::string const& O,
499 508 std::string const& OE,
... ...
include/qpdf/QPDFWriter.hh
... ... @@ -23,6 +23,7 @@
23 23 #include <qpdf/DLL.h>
24 24 #include <qpdf/Types.h>
25 25  
  26 +#include <bitset>
26 27 #include <cstdio>
27 28 #include <functional>
28 29 #include <list>
... ... @@ -510,9 +511,6 @@ class QPDFWriter
510 511 std::string getOriginalID1();
511 512 void generateID();
512 513 void interpretR3EncryptionParameters(
513   - std::set<int>& bits_to_clear,
514   - char const* user_password,
515   - char const* owner_password,
516 514 bool allow_accessibility,
517 515 bool allow_extract,
518 516 bool allow_assemble,
... ... @@ -524,14 +522,7 @@ class QPDFWriter
524 522 void disableIncompatibleEncryption(int major, int minor, int extension_level);
525 523 void parseVersion(std::string const& version, int& major, int& minor) const;
526 524 int compareVersions(int major1, int minor1, int major2, int minor2) const;
527   - void setEncryptionParameters(
528   - char const* user_password,
529   - char const* owner_password,
530   - int V,
531   - int R,
532   - int key_len,
533   - bool encrypt_metadata,
534   - std::set<int>& bits_to_clear);
  525 + void setEncryptionParameters(char const* user_password, char const* owner_password);
535 526 void setEncryptionParametersInternal(
536 527 std::string const& user_password, std::string const& encryption_key);
537 528 void setDataKey(int objid);
... ...
libqpdf/QPDFWriter.cc
... ... @@ -430,21 +430,20 @@ QPDFWriter::setR2EncryptionParametersInsecure(
430 430 bool allow_extract,
431 431 bool allow_annotate)
432 432 {
433   - std::set<int> clear;
  433 + m->encryption = std::make_unique<QPDF::EncryptionData>(1, 2, 5, true);
434 434 if (!allow_print) {
435   - clear.insert(3);
  435 + m->encryption->setP(3, false);
436 436 }
437 437 if (!allow_modify) {
438   - clear.insert(4);
  438 + m->encryption->setP(4, false);
439 439 }
440 440 if (!allow_extract) {
441   - clear.insert(5);
  441 + m->encryption->setP(5, false);
442 442 }
443 443 if (!allow_annotate) {
444   - clear.insert(6);
  444 + m->encryption->setP(6, false);
445 445 }
446   -
447   - setEncryptionParameters(user_password, owner_password, 1, 2, 5, true, clear);
  446 + setEncryptionParameters(user_password, owner_password);
448 447 }
449 448  
450 449 void
... ... @@ -459,11 +458,8 @@ QPDFWriter::setR3EncryptionParametersInsecure(
459 458 bool allow_modify_other,
460 459 qpdf_r3_print_e print)
461 460 {
462   - std::set<int> clear;
  461 + m->encryption = std::make_unique<QPDF::EncryptionData>(2, 3, 16, true);
463 462 interpretR3EncryptionParameters(
464   - clear,
465   - user_password,
466   - owner_password,
467 463 allow_accessibility,
468 464 allow_extract,
469 465 allow_assemble,
... ... @@ -472,7 +468,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
472 468 allow_modify_other,
473 469 print,
474 470 qpdf_r3m_all);
475   - setEncryptionParameters(user_password, owner_password, 2, 3, 16, true, clear);
  471 + setEncryptionParameters(user_password, owner_password);
476 472 }
477 473  
478 474 void
... ... @@ -489,11 +485,9 @@ QPDFWriter::setR4EncryptionParametersInsecure(
489 485 bool encrypt_metadata,
490 486 bool use_aes)
491 487 {
492   - std::set<int> clear;
  488 + m->encryption = std::make_unique<QPDF::EncryptionData>(4, 4, 16, encrypt_metadata);
  489 + m->encrypt_use_aes = use_aes;
493 490 interpretR3EncryptionParameters(
494   - clear,
495   - user_password,
496   - owner_password,
497 491 allow_accessibility,
498 492 allow_extract,
499 493 allow_assemble,
... ... @@ -502,8 +496,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
502 496 allow_modify_other,
503 497 print,
504 498 qpdf_r3m_all);
505   - m->encrypt_use_aes = use_aes;
506   - setEncryptionParameters(user_password, owner_password, 4, 4, 16, encrypt_metadata, clear);
  499 + setEncryptionParameters(user_password, owner_password);
507 500 }
508 501  
509 502 void
... ... @@ -519,11 +512,9 @@ QPDFWriter::setR5EncryptionParameters(
519 512 qpdf_r3_print_e print,
520 513 bool encrypt_metadata)
521 514 {
522   - std::set<int> clear;
  515 + m->encryption = std::make_unique<QPDF::EncryptionData>(5, 5, 32, encrypt_metadata);
  516 + m->encrypt_use_aes = true;
523 517 interpretR3EncryptionParameters(
524   - clear,
525   - user_password,
526   - owner_password,
527 518 allow_accessibility,
528 519 allow_extract,
529 520 allow_assemble,
... ... @@ -532,8 +523,7 @@ QPDFWriter::setR5EncryptionParameters(
532 523 allow_modify_other,
533 524 print,
534 525 qpdf_r3m_all);
535   - m->encrypt_use_aes = true;
536   - setEncryptionParameters(user_password, owner_password, 5, 5, 32, encrypt_metadata, clear);
  526 + setEncryptionParameters(user_password, owner_password);
537 527 }
538 528  
539 529 void
... ... @@ -549,11 +539,8 @@ QPDFWriter::setR6EncryptionParameters(
549 539 qpdf_r3_print_e print,
550 540 bool encrypt_metadata)
551 541 {
552   - std::set<int> clear;
  542 + m->encryption = std::make_unique<QPDF::EncryptionData>(5, 6, 32, encrypt_metadata);
553 543 interpretR3EncryptionParameters(
554   - clear,
555   - user_password,
556   - owner_password,
557 544 allow_accessibility,
558 545 allow_extract,
559 546 allow_assemble,
... ... @@ -563,14 +550,11 @@ QPDFWriter::setR6EncryptionParameters(
563 550 print,
564 551 qpdf_r3m_all);
565 552 m->encrypt_use_aes = true;
566   - setEncryptionParameters(user_password, owner_password, 5, 6, 32, encrypt_metadata, clear);
  553 + setEncryptionParameters(user_password, owner_password);
567 554 }
568 555  
569 556 void
570 557 QPDFWriter::interpretR3EncryptionParameters(
571   - std::set<int>& clear,
572   - char const* user_password,
573   - char const* owner_password,
574 558 bool allow_accessibility,
575 559 bool allow_extract,
576 560 bool allow_assemble,
... ... @@ -609,27 +593,24 @@ QPDFWriter::interpretR3EncryptionParameters(
609 593 // 10: accessibility; ignored by readers, should always be set
610 594 // 11: document assembly even if 4 is clear
611 595 // 12: high-resolution printing
612   -
613   - if (!allow_accessibility) {
614   - // setEncryptionParameters sets this if R > 3
615   - clear.insert(10);
  596 + if (!allow_accessibility && m->encryption->getR() <= 3) {
  597 + // Bit 10 is deprecated and should always be set. This used to mean accessibility. There
  598 + // is no way to disable accessibility with R > 3.
  599 + m->encryption->setP(10, false);
616 600 }
617 601 if (!allow_extract) {
618   - clear.insert(5);
  602 + m->encryption->setP(5, false);
619 603 }
620 604  
621   - // Note: these switch statements all "fall through" (no break statements). Each option clears
622   - // successively more access bits.
623 605 switch (print) {
624 606 case qpdf_r3p_none:
625   - clear.insert(3); // any printing
626   -
  607 + m->encryption->setP(3, false); // any printing
  608 + [[fallthrough]];
627 609 case qpdf_r3p_low:
628   - clear.insert(12); // high resolution printing
629   -
  610 + m->encryption->setP(12, false); // high resolution printing
  611 + [[fallthrough]];
630 612 case qpdf_r3p_full:
631 613 break;
632   -
633 614 // no default so gcc warns for missing cases
634 615 }
635 616  
... ... @@ -640,71 +621,42 @@ QPDFWriter::interpretR3EncryptionParameters(
640 621 // NOT EXERCISED IN TEST SUITE
641 622 switch (modify) {
642 623 case qpdf_r3m_none:
643   - clear.insert(11); // document assembly
644   -
  624 + m->encryption->setP(11, false); // document assembly
  625 + [[fallthrough]];
645 626 case qpdf_r3m_assembly:
646   - clear.insert(9); // filling in form fields
647   -
  627 + m->encryption->setP(9, false); // filling in form fields
  628 + [[fallthrough]];
648 629 case qpdf_r3m_form:
649   - clear.insert(6); // modify annotations, fill in form fields
650   -
  630 + m->encryption->setP(6, false); // modify annotations, fill in form fields
  631 + [[fallthrough]];
651 632 case qpdf_r3m_annotate:
652   - clear.insert(4); // other modifications
653   -
  633 + m->encryption->setP(4, false); // other modifications
  634 + [[fallthrough]];
654 635 case qpdf_r3m_all:
655 636 break;
656   -
657 637 // no default so gcc warns for missing cases
658 638 }
659 639 // END NOT EXERCISED IN TEST SUITE
660 640  
661 641 if (!allow_assemble) {
662   - clear.insert(11);
  642 + m->encryption->setP(11, false);
663 643 }
664 644 if (!allow_annotate_and_form) {
665   - clear.insert(6);
  645 + m->encryption->setP(6, false);
666 646 }
667 647 if (!allow_form_filling) {
668   - clear.insert(9);
  648 + m->encryption->setP(9, false);
669 649 }
670 650 if (!allow_modify_other) {
671   - clear.insert(4);
  651 + m->encryption->setP(4, false);
672 652 }
673 653 }
674 654  
675 655 void
676   -QPDFWriter::setEncryptionParameters(
677   - char const* user_password,
678   - char const* owner_password,
679   - int V,
680   - int R,
681   - int key_len,
682   - bool encrypt_metadata,
683   - std::set<int>& bits_to_clear)
  656 +QPDFWriter::setEncryptionParameters(char const* user_password, char const* owner_password)
684 657 {
685   - // PDF specification refers to bits with the low bit numbered 1.
686   - // We have to convert this into a bit field.
687   -
688   - // Specification always requires bits 1 and 2 to be cleared.
689   - bits_to_clear.insert(1);
690   - bits_to_clear.insert(2);
691   -
692   - if (R > 3) {
693   - // Bit 10 is deprecated and should always be set. This used to mean accessibility. There
694   - // is no way to disable accessibility with R > 3.
695   - bits_to_clear.erase(10);
696   - }
697   -
698   - int P = 0;
699   - // Create the complement of P, then invert.
700   - for (int b: bits_to_clear) {
701   - P |= (1 << (b - 1));
702   - }
703   - P = ~P;
704   -
705 658 generateID();
706   - m->encryption = std::make_unique<QPDF::EncryptionData>(
707   - V, R, key_len, P, "", "", "", "", "", m->id1, encrypt_metadata);
  659 + m->encryption->setId1(m->id1);
708 660 auto encryption_key = m->encryption->compute_parameters(user_password, owner_password);
709 661 setEncryptionParametersInternal(user_password, encryption_key);
710 662 }
... ... @@ -754,7 +706,6 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
754 706 V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(),
755 707 m->id1, // m->id1 == the other file's id1
756 708 encrypt_metadata);
757   -
758 709 setEncryptionParametersInternal(qpdf.getPaddedUserPassword(), encryption_key);
759 710 }
760 711 }
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -54,7 +54,7 @@ QPDF::EncryptionData::getLengthBytes() const
54 54 int
55 55 QPDF::EncryptionData::getP() const
56 56 {
57   - return static_cast<int>(P.to_ullong());
  57 + return static_cast<int>(P.to_ulong());
58 58 }
59 59  
60 60 bool
... ... @@ -133,6 +133,18 @@ QPDF::EncryptionData::setP(size_t bit, bool val)
133 133 }
134 134  
135 135 void
  136 +QPDF::EncryptionData::setP(unsigned long val)
  137 +{
  138 + P = std::bitset<32>(val);
  139 +}
  140 +
  141 +void
  142 +QPDF::EncryptionData::setId1(std::string const& val)
  143 +{
  144 + id1 = val;
  145 +}
  146 +
  147 +void
136 148 QPDF::EncryptionData::setV5EncryptionParameters(
137 149 std::string const& O,
138 150 std::string const& OE,
... ...