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,6 +476,13 @@ class QPDF
476 encrypt_metadata(encrypt_metadata) 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 int getV() const; 487 int getV() const;
481 int getR() const; 488 int getR() const;
@@ -492,8 +499,10 @@ class QPDF @@ -492,8 +499,10 @@ class QPDF
492 bool getEncryptMetadata() const; 499 bool getEncryptMetadata() const;
493 // Bits in P are numbered from 1 as in the PDF spec. 500 // Bits in P are numbered from 1 as in the PDF spec.
494 void setP(size_t bit, bool val); 501 void setP(size_t bit, bool val);
  502 + void setP(unsigned long val);
495 void setO(std::string const&); 503 void setO(std::string const&);
496 void setU(std::string const&); 504 void setU(std::string const&);
  505 + void setId1(std::string const& val);
497 void setV5EncryptionParameters( 506 void setV5EncryptionParameters(
498 std::string const& O, 507 std::string const& O,
499 std::string const& OE, 508 std::string const& OE,
include/qpdf/QPDFWriter.hh
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 #include <qpdf/DLL.h> 23 #include <qpdf/DLL.h>
24 #include <qpdf/Types.h> 24 #include <qpdf/Types.h>
25 25
  26 +#include <bitset>
26 #include <cstdio> 27 #include <cstdio>
27 #include <functional> 28 #include <functional>
28 #include <list> 29 #include <list>
@@ -510,9 +511,6 @@ class QPDFWriter @@ -510,9 +511,6 @@ class QPDFWriter
510 std::string getOriginalID1(); 511 std::string getOriginalID1();
511 void generateID(); 512 void generateID();
512 void interpretR3EncryptionParameters( 513 void interpretR3EncryptionParameters(
513 - std::set<int>& bits_to_clear,  
514 - char const* user_password,  
515 - char const* owner_password,  
516 bool allow_accessibility, 514 bool allow_accessibility,
517 bool allow_extract, 515 bool allow_extract,
518 bool allow_assemble, 516 bool allow_assemble,
@@ -524,14 +522,7 @@ class QPDFWriter @@ -524,14 +522,7 @@ class QPDFWriter
524 void disableIncompatibleEncryption(int major, int minor, int extension_level); 522 void disableIncompatibleEncryption(int major, int minor, int extension_level);
525 void parseVersion(std::string const& version, int& major, int& minor) const; 523 void parseVersion(std::string const& version, int& major, int& minor) const;
526 int compareVersions(int major1, int minor1, int major2, int minor2) const; 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 void setEncryptionParametersInternal( 526 void setEncryptionParametersInternal(
536 std::string const& user_password, std::string const& encryption_key); 527 std::string const& user_password, std::string const& encryption_key);
537 void setDataKey(int objid); 528 void setDataKey(int objid);
libqpdf/QPDFWriter.cc
@@ -430,21 +430,20 @@ QPDFWriter::setR2EncryptionParametersInsecure( @@ -430,21 +430,20 @@ QPDFWriter::setR2EncryptionParametersInsecure(
430 bool allow_extract, 430 bool allow_extract,
431 bool allow_annotate) 431 bool allow_annotate)
432 { 432 {
433 - std::set<int> clear; 433 + m->encryption = std::make_unique<QPDF::EncryptionData>(1, 2, 5, true);
434 if (!allow_print) { 434 if (!allow_print) {
435 - clear.insert(3); 435 + m->encryption->setP(3, false);
436 } 436 }
437 if (!allow_modify) { 437 if (!allow_modify) {
438 - clear.insert(4); 438 + m->encryption->setP(4, false);
439 } 439 }
440 if (!allow_extract) { 440 if (!allow_extract) {
441 - clear.insert(5); 441 + m->encryption->setP(5, false);
442 } 442 }
443 if (!allow_annotate) { 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 void 449 void
@@ -459,11 +458,8 @@ QPDFWriter::setR3EncryptionParametersInsecure( @@ -459,11 +458,8 @@ QPDFWriter::setR3EncryptionParametersInsecure(
459 bool allow_modify_other, 458 bool allow_modify_other,
460 qpdf_r3_print_e print) 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 interpretR3EncryptionParameters( 462 interpretR3EncryptionParameters(
464 - clear,  
465 - user_password,  
466 - owner_password,  
467 allow_accessibility, 463 allow_accessibility,
468 allow_extract, 464 allow_extract,
469 allow_assemble, 465 allow_assemble,
@@ -472,7 +468,7 @@ QPDFWriter::setR3EncryptionParametersInsecure( @@ -472,7 +468,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
472 allow_modify_other, 468 allow_modify_other,
473 print, 469 print,
474 qpdf_r3m_all); 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 void 474 void
@@ -489,11 +485,9 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -489,11 +485,9 @@ QPDFWriter::setR4EncryptionParametersInsecure(
489 bool encrypt_metadata, 485 bool encrypt_metadata,
490 bool use_aes) 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 interpretR3EncryptionParameters( 490 interpretR3EncryptionParameters(
494 - clear,  
495 - user_password,  
496 - owner_password,  
497 allow_accessibility, 491 allow_accessibility,
498 allow_extract, 492 allow_extract,
499 allow_assemble, 493 allow_assemble,
@@ -502,8 +496,7 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -502,8 +496,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
502 allow_modify_other, 496 allow_modify_other,
503 print, 497 print,
504 qpdf_r3m_all); 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 void 502 void
@@ -519,11 +512,9 @@ QPDFWriter::setR5EncryptionParameters( @@ -519,11 +512,9 @@ QPDFWriter::setR5EncryptionParameters(
519 qpdf_r3_print_e print, 512 qpdf_r3_print_e print,
520 bool encrypt_metadata) 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 interpretR3EncryptionParameters( 517 interpretR3EncryptionParameters(
524 - clear,  
525 - user_password,  
526 - owner_password,  
527 allow_accessibility, 518 allow_accessibility,
528 allow_extract, 519 allow_extract,
529 allow_assemble, 520 allow_assemble,
@@ -532,8 +523,7 @@ QPDFWriter::setR5EncryptionParameters( @@ -532,8 +523,7 @@ QPDFWriter::setR5EncryptionParameters(
532 allow_modify_other, 523 allow_modify_other,
533 print, 524 print,
534 qpdf_r3m_all); 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 void 529 void
@@ -549,11 +539,8 @@ QPDFWriter::setR6EncryptionParameters( @@ -549,11 +539,8 @@ QPDFWriter::setR6EncryptionParameters(
549 qpdf_r3_print_e print, 539 qpdf_r3_print_e print,
550 bool encrypt_metadata) 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 interpretR3EncryptionParameters( 543 interpretR3EncryptionParameters(
554 - clear,  
555 - user_password,  
556 - owner_password,  
557 allow_accessibility, 544 allow_accessibility,
558 allow_extract, 545 allow_extract,
559 allow_assemble, 546 allow_assemble,
@@ -563,14 +550,11 @@ QPDFWriter::setR6EncryptionParameters( @@ -563,14 +550,11 @@ QPDFWriter::setR6EncryptionParameters(
563 print, 550 print,
564 qpdf_r3m_all); 551 qpdf_r3m_all);
565 m->encrypt_use_aes = true; 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 void 556 void
570 QPDFWriter::interpretR3EncryptionParameters( 557 QPDFWriter::interpretR3EncryptionParameters(
571 - std::set<int>& clear,  
572 - char const* user_password,  
573 - char const* owner_password,  
574 bool allow_accessibility, 558 bool allow_accessibility,
575 bool allow_extract, 559 bool allow_extract,
576 bool allow_assemble, 560 bool allow_assemble,
@@ -609,27 +593,24 @@ QPDFWriter::interpretR3EncryptionParameters( @@ -609,27 +593,24 @@ QPDFWriter::interpretR3EncryptionParameters(
609 // 10: accessibility; ignored by readers, should always be set 593 // 10: accessibility; ignored by readers, should always be set
610 // 11: document assembly even if 4 is clear 594 // 11: document assembly even if 4 is clear
611 // 12: high-resolution printing 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 if (!allow_extract) { 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 switch (print) { 605 switch (print) {
624 case qpdf_r3p_none: 606 case qpdf_r3p_none:
625 - clear.insert(3); // any printing  
626 - 607 + m->encryption->setP(3, false); // any printing
  608 + [[fallthrough]];
627 case qpdf_r3p_low: 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 case qpdf_r3p_full: 612 case qpdf_r3p_full:
631 break; 613 break;
632 -  
633 // no default so gcc warns for missing cases 614 // no default so gcc warns for missing cases
634 } 615 }
635 616
@@ -640,71 +621,42 @@ QPDFWriter::interpretR3EncryptionParameters( @@ -640,71 +621,42 @@ QPDFWriter::interpretR3EncryptionParameters(
640 // NOT EXERCISED IN TEST SUITE 621 // NOT EXERCISED IN TEST SUITE
641 switch (modify) { 622 switch (modify) {
642 case qpdf_r3m_none: 623 case qpdf_r3m_none:
643 - clear.insert(11); // document assembly  
644 - 624 + m->encryption->setP(11, false); // document assembly
  625 + [[fallthrough]];
645 case qpdf_r3m_assembly: 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 case qpdf_r3m_form: 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 case qpdf_r3m_annotate: 632 case qpdf_r3m_annotate:
652 - clear.insert(4); // other modifications  
653 - 633 + m->encryption->setP(4, false); // other modifications
  634 + [[fallthrough]];
654 case qpdf_r3m_all: 635 case qpdf_r3m_all:
655 break; 636 break;
656 -  
657 // no default so gcc warns for missing cases 637 // no default so gcc warns for missing cases
658 } 638 }
659 // END NOT EXERCISED IN TEST SUITE 639 // END NOT EXERCISED IN TEST SUITE
660 640
661 if (!allow_assemble) { 641 if (!allow_assemble) {
662 - clear.insert(11); 642 + m->encryption->setP(11, false);
663 } 643 }
664 if (!allow_annotate_and_form) { 644 if (!allow_annotate_and_form) {
665 - clear.insert(6); 645 + m->encryption->setP(6, false);
666 } 646 }
667 if (!allow_form_filling) { 647 if (!allow_form_filling) {
668 - clear.insert(9); 648 + m->encryption->setP(9, false);
669 } 649 }
670 if (!allow_modify_other) { 650 if (!allow_modify_other) {
671 - clear.insert(4); 651 + m->encryption->setP(4, false);
672 } 652 }
673 } 653 }
674 654
675 void 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 generateID(); 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 auto encryption_key = m->encryption->compute_parameters(user_password, owner_password); 660 auto encryption_key = m->encryption->compute_parameters(user_password, owner_password);
709 setEncryptionParametersInternal(user_password, encryption_key); 661 setEncryptionParametersInternal(user_password, encryption_key);
710 } 662 }
@@ -754,7 +706,6 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -754,7 +706,6 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
754 V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(), 706 V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(),
755 m->id1, // m->id1 == the other file's id1 707 m->id1, // m->id1 == the other file's id1
756 encrypt_metadata); 708 encrypt_metadata);
757 -  
758 setEncryptionParametersInternal(qpdf.getPaddedUserPassword(), encryption_key); 709 setEncryptionParametersInternal(qpdf.getPaddedUserPassword(), encryption_key);
759 } 710 }
760 } 711 }
libqpdf/QPDF_encryption.cc
@@ -54,7 +54,7 @@ QPDF::EncryptionData::getLengthBytes() const @@ -54,7 +54,7 @@ QPDF::EncryptionData::getLengthBytes() const
54 int 54 int
55 QPDF::EncryptionData::getP() const 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 bool 60 bool
@@ -133,6 +133,18 @@ QPDF::EncryptionData::setP(size_t bit, bool val) @@ -133,6 +133,18 @@ QPDF::EncryptionData::setP(size_t bit, bool val)
133 } 133 }
134 134
135 void 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 QPDF::EncryptionData::setV5EncryptionParameters( 148 QPDF::EncryptionData::setV5EncryptionParameters(
137 std::string const& O, 149 std::string const& O,
138 std::string const& OE, 150 std::string const& OE,